@skaile/workspaces 1.12.0 → 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 +17 -0
- package/dist/{chunk-SORYPIC3.js → chunk-IO3K4PTB.js} +115 -6
- package/dist/chunk-IO3K4PTB.js.map +1 -0
- package/dist/{chunk-EN42DDM6.js → chunk-YSEKT6AV.js} +2 -2
- package/dist/{chunk-EN42DDM6.js.map → chunk-YSEKT6AV.js.map} +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/factory-assets/connectors/flow/run-flow.js +1 -1
- package/dist/runner/index.js +1 -1
- package/dist/runner/src/driver-swap-gate.d.ts +44 -0
- package/dist/runner/src/driver-swap-gate.d.ts.map +1 -0
- package/dist/runner/src/serve.d.ts +56 -3
- package/dist/runner/src/serve.d.ts.map +1 -1
- package/dist/sdk/index.js +1 -1
- package/dist/sdk/runner.js +1 -1
- package/dist/tui/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-SORYPIC3.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
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
|
+
|
|
14
|
+
## 1.12.1
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- [#419](https://github.com/skaile-ai/workspaces/pull/419) [`da98cc1`](https://github.com/skaile-ai/workspaces/commit/da98cc1b86636267e1f0855134fbeb229869ae2a) Thanks [@henkbla](https://github.com/henkbla)! - serve: stop treating a finished flow run as an active one. `activeFlows` was never pruned, so a run that reached `complete` / `cancelled` permanently blocked `flow.start` in a warm session — with a reject message advertising a remedy ("cancel it first") that could not work, since cancel leaves the entry in place — and kept re-emitting its settled snapshot on every reconnect. The `start` guard now rejects only on a non-terminal run, and a finished run is dropped when the next one registers. `failed` and `paused` stay non-terminal (`retryNode` revives them). A settled run is still replayed on connect until it is superseded: the turn loop keeps driving with no host attached, so that replay is the only path by which a host that was detached when the run finished ever learns it did.
|
|
19
|
+
|
|
3
20
|
## 1.12.0
|
|
4
21
|
|
|
5
22
|
### Minor 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
|
|
@@ -4108,6 +4172,26 @@ function buildResourcesAvailablePayload(manager, mcpManager, driverLive, bakedSe
|
|
|
4108
4172
|
}));
|
|
4109
4173
|
return { type: "resources_available", mounts, connectors, mcp_servers };
|
|
4110
4174
|
}
|
|
4175
|
+
function isTerminalFlowStatus(status) {
|
|
4176
|
+
return status === "complete" || status === "cancelled";
|
|
4177
|
+
}
|
|
4178
|
+
function hasNonTerminalFlow(flows) {
|
|
4179
|
+
for (const f of flows) {
|
|
4180
|
+
if (!isTerminalFlowStatus(f.adapter.getExecution(f.handle).status)) return true;
|
|
4181
|
+
}
|
|
4182
|
+
return false;
|
|
4183
|
+
}
|
|
4184
|
+
function registerActiveFlow(flows, runId, entry) {
|
|
4185
|
+
const removed = [];
|
|
4186
|
+
for (const [key, existing] of flows) {
|
|
4187
|
+
if (isTerminalFlowStatus(existing.adapter.getExecution(existing.handle).status)) {
|
|
4188
|
+
flows.delete(key);
|
|
4189
|
+
removed.push(key);
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
flows.set(runId, entry);
|
|
4193
|
+
return removed;
|
|
4194
|
+
}
|
|
4111
4195
|
async function buildStoreSnapshotReplay(manager, activeFlows) {
|
|
4112
4196
|
const events = [];
|
|
4113
4197
|
for (const { runId, adapter, handle } of activeFlows) {
|
|
@@ -4258,10 +4342,10 @@ function planFlowMutate(cmd, ctx) {
|
|
|
4258
4342
|
reason: "flow.start payload missing required fields (need payload.flow.id, payload.seed.runId, payload.seed.startedBy)"
|
|
4259
4343
|
};
|
|
4260
4344
|
}
|
|
4261
|
-
if (ctx.
|
|
4345
|
+
if (ctx.hasRunningFlow) {
|
|
4262
4346
|
return {
|
|
4263
4347
|
kind: "reject",
|
|
4264
|
-
reason: "flow.start ignored: a flow is
|
|
4348
|
+
reason: "flow.start ignored: a flow is still running (one active flow per session; cancel it, then start the next)"
|
|
4265
4349
|
};
|
|
4266
4350
|
}
|
|
4267
4351
|
return { kind: "create-start", flowDef, seed };
|
|
@@ -4817,6 +4901,7 @@ async function startAgentServer(opts) {
|
|
|
4817
4901
|
sendEvent(rewriteFileChangedPath(event, opts.projectDir, mounts));
|
|
4818
4902
|
}
|
|
4819
4903
|
let driverStarted = false;
|
|
4904
|
+
const driverSwapGate = new DriverSwapGate();
|
|
4820
4905
|
let driver = secretsMode === "env" ? agentSession.driver : void 0;
|
|
4821
4906
|
let secretsProvisioned = secretsMode === "env";
|
|
4822
4907
|
function swapDriver() {
|
|
@@ -5012,7 +5097,8 @@ async function startAgentServer(opts) {
|
|
|
5012
5097
|
}
|
|
5013
5098
|
}
|
|
5014
5099
|
const entry = { runId, flowDef, adapter, handle, connectorId };
|
|
5015
|
-
activeFlows
|
|
5100
|
+
const pruned = registerActiveFlow(activeFlows, runId, entry);
|
|
5101
|
+
if (pruned.length > 0) serverLog.debug("pruned terminal flow runs", { runIds: pruned });
|
|
5016
5102
|
return entry;
|
|
5017
5103
|
}
|
|
5018
5104
|
const refreshFlagPath = join(opts.projectDir, ".skaile", "refresh-request");
|
|
@@ -5229,6 +5315,20 @@ async function startAgentServer(opts) {
|
|
|
5229
5315
|
return { needsWireFallbackRecreate, needsWireAgentConfigRecreate };
|
|
5230
5316
|
}
|
|
5231
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) {
|
|
5232
5332
|
const disposedManager = agentSession.resourceManager;
|
|
5233
5333
|
await agentSession.dispose();
|
|
5234
5334
|
agentSession = await createAgentSession({ ...sessionConfig, ...overrides });
|
|
@@ -5561,6 +5661,7 @@ async function startAgentServer(opts) {
|
|
|
5561
5661
|
return;
|
|
5562
5662
|
}
|
|
5563
5663
|
log(`[serve] prompt: ${cmd.prompt.slice(0, 80)}...`);
|
|
5664
|
+
await driverSwapGate.awaitSwap();
|
|
5564
5665
|
await restageMaterializedSkills(true);
|
|
5565
5666
|
const activeFlow = getOnlyActiveFlow();
|
|
5566
5667
|
if (activeFlow) {
|
|
@@ -5610,18 +5711,23 @@ async function startAgentServer(opts) {
|
|
|
5610
5711
|
});
|
|
5611
5712
|
}
|
|
5612
5713
|
sendEvent({ type: "status", phase: "thinking" });
|
|
5714
|
+
const endTurn = driverSwapGate.beginTurn();
|
|
5613
5715
|
try {
|
|
5614
5716
|
await driver.prompt(cmd.prompt);
|
|
5615
5717
|
} catch (err) {
|
|
5616
5718
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
5617
5719
|
log(`[serve] prompt failed: ${errMsg}`);
|
|
5618
5720
|
sendEvent({ type: "error", message: `Prompt failed: ${errMsg}`, fatal: true });
|
|
5721
|
+
} finally {
|
|
5722
|
+
endTurn();
|
|
5619
5723
|
}
|
|
5620
5724
|
}
|
|
5621
5725
|
async function handleReply(cmd) {
|
|
5622
5726
|
if (!await awaitSessionReadyOrFail()) return;
|
|
5623
5727
|
log(`[serve] reply: ${cmd.answer.slice(0, 80)}...`);
|
|
5728
|
+
await driverSwapGate.awaitSwap();
|
|
5624
5729
|
if (driver.hasPendingQuestion() && driver.answerQuestion(cmd.answer)) return;
|
|
5730
|
+
const endTurn = driverSwapGate.beginTurn();
|
|
5625
5731
|
try {
|
|
5626
5732
|
await driver.prompt(cmd.answer);
|
|
5627
5733
|
} catch (err) {
|
|
@@ -5630,6 +5736,8 @@ async function startAgentServer(opts) {
|
|
|
5630
5736
|
message: `Reply failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
5631
5737
|
fatal: false
|
|
5632
5738
|
});
|
|
5739
|
+
} finally {
|
|
5740
|
+
endTurn();
|
|
5633
5741
|
}
|
|
5634
5742
|
}
|
|
5635
5743
|
async function handleCancel() {
|
|
@@ -5678,6 +5786,7 @@ async function startAgentServer(opts) {
|
|
|
5678
5786
|
targetExists: !!targetEntry,
|
|
5679
5787
|
isExistingFlow,
|
|
5680
5788
|
hasActiveFlow: activeFlows.size > 0,
|
|
5789
|
+
hasRunningFlow: hasNonTerminalFlow(activeFlows.values()),
|
|
5681
5790
|
onlyActiveConnectorId: getOnlyActiveFlow()?.connectorId ?? null
|
|
5682
5791
|
});
|
|
5683
5792
|
switch (plan.kind) {
|
|
@@ -6202,6 +6311,6 @@ function touchSession(state) {
|
|
|
6202
6311
|
return { ...state, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
6203
6312
|
}
|
|
6204
6313
|
|
|
6205
|
-
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, hostOpResumeStimulus, installPreInitRingSink, listSessions, loadAgentManifest, loadCompileManifest, loadCompileManifestFromDir, loadSession, loadSessionById, mcpAuthSecretKey, newSession, pickRepointedManager, planFlowMutate, registerCompositionCapabilities, rejectCapabilityOnApprovalDeny, resetRunnerLogStore, resolveAgentComposition, resolveAgentMixins, resolveBinding, resolveCapabilityCallTimeoutMs, resolveCapabilityResult, resolveComposition, resolveMixin, runAgentChat, saveSession, setCurrentSession, startAgentServer, stimulusFromMetas, touchSession, writeClaudeCodeCredentialsFile };
|
|
6206
|
-
//# sourceMappingURL=chunk-
|
|
6207
|
-
//# sourceMappingURL=chunk-
|
|
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 };
|
|
6315
|
+
//# sourceMappingURL=chunk-IO3K4PTB.js.map
|
|
6316
|
+
//# sourceMappingURL=chunk-IO3K4PTB.js.map
|