ubume 0.1.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 (182) hide show
  1. package/README.md +88 -0
  2. package/bin/codexa-local-harness-bridge.js +3 -0
  3. package/bin/codexa.js +28 -0
  4. package/bin/ubume-local-harness-bridge.js +325 -0
  5. package/bin/ubume.js +398 -0
  6. package/package.json +66 -0
  7. package/src/app.tsx +5759 -0
  8. package/src/commands/handler.ts +889 -0
  9. package/src/config/appVersion.ts +69 -0
  10. package/src/config/buildInfo.ts +3 -0
  11. package/src/config/launchArgs.ts +196 -0
  12. package/src/config/layeredConfig.ts +853 -0
  13. package/src/config/legacyEnv.ts +16 -0
  14. package/src/config/persistence.ts +377 -0
  15. package/src/config/runtimeConfig.ts +558 -0
  16. package/src/config/settings.ts +405 -0
  17. package/src/config/toml-serialize.ts +109 -0
  18. package/src/config/trustStore.ts +84 -0
  19. package/src/config/updateCheckCache.ts +85 -0
  20. package/src/core/README.md +55 -0
  21. package/src/core/agent/loop.ts +464 -0
  22. package/src/core/agent/protocol.ts +345 -0
  23. package/src/core/agent/tools.ts +423 -0
  24. package/src/core/auth/codexAuth.ts +359 -0
  25. package/src/core/codex/codexExecArgs.ts +166 -0
  26. package/src/core/codex/codexLaunch.ts +163 -0
  27. package/src/core/codex/codexPrompt.ts +429 -0
  28. package/src/core/debug/inputDebug.ts +51 -0
  29. package/src/core/debug/localStreamDebug.ts +50 -0
  30. package/src/core/debug/modelStateDebug.ts +35 -0
  31. package/src/core/executables/antigravityExecutable.ts +48 -0
  32. package/src/core/executables/claudeExecutable.ts +63 -0
  33. package/src/core/executables/codexExecutable.ts +160 -0
  34. package/src/core/executables/executableResolver.ts +164 -0
  35. package/src/core/executables/geminiExecutable.ts +78 -0
  36. package/src/core/models/codexCapabilities.ts +97 -0
  37. package/src/core/models/codexModelCapabilities.ts +624 -0
  38. package/src/core/models/codexModelsCacheSeed.ts +153 -0
  39. package/src/core/models/modelSpecs.ts +303 -0
  40. package/src/core/models/providerModelCache.ts +94 -0
  41. package/src/core/perf/profiler.ts +125 -0
  42. package/src/core/perf/renderDebug.ts +398 -0
  43. package/src/core/process/CommandRunner.ts +280 -0
  44. package/src/core/process/processValidation.ts +111 -0
  45. package/src/core/providerLauncher/launcher.ts +220 -0
  46. package/src/core/providerLauncher/registry.ts +354 -0
  47. package/src/core/providerLauncher/types.ts +95 -0
  48. package/src/core/providerLauncher/workspaceConfig.ts +487 -0
  49. package/src/core/providerRuntime/anthropic.ts +580 -0
  50. package/src/core/providerRuntime/antigravity.ts +500 -0
  51. package/src/core/providerRuntime/capabilityProfile.ts +383 -0
  52. package/src/core/providerRuntime/claudeCodeDiscovery.ts +724 -0
  53. package/src/core/providerRuntime/claudeCodeDiscoveryDebug.ts +55 -0
  54. package/src/core/providerRuntime/codexaCupy.ts +97 -0
  55. package/src/core/providerRuntime/codexaNative.ts +425 -0
  56. package/src/core/providerRuntime/contextMetadata.ts +397 -0
  57. package/src/core/providerRuntime/gemini.ts +789 -0
  58. package/src/core/providerRuntime/lmstudio.ts +118 -0
  59. package/src/core/providerRuntime/local.ts +770 -0
  60. package/src/core/providerRuntime/localHarness/runtime.ts +1090 -0
  61. package/src/core/providerRuntime/localOutputBudget.ts +17 -0
  62. package/src/core/providerRuntime/mistralVibe.ts +667 -0
  63. package/src/core/providerRuntime/models.ts +175 -0
  64. package/src/core/providerRuntime/reasoning.ts +20 -0
  65. package/src/core/providerRuntime/registry.ts +286 -0
  66. package/src/core/providerRuntime/types.ts +142 -0
  67. package/src/core/providerRuntime/unsloth.ts +216 -0
  68. package/src/core/providers/codexJsonStream.ts +305 -0
  69. package/src/core/providers/codexSubprocess.ts +378 -0
  70. package/src/core/providers/codexTranscript.ts +695 -0
  71. package/src/core/providers/openaiNative.ts +13 -0
  72. package/src/core/providers/registry.ts +21 -0
  73. package/src/core/providers/types.ts +91 -0
  74. package/src/core/shared/attachments.ts +101 -0
  75. package/src/core/shared/cleanupFastFail.ts +67 -0
  76. package/src/core/shared/clipboard.ts +24 -0
  77. package/src/core/shared/clipboardImage.ts +111 -0
  78. package/src/core/shared/githubDiagnostics.ts +222 -0
  79. package/src/core/shared/hollowResponseFormat.ts +39 -0
  80. package/src/core/terminal/clearFrameBoundary.ts +852 -0
  81. package/src/core/terminal/frameLock.ts +110 -0
  82. package/src/core/terminal/inkRenderReset.ts +123 -0
  83. package/src/core/terminal/startupClear.ts +20 -0
  84. package/src/core/terminal/terminalCapabilities.ts +100 -0
  85. package/src/core/terminal/terminalControl.ts +169 -0
  86. package/src/core/terminal/terminalSanitize.ts +147 -0
  87. package/src/core/terminal/terminalTitle.ts +400 -0
  88. package/src/core/version/channel.ts +27 -0
  89. package/src/core/version/packageManager.ts +119 -0
  90. package/src/core/version/updateCheck.ts +203 -0
  91. package/src/core/workspace/appData.ts +107 -0
  92. package/src/core/workspace/conversationStore.ts +335 -0
  93. package/src/core/workspace/launchContext.ts +259 -0
  94. package/src/core/workspace/planStorage.ts +135 -0
  95. package/src/core/workspace/projectInstructions.ts +54 -0
  96. package/src/core/workspace/scratchDir.ts +64 -0
  97. package/src/core/workspace/workspaceActivity.ts +384 -0
  98. package/src/core/workspace/workspaceGuard.ts +377 -0
  99. package/src/core/workspace/workspaceRoot.ts +47 -0
  100. package/src/exec.ts +73 -0
  101. package/src/headless/execArgs.ts +296 -0
  102. package/src/headless/execRunner.ts +304 -0
  103. package/src/index.tsx +270 -0
  104. package/src/legacyEnvBootstrap.ts +5 -0
  105. package/src/session/appSession.ts +771 -0
  106. package/src/session/chatLifecycle.ts +994 -0
  107. package/src/session/conversation.ts +107 -0
  108. package/src/session/liveRenderScheduler.ts +214 -0
  109. package/src/session/persistedResponse.ts +93 -0
  110. package/src/session/planFlow.ts +159 -0
  111. package/src/session/planTranscript.ts +19 -0
  112. package/src/session/promptRunSchedule.ts +26 -0
  113. package/src/session/types.ts +234 -0
  114. package/src/test/runtimeTestUtils.ts +14 -0
  115. package/src/types/react-dom.d.ts +3 -0
  116. package/src/ui/chrome/ActivityBars.tsx +68 -0
  117. package/src/ui/chrome/ActivityIndicator.tsx +58 -0
  118. package/src/ui/chrome/AnimatedStatusText.tsx +69 -0
  119. package/src/ui/chrome/AppShell.tsx +474 -0
  120. package/src/ui/chrome/BottomComposer.tsx +1135 -0
  121. package/src/ui/chrome/DashCard.tsx +82 -0
  122. package/src/ui/chrome/RunFooter.tsx +65 -0
  123. package/src/ui/chrome/RuntimeStatusBar.tsx +108 -0
  124. package/src/ui/chrome/Spinner.tsx +25 -0
  125. package/src/ui/chrome/TopHeader.tsx +439 -0
  126. package/src/ui/chrome/UpdateAvailableCard.tsx +42 -0
  127. package/src/ui/chrome/busyStatusAnimation.ts +11 -0
  128. package/src/ui/input/commandNormalize.ts +66 -0
  129. package/src/ui/input/focus.ts +73 -0
  130. package/src/ui/input/imageAttachments.ts +18 -0
  131. package/src/ui/input/inputBuffer.ts +203 -0
  132. package/src/ui/input/pastedContent.ts +75 -0
  133. package/src/ui/input/rawArrowKeys.ts +28 -0
  134. package/src/ui/input/slashCommands.ts +43 -0
  135. package/src/ui/input/useStdinRawModeLease.ts +23 -0
  136. package/src/ui/layout.ts +560 -0
  137. package/src/ui/panels/AttachmentImportPanel.tsx +131 -0
  138. package/src/ui/panels/AuthPanel.tsx +149 -0
  139. package/src/ui/panels/BackendPicker.tsx +28 -0
  140. package/src/ui/panels/ModePicker.tsx +31 -0
  141. package/src/ui/panels/ModelPicker.tsx +31 -0
  142. package/src/ui/panels/ModelPickerScreen.tsx +761 -0
  143. package/src/ui/panels/ModelReasoningPicker.tsx +458 -0
  144. package/src/ui/panels/Panel.tsx +51 -0
  145. package/src/ui/panels/PermissionsPanel.tsx +78 -0
  146. package/src/ui/panels/PlanActionPicker.tsx +187 -0
  147. package/src/ui/panels/ProviderPicker.tsx +753 -0
  148. package/src/ui/panels/ProviderSetupPrompt.tsx +52 -0
  149. package/src/ui/panels/ReasoningPicker.tsx +46 -0
  150. package/src/ui/panels/ResumePicker.tsx +90 -0
  151. package/src/ui/panels/SelectionPanel.tsx +138 -0
  152. package/src/ui/panels/SettingsPanel.tsx +156 -0
  153. package/src/ui/panels/TextEntryPanel.tsx +139 -0
  154. package/src/ui/panels/ThemePicker.tsx +32 -0
  155. package/src/ui/panels/ToolApprovalPanel.tsx +46 -0
  156. package/src/ui/panels/UpdatePromptPanel.tsx +236 -0
  157. package/src/ui/panels/responsivePickerViewport.ts +64 -0
  158. package/src/ui/render/Markdown.tsx +331 -0
  159. package/src/ui/render/diffRenderer.ts +116 -0
  160. package/src/ui/render/logoVariants.ts +113 -0
  161. package/src/ui/render/modeDisplay.ts +52 -0
  162. package/src/ui/render/outputPipeline.ts +64 -0
  163. package/src/ui/render/runtimeDisplay.ts +128 -0
  164. package/src/ui/render/terminalAnswerFormat.ts +128 -0
  165. package/src/ui/render/textLayout.ts +392 -0
  166. package/src/ui/theme.tsx +274 -0
  167. package/src/ui/themeFlow.ts +41 -0
  168. package/src/ui/timeline/ActionRequiredBlock.tsx +38 -0
  169. package/src/ui/timeline/AgentBlock.tsx +130 -0
  170. package/src/ui/timeline/StaticIntroItem.tsx +54 -0
  171. package/src/ui/timeline/ThinkingBlock.tsx +100 -0
  172. package/src/ui/timeline/Timeline.tsx +1410 -0
  173. package/src/ui/timeline/TranscriptShell.tsx +302 -0
  174. package/src/ui/timeline/TurnGroup.tsx +673 -0
  175. package/src/ui/timeline/layoutListWindow.ts +145 -0
  176. package/src/ui/timeline/liveViewportWindow.ts +68 -0
  177. package/src/ui/timeline/progressEntries.ts +156 -0
  178. package/src/ui/timeline/runActivityView.ts +37 -0
  179. package/src/ui/timeline/staticTranscriptCache.ts +174 -0
  180. package/src/ui/timeline/streamCoalesce.ts +53 -0
  181. package/src/ui/timeline/timelineMeasure.ts +3273 -0
  182. package/src/ui/useThrottledValue.ts +31 -0
