@jskit-ai/assistant-core 0.1.213 → 0.1.214

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/examples/conversation/package.json +5 -5
  2. package/fixtures/responsive-assistant/ConversationFixture.vue +7 -1
  3. package/package.json +5 -5
  4. package/src/client/conversation/AssistantConversationElement.vue +5 -1
  5. package/src/client/conversation/AssistantTranscript.vue +10 -1
  6. package/src/server/conversation/agentRun.js +38 -11
  7. package/src/server/conversation/claudeHistory.js +40 -5
  8. package/src/server/conversation/claudeProcess.js +16 -2
  9. package/src/server/conversation/claudeTurn.js +28 -8
  10. package/src/server/conversation/codexConfiguration.js +1 -1
  11. package/src/server/conversation/codexHistoryAdapter.js +11 -4
  12. package/src/server/conversation/codexNotificationQueue.js +11 -2
  13. package/src/server/conversation/codexObservationControl.js +8 -8
  14. package/src/server/conversation/codexOutput.js +20 -15
  15. package/src/server/conversation/codexProvider.js +40 -3
  16. package/src/server/conversation/codexProviderOwner.js +110 -1
  17. package/src/server/conversation/codexTurn.js +3 -0
  18. package/src/server/conversation/continuity.js +24 -6
  19. package/src/server/conversation/index.js +1 -1
  20. package/src/server/conversation/nativeHistoryExport.js +97 -0
  21. package/src/server/conversation/openCodeClient.js +47 -1
  22. package/src/server/conversation/openCodeRuntime.js +132 -24
  23. package/src/server/conversation/openCodeTurn.js +65 -24
  24. package/src/server/conversation/providers/api.js +7 -0
  25. package/src/server/conversation/providers/claudeDriver.js +52 -7
  26. package/src/server/conversation/providers/codex.js +19 -2
  27. package/src/server/conversation/providers/codexDriver.js +92 -14
  28. package/src/server/conversation/providers/opencodeDriver.js +31 -9
  29. package/src/server/conversation/runtime.js +418 -64
  30. package/src/server/conversation/runtimeStateUpgrade.js +77 -2
  31. package/src/server/conversation/streams.js +25 -5
  32. package/src/server/conversation/tools.js +2 -2
  33. package/src/server/conversation/transcript.js +23 -8
  34. package/src/server/hosts/claudeHistory.js +1 -0
  35. package/src/server/hosts/claudeProcess.js +1 -0
  36. package/src/server/hosts/codexProvider.js +3 -0
  37. package/src/server/hosts/nativeHistory.js +1 -0
  38. package/src/server/hosts/openCodeClient.js +1 -0
  39. package/src/server/hosts/openCodeTurn.js +1 -0
  40. package/src/server/index.js +1 -1
  41. package/src/server/lib/assistantToolLoop.js +99 -2
  42. package/test/aiConnectionClient.test.js +57 -0
  43. package/test/assistantBoundedToolLoop.test.js +332 -0
  44. package/test/assistantScroll.browser.test.js +151 -0
  45. package/test/claudeConversationSession.test.js +1329 -3
  46. package/test/claudeProcess.test.js +101 -0
  47. package/test/claudeTurn.test.js +31 -0
  48. package/test/codexConversationSession.test.js +1257 -12
  49. package/test/codexHistoryAdapter.test.js +74 -1
  50. package/test/codexTurn.test.js +282 -1
  51. package/test/conversation.test.js +83 -0
  52. package/test/conversationAgentRun.test.js +181 -0
  53. package/test/conversationRuntime.test.js +95 -0
  54. package/test/conversationRuntimeStateUpgrade.test.js +89 -0
  55. package/test/conversationStreaming.test.js +85 -0
  56. package/test/conversationTools.test.js +112 -1
  57. package/test/openCodeClient.test.js +186 -0
  58. package/test/openCodeRuntime.test.js +78 -0
  59. package/test/opencodeConversationSession.test.js +637 -5
