@bastani/atomic 0.9.5-alpha.6 → 0.9.5-alpha.8
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 +35 -2
- package/dist/builtin/cursor/package.json +2 -2
- package/dist/builtin/intercom/CHANGELOG.md +17 -0
- package/dist/builtin/intercom/README.md +27 -21
- package/dist/builtin/intercom/broker/broker.ts +3 -6
- package/dist/builtin/intercom/broker/paths.ts +42 -5
- package/dist/builtin/intercom/broker/spawn.ts +78 -10
- package/dist/builtin/intercom/config.ts +6 -3
- package/dist/builtin/intercom/contact-supervisor-tool.ts +6 -4
- package/dist/builtin/intercom/index-heavy.ts +1 -1
- package/dist/builtin/intercom/index.ts +162 -130
- package/dist/builtin/intercom/intercom-tool.ts +9 -3
- package/dist/builtin/intercom/package.json +10 -1
- package/dist/builtin/intercom/result-renderers.ts +1 -1
- package/dist/builtin/intercom/ui/compose.ts +2 -2
- package/dist/builtin/intercom/ui/inline-message.ts +2 -2
- package/dist/builtin/intercom/ui/session-list.ts +2 -2
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +6 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/tui/render-event-formatting.ts +15 -48
- package/dist/builtin/subagents/src/tui/render-layout.ts +9 -0
- package/dist/builtin/subagents/src/tui/render-widget-graph.ts +27 -25
- package/dist/builtin/subagents/src/tui/render-widget.ts +46 -69
- package/dist/builtin/subagents/src/tui/render.ts +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +11 -0
- package/dist/builtin/workflows/README.md +3 -1
- package/dist/builtin/workflows/builtin/goal-artifacts.ts +3 -1
- package/dist/builtin/workflows/builtin/goal-prompts.ts +23 -2
- package/dist/builtin/workflows/builtin/goal-reducer.ts +21 -0
- package/dist/builtin/workflows/builtin/goal-review.ts +60 -6
- package/dist/builtin/workflows/builtin/goal-runner.ts +32 -21
- package/dist/builtin/workflows/builtin/goal-types.ts +7 -1
- package/dist/builtin/workflows/builtin/ralph-core.ts +52 -4
- package/dist/builtin/workflows/builtin/ralph-review-gate.ts +10 -3
- package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +23 -2
- package/dist/builtin/workflows/builtin/ralph-runner.ts +38 -16
- package/dist/builtin/workflows/builtin/review-convergence.ts +111 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/create-spec/SKILL.md +2 -0
- package/dist/builtin/workflows/src/durable/child-primitive.ts +12 -8
- package/dist/builtin/workflows/src/durable/dbos-backend.ts +47 -2
- package/dist/builtin/workflows/src/durable/dbos-envelope.ts +28 -0
- package/dist/builtin/workflows/src/durable/stage-primitive.ts +165 -27
- package/dist/builtin/workflows/src/durable/types.ts +14 -1
- package/dist/builtin/workflows/src/engine/run.ts +12 -13
- package/dist/builtin/workflows/src/extension/workflow-prompts.ts +2 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-continuation.ts +13 -3
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +1 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +18 -19
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-unresolved-overflow.ts +38 -0
- package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +18 -1
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +23 -5
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +4 -0
- package/dist/bun/internal-intercom-broker.d.ts +6 -0
- package/dist/bun/internal-intercom-broker.d.ts.map +1 -0
- package/dist/bun/internal-intercom-broker.js +39 -0
- package/dist/bun/internal-intercom-broker.js.map +1 -0
- package/dist/bun/split-loader.d.ts +2 -0
- package/dist/bun/split-loader.d.ts.map +1 -0
- package/dist/bun/split-loader.js +50 -0
- package/dist/bun/split-loader.js.map +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -4
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-auto-compaction.d.ts +7 -7
- package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-auto-compaction.js +62 -16
- package/dist/core/agent-session-auto-compaction.js.map +1 -1
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +3 -1
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
- package/dist/core/agent-session-extension-bindings.js +1 -1
- package/dist/core/agent-session-extension-bindings.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-models.d.ts +2 -2
- package/dist/core/agent-session-models.d.ts.map +1 -1
- package/dist/core/agent-session-models.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +1 -0
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-retry.d.ts +2 -0
- package/dist/core/agent-session-retry.d.ts.map +1 -1
- package/dist/core/agent-session-retry.js +152 -4
- package/dist/core/agent-session-retry.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session-types.d.ts +15 -1
- package/dist/core/agent-session-types.d.ts.map +1 -1
- package/dist/core/agent-session-types.js.map +1 -1
- package/dist/core/agent-session.d.ts +4 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +4 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/builtin-packages.d.ts.map +1 -1
- package/dist/core/builtin-packages.js +8 -4
- package/dist/core/builtin-packages.js.map +1 -1
- package/dist/core/compaction/context-compaction-runner.d.ts.map +1 -1
- package/dist/core/compaction/context-compaction-runner.js +36 -20
- package/dist/core/compaction/context-compaction-runner.js.map +1 -1
- package/dist/core/copilot-anthropic-sse-repair.d.ts +23 -0
- package/dist/core/copilot-anthropic-sse-repair.d.ts.map +1 -0
- package/dist/core/copilot-anthropic-sse-repair.js +340 -0
- package/dist/core/copilot-anthropic-sse-repair.js.map +1 -0
- package/dist/core/copilot-gemini-reasoning.d.ts +16 -8
- package/dist/core/copilot-gemini-reasoning.d.ts.map +1 -1
- package/dist/core/copilot-gemini-reasoning.js +27 -22
- package/dist/core/copilot-gemini-reasoning.js.map +1 -1
- package/dist/core/copilot-hosts.d.ts +12 -0
- package/dist/core/copilot-hosts.d.ts.map +1 -0
- package/dist/core/copilot-hosts.js +33 -0
- package/dist/core/copilot-hosts.js.map +1 -0
- package/dist/core/copilot-model-catalog.d.ts.map +1 -1
- package/dist/core/copilot-model-catalog.js +3 -2
- package/dist/core/copilot-model-catalog.js.map +1 -1
- package/dist/core/extensions/agent-events.d.ts +1 -1
- package/dist/core/extensions/agent-events.d.ts.map +1 -1
- package/dist/core/extensions/agent-events.js.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.js +8 -3
- package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +5 -5
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/openai-responses-payload-sanitizer.d.ts +6 -0
- package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -0
- package/dist/core/openai-responses-payload-sanitizer.js +59 -0
- package/dist/core/openai-responses-payload-sanitizer.js.map +1 -0
- package/dist/core/package-manager-auto-resources.d.ts +2 -2
- package/dist/core/package-manager-auto-resources.d.ts.map +1 -1
- package/dist/core/package-manager-auto-resources.js +32 -52
- package/dist/core/package-manager-auto-resources.js.map +1 -1
- package/dist/core/package-manager-resolver.d.ts.map +1 -1
- package/dist/core/package-manager-resolver.js +23 -14
- package/dist/core/package-manager-resolver.js.map +1 -1
- package/dist/core/package-manager-resource-collector.d.ts +3 -3
- package/dist/core/package-manager-resource-collector.d.ts.map +1 -1
- package/dist/core/package-manager-resource-collector.js +76 -73
- package/dist/core/package-manager-resource-collector.js.map +1 -1
- package/dist/core/package-manager-resource-files.d.ts +9 -8
- package/dist/core/package-manager-resource-files.d.ts.map +1 -1
- package/dist/core/package-manager-resource-files.js +92 -81
- package/dist/core/package-manager-resource-files.js.map +1 -1
- package/dist/core/prompt-templates-async.d.ts +3 -0
- package/dist/core/prompt-templates-async.d.ts.map +1 -0
- package/dist/core/prompt-templates-async.js +118 -0
- package/dist/core/prompt-templates-async.js.map +1 -0
- package/dist/core/provider-context-usage.d.ts +11 -0
- package/dist/core/provider-context-usage.d.ts.map +1 -0
- package/dist/core/provider-context-usage.js +58 -0
- package/dist/core/provider-context-usage.js.map +1 -0
- package/dist/core/resource-loader-assets.d.ts +3 -3
- package/dist/core/resource-loader-assets.d.ts.map +1 -1
- package/dist/core/resource-loader-assets.js +66 -83
- package/dist/core/resource-loader-assets.js.map +1 -1
- package/dist/core/resource-loader-core.d.ts +1 -1
- package/dist/core/resource-loader-core.d.ts.map +1 -1
- package/dist/core/resource-loader-core.js +5 -5
- package/dist/core/resource-loader-core.js.map +1 -1
- package/dist/core/resource-loader-reload.d.ts.map +1 -1
- package/dist/core/resource-loader-reload.js +43 -7
- package/dist/core/resource-loader-reload.js.map +1 -1
- package/dist/core/resource-loader-types.d.ts +7 -1
- package/dist/core/resource-loader-types.d.ts.map +1 -1
- package/dist/core/resource-loader-types.js.map +1 -1
- package/dist/core/sdk-types.d.ts +2 -0
- package/dist/core/sdk-types.d.ts.map +1 -1
- package/dist/core/sdk-types.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +7 -4
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager-basic-accessors.d.ts +1 -0
- package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-basic-accessors.js +6 -0
- package/dist/core/settings-manager-basic-accessors.js.map +1 -1
- package/dist/core/settings-types.d.ts +1 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/skills-async.d.ts +3 -0
- package/dist/core/skills-async.d.ts.map +1 -0
- package/dist/core/skills-async.js +269 -0
- package/dist/core/skills-async.js.map +1 -0
- package/dist/index-extensions.d.ts +1 -1
- package/dist/index-extensions.d.ts.map +1 -1
- package/dist/index-extensions.js.map +1 -1
- package/dist/main-deferred-startup.d.ts +14 -0
- package/dist/main-deferred-startup.d.ts.map +1 -1
- package/dist/main-deferred-startup.js +23 -0
- package/dist/main-deferred-startup.js.map +1 -1
- package/dist/main-early-input.d.ts +41 -0
- package/dist/main-early-input.d.ts.map +1 -0
- package/dist/main-early-input.js +147 -0
- package/dist/main-early-input.js.map +1 -0
- package/dist/main.d.ts +0 -6
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +23 -17
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.js +15 -0
- package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
- package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-agent-events.js +34 -1
- package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
- package/dist/modes/interactive/interactive-deferred-startup.d.ts +6 -0
- package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-deferred-startup.js +27 -13
- package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
- package/dist/modes/interactive/interactive-editor-actions.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-editor-actions.js +13 -13
- package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-extension-runtime.js +16 -0
- package/dist/modes/interactive/interactive-extension-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.js +324 -178
- package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +14 -0
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +41 -0
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +22 -6
- 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-types.d.ts +3 -0
- package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-types.js.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.js +3 -3
- package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-onboarding.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-onboarding.js +1 -0
- package/dist/modes/interactive/interactive-onboarding.js.map +1 -1
- package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-process-lifecycle.js +43 -6
- package/dist/modes/interactive/interactive-process-lifecycle.js.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.js +78 -9
- package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
- package/dist/modes/interactive/interactive-resource-disclosure.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-resource-disclosure.js +3 -2
- package/dist/modes/interactive/interactive-resource-disclosure.js.map +1 -1
- package/dist/modes/interactive/interactive-resource-rendering.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-resource-rendering.js +10 -8
- package/dist/modes/interactive/interactive-resource-rendering.js.map +1 -1
- package/dist/modes/interactive/interactive-session-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-session-runtime.js +1 -1
- package/dist/modes/interactive/interactive-session-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +65 -53
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive/theme/theme-loading.d.ts +1 -0
- package/dist/modes/interactive/theme/theme-loading.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-loading.js +3 -0
- package/dist/modes/interactive/theme/theme-loading.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +1 -1
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/utils/module-require.d.ts +11 -0
- package/dist/utils/module-require.d.ts.map +1 -1
- package/dist/utils/module-require.js +14 -1
- package/dist/utils/module-require.js.map +1 -1
- package/dist/utils/split-launcher.d.ts +38 -0
- package/dist/utils/split-launcher.d.ts.map +1 -0
- package/dist/utils/split-launcher.js +63 -0
- package/dist/utils/split-launcher.js.map +1 -0
- package/docs/compaction.md +7 -0
- package/docs/json.md +3 -1
- package/docs/keybindings.md +2 -2
- package/docs/providers.md +2 -1
- package/docs/rpc.md +3 -1
- package/docs/settings.md +28 -2
- package/docs/terminal-setup.md +4 -0
- package/docs/usage.md +3 -3
- package/docs/workflows.md +13 -3
- package/npm-shrinkwrap.json +513 -23
- package/package.json +5 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Durable `ctx.stage` / `ctx.task` replay and checkpoint helpers. */
|
|
2
2
|
|
|
3
|
-
import type { StageContext, StageOptions, WorkflowTaskOptions, WorkflowTaskResult } from "../shared/types.js";
|
|
3
|
+
import type { StageContext, StageOptions, WorkflowChildResult, WorkflowOutputValues, WorkflowTaskOptions, WorkflowTaskResult } from "../shared/types.js";
|
|
4
4
|
import type { StageSnapshot } from "../shared/store-types.js";
|
|
5
5
|
import type { WorkflowSerializableValue } from "../shared/types.js";
|
|
6
6
|
import type { DurableWorkflowBackend } from "./backend.js";
|
|
@@ -8,6 +8,7 @@ import type { ParallelFailFastScope } from "../runs/foreground/executor-types.js
|
|
|
8
8
|
import { durableHash } from "./backend.js";
|
|
9
9
|
import { recordCheckpointDurably } from "./tool-primitive.js";
|
|
10
10
|
import type { DurableStageCheckpoint } from "./types.js";
|
|
11
|
+
export type DurableCompletedStageCheckpoint = DurableStageCheckpoint & { readonly output: WorkflowSerializableValue };
|
|
11
12
|
|
|
12
13
|
export interface DurableStageDeps {
|
|
13
14
|
readonly workflowId: string;
|
|
@@ -20,16 +21,28 @@ export interface DurableStageDeps {
|
|
|
20
21
|
export async function recordStageCheckpoint(deps: DurableStageDeps, stage: StageSnapshot): Promise<boolean> {
|
|
21
22
|
if (stage.status !== "completed") return false;
|
|
22
23
|
const replayKey = deps.replayKeyForCompletedStage?.(stage) ?? stage.replayKey ?? deps.nextReplayKey(stage.name);
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
const metadata = checkpointMetadata(stage);
|
|
25
|
+
const hasExistingOutput = deps.backend.getStageOutput(deps.workflowId, replayKey) !== undefined;
|
|
26
|
+
const checkpoint: DurableStageCheckpoint = hasExistingOutput
|
|
27
|
+
? {
|
|
28
|
+
kind: "stage",
|
|
29
|
+
workflowId: deps.workflowId,
|
|
30
|
+
checkpointId: stageMetadataCheckpointId(replayKey, stage),
|
|
31
|
+
name: stage.name,
|
|
32
|
+
replayKey,
|
|
33
|
+
completedAt: stage.endedAt ?? Date.now(),
|
|
34
|
+
...metadata,
|
|
35
|
+
}
|
|
36
|
+
: {
|
|
37
|
+
kind: "stage",
|
|
38
|
+
workflowId: deps.workflowId,
|
|
39
|
+
checkpointId: stableCheckpointId("stage", replayKey),
|
|
40
|
+
name: stage.name,
|
|
41
|
+
replayKey,
|
|
42
|
+
output: stageOutput(stage),
|
|
43
|
+
completedAt: stage.endedAt ?? Date.now(),
|
|
44
|
+
...metadata,
|
|
45
|
+
};
|
|
33
46
|
await recordCheckpointDurably(deps.backend, checkpoint);
|
|
34
47
|
return true;
|
|
35
48
|
}
|
|
@@ -79,14 +92,14 @@ export function createDurableStagePrimitive(input: {
|
|
|
79
92
|
readonly backend: DurableWorkflowBackend;
|
|
80
93
|
readonly nextReplayKey: (stageName: string) => string;
|
|
81
94
|
readonly stage: (name: string, options: StageOptions | undefined, replayKey: string) => StageContext;
|
|
82
|
-
readonly recordCachedStage?: (name: string, replayKey: string,
|
|
95
|
+
readonly recordCachedStage?: (name: string, replayKey: string, checkpoint: DurableCompletedStageCheckpoint) => void;
|
|
83
96
|
}): (name: string, options?: StageOptions) => StageContext {
|
|
84
97
|
return (name: string, options?: StageOptions): StageContext => {
|
|
85
98
|
const replayKey = input.nextReplayKey(name);
|
|
86
|
-
const cached = input.backend
|
|
99
|
+
const cached = stageCheckpointWithOutput(input.backend, input.workflowId, replayKey);
|
|
87
100
|
if (cached !== undefined) {
|
|
88
101
|
input.recordCachedStage?.(name, replayKey, cached);
|
|
89
|
-
return createCachedStageContext(name, cached);
|
|
102
|
+
return createCachedStageContext(name, cached.output, cached.result);
|
|
90
103
|
}
|
|
91
104
|
const session = input.backend.getStageSession(input.workflowId, replayKey);
|
|
92
105
|
const isMidSessionResume = session?.sessionFile !== undefined;
|
|
@@ -112,14 +125,14 @@ export function createDurableTaskPrimitive(input: {
|
|
|
112
125
|
readonly backend: DurableWorkflowBackend;
|
|
113
126
|
readonly nextReplayKey: (stageName: string) => string;
|
|
114
127
|
readonly task: (name: string, options: WorkflowTaskOptions, stageFailFastScope?: ParallelFailFastScope) => Promise<WorkflowTaskResult>;
|
|
115
|
-
readonly recordCachedTask?: (name: string, replayKey: string,
|
|
128
|
+
readonly recordCachedTask?: (name: string, replayKey: string, checkpoint: DurableCompletedStageCheckpoint, stageFailFastScope?: ParallelFailFastScope) => void;
|
|
116
129
|
}): (name: string, options: WorkflowTaskOptions) => Promise<WorkflowTaskResult> {
|
|
117
130
|
return async (name: string, options: WorkflowTaskOptions, stageFailFastScope?: ParallelFailFastScope): Promise<WorkflowTaskResult> => {
|
|
118
131
|
const replayKey = input.nextReplayKey(`task:${name}`);
|
|
119
|
-
const cached = input.backend
|
|
120
|
-
if (cached !== undefined && isWorkflowTaskResult(cached)) {
|
|
121
|
-
input.recordCachedTask?.(name, replayKey, cached);
|
|
122
|
-
return cached;
|
|
132
|
+
const cached = stageCheckpointWithOutput(input.backend, input.workflowId, replayKey, isWorkflowTaskResult);
|
|
133
|
+
if (cached !== undefined && isWorkflowTaskResult(cached.output)) {
|
|
134
|
+
input.recordCachedTask?.(name, replayKey, cached, stageFailFastScope);
|
|
135
|
+
return cached.output;
|
|
123
136
|
}
|
|
124
137
|
const session = input.backend.getStageSession(input.workflowId, replayKey);
|
|
125
138
|
const taskOptions: WorkflowTaskOptions = {
|
|
@@ -136,6 +149,7 @@ export function createDurableTaskPrimitive(input: {
|
|
|
136
149
|
replayKey,
|
|
137
150
|
output: result,
|
|
138
151
|
completedAt: Date.now(),
|
|
152
|
+
...taskCheckpointMetadata(result),
|
|
139
153
|
});
|
|
140
154
|
return result;
|
|
141
155
|
};
|
|
@@ -174,8 +188,8 @@ function wrapSchemaStageForDurability(input: {
|
|
|
174
188
|
return wrapped;
|
|
175
189
|
}
|
|
176
190
|
|
|
177
|
-
function createCachedStageContext(name: string, output: WorkflowSerializableValue): StageContext {
|
|
178
|
-
const text = typeof output === "string" ? output : JSON.stringify(output);
|
|
191
|
+
function createCachedStageContext(name: string, output: WorkflowSerializableValue, result?: string): StageContext {
|
|
192
|
+
const text = result ?? (typeof output === "string" ? output : JSON.stringify(output));
|
|
179
193
|
const unsupported = async (): Promise<never> => { throw new Error(`Stage "${name}" was replayed from a durable checkpoint; live session operations are unavailable.`); };
|
|
180
194
|
const cached = {
|
|
181
195
|
name,
|
|
@@ -213,6 +227,82 @@ function stageOutput(stage: StageSnapshot): WorkflowSerializableValue {
|
|
|
213
227
|
return { status: stage.status, stageId: stage.id };
|
|
214
228
|
}
|
|
215
229
|
|
|
230
|
+
function checkpointMetadata(stage: StageSnapshot): Partial<DurableStageCheckpoint> {
|
|
231
|
+
return {
|
|
232
|
+
...(stage.startedAt !== undefined ? { startedAt: stage.startedAt } : {}),
|
|
233
|
+
...(stage.endedAt !== undefined ? { endedAt: stage.endedAt } : {}),
|
|
234
|
+
...(stage.durationMs !== undefined ? { durationMs: stage.durationMs } : {}),
|
|
235
|
+
...(stage.result !== undefined ? { result: stage.result } : {}),
|
|
236
|
+
...(stage.sessionId !== undefined ? { sessionId: stage.sessionId } : {}),
|
|
237
|
+
...(stage.sessionFile !== undefined ? { sessionFile: stage.sessionFile } : {}),
|
|
238
|
+
...(stage.model !== undefined ? { model: stage.model } : {}),
|
|
239
|
+
...(stage.fastMode !== undefined ? { fastMode: stage.fastMode } : {}),
|
|
240
|
+
...(stage.attemptedModels !== undefined ? { attemptedModels: [...stage.attemptedModels] } : {}),
|
|
241
|
+
...(stage.modelAttempts !== undefined ? { modelAttempts: [...stage.modelAttempts] } : {}),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function taskCheckpointMetadata(result: WorkflowTaskResult): Partial<DurableStageCheckpoint> {
|
|
246
|
+
return {
|
|
247
|
+
result: result.text,
|
|
248
|
+
...(result.sessionId !== undefined ? { sessionId: result.sessionId } : {}),
|
|
249
|
+
...(result.sessionFile !== undefined ? { sessionFile: result.sessionFile } : {}),
|
|
250
|
+
...(result.model !== undefined ? { model: result.model } : {}),
|
|
251
|
+
...(result.fastMode !== undefined ? { fastMode: result.fastMode } : {}),
|
|
252
|
+
...(result.attemptedModels !== undefined ? { attemptedModels: [...result.attemptedModels] } : {}),
|
|
253
|
+
...(result.modelAttempts !== undefined ? { modelAttempts: [...result.modelAttempts] } : {}),
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function stageCheckpointWithOutput(
|
|
258
|
+
backend: DurableWorkflowBackend,
|
|
259
|
+
workflowId: string,
|
|
260
|
+
replayKey: string,
|
|
261
|
+
matchesOutput?: (value: WorkflowSerializableValue) => boolean,
|
|
262
|
+
): DurableCompletedStageCheckpoint | undefined {
|
|
263
|
+
const checkpoints = backend.listCheckpoints(workflowId)
|
|
264
|
+
.filter((checkpoint): checkpoint is DurableStageCheckpoint =>
|
|
265
|
+
checkpoint.kind === "stage" && checkpoint.replayKey === replayKey,
|
|
266
|
+
);
|
|
267
|
+
const outputCheckpoints = checkpoints.filter((checkpoint): checkpoint is DurableCompletedStageCheckpoint =>
|
|
268
|
+
checkpoint.output !== undefined,
|
|
269
|
+
);
|
|
270
|
+
const replayValueCheckpoint = matchesOutput === undefined
|
|
271
|
+
? outputCheckpoints[0]
|
|
272
|
+
: outputCheckpoints.find((checkpoint) => matchesOutput(checkpoint.output));
|
|
273
|
+
if (replayValueCheckpoint === undefined) return undefined;
|
|
274
|
+
return mergeCheckpointHydrationMetadata(replayValueCheckpoint, checkpoints);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function mergeCheckpointHydrationMetadata(
|
|
278
|
+
replayValueCheckpoint: DurableCompletedStageCheckpoint,
|
|
279
|
+
checkpoints: readonly DurableStageCheckpoint[],
|
|
280
|
+
): DurableCompletedStageCheckpoint {
|
|
281
|
+
if (checkpoints.length === 0) return replayValueCheckpoint;
|
|
282
|
+
return {
|
|
283
|
+
...replayValueCheckpoint,
|
|
284
|
+
...(replayValueCheckpoint.startedAt === undefined ? metadataValue(checkpoints, "startedAt") : {}),
|
|
285
|
+
...(replayValueCheckpoint.endedAt === undefined ? metadataValue(checkpoints, "endedAt") : {}),
|
|
286
|
+
...(replayValueCheckpoint.durationMs === undefined ? metadataValue(checkpoints, "durationMs") : {}),
|
|
287
|
+
...(replayValueCheckpoint.result === undefined ? metadataValue(checkpoints, "result") : {}),
|
|
288
|
+
...(replayValueCheckpoint.sessionId === undefined ? metadataValue(checkpoints, "sessionId") : {}),
|
|
289
|
+
...(replayValueCheckpoint.sessionFile === undefined ? metadataValue(checkpoints, "sessionFile") : {}),
|
|
290
|
+
...(replayValueCheckpoint.model === undefined ? metadataValue(checkpoints, "model") : {}),
|
|
291
|
+
...(replayValueCheckpoint.fastMode === undefined ? metadataValue(checkpoints, "fastMode") : {}),
|
|
292
|
+
...(replayValueCheckpoint.attemptedModels === undefined ? metadataValue(checkpoints, "attemptedModels") : {}),
|
|
293
|
+
...(replayValueCheckpoint.modelAttempts === undefined ? metadataValue(checkpoints, "modelAttempts") : {}),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function metadataValue<K extends keyof DurableStageCheckpoint>(
|
|
298
|
+
checkpoints: readonly DurableStageCheckpoint[],
|
|
299
|
+
key: K,
|
|
300
|
+
): Pick<DurableStageCheckpoint, K> | Record<string, never> {
|
|
301
|
+
const value = checkpoints.find((checkpoint) => checkpoint[key] !== undefined)?.[key];
|
|
302
|
+
return value === undefined ? {} : { [key]: value } as Pick<DurableStageCheckpoint, K>;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
|
|
216
306
|
export function createStageReplayKeyGenerator(_workflowId: string): (stageName: string, stageId?: string) => string {
|
|
217
307
|
const counts = new Map<string, number>();
|
|
218
308
|
return (stageName: string, _stageId?: string): string => {
|
|
@@ -236,6 +326,15 @@ function stageSessionCheckpointId(replayKey: string, stage: StageSnapshot): stri
|
|
|
236
326
|
export function cachedStageId(runId: string, replayKey: string): string {
|
|
237
327
|
return `durable-${durableHash({ runId, replayKey })}`;
|
|
238
328
|
}
|
|
329
|
+
function stageMetadataCheckpointId(replayKey: string, stage: StageSnapshot): string {
|
|
330
|
+
return `${stableCheckpointId("stage-meta", replayKey)}:${durableHash({
|
|
331
|
+
stageId: stage.id,
|
|
332
|
+
endedAt: stage.endedAt ?? 0,
|
|
333
|
+
durationMs: stage.durationMs ?? 0,
|
|
334
|
+
result: stage.result ?? "",
|
|
335
|
+
})}`;
|
|
336
|
+
}
|
|
337
|
+
|
|
239
338
|
|
|
240
339
|
export function recordCachedStageIntoStore(
|
|
241
340
|
store: import("../shared/store.js").Store,
|
|
@@ -245,13 +344,24 @@ export function recordCachedStageIntoStore(
|
|
|
245
344
|
output: WorkflowSerializableValue,
|
|
246
345
|
completedStageReplayKeys: Map<string, string>,
|
|
247
346
|
parentIds?: readonly string[],
|
|
347
|
+
checkpoint?: DurableCompletedStageCheckpoint,
|
|
248
348
|
): void {
|
|
249
349
|
const now = Date.now();
|
|
250
350
|
const stageId = cachedStageId(runId, replayKey);
|
|
251
|
-
const result = typeof output === "string" ? output : JSON.stringify(output);
|
|
351
|
+
const result = checkpoint?.result ?? (typeof output === "string" ? output : JSON.stringify(output));
|
|
352
|
+
const endedAt = checkpoint?.endedAt ?? checkpoint?.completedAt ?? now;
|
|
353
|
+
const workflowChild = isWorkflowChildResult(output) ? workflowChildSnapshotFromResult(output) : undefined;
|
|
252
354
|
const snapshot: StageSnapshot = {
|
|
253
|
-
id: stageId, name, status: "completed", parentIds: parentIds !== undefined ? Object.freeze([...parentIds]) : [],
|
|
355
|
+
id: stageId, name, status: "completed", parentIds: parentIds !== undefined ? Object.freeze([...parentIds]) : [],
|
|
356
|
+
startedAt: checkpoint?.startedAt ?? endedAt, endedAt, durationMs: checkpoint?.durationMs ?? 0, result,
|
|
254
357
|
replayKey, replayed: true, skippedReason: "durable checkpoint replay", toolEvents: [], attachable: false,
|
|
358
|
+
...(workflowChild !== undefined ? { workflowChild } : {}),
|
|
359
|
+
...(checkpoint?.sessionId !== undefined ? { sessionId: checkpoint.sessionId } : {}),
|
|
360
|
+
...(checkpoint?.sessionFile !== undefined ? { sessionFile: checkpoint.sessionFile } : {}),
|
|
361
|
+
...(checkpoint?.model !== undefined ? { model: checkpoint.model } : {}),
|
|
362
|
+
...(checkpoint?.fastMode !== undefined ? { fastMode: checkpoint.fastMode } : {}),
|
|
363
|
+
...(checkpoint?.attemptedModels !== undefined ? { attemptedModels: checkpoint.attemptedModels } : {}),
|
|
364
|
+
...(checkpoint?.modelAttempts !== undefined ? { modelAttempts: checkpoint.modelAttempts } : {}),
|
|
255
365
|
};
|
|
256
366
|
store.recordStageStart(runId, snapshot);
|
|
257
367
|
store.recordStageEnd(runId, snapshot);
|
|
@@ -269,16 +379,44 @@ export function recordCachedStageWithTracker(
|
|
|
269
379
|
runId: string,
|
|
270
380
|
name: string,
|
|
271
381
|
replayKey: string,
|
|
272
|
-
|
|
382
|
+
checkpoint: DurableCompletedStageCheckpoint,
|
|
273
383
|
completedStageReplayKeys: Map<string, string>,
|
|
384
|
+
stageFailFastScope?: ParallelFailFastScope,
|
|
274
385
|
): void {
|
|
275
386
|
const stageId = cachedStageId(runId, replayKey);
|
|
276
|
-
|
|
277
|
-
|
|
387
|
+
let parentIds = tracker.onSpawn(stageId, name);
|
|
388
|
+
const scopeParentIds = stageFailFastScope?.parentIds ?? [];
|
|
389
|
+
if (stageFailFastScope !== undefined) {
|
|
390
|
+
tracker.replaceParents(stageId, scopeParentIds);
|
|
391
|
+
parentIds = [...scopeParentIds];
|
|
392
|
+
}
|
|
393
|
+
recordCachedStageIntoStore(store, runId, name, replayKey, checkpoint.output, completedStageReplayKeys, parentIds, checkpoint);
|
|
278
394
|
tracker.onSettle(stageId);
|
|
279
395
|
}
|
|
280
|
-
|
|
281
396
|
function isWorkflowTaskResult(value: WorkflowSerializableValue): value is WorkflowTaskResult {
|
|
282
397
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
283
398
|
return typeof (value as Record<string, WorkflowSerializableValue>)["text"] === "string";
|
|
284
399
|
}
|
|
400
|
+
|
|
401
|
+
function isWorkflowChildResult(value: WorkflowSerializableValue): value is WorkflowChildResult<WorkflowOutputValues> {
|
|
402
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
403
|
+
const objectValue = value as Record<string, WorkflowSerializableValue | undefined>;
|
|
404
|
+
return typeof objectValue["workflow"] === "string"
|
|
405
|
+
&& typeof objectValue["runId"] === "string"
|
|
406
|
+
&& typeof objectValue["status"] === "string"
|
|
407
|
+
&& typeof objectValue["outputs"] === "object"
|
|
408
|
+
&& objectValue["outputs"] !== null
|
|
409
|
+
&& !Array.isArray(objectValue["outputs"]);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function workflowChildSnapshotFromResult(result: WorkflowChildResult<WorkflowOutputValues>): StageSnapshot["workflowChild"] {
|
|
413
|
+
return {
|
|
414
|
+
alias: result.workflow,
|
|
415
|
+
workflow: result.workflow,
|
|
416
|
+
runId: result.runId,
|
|
417
|
+
status: result.status,
|
|
418
|
+
...(result.exited !== undefined ? { exited: result.exited } : {}),
|
|
419
|
+
outputs: result.outputs,
|
|
420
|
+
...(typeof result.exitReason === "string" ? { exitReason: result.exitReason } : {}),
|
|
421
|
+
};
|
|
422
|
+
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* cross-ref: issue #1498
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type { WorkflowSerializableValue } from "../shared/types.js";
|
|
12
|
+
import type { WorkflowModelAttempt, WorkflowSerializableValue } from "../shared/types.js";
|
|
13
13
|
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
15
|
// Top-level workflow identity
|
|
@@ -113,6 +113,19 @@ export interface DurableStageCheckpoint {
|
|
|
113
113
|
readonly sessionId?: string;
|
|
114
114
|
readonly sessionFile?: string;
|
|
115
115
|
readonly completedAt: number;
|
|
116
|
+
/** Original stage start timestamp, when available. */
|
|
117
|
+
readonly startedAt?: number;
|
|
118
|
+
/** Original stage end timestamp, when available. */
|
|
119
|
+
readonly endedAt?: number;
|
|
120
|
+
/** Original stage duration, when available. */
|
|
121
|
+
readonly durationMs?: number;
|
|
122
|
+
/** Display/result text recorded for the completed stage. */
|
|
123
|
+
readonly result?: string;
|
|
124
|
+
/** Completed stage/task model metadata used to hydrate replayed snapshots. */
|
|
125
|
+
readonly model?: string;
|
|
126
|
+
readonly fastMode?: boolean;
|
|
127
|
+
readonly attemptedModels?: readonly string[];
|
|
128
|
+
readonly modelAttempts?: readonly WorkflowModelAttempt[];
|
|
116
129
|
}
|
|
117
130
|
|
|
118
131
|
export type UiPromptKind = "input" | "confirm" | "select" | "editor" | "custom";
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import type { RunSnapshot } from "../shared/store-types.js";
|
|
2
|
-
import type {
|
|
3
|
-
WorkflowDefinition,
|
|
4
|
-
WorkflowInputValues,
|
|
5
|
-
WorkflowOutputValues,
|
|
6
|
-
WorkflowRunContext,
|
|
7
|
-
WorkflowSerializableValue,
|
|
8
|
-
} from "../shared/types.js";
|
|
1
|
+
import type { RunSnapshot, StageSnapshot } from "../shared/store-types.js";
|
|
2
|
+
import type { WorkflowDefinition, WorkflowInputValues, WorkflowOutputValues, WorkflowRunContext } from "../shared/types.js";
|
|
9
3
|
import type { WorkflowFailure } from "../shared/workflow-failures.js";
|
|
10
4
|
import { classifyWorkflowFailure } from "../shared/workflow-failures.js";
|
|
11
5
|
import { store as defaultStore } from "../shared/store.js";
|
|
@@ -45,12 +39,12 @@ import { classifyReturnedRunStatus } from "./run-returned-status.js";
|
|
|
45
39
|
import { createToolPrimitive, createCheckpointIdGenerator } from "../durable/tool-primitive.js";
|
|
46
40
|
import { persistDurableCacheEntry } from "../durable/resume-catalog.js";
|
|
47
41
|
import { createDurableStagePrimitive, createDurableTaskPrimitive, recordStageCheckpoint, createStageReplayKeyGenerator, recordCachedStageWithTracker } from "../durable/stage-primitive.js";
|
|
42
|
+
import type { DurableCompletedStageCheckpoint } from "../durable/stage-primitive.js";
|
|
48
43
|
import { createDurableChildWorkflowPrimitive } from "../durable/child-primitive.js";
|
|
49
44
|
import { ScopedDurableBackend, type DurableScope } from "../durable/scoped-backend.js";
|
|
50
45
|
import { finalizeDurableTerminalStatus } from "./run-durable-finalize.js";
|
|
51
46
|
import { createDurableStageSessionRecorder } from "./run-durable-stage-session.js";
|
|
52
47
|
import type { DurableWorkflowBackend } from "../durable/backend.js";
|
|
53
|
-
import type { StageSnapshot } from "../shared/store-types.js";
|
|
54
48
|
|
|
55
49
|
function nextEventLoopTurn(): Promise<void> {
|
|
56
50
|
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
@@ -283,6 +277,8 @@ export async function run<
|
|
|
283
277
|
});
|
|
284
278
|
const workflowBoundaryReplayCounts = new Map<string, number>();
|
|
285
279
|
const nextWorkflowBoundaryReplayKey = (name: string): string => {
|
|
280
|
+
const durableScopePrefix = pendingChildDurableScope?.scopePrefix;
|
|
281
|
+
if (durableScopePrefix !== undefined && durableScopePrefix.startsWith(`workflow:${name}:`)) return durableScopePrefix;
|
|
286
282
|
const next = (workflowBoundaryReplayCounts.get(name) ?? 0) + 1;
|
|
287
283
|
workflowBoundaryReplayCounts.set(name, next);
|
|
288
284
|
return `workflow:${name}:${next}`;
|
|
@@ -346,12 +342,13 @@ export async function run<
|
|
|
346
342
|
|
|
347
343
|
// Durable ctx.ui wrapper — caches completed user responses so a resumed workflow does not re-ask answered prompts.
|
|
348
344
|
const durableUiDeps = { workflowId: runId, backend: durableBackend, nextCheckpointId: checkpointIdGenerator };
|
|
349
|
-
const recordCachedStage = (name: string, replayKey: string,
|
|
350
|
-
recordCachedStageWithTracker(activeStore, tracker, runId, name, replayKey,
|
|
345
|
+
const recordCachedStage = (name: string, replayKey: string, checkpoint: DurableCompletedStageCheckpoint): void =>
|
|
346
|
+
recordCachedStageWithTracker(activeStore, tracker, runId, name, replayKey, checkpoint, completedStageReplayKeys);
|
|
351
347
|
const durableTask = createDurableTaskPrimitive({
|
|
352
348
|
workflowId: runId, backend: durableBackend,
|
|
353
349
|
nextReplayKey: (stageName) => stageReplayKeyGenerator(stageName), task: taskRunners.task,
|
|
354
|
-
recordCachedTask: (name, replayKey,
|
|
350
|
+
recordCachedTask: (name, replayKey, checkpoint, scope) =>
|
|
351
|
+
recordCachedStageWithTracker(activeStore, tracker, runId, name, replayKey, checkpoint, completedStageReplayKeys, scope),
|
|
355
352
|
});
|
|
356
353
|
const durableWorkflow = createDurableChildWorkflowPrimitive({
|
|
357
354
|
workflowId: runId, rootWorkflowId: opts.parentRun?.rootRunId ?? runId, backend: durableBackend,
|
|
@@ -426,7 +423,7 @@ export async function run<
|
|
|
426
423
|
await durableBackend.flush?.();
|
|
427
424
|
const returned = classifyReturnedRunStatus(result);
|
|
428
425
|
const recorded = activeStore.recordRunEnd(runId, returned.status, result, returned.error, returned.metadata);
|
|
429
|
-
appendRunEndWhenRecorded(opts.persistence, recorded, { runId, status: returned.status, result, ...(returned.error !== undefined ? { error: returned.error } : {}), ...(returned.metadata ?? {}), ts: Date.now() });
|
|
426
|
+
appendRunEndWhenRecorded(opts.persistence, recorded, { runId, status: returned.status, result, ...(returned.error !== undefined ? { error: returned.error } : {}), ...(returned.metadata ?? {}), ...(runSnapshot.endedAt !== undefined ? { endedAt: runSnapshot.endedAt } : {}), ...(runSnapshot.durationMs !== undefined ? { durationMs: runSnapshot.durationMs } : {}), ts: Date.now() });
|
|
430
427
|
durableBackend.setWorkflowStatus(runId, returned.status, undefined, returned.metadata?.resumable);
|
|
431
428
|
await durableBackend.flush?.();
|
|
432
429
|
if (opts.persistence && durableBackend.persistent) {
|
|
@@ -480,6 +477,8 @@ export async function run<
|
|
|
480
477
|
...(metadata.failedStageId !== undefined ? { failedStageId: metadata.failedStageId } : {}),
|
|
481
478
|
resumable: metadata.resumable,
|
|
482
479
|
...(metadata.retryAfterMs !== undefined ? { retryAfterMs: metadata.retryAfterMs } : {}),
|
|
480
|
+
...(runSnapshot.endedAt !== undefined ? { endedAt: runSnapshot.endedAt } : {}),
|
|
481
|
+
...(runSnapshot.durationMs !== undefined ? { durationMs: runSnapshot.durationMs } : {}),
|
|
483
482
|
ts: Date.now(),
|
|
484
483
|
});
|
|
485
484
|
return reconcileTerminalRunResult(runId, runSnapshot, activeStore, { status: "failed", error: metadata.errorMessage }, opts.onRunEnd);
|
|
@@ -25,6 +25,8 @@ export const DEFAULT_PROMPT_GUIDANCE: string[] = [
|
|
|
25
25
|
- For transcripts, avoid reading whole session transcripts at once. Use \`stages\` or \`stage\` to get \`sessionFile\`/\`transcriptPath\`, quote the exact path without rewriting separators (preserve Windows backslashes), search it with \`rg\`/\`grep\`, and read small relevant ranges; use \`transcript\` with explicit \`tail\` or \`limit\` only for quick recent-context checks.
|
|
26
26
|
- If a user asks to create or edit a workflow, use the create-spec skill when available and ask detailed clarifying questions until you understand its purpose, inputs, stages, handoffs, validation, success criteria, and selected starter pattern. Then read the workflow docs/examples and implement the workflow from the created spec directly as a TypeScript definition. After you implement the workflow, reload it to access it and run it with test inputs to validate it works as intended before presenting it to the user.
|
|
27
27
|
- Tip: when designing workflows, implement it in a way that you pass information from stage to stage by writing it to a file or artifact (either deterministic or model-driven), pass the path with \`reads\`, and explicitly prompt the downstream agent with wording like \`Read the file at <path>...\`; do not inject large \`previous\` payloads or session history into the next prompt unless explicitly requested to.
|
|
28
|
+
- When designing reviewer-gated workflows, distinguish implementation/review acceptance from post-approval final actions such as PR/MR/review creation, release tagging, deployment, or publication. Prompt reviewers/reducers to approve and stop the implementation loop when implementation and validation criteria are proven and only an explicitly authorized final action remains; carry that remainder as a next action or final-action field instead of another implementation turn.
|
|
29
|
+
- When authoring workflow, stage, reviewer, or reducer prompts, remember that a model stage sees its local prompt, artifacts, tools, and reads — not the workflow graph's name or surrounding implementation details unless you explicitly provide them. Describe the concrete action, available evidence, and success criteria directly (for example "review the current code delta" or "create/update the review request") instead of asking the model to reason from labels like "the create-PR workflow stage", "this Goal run", or "the Ralph reviewer". Use workflow or stage names only when the name itself is user-visible context or materially affects behavior.
|
|
28
30
|
- Prefer the \`goal\` workflow for small-to-medium non-trivial one-off fixes, debugging, docs/code changes, scoped multi-file edits, or test/fix loops when you can name the work surface, outcome, and validation; prefer the \`ralph\` workflow for larger/riskier work, migrations, new features, broad refactors, multi-package changes, or tasks estimated over ~2K LoC.
|
|
29
31
|
- Adjust \`goal.max_turns\` for Goal runs and \`ralph.max_loops\` for Ralph runs based on task complexity (estimated LoC, risk, validation effort, and number of unique files/areas touched).
|
|
30
32
|
- Define an objective that includes tight scope, concrete and verifiable done criteria, and validation steps; then monitor progress as above instead of doing parallel implementation yourself.`,
|
|
@@ -68,14 +68,23 @@ export function createContinuationReplayIndex(continuation: RunContinuationOpts
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
const stagesByReplayIdentity = new Map<string, StageSnapshot[]>();
|
|
71
|
+
const stagesByDisplayName = new Map<string, StageSnapshot[]>();
|
|
71
72
|
const promptDuplicateCounts = new Map<string, number>();
|
|
72
73
|
for (const stage of continuation.source.stages) {
|
|
73
74
|
const identity = stage.replayKey ?? stage.name;
|
|
74
75
|
const stages = stagesByReplayIdentity.get(identity);
|
|
75
76
|
if (stages === undefined) stagesByReplayIdentity.set(identity, [stage]);
|
|
76
77
|
else stages.push(stage);
|
|
77
|
-
const
|
|
78
|
-
|
|
78
|
+
const namedStages = stagesByDisplayName.get(stage.name);
|
|
79
|
+
if (namedStages === undefined) stagesByDisplayName.set(stage.name, [stage]);
|
|
80
|
+
else namedStages.push(stage);
|
|
81
|
+
const duplicateKeys = [
|
|
82
|
+
`${identity}\u0001${sortedIdentity(stage.parentIds)}`,
|
|
83
|
+
...(identity === stage.name ? [] : [`${stage.name}\u0001${sortedIdentity(stage.parentIds)}`]),
|
|
84
|
+
];
|
|
85
|
+
for (const duplicateKey of duplicateKeys) {
|
|
86
|
+
promptDuplicateCounts.set(duplicateKey, (promptDuplicateCounts.get(duplicateKey) ?? 0) + 1);
|
|
87
|
+
}
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
const consumedSourceStageIds = new Set<string>();
|
|
@@ -126,7 +135,8 @@ export function createContinuationReplayIndex(continuation: RunContinuationOpts
|
|
|
126
135
|
let candidates = stagesByReplayIdentity.get(replayKey)?.filter((stage) => !consumedSourceStageIds.has(stage.id)) ?? [];
|
|
127
136
|
if (candidates.length === 0) {
|
|
128
137
|
identity = displayName;
|
|
129
|
-
|
|
138
|
+
const namedCandidates = stagesByDisplayName.get(displayName)?.filter((stage) => !consumedSourceStageIds.has(stage.id)) ?? [];
|
|
139
|
+
candidates = kind === "prompt" ? namedCandidates.filter((stage) => stage.replayKey === undefined) : namedCandidates;
|
|
130
140
|
}
|
|
131
141
|
if (candidates.length === 0) return { kind: "execute", parentIds, answerReplay: "unavailable" };
|
|
132
142
|
|
|
@@ -54,7 +54,7 @@ export function createWorkflowStageFactory(input: {
|
|
|
54
54
|
input.tracker.replaceParents(stageId, scopedParentIds);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const replayKey = `stage:${name}`;
|
|
57
|
+
const replayKey = options?.durableReplayKey ?? `stage:${name}`;
|
|
58
58
|
const replayDecision = input.replayIndex.decide({
|
|
59
59
|
displayName: name,
|
|
60
60
|
replayKey,
|
|
@@ -6,6 +6,7 @@ import { missingAdapter, stripWorkflowOnlyOptions, unavailableSync } from "./sta
|
|
|
6
6
|
import { asAgentSession, disposeStageSession, normalizeSessionCreateResult } from "./stage-runner-session.js";
|
|
7
7
|
import { structuredOutputToolErrorFromEvent } from "./stage-runner-structured-output.js";
|
|
8
8
|
import { sendStageUserMessage } from "./stage-runner-send-user-message.js";
|
|
9
|
+
import { nextResumedContextOverflowFallbackIndex, terminatingToolCallId, unresolvedContextOverflowFailure, unresolvedContextOverflowMessage } from "./stage-runner-unresolved-overflow.js";
|
|
9
10
|
import type { AgentSessionConsumer, StageModelFallbackMeta, StageRunnerOpts, StageSessionCreateOptions, StageSessionCreateResult, StageSessionEvent, StageSessionRuntime, WorkflowFastModeSettingsManager } from "./stage-runner-types.js";
|
|
10
11
|
|
|
11
12
|
type PauseRequest = {
|
|
@@ -19,6 +20,7 @@ export class StageSessionController {
|
|
|
19
20
|
private readonly terminatingToolCallIds = new Set<string>();
|
|
20
21
|
private latestStructuredOutputToolErrorValue: string | undefined;
|
|
21
22
|
private unsubscribeTerminateWatcher: (() => void) | undefined;
|
|
23
|
+
private unresolvedContextOverflowMessage: string | undefined;
|
|
22
24
|
private disposed = false;
|
|
23
25
|
private pendingThinkingLevel: Parameters<StageContext["setThinkingLevel"]>[0] | undefined;
|
|
24
26
|
private readonly pendingListeners = new Set<(event: StageSessionEvent) => void>();
|
|
@@ -325,7 +327,9 @@ export class StageSessionController {
|
|
|
325
327
|
}
|
|
326
328
|
this.unsubscribeTerminateWatcher?.();
|
|
327
329
|
this.unsubscribeTerminateWatcher = result.session.subscribe((event) => {
|
|
328
|
-
|
|
330
|
+
const terminatingId = terminatingToolCallId(event);
|
|
331
|
+
if (terminatingId !== undefined) this.terminatingToolCallIds.add(terminatingId);
|
|
332
|
+
this.unresolvedContextOverflowMessage = unresolvedContextOverflowMessage(event) ?? this.unresolvedContextOverflowMessage;
|
|
329
333
|
this.latestStructuredOutputToolErrorValue =
|
|
330
334
|
structuredOutputToolErrorFromEvent(event) ?? this.latestStructuredOutputToolErrorValue;
|
|
331
335
|
});
|
|
@@ -361,24 +365,29 @@ export class StageSessionController {
|
|
|
361
365
|
continue;
|
|
362
366
|
}
|
|
363
367
|
const promptStartIndex = activeSession.messages.length;
|
|
368
|
+
this.unresolvedContextOverflowMessage = undefined;
|
|
364
369
|
try {
|
|
365
370
|
await activeSession.prompt(nextText, sdkOptions);
|
|
366
371
|
const pendingPauseAfterPrompt = this.pauseRequest;
|
|
367
372
|
if (pendingPauseAfterPrompt) {
|
|
368
373
|
const { message } = await pendingPauseAfterPrompt.deferred.promise;
|
|
374
|
+
this.throwUnresolvedContextOverflowIfPresent();
|
|
369
375
|
nextText = message;
|
|
370
376
|
if (nextText === undefined) return { terminalScanStartIndex: activeSession.messages.length };
|
|
371
377
|
continue;
|
|
372
378
|
}
|
|
379
|
+
this.throwUnresolvedContextOverflowIfPresent();
|
|
373
380
|
return { terminalScanStartIndex: promptStartIndex };
|
|
374
381
|
} catch (err) {
|
|
375
382
|
const pendingPauseAfterThrow = this.pauseRequest;
|
|
376
383
|
if (pendingPauseAfterThrow) {
|
|
377
384
|
const { message } = await pendingPauseAfterThrow.deferred.promise;
|
|
385
|
+
this.throwUnresolvedContextOverflowIfPresent();
|
|
378
386
|
nextText = message;
|
|
379
387
|
if (nextText === undefined) return { terminalScanStartIndex: activeSession.messages.length };
|
|
380
388
|
continue;
|
|
381
389
|
}
|
|
390
|
+
this.throwUnresolvedContextOverflowIfPresent();
|
|
382
391
|
throw err;
|
|
383
392
|
}
|
|
384
393
|
}
|
|
@@ -420,10 +429,10 @@ export class StageSessionController {
|
|
|
420
429
|
this.notifyModelFallbackMetaChange();
|
|
421
430
|
throw err;
|
|
422
431
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
this.
|
|
426
|
-
return false;
|
|
432
|
+
const resumedOverflowNextIndex = nextResumedContextOverflowFallbackIndex(err, this.activeCandidateIndex, candidates.length);
|
|
433
|
+
if (resumedOverflowNextIndex === "terminal") { this.modelWarnings.push(...this.pendingFallbackWarnings); this.pendingFallbackWarnings.length = 0; this.notifyModelFallbackMetaChange(); throw err; }
|
|
434
|
+
this.pendingFallbackWarnings.push(resumedOverflowNextIndex === undefined ? `[fallback] resume on ${resumedLabel} failed: ${message}. Restarting fallback from ${this.candidateLabel(candidates[0]!)}.` : `[fallback] resume on ${resumedLabel} failed: ${message}. Retrying with ${this.candidateLabel(candidates[resumedOverflowNextIndex]!)}.`);
|
|
435
|
+
await this.disposeCurrentSession(); this.activeCandidateIndex = resumedOverflowNextIndex; return false;
|
|
427
436
|
}
|
|
428
437
|
}
|
|
429
438
|
|
|
@@ -479,22 +488,12 @@ export class StageSessionController {
|
|
|
479
488
|
private candidateLabel(candidate: WorkflowResolvedModelCandidate): string {
|
|
480
489
|
return candidate.reasoningLevel !== undefined ? `${candidate.id}:${candidate.reasoningLevel}` : candidate.id;
|
|
481
490
|
}
|
|
482
|
-
|
|
483
491
|
private isWorkflowFastModeEnabled(): boolean | undefined {
|
|
484
492
|
const model = this.session?.model;
|
|
485
493
|
const settingsManager = this.sessionSettingsManager ?? this.effectiveStageOptions?.settingsManager;
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
private recordTerminatingToolCall(event: unknown): void {
|
|
491
|
-
if (event === null || typeof event !== "object") return;
|
|
492
|
-
const record = event as Record<string, unknown>;
|
|
493
|
-
if (record["type"] !== "tool_execution_end") return;
|
|
494
|
-
const result = record["result"];
|
|
495
|
-
if (result === null || typeof result !== "object") return;
|
|
496
|
-
if ((result as Record<string, unknown>)["terminate"] !== true) return;
|
|
497
|
-
const callId = record["toolCallId"];
|
|
498
|
-
if (typeof callId === "string" && callId.length > 0) this.terminatingToolCallIds.add(callId);
|
|
494
|
+
return model === undefined || settingsManager === undefined
|
|
495
|
+
? undefined
|
|
496
|
+
: shouldApplyCodexFastModeForScope(model, settingsManager.getCodexFastModeSettings(), "workflow");
|
|
499
497
|
}
|
|
498
|
+
private throwUnresolvedContextOverflowIfPresent(): void { const message = this.unresolvedContextOverflowMessage; this.unresolvedContextOverflowMessage = undefined; if (message !== undefined) throw unresolvedContextOverflowFailure(message); }
|
|
500
499
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { WorkflowPromptModelFailure } from "./stage-runner-messages.js";
|
|
2
|
+
|
|
3
|
+
export function unresolvedContextOverflowMessage(event: unknown): string | undefined {
|
|
4
|
+
if (event === null || typeof event !== "object") return undefined;
|
|
5
|
+
const record = event as Record<string, unknown>;
|
|
6
|
+
if (record["type"] !== "compaction_end" || record["unresolvedOverflow"] !== true) return undefined;
|
|
7
|
+
const message = record["errorMessage"];
|
|
8
|
+
return typeof message === "string" && message.trim().length > 0
|
|
9
|
+
? message
|
|
10
|
+
: "Context overflow recovery was exhausted for the current model.";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function unresolvedContextOverflowFailure(message: string): WorkflowPromptModelFailure {
|
|
14
|
+
return new WorkflowPromptModelFailure({ message, code: "context_length_exceeded", stopReason: "error" });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isUnresolvedContextOverflowFailure(error: unknown): boolean {
|
|
18
|
+
if (!(error instanceof WorkflowPromptModelFailure)) return false;
|
|
19
|
+
const cause = error.cause;
|
|
20
|
+
if (cause === null || typeof cause !== "object") return false;
|
|
21
|
+
return (cause as { readonly code?: unknown }).code === "context_length_exceeded";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function nextResumedContextOverflowFallbackIndex(error: unknown, currentIndex: number | undefined, candidateCount: number): number | "terminal" | undefined {
|
|
25
|
+
if (!isUnresolvedContextOverflowFailure(error) || currentIndex === undefined) return undefined;
|
|
26
|
+
const nextIndex = currentIndex + 1;
|
|
27
|
+
return nextIndex >= candidateCount ? "terminal" : nextIndex;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function terminatingToolCallId(event: unknown): string | undefined {
|
|
31
|
+
if (event === null || typeof event !== "object") return undefined;
|
|
32
|
+
const record = event as Record<string, unknown>;
|
|
33
|
+
const result = record["result"];
|
|
34
|
+
if (record["type"] !== "tool_execution_end" || result === null || typeof result !== "object") return undefined;
|
|
35
|
+
if ((result as Record<string, unknown>)["terminate"] !== true) return undefined;
|
|
36
|
+
const callId = record["toolCallId"];
|
|
37
|
+
return typeof callId === "string" && callId.length > 0 ? callId : undefined;
|
|
38
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Value } from "typebox/value";
|
|
2
2
|
import { workflowSerializableObjectSchema } from "./serializable.js";
|
|
3
3
|
import type { Store } from "./store.js";
|
|
4
|
-
import type { SessionEntry } from "./persistence-restore.js";
|
|
4
|
+
import type { NormalizedSessionEntry as SessionEntry } from "./persistence-restore.js";
|
|
5
5
|
import type {
|
|
6
6
|
RunStatus,
|
|
7
7
|
StageSnapshot,
|
|
@@ -255,6 +255,14 @@ function numericRetryAfterMs(value: unknown): number | undefined {
|
|
|
255
255
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
+
function numericTimestamp(value: unknown): number | undefined {
|
|
259
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function numericDuration(value: unknown): number | undefined {
|
|
263
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
264
|
+
}
|
|
265
|
+
|
|
258
266
|
export function findRunBlockedMetadata(
|
|
259
267
|
entries: readonly SessionEntry[],
|
|
260
268
|
runId: string,
|
|
@@ -360,6 +368,8 @@ export function restoreTerminalRuns(entries: readonly SessionEntry[], store: Sto
|
|
|
360
368
|
const retryAfterMs = numericRetryAfterMs(end["retryAfterMs"]);
|
|
361
369
|
const failureMessage = end["failureMessage"];
|
|
362
370
|
const failedStageId = end["failedStageId"];
|
|
371
|
+
const restoredEndedAt = numericTimestamp(end["endedAt"]);
|
|
372
|
+
const restoredDurationMs = numericDuration(end["durationMs"]);
|
|
363
373
|
store.recordRunEnd(
|
|
364
374
|
runId,
|
|
365
375
|
status,
|
|
@@ -378,6 +388,13 @@ export function restoreTerminalRuns(entries: readonly SessionEntry[], store: Sto
|
|
|
378
388
|
...(typeof exitReason === "string" ? { exitReason } : {}),
|
|
379
389
|
},
|
|
380
390
|
);
|
|
391
|
+
const restoredRun = store.runs().find((run) => run.id === runId);
|
|
392
|
+
if (restoredRun !== undefined && (restoredEndedAt !== undefined || restoredDurationMs !== undefined)) {
|
|
393
|
+
Object.assign(restoredRun, {
|
|
394
|
+
...(restoredEndedAt !== undefined ? { endedAt: restoredEndedAt } : {}),
|
|
395
|
+
...(restoredDurationMs !== undefined ? { durationMs: restoredDurationMs } : {}),
|
|
396
|
+
});
|
|
397
|
+
}
|
|
381
398
|
}
|
|
382
399
|
}
|
|
383
400
|
|