@jskit-ai/assistant-core 0.2.7 → 0.2.8
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.
- package/examples/conversation/package.json +5 -5
- package/package.json +18 -7
- package/src/client/browser.js +21 -0
- package/src/client/conversation/AssistantAttachmentQueue.vue +25 -108
- package/src/client/conversation/AssistantComposerActions.vue +1 -1
- package/src/client/conversation/AssistantConversationElement.vue +14 -52
- package/src/client/conversation/AssistantGoalControl.vue +13 -10
- package/src/client/conversation/AssistantPromptInput.vue +6 -91
- package/src/client/conversation/AssistantTranscript.css +505 -0
- package/src/client/conversation/AssistantTranscript.vue +24 -1197
- package/src/client/conversation/attachmentFiles.js +158 -0
- package/src/client/conversation/attachmentQueueItems.js +121 -0
- package/src/client/conversation/attachmentUploadRows.js +265 -0
- package/src/client/conversation/conversationDisplay.js +54 -0
- package/src/client/conversation/messageDelivery.js +13 -9
- package/src/client/conversation/retainedConversation.js +14 -3
- package/src/client/conversation/submitText.js +13 -2
- package/src/client/conversation/transcriptEntries.js +225 -0
- package/src/client/conversation/useAssistantAttachments.js +86 -388
- package/src/client/conversation/useAssistantQuestions.js +16 -3
- package/src/client/conversation/useAssistantSuggestions.js +16 -2
- package/src/client/conversation/useScrollToBottom.js +17 -19
- package/src/client/conversation/useTextareaAutoGrow.js +115 -0
- package/src/client/conversation/useTranscriptScrollFollow.js +525 -0
- package/src/client/lib/assistantApi.js +192 -161
- package/src/client/types.js +110 -0
- package/src/server/conversation/attachments.js +21 -6
- package/src/server/conversation/boundedToolLoop.js +1 -0
- package/src/server/conversation/claudeClient.js +71 -20
- package/src/server/conversation/claudeCommandRun.js +191 -0
- package/src/server/conversation/claudeHistory.js +7 -0
- package/src/server/conversation/claudeNativeTurn.js +708 -0
- package/src/server/conversation/claudeProcess.js +186 -112
- package/src/server/conversation/claudeProcessStart.js +214 -0
- package/src/server/conversation/claudePromptDelivery.js +183 -0
- package/src/server/conversation/claudeSessionCleanup.js +248 -0
- package/src/server/conversation/claudeTools.js +15 -3
- package/src/server/conversation/claudeTurn.js +212 -1388
- package/src/server/conversation/codexAccountReader.js +168 -0
- package/src/server/conversation/codexAuthPreflight.js +5 -4
- package/src/server/conversation/codexClient.js +18 -6
- package/src/server/conversation/codexConfiguration.js +8 -844
- package/src/server/conversation/codexConversationStops.js +377 -0
- package/src/server/conversation/codexConversationStore.js +119 -85
- package/src/server/conversation/codexConversationTurns.js +378 -0
- package/src/server/conversation/codexConversations.js +134 -866
- package/src/server/conversation/codexDetachedConversation.js +203 -0
- package/src/server/conversation/codexDetachedTurn.js +145 -104
- package/src/server/conversation/codexEvents.js +5 -200
- package/src/server/conversation/codexGoalCommands.js +14 -4
- package/src/server/conversation/codexHelperExecution.js +57 -57
- package/src/server/conversation/codexHelperThreadLedger.js +30 -288
- package/src/server/conversation/codexHelperThreadRecord.js +304 -0
- package/src/server/conversation/codexHelperThreadRecovery.js +54 -76
- package/src/server/conversation/codexHelperThreads.js +94 -93
- package/src/server/conversation/codexHistoryAdapter.js +13 -8
- package/src/server/conversation/codexIsolation.js +672 -0
- package/src/server/conversation/codexLiveItems.js +379 -0
- package/src/server/conversation/codexMessageCommands.js +68 -286
- package/src/server/conversation/codexMessageDelivery.js +46 -37
- package/src/server/conversation/codexModelCatalog.js +86 -7
- package/src/server/conversation/codexNotificationObserver.js +81 -88
- package/src/server/conversation/codexNotificationQueue.js +21 -10
- package/src/server/conversation/codexObservationControl.js +105 -89
- package/src/server/conversation/codexOutput.js +84 -419
- package/src/server/conversation/codexProcess.js +29 -1053
- package/src/server/conversation/codexProcessLeader.js +1 -0
- package/src/server/conversation/codexPromptDispatch.js +241 -0
- package/src/server/conversation/codexProvider.js +147 -1807
- package/src/server/conversation/codexProviderAuth.js +348 -0
- package/src/server/conversation/codexProviderInvalidation.js +371 -0
- package/src/server/conversation/codexProviderInventories.js +437 -0
- package/src/server/conversation/codexProviderOwner.js +149 -957
- package/src/server/conversation/codexProviderRelease.js +465 -0
- package/src/server/conversation/codexProviderSelection.js +26 -15
- package/src/server/conversation/codexProviderThreads.js +698 -0
- package/src/server/conversation/codexProviderTurns.js +453 -0
- package/src/server/conversation/codexReasoning.js +32 -23
- package/src/server/conversation/codexRenewalCommands.js +18 -6
- package/src/server/conversation/codexRenewalThreads.js +7 -22
- package/src/server/conversation/codexResultSettlement.js +70 -88
- package/src/server/conversation/codexRunJournal.js +91 -97
- package/src/server/conversation/codexRuntime.js +426 -0
- package/src/server/conversation/codexRuntimeInventory.js +113 -0
- package/src/server/conversation/codexRuntimeLifecycle.js +39 -13
- package/src/server/conversation/codexRuntimeStart.js +431 -0
- package/src/server/conversation/codexRuntimeStop.js +324 -0
- package/src/server/conversation/codexSessionCleanup.js +25 -10
- package/src/server/conversation/codexSessionReconciliation.js +21 -7
- package/src/server/conversation/codexThreadReadiness.js +49 -52
- package/src/server/conversation/codexThreadSnapshot.js +210 -0
- package/src/server/conversation/codexThreadStatus.js +48 -62
- package/src/server/conversation/codexTurn.js +171 -55
- package/src/server/conversation/codexTurnActivity.js +210 -0
- package/src/server/conversation/codexTurnControl.js +44 -48
- package/src/server/conversation/codexTurnEndings.js +610 -0
- package/src/server/conversation/codexTurnRecovery.js +97 -848
- package/src/server/conversation/codexTurnState.js +138 -111
- package/src/server/conversation/completedResponses.js +227 -0
- package/src/server/conversation/configuration.js +2 -2
- package/src/server/conversation/continuity.js +43 -6
- package/src/server/conversation/conversationDelivery.js +394 -0
- package/src/server/conversation/conversationEntries.js +365 -0
- package/src/server/conversation/conversationEvents.js +192 -0
- package/src/server/conversation/conversationGoals.js +187 -0
- package/src/server/conversation/conversationHandles.js +237 -0
- package/src/server/conversation/conversationReads.js +206 -0
- package/src/server/conversation/conversationRefusals.js +58 -0
- package/src/server/conversation/conversationReplacement.js +254 -0
- package/src/server/conversation/conversationSending.js +273 -0
- package/src/server/conversation/conversationStopping.js +121 -0
- package/src/server/conversation/conversationTurnDelivery.js +413 -0
- package/src/server/conversation/conversationTurns.js +511 -0
- package/src/server/conversation/deliveryTiming.js +25 -3
- package/src/server/conversation/fileStorage.js +11 -6
- package/src/server/conversation/hookBridge.js +14 -3
- package/src/server/conversation/index.js +1 -1
- package/src/server/conversation/localExecution.js +82 -625
- package/src/server/conversation/nativeCommandCallbacks.js +10 -0
- package/src/server/conversation/nativeConversationLifecycle.js +379 -0
- package/src/server/conversation/nativeHistoryExport.js +27 -18
- package/src/server/conversation/normalize.js +9 -0
- package/src/server/conversation/openCodeClient.js +295 -366
- package/src/server/conversation/openCodeNativeHistory.js +141 -0
- package/src/server/conversation/openCodeNativeSession.js +79 -0
- package/src/server/conversation/openCodeProcess.js +109 -53
- package/src/server/conversation/openCodeProcessOwner.js +565 -0
- package/src/server/conversation/openCodeRuntime.js +111 -1814
- package/src/server/conversation/openCodeSessionTurns.js +547 -0
- package/src/server/conversation/openCodeTemporaryConversations.js +394 -0
- package/src/server/conversation/openCodeTools.js +28 -14
- package/src/server/conversation/openCodeTurn.js +80 -74
- package/src/server/conversation/openCodeTurnMonitor.js +388 -0
- package/src/server/conversation/processIdentity.js +582 -0
- package/src/server/conversation/provider.js +21 -6
- package/src/server/conversation/providers/api.js +268 -107
- package/src/server/conversation/providers/claude.js +29 -6
- package/src/server/conversation/providers/claudeDriver.js +325 -250
- package/src/server/conversation/providers/codex.js +69 -33
- package/src/server/conversation/providers/codexDriver.js +503 -1081
- package/src/server/conversation/providers/codexDriverConnection.js +331 -0
- package/src/server/conversation/providers/codexDriverTurns.js +464 -0
- package/src/server/conversation/providers/opencode.js +30 -4
- package/src/server/conversation/providers/opencodeDriver.js +321 -659
- package/src/server/conversation/providers/opencodeStandalone.js +475 -0
- package/src/server/conversation/providers/scoped.js +64 -19
- package/src/server/conversation/runtime.js +171 -2863
- package/src/server/conversation/storage.js +91 -50
- package/src/server/conversation/streams.js +15 -2
- package/src/server/conversation/structuredOutput.js +7 -11
- package/src/server/conversation/systemPrompt.js +13 -1
- package/src/server/conversation/tools.js +47 -15
- package/src/server/conversation/transcript.js +49 -20
- package/src/server/hosts/claudeTurn.js +2 -4
- package/src/server/hosts/codexConfiguration.js +6 -8
- package/src/server/hosts/codexEvents.js +4 -2
- package/src/server/hosts/codexProcess.js +1 -1
- package/src/server/hosts/codexProvider.js +11 -9
- package/src/server/hosts/codexTurn.js +3 -3
- package/src/server/hosts/openCodeClient.js +4 -2
- package/src/server/index.js +0 -2
- package/src/server/lib/aiConnectionClient.js +35 -25
- package/src/server/lib/ndjson.js +1 -0
- package/src/server/lib/providers/anthropicClient.js +15 -6
- package/src/server/lib/providers/common.js +10 -6
- package/src/server/lib/providers/openAiCompatibleClient.js +40 -23
- package/src/server/lib/serviceToolCatalog.js +29 -283
- package/src/server/lib/serviceToolEntries.js +270 -0
- package/src/server/types.js +78 -0
- package/src/shared/assistantResource.js +0 -23
- package/src/shared/conversation/answerChoiceSugar.js +2 -14
- package/src/shared/conversation/messageInputSugar.js +18 -0
- package/src/shared/conversation/numberedQuestionSugar.js +6 -16
- package/src/shared/conversation/pagination.js +1 -1
- package/src/shared/conversation/questions.js +1 -0
- package/src/shared/conversation/streaming.js +1 -0
- package/src/shared/index.js +0 -1
- package/src/testing/conversationStorageContract.js +20 -0
- package/test/aiConnectionClient.test.js +42 -1
- package/test/apiConversationDriver.test.js +217 -38
- package/test/assistantBoundedToolLoop.test.js +1 -1
- package/test/claudeConversationSession.test.js +15 -11
- package/test/claudeInstructionResume.test.js +1 -1
- package/test/claudeProcess.test.js +9 -9
- package/test/claudeTurn.test.js +21 -8
- package/test/codexAccountChange.test.js +5 -3
- package/test/codexConversationSession.test.js +22 -21
- package/test/codexEvents.test.js +3 -1
- package/test/codexHelperDeletion.test.js +3 -7
- package/test/codexIsolation.test.js +6 -6
- package/test/codexModelCatalog.test.js +6 -7
- package/test/codexProcess.test.js +3 -3
- package/test/codexThreadSelection.test.js +4 -1
- package/test/codexTurn.test.js +29 -20
- package/test/conversationApiFacilities.test.js +11 -9
- package/test/conversationAttachments.test.js +3 -3
- package/test/conversationConfiguration.test.js +4 -4
- package/test/conversationNativeStorage.test.js +11 -10
- package/test/conversationReplacement.test.js +13 -13
- package/test/conversationRuntime.test.js +9 -5
- package/test/conversationSession.test.js +78 -22
- package/test/conversationTools.test.js +47 -26
- package/test/fileConversationStorage.test.js +10 -3
- package/test/fixtures/codexHelperThreadLedger.js +3 -3
- package/test/frameworkActions.test.js +33 -18
- package/test/localConversationExecution.test.js +4 -4
- package/test/localExecution.test.js +1 -1
- package/test/openCodeConnectionChange.test.js +3 -2
- package/test/openCodeRuntime.test.js +8 -7
- package/test/openCodeTools.test.js +1 -1
- package/test/opencodeConversationSession.test.js +4 -3
- package/test/serviceToolCatalog.test.js +3 -8
- package/src/server/lib/assistantCompletion.js +0 -190
- 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
|
+
}
|