@@ -12,6 +12,8 @@ import { createCodexAppServerRunOwner, codexAppServerTurnState, codexAppServerFr
12
12
  import { createLocalConversationExecution } from "../localExecution.js";
13
13
  import { validateConversationConfiguration, validateConnectionModel } from "../configuration.js";
14
14
  import { codexCommandHookCommand } from "../commandWrapper.js";
15
+ import { isCompletedEnvelopeTurn } from "../transcript.js";
16
+ import { createInertCodexConversationBinding } from "../runtimeStateUpgrade.js";
15
17
 
16
18
  const hash = value => createHash("sha256").update(JSON.stringify(value)).digest("hex");
17
19
  const goalValue = goal => goal ? {
@@ -221,7 +223,7 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
221
223
  return native.runOwner.runDetachedConversation(sessionId, input, options);
222
224
  },
223
225
  async createBinding() {
224
- return { threadId: "", workdir: await realpath(workdir), configRoot, executionId: "" };
226
+ return createInertCodexConversationBinding({ workdir: await realpath(workdir), configRoot });
225
227
  },
226
228
  async open({ binding, writeBinding, onFailure: reportFailure, conversation }) {
227
229
  const supplied = conversation?.native;
@@ -281,8 +283,29 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
281
283
  const boundedWriter = name => (id, input) => {
282
284
  const limit = name === "writeConversationThinkingMessage" ? 4 * 1024 * 1024
283
285
  : name.includes("AssistantMessage") ? Math.min(maximumOutput, maximumFinalReply) : maximumOutput;
284
- if ((input.text?.length || 0) > limit) throw new Error("Codex output exceeded the configured limit.");
285
- return nativeStore[name](id, input);
286
+ const length = input.text?.length || 0;
287
+ if (length <= limit) return nativeStore[name](id, input);
288
+ if (!name.includes("AssistantMessage") || length > maximumOutput) throw new Error("Codex output exceeded the configured limit.");
289
+ return nativeStore.mutateSession(id, async transaction => {
290
+ const { runtime: state } = await transaction.readMetadata();
291
+ const identity = input.nativeIdentity;
292
+ const run = state.binding?.codexAppServerRun;
293
+ if (identity?.threadId && identity.turnId && state.engine === "codex" &&
294
+ state.binding.threadId === identity.threadId && run?.providerThreadId === identity.threadId &&
295
+ run.providerTurnId === identity.turnId && run.outerTurnId) {
296
+ for (const turnId of (await transaction.listTurnIds()).reverse()) {
297
+ const turn = await transaction.readTurn(turnId);
298
+ if (turn?.system?.messageId !== run.outerTurnId) continue;
299
+ if (turn.system.origin === "application" && !turn.system.goal && isCompletedEnvelopeTurn(turn) &&
300
+ turn.metadata.runtime.engine === "codex" && turn.metadata.runtime.segmentId === state.segmentId &&
301
+ turn.metadata.runtime.nativeTurnId === identity.turnId) {
302
+ return nativeStore[name](id, input);
303
+ }
304
+ break;
305
+ }
306
+ }
307
+ throw new Error("Codex output exceeded the configured limit.");
308
+ });
286
309
  };
287
310
  const runtime = supplied ? conversation.runtime : { ...conversation.runtime, store: { ...nativeStore,
288
311
  writeConversationAssistantMessage: boundedWriter("writeConversationAssistantMessage"),
@@ -303,6 +326,7 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
303
326
  }
304
327
  } };
305
328
  const owner = supplied?.runOwner || createCodexAppServerRunOwner({
329
+ persistCommentary: conversation.persistCommentary,
306
330
  finalizingGraceMs: limits.codexFinalizingGraceMs,
307
331
  finalizingGraceAfterHistoryRead: limits.codexFinalizingGraceAfterHistoryRead,
308
332
  failureDetailGraceMs: limits.codexFailureDetailGraceMs,
@@ -331,6 +355,8 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
331
355
  }
332
356
  }
333
357
  });
358
+ // Retain this queue's exact opening namespace, never its admission grant.
359
+ const drainNotifications = owner.notificationQueue.captureDrain(sessionId);
334
360
  async function publishNative(event) {
335
361
  if (supplied) binding.threadId = await conversation.identity.read();
336
362
  await conversation.publish(event);
@@ -406,10 +432,11 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
406
432
  } });
407
433
  }
408
434
 
