@jskit-ai/assistant-core 0.1.200 → 0.1.202
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/README.md +8 -1
- package/examples/conversation/package.json +5 -5
- package/fixtures/responsive-assistant/App.vue +4 -1
- package/package.json +99 -36
- package/src/client/conversation/AssistantConversationElement.vue +27 -4
- package/src/client/conversation/AssistantConversationStatus.vue +24 -0
- package/src/client/conversation/AssistantGoalControl.vue +8 -5
- package/src/client/conversation/AssistantTranscript.vue +10 -3
- package/src/client/conversation/index.js +2 -0
- package/src/client/conversation/messageDelivery.js +115 -32
- package/src/client/conversation/retainedConversation.js +32 -0
- package/src/client/conversation/useAssistantQuestions.js +76 -0
- package/src/client/lib/assistantApi.js +72 -2
- package/src/server/conversation/agentRun.js +222 -0
- package/src/server/conversation/attachments.js +93 -0
- package/src/server/conversation/claudeClient.js +221 -0
- package/src/server/conversation/claudeHistory.js +256 -0
- package/src/server/conversation/claudeProcess.js +327 -0
- package/src/server/conversation/claudeTools.js +42 -0
- package/src/server/conversation/claudeTurn.js +1082 -0
- package/src/server/conversation/codexConfiguration.js +1183 -0
- package/src/server/conversation/codexConversations.js +1082 -0
- package/src/server/conversation/codexDetachedTurn.js +332 -0
- package/src/server/conversation/codexEvents.js +194 -1
- package/src/server/conversation/codexGoalCommands.js +124 -0
- package/src/server/conversation/codexHelperExecution.js +306 -0
- package/src/server/conversation/codexHelperThreadLedger.js +804 -0
- package/src/server/conversation/codexHelperThreadRecovery.js +395 -0
- package/src/server/conversation/codexHelperThreads.js +638 -0
- package/src/server/conversation/codexHistoryAdapter.js +369 -0
- package/src/server/conversation/codexMessageCommands.js +663 -0
- package/src/server/conversation/codexMessageDelivery.js +211 -0
- package/src/server/conversation/codexNativeHistoryExport.js +95 -0
- package/src/server/conversation/codexNotificationObserver.js +482 -0
- package/src/server/conversation/codexNotificationQueue.js +103 -0
- package/src/server/conversation/codexObservationControl.js +174 -0
- package/src/server/conversation/codexOutput.js +931 -0
- package/src/server/conversation/codexProcess.js +1559 -0
- package/src/server/conversation/codexProcessLeader.js +14 -0
- package/src/server/conversation/codexProvider.js +2710 -0
- package/src/server/conversation/codexProviderOwner.js +1058 -0
- package/src/server/conversation/codexProviderSelection.js +218 -0
- package/src/server/conversation/codexReasoning.js +314 -0
- package/src/server/conversation/codexRenewalCommands.js +290 -0
- package/src/server/conversation/codexResultSettlement.js +409 -0
- package/src/server/conversation/codexRunJournal.js +658 -0
- package/src/server/conversation/codexRuntimeLifecycle.js +288 -0
- package/src/server/conversation/codexSessionCleanup.js +96 -0
- package/src/server/conversation/codexSessionReconciliation.js +284 -0
- package/src/server/conversation/codexThreadReadiness.js +397 -0
- package/src/server/conversation/codexThreadStatus.js +354 -0
- package/src/server/conversation/codexTurn.js +701 -255
- package/src/server/conversation/codexTurnControl.js +317 -0
- package/src/server/conversation/codexTurnRecovery.js +990 -0
- package/src/server/conversation/codexTurnState.js +549 -0
- package/src/server/conversation/commandHook.js +18 -0
- package/src/server/conversation/commandWrapper.js +37 -0
- package/src/server/conversation/configuration.js +43 -0
- package/src/server/conversation/continuity.js +289 -0
- package/src/server/conversation/fileStorage.js +55 -0
- package/src/server/conversation/hookBridge.js +19 -0
- package/src/server/conversation/index.js +13 -0
- package/src/server/conversation/localExecution.js +665 -0
- package/src/server/conversation/memoryStorage.js +7 -72
- package/src/server/conversation/nativeHistoryExport.js +90 -0
- package/src/server/conversation/nativeStorage.js +23 -0
- package/src/server/conversation/openCodeClient.js +209 -19
- package/src/server/conversation/openCodePlugin.js +84 -0
- package/src/server/conversation/openCodeProcess.js +476 -0
- package/src/server/conversation/openCodeRuntime.js +1578 -0
- package/src/server/conversation/openCodeTools.js +60 -0
- package/src/server/conversation/openCodeTurn.js +557 -0
- package/src/server/conversation/provider.js +30 -0
- package/src/server/conversation/providers/api.js +136 -0
- package/src/server/conversation/providers/claude.js +63 -0
- package/src/server/conversation/providers/claudeDriver.js +425 -0
- package/src/server/conversation/providers/codex.js +248 -0
- package/src/server/conversation/providers/codexDriver.js +1078 -0
- package/src/server/conversation/providers/opencode.js +49 -0
- package/src/server/conversation/providers/opencodeDriver.js +650 -0
- package/src/server/conversation/providers/scoped.js +312 -0
- package/src/server/conversation/runtime.js +2220 -0
- package/src/server/conversation/runtimeStateUpgrade.js +125 -0
- package/src/server/conversation/storage.js +184 -0
- package/src/server/conversation/streams.js +7 -2
- package/src/server/conversation/structuredOutput.js +240 -0
- package/src/server/conversation/systemPrompt.js +54 -0
- package/src/server/conversation/tools.js +84 -0
- package/src/server/conversation/transcript.js +5 -4
- package/src/server/hosts/claudeHistory.js +5 -0
- package/src/server/hosts/claudeProcess.js +11 -0
- package/src/server/hosts/claudeTurn.js +4 -0
- package/src/server/hosts/codexConfiguration.js +20 -0
- package/src/server/hosts/codexEvents.js +13 -0
- package/src/server/hosts/codexProcess.js +12 -0
- package/src/server/hosts/codexProvider.js +23 -0
- package/src/server/hosts/codexTurn.js +20 -0
- package/src/server/hosts/nativeHistory.js +3 -0
- package/src/server/hosts/openCodeClient.js +3 -0
- package/src/server/hosts/openCodeProcess.js +23 -0
- package/src/server/hosts/openCodeTurn.js +7 -0
- package/src/server/index.js +2 -0
- package/src/server/lib/aiConnectionClient.js +13 -6
- package/src/server/lib/assistantCompletion.js +190 -0
- package/src/server/lib/assistantToolLoop.js +314 -0
- package/src/server/lib/serviceToolCatalog.js +25 -10
- package/src/shared/assistantResponseText.js +29 -0
- package/src/shared/conversation/attachments.js +19 -0
- package/src/shared/conversation/index.js +3 -0
- package/src/shared/conversation/pagination.js +78 -0
- package/src/shared/conversation/patches.js +150 -0
- package/src/shared/conversation/questions.js +48 -0
- package/src/shared/conversation/streaming.js +7 -4
- package/src/shared/index.js +2 -0
- package/src/shared/nativeProviders.js +45 -0
- package/src/testing/conversationStorageContract.js +31 -1
- package/src/testing/nativeCodex.js +11 -0
- package/src/testing/nativeOpenCode.js +3 -0
- package/test/aiConnectionClient.test.js +2 -0
- package/test/apiConversationDriver.test.js +73 -0
- package/test/assistantApiSurfaceHeader.test.js +85 -0
- package/test/assistantBoundedToolLoop.test.js +285 -0
- package/test/assistantScroll.browser.test.js +16 -0
- package/test/claudeClient.test.js +189 -0
- package/test/claudeConversationSession.test.js +1173 -0
- package/test/claudeInstructionResume.test.js +82 -0
- package/test/claudeProcess.test.js +204 -0
- package/test/claudeTurn.test.js +253 -0
- package/test/codexConversationSession.test.js +2122 -0
- package/test/codexGoal.test.js +37 -0
- package/test/codexHelperDeletion.test.js +163 -0
- package/test/codexHelperThreadLedger.test.js +494 -0
- package/test/codexHistoryAdapter.test.js +541 -0
- package/test/codexInteractiveArguments.test.js +30 -0
- package/test/codexIsolation.test.js +756 -0
- package/test/codexModelCatalog.test.js +362 -0
- package/test/codexNativeHistoryExport.test.js +81 -0
- package/test/codexThreadSelection.test.js +136 -0
- package/test/codexTurn.test.js +2158 -0
- package/test/conversation.test.js +110 -0
- package/test/conversationAgentRun.test.js +326 -0
- package/test/conversationApiFacilities.test.js +90 -0
- package/test/conversationAttachments.test.js +238 -0
- package/test/conversationCommandWrapper.test.js +48 -0
- package/test/conversationConfiguration.test.js +174 -0
- package/test/conversationNativeStorage.test.js +25 -0
- package/test/conversationReplacement.test.js +172 -0
- package/test/conversationRuntime.test.js +241 -0
- package/test/conversationRuntimeStateUpgrade.test.js +144 -0
- package/test/conversationSession.test.js +1080 -0
- package/test/conversationStreaming.test.js +68 -0
- package/test/conversationSystemPrompt.test.js +84 -0
- package/test/conversationTools.test.js +416 -0
- package/test/fileConversationStorage.test.js +73 -0
- package/test/fixtures/codexHelperThreadLedger.js +39 -0
- package/test/localConversationExecution.test.js +90 -0
- package/test/messageDelivery.test.js +234 -1
- package/test/nativeConversationRetirement.test.js +215 -0
- package/test/openCodeClient.test.js +515 -0
- package/test/openCodeProcess.test.js +177 -0
- package/test/openCodeRuntime.test.js +322 -0
- package/test/openCodeTools.test.js +287 -0
- package/test/opencodeConversationSession.test.js +789 -0
- package/test/serviceToolCatalog.test.js +24 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
const INSTRUCTION_REVISION_ENV = "JSKIT_CONVERSATION_INSTRUCTIONS";
|
|
4
|
+
|
|
5
|
+
/** Codex native context restoration; host callbacks own resources and access. */
|
|
6
|
+
export function createCodexConversationAdapter(host) {
|
|
7
|
+
const bindings = new Map();
|
|
8
|
+
// Losing a control binding does not erase an acknowledged prompt. Keeping
|
|
9
|
+
// that acknowledgement avoids reinjecting it after unrelated control failures.
|
|
10
|
+
const instructionPrompts = new Map();
|
|
11
|
+
const tasks = new Map();
|
|
12
|
+
const goalChanges = new Map();
|
|
13
|
+
let contextGeneration = 0;
|
|
14
|
+
|
|
15
|
+
function instructionEnvironment(params, environment) {
|
|
16
|
+
if (params.developerInstructions == null) return environment;
|
|
17
|
+
return { ...environment, [INSTRUCTION_REVISION_ENV]: createHash("sha256").update(params.developerInstructions).digest("hex") };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function threadParameters(params, environment) {
|
|
21
|
+
const result = host.threadParameters(params, environment);
|
|
22
|
+
if (params.developerInstructions == null) return result;
|
|
23
|
+
const policy = result.config?.shell_environment_policy || { inherit: "none", set: environment || {} };
|
|
24
|
+
return { ...result, config: { ...result.config, shell_environment_policy: {
|
|
25
|
+
...policy, set: instructionEnvironment(params, policy.set || {})
|
|
26
|
+
} } };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function prepareInstructions({ systemPrompt, ...params }, threadId) {
|
|
30
|
+
if (systemPrompt === undefined && params.developerInstructions != null) return params;
|
|
31
|
+
const supplied = systemPrompt ?? await host.readInstructions?.(params, threadId);
|
|
32
|
+
if (supplied === undefined || supplied === null) return params;
|
|
33
|
+
if (typeof supplied !== "string" || !supplied.trim()) throw new TypeError("Codex instructions must contain text.");
|
|
34
|
+
return { ...params, developerInstructions: supplied };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// A loaded native thread can ignore shell_environment_policy on resume.
|
|
38
|
+
// Detaching alone is insufficient when another subscriber retains it: prove
|
|
39
|
+
// the effective managed environment before recording a new binding.
|
|
40
|
+
async function withThreadContext(threadId, params, operation) {
|
|
41
|
+
const previous = tasks.get(threadId) || Promise.resolve();
|
|
42
|
+
const task = previous.catch(() => null).then(() => host.runRequest(async () => {
|
|
43
|
+
const client = await host.client();
|
|
44
|
+
params = await prepareInstructions(params, threadId);
|
|
45
|
+
const generation = contextGeneration;
|
|
46
|
+
const signal = AbortSignal.timeout(host.timeoutMs || 10_000);
|
|
47
|
+
const assertRecoveryOpen = () => {
|
|
48
|
+
signal.throwIfAborted();
|
|
49
|
+
if (generation !== contextGeneration) {
|
|
50
|
+
throw new Error("The assistant connection closed during recovery.");
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const request = (method, input) => {
|
|
54
|
+
assertRecoveryOpen();
|
|
55
|
+
return client.request(method, input, { signal });
|
|
56
|
+
};
|
|
57
|
+
const bound = bindings.get(threadId);
|
|
58
|
+
const fixedPolicy = params.config?.shell_environment_policy ||
|
|
59
|
+
(bound?.managed === false ? bound.params.config.shell_environment_policy : null);
|
|
60
|
+
if (fixedPolicy && (params.developerInstructions == null || params.ephemeral || bound?.params.ephemeral)) {
|
|
61
|
+
if (bound && params.developerInstructions != null && instructionPrompts.get(threadId) !== params.developerInstructions) {
|
|
62
|
+
throw new Error("Start a new ephemeral conversation to change its instructions.");
|
|
63
|
+
}
|
|
64
|
+
return operation(threadParameters({ ...bound?.params, ...params }, {}));
|
|
65
|
+
}
|
|
66
|
+
const environment = instructionEnvironment(params, fixedPolicy?.set || await host.prepareEnvironment(
|
|
67
|
+
bound?.params?.config?.shell_environment_policy?.set || host.environment || {}
|
|
68
|
+
));
|
|
69
|
+
const previousConfig = { ...bound?.params?.config };
|
|
70
|
+
if (params.modelProvider) {
|
|
71
|
+
for (const key of Object.keys(previousConfig)) {
|
|
72
|
+
if (key === "model_providers" || key === "model_catalog_json" || key === "web_search" || key === "openai_base_url" || key.startsWith("model_providers.")) delete previousConfig[key];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
let requestParams = {
|
|
76
|
+
...bound?.params,
|
|
77
|
+
...params,
|
|
78
|
+
config: {
|
|
79
|
+
...previousConfig,
|
|
80
|
+
...params?.config,
|
|
81
|
+
shell_environment_policy: { ...(fixedPolicy || { inherit: "none" }), set: environment }
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
let nativeModelProvider = "";
|
|
85
|
+
let nativeHistoryPath = "";
|
|
86
|
+
const readStatus = async () => {
|
|
87
|
+
const { thread } = await request("thread/read", { threadId, includeTurns: false });
|
|
88
|
+
if (thread?.historyMode !== "paginated") {
|
|
89
|
+
throw Object.assign(new Error("This conversation uses an unsupported Codex history format. Renew the session to keep your files and saved chat and start a fresh conversation. You can write the handover yourself."), {
|
|
90
|
+
code: "assistant_codex_history_unsupported"
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
nativeModelProvider = thread?.modelProvider || "";
|
|
94
|
+
nativeHistoryPath = thread?.path || "";
|
|
95
|
+
return typeof thread?.status === "string" ? thread.status : thread?.status?.type;
|
|
96
|
+
};
|
|
97
|
+
const nativeStatus = await readStatus();
|
|
98
|
+
if (!bound && !requestParams.modelProvider && nativeModelProvider) {
|
|
99
|
+
// A restored observer has no saved request parameters. Recover the
|
|
100
|
+
// native thread's provider configuration before resuming its controls.
|
|
101
|
+
requestParams = { ...requestParams, modelProvider: nativeModelProvider };
|
|
102
|
+
requestParams = await host.prepareParameters?.(requestParams) || requestParams;
|
|
103
|
+
}
|
|
104
|
+
requestParams = await host.prepareHistory(requestParams, client, { signal, historyPath: nativeHistoryPath });
|
|
105
|
+
const providerChanged = Boolean(requestParams.modelProvider && nativeModelProvider &&
|
|
106
|
+
requestParams.modelProvider !== nativeModelProvider);
|
|
107
|
+
if (providerChanged) {
|
|
108
|
+
const { goal } = await request("thread/goal/get", { threadId });
|
|
109
|
+
if (!["idle", "notLoaded"].includes(nativeStatus) || goal && !["complete", "completed"].includes(goal.status)) {
|
|
110
|
+
throw Object.assign(new Error("Stop the current turn and finish or clear its goal before changing model providers."), {
|
|
111
|
+
code: "assistant_codex_provider_switch_busy"
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const executionId = String(host.runtime()?.executionId || "");
|
|
116
|
+
// Refresh project hook trust only for a cold thread in a replacement
|
|
117
|
+
// process. A new socket or a changed command environment is not evidence
|
|
118
|
+
// that the provider process died.
|
|
119
|
+
if (nativeStatus === "notLoaded" && executionId && bound?.executionId !== executionId) {
|
|
120
|
+
requestParams = await host.prepareResume?.(threadId, requestParams, {
|
|
121
|
+
runtime: host.runtime(),
|
|
122
|
+
processChanged: Boolean(bound?.executionId)
|
|
123
|
+
}) || requestParams;
|
|
124
|
+
}
|
|
125
|
+
const bindingMatches = !providerChanged && bound?.client === client &&
|
|
126
|
+
bound.params.developerInstructions === requestParams.developerInstructions &&
|
|
127
|
+
bound.params.config.openai_base_url === requestParams.config.openai_base_url &&
|
|
128
|
+
JSON.stringify(bound.params.config.model_providers) === JSON.stringify(requestParams.config.model_providers) &&
|
|
129
|
+
Object.keys(requestParams.config).filter((key) => key.startsWith("model_providers.")).every((key) =>
|
|
130
|
+
JSON.stringify(bound.params.config[key]) === JSON.stringify(requestParams.config[key])) &&
|
|
131
|
+
JSON.stringify(bound.params.config.shell_environment_policy) === JSON.stringify(requestParams.config.shell_environment_policy);
|
|
132
|
+
const instructionsChanged = requestParams.developerInstructions != null &&
|
|
133
|
+
instructionPrompts.get(threadId) !== requestParams.developerInstructions;
|
|
134
|
+
let pausedGoal = null;
|
|
135
|
+
let resumed = null;
|
|
136
|
+
const goalChange = goalChanges.get(threadId);
|
|
137
|
+
const isSamePausedGoal = (goal, original) => goal?.status === "paused" &&
|
|
138
|
+
goal.createdAt === original.createdAt && goal.objective === original.objective;
|
|
139
|
+
const log = (event, fields = {}) => host.log?.(event, fields, { threadId, environment });
|
|
140
|
+
try {
|
|
141
|
+
if (!bindingMatches || nativeStatus === "notLoaded") {
|
|
142
|
+
log("reload_started", { reason: bound ? "control-environment-changed" : "unverified-thread-binding", nativeStatus });
|
|
143
|
+
if (nativeStatus !== "notLoaded") {
|
|
144
|
+
const { goal } = await request("thread/goal/get", { threadId });
|
|
145
|
+
if (goal?.status === "active") {
|
|
146
|
+
const result = await request("thread/goal/set", { threadId, status: "paused" });
|
|
147
|
+
pausedGoal = result.goal;
|
|
148
|
+
if (!isSamePausedGoal(pausedGoal, goal)) {
|
|
149
|
+
throw new Error("Codex did not confirm that the same goal stopped scheduling work.");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (await readStatus() !== "idle") {
|
|
153
|
+
const page = await request("thread/turns/list", { threadId, limit: 1, sortDirection: "desc", itemsView: "summary" });
|
|
154
|
+
const turnId = page.data?.[0]?.id;
|
|
155
|
+
if (!turnId) throw new Error("Codex's running turn could not be identified for control recovery.");
|
|
156
|
+
await request("turn/interrupt", { threadId, turnId });
|
|
157
|
+
while (await readStatus() !== "idle") {
|
|
158
|
+
signal.throwIfAborted();
|
|
159
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (pausedGoal) {
|
|
163
|
+
const { goal } = await request("thread/goal/get", { threadId });
|
|
164
|
+
pausedGoal = isSamePausedGoal(goal, pausedGoal) ? goal : null;
|
|
165
|
+
}
|
|
166
|
+
const detached = await request("thread/unsubscribe", { threadId });
|
|
167
|
+
if (!["unsubscribed", "notSubscribed", "notLoaded"].includes(detached?.status)) {
|
|
168
|
+
throw new Error("Codex did not confirm detachment before control recovery.");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
bindings.delete(threadId);
|
|
172
|
+
await host.beforeResume?.(threadId);
|
|
173
|
+
resumed = await request("thread/resume", { excludeTurns: true, ...requestParams, threadId });
|
|
174
|
+
if (requestParams.modelProvider && resumed.modelProvider !== requestParams.modelProvider) {
|
|
175
|
+
throw Object.assign(new Error("Another native subscriber retained the previous model provider. Close the native assistant terminal and retry."), {
|
|
176
|
+
code: "assistant_codex_provider_binding_retained"
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (!["idle", "active"].includes(await readStatus())) throw new Error("Codex did not confirm the thread loaded.");
|
|
180
|
+
if (nativeStatus !== "notLoaded") {
|
|
181
|
+
await host.verifyEnvironment(threadId, environment, request, signal, {
|
|
182
|
+
requiredKeys: requestParams.developerInstructions == null ? [] : [INSTRUCTION_REVISION_ENV]
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const checked = fixedPolicy ? environment : instructionEnvironment(params, await host.prepareEnvironment(environment));
|
|
186
|
+
if (JSON.stringify(checked) !== JSON.stringify(environment)) {
|
|
187
|
+
throw new Error("Managed controls changed again during thread recovery.");
|
|
188
|
+
}
|
|
189
|
+
if (instructionsChanged) {
|
|
190
|
+
// Resume config supplies the next compacted context, but existing
|
|
191
|
+
// history retains its original developer message. A revision in
|
|
192
|
+
// the developer lane makes this change effective before new work.
|
|
193
|
+
// An unknown binding also needs restoration; ordinary turns and
|
|
194
|
+
// socket reconnects with an acknowledged binding do not repeat it.
|
|
195
|
+
await request("thread/inject_items", { threadId, items: [{ type: "message", role: "developer", content: [{
|
|
196
|
+
type: "input_text", text: "These are the current application instructions. They replace earlier application instructions; other instructions remain in effect.\n\n" + requestParams.developerInstructions
|
|
197
|
+
}] }] });
|
|
198
|
+
assertRecoveryOpen();
|
|
199
|
+
instructionPrompts.set(threadId, requestParams.developerInstructions);
|
|
200
|
+
}
|
|
201
|
+
assertRecoveryOpen();
|
|
202
|
+
bindings.set(threadId, { client, executionId, params: requestParams, managed: !fixedPolicy });
|
|
203
|
+
log("ready");
|
|
204
|
+
}
|
|
205
|
+
} catch (cause) {
|
|
206
|
+
host.onRecoveryFailure?.(threadId);
|
|
207
|
+
bindings.delete(threadId);
|
|
208
|
+
log("recovery_failed", { code: cause?.code || "", reason: cause.message });
|
|
209
|
+
throw Object.assign(new Error(["assistant_agent_control_binding_retained", "assistant_codex_provider_binding_retained"].includes(cause?.code)
|
|
210
|
+
? `${cause.message} Your conversation and work are preserved.`
|
|
211
|
+
: "The assistant connection could not be verified. Your conversation and files are preserved. Retry the connection, or renew the session to continue in a fresh conversation.", { cause }), {
|
|
212
|
+
code: "assistant_agent_control_recovery_failed", retryable: true
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
// Never retry the caller's operation: its outcome may already be durable.
|
|
216
|
+
assertRecoveryOpen();
|
|
217
|
+
const result = await operation(requestParams, resumed);
|
|
218
|
+
if (pausedGoal && goalChanges.get(threadId) === goalChange) {
|
|
219
|
+
const { goal } = await request("thread/goal/get", { threadId });
|
|
220
|
+
if (isSamePausedGoal(goal, pausedGoal) && goal.updatedAt === pausedGoal.updatedAt) {
|
|
221
|
+
await request("thread/goal/set", { threadId, status: "active" });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}, "codex-app-server-thread-controls"));
|
|
226
|
+
tasks.set(threadId, task);
|
|
227
|
+
try {
|
|
228
|
+
return await task;
|
|
229
|
+
} finally {
|
|
230
|
+
if (tasks.get(threadId) === task) tasks.delete(threadId);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return Object.freeze({
|
|
235
|
+
prepareInstructions,
|
|
236
|
+
threadParameters,
|
|
237
|
+
withThreadContext,
|
|
238
|
+
registerThread(id, binding) {
|
|
239
|
+
bindings.set(id, binding);
|
|
240
|
+
if (binding.params.developerInstructions != null) instructionPrompts.set(id, binding.params.developerInstructions);
|
|
241
|
+
},
|
|
242
|
+
goalChanged: (id) => goalChanges.set(id, (goalChanges.get(id) || 0) + 1),
|
|
243
|
+
get bindingCount() { return bindings.size; },
|
|
244
|
+
get threadEnvironments() { return bindings; },
|
|
245
|
+
get threadEnvironmentTasks() { return tasks; },
|
|
246
|
+
close() { contextGeneration += 1; bindings.clear(); instructionPrompts.clear(); }
|
|
247
|
+
});
|
|
248
|
+
}
|