Get this on every PR automatically. Webhook fires, AI reviews, you approve.
Install on vercel/eve →
// reviewed by pulllight

PullLight found 3 issues

vercel/eve #1197 — feat(eve): publish canonical harness lifecycle events by @chadhietala Jul 27, 2026
1 high 2 medium
🔍
// convert to your repo
PullLight caught 3 1 high, 2 medium on this PR.
Install on vercel/eve to get this on every PR automatically — 60 seconds.
Findings
3 issues
high logic
`activeTurnId` is read by `toLifecycleEvent` before it is updated for `turn.started` events. On line 27, `toLifecycleEvent` is called with the old (undefined) `activeTurnId`, and only on line 28 is `activeTurnId` set to `event.data.turnId`. This means the `turn.started` lifecycle event is published with `turnId: undefined` instead of the correct turn ID. The assignment should happen before the call to `toLifecycleEvent`.
// packages/eve/src/harness/instrumentation-native-events.ts:28
⚡ Suggested fix
    await handleEvent(event, messages);
    if (event.type === "turn.started") activeTurnId = event.data.turnId;
    const lifecycleEvent = toLifecycleEvent(event, input, activeTurnId);
    if (lifecycleEvent !== undefined) await hooks.publish(lifecycleEvent);
medium logic
When `executeModelCall()` throws, `rethrowNoOutputAsEmptyResponse(error)` is called and its return value is returned. However, `rethrowNoOutputAsEmptyResponse` is designed to re-throw (its name implies it always throws or rethrows), so if it does not throw for a given error, the `attempt.failed` event is published but the caller receives a resolved promise with whatever `rethrowNoOutputAsEmptyResponse` returns — silently swallowing the error. The original code used `.catch(rethrowNoOutputAsEmptyResponse)` which preserved the rejection chain correctly. Verify that returning the result of `rethrowNoOutputAsEmptyResponse` is intentional and covers all error cases.
// packages/eve/src/harness/tool-loop.ts:1051
medium logic
`createInstrumentationHandleEvent` is called inside the step function (per-step), which means a new closure with a fresh `activeTurnId = undefined` is created on every step invocation. If the same session progresses through multiple steps, `activeTurnId` will never carry over between steps, so `session.waiting`/`session.completed`/`session.failed` lifecycle events emitted in a later step will have `turnId: undefined`. Consider hoisting this call outside the per-step closure, or passing the active turn ID explicitly.
// packages/eve/src/harness/tool-loop.ts:539
model: claude-sonnet-4-5   input: 4742 tokens   output: 828 tokens