409
- async function completeObservation({ provider, error, sharedStop }) {
435
+ async function completeObservation({ provider, error, sharedStop, threadIds }) {
436
+ if (threadIds && !threadIds.includes(binding.threadId)) return;
410
437
  await updateBinding({ observationLoss: { ...binding.observationLoss, stopped: true },
411
438
  ...(sharedStop ? { executionId: "" } : {}) });
412
- await observation.complete({ provider, error, sharedStop });
439
+ await observation.complete({ provider, error, sharedStop, threadIds });
413
440
  observationError = null;
414
441
  await reportFailure(error, { recovered: true });
415
442
  current?.completion.reject(error);
@@ -469,6 +496,7 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
469
496
  create: onObservationLost => new CodexAppServerAgentProvider({ ...providerOptions, onObservationLost }, facilities),
470
497
  observation: {
471
498
  async prepare(input) {
499
+ if (input.threadIds && !input.threadIds.includes(binding.threadId)) return new Map();
472
500
  observationError = input.error;
473
501
  await reportFailure(input.error);
474
502
  const threads = await observation.prepare(input);
@@ -476,6 +504,7 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
476
504
  return threads;
477
505
  },
478
506
  async barrier(input) {
507
+ if (input.threadIds && !input.threadIds.includes(binding.threadId)) return;
479
508
  await observation.barrier(input);
480
509
  await recordObservationBarrier(input);
481
510
  },
@@ -514,8 +543,14 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
514
543
  return stopping;
515
544
  }
516
545
 
517
- async function selection(configuration, context, signal) {
518
- if (!configuration.integrationId) return { model: configuration.model, providerId: "openai" };
546
+ async function selection(configuration, context, signal, requireBound = false, receiptBinding) {
547
+ if (!configuration.integrationId) {
548
+ if (requireBound && !receiptBinding?.accountIdentity) throw new Error("The completed Codex response has no saved native account fingerprint.");
549
+ if (requireBound && binding.accountIdentity !== receiptBinding.accountIdentity) {
550
+ throw new Error("The completed Codex response belongs to a different native account binding.");
551
+ }
552
+ return { model: configuration.model, providerId: "openai" };
553
+ }
519
554
  const connection = await connections.resolve({ context, integrationId: configuration.integrationId });
520
555
  signal.throwIfAborted();
521
556
  const provider = nativeAiProvider(connection.providerId);
@@ -525,6 +560,11 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
525
560
  if (configuration.effort && !model.variants.includes(configuration.effort)) throw new Error("The selected Codex model does not support this reasoning effort.");
526
561
  const identity = hash([provider.id, provider.baseUrl, connection.apiKey]);
527
562
  const previous = binding.connectionIdentities?.[provider.id];
563
+ const expected = receiptBinding?.connectionIdentities?.[provider.id];
564
+ if (requireBound && !expected) throw new Error("The completed Codex response has no saved provider account fingerprint.");
565
+ if (requireBound && (previous !== expected || identity !== expected)) {
566
+ throw new Error("The completed Codex response belongs to a different provider account binding.");
567
+ }
528
568
  if (previous && previous !== identity) throw new Error("This conversation belongs to another Codex provider account. Restore that connection or start a new conversation.");
529
569
  if (!previous) await updateBinding({ connectionIdentities: { ...binding.connectionIdentities, [provider.id]: identity } });
530
570
  return { model: model.id, providerId: provider.id, accountIdentity: identity, config: codexProviderConfiguration(provider, connection.apiKey) };
@@ -832,6 +872,13 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
832
872
  await native.ensureAvailable();
833
873
  return native;
834
874
  }
875
+ }).catch(error => {
876
+ // A thrown control error also leaves this native owner unconfirmed.
877
+ if (error === current?.toolFailure && !current.signal.aborted && !error.cleanupFailed) {
878
+ current.toolFailureCleanup = error;
879
+ }
880
+ error.cleanupFailed = true;
881
+ throw error;
835
882
  });
836
883
  if (result.value?.ok === false) throw Object.assign(new Error(result.value.error || "Codex could not confirm that work stopped."), { cleanupFailed: true });
837
884
  return result.value;
@@ -901,7 +948,7 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
901
948
  if (result.ok === false) throw new Error(result.error);
902
949
  return goalValue(result.goal);
903
950
  },
