@kodelyth/codex 2026.5.40 → 2026.5.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/dist/client-ChMX13_o.js +642 -0
  2. package/dist/client-factory-D3dIsp4Y.js +9 -0
  3. package/dist/command-formatters-BRW7_Nu7.js +519 -0
  4. package/dist/command-handlers-P2IqtXaZ.js +1462 -0
  5. package/dist/compact-baos5flR.js +329 -0
  6. package/dist/computer-use-VfLvTMaa.js +367 -0
  7. package/dist/config-CezENx_E.js +510 -0
  8. package/dist/doctor-contract-api.js +53 -0
  9. package/dist/harness.js +51 -0
  10. package/dist/index.js +1133 -0
  11. package/dist/media-understanding-provider.js +335 -0
  12. package/dist/models-B9DhrIwD.js +110 -0
  13. package/dist/node-cli-sessions-De4_DuFw.js +1216 -0
  14. package/dist/plugin-activation-BlMuJeXz.js +452 -0
  15. package/dist/prompt-overlay.js +12 -0
  16. package/dist/protocol-C9UWI98H.js +9 -0
  17. package/dist/protocol-validators-BGBspNmF.js +5988 -0
  18. package/dist/provider-catalog.js +84 -0
  19. package/dist/provider-discovery.js +33 -0
  20. package/dist/provider.js +150 -0
  21. package/dist/rate-limit-cache-CHuacE27.js +24 -0
  22. package/dist/request-CTQKUxaa.js +89 -0
  23. package/dist/rolldown-runtime-DUslC3ob.js +14 -0
  24. package/dist/run-attempt-DqV2OU1R.js +5366 -0
  25. package/dist/session-binding-3PzU7ZTW.js +222 -0
  26. package/dist/shared-client-Cnyr9dyT.js +631 -0
  27. package/dist/side-question-CP5XlA0U.js +667 -0
  28. package/dist/test-api.js +45 -0
  29. package/dist/thread-lifecycle-DBJetBuV.js +1561 -0
  30. package/dist/vision-tools-Cl_5a93K.js +1379 -0
  31. package/doctor-contract-api.test.ts +44 -0
  32. package/doctor-contract-api.ts +68 -0
  33. package/harness.ts +72 -0
  34. package/index.test.ts +230 -0
  35. package/index.ts +66 -0
  36. package/klaw.plugin.json +24 -85
  37. package/media-understanding-provider.test.ts +486 -0
  38. package/media-understanding-provider.ts +521 -0
  39. package/package.json +3 -3
  40. package/prompt-overlay-runtime-contract.test.ts +48 -0
  41. package/prompt-overlay.ts +21 -0
  42. package/provider-catalog.ts +83 -0
  43. package/provider-discovery.ts +45 -0
  44. package/provider.test.ts +384 -0
  45. package/provider.ts +243 -0
  46. package/src/app-server/app-inventory-cache.test.ts +176 -0
  47. package/src/app-server/app-inventory-cache.ts +324 -0
  48. package/src/app-server/approval-bridge.test.ts +1471 -0
  49. package/src/app-server/approval-bridge.ts +1211 -0
  50. package/src/app-server/auth-bridge.test.ts +1449 -0
  51. package/src/app-server/auth-bridge.ts +614 -0
  52. package/src/app-server/auth-profile-runtime-contract.test.ts +239 -0
  53. package/src/app-server/capabilities.ts +27 -0
  54. package/src/app-server/client-factory.ts +24 -0
  55. package/src/app-server/client.test.ts +563 -0
  56. package/src/app-server/client.ts +715 -0
  57. package/src/app-server/compact.test.ts +710 -0
  58. package/src/app-server/compact.ts +500 -0
  59. package/src/app-server/computer-use.test.ts +788 -0
  60. package/src/app-server/computer-use.ts +683 -0
  61. package/src/app-server/config.test.ts +879 -0
  62. package/src/app-server/config.ts +1038 -0
  63. package/src/app-server/context-engine-projection.test.ts +252 -0
  64. package/src/app-server/context-engine-projection.ts +403 -0
  65. package/src/app-server/delivery-no-reply-runtime-contract.test.ts +80 -0
  66. package/src/app-server/dynamic-tool-diagnostics.ts +73 -0
  67. package/src/app-server/dynamic-tool-profile.ts +69 -0
  68. package/src/app-server/dynamic-tools.test.ts +1302 -0
  69. package/src/app-server/dynamic-tools.ts +623 -0
  70. package/src/app-server/elicitation-bridge.test.ts +1056 -0
  71. package/src/app-server/elicitation-bridge.ts +783 -0
  72. package/src/app-server/event-projector.test.ts +2668 -0
  73. package/src/app-server/event-projector.ts +2057 -0
  74. package/src/app-server/image-payload-sanitizer.test.ts +49 -0
  75. package/src/app-server/image-payload-sanitizer.ts +167 -0
  76. package/src/app-server/klaw-owned-tool-runtime-contract.test.ts +456 -0
  77. package/src/app-server/local-runtime-attribution.ts +39 -0
  78. package/src/app-server/managed-binary.test.ts +139 -0
  79. package/src/app-server/managed-binary.ts +193 -0
  80. package/src/app-server/models.test.ts +246 -0
  81. package/src/app-server/models.ts +172 -0
  82. package/src/app-server/native-hook-relay.test.ts +271 -0
  83. package/src/app-server/native-hook-relay.ts +150 -0
  84. package/src/app-server/native-subagent-task-mirror.test.ts +573 -0
  85. package/src/app-server/native-subagent-task-mirror.ts +497 -0
  86. package/src/app-server/outcome-fallback-runtime-contract.test.ts +404 -0
  87. package/src/app-server/plugin-activation.test.ts +336 -0
  88. package/src/app-server/plugin-activation.ts +283 -0
  89. package/src/app-server/plugin-app-cache-key.ts +74 -0
  90. package/src/app-server/plugin-approval-roundtrip.ts +122 -0
  91. package/src/app-server/plugin-inventory.test.ts +355 -0
  92. package/src/app-server/plugin-inventory.ts +357 -0
  93. package/src/app-server/plugin-thread-config.test.ts +865 -0
  94. package/src/app-server/plugin-thread-config.ts +455 -0
  95. package/src/app-server/protocol-generated/json/DynamicToolCallParams.json +33 -0
  96. package/src/app-server/protocol-generated/json/v2/ErrorNotification.json +199 -0
  97. package/src/app-server/protocol-generated/json/v2/GetAccountResponse.json +102 -0
  98. package/src/app-server/protocol-generated/json/v2/ModelListResponse.json +227 -0
  99. package/src/app-server/protocol-generated/json/v2/ThreadResumeResponse.json +2630 -0
  100. package/src/app-server/protocol-generated/json/v2/ThreadStartResponse.json +2630 -0
  101. package/src/app-server/protocol-generated/json/v2/TurnCompletedNotification.json +1659 -0
  102. package/src/app-server/protocol-generated/json/v2/TurnStartResponse.json +1655 -0
  103. package/src/app-server/protocol-validators.test.ts +75 -0
  104. package/src/app-server/protocol-validators.ts +203 -0
  105. package/src/app-server/protocol.ts +520 -0
  106. package/src/app-server/rate-limit-cache.ts +48 -0
  107. package/src/app-server/rate-limits.test.ts +202 -0
  108. package/src/app-server/rate-limits.ts +583 -0
  109. package/src/app-server/request.ts +73 -0
  110. package/src/app-server/run-attempt.context-engine.test.ts +1004 -0
  111. package/src/app-server/run-attempt.test.ts +9477 -0
  112. package/src/app-server/run-attempt.ts +4683 -0
  113. package/src/app-server/run-attempt.vision-tools.test.ts +35 -0
  114. package/src/app-server/schema-normalization-runtime-contract.test.ts +206 -0
  115. package/src/app-server/session-binding.test.ts +303 -0
  116. package/src/app-server/session-binding.ts +398 -0
  117. package/src/app-server/session-history.ts +44 -0
  118. package/src/app-server/shared-client.test.ts +589 -0
  119. package/src/app-server/shared-client.ts +289 -0
  120. package/src/app-server/side-question.test.ts +1175 -0
  121. package/src/app-server/side-question.ts +1007 -0
  122. package/src/app-server/test-support.ts +48 -0
  123. package/src/app-server/thread-lifecycle.test.ts +447 -0
  124. package/src/app-server/thread-lifecycle.ts +939 -0
  125. package/src/app-server/thread-lifecycle.user-mcp-servers.test.ts +442 -0
  126. package/src/app-server/timeout.ts +9 -0
  127. package/src/app-server/tool-progress-normalization.ts +77 -0
  128. package/src/app-server/trajectory.test.ts +205 -0
  129. package/src/app-server/trajectory.ts +365 -0
  130. package/src/app-server/transcript-mirror.test.ts +524 -0
  131. package/src/app-server/transcript-mirror.ts +208 -0
  132. package/src/app-server/transcript-repair-runtime-contract.test.ts +44 -0
  133. package/src/app-server/transport-stdio.test.ts +171 -0
  134. package/src/app-server/transport-stdio.ts +107 -0
  135. package/src/app-server/transport-websocket.test.ts +69 -0
  136. package/src/app-server/transport-websocket.ts +90 -0
  137. package/src/app-server/transport.ts +117 -0
  138. package/src/app-server/user-input-bridge.test.ts +249 -0
  139. package/src/app-server/user-input-bridge.ts +316 -0
  140. package/src/app-server/version.ts +4 -0
  141. package/src/app-server/vision-tools.ts +12 -0
  142. package/src/command-account.ts +544 -0
  143. package/src/command-formatters.ts +425 -0
  144. package/src/command-handlers.ts +2004 -0
  145. package/src/command-rpc.test.ts +16 -0
  146. package/src/command-rpc.ts +142 -0
  147. package/src/commands.test.ts +3312 -0
  148. package/src/commands.ts +65 -0
  149. package/src/conversation-binding-data.ts +124 -0
  150. package/src/conversation-binding.test.ts +599 -0
  151. package/src/conversation-binding.ts +561 -0
  152. package/src/conversation-control.test.ts +126 -0
  153. package/src/conversation-control.ts +303 -0
  154. package/src/conversation-turn-collector.test.ts +191 -0
  155. package/src/conversation-turn-collector.ts +186 -0
  156. package/src/conversation-turn-input.test.ts +141 -0
  157. package/src/conversation-turn-input.ts +106 -0
  158. package/src/manifest.test.ts +20 -0
  159. package/src/migration/apply.ts +501 -0
  160. package/src/migration/helpers.ts +55 -0
  161. package/src/migration/plan.ts +461 -0
  162. package/src/migration/provider.test.ts +1741 -0
  163. package/src/migration/provider.ts +41 -0
  164. package/src/migration/source.ts +643 -0
  165. package/src/migration/targets.ts +25 -0
  166. package/src/node-cli-sessions.test.ts +180 -0
  167. package/src/node-cli-sessions.ts +711 -0
  168. package/test-api.ts +82 -0
  169. package/tsconfig.json +16 -0
  170. package/doctor-contract-api.js +0 -7
  171. package/harness.js +0 -7
  172. package/index.js +0 -7
  173. package/media-understanding-provider.js +0 -7
  174. package/prompt-overlay.js +0 -7
  175. package/provider-catalog.js +0 -7
  176. package/provider-discovery.js +0 -7
  177. package/provider.js +0 -7
  178. package/test-api.js +0 -7
