@bastani/atomic 0.9.13-alpha.2 → 0.9.13-alpha.3
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 +14 -0
- package/README.md +1 -1
- package/dist/builtin/i-have-adhd/package.json +1 -1
- package/dist/builtin/intercom/CHANGELOG.md +14 -0
- package/dist/builtin/intercom/README.md +12 -12
- package/dist/builtin/intercom/incoming-message-delivery.ts +1 -1
- package/dist/builtin/intercom/index.ts +1 -1
- package/dist/builtin/intercom/intercom-tool.ts +1 -1
- package/dist/builtin/intercom/intercom-utils.ts +3 -6
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/result-renderers.ts +1 -1
- package/dist/builtin/intercom/session-target.ts +1 -15
- package/dist/builtin/intercom/skills/intercom/SKILL.md +6 -6
- package/dist/builtin/intercom/terminal-ordering-barrier.ts +2 -2
- package/dist/builtin/intercom/ui/inline-message.ts +7 -5
- package/dist/builtin/intercom/ui/session-list.ts +23 -10
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +19 -0
- package/dist/builtin/subagents/README.md +29 -73
- package/dist/builtin/subagents/package.json +2 -2
- package/dist/builtin/subagents/prompts/parallel-cleanup.md +1 -1
- package/dist/builtin/subagents/prompts/parallel-review.md +1 -1
- package/dist/builtin/subagents/prompts/review-loop.md +2 -2
- package/dist/builtin/subagents/skills/subagent/SKILL.md +23 -59
- package/dist/builtin/subagents/skills/tmux/SKILL.md +1 -1
- package/dist/builtin/subagents/src/extension/control-notices.ts +2 -3
- package/dist/builtin/subagents/src/extension/doctor.ts +2 -14
- package/dist/builtin/subagents/src/extension/fanout-child.ts +1 -17
- package/dist/builtin/subagents/src/extension/index.ts +9 -70
- package/dist/builtin/subagents/src/extension/notification-content.ts +1 -1
- package/dist/builtin/subagents/src/extension/prompt-guidance.ts +2 -2
- package/dist/builtin/subagents/src/extension/schemas.ts +2 -8
- package/dist/builtin/subagents/src/extension/tool-description.ts +2 -3
- package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +1 -1
- package/dist/builtin/subagents/src/intercom/result-intercom.ts +5 -46
- package/dist/builtin/subagents/src/runs/{background → foreground}/completion-notification.ts +2 -2
- package/dist/builtin/subagents/src/runs/foreground/inprocess-run-sync.ts +25 -88
- package/dist/builtin/subagents/src/runs/{background → foreground}/notify.ts +5 -5
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +11 -28
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +3 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +2 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-runtime.ts +0 -5
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +30 -20
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +4 -4
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +0 -13
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +104 -48
- package/dist/builtin/subagents/src/runs/inprocess/index.ts +1 -1
- package/dist/builtin/subagents/src/runs/inprocess/runner.ts +5 -5
- package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-api.ts +0 -7
- package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-core.ts +0 -10
- package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-projection.ts +3 -108
- package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-sanitize.ts +0 -4
- package/dist/builtin/subagents/src/runs/shared/subagent-control.ts +1 -1
- package/dist/builtin/subagents/src/shared/formatters.ts +3 -5
- package/dist/builtin/subagents/src/shared/model-resolution.ts +78 -0
- package/dist/builtin/subagents/src/shared/status-format.ts +4 -11
- package/dist/builtin/subagents/src/shared/types-config.ts +10 -9
- package/dist/builtin/subagents/src/shared/types-nested.ts +127 -0
- package/dist/builtin/subagents/src/shared/types-results.ts +2 -8
- package/dist/builtin/subagents/src/shared/types-runtime.ts +0 -9
- package/dist/builtin/subagents/src/shared/types.ts +1 -1
- package/dist/builtin/subagents/src/shared/utils.ts +1 -109
- package/dist/builtin/subagents/src/slash/slash-commands.ts +7 -17
- package/dist/builtin/subagents/src/tui/render-event-formatting.ts +2 -252
- package/dist/builtin/subagents/src/tui/render-layout.ts +4 -4
- package/dist/builtin/subagents/src/tui/render-result.ts +0 -9
- package/dist/builtin/subagents/src/tui/render-stable-output.ts +1 -34
- package/dist/builtin/subagents/src/tui/render.ts +0 -2
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +12 -0
- package/dist/builtin/workflows/README.md +3 -3
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/durable/backend.ts +11 -0
- package/dist/builtin/workflows/src/durable/completed-catalog-stage-groups.ts +63 -25
- package/dist/builtin/workflows/src/durable/completed-catalog.ts +166 -25
- package/dist/builtin/workflows/src/durable/dbos-backend.ts +42 -24
- package/dist/builtin/workflows/src/durable/dbos-envelope.ts +2 -1
- package/dist/builtin/workflows/src/durable/run-timing.ts +28 -7
- package/dist/builtin/workflows/src/durable/scoped-backend.ts +4 -0
- package/dist/builtin/workflows/src/durable/targeted-inspection.ts +110 -0
- package/dist/builtin/workflows/src/engine/run-durable-stage-session.ts +2 -2
- package/dist/builtin/workflows/src/engine/runtime.ts +19 -2
- package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +3 -0
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +680 -152
- package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +6 -0
- package/dist/builtin/workflows/src/extension/ui-surface.ts +2 -0
- package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +6 -1
- package/dist/builtin/workflows/src/extension/workflow-targets.ts +18 -10
- package/dist/builtin/workflows/src/extension/workflow-tool-inspection.ts +31 -16
- package/dist/builtin/workflows/src/extension/workflow-tool-send.ts +75 -3
- package/dist/builtin/workflows/src/extension/workflow-tool.ts +53 -6
- package/dist/builtin/workflows/src/runs/background/run-inspect.ts +5 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +94 -60
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-context.ts +1 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-control.ts +7 -7
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +25 -4
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-types.ts +4 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +2 -0
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +142 -4
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-types.ts +2 -0
- package/dist/builtin/workflows/src/runs/foreground/stage-session-heartbeat.ts +88 -0
- package/dist/builtin/workflows/src/tui/run-detail.ts +25 -12
- package/dist/builtin/workflows/src/tui/workflow-resume-selector.ts +1 -0
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +16 -5
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-export.d.ts.map +1 -1
- package/dist/core/agent-session-export.js +2 -1
- package/dist/core/agent-session-export.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +8 -4
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +5 -0
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-summary.d.ts +21 -0
- package/dist/core/agent-session-summary.d.ts.map +1 -0
- package/dist/core/agent-session-summary.js +156 -0
- package/dist/core/agent-session-summary.js.map +1 -0
- package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
- package/dist/core/agent-session-tool-registry.js +0 -4
- package/dist/core/agent-session-tool-registry.js.map +1 -1
- package/dist/core/agent-session-transfer.d.ts.map +1 -1
- package/dist/core/agent-session-transfer.js +0 -2
- package/dist/core/agent-session-transfer.js.map +1 -1
- package/dist/core/agent-session-tree.d.ts.map +1 -1
- package/dist/core/agent-session-tree.js +6 -0
- package/dist/core/agent-session-tree.js.map +1 -1
- package/dist/core/agent-session.d.ts +7 -3
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +8 -5
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/atomic-guide-command.js +2 -2
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +1 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/index.d.ts +1 -0
- package/dist/core/compaction/index.d.ts.map +1 -1
- package/dist/core/compaction/index.js +1 -0
- package/dist/core/compaction/index.js.map +1 -1
- package/dist/core/compaction/session-summarization.d.ts +41 -0
- package/dist/core/compaction/session-summarization.d.ts.map +1 -0
- package/dist/core/compaction/session-summarization.js +97 -0
- package/dist/core/compaction/session-summarization.js.map +1 -0
- package/dist/core/extensions/ui-types.d.ts +2 -0
- package/dist/core/extensions/ui-types.d.ts.map +1 -1
- package/dist/core/extensions/ui-types.js.map +1 -1
- package/dist/core/session-manager-core.d.ts +3 -1
- package/dist/core/session-manager-core.d.ts.map +1 -1
- package/dist/core/session-manager-core.js +9 -1
- package/dist/core/session-manager-core.js.map +1 -1
- package/dist/core/session-manager-entries.d.ts +24 -1
- package/dist/core/session-manager-entries.d.ts.map +1 -1
- package/dist/core/session-manager-entries.js +48 -0
- package/dist/core/session-manager-entries.js.map +1 -1
- package/dist/core/session-manager-list.d.ts.map +1 -1
- package/dist/core/session-manager-list.js +10 -0
- package/dist/core/session-manager-list.js.map +1 -1
- package/dist/core/session-manager-types.d.ts +10 -1
- package/dist/core/session-manager-types.d.ts.map +1 -1
- package/dist/core/session-manager-types.js.map +1 -1
- package/dist/core/settings-manager-basic-accessors.d.ts +3 -0
- package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-basic-accessors.js +5 -0
- package/dist/core/settings-manager-basic-accessors.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-types.d.ts +4 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/slash-commands.js +2 -2
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tools/bash.d.ts +0 -15
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +3 -81
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/fetch-url.d.ts +12 -0
- package/dist/core/tools/fetch-url.d.ts.map +1 -1
- package/dist/core/tools/fetch-url.js +76 -10
- package/dist/core/tools/fetch-url.js.map +1 -1
- package/dist/core/tools/index.js +4 -4
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/session-temp-dir.d.ts +2 -2
- package/dist/core/tools/session-temp-dir.d.ts.map +1 -1
- package/dist/core/tools/session-temp-dir.js +6 -7
- package/dist/core/tools/session-temp-dir.js.map +1 -1
- package/dist/core/usage-totals.js +1 -1
- package/dist/core/usage-totals.js.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/host-session-picker.d.ts.map +1 -1
- package/dist/modes/interactive/components/host-session-picker.js +1 -0
- package/dist/modes/interactive/components/host-session-picker.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-list.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector-list.js +29 -4
- package/dist/modes/interactive/components/session-selector-list.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.js +1 -1
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector-content.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector-content.js +6 -0
- package/dist/modes/interactive/components/tree-selector-content.js.map +1 -1
- package/dist/modes/interactive-engine/protocol.d.ts.map +1 -1
- package/dist/modes/interactive-engine/protocol.js +4 -1
- package/dist/modes/interactive-engine/protocol.js.map +1 -1
- package/docs/environment-variables.md +1 -1
- package/docs/intercom.md +20 -21
- package/docs/quickstart.md +5 -1
- package/docs/sdk.md +1 -1
- package/docs/session-format.md +15 -0
- package/docs/sessions.md +8 -0
- package/docs/settings.md +6 -0
- package/docs/subagents.md +19 -31
- package/docs/tools.md +3 -5
- package/docs/usage.md +1 -1
- package/docs/windows.md +1 -1
- package/docs/workflows.md +60 -32
- package/npm-shrinkwrap.json +29 -29
- package/package.json +2 -2
- package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +0 -331
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +0 -161
- package/dist/builtin/subagents/src/runs/inprocess/background-single.ts +0 -195
- package/dist/builtin/subagents/src/runs/inprocess/background.ts +0 -90
- package/dist/builtin/subagents/src/shared/types-async.ts +0 -247
- package/dist/builtin/subagents/src/tui/render-widget-graph.ts +0 -217
- package/dist/builtin/subagents/src/tui/render-widget.ts +0 -366
- package/dist/core/async/format.d.ts +0 -3
- package/dist/core/async/format.d.ts.map +0 -1
- package/dist/core/async/format.js +0 -52
- package/dist/core/async/format.js.map +0 -1
- package/dist/core/async/job-manager.d.ts +0 -40
- package/dist/core/async/job-manager.d.ts.map +0 -1
- package/dist/core/async/job-manager.js +0 -293
- package/dist/core/async/job-manager.js.map +0 -1
- package/dist/core/async/session-manager.d.ts +0 -16
- package/dist/core/async/session-manager.d.ts.map +0 -1
- package/dist/core/async/session-manager.js +0 -34
- package/dist/core/async/session-manager.js.map +0 -1
- package/dist/core/async/types.d.ts +0 -23
- package/dist/core/async/types.d.ts.map +0 -1
- package/dist/core/async/types.js +0 -2
- package/dist/core/async/types.js.map +0 -1
- package/dist/core/tools/bash-async-execution.d.ts +0 -27
- package/dist/core/tools/bash-async-execution.d.ts.map +0 -1
- package/dist/core/tools/bash-async-execution.js +0 -81
- package/dist/core/tools/bash-async-execution.js.map +0 -1
- package/dist/core/tools/bash-async-jobs.d.ts +0 -30
- package/dist/core/tools/bash-async-jobs.d.ts.map +0 -1
- package/dist/core/tools/bash-async-jobs.js +0 -87
- package/dist/core/tools/bash-async-jobs.js.map +0 -1
- package/dist/core/tools/bash-async-output.d.ts +0 -13
- package/dist/core/tools/bash-async-output.d.ts.map +0 -1
- package/dist/core/tools/bash-async-output.js +0 -122
- package/dist/core/tools/bash-async-output.js.map +0 -1
|
@@ -28,7 +28,7 @@ export function createStageControlHandle(runtime: LiveStageRuntime): StageContro
|
|
|
28
28
|
const sessionFile = messagePreparation().sessionFile;
|
|
29
29
|
if (sessionFile === undefined) return;
|
|
30
30
|
await runtime.innerCtx.__ensureSessionFromFile(sessionFile);
|
|
31
|
-
runtime.captureStageSessionMeta();
|
|
31
|
+
await runtime.captureStageSessionMeta({ awaitDurable: true });
|
|
32
32
|
};
|
|
33
33
|
const toolExecutions = new StageToolExecutionBuffer();
|
|
34
34
|
const queuedUserMessages = new StageQueuedUserMessageBuffer();
|
|
@@ -75,7 +75,7 @@ export function createStageControlHandle(runtime: LiveStageRuntime): StageContro
|
|
|
75
75
|
await ensureMessagingSession();
|
|
76
76
|
await runtime.innerCtx.__ensureSession();
|
|
77
77
|
runtime.throwIfStageMutationBlocked();
|
|
78
|
-
runtime.captureStageSessionMeta();
|
|
78
|
+
await runtime.captureStageSessionMeta({ awaitDurable: true });
|
|
79
79
|
},
|
|
80
80
|
async sendUserMessage(text, options, beforeDelivery) {
|
|
81
81
|
runtime.throwIfStageMutationBlocked();
|
|
@@ -91,7 +91,7 @@ export function createStageControlHandle(runtime: LiveStageRuntime): StageContro
|
|
|
91
91
|
}
|
|
92
92
|
return action;
|
|
93
93
|
} finally {
|
|
94
|
-
runtime.captureStageSessionMeta();
|
|
94
|
+
void runtime.captureStageSessionMeta();
|
|
95
95
|
}
|
|
96
96
|
},
|
|
97
97
|
async prompt(text: string) {
|
|
@@ -106,7 +106,7 @@ export function createStageControlHandle(runtime: LiveStageRuntime): StageContro
|
|
|
106
106
|
preparation,
|
|
107
107
|
);
|
|
108
108
|
} finally {
|
|
109
|
-
runtime.captureStageSessionMeta();
|
|
109
|
+
void runtime.captureStageSessionMeta();
|
|
110
110
|
}
|
|
111
111
|
if (action !== "handled") runtime.throwIfStageMutationBlocked();
|
|
112
112
|
},
|
|
@@ -124,7 +124,7 @@ export function createStageControlHandle(runtime: LiveStageRuntime): StageContro
|
|
|
124
124
|
await runtime.innerCtx.steer(text);
|
|
125
125
|
if (queuedIntoInFlightTurn) runtime.state.resumeContinuationPending = "queued-user-message";
|
|
126
126
|
} finally {
|
|
127
|
-
runtime.captureStageSessionMeta();
|
|
127
|
+
void runtime.captureStageSessionMeta();
|
|
128
128
|
}
|
|
129
129
|
},
|
|
130
130
|
async followUp(text: string) {
|
|
@@ -137,7 +137,7 @@ export function createStageControlHandle(runtime: LiveStageRuntime): StageContro
|
|
|
137
137
|
await runtime.innerCtx.followUp(text);
|
|
138
138
|
if (queuedIntoInFlightTurn) runtime.state.resumeContinuationPending = "queued-user-message";
|
|
139
139
|
} finally {
|
|
140
|
-
runtime.captureStageSessionMeta();
|
|
140
|
+
void runtime.captureStageSessionMeta();
|
|
141
141
|
}
|
|
142
142
|
},
|
|
143
143
|
async pause() {
|
|
@@ -206,7 +206,7 @@ export function createStageControlHandle(runtime: LiveStageRuntime): StageContro
|
|
|
206
206
|
}
|
|
207
207
|
throw err;
|
|
208
208
|
} finally {
|
|
209
|
-
runtime.captureStageSessionMeta();
|
|
209
|
+
void runtime.captureStageSessionMeta();
|
|
210
210
|
}
|
|
211
211
|
},
|
|
212
212
|
subscribe(listener: AgentSessionEventListener) {
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
type InternalStageContext,
|
|
41
41
|
type StageAdapters,
|
|
42
42
|
} from "./stage-runner.js";
|
|
43
|
+
import { createStageSessionHeartbeat } from "./stage-session-heartbeat.js";
|
|
43
44
|
|
|
44
45
|
export function createWorkflowStageFactory(input: {
|
|
45
46
|
readonly runId: string;
|
|
@@ -151,6 +152,15 @@ export function createWorkflowStageFactory(input: {
|
|
|
151
152
|
if (meta.modelAttempts !== undefined) stageSnapshot.modelAttempts = meta.modelAttempts;
|
|
152
153
|
};
|
|
153
154
|
|
|
155
|
+
let runtime!: LiveStageRuntime;
|
|
156
|
+
const stageSessionHeartbeat = createStageSessionHeartbeat(async () => {
|
|
157
|
+
try {
|
|
158
|
+
await runtime.captureStageSessionMeta({ awaitDurable: true });
|
|
159
|
+
} catch (error) {
|
|
160
|
+
await runtime.innerCtx.abort().catch(() => {});
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
154
164
|
const innerCtx = createInnerStageContext({
|
|
155
165
|
stageId,
|
|
156
166
|
stageName: name,
|
|
@@ -166,6 +176,11 @@ export function createWorkflowStageFactory(input: {
|
|
|
166
176
|
applyModelFallbackMeta(meta);
|
|
167
177
|
if (stageSnapshot.status === "running") input.activeStore.recordStageStart(input.runId, stageSnapshot);
|
|
168
178
|
},
|
|
179
|
+
async onSessionReady() {
|
|
180
|
+
if (stageSnapshot.status !== "running") return;
|
|
181
|
+
await runtime.captureStageSessionMeta({ awaitDurable: true });
|
|
182
|
+
runtime.startStageSessionHeartbeat();
|
|
183
|
+
},
|
|
169
184
|
});
|
|
170
185
|
|
|
171
186
|
const state: LiveStageMutableState = {
|
|
@@ -234,21 +249,23 @@ export function createWorkflowStageFactory(input: {
|
|
|
234
249
|
await innerCtx.__dispose();
|
|
235
250
|
};
|
|
236
251
|
|
|
237
|
-
|
|
238
|
-
const captureStageSessionMeta = (checkpointOptions?: StageSessionCheckpointOptions): unknown => {
|
|
252
|
+
const captureStageSessionMeta = async (checkpointOptions?: StageSessionCheckpointOptions): Promise<void> => {
|
|
239
253
|
const meta = innerCtx.__sessionMeta();
|
|
240
254
|
if (meta.sessionId !== undefined) stageSnapshot.sessionId = meta.sessionId;
|
|
241
255
|
if (meta.sessionFile !== undefined) stageSnapshot.sessionFile = meta.sessionFile;
|
|
242
256
|
if (meta.sessionId !== undefined || meta.sessionFile !== undefined)
|
|
243
257
|
input.activeStore.recordStageSession(input.runId, stageId, meta);
|
|
244
258
|
const pending = input.opts.onStageSession?.(input.runId, stageSnapshot, checkpointOptions);
|
|
245
|
-
if (checkpointOptions?.forceDurable === true)
|
|
259
|
+
if (checkpointOptions?.forceDurable === true || checkpointOptions?.awaitDurable === true) {
|
|
260
|
+
await pending;
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
246
263
|
void Promise.resolve(pending).catch(() => {});
|
|
247
|
-
return undefined;
|
|
248
264
|
};
|
|
249
265
|
const releaseLiveHandle = async (): Promise<void> => {
|
|
250
266
|
if (state.liveHandleReleased) return;
|
|
251
267
|
state.liveHandleReleased = true;
|
|
268
|
+
stageSessionHeartbeat.stop();
|
|
252
269
|
runtime.dropStageControlHandle();
|
|
253
270
|
runtime.unregisterStageHandle();
|
|
254
271
|
await disposeInnerContext();
|
|
@@ -282,6 +299,7 @@ export function createWorkflowStageFactory(input: {
|
|
|
282
299
|
};
|
|
283
300
|
|
|
284
301
|
const finalizeStageSnapshot = async (): Promise<boolean> => {
|
|
302
|
+
stageSessionHeartbeat.stop();
|
|
285
303
|
if (state.stageFinalized) return false;
|
|
286
304
|
if (stageSnapshot.endedAt !== undefined && isTerminalStage(stageSnapshot)) {
|
|
287
305
|
state.stageFinalized = true;
|
|
@@ -371,6 +389,9 @@ export function createWorkflowStageFactory(input: {
|
|
|
371
389
|
dropStageControlHandle: () => {},
|
|
372
390
|
unregisterWorkflowExitCleanup: () => {},
|
|
373
391
|
captureStageSessionMeta,
|
|
392
|
+
startStageSessionHeartbeat: stageSessionHeartbeat.start,
|
|
393
|
+
drainStageSessionHeartbeat: stageSessionHeartbeat.drain,
|
|
394
|
+
raceStageSessionHeartbeat: stageSessionHeartbeat.race,
|
|
374
395
|
applyModelFallbackMeta,
|
|
375
396
|
appendStageStartOnce,
|
|
376
397
|
finalizeStageSnapshot,
|
|
@@ -51,7 +51,10 @@ export interface LiveStageRuntime {
|
|
|
51
51
|
unregisterStageHandle: () => void;
|
|
52
52
|
dropStageControlHandle: () => void;
|
|
53
53
|
unregisterWorkflowExitCleanup: () => void;
|
|
54
|
-
readonly captureStageSessionMeta: (options?: StageSessionCheckpointOptions) =>
|
|
54
|
+
readonly captureStageSessionMeta: (options?: StageSessionCheckpointOptions) => Promise<void>;
|
|
55
|
+
readonly startStageSessionHeartbeat: () => void;
|
|
56
|
+
readonly drainStageSessionHeartbeat: () => Promise<void>;
|
|
57
|
+
readonly raceStageSessionHeartbeat: <T>(work: Promise<T>) => Promise<T>;
|
|
55
58
|
readonly applyModelFallbackMeta: (meta: ReturnType<InternalStageContext["__modelFallbackMeta"]>) => void;
|
|
56
59
|
readonly appendStageStartOnce: () => void;
|
|
57
60
|
readonly finalizeStageSnapshot: () => Promise<boolean>;
|
|
@@ -34,6 +34,8 @@ export interface RunContinuationOpts {
|
|
|
34
34
|
|
|
35
35
|
export interface StageSessionCheckpointOptions {
|
|
36
36
|
readonly forceDurable?: boolean;
|
|
37
|
+
/** Await and surface the persistence result without bypassing duration bucketing. */
|
|
38
|
+
readonly awaitDurable?: boolean;
|
|
37
39
|
}
|
|
38
40
|
export interface RunOpts
|
|
39
41
|
extends Omit<
|
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
StageUserMessageContent,
|
|
15
15
|
WorkflowModelAttempt,
|
|
16
16
|
WorkflowModelCatalogPort,
|
|
17
|
+
WorkflowModelUsage,
|
|
17
18
|
} from "../../shared/types.js";
|
|
18
19
|
import {
|
|
19
20
|
buildModelCandidatesFromCatalog,
|
|
@@ -67,6 +68,58 @@ import {
|
|
|
67
68
|
|
|
68
69
|
type RetryPauseResume = NonNullable<ReturnType<StageSessionPause["currentResume"]>>;
|
|
69
70
|
|
|
71
|
+
type StageMessage = StageSessionRuntime["messages"][number];
|
|
72
|
+
type StageAssistantMessage = Extract<StageMessage, { role: "assistant" }>;
|
|
73
|
+
|
|
74
|
+
type AttemptUsageTotals = {
|
|
75
|
+
input: number;
|
|
76
|
+
output: number;
|
|
77
|
+
cacheRead: number;
|
|
78
|
+
cacheWrite: number;
|
|
79
|
+
cost: number;
|
|
80
|
+
turns: number;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* A present usage object is meaningful when any token bucket, the total token
|
|
85
|
+
* count, or the provider-reported total cost is positive. Reject the entire
|
|
86
|
+
* provider record when any aggregated value is non-finite or negative so
|
|
87
|
+
* malformed telemetry cannot poison a durable stage checkpoint.
|
|
88
|
+
*/
|
|
89
|
+
function hasMeaningfulUsage(usage: StageAssistantMessage["usage"] | undefined): boolean {
|
|
90
|
+
if (usage === undefined) return false;
|
|
91
|
+
const values = [usage.input, usage.output, usage.cacheRead, usage.cacheWrite, usage.totalTokens, usage.cost.total];
|
|
92
|
+
return values.every((value) => Number.isFinite(value) && value >= 0) && values.some((value) => value > 0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Aggregate the assistant-message usage admitted since the latched attempt
|
|
97
|
+
* boundary, plus usage accrued from error assistants that a retry restoration
|
|
98
|
+
* removed from the live transcript. Returns undefined when nothing meaningful
|
|
99
|
+
* was seen, so no `usage: undefined` enters the serializable attempt record.
|
|
100
|
+
*/
|
|
101
|
+
function usageForAttempt(
|
|
102
|
+
messages: readonly StageMessage[],
|
|
103
|
+
start: number,
|
|
104
|
+
discarded: AttemptUsageTotals,
|
|
105
|
+
): WorkflowModelUsage | undefined {
|
|
106
|
+
const totals = { ...discarded };
|
|
107
|
+
let meaningful = totals.turns > 0;
|
|
108
|
+
|
|
109
|
+
for (const message of messages.slice(start)) {
|
|
110
|
+
if (message.role !== "assistant" || !hasMeaningfulUsage(message.usage)) continue;
|
|
111
|
+
totals.input += message.usage.input;
|
|
112
|
+
totals.output += message.usage.output;
|
|
113
|
+
totals.cacheRead += message.usage.cacheRead;
|
|
114
|
+
totals.cacheWrite += message.usage.cacheWrite;
|
|
115
|
+
totals.cost += message.usage.cost.total;
|
|
116
|
+
totals.turns += 1;
|
|
117
|
+
meaningful = true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return meaningful ? totals : undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
70
123
|
interface ThrownErrorRetryState {
|
|
71
124
|
readonly controller: AbortController;
|
|
72
125
|
pauseResume?: RetryPauseResume;
|
|
@@ -159,6 +212,17 @@ export class StageSessionController {
|
|
|
159
212
|
private sessionPromise: Promise<StageSessionRuntime> | undefined;
|
|
160
213
|
private reattachSessionFile: string | undefined;
|
|
161
214
|
private lastPromptStartIndex: number | undefined;
|
|
215
|
+
/** Message index latched once per high-level candidate prompt; never re-read later. */
|
|
216
|
+
private attemptUsageStartIndex: number | undefined;
|
|
217
|
+
/** Usage accrued from error assistants removed by retry restoration. */
|
|
218
|
+
private discardedAttemptUsage: AttemptUsageTotals = {
|
|
219
|
+
input: 0,
|
|
220
|
+
output: 0,
|
|
221
|
+
cacheRead: 0,
|
|
222
|
+
cacheWrite: 0,
|
|
223
|
+
cost: 0,
|
|
224
|
+
turns: 0,
|
|
225
|
+
};
|
|
162
226
|
private readonly terminatingToolCallIds = new Set<string>();
|
|
163
227
|
private latestStructuredOutputToolErrorValue: string | undefined;
|
|
164
228
|
private unsubscribeTerminateWatcher: (() => void) | undefined;
|
|
@@ -418,6 +482,7 @@ export class StageSessionController {
|
|
|
418
482
|
this.activeCandidateIndex = index;
|
|
419
483
|
this.selectedModel = candidate.id;
|
|
420
484
|
this.notifyModelFallbackMetaChange();
|
|
485
|
+
this.beginAttemptUsage(activeSession);
|
|
421
486
|
const { terminalScanStartIndex } = await this.promptWithThrownErrorRetry(
|
|
422
487
|
activeSession,
|
|
423
488
|
promptText,
|
|
@@ -563,6 +628,49 @@ export class StageSessionController {
|
|
|
563
628
|
}
|
|
564
629
|
return undefined;
|
|
565
630
|
}
|
|
631
|
+
/**
|
|
632
|
+
* Latch a fresh high-level usage window against the active session. The
|
|
633
|
+
* boundary is the message length before the candidate prompt, so retries,
|
|
634
|
+
* pause/re-prompts, and recording never re-read a later index.
|
|
635
|
+
*/
|
|
636
|
+
private beginAttemptUsage(session: StageSessionRuntime): void {
|
|
637
|
+
this.attemptUsageStartIndex = session.messages.length;
|
|
638
|
+
this.discardedAttemptUsage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Consume the current window exactly once. A session-creation failure has no
|
|
643
|
+
* window: clear the latch instead of reading a previous session's messages.
|
|
644
|
+
*/
|
|
645
|
+
private takeAttemptUsage(session: StageSessionRuntime | undefined): WorkflowModelUsage | undefined {
|
|
646
|
+
const start = this.attemptUsageStartIndex;
|
|
647
|
+
const usage =
|
|
648
|
+
session === undefined || start === undefined
|
|
649
|
+
? undefined
|
|
650
|
+
: usageForAttempt(session.messages, start, this.discardedAttemptUsage);
|
|
651
|
+
this.clearAttemptUsage();
|
|
652
|
+
return usage;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
private clearAttemptUsage(): void {
|
|
656
|
+
this.attemptUsageStartIndex = undefined;
|
|
657
|
+
this.discardedAttemptUsage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Keep the usage of an error assistant that a retry restoration is about to
|
|
662
|
+
* remove from the live transcript, so the final attempt total still counts it.
|
|
663
|
+
*/
|
|
664
|
+
private accrueDiscardedAttemptUsage(message: StageAssistantMessage): void {
|
|
665
|
+
if (!hasMeaningfulUsage(message.usage)) return;
|
|
666
|
+
this.discardedAttemptUsage.input += message.usage.input;
|
|
667
|
+
this.discardedAttemptUsage.output += message.usage.output;
|
|
668
|
+
this.discardedAttemptUsage.cacheRead += message.usage.cacheRead;
|
|
669
|
+
this.discardedAttemptUsage.cacheWrite += message.usage.cacheWrite;
|
|
670
|
+
this.discardedAttemptUsage.cost += message.usage.cost.total;
|
|
671
|
+
this.discardedAttemptUsage.turns += 1;
|
|
672
|
+
}
|
|
673
|
+
|
|
566
674
|
/**
|
|
567
675
|
* Drop this attempt's failed input from live state before a same-candidate
|
|
568
676
|
* retry, keeping unrelated concurrent messages. The durable transcript keeps
|
|
@@ -581,6 +689,12 @@ export class StageSessionController {
|
|
|
581
689
|
): StageSessionRuntime["messages"][number] | undefined {
|
|
582
690
|
const snapshotMessages = new Set(snapshot);
|
|
583
691
|
const admitted = session.messages.filter((message) => !snapshotMessages.has(message));
|
|
692
|
+
// Error assistants the filter below removes still carry real provider
|
|
693
|
+
// usage; accrue it before the splice so the retry total keeps it.
|
|
694
|
+
for (const message of admitted) {
|
|
695
|
+
if (message.role !== "assistant" || message.stopReason !== "error") continue;
|
|
696
|
+
this.accrueDiscardedAttemptUsage(message);
|
|
697
|
+
}
|
|
584
698
|
const failedAssistantIndex = admitted.findLastIndex(
|
|
585
699
|
(message) => message.role === "assistant" && message.stopReason === "error",
|
|
586
700
|
);
|
|
@@ -1054,9 +1168,12 @@ export class StageSessionController {
|
|
|
1054
1168
|
throw new Error(`atomic-workflows: stage "${this.opts.stageName}" session has been disposed`);
|
|
1055
1169
|
throw this.staleCreationReason(startGeneration);
|
|
1056
1170
|
}
|
|
1057
|
-
|
|
1171
|
+
const session = attachCreatedStageSession(created, this.disposed, this.opts.stageName, (result) =>
|
|
1058
1172
|
this.attachSession(result),
|
|
1059
1173
|
);
|
|
1174
|
+
if (session instanceof Promise) return await session;
|
|
1175
|
+
await this.opts.onSessionReady?.();
|
|
1176
|
+
return session;
|
|
1060
1177
|
}
|
|
1061
1178
|
|
|
1062
1179
|
private attachSession(created: StageSessionRuntime | StageSessionCreateResult): StageSessionRuntime {
|
|
@@ -1175,11 +1292,17 @@ export class StageSessionController {
|
|
|
1175
1292
|
const resumedSession = this.session;
|
|
1176
1293
|
const resumedLabel = this.selectedModel ?? workflowModelId(resumedSession.model) ?? candidates[0]!.id;
|
|
1177
1294
|
this.notifyModelFallbackMetaChange();
|
|
1295
|
+
this.beginAttemptUsage(resumedSession);
|
|
1178
1296
|
try {
|
|
1179
1297
|
const { terminalScanStartIndex } = await this.promptWithThrownErrorRetry(resumedSession, text, sdkOptions);
|
|
1180
1298
|
const terminalFailure = latestTerminalAssistantFailureSince(resumedSession.messages, terminalScanStartIndex);
|
|
1181
1299
|
if (terminalFailure === undefined || this.capturedStructuredOutputForAttempt()) {
|
|
1182
|
-
this.
|
|
1300
|
+
const usage = this.takeAttemptUsage(resumedSession);
|
|
1301
|
+
this.modelAttempts.push({
|
|
1302
|
+
model: resumedLabel,
|
|
1303
|
+
success: true,
|
|
1304
|
+
...(usage === undefined ? {} : { usage }),
|
|
1305
|
+
});
|
|
1183
1306
|
this.pendingFallbackWarnings.length = 0;
|
|
1184
1307
|
this.resumeCurrentSession = true;
|
|
1185
1308
|
return true;
|
|
@@ -1187,13 +1310,24 @@ export class StageSessionController {
|
|
|
1187
1310
|
throw new WorkflowPromptModelFailure(terminalFailure);
|
|
1188
1311
|
} catch (err) {
|
|
1189
1312
|
if (this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err)) {
|
|
1190
|
-
this.
|
|
1313
|
+
const usage = this.takeAttemptUsage(resumedSession);
|
|
1314
|
+
this.modelAttempts.push({
|
|
1315
|
+
model: resumedLabel,
|
|
1316
|
+
success: true,
|
|
1317
|
+
...(usage === undefined ? {} : { usage }),
|
|
1318
|
+
});
|
|
1191
1319
|
this.pendingFallbackWarnings.length = 0;
|
|
1192
1320
|
this.resumeCurrentSession = true;
|
|
1193
1321
|
return true;
|
|
1194
1322
|
}
|
|
1195
1323
|
const message = errorMessage(err);
|
|
1196
|
-
this.
|
|
1324
|
+
const usage = this.takeAttemptUsage(resumedSession);
|
|
1325
|
+
this.modelAttempts.push({
|
|
1326
|
+
model: resumedLabel,
|
|
1327
|
+
success: false,
|
|
1328
|
+
...(usage === undefined ? {} : { usage }),
|
|
1329
|
+
error: message,
|
|
1330
|
+
});
|
|
1197
1331
|
if (this.opts.signal?.aborted || !isRetryableModelFailure(err)) {
|
|
1198
1332
|
this.modelWarnings.push(...this.pendingFallbackWarnings);
|
|
1199
1333
|
this.pendingFallbackWarnings.length = 0;
|
|
@@ -1233,10 +1367,12 @@ export class StageSessionController {
|
|
|
1233
1367
|
this.recordSuccessfulAttempt(candidate);
|
|
1234
1368
|
return "handled";
|
|
1235
1369
|
}
|
|
1370
|
+
const usage = this.takeAttemptUsage(this.session);
|
|
1236
1371
|
this.modelAttempts.push({
|
|
1237
1372
|
model: candidate.id,
|
|
1238
1373
|
success: false,
|
|
1239
1374
|
...modelAttemptReasoning(candidate, this.effectiveStageOptions?.thinkingLevel),
|
|
1375
|
+
...(usage === undefined ? {} : { usage }),
|
|
1240
1376
|
error: message,
|
|
1241
1377
|
});
|
|
1242
1378
|
if (this.opts.signal?.aborted || !isRetryableModelFailure(err) || index === candidates.length - 1) {
|
|
@@ -1258,10 +1394,12 @@ export class StageSessionController {
|
|
|
1258
1394
|
}
|
|
1259
1395
|
|
|
1260
1396
|
private recordSuccessfulAttempt(candidate: WorkflowResolvedModelCandidate): void {
|
|
1397
|
+
const usage = this.takeAttemptUsage(this.session);
|
|
1261
1398
|
this.modelAttempts.push({
|
|
1262
1399
|
model: candidate.id,
|
|
1263
1400
|
success: true,
|
|
1264
1401
|
...modelAttemptReasoning(candidate, this.effectiveStageOptions?.thinkingLevel),
|
|
1402
|
+
...(usage === undefined ? {} : { usage }),
|
|
1265
1403
|
});
|
|
1266
1404
|
this.pendingFallbackWarnings.length = 0;
|
|
1267
1405
|
this.resumeCurrentSession = true;
|
|
@@ -147,6 +147,8 @@ export interface StageRunnerOpts {
|
|
|
147
147
|
defaultSessionDir?: string;
|
|
148
148
|
/** Internal: notifies the executor when an in-flight fallback changes model/fast metadata. */
|
|
149
149
|
onModelFallbackMetaChange?: (meta: StageModelFallbackMeta) => void;
|
|
150
|
+
/** Internal: persist stage-session identity once the SDK has created its path. */
|
|
151
|
+
onSessionReady?: () => void | Promise<void>;
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
export interface InternalStageContext extends StageContext {
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export const STAGE_SESSION_HEARTBEAT_INTERVAL_MS = 30_000;
|
|
2
|
+
|
|
3
|
+
export interface StageSessionHeartbeat {
|
|
4
|
+
start(): void;
|
|
5
|
+
stop(): void;
|
|
6
|
+
/**
|
|
7
|
+
* Stop scheduling and await the checkpoint that is still in flight, so a late
|
|
8
|
+
* durable failure surfaces at shutdown instead of being discarded with the
|
|
9
|
+
* cancelled timer. Rejects with a failure recorded earlier via `fail()` too.
|
|
10
|
+
*/
|
|
11
|
+
drain(): Promise<void>;
|
|
12
|
+
fail(error: unknown): void;
|
|
13
|
+
race<T>(work: Promise<T>): Promise<T>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Run one non-overlapping, unref'd durability checkpoint every 30 seconds. */
|
|
17
|
+
export function createStageSessionHeartbeat(
|
|
18
|
+
checkpoint: () => Promise<void>,
|
|
19
|
+
intervalMs: number = STAGE_SESSION_HEARTBEAT_INTERVAL_MS,
|
|
20
|
+
): StageSessionHeartbeat {
|
|
21
|
+
let active = false;
|
|
22
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
23
|
+
let generation = 0;
|
|
24
|
+
let nextDeadline = 0;
|
|
25
|
+
let failure: unknown;
|
|
26
|
+
let inFlight: Promise<void> | undefined;
|
|
27
|
+
let rejectFailure!: (reason: unknown) => void;
|
|
28
|
+
const failureSignal = new Promise<never>((_resolve, reject) => {
|
|
29
|
+
rejectFailure = reject;
|
|
30
|
+
});
|
|
31
|
+
// A race attaches the normal consumer; this guard prevents a late checkpoint
|
|
32
|
+
// rejection from becoming an unhandled process error during teardown.
|
|
33
|
+
void failureSignal.catch(() => undefined);
|
|
34
|
+
const stopScheduling = (): void => {
|
|
35
|
+
active = false;
|
|
36
|
+
generation += 1;
|
|
37
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
38
|
+
timer = undefined;
|
|
39
|
+
};
|
|
40
|
+
const fail = (error: unknown): void => {
|
|
41
|
+
if (failure !== undefined) return;
|
|
42
|
+
failure = error;
|
|
43
|
+
stopScheduling();
|
|
44
|
+
rejectFailure(error);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const schedule = (scheduledGeneration: number): void => {
|
|
48
|
+
if (!active || failure !== undefined || scheduledGeneration !== generation) return;
|
|
49
|
+
const delayMs = Math.max(0, nextDeadline - Date.now());
|
|
50
|
+
timer = setTimeout(() => {
|
|
51
|
+
timer = undefined;
|
|
52
|
+
const startedAt = Date.now();
|
|
53
|
+
do nextDeadline += intervalMs;
|
|
54
|
+
while (nextDeadline <= startedAt);
|
|
55
|
+
const running = checkpoint()
|
|
56
|
+
.catch(fail)
|
|
57
|
+
.finally(() => {
|
|
58
|
+
if (inFlight === running) inFlight = undefined;
|
|
59
|
+
schedule(scheduledGeneration);
|
|
60
|
+
});
|
|
61
|
+
inFlight = running;
|
|
62
|
+
}, delayMs);
|
|
63
|
+
(timer as { unref?: () => void }).unref?.();
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const drain = async (): Promise<void> => {
|
|
67
|
+
stopScheduling();
|
|
68
|
+
await inFlight;
|
|
69
|
+
if (failure !== undefined) throw failure;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
start(): void {
|
|
74
|
+
if (active || failure !== undefined) return;
|
|
75
|
+
active = true;
|
|
76
|
+
generation += 1;
|
|
77
|
+
nextDeadline = Date.now() + intervalMs;
|
|
78
|
+
schedule(generation);
|
|
79
|
+
},
|
|
80
|
+
stop: stopScheduling,
|
|
81
|
+
drain,
|
|
82
|
+
fail,
|
|
83
|
+
race<T>(work: Promise<T>): Promise<T> {
|
|
84
|
+
if (failure !== undefined) return Promise.reject(failure);
|
|
85
|
+
return Promise.race([work, failureSignal]);
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -182,6 +182,7 @@ function summaryRows(detail: RunDetail, now: number): Array<[string, string | un
|
|
|
182
182
|
if (detail.error) {
|
|
183
183
|
rows.push(["error", detail.error.split("\n")[0] ?? ""]);
|
|
184
184
|
}
|
|
185
|
+
if (detail.resumeGuidance) rows.push(["guidance", detail.resumeGuidance]);
|
|
185
186
|
return rows;
|
|
186
187
|
}
|
|
187
188
|
|
|
@@ -319,6 +320,8 @@ function stateBadges(detail: RunDetail, theme: GraphTheme): FlatBandBadge[] {
|
|
|
319
320
|
switch (detail.status) {
|
|
320
321
|
case "running":
|
|
321
322
|
return [{ text: "● running", fg: theme.warning }];
|
|
323
|
+
case "crashed":
|
|
324
|
+
return [{ text: detail.resumable === true ? "✗ crashed · resumable" : "✗ crashed", fg: theme.error }];
|
|
322
325
|
case "paused":
|
|
323
326
|
return [{ text: "❚❚ paused", fg: theme.warning }];
|
|
324
327
|
case "completed":
|
|
@@ -342,6 +345,8 @@ function stateLabel(detail: RunDetail): string {
|
|
|
342
345
|
switch (detail.status) {
|
|
343
346
|
case "running":
|
|
344
347
|
return "● running";
|
|
348
|
+
case "crashed":
|
|
349
|
+
return detail.resumable === true ? "✗ crashed · resumable" : "✗ crashed";
|
|
345
350
|
case "paused":
|
|
346
351
|
return "❚❚ paused";
|
|
347
352
|
case "completed":
|
|
@@ -387,18 +392,26 @@ function renderIdentifierRows(id: string, width: number, theme?: GraphTheme): st
|
|
|
387
392
|
}
|
|
388
393
|
|
|
389
394
|
function renderDetailHintRows(detail: RunDetail, width: number, theme?: GraphTheme): string[] {
|
|
390
|
-
const
|
|
391
|
-
detail.
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
detail.endedAt
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
395
|
+
const resumable =
|
|
396
|
+
(detail.status === "paused" && detail.resumable !== false) ||
|
|
397
|
+
(detail.resumable === true &&
|
|
398
|
+
(detail.status === "crashed" || detail.status === "failed" || detail.status === "blocked"));
|
|
399
|
+
const inspectOnly =
|
|
400
|
+
detail.ownerActiveElsewhere === true ||
|
|
401
|
+
(detail.status === "crashed" && !resumable) ||
|
|
402
|
+
(detail.endedAt !== undefined && !resumable);
|
|
403
|
+
const prefix = inspectOnly
|
|
404
|
+
? " ▸ workflow status id="
|
|
405
|
+
: resumable
|
|
406
|
+
? " ▸ workflow resume id="
|
|
407
|
+
: " ▸ workflow interrupt id=";
|
|
408
|
+
const suffix = inspectOnly
|
|
409
|
+
? detail.ownerActiveElsewhere === true
|
|
410
|
+
? " inspect; owner active elsewhere "
|
|
411
|
+
: " inspect retained state "
|
|
412
|
+
: resumable
|
|
413
|
+
? " continue workflow "
|
|
414
|
+
: " cancel ";
|
|
402
415
|
const continuation = " ";
|
|
403
416
|
const rows = wrapIdentifierLines(detail.runId, width, prefix, continuation);
|
|
404
417
|
const last = rows[rows.length - 1]!;
|
|
@@ -191,6 +191,7 @@ function toPickerRow(session: SessionInfo): PiHostSessionPickerRow {
|
|
|
191
191
|
modifiedAt: session.modified.getTime(),
|
|
192
192
|
messageCount: session.messageCount,
|
|
193
193
|
firstMessage: session.firstMessage,
|
|
194
|
+
...(session.summary !== undefined ? { summary: session.summary } : {}),
|
|
194
195
|
allMessagesText: session.allMessagesText,
|
|
195
196
|
...(session.name !== undefined ? { name: session.name } : {}),
|
|
196
197
|
...(session.messageColor !== undefined ? { messageColor: session.messageColor } : {}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-session-events.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAe,MAAM,8BAA8B,CAAC;AAE3F,OAAO,KAAK,EAAE,2BAA2B,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAS9F,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAI9B,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-session-events.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAe,MAAM,8BAA8B,CAAC;AAE3F,OAAO,KAAK,EAAE,2BAA2B,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAS9F,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAI9B,MAAM,0BAA0B,CAAC;AAgBlC,wBAAgB,KAAK,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAIxE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAMzD;AAED,4EAA4E;AAE5E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CA2B7F;AAED,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CA6B1F;AAED,wBAAgB,4BAA4B,CAC3C,IAAI,EAAE,YAAY,EAClB,QAAQ,EAAE,YAAY,EAAE,GACtB,gBAAgB,GAAG,SAAS,CAQ9B;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAgM7F;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CA0BvF;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAQvF;AAED,wCAAwC;AAExC,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAMtD;AAED,0CAA0C;AAE1C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAMhF;AAED,8EAA8E;AAE9E,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,YAAY,GAAG,gBAAgB,GAAG,SAAS,CAS1F;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,GAAG,IAAI,CAchH;AAED,kDAAkD;AAElD,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAsE9F;AAED;;;;GAIG;AAEH,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,yBAAyB,GAAG,MAAM,IAAI,CAU7F;AAED,oDAAoD;AACpD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAK7D;AAED;;;GAGG;AAEH,wBAAgB,OAAO,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAmBhD;AAMD,uBAAuB;AAEvB,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;CAiBrC,CAAC"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { cleanupSessionResources } from "@earendil-works/pi-ai/compat";
|
|
2
2
|
import { isProtectedStreamingCustomMessage, markProtectedStreamingCustomMessageConsumed, markProtectedStreamingCustomMessagePersistenceFailed, persistProtectedStreamingCustomMessage, prepareProtectedStreamingCustomMessagesForDisposal, retryConsumedProtectedStreamingCustomMessages, } from "./agent-session-persistent-custom-messages.js";
|
|
3
3
|
import { customMessageExcludesContext, isSingleGenericAbortTextContent, replacementAbortContent, } from "./agent-session-types.js";
|
|
4
|
-
import { disposeSessionAsyncJobManager } from "./async/session-manager.js";
|
|
5
4
|
import { formatCodexProviderError } from "./codex-errors.js";
|
|
6
5
|
import { STALE_EXTENSION_CONTEXT_MESSAGE } from "./extensions/stale-context.js";
|
|
7
6
|
import { normalizeMessageContent } from "./messages.js";
|
|
@@ -235,6 +234,12 @@ export async function _processAgentEvent(event) {
|
|
|
235
234
|
if (typeof this._restoreFallbackModel === "function")
|
|
236
235
|
await this._restoreFallbackModel();
|
|
237
236
|
}
|
|
237
|
+
// Launched last so the fallback lifecycle wins: a switch above returns before this line, and
|
|
238
|
+
// the restore has already put `this.model` back to the user's selection, which the launch
|
|
239
|
+
// reads synchronously before it parks on waitForIdle(). Guarded like the fallback methods
|
|
240
|
+
// above because the fallback suites drive this function on a synthetic session.
|
|
241
|
+
if (event.type === "agent_end" && typeof this._maybeGenerateSessionSummary === "function")
|
|
242
|
+
void this._maybeGenerateSessionSummary();
|
|
238
243
|
}
|
|
239
244
|
}
|
|
240
245
|
export function _applyInterruptAbortMessage(event) {
|
|
@@ -421,17 +426,23 @@ export function _disconnectFromAgent() {
|
|
|
421
426
|
* Call this when completely done with the session.
|
|
422
427
|
*/
|
|
423
428
|
export function dispose() {
|
|
429
|
+
// Terminal and idempotent: callers legitimately dispose more than once (an explicit dispose
|
|
430
|
+
// followed by a harness teardown), and the steps below are not all safe to repeat.
|
|
431
|
+
if (this._disposed)
|
|
432
|
+
return;
|
|
433
|
+
// Summary work queued before its AbortController exists cannot be reached by
|
|
434
|
+
// abortSessionSummary(), so disposal is recorded as state that every checkpoint consults.
|
|
435
|
+
this._disposed = true;
|
|
436
|
+
// A background summary must never keep the process alive past shutdown.
|
|
437
|
+
this.abortSessionSummary();
|
|
424
438
|
// Fail closed while protected input remains queued, or flush a consumed
|
|
425
439
|
// reconciliation before invalidation can discard its recovery state.
|
|
426
440
|
prepareProtectedStreamingCustomMessagesForDisposal(this);
|
|
427
441
|
this._extensionRunner.invalidate(STALE_EXTENSION_CONTEXT_MESSAGE);
|
|
428
|
-
disposeSessionAsyncJobManager(this._asyncJobManager, this._asyncJobManagerSessionId);
|
|
429
442
|
this._disconnectFromAgent();
|
|
430
443
|
this._eventListeners = [];
|
|
431
444
|
cleanupSessionResources(this.sessionId);
|
|
432
|
-
//
|
|
433
|
-
// releasing here stops protecting a tree nothing is using without cutting a
|
|
434
|
-
// writer that outlived the session object.
|
|
445
|
+
// Releasing the session lease stops protecting a tree that is no longer in use.
|
|
435
446
|
this._tempStorageLease?.release();
|
|
436
447
|
this._tempStorageLease = undefined;
|
|
437
448
|
}
|