904
- async inspectAdmission({ messageId, threadId, nativeTurnId, configuration, context, goal: command, representation }) {
951
+ async inspectAdmission({ messageId, threadId, nativeTurnId, configuration, context, goal: command, representation, verifyAccount = false, receiptBinding }) {
905
952
  if (supplied) {
906
953
  const prepared = await supplied.inspectionPreparation({ messageId, threadId }, context);
907
954
  if (prepared.ok === false) return representation === "native" ? prepared : { accepted: false, ...prepared };
@@ -915,9 +962,13 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
915
962
  // cannot certify the authored delivery invented by that adapter.
916
963
  return { accepted: false, recoveryLimitation: "This older goal-message record requires offline inspection. Its saved history and delivery evidence were preserved." };
917
964
  }
965
+ if (verifyAccount === true && (!receiptBinding || threadId !== binding.threadId || receiptBinding.threadId !== binding.threadId ||
966
+ receiptBinding.workdir !== binding.workdir || receiptBinding.configRoot !== binding.configRoot)) {
967
+ throw new Error("The completed Codex response belongs to a different native conversation or host scope.");
968
+ }
918
969
  if (!binding.threadId) return { accepted: false };
919
970
  const signal = AbortSignal.timeout(30_000);
920
- await connect(signal, await selection(configuration, context, signal));
971
+ await connect(signal, await selection(configuration, context, signal, verifyAccount === true, receiptBinding));
921
972
  let acceptedTurn;
922
973
  let cursor;
923
974
  do {
@@ -973,6 +1024,7 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
973
1024
  onEvent, onNativeTurn, signal, completion, admission, inputReady: admission.promise, finishIfCurrent,
974
1025
  admitted: false, dispatched: false, turnId: "" };
975
1026
  let cancellation;
1027
+ let toolFailureRecovery;
976
1028
  let problem;
977
1029
  let delivered;
978
1030
  let waitForTurn = false;
@@ -1007,7 +1059,15 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
1007
1059
  owned = false;
1008
1060
  throw new Error("This application tool call belongs to another turn.");
1009
1061
  }
1010
- const result = await tools.execute({ id: params.callId, name: params.tool, arguments: JSON.stringify(params.arguments) });
1062
+ let result;
1063
+ try {
1064
+ result = await tools.execute({ id: params.callId, name: params.tool, arguments: JSON.stringify(params.arguments) });
1065
+ } catch (error) {
1066
+ if (!supplied && owned && current === active && !disposed && !signal.aborted &&
1067
+ provider === executingProvider && provider === native &&
1068
+ params.threadId === binding.threadId && params.turnId === active.turnId) active.toolFailure ||= error;
1069
+ throw error;
1070
+ }
1011
1071
  return { success: result.ok, contentItems: [{ type: "inputText", text: JSON.stringify(result) }] };
1012
1072
  })();
1013
1073
  toolCalls.add(operation);
@@ -1075,7 +1135,7 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
1075
1135
  waitForTurn = !supplied || delivered.value?.delivered === true;
1076
1136
  if (!finishIfCurrent && waitForTurn) {
1077
1137
  await completion.promise;
1078
- await owner.notificationQueue.drain(sessionId);
1138
+ await drainNotifications();
1079
1139
  }
1080
1140
  } catch (error) { problem = error; }
1081
1141
  if (finishIfCurrent) {
@@ -1092,23 +1152,41 @@ export function createCodexConversationDriver({ connections, host = {}, limits =
1092
1152
  problem = undefined;
1093
1153
  waitForTurn = true;
1094
1154
  }
1095
- if (!problem && waitForTurn) await owner.notificationQueue.drain(sessionId);
1155
+ if (!problem && waitForTurn) await drainNotifications();
1096
1156
  } catch (error) { problem = error; }
1097
1157
  }
1098
1158
  signal.removeEventListener("abort", abort);
1099
1159
  admission.reject(problem || signal.reason);