@@ -0,0 +1,573 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import {
3
+ codexNativeSubagentRunId,
4
+ CodexNativeSubagentTaskMirror,
5
+ type TaskLifecycleRuntime,
6
+ } from "./native-subagent-task-mirror.js";
7
+
8
+ function createRuntime() {
9
+ return {
10
+ createRunningTaskRun: vi.fn(),
11
+ recordTaskRunProgressByRunId: vi.fn(() => []),
12
+ finalizeTaskRunByRunId: vi.fn(() => []),
13
+ } as unknown as TaskLifecycleRuntime;
14
+ }
15
+
16
+ describe("CodexNativeSubagentTaskMirror", () => {
17
+ it("creates a silent task-registry task for a native Codex subagent thread", () => {
18
+ const runtime = createRuntime();
19
+ const mirror = new CodexNativeSubagentTaskMirror(
20
+ {
21
+ parentThreadId: "parent-thread",
22
+ requesterSessionKey: "agent:main:main",
23
+ agentId: "main",
24
+ now: () => 20_000,
25
+ },
26
+ runtime,
27
+ );
28
+
29
+ mirror.handleNotification({
30
+ method: "thread/started",
31
+ params: {
32
+ thread: {
33
+ id: "child-thread",
34
+ sessionId: "session-tree",
35
+ preview: "write the Madrid wine script",
36
+ createdAt: 10,
37
+ status: { type: "active", activeFlags: [] },
38
+ source: {
39
+ subAgent: {
40
+ thread_spawn: {
41
+ parent_thread_id: "parent-thread",
42
+ depth: 1,
43
+ agent_nickname: "Poincare",
44
+ agent_role: "worker",
45
+ },
46
+ },
47
+ },
48
+ },
49
+ },
50
+ });
51
+
52
+ expect(runtime.createRunningTaskRun).toHaveBeenCalledWith({
53
+ runtime: "subagent",
54
+ taskKind: "codex-native",
55
+ sourceId: "codex-thread:child-thread",
56
+ requesterSessionKey: "agent:main:main",
57
+ ownerKey: "agent:main:main",
58
+ scopeKind: "session",
59
+ agentId: "main",
60
+ runId: "codex-thread:child-thread",
61
+ label: "Poincare",
62
+ task: "write the Madrid wine script",
63
+ notifyPolicy: "silent",
64
+ deliveryStatus: "not_applicable",
65
+ preferMetadata: true,
66
+ startedAt: 10_000,
67
+ lastEventAt: 20_000,
68
+ progressSummary: "Codex native subagent started.",
69
+ });
70
+ expect(vi.mocked(runtime.createRunningTaskRun).mock.calls[0]?.[0]).not.toHaveProperty(
71
+ "childSessionKey",
72
+ );
73
+ expect(runtime.recordTaskRunProgressByRunId).toHaveBeenCalledWith({
74
+ runId: "codex-thread:child-thread",
75
+ runtime: "subagent",
76
+ lastEventAt: 20_000,
77
+ progressSummary: "Codex native subagent is active.",
78
+ });
79
+ });
80
+
81
+ it("ignores subagent threads spawned by a different parent thread", () => {
82
+ const runtime = createRuntime();
83
+ const mirror = new CodexNativeSubagentTaskMirror(
84
+ {
85
+ parentThreadId: "parent-thread",
86
+ requesterSessionKey: "agent:main:main",
87
+ },
88
+ runtime,
89
+ );
90
+
91
+ mirror.handleNotification({
92
+ method: "thread/started",
93
+ params: {
94
+ thread: {
95
+ id: "other-child",
96
+ source: {
97
+ subAgent: {
98
+ thread_spawn: {
99
+ parent_thread_id: "other-parent",
100
+ depth: 1,
101
+ },
102
+ },
103
+ },
104
+ },
105
+ },
106
+ });
107
+
108
+ expect(runtime.createRunningTaskRun).not.toHaveBeenCalled();
109
+ expect(runtime.recordTaskRunProgressByRunId).not.toHaveBeenCalled();
110
+ expect(runtime.finalizeTaskRunByRunId).not.toHaveBeenCalled();
111
+ });
112
+
113
+ it("deduplicates repeated thread-started notifications for the same child thread", () => {
114
+ const runtime = createRuntime();
115
+ const mirror = new CodexNativeSubagentTaskMirror(
116
+ {
117
+ parentThreadId: "parent-thread",
118
+ requesterSessionKey: "agent:main:main",
119
+ },
120
+ runtime,
121
+ );
122
+ const notification = {
123
+ method: "thread/started",
124
+ params: {
125
+ thread: {
126
+ id: "child-thread",
127
+ source: {
128
+ subAgent: {
129
+ thread_spawn: {
130
+ parent_thread_id: "parent-thread",
131
+ depth: 1,
132
+ },
133
+ },
134
+ },
135
+ },
136
+ },
137
+ } as const;
138
+
139
+ mirror.handleNotification(notification);
140
+ mirror.handleNotification(notification);
141
+
142
+ expect(runtime.createRunningTaskRun).toHaveBeenCalledTimes(1);
143
+ });
144
+
145
+ it("maps Codex thread status changes onto the mirrored task run", () => {
146
+ const runtime = createRuntime();
147
+ const mirror = new CodexNativeSubagentTaskMirror(
148
+ {
149
+ parentThreadId: "parent-thread",
150
+ requesterSessionKey: "agent:main:main",
151
+ now: () => 30_000,
152
+ },
153
+ runtime,
154
+ );
155
+
156
+ mirror.handleNotification({
157
+ method: "thread/status/changed",
158
+ params: {
159
+ threadId: "child-thread",
160
+ status: { type: "idle" },
161
+ },
162
+ });
163
+ mirror.handleNotification({
164
+ method: "thread/status/changed",
165
+ params: {
166
+ threadId: "failed-child",
167
+ status: { type: "systemError" },
168
+ },
169
+ });
170
+
171
+ expect(runtime.finalizeTaskRunByRunId).toHaveBeenNthCalledWith(1, {
172
+ runId: codexNativeSubagentRunId("child-thread"),
173
+ runtime: "subagent",
174
+ status: "succeeded",
175
+ endedAt: 30_000,
176
+ lastEventAt: 30_000,
177
+ progressSummary: "Codex native subagent is idle.",
178
+ terminalSummary: "Codex native subagent finished.",
179
+ });
180
+ expect(runtime.finalizeTaskRunByRunId).toHaveBeenNthCalledWith(2, {
181
+ runId: codexNativeSubagentRunId("failed-child"),
182
+ runtime: "subagent",
183
+ status: "failed",
184
+ endedAt: 30_000,
185
+ lastEventAt: 30_000,
186
+ error: "Codex app-server reported a system error for the native subagent thread.",
187
+ progressSummary: "Codex native subagent hit a system error.",
188
+ terminalSummary: "Codex native subagent failed.",
189
+ });
190
+ });
191
+
192
+ it("creates and updates tasks from Codex collab agent item state", () => {
193
+ const runtime = createRuntime();
194
+ const mirror = new CodexNativeSubagentTaskMirror(
195
+ {
196
+ parentThreadId: "parent-thread",
197
+ requesterSessionKey: "agent:main:main",
198
+ now: () => 40_000,
199
+ },
200
+ runtime,
201
+ );
202
+
203
+ mirror.handleNotification({
204
+ method: "item/completed",
205
+ params: {
206
+ item: {
207
+ type: "collabAgentToolCall",
208
+ tool: "spawnAgent",
209
+ senderThreadId: "parent-thread",
210
+ receiverThreadIds: ["child-thread"],
211
+ prompt: "write the proof file",
212
+ agentsStates: {
213
+ "child-thread": {
214
+ status: "pendingInit",
215
+ message: null,
216
+ },
217
+ },
218
+ },
219
+ },
220
+ });
221
+ mirror.handleNotification({
222
+ method: "item/completed",
223
+ params: {
224
+ item: {
225
+ type: "collabAgentToolCall",
226
+ tool: "wait",
227
+ senderThreadId: "parent-thread",
228
+ receiverThreadIds: [],
229
+ agentsStates: {
230
+ "child-thread": {
231
+ status: "completed",
232
+ message: "done",
233
+ },
234
+ },
235
+ },
236
+ },
237
+ });
238
+
239
+ expect(runtime.createRunningTaskRun).toHaveBeenCalledWith({
240
+ runtime: "subagent",
241
+ taskKind: "codex-native",
242
+ sourceId: "codex-thread:child-thread",
243
+ requesterSessionKey: "agent:main:main",
244
+ ownerKey: "agent:main:main",
245
+ scopeKind: "session",
246
+ runId: "codex-thread:child-thread",
247
+ label: "Codex subagent",
248
+ task: "write the proof file",
249
+ notifyPolicy: "silent",
250
+ deliveryStatus: "not_applicable",
251
+ preferMetadata: true,
252
+ startedAt: 40_000,
253
+ lastEventAt: 40_000,
254
+ progressSummary: "Codex native subagent spawned.",
255
+ });
256
+ expect(vi.mocked(runtime.createRunningTaskRun).mock.calls[0]?.[0]).not.toHaveProperty(
257
+ "childSessionKey",
258
+ );
259
+ expect(runtime.recordTaskRunProgressByRunId).toHaveBeenCalledWith({
260
+ runId: "codex-thread:child-thread",
261
+ runtime: "subagent",
262
+ lastEventAt: 40_000,
263
+ progressSummary: "Codex native subagent is initializing.",
264
+ });
265
+ expect(runtime.finalizeTaskRunByRunId).toHaveBeenCalledWith({
266
+ runId: "codex-thread:child-thread",
267
+ runtime: "subagent",
268
+ status: "succeeded",
269
+ endedAt: 40_000,
270
+ lastEventAt: 40_000,
271
+ progressSummary: "done",
272
+ terminalSummary: "done",
273
+ });
274
+ });
275
+
276
+ it("finalizes stale collab agent state from the blocked tool call status", () => {
277
+ const runtime = createRuntime();
278
+ const mirror = new CodexNativeSubagentTaskMirror(
279
+ {
280
+ parentThreadId: "parent-thread",
281
+ requesterSessionKey: "agent:main:main",
282
+ now: () => 45_000,
283
+ },
284
+ runtime,
285
+ );
286
+
287
+ mirror.handleNotification({
288
+ method: "item/completed",
289
+ params: {
290
+ item: {
291
+ type: "collabAgentToolCall",
292
+ tool: "spawnAgent",
293
+ status: "blocked",
294
+ senderThreadId: "parent-thread",
295
+ receiverThreadIds: ["child-thread"],
296
+ prompt: "read cwd",
297
+ agentsStates: {
298
+ "child-thread": {
299
+ status: "pendingInit",
300
+ message: "Native hook relay unavailable",
301
+ },
302
+ },
303
+ },
304
+ },
305
+ });
306
+
307
+ expect(runtime.recordTaskRunProgressByRunId).not.toHaveBeenCalledWith({
308
+ runId: "codex-thread:child-thread",
309
+ runtime: "subagent",
310
+ lastEventAt: 45_000,
311
+ progressSummary: "Native hook relay unavailable",
312
+ });
313
+ expect(runtime.finalizeTaskRunByRunId).toHaveBeenCalledWith({
314
+ runId: "codex-thread:child-thread",
315
+ runtime: "subagent",
316
+ status: "succeeded",
317
+ endedAt: 45_000,
318
+ lastEventAt: 45_000,
319
+ progressSummary: "Native hook relay unavailable",
320
+ terminalSummary: "Native hook relay unavailable",
321
+ terminalOutcome: "blocked",
322
+ });
323
+ });
324
+
325
+ it("does not treat completed tool calls as completed subagents", () => {
326
+ const runtime = createRuntime();
327
+ const mirror = new CodexNativeSubagentTaskMirror(
328
+ {
329
+ parentThreadId: "parent-thread",
330
+ requesterSessionKey: "agent:main:main",
331
+ now: () => 46_000,
332
+ },
333
+ runtime,
334
+ );
335
+
336
+ mirror.handleNotification({
337
+ method: "item/completed",
338
+ params: {
339
+ item: {
340
+ type: "collabAgentToolCall",
341
+ tool: "spawnAgent",
342
+ status: "completed",
343
+ senderThreadId: "parent-thread",
344
+ receiverThreadIds: ["child-thread"],
345
+ prompt: "read cwd",
346
+ agentsStates: {
347
+ "child-thread": {
348
+ status: "pendingInit",
349
+ message: null,
350
+ },
351
+ },
352
+ },
353
+ },
354
+ });
355
+
356
+ expect(runtime.recordTaskRunProgressByRunId).toHaveBeenCalledWith({
357
+ runId: "codex-thread:child-thread",
358
+ runtime: "subagent",
359
+ lastEventAt: 46_000,
360
+ progressSummary: "Codex native subagent is initializing.",
361
+ });
362
+ expect(runtime.finalizeTaskRunByRunId).not.toHaveBeenCalled();
363
+ });
364
+
365
+ it("does not treat failed non-spawn tool calls as failed subagents", () => {
366
+ const runtime = createRuntime();
367
+ const mirror = new CodexNativeSubagentTaskMirror(
368
+ {
369
+ parentThreadId: "parent-thread",
370
+ requesterSessionKey: "agent:main:main",
371
+ now: () => 47_000,
372
+ },
373
+ runtime,
374
+ );
375
+
376
+ mirror.handleNotification({
377
+ method: "item/completed",
378
+ params: {
379
+ item: {
380
+ type: "collabAgentToolCall",
381
+ tool: "wait",
382
+ status: "failed",
383
+ senderThreadId: "parent-thread",
384
+ receiverThreadIds: [],
385
+ agentsStates: {
386
+ "child-thread": {
387
+ status: "running",
388
+ message: "wait timed out",
389
+ },
390
+ },
391
+ },
392
+ },
393
+ });
394
+
395
+ expect(runtime.recordTaskRunProgressByRunId).toHaveBeenCalledWith({
396
+ runId: "codex-thread:child-thread",
397
+ runtime: "subagent",
398
+ lastEventAt: 47_000,
399
+ progressSummary: "wait timed out",
400
+ });
401
+ expect(runtime.finalizeTaskRunByRunId).not.toHaveBeenCalled();
402
+ });
403
+
404
+ it("preserves a completed collab agent message when the thread later goes idle", () => {
405
+ const runtime = createRuntime();
406
+ const mirror = new CodexNativeSubagentTaskMirror(
407
+ {
408
+ parentThreadId: "parent-thread",
409
+ requesterSessionKey: "agent:main:main",
410
+ now: () => 50_000,
411
+ },
412
+ runtime,
413
+ );
414
+
415
+ mirror.handleNotification({
416
+ method: "item/completed",
417
+ params: {
418
+ item: {
419
+ type: "collabAgentToolCall",
420
+ tool: "spawnAgent",
421
+ senderThreadId: "parent-thread",
422
+ receiverThreadIds: ["child-thread"],
423
+ prompt: "write the proof file",
424
+ agentsStates: {
425
+ "child-thread": {
426
+ status: "completed",
427
+ message: "No user task is specified.",
428
+ },
429
+ },
430
+ },
431
+ },
432
+ });
433
+ mirror.handleNotification({
434
+ method: "thread/status/changed",
435
+ params: {
436
+ threadId: "child-thread",
437
+ status: { type: "idle" },
438
+ },
439
+ });
440
+
441
+ expect(runtime.finalizeTaskRunByRunId).toHaveBeenCalledTimes(1);
442
+ expect(runtime.finalizeTaskRunByRunId).toHaveBeenCalledWith({
443
+ runId: "codex-thread:child-thread",
444
+ runtime: "subagent",
445
+ status: "succeeded",
446
+ endedAt: 50_000,
447
+ lastEventAt: 50_000,
448
+ progressSummary: "No user task is specified.",
449
+ terminalSummary: "No user task is specified.",
450
+ });
451
+ });
452
+
453
+ it("lets terminal collab agent state correct an earlier idle thread status", () => {
454
+ const runtime = createRuntime();
455
+ const mirror = new CodexNativeSubagentTaskMirror(
456
+ {
457
+ parentThreadId: "parent-thread",
458
+ requesterSessionKey: "agent:main:main",
459
+ now: () => 55_000,
460
+ },
461
+ runtime,
462
+ );
463
+
464
+ mirror.handleNotification({
465
+ method: "thread/status/changed",
466
+ params: {
467
+ threadId: "child-thread",
468
+ status: { type: "idle" },
469
+ },
470
+ });
471
+ mirror.handleNotification({
472
+ method: "item/completed",
473
+ params: {
474
+ item: {
475
+ type: "collabAgentToolCall",
476
+ tool: "spawnAgent",
477
+ status: "failed",
478
+ senderThreadId: "parent-thread",
479
+ receiverThreadIds: ["child-thread"],
480
+ prompt: "read cwd",
481
+ agentsStates: {
482
+ "child-thread": {
483
+ status: "pendingInit",
484
+ message: "Native hook relay unavailable",
485
+ },
486
+ },
487
+ },
488
+ },
489
+ });
490
+
491
+ expect(runtime.finalizeTaskRunByRunId).toHaveBeenNthCalledWith(1, {
492
+ runId: "codex-thread:child-thread",
493
+ runtime: "subagent",
494
+ status: "succeeded",
495
+ endedAt: 55_000,
496
+ lastEventAt: 55_000,
497
+ progressSummary: "Codex native subagent is idle.",
498
+ terminalSummary: "Codex native subagent finished.",
499
+ });
500
+ expect(runtime.finalizeTaskRunByRunId).toHaveBeenNthCalledWith(2, {
501
+ runId: "codex-thread:child-thread",
502
+ runtime: "subagent",
503
+ status: "failed",
504
+ endedAt: 55_000,
505
+ lastEventAt: 55_000,
506
+ error: "Native hook relay unavailable",
507
+ progressSummary: "Native hook relay unavailable",
508
+ terminalSummary: "Native hook relay unavailable",
509
+ });
510
+ });
511
+
512
+ it("normalizes collab agent status spelling from alternate event surfaces", () => {
513
+ const runtime = createRuntime();
514
+ const mirror = new CodexNativeSubagentTaskMirror(
515
+ {
516
+ parentThreadId: "parent-thread",
517
+ requesterSessionKey: "agent:main:main",
518
+ now: () => 60_000,
519
+ },
520
+ runtime,
521
+ );
522
+
523
+ mirror.handleNotification({
524
+ method: "item/completed",
525
+ params: {
526
+ item: {
527
+ type: "collabAgentToolCall",
528
+ tool: "spawnAgent",
529
+ senderThreadId: "parent-thread",
530
+ receiverThreadIds: ["child-thread"],
531
+ agentsStates: {
532
+ "child-thread": {
533
+ status: "pending_init",
534
+ message: null,
535
+ },
536
+ },
537
+ },
538
+ },
539
+ });
540
+ mirror.handleNotification({
541
+ method: "item/completed",
542
+ params: {
543
+ item: {
544
+ type: "collabAgentToolCall",
545
+ tool: "wait",
546
+ senderThreadId: "parent-thread",
547
+ agentsStates: {
548
+ "child-thread": {
549
+ status: "success",
550
+ message: "done",
551
+ },
552
+ },
553
+ },
554
+ },
555
+ });
556
+
557
+ expect(runtime.recordTaskRunProgressByRunId).toHaveBeenCalledWith({
558
+ runId: "codex-thread:child-thread",
559
+ runtime: "subagent",
560
+ lastEventAt: 60_000,
561
+ progressSummary: "Codex native subagent is initializing.",
562
+ });
563
+ expect(runtime.finalizeTaskRunByRunId).toHaveBeenCalledWith({
564
+ runId: "codex-thread:child-thread",
565
+ runtime: "subagent",
566
+ status: "succeeded",
567
+ endedAt: 60_000,
568
+ lastEventAt: 60_000,
569
+ progressSummary: "done",
570
+ terminalSummary: "done",
571
+ });
572
+ });
573
+ });