@jskit-ai/assistant-core 0.2.7 → 0.2.9

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 (214) hide show
  1. package/examples/conversation/package.json +5 -5
  2. package/package.json +18 -7
  3. package/src/client/browser.js +21 -0
  4. package/src/client/conversation/AssistantAttachmentQueue.vue +25 -108
  5. package/src/client/conversation/AssistantComposerActions.vue +1 -1
  6. package/src/client/conversation/AssistantConversationElement.vue +14 -52
  7. package/src/client/conversation/AssistantGoalControl.vue +13 -10
  8. package/src/client/conversation/AssistantPromptInput.vue +6 -91
  9. package/src/client/conversation/AssistantTranscript.css +505 -0
  10. package/src/client/conversation/AssistantTranscript.vue +24 -1197
  11. package/src/client/conversation/attachmentFiles.js +158 -0
  12. package/src/client/conversation/attachmentQueueItems.js +121 -0
  13. package/src/client/conversation/attachmentUploadRows.js +265 -0
  14. package/src/client/conversation/conversationDisplay.js +54 -0
  15. package/src/client/conversation/messageDelivery.js +13 -9
  16. package/src/client/conversation/retainedConversation.js +14 -3
  17. package/src/client/conversation/submitText.js +13 -2
  18. package/src/client/conversation/transcriptEntries.js +225 -0
  19. package/src/client/conversation/useAssistantAttachments.js +86 -388
  20. package/src/client/conversation/useAssistantQuestions.js +16 -3
  21. package/src/client/conversation/useAssistantSuggestions.js +16 -2
  22. package/src/client/conversation/useScrollToBottom.js +17 -19
  23. package/src/client/conversation/useTextareaAutoGrow.js +115 -0
  24. package/src/client/conversation/useTranscriptScrollFollow.js +525 -0
  25. package/src/client/lib/assistantApi.js +192 -161
  26. package/src/client/types.js +110 -0
  27. package/src/server/conversation/attachments.js +21 -6
  28. package/src/server/conversation/boundedToolLoop.js +1 -0
  29. package/src/server/conversation/claudeClient.js +71 -20
  30. package/src/server/conversation/claudeCommandRun.js +191 -0
  31. package/src/server/conversation/claudeHistory.js +7 -0
  32. package/src/server/conversation/claudeNativeTurn.js +708 -0
  33. package/src/server/conversation/claudeProcess.js +186 -112
  34. package/src/server/conversation/claudeProcessStart.js +214 -0
  35. package/src/server/conversation/claudePromptDelivery.js +183 -0
  36. package/src/server/conversation/claudeSessionCleanup.js +248 -0
  37. package/src/server/conversation/claudeTools.js +15 -3
  38. package/src/server/conversation/claudeTurn.js +212 -1388
  39. package/src/server/conversation/codexAccountReader.js +168 -0
  40. package/src/server/conversation/codexAuthPreflight.js +5 -4
  41. package/src/server/conversation/codexClient.js +18 -6
  42. package/src/server/conversation/codexConfiguration.js +8 -844
  43. package/src/server/conversation/codexConversationStops.js +377 -0
  44. package/src/server/conversation/codexConversationStore.js +119 -85
  45. package/src/server/conversation/codexConversationTurns.js +378 -0
  46. package/src/server/conversation/codexConversations.js +134 -866
  47. package/src/server/conversation/codexDetachedConversation.js +203 -0
  48. package/src/server/conversation/codexDetachedTurn.js +145 -104
  49. package/src/server/conversation/codexEvents.js +5 -200
  50. package/src/server/conversation/codexGoalCommands.js +14 -4
  51. package/src/server/conversation/codexHelperExecution.js +57 -57
  52. package/src/server/conversation/codexHelperThreadLedger.js +30 -288
  53. package/src/server/conversation/codexHelperThreadRecord.js +304 -0
  54. package/src/server/conversation/codexHelperThreadRecovery.js +54 -76
  55. package/src/server/conversation/codexHelperThreads.js +94 -93
  56. package/src/server/conversation/codexHistoryAdapter.js +13 -8
  57. package/src/server/conversation/codexIsolation.js +672 -0
  58. package/src/server/conversation/codexLiveItems.js +379 -0
  59. package/src/server/conversation/codexMessageCommands.js +68 -286
  60. package/src/server/conversation/codexMessageDelivery.js +46 -37
  61. package/src/server/conversation/codexModelCatalog.js +86 -7
  62. package/src/server/conversation/codexNotificationObserver.js +81 -88
  63. package/src/server/conversation/codexNotificationQueue.js +21 -10
  64. package/src/server/conversation/codexObservationControl.js +105 -89
  65. package/src/server/conversation/codexOutput.js +84 -419
  66. package/src/server/conversation/codexProcess.js +29 -1053
  67. package/src/server/conversation/codexProcessLeader.js +1 -0
  68. package/src/server/conversation/codexPromptDispatch.js +241 -0
  69. package/src/server/conversation/codexProvider.js +147 -1807
  70. package/src/server/conversation/codexProviderAuth.js +348 -0
  71. package/src/server/conversation/codexProviderInvalidation.js +371 -0
  72. package/src/server/conversation/codexProviderInventories.js +437 -0
  73. package/src/server/conversation/codexProviderOwner.js +149 -957
  74. package/src/server/conversation/codexProviderRelease.js +465 -0
  75. package/src/server/conversation/codexProviderSelection.js +26 -15
  76. package/src/server/conversation/codexProviderThreads.js +698 -0
  77. package/src/server/conversation/codexProviderTurns.js +453 -0
  78. package/src/server/conversation/codexReasoning.js +32 -23
  79. package/src/server/conversation/codexRenewalCommands.js +18 -6
  80. package/src/server/conversation/codexRenewalThreads.js +7 -22
  81. package/src/server/conversation/codexResultSettlement.js +70 -88
  82. package/src/server/conversation/codexRunJournal.js +91 -97
  83. package/src/server/conversation/codexRuntime.js +426 -0
  84. package/src/server/conversation/codexRuntimeInventory.js +113 -0
  85. package/src/server/conversation/codexRuntimeLifecycle.js +39 -13
  86. package/src/server/conversation/codexRuntimeStart.js +431 -0
  87. package/src/server/conversation/codexRuntimeStop.js +324 -0
  88. package/src/server/conversation/codexSessionCleanup.js +25 -10
  89. package/src/server/conversation/codexSessionReconciliation.js +21 -7
  90. package/src/server/conversation/codexThreadReadiness.js +49 -52
  91. package/src/server/conversation/codexThreadSnapshot.js +210 -0
  92. package/src/server/conversation/codexThreadStatus.js +48 -62
  93. package/src/server/conversation/codexTurn.js +171 -55
  94. package/src/server/conversation/codexTurnActivity.js +210 -0
  95. package/src/server/conversation/codexTurnControl.js +44 -48
  96. package/src/server/conversation/codexTurnEndings.js +610 -0
  97. package/src/server/conversation/codexTurnRecovery.js +97 -848
  98. package/src/server/conversation/codexTurnState.js +138 -111
  99. package/src/server/conversation/completedResponses.js +227 -0
  100. package/src/server/conversation/configuration.js +2 -2
  101. package/src/server/conversation/continuity.js +43 -6
  102. package/src/server/conversation/conversationDelivery.js +394 -0
  103. package/src/server/conversation/conversationEntries.js +365 -0
  104. package/src/server/conversation/conversationEvents.js +192 -0
  105. package/src/server/conversation/conversationGoals.js +187 -0
  106. package/src/server/conversation/conversationHandles.js +237 -0
  107. package/src/server/conversation/conversationReads.js +206 -0
  108. package/src/server/conversation/conversationRefusals.js +58 -0
  109. package/src/server/conversation/conversationReplacement.js +254 -0
  110. package/src/server/conversation/conversationSending.js +273 -0
  111. package/src/server/conversation/conversationStopping.js +121 -0
  112. package/src/server/conversation/conversationTurnDelivery.js +413 -0
  113. package/src/server/conversation/conversationTurns.js +511 -0
  114. package/src/server/conversation/deliveryTiming.js +25 -3
  115. package/src/server/conversation/fileStorage.js +11 -6
  116. package/src/server/conversation/hookBridge.js +14 -3
  117. package/src/server/conversation/index.js +1 -1
  118. package/src/server/conversation/localExecution.js +82 -625
  119. package/src/server/conversation/nativeCommandCallbacks.js +10 -0
  120. package/src/server/conversation/nativeConversationLifecycle.js +379 -0
  121. package/src/server/conversation/nativeHistoryExport.js +27 -18
  122. package/src/server/conversation/normalize.js +9 -0
  123. package/src/server/conversation/openCodeClient.js +295 -366
  124. package/src/server/conversation/openCodeNativeHistory.js +141 -0
  125. package/src/server/conversation/openCodeNativeSession.js +79 -0
  126. package/src/server/conversation/openCodeProcess.js +109 -53
  127. package/src/server/conversation/openCodeProcessOwner.js +565 -0
  128. package/src/server/conversation/openCodeRuntime.js +111 -1814
  129. package/src/server/conversation/openCodeSessionTurns.js +547 -0
  130. package/src/server/conversation/openCodeTemporaryConversations.js +394 -0
  131. package/src/server/conversation/openCodeTools.js +28 -14
  132. package/src/server/conversation/openCodeTurn.js +80 -74
  133. package/src/server/conversation/openCodeTurnMonitor.js +388 -0
  134. package/src/server/conversation/processIdentity.js +582 -0
  135. package/src/server/conversation/provider.js +21 -6
  136. package/src/server/conversation/providers/api.js +268 -107
  137. package/src/server/conversation/providers/claude.js +29 -6
  138. package/src/server/conversation/providers/claudeDriver.js +325 -250
  139. package/src/server/conversation/providers/codex.js +69 -33
  140. package/src/server/conversation/providers/codexDriver.js +503 -1081
  141. package/src/server/conversation/providers/codexDriverConnection.js +331 -0
  142. package/src/server/conversation/providers/codexDriverTurns.js +464 -0
  143. package/src/server/conversation/providers/opencode.js +30 -4
  144. package/src/server/conversation/providers/opencodeDriver.js +321 -659
  145. package/src/server/conversation/providers/opencodeStandalone.js +475 -0
  146. package/src/server/conversation/providers/scoped.js +64 -19
  147. package/src/server/conversation/runtime.js +171 -2863
  148. package/src/server/conversation/storage.js +91 -50
  149. package/src/server/conversation/streams.js +15 -2
  150. package/src/server/conversation/structuredOutput.js +7 -11
  151. package/src/server/conversation/systemPrompt.js +13 -1
  152. package/src/server/conversation/tools.js +47 -15
  153. package/src/server/conversation/transcript.js +49 -20
  154. package/src/server/hosts/claudeTurn.js +2 -4
  155. package/src/server/hosts/codexConfiguration.js +6 -8
  156. package/src/server/hosts/codexEvents.js +4 -2
  157. package/src/server/hosts/codexProcess.js +1 -1
  158. package/src/server/hosts/codexProvider.js +11 -9
  159. package/src/server/hosts/codexTurn.js +3 -3
  160. package/src/server/hosts/openCodeClient.js +4 -2
  161. package/src/server/index.js +0 -2
  162. package/src/server/lib/aiConnectionClient.js +35 -25
  163. package/src/server/lib/ndjson.js +1 -0
  164. package/src/server/lib/providers/anthropicClient.js +15 -6
  165. package/src/server/lib/providers/common.js +10 -6
  166. package/src/server/lib/providers/openAiCompatibleClient.js +40 -23
  167. package/src/server/lib/serviceToolCatalog.js +29 -283
  168. package/src/server/lib/serviceToolEntries.js +270 -0
  169. package/src/server/types.js +78 -0
  170. package/src/shared/assistantResource.js +0 -23
  171. package/src/shared/conversation/answerChoiceSugar.js +2 -14
  172. package/src/shared/conversation/messageInputSugar.js +18 -0
  173. package/src/shared/conversation/numberedQuestionSugar.js +6 -16
  174. package/src/shared/conversation/pagination.js +1 -1
  175. package/src/shared/conversation/questions.js +1 -0
  176. package/src/shared/conversation/streaming.js +1 -0
  177. package/src/shared/index.js +0 -1
  178. package/src/testing/conversationStorageContract.js +20 -0
  179. package/test/aiConnectionClient.test.js +42 -1
  180. package/test/apiConversationDriver.test.js +217 -38
  181. package/test/assistantBoundedToolLoop.test.js +1 -1
  182. package/test/claudeConversationSession.test.js +15 -11
  183. package/test/claudeInstructionResume.test.js +1 -1
  184. package/test/claudeProcess.test.js +9 -9
  185. package/test/claudeTurn.test.js +21 -8
  186. package/test/codexAccountChange.test.js +5 -3
  187. package/test/codexConversationSession.test.js +22 -21
  188. package/test/codexEvents.test.js +3 -1
  189. package/test/codexHelperDeletion.test.js +3 -7
  190. package/test/codexIsolation.test.js +6 -6
  191. package/test/codexModelCatalog.test.js +6 -7
  192. package/test/codexProcess.test.js +3 -3
  193. package/test/codexThreadSelection.test.js +4 -1
  194. package/test/codexTurn.test.js +29 -20
  195. package/test/conversationApiFacilities.test.js +11 -9
  196. package/test/conversationAttachments.test.js +3 -3
  197. package/test/conversationConfiguration.test.js +4 -4
  198. package/test/conversationNativeStorage.test.js +11 -10
  199. package/test/conversationReplacement.test.js +13 -13
  200. package/test/conversationRuntime.test.js +9 -5
  201. package/test/conversationSession.test.js +78 -22
  202. package/test/conversationTools.test.js +47 -26
  203. package/test/fileConversationStorage.test.js +10 -3
  204. package/test/fixtures/codexHelperThreadLedger.js +3 -3
  205. package/test/frameworkActions.test.js +33 -18
  206. package/test/localConversationExecution.test.js +4 -4
  207. package/test/localExecution.test.js +1 -1
  208. package/test/openCodeConnectionChange.test.js +3 -2
  209. package/test/openCodeRuntime.test.js +8 -7
  210. package/test/openCodeTools.test.js +1 -1
  211. package/test/opencodeConversationSession.test.js +4 -3
  212. package/test/serviceToolCatalog.test.js +3 -8
  213. package/src/server/lib/assistantCompletion.js +0 -190
  214. package/src/server/lib/assistantToolLoop.js +0 -259
