@astrosheep/keiyaku 2.8.4 → 2.9.0

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 (229) hide show
  1. package/README.md +35 -15
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/{call-terms_v2.js → call-terms.js} +12 -8
  4. package/build/agents/harness/control-types.js +1 -0
  5. package/build/agents/harness/execution-handle.js +23 -3
  6. package/build/agents/harness/index.js +6 -607
  7. package/build/agents/harness/outcome.js +141 -0
  8. package/build/agents/harness/projection.js +310 -0
  9. package/build/agents/harness/router.js +201 -0
  10. package/build/agents/harness/runtime.js +2 -1
  11. package/build/agents/{launch-snapshot_v2.js → launch-snapshot/model.js} +69 -74
  12. package/build/agents/launch-snapshot/resolve.js +72 -0
  13. package/build/agents/opencode-sdk.js +24 -3
  14. package/build/agents/providers/{claude-agent-sdk.js → claude-agent-sdk/adapter.js} +46 -368
  15. package/build/agents/providers/claude-agent-sdk/events.js +218 -0
  16. package/build/agents/providers/claude-agent-sdk/session.js +158 -0
  17. package/build/agents/providers/codex-app-server/adapter.js +347 -0
  18. package/build/agents/providers/codex-app-server/events.js +352 -0
  19. package/build/agents/providers/codex-app-server/session.js +266 -0
  20. package/build/agents/providers/codex-sdk.js +23 -5
  21. package/build/agents/providers/opencode-sdk/adapter.js +344 -0
  22. package/build/agents/providers/opencode-sdk/events.js +354 -0
  23. package/build/agents/providers/opencode-sdk/session.js +180 -0
  24. package/build/agents/providers/pi/adapter.js +269 -0
  25. package/build/agents/providers/pi/checkpoint.js +41 -0
  26. package/build/agents/providers/pi/events.js +282 -0
  27. package/build/agents/{selector_v2.js → selector.js} +8 -6
  28. package/build/cli/commands/akuma/akuma/handler.js +4 -0
  29. package/build/cli/commands/akuma/akuma/meta.js +16 -0
  30. package/build/cli/commands/akuma/catalog.js +8 -0
  31. package/build/cli/commands/akuma/list/handler.js +15 -0
  32. package/build/cli/commands/akuma/list/meta-ls.js +9 -0
  33. package/build/cli/commands/akuma/view/handler.js +34 -0
  34. package/build/cli/commands/akuma/view/meta.js +8 -0
  35. package/build/cli/commands/akuma.js +1 -1
  36. package/build/cli/commands/contract/amend/handler.js +10 -0
  37. package/build/cli/commands/contract/amend/meta.js +12 -0
  38. package/build/cli/commands/contract/arc/handler.js +9 -0
  39. package/build/cli/commands/contract/arc/meta.js +26 -0
  40. package/build/cli/commands/contract/bind/handler.js +23 -0
  41. package/build/cli/commands/contract/bind/meta.js +30 -0
  42. package/build/cli/commands/contract/catalog.js +16 -0
  43. package/build/cli/commands/contract/forfeit/handler.js +13 -0
  44. package/build/cli/commands/contract/forfeit/meta.js +12 -0
  45. package/build/cli/commands/contract/log/handler.js +12 -0
  46. package/build/cli/commands/contract/log/meta.js +12 -0
  47. package/build/cli/commands/contract/petition/handler.js +26 -0
  48. package/build/cli/commands/contract/petition/meta.js +23 -0
  49. package/build/cli/commands/contract/renew/handler.js +10 -0
  50. package/build/cli/commands/contract/renew/meta.js +17 -0
  51. package/build/cli/commands/projection/call/handler.js +39 -0
  52. package/build/cli/commands/projection/call/meta.js +9 -0
  53. package/build/cli/commands/projection/catalog.js +14 -0
  54. package/build/cli/commands/projection/kill/handler.js +35 -0
  55. package/build/cli/commands/projection/kill/meta.js +11 -0
  56. package/build/cli/commands/projection/revive/handler.js +53 -0
  57. package/build/cli/commands/projection/revive/meta.js +9 -0
  58. package/build/cli/commands/projection/status/handler.js +23 -0
  59. package/build/cli/commands/projection/status/meta.js +9 -0
  60. package/build/cli/commands/projection/tell/handler.js +34 -0
  61. package/build/cli/commands/projection/tell/meta.js +9 -0
  62. package/build/cli/commands/projection/wait/handler.js +21 -0
  63. package/build/cli/commands/projection/wait/meta.js +11 -0
  64. package/build/cli/commands/registry.js +100 -0
  65. package/build/cli/commands/shared.js +71 -0
  66. package/build/cli/commands/skills/catalog.js +6 -0
  67. package/build/cli/commands/skills/install/handler.js +10 -0
  68. package/build/cli/commands/skills/install/meta.js +9 -0
  69. package/build/cli/commands/skills/skills/handler.js +4 -0
  70. package/build/cli/commands/skills/skills/meta.js +10 -0
  71. package/build/cli/commands/system/catalog.js +8 -0
  72. package/build/cli/commands/system/completion/handler.js +10 -0
  73. package/build/cli/commands/system/completion/meta.js +9 -0
  74. package/build/cli/commands/system/dump-env/handler.js +3 -0
  75. package/build/cli/commands/system/dump-env/meta.js +10 -0
  76. package/build/cli/commands/system/guide/handler.js +4 -0
  77. package/build/cli/commands/system/guide/meta.js +9 -0
  78. package/build/cli/commands/task/add/handler.js +21 -0
  79. package/build/cli/commands/task/add/meta.js +8 -0
  80. package/build/cli/commands/task/catalog.js +22 -0
  81. package/build/cli/commands/task/doctor/handler.js +4 -0
  82. package/build/cli/commands/task/doctor/meta.js +8 -0
  83. package/build/cli/commands/task/done/handler.js +17 -0
  84. package/build/cli/commands/task/done/meta.js +8 -0
  85. package/build/cli/commands/task/drop/handler.js +6 -0
  86. package/build/cli/commands/task/drop/meta.js +8 -0
  87. package/build/cli/commands/task/ls/handler.js +13 -0
  88. package/build/cli/commands/task/ls/meta.js +8 -0
  89. package/build/cli/commands/task/shared.js +131 -0
  90. package/build/cli/commands/task/start/handler.js +6 -0
  91. package/build/cli/commands/task/start/meta.js +8 -0
  92. package/build/cli/commands/task/stop/handler.js +17 -0
  93. package/build/cli/commands/task/stop/meta.js +8 -0
  94. package/build/cli/commands/task/task/handler.js +5 -0
  95. package/build/cli/commands/task/task/meta.js +11 -0
  96. package/build/cli/commands/task/update/handler.js +18 -0
  97. package/build/cli/commands/task/update/meta.js +8 -0
  98. package/build/cli/commands/task/view/handler.js +14 -0
  99. package/build/cli/commands/task/view/meta.js +8 -0
  100. package/build/cli/completion.js +26 -27
  101. package/build/cli/flags.js +79 -2
  102. package/build/cli/help.js +27 -49
  103. package/build/cli/index.js +121 -374
  104. package/build/cli/output-stream-error-policy.js +17 -0
  105. package/build/cli/parse.js +159 -72
  106. package/build/cli/projection-address.js +5 -0
  107. package/build/cli/render/arc.js +1 -1
  108. package/build/cli/render/errors.js +4 -3
  109. package/build/cli/render/format.js +14 -0
  110. package/build/cli/render/kanshi.js +106 -0
  111. package/build/cli/render/petition.js +24 -1
  112. package/build/cli/render/projection-activity.js +5 -1
  113. package/build/cli/render/shared.js +2 -2
  114. package/build/cli/render/status.js +79 -13
  115. package/build/cli/render/task.js +113 -0
  116. package/build/cli/render/wait.js +23 -3
  117. package/build/cli/types.js +1 -1
  118. package/build/config/{akuma-loader_v2.js → akuma-loader.js} +2 -2
  119. package/build/config/env.js +9 -4
  120. package/build/config/settings/disease.js +26 -0
  121. package/build/config/settings/knobs.js +8 -0
  122. package/build/config/settings/loader.js +244 -0
  123. package/build/config/settings/schema.js +276 -0
  124. package/build/core/addressing.js +1 -1
  125. package/build/core/amend.js +3 -3
  126. package/build/core/arc.js +5 -4
  127. package/build/core/bind-workspace.js +123 -0
  128. package/build/core/bind.js +75 -155
  129. package/build/core/call/call.js +224 -0
  130. package/build/core/call/context.js +238 -0
  131. package/build/core/call/execution.js +217 -0
  132. package/build/core/call/prompt.js +42 -0
  133. package/build/core/call/reference.js +24 -0
  134. package/build/core/call-persist.js +1 -1
  135. package/build/core/claim-delivery.js +247 -0
  136. package/build/core/claim.js +39 -277
  137. package/build/core/context.js +2 -1
  138. package/build/core/{render.js → contract-view.js} +12 -140
  139. package/build/core/draft.js +107 -9
  140. package/build/core/entry.js +9 -1
  141. package/build/core/execution-coordinate.js +4 -20
  142. package/build/core/execution-pact.js +16 -31
  143. package/build/core/forfeit.js +35 -22
  144. package/build/core/hints.js +2 -2
  145. package/build/core/log.js +2 -2
  146. package/build/core/petition-claim-gates.js +8 -6
  147. package/build/core/petition-preview.js +97 -0
  148. package/build/core/petition-run.js +3 -2
  149. package/build/core/petition.js +39 -26
  150. package/build/core/projection/generation/database.js +306 -0
  151. package/build/core/projection/generation/identity.js +84 -0
  152. package/build/core/projection/generation/model.js +201 -0
  153. package/build/core/projection/generation/store.js +73 -0
  154. package/build/core/projection/generation/transitions.js +258 -0
  155. package/build/core/projection/heart.js +0 -307
  156. package/build/core/projection/tell/database.js +109 -0
  157. package/build/core/projection/tell/model.js +97 -0
  158. package/build/core/{projection-tells.js → projection/tell/store.js} +3 -158
  159. package/build/core/projection-activity.js +23 -2
  160. package/build/core/projection-core.js +2 -1
  161. package/build/core/projection-execution-observer.js +259 -0
  162. package/build/core/projection-generation-continuation.js +1 -1
  163. package/build/core/projection-generation-execution.js +90 -20
  164. package/build/core/projection-generation-launcher.js +1 -1
  165. package/build/core/projection-generation-runner.js +198 -4
  166. package/build/core/projection-identity.js +1 -1
  167. package/build/core/projection-kill.js +50 -17
  168. package/build/core/projection-life-observer.js +1 -1
  169. package/build/core/projection-life-protocol.js +10 -0
  170. package/build/core/projection-mint.js +1 -1
  171. package/build/core/projection-status.js +45 -32
  172. package/build/core/projection-wait.js +12 -2
  173. package/build/core/projection-wake.js +5 -2
  174. package/build/core/{queue_v2.js → queue.js} +2 -1
  175. package/build/core/registry.js +1 -1
  176. package/build/core/renew.js +7 -7
  177. package/build/core/response-artifact-catalog-sqlite.js +112 -0
  178. package/build/core/response-artifact-catalog.js +7 -0
  179. package/build/core/seal.js +44 -16
  180. package/build/core/status/board.js +286 -0
  181. package/build/core/status/drift.js +99 -0
  182. package/build/core/status/lifecycle.js +84 -0
  183. package/build/core/status/reconciliation.js +131 -0
  184. package/build/core/stored-agent-event.js +28 -4
  185. package/build/core/target-ref.js +10 -0
  186. package/build/core/task/board.js +175 -0
  187. package/build/core/task/commands.js +211 -0
  188. package/build/core/task/document.js +189 -0
  189. package/build/core/task/model.js +1 -0
  190. package/build/core/task/settlement-git.js +207 -0
  191. package/build/core/task/settlement-policy.js +82 -0
  192. package/build/core/task/source-board.js +84 -0
  193. package/build/core/task/source-model.js +1 -0
  194. package/build/core/task/validation.js +44 -0
  195. package/build/core/task-contract.js +224 -0
  196. package/build/core/task-doctor.js +14 -0
  197. package/build/core/task-git-actor.js +38 -0
  198. package/build/core/task-git-runtime.js +34 -0
  199. package/build/core/task-git-store.js +375 -0
  200. package/build/core/task-repository.js +1 -0
  201. package/build/core/task-store-repository.js +86 -0
  202. package/build/core/task-store.js +1 -0
  203. package/build/core/task-worktree.js +51 -0
  204. package/build/core/task.js +4 -0
  205. package/build/core/transcripts.js +138 -56
  206. package/build/core/validation.js +17 -0
  207. package/build/core/verdict.js +2 -2
  208. package/build/core/worktree-path.js +24 -6
  209. package/build/flow-error.js +3 -2
  210. package/build/generated/version.js +1 -1
  211. package/build/git/commits.js +3 -0
  212. package/build/git/diff/parsers.js +0 -94
  213. package/build/index.js +2 -0
  214. package/build/keiyaku-constants.js +2 -0
  215. package/build/keiyaku.js +4 -3
  216. package/package.json +3 -2
  217. package/skills/keiyaku/SKILL.md +1 -1
  218. package/skills/keiyaku-akuma/SKILL.md +11 -6
  219. package/build/agents/harness/pump.js +0 -158
  220. package/build/agents/providers/codex-app-server.js +0 -864
  221. package/build/agents/providers/opencode-sdk.js +0 -363
  222. package/build/agents/providers/pi.js +0 -557
  223. package/build/config/settings.js +0 -533
  224. package/build/core/call.js +0 -686
  225. package/build/core/projection/generation.js +0 -412
  226. package/build/core/projection/identity.js +0 -81
  227. package/build/core/projection/mint.js +0 -174
  228. package/build/core/projection-generation-store.js +0 -707
  229. package/build/core/status.js +0 -539
