@bastani/atomic 0.9.19 → 0.9.20-alpha.1
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 +6 -0
- package/dist/builtin/intercom/CHANGELOG.md +6 -0
- package/dist/builtin/intercom/index.bundle.mjs +15 -1
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +8 -0
- package/dist/builtin/workflows/builtin/{chunk-nkrafh9s.js → chunk-ewkkkbak.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-1jth021m.js → chunk-zqtm1d2c.js} +1 -1
- package/dist/builtin/workflows/builtin/goal.js +2 -2
- package/dist/builtin/workflows/builtin/index.js +3 -3
- package/dist/builtin/workflows/builtin/ralph.js +2 -2
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +312 -67
- package/dist/builtin/workflows/src/index.js +231 -56
- package/docs/background-tasks.md +2 -0
- package/docs/workflows/operations.md +8 -0
- package/npm-shrinkwrap.json +32 -32
- package/package.json +3 -3
- /package/dist/builtin/workflows/builtin/{chunk-ngkqkzej.js → chunk-m0xfsef8.js} +0 -0
|
@@ -2304,6 +2304,13 @@ function isTopLevelWorkflowRun(run) {
|
|
|
2304
2304
|
}
|
|
2305
2305
|
|
|
2306
2306
|
// dist/builtin/workflows/src/shared/timing.ts
|
|
2307
|
+
function stageTimingFields(stage = {}) {
|
|
2308
|
+
return {
|
|
2309
|
+
...stage.startedAt !== undefined ? { startedAt: stage.startedAt } : {},
|
|
2310
|
+
...stage.endedAt !== undefined ? { endedAt: stage.endedAt } : {},
|
|
2311
|
+
...stage.durationMs !== undefined ? { durationMs: stage.durationMs } : {}
|
|
2312
|
+
};
|
|
2313
|
+
}
|
|
2307
2314
|
function nonNegative(ms) {
|
|
2308
2315
|
return Math.max(0, ms);
|
|
2309
2316
|
}
|
|
@@ -2329,6 +2336,8 @@ function elapsedStageMs(stage, now = Date.now()) {
|
|
|
2329
2336
|
return nonNegative(stage.durationMs);
|
|
2330
2337
|
if (stage.startedAt === undefined)
|
|
2331
2338
|
return;
|
|
2339
|
+
if (stage.replayed && stage.endedAt === undefined)
|
|
2340
|
+
return;
|
|
2332
2341
|
const effectiveNow = stage.endedAt ?? now;
|
|
2333
2342
|
return elapsedFromStart(stage.startedAt, effectiveNow, stage.pausedDurationMs, stage.pausedAt);
|
|
2334
2343
|
}
|
|
@@ -5994,6 +6003,7 @@ function cachedStageId(runId, replayKey) {
|
|
|
5994
6003
|
function stageMetadataCheckpointId(replayKey, stage) {
|
|
5995
6004
|
return `${stableCheckpointId("stage-meta", replayKey)}:${durableHash({
|
|
5996
6005
|
stageId: stage.id,
|
|
6006
|
+
...stage.replayed === true ? { replayed: true } : {},
|
|
5997
6007
|
status: stage.status,
|
|
5998
6008
|
endedAt: stage.endedAt ?? 0,
|
|
5999
6009
|
durationMs: stage.durationMs ?? 0,
|
|
@@ -6001,11 +6011,9 @@ function stageMetadataCheckpointId(replayKey, stage) {
|
|
|
6001
6011
|
})}`;
|
|
6002
6012
|
}
|
|
6003
6013
|
function recordCachedStageIntoStore(store, runId, name, replayKey, output, completedStageReplayKeys, parentIds, checkpoint) {
|
|
6004
|
-
const now = Date.now();
|
|
6005
6014
|
const sourceStageId = checkpoint?.topology?.run?.runId === runId ? checkpoint.topology.stageId : undefined;
|
|
6006
6015
|
const stageId = sourceStageId ?? cachedStageId(runId, replayKey);
|
|
6007
6016
|
const result = checkpoint?.result ?? (typeof output === "string" ? output : JSON.stringify(output));
|
|
6008
|
-
const endedAt = checkpoint?.endedAt ?? checkpoint?.completedAt ?? now;
|
|
6009
6017
|
const hasCurrentIdentity = checkpoint?.topology?.sourceOrder !== undefined || checkpoint?.topology?.status !== undefined || checkpoint?.topology?.occurrenceKey !== undefined || checkpoint?.topology?.boundary !== undefined;
|
|
6010
6018
|
const childResult = parseWorkflowChildResult(output) ?? (hasCurrentIdentity ? undefined : parseLegacyWorkflowChildResult(output));
|
|
6011
6019
|
const workflowChild = childResult === undefined ? undefined : workflowChildSnapshotFromResult(childResult);
|
|
@@ -6015,9 +6023,7 @@ function recordCachedStageIntoStore(store, runId, name, replayKey, output, compl
|
|
|
6015
6023
|
name,
|
|
6016
6024
|
status: "completed",
|
|
6017
6025
|
parentIds: parentIds !== undefined ? Object.freeze([...parentIds]) : [],
|
|
6018
|
-
|
|
6019
|
-
endedAt,
|
|
6020
|
-
durationMs: checkpoint?.durationMs ?? 0,
|
|
6026
|
+
...stageTimingFields(checkpoint),
|
|
6021
6027
|
result,
|
|
6022
6028
|
replayKey,
|
|
6023
6029
|
replayed: true,
|
|
@@ -8908,7 +8914,7 @@ function appendStageStart(api, payload) {
|
|
|
8908
8914
|
...payload.replayKey !== undefined ? { replayKey: payload.replayKey } : {},
|
|
8909
8915
|
...payload.replayedFromStageId !== undefined ? { replayedFromStageId: payload.replayedFromStageId } : {},
|
|
8910
8916
|
...payload.replayed !== undefined ? { replayed: payload.replayed } : {},
|
|
8911
|
-
ts: payload.ts
|
|
8917
|
+
...payload.ts !== undefined ? { ts: payload.ts } : {}
|
|
8912
8918
|
});
|
|
8913
8919
|
}
|
|
8914
8920
|
function appendStageEnd(api, payload, opts) {
|
|
@@ -8919,6 +8925,7 @@ function appendStageEnd(api, payload, opts) {
|
|
|
8919
8925
|
stageId: payload.stageId,
|
|
8920
8926
|
status: payload.status,
|
|
8921
8927
|
...payload.durationMs !== undefined ? { durationMs: payload.durationMs } : {},
|
|
8928
|
+
...payload.endedAt !== undefined ? { endedAt: payload.endedAt } : {},
|
|
8922
8929
|
...payload.summary !== undefined ? { summary: payload.summary } : {},
|
|
8923
8930
|
...payload.error !== undefined ? { error: payload.error } : {},
|
|
8924
8931
|
...payload.failureKind !== undefined ? { failureKind: payload.failureKind } : {},
|
|
@@ -9622,6 +9629,26 @@ function createContinuationReplayIndex(continuation, sourceToContinuationNodeIds
|
|
|
9622
9629
|
}
|
|
9623
9630
|
|
|
9624
9631
|
// dist/builtin/workflows/src/runs/foreground/executor-prompt-nodes.ts
|
|
9632
|
+
function continuationPromptAnswer(store, sourceRun, sourceStage) {
|
|
9633
|
+
let run = sourceRun;
|
|
9634
|
+
let stage = sourceStage;
|
|
9635
|
+
const visited = new Set;
|
|
9636
|
+
while (!visited.has(stage.id)) {
|
|
9637
|
+
visited.add(stage.id);
|
|
9638
|
+
const answer = store.getStagePromptAnswer(run.id, stage.id);
|
|
9639
|
+
if (answer !== undefined)
|
|
9640
|
+
return answer;
|
|
9641
|
+
if (!stage.replayed || stage.replayedFromStageId === undefined || run.resumedFromRunId === undefined)
|
|
9642
|
+
break;
|
|
9643
|
+
const parentRun = store.runs().find((candidate) => candidate.id === run.resumedFromRunId);
|
|
9644
|
+
const parentStage = parentRun?.stages.find((candidate) => candidate.id === stage.replayedFromStageId);
|
|
9645
|
+
if (parentRun === undefined || parentStage === undefined)
|
|
9646
|
+
break;
|
|
9647
|
+
run = parentRun;
|
|
9648
|
+
stage = parentStage;
|
|
9649
|
+
}
|
|
9650
|
+
return;
|
|
9651
|
+
}
|
|
9625
9652
|
function buildPromptNodeUiAdapter(input) {
|
|
9626
9653
|
const ask = async (descriptor, durableReplay) => {
|
|
9627
9654
|
input.throwIfWorkflowExitSelected();
|
|
@@ -9636,6 +9663,9 @@ function buildPromptNodeUiAdapter(input) {
|
|
|
9636
9663
|
const prompt = makePrompt(descriptor);
|
|
9637
9664
|
const replayKey = promptReplayKey(descriptor);
|
|
9638
9665
|
const durableTopology = input.durableTopologyForReplayKey?.(replayKey);
|
|
9666
|
+
if (durableTopology?.status === "completed" && durableReplay === undefined) {
|
|
9667
|
+
throw new Error(`insufficient_state: missing durable UI answer for completed prompt ${durableTopology.stageId}`);
|
|
9668
|
+
}
|
|
9639
9669
|
const stageId = durableTopology?.stageId ?? crypto.randomUUID();
|
|
9640
9670
|
const provisionalParentIds = input.tracker.onSpawn(stageId, descriptor.kind);
|
|
9641
9671
|
const replayDecision = input.replayIndex.decide({
|
|
@@ -9649,7 +9679,7 @@ function buildPromptNodeUiAdapter(input) {
|
|
|
9649
9679
|
if (!sameStringSet(parentIds, provisionalParentIds))
|
|
9650
9680
|
input.tracker.replaceParents(stageId, parentIds);
|
|
9651
9681
|
const replaySource = replayDecision.source;
|
|
9652
|
-
const continuationAnswer = replayDecision.kind === "replay" ? input.activeStore
|
|
9682
|
+
const continuationAnswer = replayDecision.kind === "replay" ? continuationPromptAnswer(input.activeStore, input.opts.continuation.source, replayDecision.source) : undefined;
|
|
9653
9683
|
const replayAnswer = durableReplay === undefined ? continuationAnswer : { value: durableReplay.response };
|
|
9654
9684
|
const shouldReplay = replayAnswer !== undefined;
|
|
9655
9685
|
if (shouldReplay && durableReplay === undefined)
|
|
@@ -9662,13 +9692,11 @@ function buildPromptNodeUiAdapter(input) {
|
|
|
9662
9692
|
replayKey,
|
|
9663
9693
|
status: shouldReplay ? "completed" : "running",
|
|
9664
9694
|
parentIds: Object.freeze(parentIds),
|
|
9665
|
-
startedAt: prompt.createdAt,
|
|
9695
|
+
...shouldReplay ? stageTimingFields(durableReplay === undefined ? replaySource : input.durableTimingForStageId?.(stageId)) : { startedAt: prompt.createdAt },
|
|
9666
9696
|
promptFootprint: { ...prompt },
|
|
9667
9697
|
toolEvents: [],
|
|
9668
9698
|
attachable: !shouldReplay,
|
|
9669
9699
|
...shouldReplay ? {
|
|
9670
|
-
endedAt: prompt.createdAt,
|
|
9671
|
-
durationMs: 0,
|
|
9672
9700
|
promptAnswerState: promptAnswerStatus,
|
|
9673
9701
|
replayedFromStageId: replaySourceId,
|
|
9674
9702
|
replayed: true
|
|
@@ -9698,8 +9726,10 @@ function buildPromptNodeUiAdapter(input) {
|
|
|
9698
9726
|
pauseGate = undefined;
|
|
9699
9727
|
currentPauseGate?.resolve();
|
|
9700
9728
|
stageSnapshot.status = status;
|
|
9701
|
-
|
|
9702
|
-
|
|
9729
|
+
if (!shouldReplay) {
|
|
9730
|
+
stageSnapshot.endedAt = Date.now();
|
|
9731
|
+
stageSnapshot.durationMs = elapsedStageMs(stageSnapshot, stageSnapshot.endedAt);
|
|
9732
|
+
}
|
|
9703
9733
|
input.activeStore.recordStageAttachable(input.runId, stageId, false);
|
|
9704
9734
|
input.activeStore.recordStageEnd(input.runId, stageSnapshot);
|
|
9705
9735
|
await input.opts.onStageEnd?.(input.runId, stageSnapshot);
|
|
@@ -9709,6 +9739,7 @@ function buildPromptNodeUiAdapter(input) {
|
|
|
9709
9739
|
stageId,
|
|
9710
9740
|
status: stageSnapshot.status,
|
|
9711
9741
|
durationMs: stageSnapshot.durationMs,
|
|
9742
|
+
endedAt: stageSnapshot.endedAt,
|
|
9712
9743
|
...stageSnapshot.error !== undefined ? { error: stageSnapshot.error } : {},
|
|
9713
9744
|
...stageSnapshot.failureKind !== undefined ? { failureKind: stageSnapshot.failureKind } : {},
|
|
9714
9745
|
...stageSnapshot.failureCode !== undefined ? { failureCode: stageSnapshot.failureCode } : {},
|
|
@@ -9775,7 +9806,7 @@ function buildPromptNodeUiAdapter(input) {
|
|
|
9775
9806
|
name: stageSnapshot.name,
|
|
9776
9807
|
parentIds: stageSnapshot.parentIds,
|
|
9777
9808
|
...stageReplayFields(stageSnapshot),
|
|
9778
|
-
ts:
|
|
9809
|
+
ts: stageSnapshot.startedAt
|
|
9779
9810
|
});
|
|
9780
9811
|
}
|
|
9781
9812
|
if (shouldReplay) {
|
|
@@ -11312,7 +11343,7 @@ function wrapUiWithDurable(base, deps) {
|
|
|
11312
11343
|
// dist/builtin/workflows/src/engine/primitives/ui.ts
|
|
11313
11344
|
function buildExitGatedUiContext(input) {
|
|
11314
11345
|
const base = input.opts.usePromptNodesForUi === true ? input.baseFromPromptNodes() : input.opts.executionMode === "non_interactive" && input.opts.ui === undefined ? makeHeadlessUnavailableUIContext() : normalizeUIContext(input.opts.ui);
|
|
11315
|
-
const promptNodeReplay = input.opts.usePromptNodesForUi === true && input.opts.continuation !== undefined;
|
|
11346
|
+
const promptNodeReplay = input.opts.usePromptNodesForUi === true && input.opts.continuation !== undefined && input.opts.continuation.source.id !== input.durableUi?.workflowId;
|
|
11316
11347
|
const durableBase = input.durableUi !== undefined && !promptNodeReplay ? wrapUiWithDurable(base, input.durableUi) : base;
|
|
11317
11348
|
const invoke = (call) => {
|
|
11318
11349
|
input.throwIfWorkflowExitSelected();
|
|
@@ -11935,7 +11966,7 @@ function mergeStageDraft(existing, checkpoint, sequence) {
|
|
|
11935
11966
|
...valueOrExisting("thinkingLevel", checkpoint, existing),
|
|
11936
11967
|
...valueOrExisting("attemptedModels", checkpoint, existing),
|
|
11937
11968
|
...valueOrExisting("modelAttempts", checkpoint, existing),
|
|
11938
|
-
...checkpoint.topology !== undefined ? { topology: checkpoint.topology } : existing?.topology !== undefined ? { topology: existing.topology } : {}
|
|
11969
|
+
...existing?.topology?.run !== undefined && checkpoint.topology?.run === undefined ? { topology: existing.topology } : checkpoint.topology !== undefined ? { topology: checkpoint.topology } : existing?.topology !== undefined ? { topology: existing.topology } : {}
|
|
11939
11970
|
};
|
|
11940
11971
|
}
|
|
11941
11972
|
function valueOrExisting(key, checkpoint, existing) {
|
|
@@ -13226,13 +13257,11 @@ function createWorkflowBoundaryFactory(input) {
|
|
|
13226
13257
|
replayKey,
|
|
13227
13258
|
status: replayedChild !== undefined ? "completed" : "running",
|
|
13228
13259
|
parentIds: Object.freeze([...parentIds]),
|
|
13229
|
-
startedAt,
|
|
13260
|
+
...replayedChild === undefined ? { startedAt } : stageTimingFields(replaySource),
|
|
13230
13261
|
toolEvents: [],
|
|
13231
13262
|
attachable: false,
|
|
13232
13263
|
...replaySource !== undefined ? { replayedFromStageId: replaySource.id, replayed: replayedChild !== undefined } : {},
|
|
13233
13264
|
...replayedChild !== undefined && replayChildSnapshot !== undefined ? {
|
|
13234
|
-
endedAt: startedAt,
|
|
13235
|
-
durationMs: 0,
|
|
13236
13265
|
...replayDecision.kind === "replay" && replayDecision.source.result !== undefined ? { result: replayDecision.source.result } : {},
|
|
13237
13266
|
workflowChild: cloneWorkflowChildReplaySnapshot(replayChildSnapshot)
|
|
13238
13267
|
} : {}
|
|
@@ -13249,7 +13278,7 @@ function createWorkflowBoundaryFactory(input) {
|
|
|
13249
13278
|
name,
|
|
13250
13279
|
parentIds: stageSnapshot.parentIds,
|
|
13251
13280
|
...stageReplayFields(stageSnapshot),
|
|
13252
|
-
ts: startedAt
|
|
13281
|
+
ts: stageSnapshot.startedAt
|
|
13253
13282
|
});
|
|
13254
13283
|
};
|
|
13255
13284
|
const appendStageEndForSnapshot = () => {
|
|
@@ -13260,6 +13289,7 @@ function createWorkflowBoundaryFactory(input) {
|
|
|
13260
13289
|
stageId,
|
|
13261
13290
|
status: stageSnapshot.status,
|
|
13262
13291
|
durationMs: stageSnapshot.durationMs,
|
|
13292
|
+
endedAt: stageSnapshot.endedAt,
|
|
13263
13293
|
...stageSnapshot.error !== undefined ? { error: stageSnapshot.error } : {},
|
|
13264
13294
|
...stageSnapshot.failureKind !== undefined ? { failureKind: stageSnapshot.failureKind } : {},
|
|
13265
13295
|
...stageSnapshot.failureCode !== undefined ? { failureCode: stageSnapshot.failureCode } : {},
|
|
@@ -13296,8 +13326,10 @@ function createWorkflowBoundaryFactory(input) {
|
|
|
13296
13326
|
clearBoundaryChildMetadata();
|
|
13297
13327
|
applyFailureToStage(stageSnapshot, input.classifyExecutorFailure(failureError));
|
|
13298
13328
|
}
|
|
13299
|
-
|
|
13300
|
-
|
|
13329
|
+
if (replayedChild === undefined) {
|
|
13330
|
+
stageSnapshot.endedAt = Date.now();
|
|
13331
|
+
stageSnapshot.durationMs = elapsedStageMs(stageSnapshot, stageSnapshot.endedAt);
|
|
13332
|
+
}
|
|
13301
13333
|
input.activeStore.recordStageEnd(input.runId, stageSnapshot);
|
|
13302
13334
|
input.opts.onStageEnd?.(input.runId, stageSnapshot);
|
|
13303
13335
|
appendStageEndForSnapshot();
|
|
@@ -13365,6 +13397,12 @@ function createWorkflowBoundaryFactory(input) {
|
|
|
13365
13397
|
// dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts
|
|
13366
13398
|
import { runCallback as runCallback3, runSynchronousCallback } from "@bastani/atomic";
|
|
13367
13399
|
|
|
13400
|
+
// dist/builtin/workflows/src/shared/pending-stage-route-readiness.ts
|
|
13401
|
+
var owners = new WeakMap;
|
|
13402
|
+
function workflowPendingStageRouteReady(store, runId) {
|
|
13403
|
+
return owners.get(store)?.ready(runId);
|
|
13404
|
+
}
|
|
13405
|
+
|
|
13368
13406
|
// dist/builtin/workflows/src/runs/foreground/executor-queued-user-message.ts
|
|
13369
13407
|
function removedMessages(before, after) {
|
|
13370
13408
|
const remaining = [...after];
|
|
@@ -14259,7 +14297,7 @@ function createReplayStageContext(input) {
|
|
|
14259
14297
|
name,
|
|
14260
14298
|
parentIds: stageSnapshot.parentIds,
|
|
14261
14299
|
...stageReplayFields(stageSnapshot),
|
|
14262
|
-
ts: stageSnapshot.startedAt
|
|
14300
|
+
ts: stageSnapshot.startedAt
|
|
14263
14301
|
});
|
|
14264
14302
|
};
|
|
14265
14303
|
const appendReplayStageEnd = () => {
|
|
@@ -14269,7 +14307,8 @@ function createReplayStageContext(input) {
|
|
|
14269
14307
|
runId,
|
|
14270
14308
|
stageId,
|
|
14271
14309
|
status: stageSnapshot.status,
|
|
14272
|
-
durationMs: stageSnapshot.durationMs
|
|
14310
|
+
durationMs: stageSnapshot.durationMs,
|
|
14311
|
+
endedAt: stageSnapshot.endedAt,
|
|
14273
14312
|
...stageSnapshot.status === "completed" && stageSnapshot.result !== undefined ? { summary: stageSnapshot.result } : {},
|
|
14274
14313
|
...stageSnapshot.skippedReason !== undefined ? { skippedReason: stageSnapshot.skippedReason } : {},
|
|
14275
14314
|
...stageSnapshot.sessionId !== undefined ? { sessionId: stageSnapshot.sessionId } : {},
|
|
@@ -14287,8 +14326,6 @@ function createReplayStageContext(input) {
|
|
|
14287
14326
|
delete stageSnapshot.result;
|
|
14288
14327
|
stageSnapshot.skippedReason = input.workflowExitSkippedReason(reason);
|
|
14289
14328
|
}
|
|
14290
|
-
stageSnapshot.endedAt = Date.now();
|
|
14291
|
-
stageSnapshot.durationMs = elapsedStageMs(stageSnapshot, stageSnapshot.endedAt);
|
|
14292
14329
|
input.activeStore.recordStageEnd(runId, stageSnapshot);
|
|
14293
14330
|
input.opts.onStageEnd?.(runId, stageSnapshot);
|
|
14294
14331
|
appendReplayStageEnd();
|
|
@@ -15673,6 +15710,21 @@ class StageSessionBindingCleanupFailure extends AggregateError {
|
|
|
15673
15710
|
this.name = "StageSessionBindingCleanupFailure";
|
|
15674
15711
|
}
|
|
15675
15712
|
}
|
|
15713
|
+
async function cleanupFailedStageSessionBinding(current, bindingError) {
|
|
15714
|
+
const cleanupErrors = [];
|
|
15715
|
+
try {
|
|
15716
|
+
await shutdownStageSession(current);
|
|
15717
|
+
} catch (error) {
|
|
15718
|
+
cleanupErrors.push(error);
|
|
15719
|
+
}
|
|
15720
|
+
try {
|
|
15721
|
+
await current.dispose();
|
|
15722
|
+
} catch (error) {
|
|
15723
|
+
cleanupErrors.push(error);
|
|
15724
|
+
}
|
|
15725
|
+
if (cleanupErrors.length > 0)
|
|
15726
|
+
throw new StageSessionBindingCleanupFailure(bindingError, cleanupErrors);
|
|
15727
|
+
}
|
|
15676
15728
|
async function disposeStageSession(current) {
|
|
15677
15729
|
if (!current)
|
|
15678
15730
|
return;
|
|
@@ -15993,6 +16045,27 @@ function terminatingToolCallId(event) {
|
|
|
15993
16045
|
return typeof callId === "string" && callId.length > 0 ? callId : undefined;
|
|
15994
16046
|
}
|
|
15995
16047
|
|
|
16048
|
+
// dist/builtin/workflows/src/runs/foreground/stage-startup-wait.ts
|
|
16049
|
+
function waitForStageStartup(operation, signal) {
|
|
16050
|
+
return new Promise((resolve, reject) => {
|
|
16051
|
+
const onAbort = () => {
|
|
16052
|
+
signal.removeEventListener("abort", onAbort);
|
|
16053
|
+
reject(signal.reason ?? new DOMException("Stage startup cancelled", "AbortError"));
|
|
16054
|
+
};
|
|
16055
|
+
operation.then((value) => {
|
|
16056
|
+
signal.removeEventListener("abort", onAbort);
|
|
16057
|
+
resolve(value);
|
|
16058
|
+
}, (error) => {
|
|
16059
|
+
signal.removeEventListener("abort", onAbort);
|
|
16060
|
+
reject(error);
|
|
16061
|
+
});
|
|
16062
|
+
if (signal.aborted)
|
|
16063
|
+
onAbort();
|
|
16064
|
+
else
|
|
16065
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
16066
|
+
});
|
|
16067
|
+
}
|
|
16068
|
+
|
|
15996
16069
|
// dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts
|
|
15997
16070
|
function hasMeaningfulUsage(usage) {
|
|
15998
16071
|
if (usage === undefined)
|
|
@@ -16073,6 +16146,9 @@ class StageSessionController {
|
|
|
16073
16146
|
activeCreation;
|
|
16074
16147
|
ownedCreationPromise;
|
|
16075
16148
|
abortGeneration = 0;
|
|
16149
|
+
routeAuthorityWait;
|
|
16150
|
+
startupWait = new AbortController;
|
|
16151
|
+
startup;
|
|
16076
16152
|
abortReason;
|
|
16077
16153
|
abortReasonGeneration = 0;
|
|
16078
16154
|
sessionPromise;
|
|
@@ -16232,25 +16308,28 @@ class StageSessionController {
|
|
|
16232
16308
|
}
|
|
16233
16309
|
if (this.disposed)
|
|
16234
16310
|
throw new Error(`atomic-workflows: stage "${this.opts.stageName}" session has been disposed`);
|
|
16235
|
-
|
|
16311
|
+
this.opts.signal?.throwIfAborted();
|
|
16312
|
+
if (this.session !== undefined && this.activeCreation === undefined)
|
|
16236
16313
|
return this.session;
|
|
16237
16314
|
if (!this.sessionPromise) {
|
|
16315
|
+
this.beginStartup();
|
|
16238
16316
|
const pending = this.createInitialSession(consumer);
|
|
16239
16317
|
this.sessionPromise = pending;
|
|
16240
16318
|
this.ownedCreationPromise = pending;
|
|
16241
|
-
const release = () => {
|
|
16319
|
+
const release = (failed = false) => {
|
|
16242
16320
|
if (this.ownedCreationPromise === pending)
|
|
16243
16321
|
this.ownedCreationPromise = undefined;
|
|
16322
|
+
this.settleStartup(failed);
|
|
16244
16323
|
};
|
|
16245
|
-
pending.then(release, () => {
|
|
16246
|
-
release();
|
|
16324
|
+
pending.then(() => release(), () => {
|
|
16325
|
+
release(true);
|
|
16247
16326
|
if (this.sessionPromise === pending) {
|
|
16248
16327
|
this.sessionPromise = undefined;
|
|
16249
16328
|
this.activeCandidateIndex = undefined;
|
|
16250
16329
|
}
|
|
16251
16330
|
});
|
|
16252
16331
|
}
|
|
16253
|
-
return this.sessionPromise;
|
|
16332
|
+
return waitForStageStartup(this.sessionPromise, this.startupWait.signal);
|
|
16254
16333
|
}
|
|
16255
16334
|
async ensureSessionFromFile(sessionFile, consumer = "prompt") {
|
|
16256
16335
|
if (!this.sessionShutdownPromise && !this.sessionPromise && !this.session)
|
|
@@ -16298,6 +16377,10 @@ class StageSessionController {
|
|
|
16298
16377
|
this.artifactCapture.close();
|
|
16299
16378
|
}
|
|
16300
16379
|
async promptWithFallback(text, sdkOptions, consumer = "prompt") {
|
|
16380
|
+
if (this.session !== undefined && this.activeCreation === undefined && this.ownedCreationPromise === undefined && this.startupWait.signal.aborted) {
|
|
16381
|
+
this.opts.signal?.throwIfAborted();
|
|
16382
|
+
this.startupWait = new AbortController;
|
|
16383
|
+
}
|
|
16301
16384
|
if (!this.hasExplicitModelFallbackConfig) {
|
|
16302
16385
|
try {
|
|
16303
16386
|
const activeSession = await this.ensureSession(consumer);
|
|
@@ -16320,7 +16403,13 @@ class StageSessionController {
|
|
|
16320
16403
|
}
|
|
16321
16404
|
return;
|
|
16322
16405
|
}
|
|
16323
|
-
const
|
|
16406
|
+
const readinessOwner = this.ownedCreationPromise ?? this.activeCreation;
|
|
16407
|
+
if (readinessOwner !== undefined)
|
|
16408
|
+
await waitForStageStartup(readinessOwner, this.startupWait.signal);
|
|
16409
|
+
if (this.session === undefined)
|
|
16410
|
+
this.beginStartup();
|
|
16411
|
+
const startupSignal = this.startupWait.signal;
|
|
16412
|
+
const candidates = await waitForStageStartup(this.modelCandidates(), startupSignal);
|
|
16324
16413
|
if (candidates.length === 0) {
|
|
16325
16414
|
try {
|
|
16326
16415
|
const activeSession = await this.ensureSession(consumer);
|
|
@@ -16336,14 +16425,9 @@ class StageSessionController {
|
|
|
16336
16425
|
}
|
|
16337
16426
|
return;
|
|
16338
16427
|
}
|
|
16339
|
-
|
|
16340
|
-
|
|
16341
|
-
|
|
16342
|
-
} catch (error) {
|
|
16343
|
-
if (error instanceof StageSessionCreationCancelled)
|
|
16344
|
-
return;
|
|
16345
|
-
}
|
|
16346
|
-
}
|
|
16428
|
+
const creationOwner = this.ownedCreationPromise ?? this.activeCreation;
|
|
16429
|
+
if (creationOwner !== undefined)
|
|
16430
|
+
await waitForStageStartup(creationOwner, startupSignal);
|
|
16347
16431
|
const resumedText = this.pendingCreationResumeMessage;
|
|
16348
16432
|
this.pendingCreationResumeMessage = undefined;
|
|
16349
16433
|
let promptText = resumedText ?? text;
|
|
@@ -16351,9 +16435,9 @@ class StageSessionController {
|
|
|
16351
16435
|
return;
|
|
16352
16436
|
let index = this.activeCandidateIndex ?? 0;
|
|
16353
16437
|
while (index < candidates.length) {
|
|
16354
|
-
if (this.
|
|
16438
|
+
if (this.ownedCreationPromise !== undefined || this.activeCreation !== undefined) {
|
|
16355
16439
|
try {
|
|
16356
|
-
await this.ownedCreationPromise;
|
|
16440
|
+
await waitForStageStartup(this.ownedCreationPromise ?? this.activeCreation, startupSignal);
|
|
16357
16441
|
} catch (error) {
|
|
16358
16442
|
if (error instanceof StageSessionCreationCancelled)
|
|
16359
16443
|
return;
|
|
@@ -16365,7 +16449,7 @@ class StageSessionController {
|
|
|
16365
16449
|
}
|
|
16366
16450
|
const candidate = candidates[index];
|
|
16367
16451
|
try {
|
|
16368
|
-
const created = this.session && this.activeCandidateIndex === index ? this.session : await this.createSessionWithThrownErrorRetry(candidate, consumer);
|
|
16452
|
+
const created = this.session && this.activeCandidateIndex === index ? this.session : await waitForStageStartup(this.createSessionWithThrownErrorRetry(candidate, consumer), startupSignal);
|
|
16369
16453
|
if (isSessionCreationPauseResult(created)) {
|
|
16370
16454
|
if (created.resumeMessage === undefined)
|
|
16371
16455
|
return;
|
|
@@ -16392,8 +16476,10 @@ class StageSessionController {
|
|
|
16392
16476
|
const failure = await this.handleCandidateFailure(err, candidate, candidates, index);
|
|
16393
16477
|
if (failure === "handled")
|
|
16394
16478
|
return;
|
|
16395
|
-
if (failure === "throw")
|
|
16479
|
+
if (failure === "throw") {
|
|
16480
|
+
this.settleStartup(true);
|
|
16396
16481
|
throw err;
|
|
16482
|
+
}
|
|
16397
16483
|
index += 1;
|
|
16398
16484
|
}
|
|
16399
16485
|
}
|
|
@@ -16436,7 +16522,8 @@ class StageSessionController {
|
|
|
16436
16522
|
this.messageAdmission.dispose();
|
|
16437
16523
|
this.deliveryActivity.dispose();
|
|
16438
16524
|
await this.replacement.dispose();
|
|
16439
|
-
|
|
16525
|
+
if (this.activeCreation === undefined)
|
|
16526
|
+
await disposeStageSession(this.session);
|
|
16440
16527
|
}
|
|
16441
16528
|
async drainPendingDisposal() {
|
|
16442
16529
|
if (!this.pendingDisposal)
|
|
@@ -16510,6 +16597,12 @@ class StageSessionController {
|
|
|
16510
16597
|
this.abortGeneration += 1;
|
|
16511
16598
|
this.abortReason = reason;
|
|
16512
16599
|
this.abortReasonGeneration = this.abortGeneration;
|
|
16600
|
+
this.routeAuthorityWait?.abort(reason);
|
|
16601
|
+
this.startupWait.abort(reason);
|
|
16602
|
+
if (this.startup?.state === "active") {
|
|
16603
|
+
this.startup = { ...this.startup, state: "cancelled" };
|
|
16604
|
+
this.opts.onStartupChange?.(this.startup);
|
|
16605
|
+
}
|
|
16513
16606
|
this.abortThrownErrorRetries(reason);
|
|
16514
16607
|
}
|
|
16515
16608
|
pauseThrownErrorRetries(resume) {
|
|
@@ -16750,10 +16843,13 @@ class StageSessionController {
|
|
|
16750
16843
|
return this.candidatesPromise;
|
|
16751
16844
|
}
|
|
16752
16845
|
async createInitialSession(consumer) {
|
|
16846
|
+
const generation = this.abortGeneration;
|
|
16753
16847
|
if (!this.hasExplicitModelFallbackConfig) {
|
|
16754
16848
|
return this.createSessionObservingPause(undefined, consumer).catch((error) => this.createInitialSessionWithRetry(undefined, consumer, { error }));
|
|
16755
16849
|
}
|
|
16756
16850
|
const candidates = await this.modelCandidates();
|
|
16851
|
+
if (this.abortGeneration !== generation || this.opts.signal?.aborted || this.disposed)
|
|
16852
|
+
throw this.staleCreationReason(generation);
|
|
16757
16853
|
const initialIndex = this.activeCandidateIndex ?? 0;
|
|
16758
16854
|
const first = candidates[initialIndex];
|
|
16759
16855
|
if (first === undefined) {
|
|
@@ -16858,7 +16954,7 @@ class StageSessionController {
|
|
|
16858
16954
|
if (errorSettingsManager !== undefined)
|
|
16859
16955
|
this.sessionSettingsManager = errorSettingsManager;
|
|
16860
16956
|
const decision = isWorkflowPendingStageDeliveryFailure(error) ? undefined : nextRetryDecision(this.retrySettings(), retryAttempt, isRetryableSameModelFailure(error));
|
|
16861
|
-
if (decision === undefined || this.disposed || this.opts.signal?.aborted === true || this.capturedStructuredOutputForAttempt()) {
|
|
16957
|
+
if (decision === undefined || this.disposed || this.opts.signal?.aborted === true || this.startupWait.signal.aborted || this.capturedStructuredOutputForAttempt()) {
|
|
16862
16958
|
throw error;
|
|
16863
16959
|
}
|
|
16864
16960
|
retryAttempt = decision.attempt;
|
|
@@ -16901,16 +16997,72 @@ class StageSessionController {
|
|
|
16901
16997
|
return this.activeCreation;
|
|
16902
16998
|
if (this.session !== undefined)
|
|
16903
16999
|
return Promise.resolve(this.session);
|
|
17000
|
+
this.startupWait.signal.throwIfAborted();
|
|
16904
17001
|
const creation = this.createSessionAttempt(candidate, consumer, resumeOptions);
|
|
16905
17002
|
this.activeCreation = creation;
|
|
16906
17003
|
creation.finally(() => {
|
|
16907
17004
|
if (this.activeCreation === creation)
|
|
16908
17005
|
this.activeCreation = undefined;
|
|
17006
|
+
this.settleStartup();
|
|
16909
17007
|
}).catch(() => {});
|
|
16910
17008
|
return creation;
|
|
16911
17009
|
}
|
|
17010
|
+
settleStartup(failed = false) {
|
|
17011
|
+
if (this.activeCreation !== undefined || this.ownedCreationPromise !== undefined || this.startup === undefined)
|
|
17012
|
+
return;
|
|
17013
|
+
if (this.startup.state === "dispatched" || this.startup.phase === "ready")
|
|
17014
|
+
return;
|
|
17015
|
+
if (this.startup.state === "active" && !failed && this.bindingCleanupFailure === undefined)
|
|
17016
|
+
return;
|
|
17017
|
+
this.startup = {
|
|
17018
|
+
...this.startup,
|
|
17019
|
+
state: this.startup.state === "active" ? "failed" : this.startup.state,
|
|
17020
|
+
ownershipPending: this.bindingCleanupFailure !== undefined,
|
|
17021
|
+
...this.bindingCleanupFailure === undefined ? { settledAt: Date.now() } : {}
|
|
17022
|
+
};
|
|
17023
|
+
this.opts.onStartupChange?.(this.startup);
|
|
17024
|
+
}
|
|
17025
|
+
beginStartup() {
|
|
17026
|
+
if (this.activeCreation !== undefined || this.ownedCreationPromise !== undefined || this.sessionPromise !== undefined)
|
|
17027
|
+
return;
|
|
17028
|
+
this.opts.signal?.throwIfAborted();
|
|
17029
|
+
if (this.startupWait.signal.aborted)
|
|
17030
|
+
this.startupWait = new AbortController;
|
|
17031
|
+
this.reportStartupPhase("model-resolution", true);
|
|
17032
|
+
}
|
|
17033
|
+
reportStartupPhase(phase, reset = false) {
|
|
17034
|
+
if (!reset && this.startup !== undefined && this.startup.state !== "active")
|
|
17035
|
+
return;
|
|
17036
|
+
const now = Date.now();
|
|
17037
|
+
this.startup = {
|
|
17038
|
+
phase,
|
|
17039
|
+
startedAt: reset ? now : this.startup?.startedAt ?? now,
|
|
17040
|
+
phaseStartedAt: now,
|
|
17041
|
+
state: phase === "first-dispatch" ? "dispatched" : "active",
|
|
17042
|
+
ownershipPending: phase !== "first-dispatch" && phase !== "ready"
|
|
17043
|
+
};
|
|
17044
|
+
this.opts.onStartupChange?.(this.startup);
|
|
17045
|
+
}
|
|
16912
17046
|
async createSessionAttempt(candidate, consumer, resumeOptions) {
|
|
16913
17047
|
const startGeneration = this.abortGeneration;
|
|
17048
|
+
if (this.disposed || this.opts.signal?.aborted)
|
|
17049
|
+
throw this.staleCreationReason(startGeneration);
|
|
17050
|
+
this.reportStartupPhase("route-authority");
|
|
17051
|
+
const authority = this.opts.routeAuthorityReady?.();
|
|
17052
|
+
if (authority !== undefined) {
|
|
17053
|
+
const wait = new AbortController;
|
|
17054
|
+
this.routeAuthorityWait = wait;
|
|
17055
|
+
try {
|
|
17056
|
+
await raceAbort2(authority.completion, wait.signal);
|
|
17057
|
+
authority.assertCurrent();
|
|
17058
|
+
if (this.disposed || this.opts.signal?.aborted || this.abortGeneration !== startGeneration)
|
|
17059
|
+
throw this.staleCreationReason(startGeneration);
|
|
17060
|
+
} finally {
|
|
17061
|
+
if (this.routeAuthorityWait === wait)
|
|
17062
|
+
this.routeAuthorityWait = undefined;
|
|
17063
|
+
}
|
|
17064
|
+
}
|
|
17065
|
+
this.reportStartupPhase("resource-preparation");
|
|
16914
17066
|
this.applyCandidateThinking(candidate);
|
|
16915
17067
|
const stageOptions = buildStageSessionOptions({
|
|
16916
17068
|
effectiveStageOptions: this.effectiveStageOptions,
|
|
@@ -16923,6 +17075,11 @@ class StageSessionController {
|
|
|
16923
17075
|
try {
|
|
16924
17076
|
created = this.opts.adapters.agentSession ? await this.opts.adapters.agentSession.create(stripWorkflowOnlyOptions(stageOptions, this.opts.defaultSessionDir, this.meta, this.opts.pendingStageDelivery), {
|
|
16925
17077
|
...this.meta,
|
|
17078
|
+
startupSignal: this.startupWait.signal,
|
|
17079
|
+
onStartupPhase: (phase) => {
|
|
17080
|
+
if (this.abortGeneration === startGeneration)
|
|
17081
|
+
this.reportStartupPhase(phase);
|
|
17082
|
+
},
|
|
16926
17083
|
stageOptions,
|
|
16927
17084
|
...this.sharedOrchestrationContext !== undefined ? { orchestrationContext: this.sharedOrchestrationContext } : {}
|
|
16928
17085
|
}) : missingAdapter(consumer);
|
|
@@ -16941,20 +17098,28 @@ class StageSessionController {
|
|
|
16941
17098
|
throw new Error(`atomic-workflows: stage "${this.opts.stageName}" session has been disposed`);
|
|
16942
17099
|
throw this.staleCreationReason(startGeneration);
|
|
16943
17100
|
}
|
|
17101
|
+
this.reportStartupPhase("session-attachment");
|
|
16944
17102
|
const session = attachCreatedStageSession(created, this.disposed, this.opts.stageName, (result) => this.attachSession(result));
|
|
16945
17103
|
const attachedSession = session instanceof Promise ? await session : session;
|
|
16946
|
-
|
|
16947
|
-
|
|
17104
|
+
this.reportStartupPhase("delivery-readiness");
|
|
17105
|
+
try {
|
|
17106
|
+
await this.sharedOrchestrationContext?.pendingStageDelivery?.ready();
|
|
17107
|
+
await this.opts.onSessionReady?.();
|
|
17108
|
+
if (this.disposed || this.opts.signal?.aborted || this.abortGeneration !== startGeneration)
|
|
17109
|
+
throw this.staleCreationReason(startGeneration);
|
|
17110
|
+
} catch (reason) {
|
|
17111
|
+
if (this.session === attachedSession)
|
|
17112
|
+
this.session = undefined;
|
|
16948
17113
|
try {
|
|
16949
|
-
await
|
|
17114
|
+
await cleanupFailedStageSessionBinding(attachedSession, reason);
|
|
16950
17115
|
} catch (error) {
|
|
16951
|
-
if (
|
|
16952
|
-
this.
|
|
16953
|
-
await disposeStageSession(attachedSession).catch(() => {});
|
|
17116
|
+
if (error instanceof StageSessionBindingCleanupFailure)
|
|
17117
|
+
this.bindingCleanupFailure = error;
|
|
16954
17118
|
throw error;
|
|
16955
17119
|
}
|
|
17120
|
+
throw reason;
|
|
16956
17121
|
}
|
|
16957
|
-
|
|
17122
|
+
this.reportStartupPhase("ready");
|
|
16958
17123
|
return attachedSession;
|
|
16959
17124
|
}
|
|
16960
17125
|
attachSession(created) {
|
|
@@ -17040,6 +17205,7 @@ class StageSessionController {
|
|
|
17040
17205
|
this.lastPromptStartIndex = promptStartIndex;
|
|
17041
17206
|
this.unresolvedContextOverflowMessage = undefined;
|
|
17042
17207
|
try {
|
|
17208
|
+
this.reportStartupPhase("first-dispatch");
|
|
17043
17209
|
await activeSession.prompt(nextText, sdkOptions);
|
|
17044
17210
|
const pendingPauseAfterPrompt = this.pauseControl.currentResume();
|
|
17045
17211
|
if (pendingPauseAfterPrompt) {
|
|
@@ -17154,7 +17320,7 @@ class StageSessionController {
|
|
|
17154
17320
|
...usage === undefined ? {} : { usage },
|
|
17155
17321
|
error: message
|
|
17156
17322
|
});
|
|
17157
|
-
if (this.opts.signal?.aborted || terminalStageDelivery || !isRetryableModelFailure(err) || index === candidates.length - 1) {
|
|
17323
|
+
if (this.opts.signal?.aborted || this.startupWait.signal.aborted || terminalStageDelivery || !isRetryableModelFailure(err) || index === candidates.length - 1) {
|
|
17158
17324
|
this.modelWarnings.push(...this.pendingFallbackWarnings);
|
|
17159
17325
|
this.pendingFallbackWarnings.length = 0;
|
|
17160
17326
|
this.notifyModelFallbackMetaChange();
|
|
@@ -17803,9 +17969,7 @@ function createWorkflowStageFactory(input) {
|
|
|
17803
17969
|
toolEvents: [],
|
|
17804
17970
|
pendingStageDeliveryAvailable,
|
|
17805
17971
|
...shouldReplay ? {
|
|
17806
|
-
|
|
17807
|
-
endedAt: Date.now(),
|
|
17808
|
-
durationMs: 0,
|
|
17972
|
+
...stageTimingFields(replaySource),
|
|
17809
17973
|
...replaySource.result !== undefined ? { result: replaySource.result } : {},
|
|
17810
17974
|
...replaySource.sessionId !== undefined ? { sessionId: replaySource.sessionId } : {},
|
|
17811
17975
|
...replaySource.sessionFile !== undefined ? { sessionFile: replaySource.sessionFile } : {},
|
|
@@ -17868,11 +18032,19 @@ function createWorkflowStageFactory(input) {
|
|
|
17868
18032
|
signal: input.signal,
|
|
17869
18033
|
stageOptions: stageOptionsForContext,
|
|
17870
18034
|
...pendingStageDeliveryAvailable ? {
|
|
18035
|
+
routeAuthorityReady: () => workflowPendingStageRouteReady(input.activeStore, input.runId),
|
|
17871
18036
|
pendingStageDelivery: createWorkflowPendingStageDelivery(input.activeStore, input.runId, stageId, name)
|
|
17872
18037
|
} : {},
|
|
17873
18038
|
models: input.opts.models,
|
|
17874
18039
|
executionMode: input.opts.executionMode,
|
|
17875
18040
|
defaultSessionDir: input.opts.defaultSessionDir,
|
|
18041
|
+
onStartupChange(startup) {
|
|
18042
|
+
const current = input.activeStore.runs().find((run) => run.id === input.runId);
|
|
18043
|
+
if (!current?.stages.includes(stageSnapshot))
|
|
18044
|
+
return;
|
|
18045
|
+
stageSnapshot.startup = startup;
|
|
18046
|
+
input.activeStore.recordStageStart(input.runId, stageSnapshot);
|
|
18047
|
+
},
|
|
17876
18048
|
onModelFallbackMetaChange(meta) {
|
|
17877
18049
|
applyModelFallbackMeta(meta);
|
|
17878
18050
|
if (stageSnapshot.status === "running")
|
|
@@ -18035,6 +18207,7 @@ function createWorkflowStageFactory(input) {
|
|
|
18035
18207
|
stageId,
|
|
18036
18208
|
status: stageSnapshot.status,
|
|
18037
18209
|
durationMs: stageSnapshot.durationMs,
|
|
18210
|
+
endedAt: stageSnapshot.endedAt,
|
|
18038
18211
|
...stageSnapshot.error !== undefined ? { error: stageSnapshot.error } : {},
|
|
18039
18212
|
...stageSnapshot.failureKind !== undefined ? { failureKind: stageSnapshot.failureKind } : {},
|
|
18040
18213
|
...stageSnapshot.failureCode !== undefined ? { failureCode: stageSnapshot.failureCode } : {},
|
|
@@ -18640,6 +18813,7 @@ async function run(def, inputs, opts = {}) {
|
|
|
18640
18813
|
};
|
|
18641
18814
|
};
|
|
18642
18815
|
const resolvePromptNodeTopology = createDurableStageTopologyResolver(durableBackend, runId);
|
|
18816
|
+
const priorPromptStageCheckpoints = durableBackend.listCheckpoints(runId).filter((checkpoint) => checkpoint.kind === "stage");
|
|
18643
18817
|
let promptNodeUi;
|
|
18644
18818
|
const getPromptNodeUi = () => {
|
|
18645
18819
|
promptNodeUi ??= buildPromptNodeUiAdapter({
|
|
@@ -18656,6 +18830,7 @@ async function run(def, inputs, opts = {}) {
|
|
|
18656
18830
|
workflowExitSkippedReason: exit.workflowExitSkippedReason,
|
|
18657
18831
|
preserveWorkflowExitSkippedReason: exit.preserveWorkflowExitSkippedReason,
|
|
18658
18832
|
durableTopologyForReplayKey: resolvePromptNodeTopology,
|
|
18833
|
+
durableTimingForStageId: (stageId) => priorPromptStageCheckpoints.find((checkpoint) => checkpoint.topology?.stageId === stageId && checkpoint.topology.status === "completed" && (checkpoint.topology.run === undefined || checkpoint.topology.run.runId === runId)),
|
|
18659
18834
|
onPendingStage: async (pendingRunId, snapshot) => pendingRunId === runId ? void await recordDurableActiveStage(durableStageDeps, snapshot) : undefined
|
|
18660
18835
|
});
|
|
18661
18836
|
return promptNodeUi;
|
package/docs/background-tasks.md
CHANGED
|
@@ -112,6 +112,8 @@ Completion creates a shaded notification card in the owning chat without dependi
|
|
|
112
112
|
|
|
113
113
|
The parent model also receives the result context. The internal receipt stays in structured message details, rather than becoming raw JSON in chat. The same persisted completion identity handles delivery retries without relaunching the child. Workflow completions remain in their owning stage chat, not the main conversation.
|
|
114
114
|
|
|
115
|
+
Supervisor progress messages are labelled **Historical supervisor update** with their original `Sent:` timestamp. They describe the child's state when sent, not its current status, and can arrive after completion. Prefer a later correction or final result over an earlier progress hypothesis; use `/tasks` for the current task state.
|
|
116
|
+
|
|
115
117
|
Stopping a subagent through `/tasks` (`x`, then `y`) also delivers a **stopped** card and stop context to the parent model. The notice arrives after termination is confirmed, not while the task is merely **Stopping**, and does not require a final response from the child. Cancelling queued work notifies without starting it. Repeated stop requests or late child results do not duplicate the notice or overwrite the recorded outcome; a task that finished before cancellation keeps its actual result. Closing the owning session or workflow stage still suppresses late notices.
|
|
116
118
|
|
|
117
119
|
Restored completions may have only an outcome and task identity if the original live task or transcript is unavailable. Atomic does not invent missing output. Excerpts are bounded; inspect retained history for more detail.
|