Problem
Two small but related gaps in the terminal lifecycle coverage from PRs C and H:
-
No uncaught-exception boundary around runEmbeddedAttempt. If the inner run function throws (network glitch, provider SDK bug, runtime OOM), setTerminalLifecycleMeta is never called and the caller gets no terminal lifecycle meta at all. The finally block at run.ts:1783 disposes context but doesn't set a fallback terminal state.
-
No "cancelled" liveness state. In src/agents/pi-embedded-runner/run/incomplete-turn.ts:184, user-initiated abort signals and hard runtime errors both map to "blocked". From the user's perspective these are very different situations — a cancelled run should be visually distinct from a blocked run, and downstream channels may want to surface them differently.
Both are on completion criterion 4 in #64227 — "replay/liveness failures are surfaced as explicit states, not silent disappearance."
Fix
For the exception boundary:
Wrap the main body of runEmbeddedPiAgent in a try/finally or try/catch where the catch path sets:
attempt.setTerminalLifecycleMeta?.({
replayInvalid: true,
livenessState: "blocked", // or a new "errored" state
});
and re-throws. This way every exit has terminal meta set, even uncaught exceptions.
For the "cancelled" state:
- Add
"cancelled" to the EmbeddedRunLivenessState union in src/agents/pi-embedded-runner/types.ts.
- Update
resolveRunLivenessState in incomplete-turn.ts:168-191 to return "cancelled" when params.aborted && !params.timedOut.
- Update any downstream consumer that does a 4-way match on liveness state to handle the new value (look for
"working" | "paused" | "blocked" | "abandoned" grep hits).
Consider whether this interacts with the channel surfacing issue (#64XXX) — the two probably want to land together so the UX is coherent.
Acceptance
runEmbeddedPiAgent sets terminal meta on every uncaught exception path (regression test with a forced throw).
- User abort produces
livenessState: "cancelled" distinct from "blocked" (regression test with a simulated abort).
Part of
#64227 (criterion 4).
Problem
Two small but related gaps in the terminal lifecycle coverage from PRs C and H:
No uncaught-exception boundary around
runEmbeddedAttempt. If the inner run function throws (network glitch, provider SDK bug, runtime OOM),setTerminalLifecycleMetais never called and the caller gets no terminal lifecycle meta at all. Thefinallyblock atrun.ts:1783disposes context but doesn't set a fallback terminal state.No
"cancelled"liveness state. Insrc/agents/pi-embedded-runner/run/incomplete-turn.ts:184, user-initiated abort signals and hard runtime errors both map to"blocked". From the user's perspective these are very different situations — a cancelled run should be visually distinct from a blocked run, and downstream channels may want to surface them differently.Both are on completion criterion 4 in #64227 — "replay/liveness failures are surfaced as explicit states, not silent disappearance."
Fix
For the exception boundary:
Wrap the main body of
runEmbeddedPiAgentin atry/finallyortry/catchwhere the catch path sets:and re-throws. This way every exit has terminal meta set, even uncaught exceptions.
For the
"cancelled"state:"cancelled"to theEmbeddedRunLivenessStateunion insrc/agents/pi-embedded-runner/types.ts.resolveRunLivenessStateinincomplete-turn.ts:168-191to return"cancelled"whenparams.aborted && !params.timedOut."working" | "paused" | "blocked" | "abandoned"grep hits).Consider whether this interacts with the channel surfacing issue (#64XXX) — the two probably want to land together so the UX is coherent.
Acceptance
runEmbeddedPiAgentsets terminal meta on every uncaught exception path (regression test with a forced throw).livenessState: "cancelled"distinct from"blocked"(regression test with a simulated abort).Part of
#64227 (criterion 4).