@bastani/atomic 0.9.11-alpha.9 → 0.9.11
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 +134 -0
- package/README.md +1 -1
- package/dist/builtin/intercom/CHANGELOG.md +6 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/subagent-relay.ts +8 -5
- package/dist/builtin/mcp/CHANGELOG.md +10 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +20 -0
- package/dist/builtin/subagents/agents/code-simplifier.md +2 -2
- package/dist/builtin/subagents/agents/codebase-analyzer.md +2 -2
- package/dist/builtin/subagents/agents/codebase-locator.md +2 -2
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +2 -2
- package/dist/builtin/subagents/agents/codebase-pattern-finder.md +2 -2
- package/dist/builtin/subagents/agents/codebase-research-analyzer.md +2 -2
- package/dist/builtin/subagents/agents/codebase-research-locator.md +2 -2
- package/dist/builtin/subagents/agents/worker.md +2 -2
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/notification-content.ts +14 -9
- package/dist/builtin/subagents/src/runs/background/notify.ts +20 -4
- package/dist/builtin/web-access/CHANGELOG.md +20 -0
- package/dist/builtin/web-access/content-tools.ts +1 -0
- package/dist/builtin/web-access/flat-string.ts +37 -0
- package/dist/builtin/web-access/github-api.ts +2 -1
- package/dist/builtin/web-access/github-extract.ts +3 -2
- package/dist/builtin/web-access/package.json +2 -2
- package/dist/builtin/workflows/CHANGELOG.md +57 -0
- package/dist/builtin/workflows/builtin/goal-ledger.ts +6 -4
- package/dist/builtin/workflows/builtin/goal-runner.ts +2 -1
- package/dist/builtin/workflows/builtin/goal.ts +1 -1
- package/dist/builtin/workflows/builtin/ralph-core.ts +8 -9
- package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +2 -4
- package/dist/builtin/workflows/builtin/ralph-models.ts +4 -4
- package/dist/builtin/workflows/builtin/ralph-runner.ts +5 -8
- package/dist/builtin/workflows/builtin/ralph.ts +2 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/durable/completed-catalog.ts +14 -8
- package/dist/builtin/workflows/src/durable/completed-inspection.ts +10 -11
- package/dist/builtin/workflows/src/durable/resume-catalog.ts +1 -1
- package/dist/builtin/workflows/src/durable/resume-eligibility.ts +42 -0
- package/dist/builtin/workflows/src/durable/resume-runtime.ts +36 -37
- package/dist/builtin/workflows/src/durable/tool-primitive.ts +10 -2
- package/dist/builtin/workflows/src/engine/run.ts +1 -0
- package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +12 -8
- package/dist/builtin/workflows/src/extension/hil-answer-notifications.ts +5 -2
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +1966 -880
- package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +6 -3
- package/dist/builtin/workflows/src/extension/postmortem-deps.ts +3 -2
- package/dist/builtin/workflows/src/extension/render-result.ts +4 -11
- package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +17 -20
- package/dist/builtin/workflows/src/extension/runtime.ts +3 -3
- package/dist/builtin/workflows/src/extension/workflow-command-completions.ts +1 -1
- package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +3 -7
- package/dist/builtin/workflows/src/extension/workflow-durable-resume-command.ts +21 -24
- package/dist/builtin/workflows/src/extension/workflow-resume-picker-rows.ts +38 -26
- package/dist/builtin/workflows/src/extension/workflow-resume-shadow.ts +2 -1
- package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +56 -64
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +2 -2
- package/dist/builtin/workflows/src/extension/workflow-status-summary.ts +0 -5
- package/dist/builtin/workflows/src/extension/workflow-targets.ts +17 -20
- package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +1 -1
- package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +24 -51
- package/dist/builtin/workflows/src/extension/workflow-tool-inspection.ts +7 -29
- package/dist/builtin/workflows/src/extension/workflow-tool-send.ts +5 -13
- package/dist/builtin/workflows/src/extension/workflow-tool.ts +4 -13
- package/dist/builtin/workflows/src/runs/background/quit.ts +4 -3
- package/dist/builtin/workflows/src/runs/background/run-inspect.ts +8 -8
- package/dist/builtin/workflows/src/runs/background/status.ts +2 -1
- package/dist/builtin/workflows/src/runs/background/workflow-lifecycle-aggregate.ts +2 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +5 -5
- package/dist/builtin/workflows/src/runs/foreground/executor-lifecycle.ts +4 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-scheduler.ts +2 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +23 -8
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +6 -2
- package/dist/builtin/workflows/src/runs/foreground/executor-task-prompts.ts +5 -0
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +56 -10
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +6 -0
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-output.ts +137 -6
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +2 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +2 -0
- package/dist/builtin/workflows/src/shared/expanded-workflow-graph.ts +159 -10
- package/dist/builtin/workflows/src/shared/flat-string.ts +33 -0
- package/dist/builtin/workflows/src/shared/graph-store-snapshot.ts +144 -0
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +2 -3
- package/dist/builtin/workflows/src/shared/run-id.ts +36 -0
- package/dist/builtin/workflows/src/shared/store-internal.ts +32 -3
- package/dist/builtin/workflows/src/shared/store-observation.ts +10 -0
- package/dist/builtin/workflows/src/shared/store-public-types.ts +8 -0
- package/dist/builtin/workflows/src/shared/store-run-methods.ts +14 -0
- package/dist/builtin/workflows/src/shared/store-types.ts +4 -0
- package/dist/builtin/workflows/src/shared/types.ts +2 -0
- package/dist/builtin/workflows/src/shared/workflow-artifacts.ts +261 -0
- package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +39 -23
- package/dist/builtin/workflows/src/tui/graph-canvas.ts +26 -9
- package/dist/builtin/workflows/src/tui/graph-view-constants.ts +3 -0
- package/dist/builtin/workflows/src/tui/graph-view-graph-render.ts +95 -98
- package/dist/builtin/workflows/src/tui/graph-view-render.ts +7 -20
- package/dist/builtin/workflows/src/tui/graph-view-state.ts +160 -13
- package/dist/builtin/workflows/src/tui/layout.ts +8 -2
- package/dist/builtin/workflows/src/tui/node-card.ts +57 -45
- package/dist/builtin/workflows/src/tui/overlay-adapter.ts +4 -2
- package/dist/builtin/workflows/src/tui/prompt-card-render.ts +291 -21
- package/dist/builtin/workflows/src/tui/prompt-card.ts +1 -1
- package/dist/builtin/workflows/src/tui/renderers.ts +0 -1
- package/dist/builtin/workflows/src/tui/run-detail.ts +52 -35
- package/dist/builtin/workflows/src/tui/run-identity-rows.ts +97 -0
- package/dist/builtin/workflows/src/tui/session-overlays.ts +24 -3
- package/dist/builtin/workflows/src/tui/session-picker.ts +94 -41
- package/dist/builtin/workflows/src/tui/stage-chat-view-archive-history.ts +190 -26
- package/dist/builtin/workflows/src/tui/stage-chat-view-footer-status.ts +52 -18
- package/dist/builtin/workflows/src/tui/stage-chat-view-input.ts +9 -5
- package/dist/builtin/workflows/src/tui/stage-chat-view-state.ts +44 -4
- package/dist/builtin/workflows/src/tui/stage-chat-view-types.ts +3 -1
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +3 -2
- package/dist/builtin/workflows/src/tui/status-list.ts +67 -45
- package/dist/builtin/workflows/src/tui/store-widget-installer.ts +7 -6
- package/dist/builtin/workflows/src/tui/widget.ts +49 -40
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +15 -10
- package/dist/builtin/workflows/src/tui/workflow-resume-selector.ts +21 -8
- package/dist/core/agent-session-accessors.d.ts.map +1 -1
- package/dist/core/agent-session-accessors.js +5 -0
- package/dist/core/agent-session-accessors.js.map +1 -1
- package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-auto-compaction.js +5 -0
- package/dist/core/agent-session-auto-compaction.js.map +1 -1
- package/dist/core/agent-session-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-compaction.js +3 -0
- package/dist/core/agent-session-compaction.js.map +1 -1
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +2 -1
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-message-queue.d.ts +3 -7
- package/dist/core/agent-session-message-queue.d.ts.map +1 -1
- package/dist/core/agent-session-message-queue.js +27 -13
- package/dist/core/agent-session-message-queue.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +5 -3
- 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-persistent-custom-messages.d.ts.map +1 -1
- package/dist/core/agent-session-persistent-custom-messages.js +6 -3
- package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
- package/dist/core/agent-session-post-tool-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-post-tool-compaction.js +11 -2
- package/dist/core/agent-session-post-tool-compaction.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +5 -3
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-tree.d.ts.map +1 -1
- package/dist/core/agent-session-tree.js +4 -0
- package/dist/core/agent-session-tree.js.map +1 -1
- package/dist/core/agent-session-types.d.ts +4 -0
- 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 +2 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage-backends.d.ts.map +1 -1
- package/dist/core/auth-storage-backends.js +12 -1
- package/dist/core/auth-storage-backends.js.map +1 -1
- package/dist/core/bounded-model-refresh.d.ts +14 -0
- package/dist/core/bounded-model-refresh.d.ts.map +1 -0
- package/dist/core/bounded-model-refresh.js +38 -0
- package/dist/core/bounded-model-refresh.js.map +1 -0
- package/dist/core/context-tool-pairing.d.ts +4 -4
- package/dist/core/context-tool-pairing.js +4 -4
- package/dist/core/context-tool-pairing.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +26 -1
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/messages.d.ts +18 -6
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +46 -18
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-refresh-timeout.d.ts +9 -0
- package/dist/core/model-refresh-timeout.d.ts.map +1 -0
- package/dist/core/model-refresh-timeout.js +9 -0
- package/dist/core/model-refresh-timeout.js.map +1 -0
- package/dist/core/model-runtime-snapshot.d.ts +3 -0
- package/dist/core/model-runtime-snapshot.d.ts.map +1 -1
- package/dist/core/model-runtime-snapshot.js +59 -0
- package/dist/core/model-runtime-snapshot.js.map +1 -1
- package/dist/core/model-runtime.d.ts +7 -1
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +72 -17
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +4 -3
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager-core.d.ts +1 -1
- package/dist/core/session-manager-core.d.ts.map +1 -1
- package/dist/core/session-manager-core.js +2 -2
- package/dist/core/session-manager-core.js.map +1 -1
- package/dist/core/session-manager-entries.d.ts +1 -1
- package/dist/core/session-manager-entries.d.ts.map +1 -1
- package/dist/core/session-manager-entries.js +2 -1
- package/dist/core/session-manager-entries.js.map +1 -1
- package/dist/core/session-manager-history.d.ts.map +1 -1
- package/dist/core/session-manager-history.js +2 -2
- package/dist/core/session-manager-history.js.map +1 -1
- package/dist/core/session-manager-types.d.ts +3 -0
- package/dist/core/session-manager-types.d.ts.map +1 -1
- package/dist/core/session-manager-types.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main-app-mode.d.ts +1 -0
- package/dist/main-app-mode.d.ts.map +1 -1
- package/dist/main-app-mode.js +3 -0
- package/dist/main-app-mode.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +8 -3
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-actions.d.ts +7 -2
- package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-actions.js +13 -4
- package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-editor.d.ts +3 -1
- package/dist/modes/interactive/components/chat-session-host-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-editor.js +2 -2
- package/dist/modes/interactive/components/chat-session-host-editor.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.d.ts +3 -0
- package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.js +5 -4
- package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.d.ts +16 -2
- package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.js +35 -5
- package/dist/modes/interactive/components/chat-session-host.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.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +0 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +25 -35
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-editor-actions.js +2 -1
- package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.js +1 -1
- package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +4 -1
- 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-model-routing.js +4 -1
- package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-pause.d.ts +3 -1
- package/dist/modes/interactive/interactive-pause.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-pause.js +5 -1
- package/dist/modes/interactive/interactive-pause.js.map +1 -1
- package/dist/modes/interactive/interactive-queueing.js +3 -3
- package/dist/modes/interactive/interactive-queueing.js.map +1 -1
- package/dist/modes/interactive-engine/engine-health.d.ts +6 -0
- package/dist/modes/interactive-engine/engine-health.d.ts.map +1 -1
- package/dist/modes/interactive-engine/engine-health.js +15 -1
- package/dist/modes/interactive-engine/engine-health.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-auth.js +1 -1
- package/dist/modes/interactive-engine/isolated-auth.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.d.ts +1 -0
- package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.js +7 -1
- package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
- package/dist/modes/interactive-engine/remote-model-catalog.d.ts.map +1 -1
- package/dist/modes/interactive-engine/remote-model-catalog.js +5 -1
- package/dist/modes/interactive-engine/remote-model-catalog.js.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.js +23 -7
- package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
- package/dist/modes/rpc/rpc-input-scheduler.js +1 -1
- package/dist/modes/rpc/rpc-input-scheduler.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +2 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/custom-provider.md +3 -3
- package/docs/extensions.md +3 -1
- package/docs/index.md +10 -0
- package/docs/keybindings.md +1 -1
- package/docs/models.md +2 -0
- package/docs/providers.md +7 -3
- package/docs/quickstart.md +7 -1
- package/docs/session-format.md +2 -0
- package/docs/termux.md +3 -1
- package/docs/usage.md +3 -2
- package/docs/workflows.md +82 -46
- package/npm-shrinkwrap.json +59 -27
- package/package.json +3 -3
- package/dist/builtin/workflows/src/tui/toast.ts +0 -107
|
@@ -2,7 +2,12 @@ import { createStructuredOutputCapture, runCallback } from "@bastani/atomic";
|
|
|
2
2
|
import type { StageExecutionMeta } from "../../shared/types.js";
|
|
3
3
|
import { StageSessionController } from "./stage-runner-controller.js";
|
|
4
4
|
import { assistantMessage } from "./stage-runner-messages.js";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
finalizePromptOutput,
|
|
7
|
+
splitPromptOptions,
|
|
8
|
+
stageOutputInstruction,
|
|
9
|
+
validatePromptOutputOptions,
|
|
10
|
+
} from "./stage-runner-output.js";
|
|
6
11
|
import {
|
|
7
12
|
formatStructuredOutputCorrectionPrompt,
|
|
8
13
|
STRUCTURED_OUTPUT_MAX_CORRECTIVE_PROMPTS,
|
|
@@ -27,18 +32,31 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
|
|
|
27
32
|
};
|
|
28
33
|
const controller = new StageSessionController(opts, meta, effectiveStageOptions, structuredOutputCapture);
|
|
29
34
|
let lastAssistantText: string | undefined;
|
|
35
|
+
let lastFinalizedOutput: string | undefined;
|
|
36
|
+
let lastFinalizedMessageCount: number | undefined;
|
|
30
37
|
let adapterMessages = [] as InternalStageContext["messages"];
|
|
31
38
|
|
|
32
39
|
function runtimeCwd(): string {
|
|
33
40
|
return typeof effectiveStageOptions?.cwd === "string" ? effectiveStageOptions.cwd : process.cwd();
|
|
34
41
|
}
|
|
35
42
|
|
|
43
|
+
function finalizedOutputIsCurrent(): boolean {
|
|
44
|
+
return (
|
|
45
|
+
lastFinalizedOutput !== undefined &&
|
|
46
|
+
(lastFinalizedMessageCount === undefined ||
|
|
47
|
+
controller.currentSession?.messages.length === lastFinalizedMessageCount)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
36
51
|
return {
|
|
37
52
|
name: stageName,
|
|
38
53
|
|
|
39
54
|
async prompt(text, options) {
|
|
40
55
|
const { sdkOptions, outputOptions } = splitPromptOptions(options);
|
|
41
56
|
validatePromptOutputOptions(outputOptions);
|
|
57
|
+
// The runtime owns the artifact write, so it states that contract itself
|
|
58
|
+
// rather than relying on every workflow definition to describe it.
|
|
59
|
+
const promptText = `${text}${stageOutputInstruction(outputOptions)}`;
|
|
42
60
|
if (structuredOutputCapture?.called) {
|
|
43
61
|
throw new Error(
|
|
44
62
|
"atomic-workflows: stage schema supports one prompt() call per stage context because structured_output may be called exactly once. Create a new ctx.stage(...) for each additional schema-backed prompt.",
|
|
@@ -52,14 +70,22 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
|
|
|
52
70
|
}
|
|
53
71
|
const rawText = await runCallback(
|
|
54
72
|
{ kind: "workflow.stage_adapter", name: `prompt:${stageName}`, runId, stageId },
|
|
55
|
-
() => adapters.prompt!.prompt(
|
|
73
|
+
() => adapters.prompt!.prompt(promptText, meta),
|
|
56
74
|
);
|
|
57
|
-
|
|
58
|
-
|
|
75
|
+
adapterMessages = assistantMessage(rawText);
|
|
76
|
+
lastAssistantText = await finalizePromptOutput(
|
|
77
|
+
rawText,
|
|
78
|
+
outputOptions,
|
|
79
|
+
runtimeCwd(),
|
|
80
|
+
runId,
|
|
81
|
+
adapterMessages,
|
|
82
|
+
);
|
|
83
|
+
lastFinalizedOutput = lastAssistantText;
|
|
84
|
+
lastFinalizedMessageCount = controller.currentSession?.messages.length;
|
|
59
85
|
return lastAssistantText;
|
|
60
86
|
}
|
|
61
87
|
if (structuredOutputCapture) {
|
|
62
|
-
let nextPrompt =
|
|
88
|
+
let nextPrompt = promptText;
|
|
63
89
|
let correctiveAttempts = 0;
|
|
64
90
|
let structuredOutputError = STRUCTURED_OUTPUT_MISSING_ERROR;
|
|
65
91
|
while (!structuredOutputCapture.called) {
|
|
@@ -74,17 +100,35 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
|
|
|
74
100
|
nextPrompt = formatStructuredOutputCorrectionPrompt(structuredOutputError, correctiveAttempts);
|
|
75
101
|
}
|
|
76
102
|
const rawStructuredText = stringifyStructuredOutputValue(structuredOutputCapture.value);
|
|
77
|
-
lastAssistantText = await finalizePromptOutput(
|
|
103
|
+
lastAssistantText = await finalizePromptOutput(
|
|
104
|
+
rawStructuredText,
|
|
105
|
+
outputOptions,
|
|
106
|
+
runtimeCwd(),
|
|
107
|
+
runId,
|
|
108
|
+
controller.currentSession?.messages,
|
|
109
|
+
);
|
|
110
|
+
lastFinalizedOutput = lastAssistantText;
|
|
111
|
+
lastFinalizedMessageCount = controller.currentSession?.messages.length;
|
|
78
112
|
return structuredOutputCapture.value as never;
|
|
79
113
|
}
|
|
80
|
-
await controller.promptWithFallback(
|
|
114
|
+
await controller.promptWithFallback(promptText, sdkOptions);
|
|
81
115
|
const rawText = controller.lastAssistantText(lastAssistantText) ?? "";
|
|
82
|
-
lastAssistantText = await finalizePromptOutput(
|
|
116
|
+
lastAssistantText = await finalizePromptOutput(
|
|
117
|
+
rawText,
|
|
118
|
+
outputOptions,
|
|
119
|
+
runtimeCwd(),
|
|
120
|
+
runId,
|
|
121
|
+
controller.currentSession?.messages,
|
|
122
|
+
);
|
|
123
|
+
lastFinalizedOutput = lastAssistantText;
|
|
124
|
+
lastFinalizedMessageCount = controller.currentSession?.messages.length;
|
|
83
125
|
return lastAssistantText;
|
|
84
126
|
},
|
|
85
127
|
|
|
86
128
|
async complete(text, completeOpts) {
|
|
87
129
|
if (adapters.complete) {
|
|
130
|
+
lastFinalizedOutput = undefined;
|
|
131
|
+
lastFinalizedMessageCount = undefined;
|
|
88
132
|
lastAssistantText = await runCallback(
|
|
89
133
|
{ kind: "workflow.stage_adapter", name: `complete:${stageName}`, runId, stageId },
|
|
90
134
|
() => adapters.complete!.complete(text, completeOpts, meta),
|
|
@@ -102,6 +146,8 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
|
|
|
102
146
|
);
|
|
103
147
|
}
|
|
104
148
|
await controller.promptWithFallback(text, undefined, "complete");
|
|
149
|
+
lastFinalizedOutput = undefined;
|
|
150
|
+
lastFinalizedMessageCount = undefined;
|
|
105
151
|
lastAssistantText = controller.lastAssistantText(lastAssistantText) ?? "";
|
|
106
152
|
return lastAssistantText;
|
|
107
153
|
},
|
|
@@ -195,11 +241,11 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
|
|
|
195
241
|
},
|
|
196
242
|
|
|
197
243
|
__getLastAssistantText() {
|
|
198
|
-
return controller.lastAssistantText(lastAssistantText);
|
|
244
|
+
return finalizedOutputIsCurrent() ? lastFinalizedOutput : controller.lastAssistantText(lastAssistantText);
|
|
199
245
|
},
|
|
200
246
|
|
|
201
247
|
getLastAssistantText() {
|
|
202
|
-
return controller.lastAssistantText(lastAssistantText);
|
|
248
|
+
return finalizedOutputIsCurrent() ? lastFinalizedOutput : controller.lastAssistantText(lastAssistantText);
|
|
203
249
|
},
|
|
204
250
|
|
|
205
251
|
async __ensureSession() {
|
|
@@ -65,6 +65,7 @@ export class StageSessionController {
|
|
|
65
65
|
private activeCreation: Promise<StageSessionRuntime> | undefined;
|
|
66
66
|
private sessionPromise: Promise<StageSessionRuntime> | undefined;
|
|
67
67
|
private reattachSessionFile: string | undefined;
|
|
68
|
+
private lastPromptStartIndex: number | undefined;
|
|
68
69
|
private readonly terminatingToolCallIds = new Set<string>();
|
|
69
70
|
private latestStructuredOutputToolErrorValue: string | undefined;
|
|
70
71
|
private unsubscribeTerminateWatcher: (() => void) | undefined;
|
|
@@ -116,6 +117,10 @@ export class StageSessionController {
|
|
|
116
117
|
return this.session;
|
|
117
118
|
}
|
|
118
119
|
|
|
120
|
+
get currentPromptStartIndex(): number | undefined {
|
|
121
|
+
return this.lastPromptStartIndex;
|
|
122
|
+
}
|
|
123
|
+
|
|
119
124
|
get latestStructuredOutputToolError(): string | undefined {
|
|
120
125
|
return this.latestStructuredOutputToolErrorValue;
|
|
121
126
|
}
|
|
@@ -478,6 +483,7 @@ export class StageSessionController {
|
|
|
478
483
|
continue;
|
|
479
484
|
}
|
|
480
485
|
const promptStartIndex = activeSession.messages.length;
|
|
486
|
+
this.lastPromptStartIndex = promptStartIndex;
|
|
481
487
|
this.unresolvedContextOverflowMessage = undefined;
|
|
482
488
|
try {
|
|
483
489
|
await activeSession.prompt(nextText, sdkOptions);
|
|
@@ -1,11 +1,29 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
-
import { dirname, isAbsolute, resolve } from "node:path";
|
|
3
|
-
import type { PromptOptions } from "@bastani/atomic";
|
|
3
|
+
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
|
+
import type { AgentSession, PromptOptions } from "@bastani/atomic";
|
|
4
5
|
import type { StageOutputOptions, StagePromptOptions, WorkflowMaxOutput } from "../../shared/types.js";
|
|
6
|
+
import { ensureWorkflowArtifactRunDirectory, workflowArtifactRunPath } from "../../shared/workflow-artifacts.js";
|
|
5
7
|
|
|
6
8
|
const DEFAULT_MAX_OUTPUT_BYTES = 200 * 1024;
|
|
7
9
|
const DEFAULT_MAX_OUTPUT_LINES = 5000;
|
|
8
10
|
|
|
11
|
+
type TranscriptContentBlock = {
|
|
12
|
+
readonly type?: string;
|
|
13
|
+
readonly text?: string;
|
|
14
|
+
readonly name?: string;
|
|
15
|
+
readonly id?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type TranscriptMessage = {
|
|
19
|
+
readonly role?: string;
|
|
20
|
+
readonly content?: string | readonly TranscriptContentBlock[];
|
|
21
|
+
readonly customType?: string;
|
|
22
|
+
readonly stageAdmissionKey?: string;
|
|
23
|
+
readonly toolCallId?: string;
|
|
24
|
+
readonly toolName?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
9
27
|
function normalizeMaxOutput(maxOutput: WorkflowMaxOutput | undefined): Required<WorkflowMaxOutput> {
|
|
10
28
|
return {
|
|
11
29
|
bytes: maxOutput?.bytes ?? DEFAULT_MAX_OUTPUT_BYTES,
|
|
@@ -45,7 +63,7 @@ function truncateOutput(text: string, maxOutput: WorkflowMaxOutput | undefined):
|
|
|
45
63
|
function countLines(text: string): number {
|
|
46
64
|
if (!text) return 0;
|
|
47
65
|
const newlineMatches = text.match(/\r\n|\r|\n/g);
|
|
48
|
-
return (newlineMatches?.length ?? 0) + (
|
|
66
|
+
return (newlineMatches?.length ?? 0) + (text.endsWith("\n") || text.endsWith("\r") ? 0 : 1);
|
|
49
67
|
}
|
|
50
68
|
|
|
51
69
|
function formatByteSize(bytes: number): string {
|
|
@@ -60,11 +78,68 @@ function formatByteSize(bytes: number): string {
|
|
|
60
78
|
return `${value.toFixed(1)} ${units[unitIndex]}`;
|
|
61
79
|
}
|
|
62
80
|
|
|
63
|
-
function
|
|
81
|
+
function transcriptPath(runId: string, outputPath: string): string {
|
|
82
|
+
const digest = createHash("sha256").update(outputPath).digest("hex").slice(0, 16);
|
|
83
|
+
return join(workflowArtifactRunPath(runId), "transcripts", `${digest}-${basename(outputPath)}.transcript.md`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function appendContent(lines: string[], content: TranscriptMessage["content"]): void {
|
|
87
|
+
if (typeof content === "string") {
|
|
88
|
+
lines.push(content);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!Array.isArray(content)) return;
|
|
92
|
+
for (const block of content) {
|
|
93
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
94
|
+
lines.push(block.text);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const serialized = JSON.stringify(block);
|
|
98
|
+
if (serialized !== undefined) lines.push(serialized);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function renderTranscript(messages: AgentSession["messages"] | undefined, fallbackOutput: string): string {
|
|
103
|
+
const lines = ["# Workflow stage transcript", ""];
|
|
104
|
+
for (const [index, message] of (messages ?? []).entries()) {
|
|
105
|
+
const record = message as TranscriptMessage;
|
|
106
|
+
const role = typeof record.role === "string" ? record.role : "message";
|
|
107
|
+
lines.push(`## ${index + 1} ${role}`);
|
|
108
|
+
if (typeof record.customType === "string") lines.push(`customType: ${record.customType}`);
|
|
109
|
+
if (typeof record.stageAdmissionKey === "string") lines.push(`stageAdmissionKey: ${record.stageAdmissionKey}`);
|
|
110
|
+
if (typeof record.toolName === "string") lines.push(`toolName: ${record.toolName}`);
|
|
111
|
+
if (typeof record.toolCallId === "string") lines.push(`toolCallId: ${record.toolCallId}`);
|
|
112
|
+
appendContent(lines, record.content);
|
|
113
|
+
lines.push("");
|
|
114
|
+
}
|
|
115
|
+
if ((messages?.length ?? 0) === 0) {
|
|
116
|
+
lines.push("## 1 assistant");
|
|
117
|
+
lines.push(fallbackOutput);
|
|
118
|
+
lines.push("");
|
|
119
|
+
}
|
|
120
|
+
return lines.join("\n");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function savedOutputReference(
|
|
124
|
+
outputPath: string,
|
|
125
|
+
fullOutput: string,
|
|
126
|
+
transcriptPathValue: string,
|
|
127
|
+
transcriptText: string,
|
|
128
|
+
transcriptAvailable: boolean,
|
|
129
|
+
warning?: string,
|
|
130
|
+
): string {
|
|
64
131
|
const absolutePath = resolve(outputPath);
|
|
65
132
|
const bytes = Buffer.byteLength(fullOutput, "utf8");
|
|
66
133
|
const lines = countLines(fullOutput);
|
|
67
|
-
|
|
134
|
+
const transcriptBytes = Buffer.byteLength(transcriptText, "utf8");
|
|
135
|
+
const transcriptLines = countLines(transcriptText);
|
|
136
|
+
const outputReference = `Output saved to: ${absolutePath} (${formatByteSize(bytes)}, ${lines} ${lines === 1 ? "line" : "lines"}). Read this file if needed.`;
|
|
137
|
+
const transcriptReference = transcriptAvailable
|
|
138
|
+
? `Transcript saved to: ${transcriptPathValue} (${formatByteSize(transcriptBytes)}, ${transcriptLines} ${transcriptLines === 1 ? "line" : "lines"}). Search it with rg, read narrow line ranges, and do not read it whole.`
|
|
139
|
+
: `Transcript unavailable at: ${transcriptPathValue}. Search it with rg when it becomes available, read narrow line ranges, and do not read it whole.`;
|
|
140
|
+
return [outputReference, transcriptReference, warning]
|
|
141
|
+
.filter((line): line is string => line !== undefined)
|
|
142
|
+
.join("\n");
|
|
68
143
|
}
|
|
69
144
|
|
|
70
145
|
function resolveOutputPath(
|
|
@@ -83,6 +158,19 @@ function resolveOutputPath(
|
|
|
83
158
|
return resolve(baseCwd, output);
|
|
84
159
|
}
|
|
85
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Report only what can be observed, never what the text appears to mean.
|
|
163
|
+
*
|
|
164
|
+
* An empty artifact is a fact. Whether a non-empty artifact is "really" a
|
|
165
|
+
* pointer, an acknowledgement, or a deliverable is an inference this code
|
|
166
|
+
* cannot make, so it does not try: the companion transcript named in every
|
|
167
|
+
* receipt is the recovery path for anything that looks wrong to a reader.
|
|
168
|
+
*/
|
|
169
|
+
function degenerateWarning(fullOutput: string): string | undefined {
|
|
170
|
+
return fullOutput.trim().length === 0
|
|
171
|
+
? "WARNING: the stage artifact is empty; search the companion transcript for this stage's work."
|
|
172
|
+
: undefined;
|
|
173
|
+
}
|
|
86
174
|
export function splitPromptOptions(options: StagePromptOptions | undefined): {
|
|
87
175
|
sdkOptions: PromptOptions | undefined;
|
|
88
176
|
outputOptions: StageOutputOptions;
|
|
@@ -122,23 +210,66 @@ export function validatePromptOutputOptions(outputOptions: StageOutputOptions):
|
|
|
122
210
|
}
|
|
123
211
|
}
|
|
124
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Instruction appended to any prompt whose stage configures `output:`.
|
|
215
|
+
*
|
|
216
|
+
* The runtime owns the artifact write, so the prompt author should not have to
|
|
217
|
+
* describe that plumbing — or remember to. Without this, a stage can end with
|
|
218
|
+
* "done, see the file" and persist that sentence as its deliverable, which is a
|
|
219
|
+
* foot-gun the workflow definition cannot see.
|
|
220
|
+
*/
|
|
221
|
+
export function stageOutputInstruction(outputOptions: StageOutputOptions): string {
|
|
222
|
+
if (typeof outputOptions.output !== "string" || outputOptions.output.length === 0) return "";
|
|
223
|
+
return "\n\n[This stage's final message is saved verbatim as its artifact and is what later stages read. Return the complete result in that message rather than a pointer to it. Files you write yourself are not the artifact.]";
|
|
224
|
+
}
|
|
225
|
+
|
|
125
226
|
export async function finalizePromptOutput(
|
|
126
227
|
fullOutput: string,
|
|
127
228
|
outputOptions: StageOutputOptions,
|
|
128
229
|
runtimeCwd: string,
|
|
230
|
+
runId: string,
|
|
231
|
+
messages?: AgentSession["messages"],
|
|
129
232
|
): Promise<string> {
|
|
130
233
|
const outputPath = resolveOutputPath(outputOptions.output, runtimeCwd, outputOptions.cwd);
|
|
131
234
|
validatePromptOutputOptions(outputOptions);
|
|
132
235
|
const displayOutput = truncateOutput(fullOutput, outputOptions.maxOutput);
|
|
133
236
|
if (outputPath === undefined) return displayOutput;
|
|
134
237
|
|
|
238
|
+
const transcriptFile = transcriptPath(runId, outputPath);
|
|
239
|
+
const transcript = renderTranscript(messages, fullOutput);
|
|
135
240
|
try {
|
|
136
241
|
await mkdir(dirname(outputPath), { recursive: true });
|
|
137
242
|
await writeFile(outputPath, fullOutput, "utf8");
|
|
138
243
|
} catch (err) {
|
|
139
244
|
return `${displayOutput}\n\nOutput file error: ${outputPath}\n${err instanceof Error ? err.message : String(err)}`;
|
|
140
245
|
}
|
|
246
|
+
let transcriptAvailable = false;
|
|
247
|
+
let transcriptError: string | undefined;
|
|
248
|
+
try {
|
|
249
|
+
await ensureWorkflowArtifactRunDirectory(runId);
|
|
250
|
+
await mkdir(dirname(transcriptFile), { recursive: true });
|
|
251
|
+
await writeFile(transcriptFile, transcript, "utf8");
|
|
252
|
+
transcriptAvailable = true;
|
|
253
|
+
} catch (err) {
|
|
254
|
+
transcriptError = err instanceof Error ? err.message : String(err);
|
|
255
|
+
}
|
|
141
256
|
|
|
142
|
-
const
|
|
257
|
+
const warning =
|
|
258
|
+
[
|
|
259
|
+
transcriptError === undefined
|
|
260
|
+
? undefined
|
|
261
|
+
: `WARNING: companion transcript unavailable at ${transcriptFile}: ${transcriptError}`,
|
|
262
|
+
degenerateWarning(fullOutput),
|
|
263
|
+
]
|
|
264
|
+
.filter((line): line is string => line !== undefined)
|
|
265
|
+
.join("\n") || undefined;
|
|
266
|
+
const reference = savedOutputReference(
|
|
267
|
+
outputPath,
|
|
268
|
+
fullOutput,
|
|
269
|
+
transcriptFile,
|
|
270
|
+
transcript,
|
|
271
|
+
transcriptAvailable,
|
|
272
|
+
warning,
|
|
273
|
+
);
|
|
143
274
|
return outputOptions.outputMode === "file-only" ? reference : `${displayOutput}\n\n${reference}`;
|
|
144
275
|
}
|
|
@@ -46,6 +46,8 @@ export interface WorkflowUIAdapter {
|
|
|
46
46
|
}
|
|
47
47
|
export interface WorkflowRunContext<TInputs extends WorkflowInputValues = WorkflowInputValues, TDefinitionBrand extends object = Record<never, never>, TOutputs extends WorkflowOutputValues = WorkflowOutputValues> {
|
|
48
48
|
readonly inputs: Readonly<TInputs>;
|
|
49
|
+
/** Stable owning workflow-run id for durable run-scoped artifacts. */
|
|
50
|
+
readonly runId?: string;
|
|
49
51
|
readonly cwd?: string;
|
|
50
52
|
exit(options?: WorkflowExitOptions<TOutputs>): never;
|
|
51
53
|
stage<TSchemaDef extends TSchema>(name: string, options: StageOptions<TSchemaDef> & {
|
|
@@ -88,6 +88,8 @@ export interface WorkflowRunContext<
|
|
|
88
88
|
TOutputs extends WorkflowOutputValues = WorkflowOutputValues,
|
|
89
89
|
> {
|
|
90
90
|
readonly inputs: Readonly<TInputs>;
|
|
91
|
+
/** Stable owning workflow-run id for durable run-scoped artifacts. */
|
|
92
|
+
readonly runId?: string;
|
|
91
93
|
readonly cwd?: string;
|
|
92
94
|
exit(options?: WorkflowExitOptions<TOutputs>): never;
|
|
93
95
|
stage<TSchemaDef extends TSchema>(
|
|
@@ -255,6 +255,157 @@ export function expandWorkflowGraph(snapshot: StoreSnapshot, rootRunId: string):
|
|
|
255
255
|
};
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
+
function sameIds(left: readonly string[], right: readonly string[]): boolean {
|
|
259
|
+
if (left.length !== right.length) return false;
|
|
260
|
+
for (let index = 0; index < left.length; index++) {
|
|
261
|
+
if (left[index] !== right[index]) return false;
|
|
262
|
+
}
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
interface RefreshRunSources {
|
|
267
|
+
readonly run: RunSnapshot;
|
|
268
|
+
readonly stages: ReadonlyMap<string, StageSnapshot>;
|
|
269
|
+
readonly tools: ReadonlyMap<string, ToolNodeSnapshot>;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function refreshRunSources(snapshot: StoreSnapshot): Map<string, RefreshRunSources> {
|
|
273
|
+
return new Map(
|
|
274
|
+
snapshot.runs.map((run) => [
|
|
275
|
+
run.id,
|
|
276
|
+
{
|
|
277
|
+
run,
|
|
278
|
+
stages: new Map(run.stages.map((stage) => [stage.id, stage])),
|
|
279
|
+
tools: new Map((run.toolNodes ?? []).map((tool) => [tool.id, tool])),
|
|
280
|
+
},
|
|
281
|
+
]),
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function sameExpandedWorkflowTopology(left: StoreSnapshot, right: StoreSnapshot): boolean {
|
|
286
|
+
if (left.runs.length !== right.runs.length) return false;
|
|
287
|
+
for (let runIndex = 0; runIndex < left.runs.length; runIndex++) {
|
|
288
|
+
const a = left.runs[runIndex]!;
|
|
289
|
+
const b = right.runs[runIndex]!;
|
|
290
|
+
if (
|
|
291
|
+
a.id !== b.id ||
|
|
292
|
+
a.parentRunId !== b.parentRunId ||
|
|
293
|
+
a.parentStageId !== b.parentStageId ||
|
|
294
|
+
a.rootRunId !== b.rootRunId ||
|
|
295
|
+
a.stages.length !== b.stages.length ||
|
|
296
|
+
(a.toolNodes?.length ?? 0) !== (b.toolNodes?.length ?? 0)
|
|
297
|
+
)
|
|
298
|
+
return false;
|
|
299
|
+
for (let stageIndex = 0; stageIndex < a.stages.length; stageIndex++) {
|
|
300
|
+
const x = a.stages[stageIndex]!;
|
|
301
|
+
const y = b.stages[stageIndex]!;
|
|
302
|
+
const xChildRunId = authoritativeWorkflowChildRunId(x);
|
|
303
|
+
const yChildRunId = authoritativeWorkflowChildRunId(y);
|
|
304
|
+
if (
|
|
305
|
+
x.id !== y.id ||
|
|
306
|
+
x.executionOrder !== y.executionOrder ||
|
|
307
|
+
x.nodeKind !== y.nodeKind ||
|
|
308
|
+
// The authoritative id becomes undefined at failed/skipped boundaries,
|
|
309
|
+
// so this comparison also catches a terminal-boundary flip.
|
|
310
|
+
xChildRunId !== yChildRunId ||
|
|
311
|
+
!sameIds(x.parentIds, y.parentIds)
|
|
312
|
+
)
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
const aTools = a.toolNodes ?? [];
|
|
316
|
+
const bTools = b.toolNodes ?? [];
|
|
317
|
+
for (let toolIndex = 0; toolIndex < aTools.length; toolIndex++) {
|
|
318
|
+
const x = aTools[toolIndex]!;
|
|
319
|
+
const y = bTools[toolIndex]!;
|
|
320
|
+
if (x.id !== y.id || x.executionOrder !== y.executionOrder || !sameIds(x.parentIds, y.parentIds)) return false;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export function refreshExpandedWorkflowGraph(
|
|
327
|
+
graph: ExpandedWorkflowGraph,
|
|
328
|
+
snapshot: StoreSnapshot,
|
|
329
|
+
): ExpandedWorkflowGraph | undefined {
|
|
330
|
+
const sourcesByRunId = refreshRunSources(snapshot);
|
|
331
|
+
const refreshedStages = new Map<string, ExpandedWorkflowStage>();
|
|
332
|
+
const refreshedTools = new Map<string, ExpandedWorkflowTool>();
|
|
333
|
+
const renderStages: ExpandedWorkflowStage[] = [];
|
|
334
|
+
|
|
335
|
+
for (const cached of graph.renderStages) {
|
|
336
|
+
const target = graph.targets.get(cached.id);
|
|
337
|
+
const sources = target ? sourcesByRunId.get(target.runId) : undefined;
|
|
338
|
+
if (!target || !sources) return undefined;
|
|
339
|
+
if (cached.nodeKind === "tool") {
|
|
340
|
+
const source = sources.tools.get(target.stageId);
|
|
341
|
+
if (!source) return undefined;
|
|
342
|
+
const stage: ExpandedWorkflowStage = {
|
|
343
|
+
id: cached.id,
|
|
344
|
+
name: source.name,
|
|
345
|
+
status: projectedToolStatus(source),
|
|
346
|
+
parentIds: cached.parentIds,
|
|
347
|
+
executionOrder: source.executionOrder,
|
|
348
|
+
nodeKind: "tool",
|
|
349
|
+
toolStatus: source.status,
|
|
350
|
+
startedAt: source.startedAt,
|
|
351
|
+
endedAt: source.endedAt,
|
|
352
|
+
result: source.resultSummary,
|
|
353
|
+
error: source.error,
|
|
354
|
+
toolEvents: [],
|
|
355
|
+
attachable: false,
|
|
356
|
+
workflowGraphTarget: target,
|
|
357
|
+
};
|
|
358
|
+
renderStages.push(stage);
|
|
359
|
+
refreshedStages.set(stage.id, stage);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
const source = sources.stages.get(target.stageId);
|
|
363
|
+
if (!source) return undefined;
|
|
364
|
+
const stage: ExpandedWorkflowStage = {
|
|
365
|
+
...source,
|
|
366
|
+
id: cached.id,
|
|
367
|
+
parentIds: cached.parentIds,
|
|
368
|
+
workflowGraphTarget: target,
|
|
369
|
+
};
|
|
370
|
+
renderStages.push(stage);
|
|
371
|
+
refreshedStages.set(stage.id, stage);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const stages = renderStages.filter((stage) => stage.nodeKind !== "tool");
|
|
375
|
+
const tools: ExpandedWorkflowTool[] = [];
|
|
376
|
+
|
|
377
|
+
for (const cached of graph.tools) {
|
|
378
|
+
const target = graph.targets.get(cached.id);
|
|
379
|
+
const sources = target ? sourcesByRunId.get(target.runId) : undefined;
|
|
380
|
+
const source = target ? sources?.tools.get(target.stageId) : undefined;
|
|
381
|
+
if (!target || !sources || !source) return undefined;
|
|
382
|
+
const tool: ExpandedWorkflowTool = {
|
|
383
|
+
...source,
|
|
384
|
+
id: cached.id,
|
|
385
|
+
parentIds: cached.parentIds,
|
|
386
|
+
runId: sources.run.id,
|
|
387
|
+
runName: sources.run.name,
|
|
388
|
+
depth: target.depth,
|
|
389
|
+
};
|
|
390
|
+
tools.push(tool);
|
|
391
|
+
refreshedTools.set(tool.id, tool);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const nodes: ExpandedWorkflowNode[] = [];
|
|
395
|
+
for (const node of graph.nodes) {
|
|
396
|
+
if (node.kind === "stage") {
|
|
397
|
+
const stage = refreshedStages.get(node.stage.id);
|
|
398
|
+
if (!stage) return undefined;
|
|
399
|
+
nodes.push({ kind: "stage", stage });
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
const tool = refreshedTools.get(node.tool.id);
|
|
403
|
+
if (!tool) return undefined;
|
|
404
|
+
nodes.push({ kind: "tool", tool });
|
|
405
|
+
}
|
|
406
|
+
return { stages, tools, nodes, renderStages, targets: graph.targets };
|
|
407
|
+
}
|
|
408
|
+
|
|
258
409
|
export function expandedStageTarget(
|
|
259
410
|
graph: ExpandedWorkflowGraph,
|
|
260
411
|
virtualStageIdValue: string,
|
|
@@ -262,24 +413,22 @@ export function expandedStageTarget(
|
|
|
262
413
|
return graph.targets.get(virtualStageIdValue);
|
|
263
414
|
}
|
|
264
415
|
|
|
416
|
+
/**
|
|
417
|
+
* Exact-match only. A stage id is never truncated to address a stage: at the
|
|
418
|
+
* root that id is a bare UUID, nested it is the `runId:nodeId` composite built
|
|
419
|
+
* by `virtualNodeId`, and a tool node carries `tool:<argsHash>`. Names match
|
|
420
|
+
* whole, so `build` never selects `build-check`.
|
|
421
|
+
*/
|
|
265
422
|
export function stageMatchesExpandedIdentifier(stage: ExpandedWorkflowStage, target: string): boolean {
|
|
266
423
|
const graphTarget = stage.workflowGraphTarget;
|
|
267
424
|
return (
|
|
268
|
-
stage.id === target ||
|
|
269
|
-
stage.name === target ||
|
|
270
|
-
stage.id.startsWith(target) ||
|
|
271
|
-
graphTarget.stageId === target ||
|
|
272
|
-
graphTarget.stageId.startsWith(target) ||
|
|
273
|
-
graphTarget.runId === target ||
|
|
274
|
-
graphTarget.runId.startsWith(target)
|
|
425
|
+
stage.id === target || stage.name === target || graphTarget.stageId === target || graphTarget.runId === target
|
|
275
426
|
);
|
|
276
427
|
}
|
|
277
428
|
|
|
278
429
|
export function expandedStageLabel(stage: ExpandedWorkflowStage): string {
|
|
279
430
|
const target = stage.workflowGraphTarget;
|
|
280
431
|
if (stage.nodeKind === "tool") return `${stage.name} (tool)`;
|
|
281
|
-
const runPrefix = target.runId.slice(0, 8);
|
|
282
|
-
const stagePrefix = target.stageId.slice(0, 8);
|
|
283
432
|
const depthPrefix = target.depth > 0 ? `${childAliasFor(stage) ?? target.runName}:` : "";
|
|
284
|
-
return `${depthPrefix}${stage.name} (${
|
|
433
|
+
return `${depthPrefix}${stage.name} (${target.runId}/${target.stageId})`;
|
|
285
434
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Truncation that actually releases the untruncated source.
|
|
3
|
+
*
|
|
4
|
+
* V8 does not copy characters for `String.prototype.slice`. For a flat parent of
|
|
5
|
+
* 13 characters or more it allocates a **SlicedString**, which is a length, an
|
|
6
|
+
* offset, and a pointer to the parent. Template interpolation then wraps that in
|
|
7
|
+
* a **ConsString**, which keeps the same pointer. So the obvious way to bound a
|
|
8
|
+
* value for storage:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* `${JSON.stringify(value).slice(0, 237)}...`
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* produces a 240-character string that reports `length === 240` while holding
|
|
15
|
+
* the entire serialized payload alive. The bound is on what you can read, not on
|
|
16
|
+
* what you retain. An 8 MiB tool result summarized to 240 characters still costs
|
|
17
|
+
* 8 MiB for as long as anything keeps the summary.
|
|
18
|
+
*
|
|
19
|
+
* That is invisible in every ordinary test: the value is correct, its length is
|
|
20
|
+
* correct, and equality holds. It only shows up as heap that never comes back.
|
|
21
|
+
*
|
|
22
|
+
* `split("").join("")` copies the code units into a fresh flat string with no
|
|
23
|
+
* parent pointer. It is code-unit exact, including lone surrogates, because
|
|
24
|
+
* `split("")` splits on UTF-16 code units rather than code points.
|
|
25
|
+
*
|
|
26
|
+
* Use this at any truncation whose result outlives the value it came from —
|
|
27
|
+
* memoized projections, durable checkpoints, catalogs, persisted summaries.
|
|
28
|
+
* A truncation that is consumed and dropped within the same turn does not need
|
|
29
|
+
* it, and paying for a copy there is waste.
|
|
30
|
+
*/
|
|
31
|
+
export function flattenTruncatedString(value: string): string {
|
|
32
|
+
return value.split("").join("");
|
|
33
|
+
}
|