1160
+ const failureThreadId = binding.threadId;
1161
+ const failureTurnId = active.turnId;
1100
1162
  try {
1101
1163
  // Account invalidation already owns this stop attempt. Its failed
1102
1164
  // proof stays available for explicit cleanup through the same owner.
1103
1165
  if ((!supplied && problem || supplied && active.toolFailure || signal.aborted) && problem?.code !== "codex_runtime_invalidated") {
1104
1166
  await (cancellation || interrupt());
1105
1167
  }
1106
- } catch (error) { problem = error; }
1168
+ } catch (error) {
1169
+ problem = error;
1170
+ // A direct Stop checkpoint can throw before its native completion
1171
+ // reaches the queue. Keep the same admitted tool failure with the
1172
+ // original observation-stop owner until its pending recovery joins.
1173
+ if (!supplied && error === active.toolFailure && current === active && !disposed && !signal.aborted &&
1174
+ executingProvider && executingProvider === native && binding.threadId === failureThreadId &&
1175
+ active.turnId === failureTurnId && error.code !== "codex_runtime_invalidated") {
1176
+ toolFailureRecovery = executingProvider.failObservation(error);
1177
+ }
1178
+ }
1107
1179
  try {
1108
- await owner.notificationQueue.drain(sessionId);
1180
+ await drainNotifications();
1109
1181
  // The original queue starts observation recovery without awaiting
1110
1182
  // it. Join only this provider's existing attempt before settling.
1111
1183
  if (!supplied) await providerOwner.pendingRecovery(providerKey, executingProvider);
1184
+ if (toolFailureRecovery) {
1185
+ await toolFailureRecovery;
1186
+ if (problem === active.toolFailureCleanup && current === active && !disposed && !signal.aborted &&
1187
+ executingProvider === native && binding.threadId === failureThreadId && active.turnId === failureTurnId &&
1188
+ !observationError && binding.observationLoss?.stopped === true) delete problem.cleanupFailed;
1189
+ }
1112
1190
  } catch (error) { problem = error; }
