@skaile/workspaces 1.12.1 → 1.12.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.12.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#422](https://github.com/skaile-ai/workspaces/pull/422) [`47a9896`](https://github.com/skaile-ai/workspaces/commit/47a989633a22c0c090fb7c50c4c3355738f73aca) Thanks [@Frozen666](https://github.com/Frozen666)! - Fix a dropped in-flight prompt when a skill-change driver restart races a live
8
+ turn. The runner now serializes prompt turns against out-of-band driver swaps via
9
+ a bidirectional gate: a swap defers to an in-flight turn (so the driver is never
10
+ killed mid-prompt), and a prompt arriving during a swap binds to the fresh driver.
11
+ Previously the killed SDK process emitted no completion event and the UI hung on
12
+ "AI is thinking…" forever.
13
+
3
14
  ## 1.12.1
4
15
 
5
16
  ### Patch Changes
@@ -2939,6 +2939,70 @@ function cleanupCloudCredentialFiles(projectDir) {
2939
2939
  }
2940
2940
  }
2941
2941
 
2942
+ // runner/src/driver-swap-gate.ts
2943
+ var DriverSwapGate = class {
2944
+ /** Resolves when the in-flight driver recreate settles (resolve or reject). */
2945
+ activeDriverSwap = null;
2946
+ /** FIFO chain; resolves once every started turn has settled. */
2947
+ turnTail = Promise.resolve();
2948
+ /**
2949
+ * Mark a turn as in flight. Call at the real `driver.prompt()` dispatch — not
2950
+ * earlier — so a same-handler restart (e.g. restaging skills before the turn)
2951
+ * cannot self-deadlock by awaiting the very turn that spawned it. Returns an
2952
+ * idempotent `endTurn`; call it in a `finally` around the dispatch.
2953
+ */
2954
+ beginTurn() {
2955
+ let resolveTurn;
2956
+ const turn = new Promise((resolve4) => {
2957
+ resolveTurn = resolve4;
2958
+ });
2959
+ this.turnTail = this.turnTail.then(() => turn);
2960
+ let ended = false;
2961
+ return () => {
2962
+ if (ended) return;
2963
+ ended = true;
2964
+ resolveTurn();
2965
+ };
2966
+ }
2967
+ /**
2968
+ * Await any in-flight turn before tearing a driver down. Awaits the FIFO tail
2969
+ * (not a single "latest turn" field) so an earlier turn that outlives a later
2970
+ * one still holds the swap off — the tail resolves only once every started
2971
+ * turn has settled. Never throws: a rejected turn is still a settled turn, and
2972
+ * the swap may proceed (the driver is idle either way).
2973
+ */
2974
+ async awaitActiveTurn() {
2975
+ try {
2976
+ await this.turnTail;
2977
+ } catch {
2978
+ }
2979
+ }
2980
+ /**
2981
+ * Register the in-flight recreate promise so concurrent prompts defer to it.
2982
+ * Auto-clears when `recreate` settles (resolve OR reject) so a failed recreate
2983
+ * never blocks prompts forever.
2984
+ */
2985
+ beginSwap(recreate) {
2986
+ this.activeDriverSwap = recreate;
2987
+ const clear = () => {
2988
+ if (this.activeDriverSwap === recreate) this.activeDriverSwap = null;
2989
+ };
2990
+ recreate.then(clear, clear);
2991
+ }
2992
+ /**
2993
+ * Await the in-flight swap before reading the live driver. Re-checks in a loop
2994
+ * so a second swap starting during the wait is also awaited.
2995
+ */
2996
+ async awaitSwap() {
2997
+ while (this.activeDriverSwap) {
2998
+ try {
2999
+ await this.activeDriverSwap;
3000
+ } catch {
3001
+ }
3002
+ }
3003
+ }
3004
+ };
3005
+
2942
3006
  // runner/src/compaction/prompt.ts
