Read the terminal event names the chat API actually writes

56eaa4243210 · AtlantisPleb · · parent 39e351a888c8

Read the terminal event names the chat API actually writes

A coder turn reached the server, ran, and finished, and the client polled past
the end of it forever. The server names its terminal events `response_completed`
and `response_failed`; this watched for `completed` and `failed`, which no event
ever carries, so the poll loop had no exit and the status line counted seconds
against a turn that was already done.

The names came from reading the source with a pattern that also matched the
tails of the real ones, which is why the mistake survived a test suite: the
tests asserted the same wrong names the client used, so they agreed with each
other and with nothing else. They now use the names from a captured event log.

A failure reports the server's `reason` and carries its `code`, so
`invalid_response` from the provider reaches the transcript as what it is
instead of as silence.

Verified against production: a turn now streams and settles, and the status
line returns to ready.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTmy4SEXrHXouw5sZbs3f4
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

Not deployed through the forge lane

No push, promotion, build, or deploy receipt references this commit (receipts are scanned over a bounded recent window). Changes shipped by full node replacement carry their proof in the release gate receipt instead.

Changed files

  • modified packages/openagents-cli/src/coder-ox.ts
  • modified packages/openagents-cli/test/coder-ox.test.ts

Diff

2 files changed, +15 -11

packages/openagents-cli/src/coder-ox.ts modified +8 -4

@@ -104,12 +104,16 @@ export class OxAlphaReplySource {

104 104
        if (event.type === "text_delta") {
105 105
          const value = event.payload?.["value"];
106 106
          if (typeof value === "string" && value.length > 0) yield value;
107
        } else if (event.type === "completed") {
107
        } else if (event.type === "response_completed") {
108 108
          finished = true;
109
        } else if (event.type === "failed") {
110
          const reason = event.payload?.["message"] ?? event.payload?.["error"];
109
        } else if (event.type === "response_failed") {
110
          // The server names the terminal events `response_completed` and
111
          // `response_failed`, and reports why in `reason` with a stable
112
          // `code` beside it.
113
          const reason = event.payload?.["reason"];
114
          const code = event.payload?.["code"];
111 115
          throw new OxAlphaUnavailable(
112
            "turn_failed",
116
            typeof code === "string" ? code : "turn_failed",
113 117
            typeof reason === "string" ? reason : "The turn failed on the server.",
114 118
          );
115 119
        }
packages/openagents-cli/test/coder-ox.test.ts modified +7 -7

@@ -44,7 +44,7 @@ describe("OxAlphaReplySource", () => {

44 44
        [
45 45
          { run_id: "run-1", sequence: 1, type: "text_delta", payload: { value: "Hello" } },
46 46
          { run_id: "run-1", sequence: 2, type: "text_delta", payload: { value: " there" } },
47
          { run_id: "run-1", sequence: 3, type: "completed", payload: {} },
47
          { run_id: "run-1", sequence: 3, type: "response_completed", payload: {} },
48 48
        ],
49 49
      ],
50 50
      json(202, { turn: { id: "run-1" } }),

@@ -60,7 +60,7 @@ describe("OxAlphaReplySource", () => {

60 60
        [
61 61
          { run_id: "run-0", sequence: 9, type: "text_delta", payload: { value: "OLD" } },
62 62
          { run_id: "run-1", sequence: 1, type: "text_delta", payload: { value: "new" } },
63
          { run_id: "run-1", sequence: 2, type: "completed", payload: {} },
63
          { run_id: "run-1", sequence: 2, type: "response_completed", payload: {} },
64 64
        ],
65 65
      ],
66 66
      json(202, { turn: { id: "run-1" } }),

@@ -77,7 +77,7 @@ describe("OxAlphaReplySource", () => {

77 77
        [
78 78
          { run_id: "run-1", sequence: 1, type: "text_delta", payload: { value: "one" } },
79 79
          { run_id: "run-1", sequence: 2, type: "text_delta", payload: { value: " two" } },
80
          { run_id: "run-1", sequence: 3, type: "completed", payload: {} },
80
          { run_id: "run-1", sequence: 3, type: "response_completed", payload: {} },
81 81
        ],
82 82
      ],
83 83
      json(202, { turn: { id: "run-1" } }),

@@ -93,7 +93,7 @@ describe("OxAlphaReplySource", () => {

93 93
        [
94 94
          { run_id: "run-1", sequence: 1, type: "reasoning_delta", payload: { value: "thinking" } },
95 95
          { run_id: "run-1", sequence: 2, type: "text_delta", payload: { value: "said" } },
96
          { run_id: "run-1", sequence: 3, type: "completed", payload: {} },
96
          { run_id: "run-1", sequence: 3, type: "response_completed", payload: {} },
97 97
        ],
98 98
      ],
99 99
      json(202, { turn: { id: "run-1" } }),

@@ -121,8 +121,8 @@ describe("OxAlphaReplySource", () => {

121 121
          {
122 122
            run_id: "run-1",
123 123
            sequence: 1,
124
            type: "failed",
125
            payload: { message: "provider refused" },
124
            type: "response_failed",
125
            payload: { reason: "provider refused", code: "invalid_response" },
126 126
          },
127 127
        ],
128 128
      ],

@@ -147,7 +147,7 @@ describe("OxAlphaReplySource", () => {

147 147
        return Promise.resolve(
148 148
          json(200, {
149 149
            events: submitted
150
              ? [{ run_id: "run-1", sequence: 1, type: "completed", payload: {} }]
150
              ? [{ run_id: "run-1", sequence: 1, type: "response_completed", payload: {} }]
151 151
              : [],
152 152
          }),
153 153
        );

This page updates live while a promote is in flight · changelog