1113
1191
  finally {
1114
1192
  for (const owned of toolRegistrations.values()) owned.registration.release();
@@ -234,7 +234,7 @@ export function createOpenCodeConversationDriver({ connections, host = {}, limit
234
234
  maxArgumentBytes: active.tools.maximumArgumentBytes, execute(input, signal) {
235
235
  const turn = supplied.owner.turns.get(acquired.key);
236
236
  const target = supplied.owner.processes.get(acquired.key);
237
- return executeOpenCodeApplicationTool({ active: current, turn, target,
237
+ const execute = () => executeOpenCodeApplicationTool({ active: current, turn, target,
238
238
  sessionId: target?.upstreamSessionId,
239
239
  assertCurrent(throwFailure = true, authored) {
240
240
  const owned = !disposed && current === active && active.turn === turn && turn?.active &&
@@ -244,6 +244,7 @@ export function createOpenCodeConversationDriver({ connections, host = {}, limit
244
244
  return owned;
245
245
  }
246
246
  }, input, signal);
247
+ return typeof acquired.runInContext === "function" ? acquired.runInContext(execute) : execute();
247
248
  }
248
249
  });
249
250
  }
@@ -417,23 +418,39 @@ export function createOpenCodeConversationDriver({ connections, host = {}, limit
417
418
  }).finally(() => { stopping = null; });
418
419
  return stopping;
419
420
  }
420
- async function resolvePreparation(configuration, context, signal) {
421
+ async function resolvePreparation(configuration, context, signal, requireBound = false, receiptBinding, authoredPreparation = false) {
421
422
  const connection = await connections.resolve({ context, integrationId: configuration.integrationId });
422
423
  signal.throwIfAborted();
423
424
  validateConnectionModel(configuration, connection);
424
425
  const identity = hash([connection.providerId, connection.baseURL || "", connection.apiKey]);
425
- if (binding.accountIdentity && identity !== binding.accountIdentity) {
426
+ if (requireBound && !receiptBinding?.accountIdentity) throw new Error("The completed OpenCode response has no saved account fingerprint.");
427
+ if (requireBound && (binding.accountIdentity !== receiptBinding.accountIdentity || identity !== receiptBinding.accountIdentity)) {
428
+ throw new Error("The completed OpenCode response belongs to a different account binding.");
429
+ }
430
+ // The existing managed host reauthorizes its own connection on every preparation.
431
+ // A key change can rotate an authored run only inside the same native storage/runtime scope.
432
+ const preparedFacilities = host.opencode ? await host.opencode({ connection, context }) : facilities;
433
+ signal.throwIfAborted();
434
+ if (host.opencode && (
435
+ binding.databasePath && binding.databasePath !== preparedFacilities.databasePath ||
436
+ binding.runtimeDirectory && binding.runtimeDirectory !== preparedFacilities.runtimeDirectory ||
437
+ facilities && facilities.runtime !== preparedFacilities.runtime
438
+ )) {
439
+ throw new Error("This OpenCode conversation belongs to another native history store or runtime. Restore its original host configuration.");
440
+ }
441
+ const allowCredentialRefresh = authoredPreparation && host.opencode && preparedFacilities.allowCredentialRefresh === true;
442
+ if (binding.accountIdentity && identity !== binding.accountIdentity && !allowCredentialRefresh) {
426
443
  throw new Error("This conversation belongs to another OpenCode account. Restore that connection or start a new conversation.");
427
444
  }
428
- if (!binding.accountIdentity) await updateBinding({ accountIdentity: identity });
445
+ if (binding.accountIdentity !== identity) await updateBinding({ accountIdentity: identity });
429
446
  const model = { providerID: connection.providerId, id: connection.model };
430
447
  const agent = openCodeConversationAgent({ nativeTools, tools: Boolean(current?.tools) });
431
448
  if (current) current.agent = agent;
432
449
  if (binding.sessionId && !binding.databasePath) {
433
450
  throw new Error("This OpenCode conversation uses the earlier private native history layout. Run the offline native-history upgrade before continuing; no history was changed.");
434
451
  }
435
- if (!facilities) {
436
- if (host.opencode) facilities = await host.opencode({ connection, context });
452
+ if (!facilities || host.opencode) {
453
+ if (host.opencode) facilities = preparedFacilities;
437
454
  else {
438
455
  const runtimeDirectory = path.join(stateDirectory, "shared", hash([identity, host.commands?.opencode || "opencode"]));
439
456
  const selected = { modelProviderId: connection.providerId, canonicalUrl: connection.baseURL || "",
@@ -649,9 +666,14 @@ export function createOpenCodeConversationDriver({ connections, host = {}, limit
649
666
  }
650
667
 
651
668
  return Object.freeze({
652
- async inspectAdmission({ messageId, configuration, context }) {
669
+ async inspectAdmission({ messageId, threadId, configuration, context, verifyAccount = false, receiptBinding }) {
670
+ if (verifyAccount === true && (!receiptBinding || threadId !== binding.sessionId || receiptBinding.sessionId !== binding.sessionId ||
671
+ receiptBinding.workdir !== binding.workdir || receiptBinding.directory !== binding.directory ||
672
+ receiptBinding.databasePath !== binding.databasePath || receiptBinding.runtimeDirectory !== binding.runtimeDirectory)) {
673
+ throw new Error("The completed OpenCode response belongs to a different native conversation or host scope.");
674
+ }
653
675
  if (!binding.sessionId) return { accepted: false, messages: [] };
654
- const prepared = await prepare(await resolvePreparation(configuration, context, AbortSignal.timeout(120_000)));
676
+ const prepared = await prepare(await resolvePreparation(configuration, context, AbortSignal.timeout(120_000), verifyAccount === true, receiptBinding));
655
677
  await ensureOpenCodeSession(prepared.target, prepared.session);
656
678
  const id = nativeMessageId(messageId);
657
679
  const { accepted = false, messages = [] } = await inspectOpenCodeMessageAdmission(native.server.client, binding.sessionId, id) || {};
@@ -671,7 +693,7 @@ export function createOpenCodeConversationDriver({ connections, host = {}, limit
671
693
  let problem;
672
694
  try {
673
695
  active.resolved = await resolvePreparation(command.configuration, command.context,
674
- AbortSignal.any([command.signal, AbortSignal.timeout(120_000)]));
696
+ AbortSignal.any([command.signal, AbortSignal.timeout(120_000)]), false, undefined, true);
675
697
  let dispatchFailure;
676
698
  try { await send(command); }
677
699
  catch (error) { dispatchFailure = error; }