2943
3007
  var DEFAULT_COMPACTION_PROMPT = `You are being asked to compact this conversation into a structured summary.
2944
3008
  This summary will replace the full conversation history when the session
@@ -4837,6 +4901,7 @@ async function startAgentServer(opts) {
4837
4901
  sendEvent(rewriteFileChangedPath(event, opts.projectDir, mounts));
4838
4902
  }
4839
4903
  let driverStarted = false;
4904
+ const driverSwapGate = new DriverSwapGate();
4840
4905
  let driver = secretsMode === "env" ? agentSession.driver : void 0;
4841
4906
  let secretsProvisioned = secretsMode === "env";
4842
4907
  function swapDriver() {
@@ -5250,6 +5315,20 @@ async function startAgentServer(opts) {
5250
5315
  return { needsWireFallbackRecreate, needsWireAgentConfigRecreate };
5251
5316
  }
5252
5317
  async function recreateAgentSession(overrides) {
5318
+ let markSwapDone;
5319
+ driverSwapGate.beginSwap(
5320
+ new Promise((resolve4) => {
5321
+ markSwapDone = resolve4;
5322
+ })
5323
+ );
5324
+ try {
5325
+ await driverSwapGate.awaitActiveTurn();
5326
+ await recreateAgentSessionInner(overrides);
5327
+ } finally {
5328
+ markSwapDone();
5329
+ }
5330
+ }
5331
+ async function recreateAgentSessionInner(overrides) {
5253
5332
  const disposedManager = agentSession.resourceManager;
5254
5333
  await agentSession.dispose();
5255
5334
  agentSession = await createAgentSession({ ...sessionConfig, ...overrides });
@@ -5582,6 +5661,7 @@ async function startAgentServer(opts) {
5582
5661
  return;
5583
5662
  }
5584
5663
  log(`[serve] prompt: ${cmd.prompt.slice(0, 80)}...`);
5664
+ await driverSwapGate.awaitSwap();
5585
5665
  await restageMaterializedSkills(true);
5586
5666
  const activeFlow = getOnlyActiveFlow();
5587
5667
  if (activeFlow) {
@@ -5631,18 +5711,23 @@ async function startAgentServer(opts) {
5631
5711
  });
5632
5712
  }
5633
5713
  sendEvent({ type: "status", phase: "thinking" });
5714
+ const endTurn = driverSwapGate.beginTurn();
5634
5715
  try {
5635
5716
  await driver.prompt(cmd.prompt);
5636
5717
  } catch (err) {
5637
5718
  const errMsg = err instanceof Error ? err.message : String(err);
5638
5719
  log(`[serve] prompt failed: ${errMsg}`);
5639
5720
  sendEvent({ type: "error", message: `Prompt failed: ${errMsg}`, fatal: true });
5721
+ } finally {
5722
+ endTurn();
5640
5723
  }
5641
5724
  }
5642
5725
  async function handleReply(cmd) {
5643
5726
  if (!await awaitSessionReadyOrFail()) return;
5644
5727
  log(`[serve] reply: ${cmd.answer.slice(0, 80)}...`);
5728
+ await driverSwapGate.awaitSwap();
5645
5729
  if (driver.hasPendingQuestion() && driver.answerQuestion(cmd.answer)) return;
5730
+ const endTurn = driverSwapGate.beginTurn();
5646
5731
  try {
5647
5732
  await driver.prompt(cmd.answer);
5648
5733
  } catch (err) {
@@ -5651,6 +5736,8 @@ async function startAgentServer(opts) {
5651
5736
  message: `Reply failed: ${err instanceof Error ? err.message : String(err)}`,
5652
5737
  fatal: false
5653
5738
  });
5739
+ } finally {
5740
+ endTurn();
5654
5741
  }
5655
5742
  }
5656
5743
  async function handleCancel() {
@@ -6225,5 +6312,5 @@ function touchSession(state) {
6225
6312
  }
6226
6313
 
6227
6314
  export { CLAUDE_CODE_CREDENTIALS_KEY, COMPILE_MANIFEST_FILENAME, CapabilityRegistry, DEFAULT_CAPABILITY_CALL_TIMEOUT_MS, DEFAULT_COALESCE_MS, MarkdownStreamer, PreInitRingSink, agentDefinitionExists, bootstrapCapabilityRegistry, bootstrapRunnerLogStore, buildAgentResources, buildClientCapabilityHandler, buildConnectorTokenMediator, buildContextSection, buildEnvironmentSection, buildResourcesAvailablePayload, buildStoreSnapshotReplay, builtinCapabilities, capabilityLogInstance, clearPreInitRingSink, clearSession, compileComposition, computeCapabilitySignature, connectorRefreshKind, createAgentSession, createSessionStimulusBus, defineCapability, deleteSession, dispatchRunnerCapabilityInvocation, emitSystemPromptComposed, ensureGitConfigInclude, extractClaudeAiOauthExpiresAt, getPreInitRingSink, handleMountResourceRequest, handleResourceRequest, hasNonTerminalFlow, hostOpResumeStimulus, installPreInitRingSink, isTerminalFlowStatus, listSessions, loadAgentManifest, loadCompileManifest, loadCompileManifestFromDir, loadSession, loadSessionById, mcpAuthSecretKey, newSession, pickRepointedManager, planFlowMutate, registerActiveFlow, registerCompositionCapabilities, rejectCapabilityOnApprovalDeny, resetRunnerLogStore, resolveAgentComposition, resolveAgentMixins, resolveBinding, resolveCapabilityCallTimeoutMs, resolveCapabilityResult, resolveComposition, resolveMixin, runAgentChat, saveSession, setCurrentSession, startAgentServer, stimulusFromMetas, touchSession, writeClaudeCodeCredentialsFile };
6228
- //# sourceMappingURL=chunk-ZA465CPE.js.map
6229
- //# sourceMappingURL=chunk-ZA465CPE.js.map
6315
+ //# sourceMappingURL=chunk-IO3K4PTB.js.map
6316
+ //# sourceMappingURL=chunk-IO3K4PTB.js.map