@@ -0,0 +1,431 @@
1
+ import WebSocket from "ws";
2
+ import { readFile, rm, stat } from "node:fs/promises";
3
+ import { randomUUID } from "node:crypto";
4
+ import path from "node:path";
5
+ import { isRecord } from "@jskit-ai/kernel/shared/support/normalize";
6
+ import { normalizeNullableText } from "./normalize.js";
7
+ import { copyCodexProviderHome } from "./codexConfiguration.js";
8
+ import { codexDisabledProcessFeatureArguments } from "./codexHomeStorage.js";
9
+ import {
10
+ CODEX_APP_SERVER_DESKTOP_BUS_ENV_NAMES, CODEX_APP_SERVER_ENDPOINT_STATUS, CODEX_APP_SERVER_EXECUTION_MODES,
11
+ CODEX_APP_SERVER_LIVENESS_TIMEOUT_MS, CODEX_APP_SERVER_METADATA_SCHEMA_VERSION, CODEX_APP_SERVER_PROCESS_STATE,
12
+ CODEX_APP_SERVER_PROVIDER_ID, CODEX_APP_SERVER_READY_TIMEOUT_MS, CODEX_APP_SERVER_TRANSPORT,
13
+ CODEX_APP_SERVER_UNIX_SOCKET_PATH_MAX_BYTES, assertExistingDirectory, codexAppServerCommandBaseEnv,
14
+ codexAppServerCredentialHome, codexAppServerExecutionMode, codexAppServerHelperCommandBaseEnv,
15
+ codexAppServerHelperHomeDir, codexAppServerHelperWorkspaceDir, codexAppServerLogPath, codexAppServerProcessCommandHash,
16
+ codexAppServerProcessCwd, codexAppServerProjectTrustOverride, codexAppServerSocketPath, codexAppServerSocketPathBytes,
17
+ codexAppServerSocketPathTooLong, codexAppServerTerminalEnvHash, codexAppServerUnixEndpoint, codexCommandHookDefinition,
18
+ codexHistoryAdapterUrlIsValid, ensureWritablePrivateDirectory, fileExists, normalizeCodexAppServerTerminalEnv,
19
+ stableHash, tailTextFile
20
+ } from "./codexProcess.js";
21
+
22
+ // Inline TOML for `-c` overrides: bare keys, JSON-quoted strings.
23
+ function tomlInlineValue(value) {
24
+ if (Array.isArray(value)) {
25
+ return `[${value.map(tomlInlineValue).join(",")}]`;
26
+ }
27
+ if (isRecord(value)) {
28
+ return `{${Object.entries(value).map(([key, item]) => `${key}=${tomlInlineValue(item)}`).join(",")}}`;
29
+ }
30
+ return JSON.stringify(value);
31
+ }
32
+
33
+ /**
34
+ * Starts the native app-server process under the host's execution facility.
35
+ * @param {object} dependencies
36
+ * @param {import("./codexRuntime.js").CodexRuntimeExecution} dependencies.execution
37
+ * @param {import("./codexRuntime.js").CodexRuntimeCredentials} dependencies.credentials
38
+ * @param {import("./codexRuntime.js").CodexProcessIdentity} dependencies.codexProcessIdentity
39
+ * @param {import("./codexRuntimeStop.js").CodexRuntimeStop} dependencies.runtimeStop
40
+ * @param {(endpoint: string, options: object) => Promise<{ status: string }>} dependencies.codexAppServerEndpointStatus
41
+ * @param {(endpoint: string, options: object) => Promise<boolean>} dependencies.waitForCodexAppServer
42
+ * @param {import("./codexRuntime.js").CodexRuntimeSettings} dependencies.settings
43
+ */
44
+ export function createCodexAppServerProcessStart({
45
+ execution,
46
+ credentials,
47
+ codexProcessIdentity,
48
+ runtimeStop,
49
+ codexAppServerEndpointStatus,
50
+ waitForCodexAppServer,
51
+ settings
52
+ }) {
53
+ const {
54
+ commandHookCommand,
55
+ errorPrefix,
56
+ modelCatalogEnvironmentName,
57
+ processLabel,
58
+ processPath,
59
+ runtimeTokenEnvironmentName,
60
+ commandHashEnvironmentName,
61
+ socketPathHelp,
62
+ umask
63
+ } = settings;
64
+
65
+ function codexAppServerSessionCommandHookConfig() {
66
+ return `hooks.PreToolUse=${tomlInlineValue(codexCommandHookDefinition(commandHookCommand))}`;
67
+ }
68
+
69
+ // An empty or null umask inherits the host's own; hosts that share files set one.
70
+ const startupUmask = normalizeNullableText(umask);
71
+ if (startupUmask && !/^[0-7]{3,4}$/u.test(startupUmask)) {
72
+ throw new TypeError(`Codex app-server umask must be octal, not ${JSON.stringify(startupUmask)}.`);
73
+ }
74
+ const CODEX_APP_SERVER_UMASK_LINES = startupUmask ? [`umask ${startupUmask}`] : [];
75
+
76
+ const CODEX_APP_SERVER_MANAGED_SHELL = "/bin/sh";
77
+
78
+ const CODEX_APP_SERVER_MANAGED_STARTUP_SCRIPT = [
79
+ ...CODEX_APP_SERVER_UMASK_LINES,
80
+ `unset ${[...CODEX_APP_SERVER_DESKTOP_BUS_ENV_NAMES].join(" ")}`,
81
+ 'exec "$@"'
82
+ ].join("\n");
83
+
84
+ const CODEX_APP_SERVER_HELPER_STARTUP_SCRIPT = [
85
+ ...CODEX_APP_SERVER_UMASK_LINES,
86
+ "exec /usr/bin/env -i \\",
87
+ ' HOME="$HOME" LOGNAME="$LOGNAME" USER="$USER" PATH="$PATH" \\',
88
+ ' CODEX_HOME="$CODEX_HOME" LANG="$LANG" LC_ALL="$LC_ALL" LC_CTYPE="$LC_CTYPE" \\',
89
+ ` ${runtimeTokenEnvironmentName}="$${runtimeTokenEnvironmentName}" ${commandHashEnvironmentName}="$${commandHashEnvironmentName}" \\`,
90
+ ' SSL_CERT_DIR="$SSL_CERT_DIR" SSL_CERT_FILE="$SSL_CERT_FILE" TERM="$TERM" TMPDIR="$TMPDIR" TZ="$TZ" \\',
91
+ ' "$@"'
92
+ ].join("\n");
93
+
94
+ function assertCodexAppServerSocketPathSupported(socketPath = "") {
95
+ if (!codexAppServerSocketPathTooLong(socketPath)) {
96
+ return;
97
+ }
98
+ throw new Error(
99
+ `Codex app-server Unix socket path is too long for this OS: ${socketPath} ` +
100
+ `(${codexAppServerSocketPathBytes(socketPath)} bytes, max ${CODEX_APP_SERVER_UNIX_SOCKET_PATH_MAX_BYTES}). ` +
101
+ socketPathHelp
102
+ );
103
+ }
104
+
105
+ async function startCodexAppServerProcess({
106
+ providerHome = false,
107
+ accountIdentitySignature = "",
108
+ authStateSignature = "",
109
+ codexCommand = "codex",
110
+ env,
111
+ executionRoot = "",
112
+ executionMode = CODEX_APP_SERVER_EXECUTION_MODES.INTERACTIVE,
113
+ killTimeoutMs = 0,
114
+ processIdentityInspector,
115
+ readyTimeoutMs = CODEX_APP_SERVER_READY_TIMEOUT_MS,
116
+ signalProcessGroup,
117
+ stopExecution,
118
+ terminalEnv = {},
119
+ termTimeoutMs = 0,
120
+ toolHomeSource = "",
121
+ WebSocketImpl = WebSocket,
122
+ workdir = "",
123
+ socketOwnerDrained = false,
124
+ runtimeDir,
125
+ ownerId = "",
126
+ compatibility
127
+ }) {
128
+ if (!env) throw new TypeError("The Codex app-server runtime start requires the application's environment, such as process.env.");
129
+ const processLifecycleOptions = {
130
+ killTimeoutMs,
131
+ processIdentityInspector,
132
+ signalProcessGroup,
133
+ stopExecution,
134
+ termTimeoutMs
135
+ };
136
+ await ensureWritablePrivateDirectory(runtimeDir);
137
+ const helper = codexAppServerExecutionMode({ executionMode }) ===
138
+ CODEX_APP_SERVER_EXECUTION_MODES.HELPER;
139
+ const normalizedToolHomeSource = normalizeNullableText(toolHomeSource);
140
+ if (normalizedToolHomeSource) {
141
+ await assertExistingDirectory(normalizedToolHomeSource, "Codex credential home");
142
+ }
143
+ const resolvedAuthStateSignature = await credentials.generation({ authStateSignature });
144
+ await credentials.assertCurrent(resolvedAuthStateSignature);
145
+ const socketPath = codexAppServerSocketPath(runtimeDir);
146
+ assertCodexAppServerSocketPathSupported(socketPath);
147
+ const endpoint = codexAppServerUnixEndpoint(socketPath);
148
+ const logPath = codexAppServerLogPath(runtimeDir);
149
+ const helperHome = helper ? codexAppServerHelperHomeDir(runtimeDir) : "";
150
+ const helperWorkspace = helper ? codexAppServerHelperWorkspaceDir(runtimeDir) : "";
151
+ if (helper) {
152
+ await Promise.all([
153
+ rm(helperHome, { force: true, recursive: true }),
154
+ rm(helperWorkspace, { force: true, recursive: true })
155
+ ]);
156
+ await Promise.all([
157
+ ensureWritablePrivateDirectory(helperHome),
158
+ ensureWritablePrivateDirectory(helperWorkspace)
159
+ ]);
160
+ if (providerHome) {
161
+ // The canonical home contains only our curated provider projection. Copy
162
+ // it into the existing isolated helper home; never load OpenAI auth here.
163
+ try {
164
+ await copyCodexProviderHome(path.join(normalizedToolHomeSource, ".codex"), helperHome);
165
+ } catch (cause) {
166
+ const error = new Error("Codex isolated helper runtime could not copy the provider home.", { cause });
167
+ error.code = `${errorPrefix}codex_helper_runtime_start_failed`;
168
+ throw error;
169
+ }
170
+ }
171
+ }
172
+ const processCwd = helper
173
+ ? helperWorkspace
174
+ : codexAppServerProcessCwd({
175
+ executionRoot,
176
+ runtimeDir,
177
+ workdir
178
+ });
179
+ const projectTrustOverride = helper ? "" : codexAppServerProjectTrustOverride(workdir);
180
+ const normalizedTerminalEnv = normalizeCodexAppServerTerminalEnv(helper ? {} : terminalEnv);
181
+ const commandBaseEnv = helper
182
+ ? codexAppServerHelperCommandBaseEnv(env, helperHome)
183
+ : codexAppServerCommandBaseEnv({
184
+ env,
185
+ terminalEnv: normalizedTerminalEnv
186
+ });
187
+ if (await fileExists(socketPath) && !socketOwnerDrained) {
188
+ const endpointStatus = await codexAppServerEndpointStatus(endpoint, {
189
+ timeoutMs: CODEX_APP_SERVER_LIVENESS_TIMEOUT_MS,
190
+ WebSocketImpl
191
+ });
192
+ if (![CODEX_APP_SERVER_ENDPOINT_STATUS.MISSING, CODEX_APP_SERVER_ENDPOINT_STATUS.UNREACHABLE]
193
+ .includes(endpointStatus.status)) {
194
+ const error = new Error("The existing Codex app-server socket still has an unretired owner.");
195
+ error.code = `${errorPrefix}codex_app_server_socket_owner_unverified`;
196
+ error.retryable = false;
197
+ throw error;
198
+ }
199
+ }
200
+ await rm(socketPath, {
201
+ force: true
202
+ });
203
+ const codexArgs = [
204
+ // Keep every override after app-server: a trailing -c (including the model
205
+ // catalogue) otherwise drops the earlier ones. See openai/codex#39012.
206
+ "app-server",
207
+ "--listen",
208
+ endpoint,
209
+ // App-server uses config overrides, not the top-level sandbox bypass flag.
210
+ ...(helper ? [] : [
211
+ "-c",
212
+ 'approval_policy="never"',
213
+ "-c",
214
+ 'sandbox_mode="danger-full-access"',
215
+ "-c",
216
+ `features.hooks=${Boolean(commandHookCommand)}`,
217
+ ...(commandHookCommand ? ["-c", codexAppServerSessionCommandHookConfig()] : [])
218
+ ]),
219
+ "-c",
220
+ "check_for_update_on_startup=false",
221
+ // Plugin and app startup syncs fill the home with unused catalogues.
222
+ ...codexDisabledProcessFeatureArguments(),
223
+ ...(projectTrustOverride
224
+ ? [
225
+ "-c",
226
+ projectTrustOverride
227
+ ]
228
+ : [])
229
+ ];
230
+ const runtimeToken = randomUUID();
231
+ const commandHash = codexAppServerProcessCommandHash({
232
+ codexArgs,
233
+ codexCommand,
234
+ executionMode,
235
+ processCwd,
236
+ runtimeDir
237
+ });
238
+ const baseEnv = {
239
+ ...commandBaseEnv,
240
+ // A curated provider home contains a provider-only static catalogue. Use
241
+ // the binary's native definitions as the base for its shared runtime.
242
+ [modelCatalogEnvironmentName]: providerHome ? "bundled" : "native",
243
+ [commandHashEnvironmentName]: commandHash,
244
+ [runtimeTokenEnvironmentName]: runtimeToken
245
+ };
246
+ const profileBinary = path.isAbsolute(codexCommand) ? await stat(codexCommand).catch(() => null) : null;
247
+ const startResult = await execution.run({
248
+ args: [
249
+ "-c",
250
+ helper ? CODEX_APP_SERVER_HELPER_STARTUP_SCRIPT : CODEX_APP_SERVER_MANAGED_STARTUP_SCRIPT,
251
+ processLabel,
252
+ ...(helper ? [] : [process.execPath, processPath, runtimeDir]),
253
+ codexCommand,
254
+ ...codexArgs
255
+ ],
256
+ baseEnv,
257
+ command: CODEX_APP_SERVER_MANAGED_SHELL,
258
+ credentialHome: codexAppServerCredentialHome(
259
+ helper ? helperHome : normalizedToolHomeSource,
260
+ baseEnv
261
+ ),
262
+ cwd: processCwd || process.cwd(),
263
+ execution: {
264
+ kind: "assistant",
265
+ label: "Codex assistant",
266
+ lifecycle: "service",
267
+ operationId: "codex-app-server",
268
+ ownerId: normalizeNullableText(ownerId) || stableHash(runtimeDir)
269
+ },
270
+ processProfile: profileBinary?.isFile() ? {
271
+ command: codexCommand, helper, executionMode,
272
+ binary: [profileBinary.dev, profileBinary.ino, profileBinary.size, profileBinary.mtimeMs, profileBinary.ctimeMs],
273
+ platform: process.platform, architecture: process.arch,
274
+ startup: helper ? CODEX_APP_SERVER_HELPER_STARTUP_SCRIPT : CODEX_APP_SERVER_MANAGED_STARTUP_SCRIPT,
275
+ historyAdapter: !helper
276
+ } : null,
277
+ logPath,
278
+ inheritProcessEnv: false,
279
+ mode: "detached",
280
+ timeout: readyTimeoutMs
281
+ });
282
+ if (!startResult.ok) {
283
+ const failedProcessIdentity = await codexProcessIdentity.capture({
284
+ commandHash,
285
+ pid: startResult.pid,
286
+ reportedIdentity: startResult.processIdentity,
287
+ runtimeToken
288
+ }).catch(() => null);
289
+ const cleanup = await runtimeStop.cleanupFailedStart(runtimeDir, {
290
+ helper,
291
+ executionId: startResult.execution?.id,
292
+ neverStarted: !Number.isSafeInteger(Number(startResult.pid)),
293
+ pid: startResult.pid,
294
+ processIdentity: failedProcessIdentity
295
+ }, processLifecycleOptions);
296
+ const error = new Error(
297
+ startResult.output || startResult.error ||
298
+ (helper ? "Codex isolated helper runtime failed to start." : "Codex app-server failed to start.")
299
+ );
300
+ error.code = cleanup.cleanupFailed
301
+ ? (helper
302
+ ? `${errorPrefix}codex_helper_runtime_cleanup_required`
303
+ : `${errorPrefix}codex_app_server_cleanup_required`)
304
+ : (startResult.code || (helper
305
+ ? `${errorPrefix}codex_helper_runtime_start_failed`
306
+ : `${errorPrefix}codex_app_server_start_failed`));
307
+ error.cleanupRequired = cleanup.cleanupFailed;
308
+ error.execution = startResult.execution;
309
+ error.retryable = cleanup.cleanupFailed ? false : startResult.retryable === true;
310
+ throw error;
311
+ }
312
+
313
+ let processIdentity;
314
+ try {
315
+ processIdentity = await codexProcessIdentity.capture({
316
+ commandHash,
317
+ pid: startResult.pid,
318
+ reportedIdentity: startResult.processIdentity,
319
+ runtimeToken
320
+ });
321
+ } catch (cause) {
322
+ const cleanup = await runtimeStop.cleanupFailedStart(runtimeDir, {
323
+ helper,
324
+ executionId: startResult.execution?.id,
325
+ pid: startResult.pid,
326
+ processIdentity: startResult.processIdentity
327
+ }, processLifecycleOptions);
328
+ const error = new Error("Codex app-server process ownership could not be recorded.", {
329
+ cause
330
+ });
331
+ error.code = cleanup.cleanupFailed
332
+ ? `${errorPrefix}codex_app_server_cleanup_required`
333
+ : `${errorPrefix}codex_app_server_process_identity_unavailable`;
334
+ error.cleanupRequired = cleanup.cleanupFailed;
335
+ throw error;
336
+ }
337
+
338
+ const ready = await waitForCodexAppServer(endpoint, {
339
+ timeoutMs: readyTimeoutMs,
340
+ WebSocketImpl
341
+ });
342
+ if (!ready) {
343
+ const logTail = await tailTextFile(logPath);
344
+ const cleanup = await runtimeStop.cleanupFailedStart(runtimeDir, {
345
+ helper,
346
+ executionId: startResult.execution?.id,
347
+ pid: startResult.pid,
348
+ processIdentity
349
+ }, processLifecycleOptions);
350
+ if (!cleanup.cleanupFailed && !helper) {
351
+ await credentials.observeFailure(logTail);
352
+ }
353
+ const error = new Error([
354
+ helper
355
+ ? "Codex isolated helper runtime did not become ready."
356
+ : `Codex app-server did not become ready at ${endpoint}.`,
357
+ logTail ? `Recent log output:\n${logTail}` : ""
358
+ ].filter(Boolean).join("\n"));
359
+ error.code = cleanup.cleanupFailed
360
+ ? (helper
361
+ ? `${errorPrefix}codex_helper_runtime_cleanup_required`
362
+ : `${errorPrefix}codex_app_server_cleanup_required`)
363
+ : (helper
364
+ ? `${errorPrefix}codex_helper_runtime_start_failed`
365
+ : `${errorPrefix}codex_app_server_ready_timeout`);
366
+ error.cleanupRequired = cleanup.cleanupFailed;
367
+ error.retryable = false;
368
+ throw error;
369
+ }
370
+
371
+ let historyAdapterBaseUrl = "";
372
+ try {
373
+ if (!helper) {
374
+ const descriptor = JSON.parse(await readFile(path.join(runtimeDir, "history-adapter.json"), "utf8"));
375
+ if (descriptor.runtimeToken !== runtimeToken || !codexHistoryAdapterUrlIsValid(descriptor.baseUrl, runtimeToken)) {
376
+ throw new Error("Codex history adapter did not confirm the current runtime.");
377
+ }
378
+ historyAdapterBaseUrl = descriptor.baseUrl;
379
+ }
380
+ await credentials.assertCurrent(resolvedAuthStateSignature);
381
+ } catch (cause) {
382
+ const cleanup = await runtimeStop.cleanupFailedStart(runtimeDir, {
383
+ helper,
384
+ executionId: startResult.execution?.id,
385
+ pid: startResult.pid,
386
+ processIdentity
387
+ }, processLifecycleOptions);
388
+ if (cleanup.cleanupFailed) {
389
+ const error = new Error("Codex app-server authentication changed and its execution could not be drained.", {
390
+ cause
391
+ });
392
+ error.code = `${errorPrefix}codex_app_server_cleanup_required`;
393
+ error.cleanupRequired = true;
394
+ error.retryable = false;
395
+ throw error;
396
+ }
397
+ throw cause;
398
+ }
399
+
400
+ return {
401
+ accountIdentitySignature: normalizeNullableText(accountIdentitySignature),
402
+ attachmentHostRoot: compatibility.attachmentHostRoot,
403
+ authStateSignature: resolvedAuthStateSignature,
404
+ endpoint,
405
+ executionId: normalizeNullableText(startResult.execution?.id),
406
+ executionMode: helper
407
+ ? CODEX_APP_SERVER_EXECUTION_MODES.HELPER
408
+ : CODEX_APP_SERVER_EXECUTION_MODES.INTERACTIVE,
409
+ executionContextHash: compatibility.executionContextHash,
410
+ healthz: "",
411
+ historyAdapterBaseUrl,
412
+ logPath,
413
+ pid: Number.isSafeInteger(Number(startResult.pid)) ? Number(startResult.pid) : null,
414
+ processCwd,
415
+ processIdentity,
416
+ processState: CODEX_APP_SERVER_PROCESS_STATE.RUNNING,
417
+ provider: CODEX_APP_SERVER_PROVIDER_ID,
418
+ readyz: "",
419
+ runtimeDir,
420
+ runtimesHash: compatibility.runtimesHash,
421
+ schemaVersion: CODEX_APP_SERVER_METADATA_SCHEMA_VERSION,
422
+ socketPath,
423
+ startedAt: new Date().toISOString(),
424
+ terminalEnvHash: codexAppServerTerminalEnvHash(normalizedTerminalEnv),
425
+ toolHomeSource: helper ? "" : normalizedToolHomeSource,
426
+ transport: CODEX_APP_SERVER_TRANSPORT.UNIX
427
+ };
428
+ }
429
+
430
+ return Object.freeze({ start: startCodexAppServerProcess });
431
+ }