@bastani/atomic 0.9.13-alpha.2 → 0.9.13-alpha.4
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 +30 -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 +23 -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 +750 -168
- package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +6 -0
- package/dist/builtin/workflows/src/extension/ui-surface.ts +4 -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/keybindings-adapter.ts +1 -0
- package/dist/builtin/workflows/src/tui/overlay-adapter.ts +1 -0
- package/dist/builtin/workflows/src/tui/run-detail.ts +25 -12
- package/dist/builtin/workflows/src/tui/stage-chat-view-archive-history.ts +27 -8
- package/dist/builtin/workflows/src/tui/stage-chat-view-input.ts +17 -1
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +20 -3
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +10 -1
- 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 +22 -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 +10 -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 +68 -0
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-queue-pause.d.ts.map +1 -1
- package/dist/core/agent-session-queue-pause.js +6 -0
- package/dist/core/agent-session-queue-pause.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 +21 -3
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +22 -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 +4 -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/keybindings.d.ts +0 -5
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +0 -2
- package/dist/core/keybindings.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/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.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/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.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/transcript-follow-indicator.d.ts +14 -0
- package/dist/modes/interactive/components/transcript-follow-indicator.d.ts.map +1 -0
- package/dist/modes/interactive/components/transcript-follow-indicator.js +36 -0
- package/dist/modes/interactive/components/transcript-follow-indicator.js.map +1 -0
- 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/interactive-extension-custom-ui.js +3 -0
- package/dist/modes/interactive/interactive-extension-custom-ui.js.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.js +0 -3
- package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +7 -1
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +3 -3
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +1 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/interactive-slash-commands.js +2 -5
- package/dist/modes/interactive/interactive-slash-commands.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +6 -0
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive/interactive-transcript-follow.d.ts +2 -0
- package/dist/modes/interactive/interactive-transcript-follow.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-transcript-follow.js +6 -0
- package/dist/modes/interactive/interactive-transcript-follow.js.map +1 -0
- package/dist/modes/interactive/interactive-tui.d.ts +13 -1
- package/dist/modes/interactive/interactive-tui.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-tui.js +59 -1
- package/dist/modes/interactive/interactive-tui.js.map +1 -1
- package/dist/modes/interactive-engine/engine-custom-ui.d.ts +2 -0
- package/dist/modes/interactive-engine/engine-custom-ui.d.ts.map +1 -1
- package/dist/modes/interactive-engine/engine-custom-ui.js +1 -0
- package/dist/modes/interactive-engine/engine-custom-ui.js.map +1 -1
- package/dist/modes/interactive-engine/protocol.d.ts +1 -0
- package/dist/modes/interactive-engine/protocol.d.ts.map +1 -1
- package/dist/modes/interactive-engine/protocol.js +5 -1
- package/dist/modes/interactive-engine/protocol.js.map +1 -1
- package/dist/modes/interactive-engine/remote-component.d.ts.map +1 -1
- package/dist/modes/interactive-engine/remote-component.js +21 -9
- package/dist/modes/interactive-engine/remote-component.js.map +1 -1
- package/docs/environment-variables.md +1 -1
- package/docs/intercom.md +20 -21
- package/docs/keybindings.md +2 -5
- 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 +3 -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
|
+
}
|
|
@@ -412,6 +412,7 @@ export function buildGraphOverlayAdapter(
|
|
|
412
412
|
// Ctrl+C (Skip / Close / cancel), so the host must forward it here
|
|
413
413
|
// instead of closing this overlay on the first press.
|
|
414
414
|
handlesCtrlC: true,
|
|
415
|
+
handlesInternalUiAction: true,
|
|
415
416
|
overlayOptions: FULLSCREEN_OVERLAY_OPTIONS,
|
|
416
417
|
onHandle: (handle) => {
|
|
417
418
|
currentHandle = handle;
|
|
@@ -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]!;
|
|
@@ -38,6 +38,8 @@ export function renderReadOnlyArchiveBody(
|
|
|
38
38
|
width: number,
|
|
39
39
|
budget: number,
|
|
40
40
|
stage: StageSnapshot | undefined,
|
|
41
|
+
indicatorLines: readonly string[] = [],
|
|
42
|
+
renderIndicator: () => readonly string[] = () => indicatorLines,
|
|
41
43
|
): string[] {
|
|
42
44
|
if (stage?.promptFootprint) {
|
|
43
45
|
return renderReadOnlyPromptArchiveBody(ctx, width, budget, stage);
|
|
@@ -69,8 +71,14 @@ export function renderReadOnlyArchiveBody(
|
|
|
69
71
|
).render(width),
|
|
70
72
|
);
|
|
71
73
|
const transcriptBudget = Math.max(0, budget - callout.length);
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
+
const reservedIndicatorRows =
|
|
75
|
+
indicatorLines.length > 0 && transcriptBudget > indicatorLines.length ? indicatorLines.length : 0;
|
|
76
|
+
const transcriptRows = transcriptBudget - reservedIndicatorRows;
|
|
77
|
+
const lines = transcriptRows > 0 ? ctx.chatHost.renderBody(width, transcriptRows) : [];
|
|
78
|
+
const visibleIndicatorLines =
|
|
79
|
+
reservedIndicatorRows > 0 ? [...renderIndicator().slice(0, reservedIndicatorRows)] : [];
|
|
80
|
+
while (visibleIndicatorLines.length < reservedIndicatorRows) visibleIndicatorLines.push(blankLine(width));
|
|
81
|
+
lines.push(...visibleIndicatorLines, ...callout);
|
|
74
82
|
while (lines.length < budget) lines.push(blankLine(width));
|
|
75
83
|
if (lines.length > budget) lines.length = budget;
|
|
76
84
|
return lines;
|
|
@@ -163,7 +171,13 @@ function formatReadOnlyPromptAnswer(value: unknown, kind: PendingPrompt["kind"])
|
|
|
163
171
|
}
|
|
164
172
|
}
|
|
165
173
|
|
|
166
|
-
export function renderPausedBody(
|
|
174
|
+
export function renderPausedBody(
|
|
175
|
+
ctx: StageChatViewContext,
|
|
176
|
+
width: number,
|
|
177
|
+
budget: number,
|
|
178
|
+
indicatorLines: readonly string[] = [],
|
|
179
|
+
renderIndicator: () => readonly string[] = () => indicatorLines,
|
|
180
|
+
): string[] {
|
|
167
181
|
const t = ctx.theme;
|
|
168
182
|
const callout: string[] = [];
|
|
169
183
|
callout.push(blankLine(width));
|
|
@@ -175,11 +189,16 @@ export function renderPausedBody(ctx: StageChatViewContext, width: number, budge
|
|
|
175
189
|
0,
|
|
176
190
|
).render(width),
|
|
177
191
|
);
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
192
|
+
const calloutRows = Math.min(callout.length, Math.max(0, budget));
|
|
193
|
+
const transcriptBudget = Math.max(0, budget - calloutRows);
|
|
194
|
+
const reservedIndicatorRows =
|
|
195
|
+
indicatorLines.length > 0 && transcriptBudget > indicatorLines.length ? indicatorLines.length : 0;
|
|
196
|
+
const transcriptRows = transcriptBudget - reservedIndicatorRows;
|
|
197
|
+
const lines = transcriptRows > 0 ? ctx.chatHost.renderBody(width, transcriptRows) : [];
|
|
198
|
+
const visibleIndicatorLines =
|
|
199
|
+
reservedIndicatorRows > 0 ? [...renderIndicator().slice(0, reservedIndicatorRows)] : [];
|
|
200
|
+
while (visibleIndicatorLines.length < reservedIndicatorRows) visibleIndicatorLines.push(blankLine(width));
|
|
201
|
+
lines.push(...visibleIndicatorLines, ...callout.slice(0, calloutRows));
|
|
183
202
|
while (lines.length < budget) lines.push(blankLine(width));
|
|
184
203
|
if (lines.length > budget) lines.length = budget;
|
|
185
204
|
return lines;
|