@jskit-ai/assistant-core 0.2.6 → 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 +18 -12
- 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,324 @@
|
|
|
1
|
+
import { rm } from "node:fs/promises";
|
|
2
|
+
import { normalizeNullableText } from "./normalize.js";
|
|
3
|
+
import {
|
|
4
|
+
CODEX_APP_SERVER_METADATA_SCHEMA_VERSION, CODEX_APP_SERVER_PROCESS_IDENTITY_STATUS, CODEX_APP_SERVER_PROCESS_STATE,
|
|
5
|
+
CODEX_APP_SERVER_PROVIDER_ID, acquireCodexRuntimeLock, codexAppServerHelperHomeDir, codexAppServerHelperWorkspaceDir,
|
|
6
|
+
codexAppServerLogPath, codexAppServerMetadataPath, codexAppServerRuntimeCleanupCanSkip, codexAppServerRuntimeDirIsManaged,
|
|
7
|
+
codexAppServerRuntimeIdentity, codexAppServerSocketPath, readCodexAppServerMetadata, removeCodexAppServerMetadataTemps,
|
|
8
|
+
removeCodexAppServerRuntimeDir, stableHash, writeCodexAppServerMetadata
|
|
9
|
+
} from "./codexProcess.js";
|
|
10
|
+
|
|
11
|
+
/** @typedef {ReturnType<typeof createCodexAppServerRuntimeStop>} CodexRuntimeStop */
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Stops a native runtime's owned process and removes what it left behind.
|
|
15
|
+
* @param {object} dependencies
|
|
16
|
+
* @param {import("./codexRuntime.js").CodexRuntimeExecution} dependencies.execution
|
|
17
|
+
* @param {import("./codexRuntime.js").CodexProcessIdentity} dependencies.codexProcessIdentity
|
|
18
|
+
* @param {string} dependencies.errorPrefix
|
|
19
|
+
*/
|
|
20
|
+
export function createCodexAppServerRuntimeStop({ execution, codexProcessIdentity, errorPrefix }) {
|
|
21
|
+
|
|
22
|
+
async function acquireRuntimeLock(runtimeDir = "", options = {}) {
|
|
23
|
+
// readable-check: allow optional-call - optional operation: managed execution hosts add prepare; the local one has none
|
|
24
|
+
await execution.prepare?.();
|
|
25
|
+
return acquireCodexRuntimeLock(runtimeDir, { ...options, busyCode: `${errorPrefix}codex_app_server_runtime_busy` });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function codexAppServerProcessMetadataIsIdentifiable(metadata = {}, runtimeDir = "") {
|
|
29
|
+
const normalizedRuntimeDir = normalizeNullableText(runtimeDir);
|
|
30
|
+
return Boolean(
|
|
31
|
+
Number(metadata.schemaVersion) === CODEX_APP_SERVER_METADATA_SCHEMA_VERSION &&
|
|
32
|
+
metadata.provider === CODEX_APP_SERVER_PROVIDER_ID &&
|
|
33
|
+
Number.isSafeInteger(Number(metadata.pid)) && Number(metadata.pid) > 0 &&
|
|
34
|
+
(!normalizedRuntimeDir || metadata.runtimeDir === normalizedRuntimeDir) &&
|
|
35
|
+
codexProcessIdentity.isValid(metadata.processIdentity) &&
|
|
36
|
+
[
|
|
37
|
+
CODEX_APP_SERVER_PROCESS_STATE.RUNNING,
|
|
38
|
+
CODEX_APP_SERVER_PROCESS_STATE.STOPPED
|
|
39
|
+
].includes(metadata.processState)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function inspectIdentifiableCodexAppServerProcess(metadata = {}) {
|
|
44
|
+
if (!codexAppServerProcessMetadataIsIdentifiable(metadata)) {
|
|
45
|
+
return { processGroupIds: [], status: CODEX_APP_SERVER_PROCESS_IDENTITY_STATUS.INVALID };
|
|
46
|
+
}
|
|
47
|
+
return codexProcessIdentity.inspect(metadata);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// A runtime caller may inspect the process identity its own way; every answer is normalized.
|
|
51
|
+
async function inspectCodexAppServerProcessIdentity(metadata = {}, options = {}) {
|
|
52
|
+
const { processIdentityInspector = inspectIdentifiableCodexAppServerProcess } = options;
|
|
53
|
+
return codexProcessIdentity.normalizeInspection(await processIdentityInspector(metadata));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function stopCodexAppServerProcessGroup(metadata = {}, options = {}) {
|
|
57
|
+
if (!codexAppServerProcessMetadataIsIdentifiable(metadata)) {
|
|
58
|
+
return {
|
|
59
|
+
identityStatus: CODEX_APP_SERVER_PROCESS_IDENTITY_STATUS.INVALID,
|
|
60
|
+
processExitVerified: false,
|
|
61
|
+
stopped: false
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return codexProcessIdentity.stop(metadata, {
|
|
65
|
+
...options,
|
|
66
|
+
processIdentityInspector: (current) => inspectCodexAppServerProcessIdentity(current, options)
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function stopOwnedCodexAppServerExecution(metadata = {}, options = {}) {
|
|
71
|
+
if (
|
|
72
|
+
metadata.processState === CODEX_APP_SERVER_PROCESS_STATE.STOPPED &&
|
|
73
|
+
metadata.processExitVerifiedAt
|
|
74
|
+
) {
|
|
75
|
+
return stopCodexAppServerProcessGroup(metadata, options);
|
|
76
|
+
}
|
|
77
|
+
const executionId = normalizeNullableText(metadata.executionId);
|
|
78
|
+
if (executionId) {
|
|
79
|
+
const { stopExecution = execution.stop } = options;
|
|
80
|
+
let executionStop;
|
|
81
|
+
try {
|
|
82
|
+
executionStop = await stopExecution(executionId, {
|
|
83
|
+
allowMissingRecordScopeRecovery: true,
|
|
84
|
+
killTimeoutMs: options.killTimeoutMs,
|
|
85
|
+
reason: options.reason || "codex-app-server-stop",
|
|
86
|
+
termTimeoutMs: options.termTimeoutMs
|
|
87
|
+
});
|
|
88
|
+
} catch (error) {
|
|
89
|
+
return {
|
|
90
|
+
error: String(error?.message || error || "The owned execution could not be stopped."),
|
|
91
|
+
executionId,
|
|
92
|
+
processExitVerified: false,
|
|
93
|
+
scopeEmpty: false,
|
|
94
|
+
stopped: false
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (executionStop?.ok === true && executionStop?.scopeEmpty === true) {
|
|
98
|
+
return {
|
|
99
|
+
...executionStop,
|
|
100
|
+
executionId,
|
|
101
|
+
processExitVerified: true,
|
|
102
|
+
stopped: executionStop.stopped === true
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
// A runtime caller's own execution stop is authoritative for its scope, as an authoritative facility is.
|
|
106
|
+
if (stopExecution !== execution.stop || execution.authoritative) {
|
|
107
|
+
return {
|
|
108
|
+
...(executionStop && typeof executionStop === "object" ? executionStop : {}),
|
|
109
|
+
executionId,
|
|
110
|
+
processExitVerified: false,
|
|
111
|
+
scopeEmpty: false,
|
|
112
|
+
stopped: false
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return stopCodexAppServerProcessGroup(metadata, options);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function stopCodexAppServerProcess(runtimeDir = "", options = {}) {
|
|
120
|
+
const normalizedRuntimeDir = normalizeNullableText(runtimeDir);
|
|
121
|
+
if (!normalizedRuntimeDir) {
|
|
122
|
+
return {
|
|
123
|
+
processExitVerified: false,
|
|
124
|
+
stopped: false
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const metadata = await readCodexAppServerMetadata(normalizedRuntimeDir);
|
|
128
|
+
if (!codexAppServerProcessMetadataIsIdentifiable(metadata, normalizedRuntimeDir)) {
|
|
129
|
+
return {
|
|
130
|
+
identityStatus: CODEX_APP_SERVER_PROCESS_IDENTITY_STATUS.INVALID,
|
|
131
|
+
processExitVerified: false,
|
|
132
|
+
stopped: false
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return stopOwnedCodexAppServerExecution(metadata, options);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function cleanupFailedCodexAppServerStart(runtimeDir = "", {
|
|
139
|
+
helper = false,
|
|
140
|
+
executionId = "",
|
|
141
|
+
neverStarted = false,
|
|
142
|
+
pid = null,
|
|
143
|
+
processIdentity = null
|
|
144
|
+
} = {}, options = {}) {
|
|
145
|
+
let processStop = {
|
|
146
|
+
processExitVerified: neverStarted === true,
|
|
147
|
+
scopeEmpty: neverStarted === true,
|
|
148
|
+
stopped: false
|
|
149
|
+
};
|
|
150
|
+
let processCleanupFailed = false;
|
|
151
|
+
if (!neverStarted) {
|
|
152
|
+
try {
|
|
153
|
+
processStop = await stopOwnedCodexAppServerExecution({
|
|
154
|
+
executionId: normalizeNullableText(executionId),
|
|
155
|
+
pid: Number(pid),
|
|
156
|
+
processIdentity,
|
|
157
|
+
processState: CODEX_APP_SERVER_PROCESS_STATE.RUNNING,
|
|
158
|
+
provider: CODEX_APP_SERVER_PROVIDER_ID,
|
|
159
|
+
runtimeDir: normalizeNullableText(runtimeDir),
|
|
160
|
+
schemaVersion: CODEX_APP_SERVER_METADATA_SCHEMA_VERSION
|
|
161
|
+
}, options);
|
|
162
|
+
} catch {
|
|
163
|
+
processCleanupFailed = true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (processCleanupFailed || processStop.processExitVerified !== true) {
|
|
167
|
+
return {
|
|
168
|
+
...processStop,
|
|
169
|
+
cleanupFailed: true
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const helperRemovals = helper
|
|
173
|
+
? [
|
|
174
|
+
rm(codexAppServerHelperHomeDir(runtimeDir), { force: true, recursive: true }),
|
|
175
|
+
rm(codexAppServerHelperWorkspaceDir(runtimeDir), { force: true, recursive: true }),
|
|
176
|
+
rm(codexAppServerLogPath(runtimeDir), { force: true })
|
|
177
|
+
]
|
|
178
|
+
: [];
|
|
179
|
+
const removals = await Promise.allSettled([
|
|
180
|
+
...helperRemovals,
|
|
181
|
+
rm(codexAppServerSocketPath(runtimeDir), { force: true }),
|
|
182
|
+
rm(codexAppServerMetadataPath(runtimeDir), { force: true }),
|
|
183
|
+
removeCodexAppServerMetadataTemps(runtimeDir)
|
|
184
|
+
]);
|
|
185
|
+
return {
|
|
186
|
+
...processStop,
|
|
187
|
+
cleanupFailed: removals.some(({ status }) => status === "rejected")
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function stopCodexAppServerRuntime(options = {}) {
|
|
192
|
+
const runtimeDir = normalizeNullableText(options.runtimeDir);
|
|
193
|
+
const preserveProcessExitProof = options.preserveProcessExitProof === true;
|
|
194
|
+
if (!runtimeDir || !codexAppServerRuntimeDirIsManaged(runtimeDir)) {
|
|
195
|
+
return {
|
|
196
|
+
processExitVerified: false,
|
|
197
|
+
runtimeDirPreserved: false,
|
|
198
|
+
runtimeDirRemoved: false,
|
|
199
|
+
stopped: false
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
let releaseLock;
|
|
203
|
+
try {
|
|
204
|
+
releaseLock = await acquireRuntimeLock(runtimeDir, options);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
if (!codexAppServerRuntimeCleanupCanSkip(error)) {
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
processExitVerified: false,
|
|
211
|
+
runtimeDirCleanupError: String(error?.message || error || ""),
|
|
212
|
+
runtimeDirCleanupSkipped: String(error?.code || "") !== "ENOENT",
|
|
213
|
+
runtimeDirPreserved: false,
|
|
214
|
+
runtimeDirRemoved: false,
|
|
215
|
+
stopped: false
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
let runtimeDirRemoved = false;
|
|
219
|
+
let runtimeDirCleanupSkipped = false;
|
|
220
|
+
let runtimeDirCleanupError = "";
|
|
221
|
+
let runtimeDirPreserved = false;
|
|
222
|
+
let processStop = {
|
|
223
|
+
processExitVerified: false,
|
|
224
|
+
stopped: false
|
|
225
|
+
};
|
|
226
|
+
try {
|
|
227
|
+
const existing = await readCodexAppServerMetadata(runtimeDir);
|
|
228
|
+
const expectedAccount = normalizeNullableText(options.expectedAccountIdentitySignature);
|
|
229
|
+
const ownedRuntime = options.ownedRuntime;
|
|
230
|
+
if (
|
|
231
|
+
ownedRuntime &&
|
|
232
|
+
codexAppServerProcessMetadataIsIdentifiable(ownedRuntime, runtimeDir) &&
|
|
233
|
+
(!expectedAccount || ownedRuntime.accountIdentitySignature === expectedAccount) &&
|
|
234
|
+
(!existing || codexAppServerRuntimeIdentity(existing) !== codexAppServerRuntimeIdentity(ownedRuntime))
|
|
235
|
+
) {
|
|
236
|
+
// Another owner may already have removed or replaced the shared runtime.
|
|
237
|
+
// Prove this provider's exact execution stopped without touching its successor.
|
|
238
|
+
return {
|
|
239
|
+
...await stopOwnedCodexAppServerExecution(ownedRuntime, options),
|
|
240
|
+
runtimeDirPreserved: Boolean(existing),
|
|
241
|
+
runtimeDirRemoved: false
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
if (!existing) {
|
|
245
|
+
// Changeover after a server restart may have no runtime.json left. The
|
|
246
|
+
// managed execution owner can still drain and prove its exact scope empty.
|
|
247
|
+
// A missing file alone is never process-exit proof, and account-specific
|
|
248
|
+
// cleanup must keep using its retained execution identity above.
|
|
249
|
+
const owned = options.verifyOwnerScope === true && !expectedAccount
|
|
250
|
+
? await execution.stopOwned({
|
|
251
|
+
kind: "assistant",
|
|
252
|
+
operationId: "codex-app-server",
|
|
253
|
+
ownerId: normalizeNullableText(options.ownerId) || stableHash(runtimeDir)
|
|
254
|
+
}, { reason: "codex-app-server-changeover" })
|
|
255
|
+
: null;
|
|
256
|
+
const ownerExitVerified = owned?.supported === true && owned?.ok === true && owned?.scopeEmpty === true;
|
|
257
|
+
return {
|
|
258
|
+
processExitVerified: ownerExitVerified,
|
|
259
|
+
runtimeDirPreserved: false,
|
|
260
|
+
runtimeDirRemoved: false,
|
|
261
|
+
stopped: ownerExitVerified && Number(owned?.closed) > 0
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
if (expectedAccount) {
|
|
265
|
+
// A replacement runtime is installed only after the previous process has
|
|
266
|
+
// drained under this same lock. Never stop that replacement for stale
|
|
267
|
+
// ephemeral-thread cleanup.
|
|
268
|
+
if (!codexAppServerProcessMetadataIsIdentifiable(existing, runtimeDir) ||
|
|
269
|
+
!/^sha256:[a-f0-9]{64}$/u.test(existing.accountIdentitySignature)) {
|
|
270
|
+
return { processExitVerified: false, runtimeDirRemoved: false, stopped: false };
|
|
271
|
+
}
|
|
272
|
+
if (existing.accountIdentitySignature !== expectedAccount) {
|
|
273
|
+
return { ownershipSuperseded: true, processExitVerified: false, runtimeDirRemoved: false, stopped: false };
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
processStop = await stopCodexAppServerProcess(runtimeDir, options);
|
|
277
|
+
if (processStop.processExitVerified === true && preserveProcessExitProof) {
|
|
278
|
+
const metadata = await readCodexAppServerMetadata(runtimeDir);
|
|
279
|
+
if (!codexAppServerProcessMetadataIsIdentifiable(metadata, runtimeDir)) {
|
|
280
|
+
return {
|
|
281
|
+
...processStop,
|
|
282
|
+
processExitVerified: false,
|
|
283
|
+
runtimeDirPreserved: false,
|
|
284
|
+
runtimeDirRemoved: false
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
await writeCodexAppServerMetadata(runtimeDir, {
|
|
288
|
+
...metadata,
|
|
289
|
+
processExitVerifiedAt: metadata.processExitVerifiedAt || new Date().toISOString(),
|
|
290
|
+
processState: CODEX_APP_SERVER_PROCESS_STATE.STOPPED
|
|
291
|
+
});
|
|
292
|
+
runtimeDirPreserved = true;
|
|
293
|
+
} else if (processStop.processExitVerified === true) {
|
|
294
|
+
try {
|
|
295
|
+
runtimeDirRemoved = await removeCodexAppServerRuntimeDir(runtimeDir);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
if (!codexAppServerRuntimeCleanupCanSkip(error)) {
|
|
298
|
+
throw error;
|
|
299
|
+
}
|
|
300
|
+
runtimeDirCleanupSkipped = true;
|
|
301
|
+
runtimeDirCleanupError = String(error?.message || error || "");
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
} finally {
|
|
305
|
+
await releaseLock();
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
...processStop,
|
|
309
|
+
runtimeDirCleanupError,
|
|
310
|
+
runtimeDirCleanupSkipped,
|
|
311
|
+
runtimeDirPreserved,
|
|
312
|
+
runtimeDirRemoved
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return Object.freeze({
|
|
317
|
+
acquireRuntimeLock,
|
|
318
|
+
processMetadataIsIdentifiable: codexAppServerProcessMetadataIsIdentifiable,
|
|
319
|
+
inspectProcessIdentity: inspectCodexAppServerProcessIdentity,
|
|
320
|
+
stopProcess: stopCodexAppServerProcess,
|
|
321
|
+
cleanupFailedStart: cleanupFailedCodexAppServerStart,
|
|
322
|
+
stopRuntime: stopCodexAppServerRuntime
|
|
323
|
+
});
|
|
324
|
+
}
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Cleanup of the run, provider and Helper owners. The application keeps
|
|
5
|
+
* its authority and storage projections; all native coordination stays here:
|
|
6
|
+
* one closure per session key (shared with the runtime lifecycle, which drains
|
|
7
|
+
* them on shutdown) and the private record of which closures are restricted.
|
|
8
|
+
* @param {object} dependencies
|
|
9
|
+
* @param {(sessionId: string) => string} dependencies.namespace
|
|
10
|
+
* @param {import("./codexRuntimeLifecycle.js").CodexRunOwner} dependencies.runOwner
|
|
11
|
+
* @param {import("./codexProviderOwner.js").CodexProviderSessions | null} dependencies.providerSessions
|
|
12
|
+
* @param {Map<string, Promise<unknown>> | undefined} dependencies.sessionClosures
|
|
13
|
+
* @param {import("./codexRuntimeLifecycle.js").CodexHelperThreadLifecycle | null} dependencies.helperLifecycle
|
|
14
|
+
* @param {ReturnType<typeof import("./codexRunJournal.js").createCodexRunJournal>} dependencies.journal
|
|
15
|
+
* @param {object} dependencies.conversations
|
|
16
|
+
* @param {import("./codexRuntimeLifecycle.js").CodexDebugLog} dependencies.debugLog
|
|
17
|
+
* @param {(error: unknown) => unknown} dependencies.debugError
|
|
18
|
+
*/
|
|
7
19
|
export function createCodexSessionCleanup({ namespace, runOwner, providerSessions, sessionClosures, helperLifecycle,
|
|
8
20
|
journal, conversations, debugLog, debugError }) {
|
|
9
21
|
const providerOwner = providerSessions?.owner;
|
|
@@ -31,7 +43,7 @@ export function createCodexSessionCleanup({ namespace, runOwner, providerSession
|
|
|
31
43
|
}
|
|
32
44
|
|
|
33
45
|
async function closeSession(sessionId, cleanup) {
|
|
34
|
-
const normalizedSessionId =
|
|
46
|
+
const normalizedSessionId = normalizeText(sessionId);
|
|
35
47
|
const sessionKey = cleanup.sessionKey;
|
|
36
48
|
let pending = sessionClosures.get(sessionKey);
|
|
37
49
|
while (pending) {
|
|
@@ -113,8 +125,8 @@ export function createCodexSessionCleanup({ namespace, runOwner, providerSession
|
|
|
113
125
|
await conversations.closeCodexAppServerConversations(sessionId);
|
|
114
126
|
} catch (error) {
|
|
115
127
|
conversationFailures.push({
|
|
116
|
-
code:
|
|
117
|
-
error:
|
|
128
|
+
code: normalizeText(error?.code),
|
|
129
|
+
error: normalizeText(error?.message || error),
|
|
118
130
|
retryable: error?.retryable !== false,
|
|
119
131
|
sessionId
|
|
120
132
|
});
|
|
@@ -122,7 +134,7 @@ export function createCodexSessionCleanup({ namespace, runOwner, providerSession
|
|
|
122
134
|
}
|
|
123
135
|
const helperRestore = await restoreSavedHelpers(() => preparation.helpers,
|
|
124
136
|
"appServerRuntime.closeProject.helperRestore.failed",
|
|
125
|
-
{ projectContextRoot:
|
|
137
|
+
{ projectContextRoot: normalizeText(input.projectContextRoot) });
|
|
126
138
|
// Every provider of the project's sessions stops, including one a
|
|
127
139
|
// Temporary chat or a read borrowed and no Main thread names.
|
|
128
140
|
const stopped = await providerOwner.stopProvidersForProject(input, {
|
|
@@ -140,5 +152,8 @@ export function createCodexSessionCleanup({ namespace, runOwner, providerSession
|
|
|
140
152
|
};
|
|
141
153
|
}
|
|
142
154
|
|
|
143
|
-
return {
|
|
155
|
+
return Object.freeze({
|
|
156
|
+
closeSession,
|
|
157
|
+
closeProject
|
|
158
|
+
});
|
|
144
159
|
}
|
|
@@ -1,8 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isRecord } from "@jskit-ai/kernel/shared/support/normalize";
|
|
1
|
+
import { codexAppServerErrorMessage as errorMessage } from "./codexTurnState.js";
|
|
2
|
+
import { isRecord, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Batch restoration, pruning and the startup unsubscribe. The run owner's
|
|
6
|
+
* lifecycle and per-thread readiness hold all state and coordination.
|
|
7
|
+
* @param {object} dependencies
|
|
8
|
+
* @param {object} dependencies.readiness
|
|
9
|
+
* @param {(sessionId: string, context: object) => Promise<object>} dependencies.recoverSessionObservationLoss
|
|
10
|
+
* @param {import("./codexProviderOwner.js").CodexProviderSessions} dependencies.providerSessions
|
|
11
|
+
* @param {import("./codexRuntimeLifecycle.js").CodexHelperThreadLifecycle | null} dependencies.helperLifecycle
|
|
12
|
+
* @param {import("./codexRuntimeLifecycle.js").CodexRuntimeLifecycle} dependencies.lifecycle
|
|
13
|
+
* @param {() => Promise<object>} dependencies.createRuntime
|
|
14
|
+
* @param {(sessionId: string) => string} dependencies.namespace
|
|
15
|
+
* @param {(session: object) => object} dependencies.turnState
|
|
16
|
+
* @param {string} dependencies.sessionIdRequiredError
|
|
17
|
+
* @param {import("./codexRuntimeLifecycle.js").CodexDebugLog} dependencies.debugLog
|
|
18
|
+
* @param {(error: unknown) => unknown} dependencies.debugError
|
|
19
|
+
*/
|
|
6
20
|
export function createCodexSessionReconciliation({
|
|
7
21
|
readiness, recoverSessionObservationLoss, providerSessions, helperLifecycle, lifecycle, createRuntime,
|
|
8
22
|
namespace, turnState, sessionIdRequiredError, debugLog, debugError
|
|
@@ -55,7 +69,7 @@ export function createCodexSessionReconciliation({
|
|
|
55
69
|
);
|
|
56
70
|
}
|
|
57
71
|
|
|
58
|
-
function codexAppServerReconcileSessionId(value
|
|
72
|
+
function codexAppServerReconcileSessionId(value) {
|
|
59
73
|
if (typeof value === "string") {
|
|
60
74
|
return normalizeText(value);
|
|
61
75
|
}
|
|
@@ -290,9 +304,9 @@ export function createCodexSessionReconciliation({
|
|
|
290
304
|
return unsubscribeCodexAppServerThreadsForSessions(sessions, preparation);
|
|
291
305
|
}
|
|
292
306
|
|
|
293
|
-
return {
|
|
307
|
+
return Object.freeze({
|
|
294
308
|
reconcileSessions,
|
|
295
309
|
unsubscribeSessions,
|
|
296
310
|
retireProviderForSession: retireAndCloseCodexAppServerProviderForSession
|
|
297
|
-
};
|
|
311
|
+
});
|
|
298
312
|
}
|