docs(coding-agent/tui): clarified silent-abort marker consumers and stamp-ordering invariant

This commit is contained in:
cognitive
2026-05-13 08:31:17 +00:00
parent 40d028d900
commit cc666f32b3
2 changed files with 20 additions and 15 deletions
@@ -1142,18 +1142,18 @@ export class AgentSession {
}
}
// Plan-mode → compaction transition: stamp the silent-abort marker on the
// persisted message BEFORE the obfuscator's display-side copy below. Both
// `displayEvent` (used for emit) and `event.message` (used for persistence
// via SessionManager.appendMessage / appendCustomMessageEntry) carry the
// marker afterward — `displayEvent.message` inherits via the `{...message}`
// spread inside the obfuscator branch; persistence sees the in-place
// mutation on `event.message`. The flag is consumed here, scoped strictly
// to this aborted message_end; the caller's `finally` (in
// InteractiveMode.#approvePlan) clears it again on every terminal
// compaction outcome so a leaked flag cannot silence a later unrelated
// abort. NOTE: if a future refactor reorders this site relative to the
// obfuscator copy, the stamp must remain BEFORE the copy.
// Plan-mode → compaction transition: stamp `SILENT_ABORT_MARKER` on the
// persisted message BEFORE the obfuscator's display-side copy below.
// Invariant (must hold across refactors): this branch precedes the
// `let displayEvent = event; ... displayEvent = { ...event, message: { ...message, content: deobfuscated } }`
// block. After stamping, both `displayEvent.message` (via the spread)
// and `event.message` (in-place mutation, used by SessionManager
// persistence) carry the marker, guaranteeing streaming render and
// history replay branch identically. The one-shot flag is consumed
// here, scoped strictly to this aborted message_end; the caller's
// `finally` (in `InteractiveMode.#approvePlan`) clears it again on
// every terminal compaction outcome (`ok` / `cancelled` / `failed` /
// throw) so a leaked flag cannot silence a later unrelated abort.
if (
event.type === "message_end" &&
event.message.role === "assistant" &&
@@ -44,11 +44,16 @@ export interface SkillPromptDetails {
* and must NOT surface as a red "Operation aborted" line. Distinct from
* `undefined` (default) so user-cancel aborts with no errorMessage still
* render normally. Persists through SessionManager so history replay
* branches identically. */
* branches identically.
*
* Consumers: `AgentSession.#handleAgentEvent` (stamper) writes this value;
* `EventController.#handleMessageEnd`, `AssistantMessageComponent`, and
* `ui-helpers.addMessageToChat` (renderers) read it via `isSilentAbort`. */
export const SILENT_ABORT_MARKER = "__omp.silent_abort__";
/** Type-guard for the marker. Renderers should branch on this rather than
* string-comparing inline. */
/** Type-guard for `SILENT_ABORT_MARKER`. Renderers MUST branch on this rather
* than string-comparing inline so refactors to the marker constant (e.g.,
* namespacing changes) propagate through every consumer in lockstep. */
export function isSilentAbort(errorMessage: string | undefined): boolean {
return errorMessage === SILENT_ABORT_MARKER;
}