@@ -0,0 +1,378 @@
1
+ import { spawn } from "child_process";
2
+ import { formatCodexLaunchError, spawnCodexProcess } from "../executables/codexExecutable.js";
3
+ import { prepareCodexExecLaunch } from "../codex/codexLaunch.js";
4
+ import * as perf from "../perf/profiler.js";
5
+ import { buildCodexPrompt } from "../codex/codexPrompt.js";
6
+ import { createTerminalTitleSequenceStripper } from "../terminal/terminalTitle.js";
7
+ import { createCodexJsonStreamParser } from "./codexJsonStream.js";
8
+ import {
9
+ createCodexTranscriptStreamParser,
10
+ createStdoutSanitizer,
11
+ isStderrNoise,
12
+ sanitizeCodexTranscript,
13
+ stripAnsi,
14
+ stripNonPrintableControls,
15
+ } from "./codexTranscript.js";
16
+ import type { BackendProvider } from "./types.js";
17
+ import { formatConversationHistory } from "../../session/conversation.js";
18
+
19
+ // Detects CLI error messages that indicate --experimental-json is not supported.
20
+ // When this fires the provider retries in legacy transcript mode.
21
+ function looksLikeUnsupportedStructuredOutput(raw: string): boolean {
22
+ return /experimental-json|unknown option|unrecognized option|unexpected argument|unexpected option/i.test(raw);
23
+ }
24
+
25
+ function isProcessTerminationNoise(line: string): boolean {
26
+ return /^SUCCESS: The process with PID \d+ .* has been terminated\.$/.test(line.trim());
27
+ }
28
+
29
+ export const codexSubprocessProvider: BackendProvider = {
30
+ id: "codex-subprocess",
31
+ label: "Ubume",
32
+ description: "Direct connection to the Codex neural network.",
33
+ authState: "delegated",
34
+ authLabel: "Authenticated via Codex",
35
+ statusMessage: "Authentication is managed via Ubume.",
36
+ supportsModels: () => true,
37
+ run: (prompt, options, handlers) => {
38
+ let done = false;
39
+ let cancelled = false;
40
+ let proc: ReturnType<typeof spawn> | null = null;
41
+ let procExited = false;
42
+ let currentRawOutput = "";
43
+ let finalAnswerObserved = false;
44
+
45
+ const finishError = (message: string) => {
46
+ if (done) return;
47
+ done = true;
48
+ handlers.onError(message, currentRawOutput);
49
+ };
50
+
51
+ const finishSuccess = (response: string) => {
52
+ if (done) return;
53
+ done = true;
54
+ handlers.onResponse(response);
55
+ };
56
+
57
+ const emitFinalAnswerObserved = (response: string) => {
58
+ if (finalAnswerObserved) return;
59
+ finalAnswerObserved = true;
60
+ handlers.onFinalAnswerObserved?.(response);
61
+ };
62
+
63
+ const startAttempt = (structuredOutput: boolean, probeCapabilities = false) => {
64
+ if (cancelled || done) return;
65
+ let firstChunkSeen = false;
66
+ let firstStderrSeen = false;
67
+ handlers.benchmarkHooks?.onProviderPrepStart?.();
68
+ void prepareCodexExecLaunch(
69
+ {
70
+ runtime: options.runtime,
71
+ cwd: options.workspaceRoot,
72
+ structuredOutput,
73
+ probeCapabilities,
74
+ imageAttachments: options.imageAttachments,
75
+ codexCommandPath: options.runtime.codexCommandPath,
76
+ },
77
+ import.meta.url,
78
+ )
79
+ .then((launchPlan) => {
80
+ if (cancelled || done) return;
81
+ if (!launchPlan.ok) {
82
+ finishError(launchPlan.error);
83
+ return;
84
+ }
85
+ if (!launchPlan.executable) {
86
+ finishError("Codex launch preparation did not return an executable.");
87
+ return;
88
+ }
89
+ handlers.benchmarkHooks?.onProviderPrepComplete?.();
90
+
91
+ let rawStdout = "";
92
+ let rawStderr = "";
93
+ let stdoutLineBuffer = "";
94
+ let mode: "undecided" | "json" | "legacy" = structuredOutput ? "undecided" : "legacy";
95
+ let legacyProgressSequence = 0;
96
+
97
+ // Consecutive thinking lines from the transcript parser are coalesced into
98
+ // a single progress update so the UI shows one accumulating block rather
99
+ // than a rapid series of individual entries. The sequence resets whenever
100
+ // an assistant delta or tool activity event breaks the run of thinking lines.
101
+ let activeTranscriptThinkingId: string | null = null;
102
+ let activeTranscriptThinkingText = "";
103
+
104
+ const resetTranscriptCoalescing = () => {
105
+ activeTranscriptThinkingId = null;
106
+ activeTranscriptThinkingText = "";
107
+ };
108
+
109
+ const emitLegacyProgress = (source: "stdout" | "stderr" | "transcript", text: string) => {
110
+ if (isProcessTerminationNoise(text)) return;
111
+ const displayText = text.length > 80 ? `${text.slice(0, 77)}...` : text;
112
+ handlers.onProgress?.({
113
+ id: `${source}-${++legacyProgressSequence}`,
114
+ source,
115
+ text: displayText,
116
+ });
117
+ };
118
+
119
+ const transcriptParser = createCodexTranscriptStreamParser({
120
+ onThinkingLine: (line) => {
121
+ if (activeTranscriptThinkingId === null) {
122
+ activeTranscriptThinkingId = `transcript-thinking-${++legacyProgressSequence}`;
123
+ activeTranscriptThinkingText = line;
124
+ } else {
125
+ activeTranscriptThinkingText = `${activeTranscriptThinkingText}\n${line}`;
126
+ }
127
+ handlers.onProgress?.({
128
+ id: activeTranscriptThinkingId,
129
+ source: "transcript",
130
+ text: activeTranscriptThinkingText,
131
+ });
132
+ },
133
+ onAssistantDelta: (chunk) => {
134
+ resetTranscriptCoalescing();
135
+ handlers.onAssistantDelta?.(chunk);
136
+ },
137
+ onToolActivity: (activity) => {
138
+ resetTranscriptCoalescing();
139
+ handlers.onToolActivity?.(activity);
140
+ },
141
+ });
142
+ const transcriptStdoutSanitizer = createStdoutSanitizer();
143
+ const transcriptStderrSanitizer = createStdoutSanitizer();
144
+ const stdoutTitleStripper = createTerminalTitleSequenceStripper({
145
+ source: "src/core/providers/codexSubprocess.ts:codex.stdout",
146
+ stream: "stdout",
147
+ origin: "codex-cli",
148
+ });
149
+ const stderrTitleStripper = createTerminalTitleSequenceStripper({
150
+ source: "src/core/providers/codexSubprocess.ts:codex.stderr",
151
+ stream: "stderr",
152
+ origin: "codex-cli",
153
+ });
154
+ const jsonParser = createCodexJsonStreamParser({
155
+ onProgress: (update) => handlers.onProgress?.(update),
156
+ onAssistantDelta: (chunk) => handlers.onAssistantDelta?.(chunk),
157
+ onFinalAnswerObserved: emitFinalAnswerObserved,
158
+ onToolActivity: (activity) => handlers.onToolActivity?.(activity),
159
+ });
160
+
161
+ const feedTranscript = (text: string, stream: "stdout" | "stderr") => {
162
+ const sanitizer = stream === "stdout" ? transcriptStdoutSanitizer : transcriptStderrSanitizer;
163
+ const clean = sanitizer.process(text);
164
+ if (clean) {
165
+ transcriptParser.feed(clean);
166
+ }
167
+ };
168
+
169
+ const switchToTranscriptFallback = () => {
170
+ if (mode === "legacy") return;
171
+ mode = "legacy";
172
+ if (rawStdout) {
173
+ feedTranscript(rawStdout, "stdout");
174
+ }
175
+ if (rawStderr) {
176
+ feedTranscript(rawStderr, "stderr");
177
+ }
178
+ stdoutLineBuffer = "";
179
+ };
180
+
181
+ const processStructuredLines = (flush: boolean) => {
182
+ while (true) {
183
+ const newlineIndex = stdoutLineBuffer.indexOf("\n");
184
+ if (newlineIndex === -1) {
185
+ if (!flush) return;
186
+ if (!stdoutLineBuffer) return;
187
+ }
188
+
189
+ const line = newlineIndex === -1
190
+ ? stdoutLineBuffer
191
+ : stdoutLineBuffer.slice(0, newlineIndex);
192
+ stdoutLineBuffer = newlineIndex === -1 ? "" : stdoutLineBuffer.slice(newlineIndex + 1);
193
+ const normalizedLine = line.replace(/\r$/, "");
194
+ if (!normalizedLine.trim()) {
195
+ continue;
196
+ }
197
+
198
+ const parsed = jsonParser.feedLine(normalizedLine);
199
+ if (!parsed) {
200
+ if (mode === "json") {
201
+ emitLegacyProgress("stdout", normalizedLine);
202
+ continue;
203
+ }
204
+ switchToTranscriptFallback();
205
+ return;
206
+ }
207
+ mode = "json";
208
+ }
209
+ };
210
+
211
+ const ingestStdoutText = (text: string) => {
212
+ if (!text) return;
213
+ currentRawOutput += text;
214
+ rawStdout += text;
215
+
216
+ if (mode === "legacy") {
217
+ feedTranscript(text, "stdout");
218
+ return;
219
+ }
220
+
221
+ stdoutLineBuffer += text;
222
+ processStructuredLines(false);
223
+ };
224
+
225
+ const ingestStderrText = (text: string) => {
226
+ if (!text) return;
227
+ currentRawOutput += text;
228
+ rawStderr += text;
229
+
230
+ if (mode === "legacy") {
231
+ feedTranscript(text, "stderr");
232
+ return;
233
+ }
234
+
235
+ const lines = stripNonPrintableControls(stripAnsi(text))
236
+ .replace(/\r\n/g, "\n")
237
+ .replace(/\r/g, "\n")
238
+ .split("\n");
239
+ for (const line of lines) {
240
+ const trimmed = line.trim();
241
+ if (!trimmed || isStderrNoise(trimmed) || isProcessTerminationNoise(trimmed)) continue;
242
+ emitLegacyProgress("stderr", trimmed);
243
+ }
244
+ };
245
+
246
+ handlers.onProcessLifecycle?.("before-spawn");
247
+ proc = spawnCodexProcess(launchPlan.executable, launchPlan.args, { stdio: ["pipe", "pipe", "pipe"] });
248
+ procExited = false;
249
+ handlers.onProcessLifecycle?.("spawned");
250
+ handlers.benchmarkHooks?.onCodexProcessSpawned?.({
251
+ executable: launchPlan.executable,
252
+ argv: launchPlan.args,
253
+ });
254
+ perf.mark("spawn_done");
255
+
256
+ proc.stdout?.on("data", (chunk: Buffer) => {
257
+ if (cancelled || done) return;
258
+ if (!firstChunkSeen) {
259
+ firstChunkSeen = true;
260
+ perf.mark("first_chunk");
261
+ handlers.benchmarkHooks?.onFirstStdout?.();
262
+ }
263
+ perf.mark("last_chunk");
264
+ ingestStdoutText(stdoutTitleStripper.process(chunk));
265
+ });
266
+
267
+ proc.stderr?.on("data", (chunk: Buffer) => {
268
+ if (cancelled || done) return;
269
+ if (!firstStderrSeen) {
270
+ firstStderrSeen = true;
271
+ handlers.benchmarkHooks?.onFirstStderr?.();
272
+ }
273
+ ingestStderrText(stderrTitleStripper.process(chunk));
274
+ });
275
+
276
+ proc.on("close", (code) => {
277
+ procExited = true;
278
+ handlers.onProcessLifecycle?.("exit");
279
+ if (cancelled || done) return;
280
+ ingestStdoutText(stdoutTitleStripper.flush());
281
+ ingestStderrText(stderrTitleStripper.flush());
282
+ if (!firstChunkSeen) {
283
+ handlers.benchmarkHooks?.onFirstStdout?.(false);
284
+ }
285
+ if (!firstStderrSeen) {
286
+ handlers.benchmarkHooks?.onFirstStderr?.(false);
287
+ }
288
+
289
+ if (mode !== "legacy") {
290
+ processStructuredLines(true);
291
+ }
292
+
293
+ if (mode === "legacy") {
294
+ const remainingStdout = transcriptStdoutSanitizer.flush();
295
+ if (remainingStdout) transcriptParser.feed(remainingStdout);
296
+ const remainingStderr = transcriptStderrSanitizer.flush();
297
+ if (remainingStderr) transcriptParser.feed(remainingStderr);
298
+ transcriptParser.flush();
299
+ }
300
+
301
+ handlers.benchmarkHooks?.onCodexProcessExit?.(code);
302
+
303
+ if (
304
+ structuredOutput
305
+ && code !== 0
306
+ && !jsonParser.hasStructuredEvents()
307
+ && looksLikeUnsupportedStructuredOutput(`${rawStdout}\n${rawStderr}`)
308
+ ) {
309
+ currentRawOutput = "";
310
+ startAttempt(false, true);
311
+ return;
312
+ }
313
+
314
+ const structuredFailure = jsonParser.getFailureMessage();
315
+ if (structuredFailure) {
316
+ finishError(structuredFailure);
317
+ return;
318
+ }
319
+
320
+ if (code === 0) {
321
+ const finalResponse = mode === "json"
322
+ ? jsonParser.getFinalResponse().trim() || sanitizeCodexTranscript(currentRawOutput)
323
+ : sanitizeCodexTranscript(currentRawOutput);
324
+ emitFinalAnswerObserved(finalResponse);
325
+ finishSuccess(finalResponse);
326
+ return;
327
+ }
328
+
329
+ finishError(`Process exited with code ${code}`);
330
+ });
331
+
332
+ proc.on("error", (err) => {
333
+ handlers.onProcessLifecycle?.("error");
334
+ if (cancelled || done) return;
335
+ const errno = err as NodeJS.ErrnoException;
336
+ finishError(formatCodexLaunchError(errno));
337
+ });
338
+
339
+ const promptPolicy = options.promptPolicy ?? "wrapped";
340
+ const history = options.conversationHistory?.length
341
+ ? `Previous conversation:\n${formatConversationHistory(options.conversationHistory)}\n\n`
342
+ : "";
343
+ const baseProviderPrompt = promptPolicy === "raw"
344
+ ? prompt
345
+ : buildCodexPrompt(prompt, options.runtime, undefined, {
346
+ projectInstructions: options.projectInstructions,
347
+ });
348
+ const providerPrompt = history ? `${history}${baseProviderPrompt}` : baseProviderPrompt;
349
+ handlers.benchmarkHooks?.onProviderPromptPrepared?.({
350
+ policy: promptPolicy,
351
+ characterCount: providerPrompt.length,
352
+ });
353
+ proc.stdin?.write(providerPrompt);
354
+ proc.stdin?.end();
355
+ })
356
+ .catch((error) => {
357
+ const errno = error as NodeJS.ErrnoException;
358
+ finishError(formatCodexLaunchError(errno));
359
+ });
360
+ };
361
+
362
+ currentRawOutput = "";
363
+ startAttempt(true);
364
+
365
+ return () => {
366
+ cancelled = true;
367
+ done = true;
368
+ handlers.benchmarkHooks?.onCleanupStart?.();
369
+ handlers.onProcessLifecycle?.("cleanup");
370
+ if (!proc || procExited || proc.killed) {
371
+ handlers.benchmarkHooks?.onCleanupComplete?.({ skipped: true });
372
+ return;
373
+ }
374
+ proc.kill();
375
+ handlers.benchmarkHooks?.onCleanupComplete?.({ skipped: false });
376
+ };
377
+ },
378
+ };