@@ -1,864 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { z } from "zod";
3
- import { appendDebugLog } from "../../telemetry/debug-log.js";
4
- import { SUBAGENT_SESSION_VERSION } from "../session.js";
5
- import { createProjectionDriver } from "../harness/execution-handle.js";
6
- import { boundActivityExcerpt, diffstatFromUnifiedPatch } from "../harness/activity-values.js";
7
- import { AGENT_PLAN_ITEM_TEXT_MAX_CHARS, AGENT_PLAN_MAX_ITEMS, AGENT_THOUGHT_TEXT_MAX_CHARS, } from "../harness/events.js";
8
- import { buildSubagentEnv, createAbortError, emitProviderEvidence, sha256Utf8 } from "../harness/runtime.js";
9
- import { coerceString } from "../harness/runtime.js";
10
- /** Identity-only opacity boundary for codex-app-server turn/start effort. */
11
- function toCodexAppServerEffort(effort) {
12
- return effort;
13
- }
14
- const CODEX_APP_SERVER_NOTIFICATION_ACKNOWLEDGED = new Set(["item/agentMessage/delta"]);
15
- const CODEX_APP_SERVER_NOTIFICATION_MAPPED = new Set([
16
- "item/completed",
17
- "turn/started",
18
- "turn/completed",
19
- "error",
20
- ]);
21
- function codexAppServerNotificationDisposition(method) {
22
- if (CODEX_APP_SERVER_NOTIFICATION_ACKNOWLEDGED.has(method))
23
- return "acknowledged-unpersisted";
24
- if (CODEX_APP_SERVER_NOTIFICATION_MAPPED.has(method))
25
- return "mapped";
26
- return "unknown";
27
- }
28
- /** Session handle for codex-app-server: thread id plus optional CODEX_HOME for cross-process resume. */
29
- export const codexAppServerSessionSchema = z.object({
30
- threadId: z.string().trim().min(1),
31
- codexHome: z.string().trim().min(1).optional(),
32
- });
33
- function appServerRecord(value) {
34
- return value !== null && typeof value === "object" && !Array.isArray(value)
35
- ? value
36
- : undefined;
37
- }
38
- const CODEX_APP_SERVER_ACTIVITY_ITEM_TYPES = [
39
- "commandExecution",
40
- "fileChange",
41
- "webSearch",
42
- "mcpToolCall",
43
- ];
44
- function isCodexAppServerActivityItemType(value) {
45
- return CODEX_APP_SERVER_ACTIVITY_ITEM_TYPES.includes(value);
46
- }
47
- function appServerActivityCall(itemType, item) {
48
- switch (itemType) {
49
- case "commandExecution":
50
- return typeof item.command === "string"
51
- ? {
52
- kind: "run",
53
- command: item.command,
54
- ...(typeof item.description === "string" && item.description ? { description: item.description } : {}),
55
- }
56
- : { kind: "other", display: itemType };
57
- case "fileChange": {
58
- const changes = Array.isArray(item.changes)
59
- ? item.changes.flatMap((entry) => {
60
- const change = appServerRecord(entry);
61
- if (!change || typeof change.path !== "string")
62
- return [];
63
- if (change.kind !== "add" && change.kind !== "update" && change.kind !== "delete")
64
- return [];
65
- const op = change.kind;
66
- return [{ op, path: change.path }];
67
- })
68
- : [];
69
- if (changes.length > 0)
70
- return { kind: "fileChange", changes };
71
- return { kind: "other", display: typeof item.path === "string" ? `fileChange ${item.path}` : itemType };
72
- }
73
- case "webSearch":
74
- return typeof item.query === "string" ? { kind: "search", query: item.query } : { kind: "other", display: itemType };
75
- case "mcpToolCall":
76
- return typeof item.server === "string" && typeof item.tool === "string"
77
- ? { kind: "other", display: `${item.server}.${item.tool}` }
78
- : { kind: "other", display: itemType };
79
- }
80
- }
81
- function appServerActivityResult(item) {
82
- const exitCode = typeof item.exitCode === "number" && Number.isFinite(item.exitCode) ? item.exitCode : undefined;
83
- const message = typeof item.aggregatedOutput === "string" && item.aggregatedOutput
84
- ? boundActivityExcerpt(item.aggregatedOutput)
85
- : typeof item.output === "string" && item.output
86
- ? boundActivityExcerpt(item.output)
87
- : typeof item.error === "string" && item.error
88
- ? boundActivityExcerpt(item.error)
89
- : undefined;
90
- const details = item.details !== null && typeof item.details === "object" && !Array.isArray(item.details)
91
- ? item.details
92
- : undefined;
93
- const patch = typeof details?.patch === "string" ? details.patch : undefined;
94
- const diffstat = patch ? diffstatFromUnifiedPatch(patch) : undefined;
95
- return {
96
- status: item.status === "failed" || (exitCode !== undefined && exitCode !== 0) ? "error" : "ok",
97
- ...(message ? { message } : {}),
98
- ...(exitCode === undefined ? {} : { exitCode }),
99
- ...(diffstat ? { diffstat } : {}),
100
- };
101
- }
102
- const CODEX_APP_SERVER_PROVIDER = "codex-app-server";
103
- const MINIMUM_CODEX_APP_SERVER_VERSION = { major: 0, minor: 143, patch: 0 };
104
- /** Non-interactive defaults for turn/start (faye #316 policy A). */
105
- export const CODEX_APP_SERVER_TURN_APPROVAL_POLICY = "never";
106
- export function buildCodexAppServerWorkspaceWriteSandboxPolicy(input) {
107
- return {
108
- type: "workspaceWrite",
109
- writableRoots: [...input.writableRoots],
110
- networkAccess: input.networkAccess,
111
- excludeTmpdirEnvVar: false,
112
- excludeSlashTmp: false,
113
- };
114
- }
115
- /** True when config.* already owns approval policy — omit turn-level approvalPolicy default. */
116
- export function configOwnsApprovalPolicy(config) {
117
- if (!config)
118
- return false;
119
- return Object.prototype.hasOwnProperty.call(config, "approval_policy");
120
- }
121
- /**
122
- * True when config.* already owns sandbox settings — omit turn-level sandboxPolicy default.
123
- * Matches `sandbox_mode` or any `sandbox_workspace_write` / `sandbox_workspace_write.*` key.
124
- */
125
- export function configOwnsSandboxPolicy(config) {
126
- if (!config)
127
- return false;
128
- for (const key of Object.keys(config)) {
129
- if (key === "sandbox_mode" || key === "sandbox_workspace_write" || key.startsWith("sandbox_workspace_write.")) {
130
- return true;
131
- }
132
- }
133
- return false;
134
- }
135
- function extractReasoningSummary(item) {
136
- const summary = item.summary;
137
- const text = typeof summary === "string"
138
- ? summary
139
- : Array.isArray(summary)
140
- ? summary.map((part) => {
141
- if (typeof part === "string")
142
- return part;
143
- if (!part || typeof part !== "object")
144
- return "";
145
- const record = part;
146
- if (record.type !== "summary_text" && record.type !== "text")
147
- return "";
148
- return typeof record.text === "string" ? record.text : "";
149
- }).join("\n")
150
- : "";
151
- return text.trim().length > 0 ? text : undefined;
152
- }
153
- function extractPlanItems(item) {
154
- if (!Array.isArray(item.items))
155
- return [];
156
- return item.items.slice(0, AGENT_PLAN_MAX_ITEMS).flatMap((value) => {
157
- if (!value || typeof value !== "object")
158
- return [];
159
- const record = value;
160
- if (typeof record.text !== "string" || record.text.trim().length === 0)
161
- return [];
162
- return [{
163
- text: record.text.slice(0, AGENT_PLAN_ITEM_TEXT_MAX_CHARS),
164
- done: record.completed === true || record.done === true,
165
- ...(record.active === true ? { active: true } : {}),
166
- }];
167
- });
168
- }
169
- function parseSemver(version) {
170
- const match = version.trim().match(/(\d+)\.(\d+)\.(\d+)/);
171
- if (!match)
172
- return undefined;
173
- return {
174
- major: Number(match[1]),
175
- minor: Number(match[2]),
176
- patch: Number(match[3]),
177
- };
178
- }
179
- export function isCodexAppServerVersionBelowMinimum(version) {
180
- if (!version)
181
- return false;
182
- const parsed = parseSemver(version);
183
- if (!parsed)
184
- return false;
185
- if (parsed.major !== MINIMUM_CODEX_APP_SERVER_VERSION.major) {
186
- return parsed.major < MINIMUM_CODEX_APP_SERVER_VERSION.major;
187
- }
188
- if (parsed.minor !== MINIMUM_CODEX_APP_SERVER_VERSION.minor) {
189
- return parsed.minor < MINIMUM_CODEX_APP_SERVER_VERSION.minor;
190
- }
191
- return parsed.patch < MINIMUM_CODEX_APP_SERVER_VERSION.patch;
192
- }
193
- function extractVersionFromInitialize(result) {
194
- if (!result || typeof result !== "object")
195
- return undefined;
196
- const record = result;
197
- const userAgent = coerceString(record.userAgent);
198
- if (userAgent) {
199
- const match = userAgent.match(/(\d+\.\d+\.\d+)/);
200
- if (match)
201
- return match[1];
202
- }
203
- return coerceString(record.version) ?? coerceString(record.cliVersion);
204
- }
205
- class CodexAppServerProcess {
206
- onEvidence;
207
- onEvidenceDiagnostic;
208
- child;
209
- pending = new Map();
210
- notificationListeners = new Set();
211
- serverRequestListeners = new Set();
212
- exitPromise;
213
- nextId = 1;
214
- stdoutBuffer = "";
215
- closed = false;
216
- exitSettled = false;
217
- codexHome;
218
- constructor(executable, env, codexHome, onEvidence, onEvidenceDiagnostic) {
219
- this.onEvidence = onEvidence;
220
- this.onEvidenceDiagnostic = onEvidenceDiagnostic;
221
- this.codexHome = codexHome;
222
- const spawnEnv = {
223
- ...env,
224
- ...(codexHome ? { CODEX_HOME: codexHome } : {}),
225
- };
226
- this.child = spawn(executable, ["app-server", "--listen", "stdio://"], {
227
- env: spawnEnv,
228
- stdio: ["pipe", "pipe", "pipe"],
229
- });
230
- if (this.onEvidence && this.child.pid)
231
- emitProviderEvidence(this.onEvidence, { type: "process", provider: CODEX_APP_SERVER_PROVIDER, event: "spawn", pid: this.child.pid }, this.onEvidenceDiagnostic);
232
- this.child.stdout.setEncoding("utf8");
233
- this.child.stderr.setEncoding("utf8");
234
- this.child.stdout.on("data", (chunk) => this.onStdout(chunk));
235
- // Preserve the pre-evidence diagnostic stream exactly. Process evidence
236
- // never retains stderr bytes, avoiding credential boundary ambiguity.
237
- this.child.stderr.on("data", (chunk) => this.onEvidenceDiagnostic?.(chunk));
238
- this.child.on("error", (error) => this.failAll(error));
239
- this.exitPromise = new Promise((resolve) => {
240
- this.child.once("close", (exitCode) => {
241
- this.exitSettled = true;
242
- this.closed = true;
243
- // With no observer, retain the historical error bytes. Evidence adds
244
- // structured exit facts without changing provider behavior.
245
- this.failAll(new Error(`${CODEX_APP_SERVER_PROVIDER} process closed`));
246
- if (this.onEvidence)
247
- emitProviderEvidence(this.onEvidence, { type: "process", provider: CODEX_APP_SERVER_PROVIDER, event: "exit", exitCode, stderrTail: "" }, this.onEvidenceDiagnostic);
248
- resolve();
249
- });
250
- });
251
- }
252
- onNotification(listener) {
253
- this.notificationListeners.add(listener);
254
- return () => this.notificationListeners.delete(listener);
255
- }
256
- onServerRequest(listener) {
257
- this.serverRequestListeners.add(listener);
258
- return () => this.serverRequestListeners.delete(listener);
259
- }
260
- onStdout(chunk) {
261
- this.stdoutBuffer += chunk;
262
- let newline = this.stdoutBuffer.indexOf("\n");
263
- while (newline >= 0) {
264
- const line = this.stdoutBuffer.slice(0, newline).trim();
265
- this.stdoutBuffer = this.stdoutBuffer.slice(newline + 1);
266
- if (line.length > 0) {
267
- this.handleLine(line);
268
- }
269
- newline = this.stdoutBuffer.indexOf("\n");
270
- }
271
- }
272
- handleLine(line) {
273
- let message;
274
- try {
275
- message = JSON.parse(line);
276
- }
277
- catch {
278
- return;
279
- }
280
- if (!message || typeof message !== "object")
281
- return;
282
- const record = message;
283
- if ("id" in record && ("result" in record || "error" in record)) {
284
- const id = record.id;
285
- const pending = this.pending.get(id);
286
- if (!pending)
287
- return;
288
- this.pending.delete(id);
289
- if ("error" in record && record.error) {
290
- const error = record.error;
291
- pending.reject(new Error(error.message ?? `${CODEX_APP_SERVER_PROVIDER} RPC error`));
292
- return;
293
- }
294
- pending.resolve(record.result);
295
- return;
296
- }
297
- // Server→client request: method + id. Never ignore, auto-approve, or auto-deny (faye #316).
298
- if (typeof record.method === "string" && "id" in record) {
299
- const request = {
300
- id: record.id,
301
- method: record.method,
302
- params: record.params ?? undefined,
303
- };
304
- for (const listener of this.serverRequestListeners) {
305
- listener(request);
306
- }
307
- return;
308
- }
309
- if (typeof record.method === "string" && !("id" in record)) {
310
- const notification = {
311
- method: record.method,
312
- params: record.params ?? undefined,
313
- };
314
- for (const listener of this.notificationListeners) {
315
- listener(notification);
316
- }
317
- }
318
- }
319
- failAll(error) {
320
- for (const pending of this.pending.values()) {
321
- pending.reject(error);
322
- }
323
- this.pending.clear();
324
- }
325
- request(method, params) {
326
- if (this.closed) {
327
- return Promise.reject(new Error(`${CODEX_APP_SERVER_PROVIDER} process is closed`));
328
- }
329
- const id = this.nextId++;
330
- const payload = params === undefined ? { id, method } : { id, method, params };
331
- const serialized = `${JSON.stringify(payload)}\n`;
332
- return new Promise((resolve, reject) => {
333
- this.pending.set(id, { resolve, reject });
334
- this.child.stdin.write(serialized, (error) => {
335
- if (error) {
336
- this.pending.delete(id);
337
- reject(error);
338
- }
339
- });
340
- // The write call above is the process boundary. Emitting afterward
341
- // avoids evidence for a synchronous write failure while keeping the
342
- // outbound fact before any asynchronous RPC acknowledgement.
343
- this.emitOutbound(method, params);
344
- });
345
- }
346
- notify(method, params) {
347
- const payload = params === undefined ? { method } : { method, params };
348
- this.child.stdin.write(`${JSON.stringify(payload)}\n`);
349
- }
350
- emitOutbound(method, params) {
351
- if (!this.onEvidence)
352
- return;
353
- const kind = method === "thread/start" ? "thread-start" : method === "thread/resume" ? "thread-resume" : method === "turn/start" ? "turn-start" : method === "turn/steer" ? "steer" : undefined;
354
- if (!kind)
355
- return;
356
- const text = method === "turn/steer"
357
- ? coerceString((params?.input?.[0]?.text))
358
- : JSON.stringify(params ?? {});
359
- if (text === undefined)
360
- return;
361
- emitProviderEvidence(this.onEvidence, { type: "outbound", provider: CODEX_APP_SERVER_PROVIDER, direction: "request", kind, ...sha256Utf8(text) }, this.onEvidenceDiagnostic);
362
- const developerInstructions = coerceString(params?.developerInstructions);
363
- if ((method === "thread/start" || method === "thread/resume") && developerInstructions !== undefined) {
364
- emitProviderEvidence(this.onEvidence, { type: "outbound", provider: CODEX_APP_SERVER_PROVIDER, direction: "request", kind: "system-prompt", ...sha256Utf8(developerInstructions) }, this.onEvidenceDiagnostic);
365
- }
366
- }
367
- async close() {
368
- if (this.exitSettled) {
369
- this.closed = true;
370
- return;
371
- }
372
- this.closed = true;
373
- this.failAll(new Error(`${CODEX_APP_SERVER_PROVIDER} process closed`));
374
- try {
375
- this.child.stdin.end();
376
- }
377
- catch {
378
- // ignore
379
- }
380
- // Destroy stdio so a hung child cannot keep the parent event loop alive via open pipes.
381
- try {
382
- this.child.stdout.destroy();
383
- }
384
- catch {
385
- // ignore
386
- }
387
- try {
388
- this.child.stderr.destroy();
389
- }
390
- catch {
391
- // ignore
392
- }
393
- if (!this.exitSettled && this.child.exitCode === null && this.child.signalCode === null) {
394
- try {
395
- this.child.kill("SIGTERM");
396
- }
397
- catch {
398
- // ignore
399
- }
400
- }
401
- // Escalate to SIGKILL if the child ignores stdin EOF + SIGTERM, then wait for exit
402
- // (or give up after a hard bound so close() itself cannot pin the event loop).
403
- const forceKill = new Promise((resolve) => {
404
- const timer = setTimeout(() => {
405
- if (!this.exitSettled) {
406
- try {
407
- this.child.kill("SIGKILL");
408
- }
409
- catch {
410
- // ignore
411
- }
412
- }
413
- resolve();
414
- }, 1000);
415
- timer.unref?.();
416
- });
417
- const hardBound = new Promise((resolve) => {
418
- const timer = setTimeout(resolve, 2000);
419
- timer.unref?.();
420
- });
421
- await Promise.race([this.exitPromise, Promise.all([forceKill, this.exitPromise]).then(() => undefined), hardBound]);
422
- }
423
- }
424
- /** Gate-facing production entry: the harness owns clocks, capture, classification, and outcome construction. */
425
- export async function startCodexAppServer(prompt, cwd, options = {}) {
426
- const executable = options.executable?.trim() || "codex";
427
- const resumeThreadId = options.threadId?.trim();
428
- let server;
429
- let activeTurnId;
430
- const responseSeenTurnIds = new Set();
431
- const completedBeforeResponseTurnIds = new Set();
432
- let activeTurnSource;
433
- let activeTurnAcceptingSteer = false;
434
- let activeThreadId;
435
- let interruptRequested = false;
436
- let lastAgentMessage = "";
437
- let builderDiagnostic;
438
- let emitTurnStarted;
439
- const pendingTells = [];
440
- let drainingTells = false;
441
- let failActiveTurn;
442
- let finishAfterTellDrain;
443
- const rejectPendingTells = (error) => {
444
- for (const pending of pendingTells.splice(0))
445
- pending.reject(error);
446
- };
447
- const recordActiveTurnId = (candidate, source) => {
448
- const turnId = coerceString(candidate);
449
- if (!turnId)
450
- throw new Error(`${CODEX_APP_SERVER_PROVIDER} ${source} omitted active turn id`);
451
- if (!activeTurnId) {
452
- activeTurnId = turnId;
453
- activeTurnSource = source;
454
- return turnId;
455
- }
456
- if (activeTurnId !== turnId) {
457
- throw new Error(`${CODEX_APP_SERVER_PROVIDER} active turn id mismatch: ${activeTurnSource ?? "first"}=${activeTurnId}, ${source}=${turnId}`);
458
- }
459
- return activeTurnId;
460
- };
461
- const recordTurnStartResponse = (candidate) => {
462
- const turnId = coerceString(candidate);
463
- if (!turnId)
464
- throw new Error(`${CODEX_APP_SERVER_PROVIDER} response omitted active turn id`);
465
- if (completedBeforeResponseTurnIds.delete(turnId))
466
- return turnId;
467
- responseSeenTurnIds.add(turnId);
468
- return recordActiveTurnId(turnId, "response");
469
- };
470
- appendDebugLog(`startCodexAppServer: executable=${executable} model=${options.model ?? "default"} mode=${resumeThreadId ? "resume" : "fresh"}`, { cwd, section: CODEX_APP_SERVER_PROVIDER });
471
- const submitSteer = async (tell, turnId) => {
472
- // turn/steer response {turnId} = enqueue acceptance (submitted), not consumption.
473
- // clientUserMessageId = tell.id for later delivered proof via userMessage item clientId.
474
- // tell.text is already core-rendered; send byte-for-byte — never parse or rewrite.
475
- const ack = await server.request("turn/steer", {
476
- threadId: activeThreadId,
477
- expectedTurnId: turnId,
478
- clientUserMessageId: tell.id,
479
- input: [{ type: "text", text: tell.text }],
480
- });
481
- const acknowledgedTurnId = coerceString(ack.turnId) ?? turnId;
482
- if (acknowledgedTurnId !== turnId) {
483
- throw new Error(`${CODEX_APP_SERVER_PROVIDER} turn/steer acknowledgement mismatched active turn: expected=${turnId}, actual=${acknowledgedTurnId}`);
484
- }
485
- // Accepted same-native-turn steer opens the next public conversation turn.
486
- emitTurnStarted?.();
487
- if (options.onEvidence) {
488
- emitProviderEvidence(options.onEvidence, {
489
- type: "ack",
490
- provider: CODEX_APP_SERVER_PROVIDER,
491
- fence: { kind: "turn", turnId: acknowledgedTurnId },
492
- outbound: "steer",
493
- }, builderDiagnostic);
494
- }
495
- return { fence: { kind: "turn", turnId: acknowledgedTurnId } };
496
- };
497
- const startTellTurn = async (tell) => {
498
- const started = await server.request("turn/start", {
499
- threadId: activeThreadId,
500
- input: [{ type: "text", text: tell.text }],
501
- ...(options.model ? { model: options.model } : {}),
502
- ...(tell.effort ? { effort: toCodexAppServerEffort(tell.effort) } : {}),
503
- ...(!configOwnsApprovalPolicy(options.config) ? { approvalPolicy: options.approvalPolicy ?? CODEX_APP_SERVER_TURN_APPROVAL_POLICY } : {}),
504
- ...(!configOwnsSandboxPolicy(options.config)
505
- ? {
506
- sandboxPolicy: options.sandboxPolicy ?? buildCodexAppServerWorkspaceWriteSandboxPolicy({
507
- writableRoots: [cwd],
508
- networkAccess: false,
509
- }),
510
- }
511
- : {}),
512
- });
513
- const startedTurn = started && typeof started === "object"
514
- ? (started.turn ?? undefined)
515
- : undefined;
516
- const newTurnId = recordTurnStartResponse(startedTurn?.id);
517
- if (options.onEvidence)
518
- emitProviderEvidence(options.onEvidence, { type: "ack", provider: CODEX_APP_SERVER_PROVIDER, fence: { kind: "turn", turnId: newTurnId }, outbound: "run" }, builderDiagnostic);
519
- return { fence: { kind: "turn", turnId: newTurnId } };
520
- };
521
- const drainPendingTells = async () => {
522
- if (drainingTells)
523
- return;
524
- drainingTells = true;
525
- try {
526
- while (pendingTells.length > 0 && server && activeThreadId) {
527
- const pending = pendingTells[0];
528
- if (pending.tell.effort && activeTurnId)
529
- return;
530
- if (!pending.tell.effort && activeTurnId && !activeTurnAcceptingSteer)
531
- return;
532
- pendingTells.shift();
533
- try {
534
- const ack = activeTurnId
535
- ? await submitSteer(pending.tell, activeTurnId)
536
- : await startTellTurn(pending.tell);
537
- pending.resolve(ack);
538
- }
539
- catch (error) {
540
- pending.reject(error);
541
- failActiveTurn?.(error);
542
- return;
543
- }
544
- }
545
- }
546
- finally {
547
- drainingTells = false;
548
- const finish = finishAfterTellDrain;
549
- finishAfterTellDrain = undefined;
550
- finish?.();
551
- }
552
- };
553
- const enqueue = async (tell) => {
554
- if (!server || !activeThreadId) {
555
- throw new Error(`${CODEX_APP_SERVER_PROVIDER} cannot steer: no active turn`);
556
- }
557
- return await new Promise((resolve, reject) => {
558
- pendingTells.push({ tell, resolve, reject });
559
- void drainPendingTells();
560
- });
561
- };
562
- return createProjectionDriver({
563
- steerMode: "mid-execution",
564
- initialTurn: options.initialTurn,
565
- enqueue,
566
- onIngress: options.onIngress,
567
- async abort() {
568
- interruptRequested = true;
569
- if (server && activeThreadId && activeTurnId) {
570
- try {
571
- // RPC acknowledgement is NOT terminal proof; wait for turn/completed interrupted.
572
- await server.request("turn/interrupt", {
573
- threadId: activeThreadId,
574
- turnId: activeTurnId,
575
- });
576
- }
577
- catch {
578
- // idle interrupt may error; still rely on process teardown
579
- }
580
- }
581
- await server?.close();
582
- },
583
- async run(builder) {
584
- builderDiagnostic = (text) => builder.emit({ type: "diagnostic", text });
585
- emitTurnStarted = () => builder.emit({ type: "turn", phase: "started" });
586
- const env = buildSubagentEnv();
587
- server = new CodexAppServerProcess(executable, env, options.codexHome, options.onEvidence, (text) => builder.emit({ type: "diagnostic", text }));
588
- const turnCompleted = new Promise((resolve, reject) => {
589
- let unsubNotifications = () => { };
590
- let unsubServerRequest = () => { };
591
- let settled = false;
592
- const failTurn = (error) => {
593
- if (settled)
594
- return;
595
- settled = true;
596
- rejectPendingTells(error);
597
- const message = error instanceof Error ? error.message : String(error);
598
- builder.emit({ type: "diagnostic", text: message });
599
- builder.emit({ type: "fail", message });
600
- unsubNotifications();
601
- unsubServerRequest();
602
- reject(error);
603
- };
604
- const failServerRequest = (request) => {
605
- // Policy A forbids interactive server→client requests. Fail immediately with named diagnostic.
606
- const message = `${CODEX_APP_SERVER_PROVIDER} unexpected server request '${request.method}' (id=${String(request.id)}); non-interactive policy forbids server→client requests`;
607
- failTurn(new Error(message));
608
- };
609
- unsubServerRequest = server.onServerRequest((request) => {
610
- builder.ingress(CODEX_APP_SERVER_PROVIDER, request.method, request, "mapped", () => {
611
- failServerRequest(request);
612
- });
613
- });
614
- unsubNotifications = server.onNotification((notification) => {
615
- try {
616
- builder.ingress(CODEX_APP_SERVER_PROVIDER, notification.method, notification, codexAppServerNotificationDisposition(notification.method), () => {
617
- if (notification.method === "item/agentMessage/delta") {
618
- const delta = coerceString(notification.params?.delta);
619
- if (delta) {
620
- lastAgentMessage = `${lastAgentMessage}${delta}`;
621
- }
622
- }
623
- if (notification.method === "item/completed") {
624
- const item = notification.params?.item;
625
- if (!item) {
626
- throw new Error(`${CODEX_APP_SERVER_PROVIDER} item/completed lacked required item`);
627
- }
628
- const id = coerceString(item.id);
629
- const itemType = coerceString(item.type);
630
- if (!id || !itemType) {
631
- throw new Error(`${CODEX_APP_SERVER_PROVIDER} item/completed lacked required item id or type`);
632
- }
633
- if (itemType === "agentMessage") {
634
- const text = coerceString(item.text);
635
- if (text) {
636
- lastAgentMessage = text;
637
- builder.emit({ type: "message", text });
638
- }
639
- }
640
- else if (itemType === "reasoning") {
641
- const text = extractReasoningSummary(item);
642
- if (text) {
643
- const truncated = text.length > AGENT_THOUGHT_TEXT_MAX_CHARS;
644
- builder.emit({
645
- type: "thought",
646
- id,
647
- text: text.slice(0, AGENT_THOUGHT_TEXT_MAX_CHARS),
648
- final: true,
649
- ...(truncated ? { truncated: true } : {}),
650
- });
651
- }
652
- }
653
- else if (itemType === "todo_list" || itemType === "todoList") {
654
- builder.emit({
655
- type: "plan",
656
- id,
657
- items: extractPlanItems(item),
658
- });
659
- }
660
- else if (isCodexAppServerActivityItemType(itemType)) {
661
- builder.emit({
662
- type: "activity",
663
- phase: "completed",
664
- id,
665
- tool: itemType,
666
- call: appServerActivityCall(itemType, item),
667
- result: appServerActivityResult(item),
668
- });
669
- }
670
- }
671
- if (notification.method === "turn/started") {
672
- const turn = notification.params?.turn;
673
- const turnId = recordActiveTurnId(turn?.id, "notification");
674
- builder.emit({ type: "turn", phase: "started" });
675
- activeTurnAcceptingSteer = true;
676
- const checkpoint = { fence: { kind: "turn", turnId } };
677
- if (options.onEvidence) {
678
- emitProviderEvidence(options.onEvidence, {
679
- type: "checkpoint",
680
- provider: CODEX_APP_SERVER_PROVIDER,
681
- ...checkpoint,
682
- source: "notification",
683
- }, builderDiagnostic);
684
- }
685
- // turn/start may respond before the server has installed its active
686
- // turn. The native notification is the first steer-safe boundary.
687
- options.onSyncCheckpoint?.({ enqueue }, checkpoint);
688
- void drainPendingTells();
689
- }
690
- if (notification.method === "turn/completed") {
691
- const turn = notification.params?.turn ?? {};
692
- const status = coerceString(turn.status) ?? "unknown";
693
- // Either response or notification may mint the turn fence first;
694
- // every later native source must agree with it.
695
- const turnId = recordActiveTurnId(turn.id, "notification");
696
- if (!responseSeenTurnIds.delete(turnId)) {
697
- completedBeforeResponseTurnIds.add(turnId);
698
- }
699
- // Public completed only for exact native status completed.
700
- if (status === "completed") {
701
- builder.emit({ type: "turn", phase: "completed" });
702
- // This must stay in the native notification call stack: the harness
703
- // checkpoint and its observer evidence describe this exact crossing.
704
- activeTurnAcceptingSteer = false;
705
- activeTurnId = undefined;
706
- activeTurnSource = undefined;
707
- const checkpoint = { fence: { kind: "turn", turnId } };
708
- if (options.onEvidence) {
709
- emitProviderEvidence(options.onEvidence, {
710
- type: "checkpoint",
711
- provider: CODEX_APP_SERVER_PROVIDER,
712
- ...checkpoint,
713
- source: "notification",
714
- }, builderDiagnostic);
715
- }
716
- options.onSyncCheckpoint?.({ enqueue }, checkpoint);
717
- void drainPendingTells();
718
- if (pendingTells.length > 0 || drainingTells) {
719
- finishAfterTellDrain = () => {
720
- if (settled || activeTurnId !== undefined || pendingTells.length > 0)
721
- return;
722
- settled = true;
723
- unsubNotifications();
724
- unsubServerRequest();
725
- resolve({ status, turn });
726
- };
727
- return;
728
- }
729
- }
730
- if (settled)
731
- return;
732
- settled = true;
733
- unsubNotifications();
734
- unsubServerRequest();
735
- resolve({ status, turn });
736
- }
737
- if (notification.method === "error") {
738
- const message = coerceString(notification.params?.message) ?? `${CODEX_APP_SERVER_PROVIDER} error`;
739
- builder.emit({ type: "diagnostic", text: message });
740
- }
741
- });
742
- }
743
- catch (error) {
744
- // Notification listeners run outside the driver's await chain;
745
- // convert validation failures into the controlled completion path.
746
- failTurn(error);
747
- }
748
- });
749
- failActiveTurn = failTurn;
750
- builder.signal.addEventListener("abort", () => {
751
- unsubNotifications();
752
- unsubServerRequest();
753
- failTurn(createAbortError(`${CODEX_APP_SERVER_PROVIDER} exec cancelled by client`));
754
- }, { once: true });
755
- });
756
- // Abort can settle this before initialization reaches the await below.
757
- // Attach a handler immediately while preserving the original awaited error.
758
- turnCompleted.catch(() => undefined);
759
- try {
760
- const initResult = await server.request("initialize", {
761
- clientInfo: {
762
- name: "keiyaku",
763
- title: "keiyaku",
764
- version: "0.0.0",
765
- },
766
- capabilities: {
767
- experimentalApi: false,
768
- requestAttestation: false,
769
- },
770
- });
771
- const version = extractVersionFromInitialize(initResult);
772
- if (isCodexAppServerVersionBelowMinimum(version)) {
773
- const message = `${CODEX_APP_SERVER_PROVIDER} refused: initialize version '${version}' is below minimum supported version 0.143.0`;
774
- builder.emit({ type: "fail", message });
775
- throw new Error(message);
776
- }
777
- const initRecord = (initResult && typeof initResult === "object" ? initResult : {});
778
- const serverCodexHome = coerceString(initRecord.codexHome) ?? options.codexHome;
779
- server.notify("initialized");
780
- const threadParams = {
781
- cwd,
782
- ...(options.model ? { model: options.model } : {}),
783
- ...(options.config ? { config: options.config } : {}),
784
- ...(options.developerInstructions ? { developerInstructions: options.developerInstructions } : {}),
785
- };
786
- const threadResult = resumeThreadId
787
- ? await server.request("thread/resume", {
788
- threadId: resumeThreadId,
789
- ...threadParams,
790
- })
791
- : await server.request("thread/start", threadParams);
792
- const thread = threadResult && typeof threadResult === "object"
793
- ? (threadResult.thread ??
794
- threadResult)
795
- : undefined;
796
- activeThreadId = coerceString(thread?.id) ?? resumeThreadId;
797
- if (!activeThreadId) {
798
- const message = `${CODEX_APP_SERVER_PROVIDER} exec could not determine thread id`;
799
- builder.emit({ type: "fail", message });
800
- throw new Error(message);
801
- }
802
- if (options.onEvidence)
803
- emitProviderEvidence(options.onEvidence, { type: "session", provider: CODEX_APP_SERVER_PROVIDER, threadId: activeThreadId }, builderDiagnostic);
804
- // Policy A defaults, but omit when config.* already owns that surface (thread/start config wins).
805
- const turnStartParams = {
806
- threadId: activeThreadId,
807
- input: [{ type: "text", text: prompt }],
808
- ...(options.model ? { model: options.model } : {}),
809
- ...(options.effort ? { effort: toCodexAppServerEffort(options.effort) } : {}),
810
- };
811
- if (!configOwnsApprovalPolicy(options.config)) {
812
- turnStartParams.approvalPolicy = options.approvalPolicy ?? CODEX_APP_SERVER_TURN_APPROVAL_POLICY;
813
- }
814
- if (!configOwnsSandboxPolicy(options.config)) {
815
- turnStartParams.sandboxPolicy = options.sandboxPolicy ?? buildCodexAppServerWorkspaceWriteSandboxPolicy({
816
- writableRoots: [cwd],
817
- networkAccess: false,
818
- });
819
- }
820
- activeTurnId = undefined;
821
- activeTurnSource = undefined;
822
- activeTurnAcceptingSteer = false;
823
- const turnStart = await server.request("turn/start", turnStartParams);
824
- const turn = turnStart && typeof turnStart === "object"
825
- ? (turnStart.turn ?? undefined)
826
- : undefined;
827
- const turnId = recordTurnStartResponse(turn?.id);
828
- if (options.onEvidence)
829
- emitProviderEvidence(options.onEvidence, { type: "checkpoint", provider: CODEX_APP_SERVER_PROVIDER, fence: { kind: "turn", turnId }, source: "response" }, builderDiagnostic);
830
- const completed = await turnCompleted;
831
- if (completed.status === "interrupted" || interruptRequested) {
832
- throw createAbortError(`${CODEX_APP_SERVER_PROVIDER} turn interrupted`);
833
- }
834
- if (completed.status === "failed") {
835
- const message = `${CODEX_APP_SERVER_PROVIDER} turn failed`;
836
- builder.emit({ type: "fail", message });
837
- throw new Error(message);
838
- }
839
- if (completed.status !== "completed") {
840
- const message = `${CODEX_APP_SERVER_PROVIDER} turn ended with status '${completed.status}'`;
841
- builder.emit({ type: "fail", message });
842
- throw new Error(message);
843
- }
844
- // Successful turn/completed is consumption evidence; interrupt ack alone is not terminal proof.
845
- const session = {
846
- provider: "codex-app-server",
847
- version: SUBAGENT_SESSION_VERSION,
848
- handle: {
849
- threadId: activeThreadId,
850
- ...(serverCodexHome ? { codexHome: serverCodexHome } : {}),
851
- },
852
- };
853
- const finalMessage = lastAgentMessage;
854
- builder.emit({ type: "done", finalMessage, session });
855
- return { finalMessage, session };
856
- }
857
- finally {
858
- rejectPendingTells(new Error(`${CODEX_APP_SERVER_PROVIDER} execution ended before queued tell acceptance`));
859
- await server?.close();
860
- server = undefined;
861
- }
862
- },
863
- });
864
- }