@bastani/atomic 0.8.21-0 → 0.8.22-0
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 +46 -9
- package/dist/builtin/intercom/broker/broker.ts +3 -3
- package/dist/builtin/intercom/config.ts +3 -3
- package/dist/builtin/intercom/index.ts +1 -1
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/ui/compose.ts +2 -2
- package/dist/builtin/mcp/host-html-template.ts +0 -3
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +13 -4
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +9 -9
- package/dist/builtin/subagents/agents/debugger.md +6 -6
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +1 -1
- package/dist/builtin/subagents/skills/browser-use/SKILL.md +234 -0
- package/dist/builtin/subagents/skills/browser-use/references/cdp-python.md +76 -0
- package/dist/builtin/subagents/skills/browser-use/references/multi-session.md +92 -0
- package/dist/builtin/subagents/skills/subagent/SKILL.md +4 -4
- package/dist/builtin/subagents/src/agents/skills.ts +19 -1
- package/dist/builtin/subagents/src/extension/index.ts +24 -22
- package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +7 -1
- package/dist/builtin/subagents/src/runs/background/async-execution.ts +23 -7
- package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +98 -3
- package/dist/builtin/subagents/src/runs/background/async-status.ts +3 -1
- package/dist/builtin/subagents/src/runs/background/run-status.ts +1 -1
- package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +3 -0
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +37 -12
- package/dist/builtin/subagents/src/runs/foreground/chain-clarify.ts +15 -15
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +26 -2
- package/dist/builtin/subagents/src/runs/shared/nested-render.ts +1 -1
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +7 -0
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +28 -1
- package/dist/builtin/subagents/src/shared/fast-mode.ts +80 -0
- package/dist/builtin/subagents/src/shared/formatters.ts +4 -2
- package/dist/builtin/subagents/src/shared/types.ts +4 -2
- package/dist/builtin/subagents/src/shared/utils.ts +3 -61
- package/dist/builtin/subagents/src/tui/render.ts +303 -157
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +101 -35
- package/dist/builtin/workflows/README.md +228 -41
- package/dist/builtin/workflows/builtin/deep-research-codebase.ts +535 -541
- package/dist/builtin/workflows/builtin/goal.ts +39 -25
- package/dist/builtin/workflows/builtin/open-claude-design.ts +66 -69
- package/dist/builtin/workflows/builtin/ralph.ts +21 -21
- package/dist/builtin/workflows/package.json +6 -5
- package/dist/builtin/workflows/skills/research-codebase/SKILL.md +1 -1
- package/dist/builtin/workflows/src/extension/background-ui-adapter.ts +2 -2
- package/dist/builtin/workflows/src/extension/discovery.ts +25 -146
- package/dist/builtin/workflows/src/extension/dispatcher.ts +72 -24
- package/dist/builtin/workflows/src/extension/hil-answer-notifications.ts +363 -0
- package/dist/builtin/workflows/src/extension/index.ts +690 -352
- package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +99 -62
- package/dist/builtin/workflows/src/extension/render-call.ts +2 -1
- package/dist/builtin/workflows/src/extension/render-result.ts +9 -3
- package/dist/builtin/workflows/src/extension/renderers.ts +5 -3
- package/dist/builtin/workflows/src/extension/runtime.ts +68 -33
- package/dist/builtin/workflows/src/extension/status-writer.ts +1 -1
- package/dist/builtin/workflows/src/extension/wiring.ts +34 -13
- package/dist/builtin/workflows/src/extension/workflow-module-loader.ts +142 -0
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +4 -4
- package/dist/builtin/workflows/src/index.ts +2 -0
- package/dist/builtin/workflows/src/intercom/result-intercom.ts +1 -1
- package/dist/builtin/workflows/src/runs/background/runner.ts +6 -4
- package/dist/builtin/workflows/src/runs/background/status.ts +45 -21
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +624 -52
- package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +1 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +80 -24
- package/dist/builtin/workflows/src/runs/shared/validate-inputs.ts +61 -24
- package/dist/builtin/workflows/src/runs/shared/workflow-runner.ts +32 -10
- package/dist/builtin/workflows/src/sdk-surface.ts +6 -0
- package/dist/builtin/workflows/src/shared/expanded-workflow-graph.ts +178 -0
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +92 -12
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +21 -3
- package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +1 -2
- package/dist/builtin/workflows/src/shared/run-visibility.ts +9 -0
- package/dist/builtin/workflows/src/shared/schema-introspection.ts +121 -0
- package/dist/builtin/workflows/src/shared/serializable.ts +132 -0
- package/dist/builtin/workflows/src/shared/stage-ui-broker.ts +91 -9
- package/dist/builtin/workflows/src/shared/store-types.ts +31 -3
- package/dist/builtin/workflows/src/shared/store.ts +58 -14
- package/dist/builtin/workflows/src/shared/types.ts +105 -40
- package/dist/builtin/workflows/src/tui/chat-surface-message.ts +129 -13
- package/dist/builtin/workflows/src/tui/chat-surface.ts +6 -1
- package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +3 -2
- package/dist/builtin/workflows/src/tui/graph-canvas.ts +1 -1
- package/dist/builtin/workflows/src/tui/graph-view.ts +91 -65
- package/dist/builtin/workflows/src/tui/inline-form-card.ts +1 -1
- package/dist/builtin/workflows/src/tui/inline-form-overlay.ts +3 -2
- package/dist/builtin/workflows/src/tui/inputs-overlay.ts +3 -2
- package/dist/builtin/workflows/src/tui/inputs-picker.ts +8 -7
- package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +2 -0
- package/dist/builtin/workflows/src/tui/node-card.ts +34 -8
- package/dist/builtin/workflows/src/tui/overlay-adapter.ts +4 -11
- package/dist/builtin/workflows/src/tui/prompt-card.ts +98 -50
- package/dist/builtin/workflows/src/tui/session-list.ts +7 -2
- package/dist/builtin/workflows/src/tui/session-picker.ts +2 -0
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +226 -55
- package/dist/builtin/workflows/src/tui/status-helpers.ts +2 -0
- package/dist/builtin/workflows/src/tui/store-widget-installer.ts +37 -158
- package/dist/builtin/workflows/src/tui/toast.ts +2 -2
- package/dist/builtin/workflows/src/tui/widget.ts +53 -12
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +270 -19
- package/dist/builtin/workflows/src/tui/workflow-notice-card.ts +184 -0
- package/dist/builtin/workflows/src/workflows/define-workflow.ts +138 -43
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +45 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +27 -9
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +196 -17
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +2 -2
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/codex-fast-mode.d.ts +36 -0
- package/dist/core/codex-fast-mode.d.ts.map +1 -0
- package/dist/core/codex-fast-mode.js +117 -0
- package/dist/core/codex-fast-mode.js.map +1 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +1 -1
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +1 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/extensions/index.d.ts +4 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -0
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +7 -2
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +23 -8
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/reactive-widget.d.ts +58 -0
- package/dist/core/extensions/reactive-widget.d.ts.map +1 -0
- package/dist/core/extensions/reactive-widget.js +182 -0
- package/dist/core/extensions/reactive-widget.js.map +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +1 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +26 -12
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/messages.d.ts +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +8 -2
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-registry.d.ts +4 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +11 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/resource-loader.d.ts +9 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +49 -21
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +22 -13
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +7 -5
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +5 -3
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +16 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +64 -5
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +7 -4
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/ask-user-question.js +2 -2
- package/dist/core/tools/ask-user-question/ask-user-question.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +12 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -1
- package/dist/modes/interactive/chat-input-actions.js.map +1 -1
- package/dist/modes/interactive/components/diff.d.ts.map +1 -1
- package/dist/modes/interactive/components/diff.js +0 -1
- package/dist/modes/interactive/components/diff.js.map +1 -1
- package/dist/modes/interactive/components/fast-mode-selector.d.ts +27 -0
- package/dist/modes/interactive/components/fast-mode-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/fast-mode-selector.js +105 -0
- package/dist/modes/interactive/components/fast-mode-selector.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +7 -12
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +132 -30
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +53 -6
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +3 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/docs/compaction.md +1 -1
- package/docs/custom-provider.md +2 -2
- package/docs/development.md +2 -2
- package/docs/docs.json +2 -2
- package/docs/extensions.md +18 -13
- package/docs/providers.md +5 -1
- package/docs/quickstart.md +5 -3
- package/docs/rpc.md +5 -5
- package/docs/sdk.md +12 -12
- package/docs/settings.md +18 -0
- package/docs/themes.md +6 -6
- package/docs/tui.md +20 -18
- package/docs/usage.md +2 -0
- package/docs/workflows.md +403 -39
- package/examples/extensions/qna.ts +2 -2
- package/package.json +4 -4
- package/dist/builtin/subagents/skills/playwright-cli/SKILL.md +0 -392
- package/dist/builtin/subagents/skills/playwright-cli/references/element-attributes.md +0 -23
- package/dist/builtin/subagents/skills/playwright-cli/references/playwright-tests.md +0 -39
- package/dist/builtin/subagents/skills/playwright-cli/references/request-mocking.md +0 -87
- package/dist/builtin/subagents/skills/playwright-cli/references/running-code.md +0 -241
- package/dist/builtin/subagents/skills/playwright-cli/references/session-management.md +0 -225
- package/dist/builtin/subagents/skills/playwright-cli/references/spec-driven-testing.md +0 -305
- package/dist/builtin/subagents/skills/playwright-cli/references/storage-state.md +0 -275
- package/dist/builtin/subagents/skills/playwright-cli/references/test-generation.md +0 -134
- package/dist/builtin/subagents/skills/playwright-cli/references/tracing.md +0 -139
- package/dist/builtin/subagents/skills/playwright-cli/references/video-recording.md +0 -143
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - No decorative progress bar. Counts live in the header pills.
|
|
14
14
|
*
|
|
15
15
|
* cross-ref:
|
|
16
|
-
* - github.com/
|
|
16
|
+
* - github.com/bastani-inc/atomic packages/atomic-sdk/src/components/session-graph-panel.tsx
|
|
17
17
|
* - DESIGN.md §4 (Elevation), §5 (Components)
|
|
18
18
|
*/
|
|
19
19
|
import type { Component } from "@earendil-works/pi-tui";
|
|
@@ -31,17 +31,21 @@ import type {
|
|
|
31
31
|
StoreSnapshot,
|
|
32
32
|
RunSnapshot,
|
|
33
33
|
} from "../shared/store-types.js";
|
|
34
|
-
import { elapsedStageMs } from "../shared/timing.js";
|
|
35
34
|
import type { GraphTheme } from "./graph-theme.js";
|
|
36
35
|
import type { SwitcherState } from "./switcher.js";
|
|
37
36
|
import type { LayoutNode } from "./layout.js";
|
|
37
|
+
import {
|
|
38
|
+
expandWorkflowGraph,
|
|
39
|
+
expandedStageTarget,
|
|
40
|
+
type ExpandedWorkflowGraph,
|
|
41
|
+
type ExpandedWorkflowStage,
|
|
42
|
+
} from "../shared/expanded-workflow-graph.js";
|
|
38
43
|
import { computeLayout, NODE_W, NODE_H } from "./layout.js";
|
|
39
44
|
import { renderHeader, renderOutlinePill } from "./header.js";
|
|
40
45
|
import { renderNodeCard } from "./node-card.js";
|
|
41
46
|
import { renderSwitcher, filterStages } from "./switcher.js";
|
|
42
47
|
import { renderToasts, createToastManager } from "./toast.js";
|
|
43
48
|
import { hexToAnsi, hexBg, RESET, BOLD } from "./color-utils.js";
|
|
44
|
-
import { fmtDuration } from "./status-helpers.js";
|
|
45
49
|
import { GraphCanvas } from "./graph-canvas.js";
|
|
46
50
|
import {
|
|
47
51
|
createPromptCardState,
|
|
@@ -50,6 +54,7 @@ import {
|
|
|
50
54
|
renderPromptCard,
|
|
51
55
|
type PromptCardState,
|
|
52
56
|
} from "./prompt-card.js";
|
|
57
|
+
import { isKeybindingsLike, type KeybindingsLike } from "./keybindings-adapter.js";
|
|
53
58
|
|
|
54
59
|
export type GraphViewMode = "overlay" | "widget";
|
|
55
60
|
|
|
@@ -115,6 +120,8 @@ export interface GraphViewOpts {
|
|
|
115
120
|
* `overlay-adapter.ts`).
|
|
116
121
|
*/
|
|
117
122
|
requestRender?: () => void;
|
|
123
|
+
/** Host Pi keybindings manager used by run-level prompt cards. */
|
|
124
|
+
piKeybindings?: unknown;
|
|
118
125
|
}
|
|
119
126
|
|
|
120
127
|
const HINT_KEYS: Array<{ key: string; label: string }> = [
|
|
@@ -177,6 +184,7 @@ export class GraphView implements Component {
|
|
|
177
184
|
private initialFocusedStageId?: string;
|
|
178
185
|
private getViewportRows?: () => number | undefined;
|
|
179
186
|
private requestRender?: () => void;
|
|
187
|
+
private piKeybindings?: unknown;
|
|
180
188
|
|
|
181
189
|
/** Active HIL prompt state, set when `_rebuildLayout` sees a new prompt id. */
|
|
182
190
|
private promptState: PromptCardState | null = null;
|
|
@@ -187,6 +195,7 @@ export class GraphView implements Component {
|
|
|
187
195
|
private toastManager = createToastManager();
|
|
188
196
|
private detailsExpanded = true;
|
|
189
197
|
private cachedLayout: LayoutNode[] = [];
|
|
198
|
+
private expandedGraph: ExpandedWorkflowGraph = { stages: [], targets: new Map() };
|
|
190
199
|
private currentSnapshot: StoreSnapshot | null = null;
|
|
191
200
|
private graphScrollOffset = 0;
|
|
192
201
|
private graphScrollColOffset = 0;
|
|
@@ -211,6 +220,7 @@ export class GraphView implements Component {
|
|
|
211
220
|
this.initialFocusedStageId = opts.initialFocusedStageId;
|
|
212
221
|
this.getViewportRows = opts.getViewportRows;
|
|
213
222
|
this.requestRender = opts.requestRender;
|
|
223
|
+
this.piKeybindings = opts.piKeybindings;
|
|
214
224
|
|
|
215
225
|
this._unsubscribe = this.store.subscribe((snap) => {
|
|
216
226
|
this.currentSnapshot = snap;
|
|
@@ -242,6 +252,7 @@ export class GraphView implements Component {
|
|
|
242
252
|
const run = this._getCurrentRun();
|
|
243
253
|
if (!run) {
|
|
244
254
|
this.cachedLayout = [];
|
|
255
|
+
this.expandedGraph = { stages: [], targets: new Map() };
|
|
245
256
|
this.focusedIndex = 0;
|
|
246
257
|
this.graphScrollOffset = 0;
|
|
247
258
|
this.graphScrollColOffset = 0;
|
|
@@ -262,7 +273,9 @@ export class GraphView implements Component {
|
|
|
262
273
|
// the same node the user just attached to.
|
|
263
274
|
if (this.initialFocusedStageId !== undefined) {
|
|
264
275
|
const idx = this.cachedLayout.findIndex(
|
|
265
|
-
(n) =>
|
|
276
|
+
(n) =>
|
|
277
|
+
n.stage.id === this.initialFocusedStageId ||
|
|
278
|
+
expandedStageTarget(this.expandedGraph, n.stage.id)?.stageId === this.initialFocusedStageId,
|
|
266
279
|
);
|
|
267
280
|
if (idx >= 0 && idx !== this.focusedIndex) {
|
|
268
281
|
this.focusedIndex = idx;
|
|
@@ -316,31 +329,37 @@ export class GraphView implements Component {
|
|
|
316
329
|
}
|
|
317
330
|
|
|
318
331
|
private _awaitingInputKey(stage: StageSnapshot): { key: string; createdAt: number } | null {
|
|
332
|
+
const target = expandedStageTarget(this.expandedGraph, stage.id);
|
|
333
|
+
const prefix = target ? `${target.runId}:${target.stageId}` : stage.id;
|
|
319
334
|
if (stage.pendingPrompt) {
|
|
320
335
|
return {
|
|
321
|
-
key: `prompt:${
|
|
336
|
+
key: `prompt:${prefix}:${stage.pendingPrompt.id}`,
|
|
322
337
|
createdAt: stage.pendingPrompt.createdAt,
|
|
323
338
|
};
|
|
324
339
|
}
|
|
325
340
|
if (stage.inputRequest) {
|
|
326
341
|
return {
|
|
327
|
-
key: `input-request:${
|
|
342
|
+
key: `input-request:${prefix}:${stage.inputRequest.id}`,
|
|
328
343
|
createdAt: stage.inputRequest.createdAt,
|
|
329
344
|
};
|
|
330
345
|
}
|
|
331
346
|
if (stage.status === "awaiting_input") {
|
|
332
347
|
return {
|
|
333
|
-
key: `awaiting:${
|
|
348
|
+
key: `awaiting:${prefix}:${stage.awaitingInputSince ?? "active"}`,
|
|
334
349
|
createdAt: stage.awaitingInputSince ?? stage.startedAt ?? 0,
|
|
335
350
|
};
|
|
336
351
|
}
|
|
337
352
|
return null;
|
|
338
353
|
}
|
|
339
354
|
|
|
340
|
-
private _graphStages(run: RunSnapshot):
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
355
|
+
private _graphStages(run: RunSnapshot): ExpandedWorkflowStage[] {
|
|
356
|
+
this.expandedGraph = this.currentSnapshot
|
|
357
|
+
? expandWorkflowGraph(this.currentSnapshot, run.id)
|
|
358
|
+
: { stages: [], targets: new Map() };
|
|
359
|
+
const stages = [...this.expandedGraph.stages];
|
|
360
|
+
const hasStagePrompt = stages.some((stage) => stage.pendingPrompt !== undefined);
|
|
361
|
+
if (!hasStagePrompt) return stages;
|
|
362
|
+
return stages.filter((stage) => {
|
|
344
363
|
// Prompt-node injection can leave unstarted author stages in the store
|
|
345
364
|
// while the prompt node owns focus; hide only these inert placeholders.
|
|
346
365
|
const isUnstartedPlaceholder =
|
|
@@ -394,11 +413,12 @@ export class GraphView implements Component {
|
|
|
394
413
|
if (!run) {
|
|
395
414
|
return [`${hexToAnsi(this.graphTheme.dim)}no active workflow${RESET}`];
|
|
396
415
|
}
|
|
397
|
-
const
|
|
398
|
-
const
|
|
416
|
+
const displayStages = this._displayStages(run);
|
|
417
|
+
const headerLines = renderHeader({ ...run, stages: displayStages }, { width, theme: this.graphTheme });
|
|
418
|
+
const counts = this._counts(displayStages);
|
|
399
419
|
const trailer =
|
|
400
420
|
`${hexToAnsi(this.graphTheme.dim)}` +
|
|
401
|
-
`${counts.completed}/${
|
|
421
|
+
`${counts.completed}/${displayStages.length} done` +
|
|
402
422
|
(counts.running > 0 ? ` · ${counts.running} running` : "") +
|
|
403
423
|
(counts.failed > 0 ? ` · ${counts.failed} failed` : "") +
|
|
404
424
|
(counts.blocked > 0 ? ` · ${counts.blocked} blocked` : "") +
|
|
@@ -473,7 +493,7 @@ export class GraphView implements Component {
|
|
|
473
493
|
|
|
474
494
|
// 1. Header chrome (3 rows: outline pill + session name + counts).
|
|
475
495
|
lines.push(
|
|
476
|
-
...renderHeader(run, { width: frameWidth, theme: this.graphTheme }),
|
|
496
|
+
...renderHeader({ ...run, stages: this._displayStages(run) }, { width: frameWidth, theme: this.graphTheme }),
|
|
477
497
|
);
|
|
478
498
|
|
|
479
499
|
// 2. Graph occupies the full body. No section labels, no focused-
|
|
@@ -502,7 +522,7 @@ export class GraphView implements Component {
|
|
|
502
522
|
lines[row] = this._blankRow(frameWidth);
|
|
503
523
|
}
|
|
504
524
|
const switcherWidth = Math.min(60, Math.max(40, frameWidth - 8));
|
|
505
|
-
const switcherLines = renderSwitcher(run
|
|
525
|
+
const switcherLines = renderSwitcher(this._displayStages(run), this.switcherState, {
|
|
506
526
|
width: switcherWidth,
|
|
507
527
|
theme: this.graphTheme,
|
|
508
528
|
});
|
|
@@ -520,8 +540,9 @@ export class GraphView implements Component {
|
|
|
520
540
|
|
|
521
541
|
// 4. Pending HIL prompt — floats over the graph body, centred. The
|
|
522
542
|
// chat editor remains free regardless: the overlay is the only
|
|
523
|
-
// surface that interacts with the prompt.
|
|
524
|
-
|
|
543
|
+
// surface that interacts with the prompt. When the stage switcher is
|
|
544
|
+
// open it owns the body/input, so hide the prompt card until it closes.
|
|
545
|
+
if (this.promptState && !this.switcherOpen) {
|
|
525
546
|
const cardWidth = Math.min(72, Math.max(40, frameWidth - 6));
|
|
526
547
|
const cardLines = renderPromptCard({
|
|
527
548
|
state: this.promptState,
|
|
@@ -677,7 +698,7 @@ export class GraphView implements Component {
|
|
|
677
698
|
focused,
|
|
678
699
|
pulsePhase,
|
|
679
700
|
theme: this.graphTheme,
|
|
680
|
-
stages:
|
|
701
|
+
stages: this.cachedLayout.map((layoutNode) => layoutNode.stage),
|
|
681
702
|
});
|
|
682
703
|
for (let li = 0; li < cardLines.length; li++) {
|
|
683
704
|
const rowIdx = node.y + li;
|
|
@@ -796,7 +817,7 @@ export class GraphView implements Component {
|
|
|
796
817
|
this._clampGraphScroll(totalRows, bodyRows);
|
|
797
818
|
}
|
|
798
819
|
|
|
799
|
-
private _focusedGraphRowRange(
|
|
820
|
+
private _focusedGraphRowRange(_frameWidth: number): { start: number; end: number } | null {
|
|
800
821
|
const node = this.cachedLayout[this.focusedIndex];
|
|
801
822
|
if (!node) return null;
|
|
802
823
|
return { start: node.y, end: node.y + NODE_H - 1 };
|
|
@@ -1014,37 +1035,13 @@ export class GraphView implements Component {
|
|
|
1014
1035
|
return `${bg}${" ".repeat(leftPad)}${RESET}${cardLine}${bg}${" ".repeat(rightPadLen)}${RESET}`;
|
|
1015
1036
|
}
|
|
1016
1037
|
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
base: string,
|
|
1022
|
-
overlay: string,
|
|
1023
|
-
leftPad: number,
|
|
1024
|
-
totalWidth: number,
|
|
1025
|
-
): string {
|
|
1026
|
-
const overlayWidth = Math.min(
|
|
1027
|
-
Math.max(0, totalWidth - leftPad),
|
|
1028
|
-
visibleWidth(overlay),
|
|
1029
|
-
);
|
|
1030
|
-
const left = this._sliceColumns(base, 0, leftPad);
|
|
1031
|
-
const panel = truncateToWidth(overlay, overlayWidth, "", true);
|
|
1032
|
-
const right = this._sliceColumns(
|
|
1033
|
-
base,
|
|
1034
|
-
leftPad + overlayWidth,
|
|
1035
|
-
totalWidth,
|
|
1036
|
-
);
|
|
1037
|
-
const merged = `${left}${panel}${right}`;
|
|
1038
|
-
const pad = Math.max(0, totalWidth - visibleWidth(merged));
|
|
1039
|
-
return `${merged}${hexBg(this.graphTheme.bg)}${" ".repeat(pad)}${RESET}`;
|
|
1038
|
+
private _displayStages(run: RunSnapshot): StageSnapshot[] {
|
|
1039
|
+
return this.cachedLayout.length > 0
|
|
1040
|
+
? this.cachedLayout.map((layoutNode) => layoutNode.stage)
|
|
1041
|
+
: [...run.stages];
|
|
1040
1042
|
}
|
|
1041
1043
|
|
|
1042
|
-
private
|
|
1043
|
-
const elapsed = elapsedStageMs(stage);
|
|
1044
|
-
return elapsed === undefined ? "" : fmtDuration(elapsed);
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
private _counts(run: RunSnapshot): {
|
|
1044
|
+
private _counts(stages: readonly StageSnapshot[]): {
|
|
1048
1045
|
pending: number;
|
|
1049
1046
|
running: number;
|
|
1050
1047
|
awaiting_input: number;
|
|
@@ -1064,7 +1061,7 @@ export class GraphView implements Component {
|
|
|
1064
1061
|
failed: 0,
|
|
1065
1062
|
skipped: 0,
|
|
1066
1063
|
};
|
|
1067
|
-
for (const s of
|
|
1064
|
+
for (const s of stages) c[s.status]++;
|
|
1068
1065
|
return c;
|
|
1069
1066
|
}
|
|
1070
1067
|
|
|
@@ -1074,25 +1071,40 @@ export class GraphView implements Component {
|
|
|
1074
1071
|
|
|
1075
1072
|
/** Returns true if consumed. */
|
|
1076
1073
|
handleInput(data: string): boolean {
|
|
1077
|
-
// Pending HIL prompt owns input — once a prompt is active, every key
|
|
1078
|
-
// routes to it until the user submits or skips. This is what keeps
|
|
1079
|
-
// the chat editor free: the workflow author called ctx.ui.editor()
|
|
1080
|
-
// long ago in a background promise; only the overlay handles the
|
|
1081
|
-
// response. The graph nav resumes after `_resolvePrompt` clears
|
|
1082
|
-
// `promptState` (mirrored from the store via `_syncPromptState`).
|
|
1083
|
-
if (this.promptState) {
|
|
1084
|
-
return this._handlePromptInput(data);
|
|
1085
|
-
}
|
|
1086
1074
|
if (this.switcherOpen) {
|
|
1087
1075
|
return this._handleSwitcherInput(data);
|
|
1088
1076
|
}
|
|
1077
|
+
// Stage-local HIL is represented by graph nodes and remains graph-first;
|
|
1078
|
+
// only the legacy run-level prompt card sets `promptState`. Keep that
|
|
1079
|
+
// fallback answerable, but let a narrow set of non-text graph controls
|
|
1080
|
+
// through first so the workflow overlay can still be detached or scrolled
|
|
1081
|
+
// instead of feeling modal while a prompt is visible. Printable keys such
|
|
1082
|
+
// as "/" belong to the prompt card while legacy run-level text/editor
|
|
1083
|
+
// prompts own input.
|
|
1084
|
+
if (this.promptState) {
|
|
1085
|
+
if (this._isNonTextGraphControlBeforePrompt(data)) {
|
|
1086
|
+
return this._handleGraphInput(data);
|
|
1087
|
+
}
|
|
1088
|
+
return this._handlePromptInput(data);
|
|
1089
|
+
}
|
|
1089
1090
|
return this._handleGraphInput(data);
|
|
1090
1091
|
}
|
|
1091
1092
|
|
|
1093
|
+
private _promptKeybindings(): KeybindingsLike | undefined {
|
|
1094
|
+
return isKeybindingsLike(this.piKeybindings) ? this.piKeybindings : undefined;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
private _isNonTextGraphControlBeforePrompt(data: string): boolean {
|
|
1098
|
+
return (
|
|
1099
|
+
this._mouseWheelDeltaRows(data) !== 0 ||
|
|
1100
|
+
matchesKey(data, Key.ctrl("d"))
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1092
1104
|
private _handlePromptInput(data: string): boolean {
|
|
1093
1105
|
const state = this.promptState;
|
|
1094
1106
|
if (!state) return false;
|
|
1095
|
-
const action = handlePromptCardInput(data, state);
|
|
1107
|
+
const action = handlePromptCardInput(data, state, this._promptKeybindings());
|
|
1096
1108
|
if (action.kind === "noop") return true;
|
|
1097
1109
|
const runId = this.runId;
|
|
1098
1110
|
if (!runId) return true;
|
|
@@ -1181,8 +1193,12 @@ export class GraphView implements Component {
|
|
|
1181
1193
|
// the overlay's setHidden() flag (not unmount); Escape/Ctrl+C closes.
|
|
1182
1194
|
if (matchesKey(data, "q")) {
|
|
1183
1195
|
const run = this._getCurrentRun();
|
|
1184
|
-
|
|
1185
|
-
|
|
1196
|
+
const targetRunId = this._focusedStageTarget()?.runId ?? run?.id;
|
|
1197
|
+
const targetRun = targetRunId !== undefined
|
|
1198
|
+
? this.currentSnapshot?.runs.find((candidate) => candidate.id === targetRunId)
|
|
1199
|
+
: undefined;
|
|
1200
|
+
if (targetRun && targetRun.endedAt === undefined && this.onKill) {
|
|
1201
|
+
this.onKill(targetRun.id);
|
|
1186
1202
|
}
|
|
1187
1203
|
this.onClose?.();
|
|
1188
1204
|
return true;
|
|
@@ -1199,8 +1215,7 @@ export class GraphView implements Component {
|
|
|
1199
1215
|
}
|
|
1200
1216
|
|
|
1201
1217
|
private _handleSwitcherInput(data: string): boolean {
|
|
1202
|
-
const
|
|
1203
|
-
const stages = run?.stages ?? [];
|
|
1218
|
+
const stages = this.cachedLayout.map((layoutNode) => layoutNode.stage);
|
|
1204
1219
|
|
|
1205
1220
|
if (matchesKey(data, Key.escape)) {
|
|
1206
1221
|
this.switcherOpen = false;
|
|
@@ -1326,10 +1341,21 @@ export class GraphView implements Component {
|
|
|
1326
1341
|
const node = this.cachedLayout[this.focusedIndex];
|
|
1327
1342
|
const run = this._getCurrentRun();
|
|
1328
1343
|
if (!node || !run) return false;
|
|
1329
|
-
this.
|
|
1344
|
+
const target = expandedStageTarget(this.expandedGraph, node.stage.id) ?? {
|
|
1345
|
+
runId: run.id,
|
|
1346
|
+
stageId: node.stage.id,
|
|
1347
|
+
};
|
|
1348
|
+
this.onStageAttach(target.runId, target.stageId);
|
|
1330
1349
|
return true;
|
|
1331
1350
|
}
|
|
1332
1351
|
|
|
1352
|
+
private _focusedStageTarget(): { runId: string; stageId: string } | undefined {
|
|
1353
|
+
const node = this.cachedLayout[this.focusedIndex];
|
|
1354
|
+
if (!node) return undefined;
|
|
1355
|
+
const target = expandedStageTarget(this.expandedGraph, node.stage.id);
|
|
1356
|
+
return target ? { runId: target.runId, stageId: target.stageId } : undefined;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1333
1359
|
private _setFocusedIndex(index: number): void {
|
|
1334
1360
|
const max = Math.max(0, this.cachedLayout.length - 1);
|
|
1335
1361
|
const next = Math.max(0, Math.min(index, max));
|
|
@@ -30,6 +30,7 @@ import type {
|
|
|
30
30
|
PiEditorFactory,
|
|
31
31
|
} from "../extension/wiring.js";
|
|
32
32
|
import type { WorkflowInputEntry } from "../extension/render-result.js";
|
|
33
|
+
import type { WorkflowInputValues } from "../shared/types.js";
|
|
33
34
|
import type { GraphTheme } from "./graph-theme.js";
|
|
34
35
|
import { renderInlineCard } from "./inline-form-card.js";
|
|
35
36
|
import { InlineFormEditor } from "./inline-form-editor.js";
|
|
@@ -51,14 +52,14 @@ interface FormMessageDetails {
|
|
|
51
52
|
* care which surface was used.
|
|
52
53
|
*/
|
|
53
54
|
export type InlineFormResult =
|
|
54
|
-
| { kind: "run"; values:
|
|
55
|
+
| { kind: "run"; values: WorkflowInputValues }
|
|
55
56
|
| { kind: "cancel" }
|
|
56
57
|
| { kind: "unsupported" };
|
|
57
58
|
|
|
58
59
|
export interface OpenInlineFormOpts {
|
|
59
60
|
workflowName: string;
|
|
60
61
|
fields: readonly WorkflowInputEntry[];
|
|
61
|
-
prefilled?:
|
|
62
|
+
prefilled?: WorkflowInputValues;
|
|
62
63
|
theme: GraphTheme;
|
|
63
64
|
}
|
|
64
65
|
|
|
@@ -38,6 +38,7 @@ import type {
|
|
|
38
38
|
} from "../extension/wiring.js";
|
|
39
39
|
import type { WorkflowInputEntry } from "../extension/render-result.js";
|
|
40
40
|
import type { GraphTheme } from "./graph-theme.js";
|
|
41
|
+
import type { WorkflowInputValues } from "../shared/types.js";
|
|
41
42
|
import {
|
|
42
43
|
coerceValues,
|
|
43
44
|
createInputsPickerState,
|
|
@@ -50,7 +51,7 @@ export interface InputsUiSurface {
|
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
export type InputsPickerResult =
|
|
53
|
-
| { kind: "run"; values:
|
|
54
|
+
| { kind: "run"; values: WorkflowInputValues }
|
|
54
55
|
| { kind: "cancel" };
|
|
55
56
|
|
|
56
57
|
export interface OpenInputsPickerOpts {
|
|
@@ -58,7 +59,7 @@ export interface OpenInputsPickerOpts {
|
|
|
58
59
|
fields: WorkflowInputEntry[];
|
|
59
60
|
/** Prefilled values (e.g. from `key=value` slash args). The form
|
|
60
61
|
* seeds these into the form so the user doesn't re-type what they typed. */
|
|
61
|
-
prefilled?:
|
|
62
|
+
prefilled?: WorkflowInputValues;
|
|
62
63
|
theme: GraphTheme;
|
|
63
64
|
}
|
|
64
65
|
|
|
@@ -24,13 +24,14 @@
|
|
|
24
24
|
* - select : vertical choice list, ←/→ cycles choices
|
|
25
25
|
*
|
|
26
26
|
* cross-ref:
|
|
27
|
-
* -
|
|
28
|
-
* -
|
|
27
|
+
* - bastani-inc/atomic research/designs/workflow-picker-tui.tsx (PROMPT phase)
|
|
28
|
+
* - bastani-inc/atomic packages/atomic-sdk/src/components/workflow-picker-panel.tsx
|
|
29
29
|
* - src/tui/session-picker.ts (sibling overlay; same chrome + key style)
|
|
30
30
|
* - DESIGN.md §5 Section Labels
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
33
|
import type { WorkflowInputEntry } from "../extension/render-result.js";
|
|
34
|
+
import type { WorkflowInputValues, WorkflowSerializableValue } from "../shared/types.js";
|
|
34
35
|
import type { GraphTheme } from "./graph-theme.js";
|
|
35
36
|
import { paint } from "./color-utils.js";
|
|
36
37
|
import {
|
|
@@ -95,7 +96,7 @@ export interface InputsPickerState {
|
|
|
95
96
|
export type InputsPickerAction =
|
|
96
97
|
| { kind: "noop" }
|
|
97
98
|
| { kind: "cancel" }
|
|
98
|
-
| { kind: "run"; values:
|
|
99
|
+
| { kind: "run"; values: WorkflowInputValues };
|
|
99
100
|
|
|
100
101
|
export interface InputsPickerRenderOpts {
|
|
101
102
|
width: number;
|
|
@@ -119,7 +120,7 @@ export interface InputsPickerRenderOpts {
|
|
|
119
120
|
*/
|
|
120
121
|
export function createInputsPickerState(
|
|
121
122
|
fields: readonly WorkflowInputEntry[],
|
|
122
|
-
prefilled:
|
|
123
|
+
prefilled: WorkflowInputValues = {},
|
|
123
124
|
): InputsPickerState {
|
|
124
125
|
const rawText: Record<string, string> = {};
|
|
125
126
|
for (const f of fields) {
|
|
@@ -169,8 +170,8 @@ export function createInputsPickerState(
|
|
|
169
170
|
export function coerceValues(
|
|
170
171
|
fields: readonly WorkflowInputEntry[],
|
|
171
172
|
raw: Record<string, string>,
|
|
172
|
-
):
|
|
173
|
-
const out: Record<string,
|
|
173
|
+
): WorkflowInputValues {
|
|
174
|
+
const out: Record<string, WorkflowSerializableValue> = {};
|
|
174
175
|
for (const f of fields) {
|
|
175
176
|
const v = raw[f.name] ?? "";
|
|
176
177
|
if (v === "" && !f.required) continue; // skip empty optionals
|
|
@@ -198,7 +199,7 @@ export function coerceValues(
|
|
|
198
199
|
(v.startsWith("[") && v.endsWith("]"))
|
|
199
200
|
) {
|
|
200
201
|
try {
|
|
201
|
-
out[f.name] = JSON.parse(v) as
|
|
202
|
+
out[f.name] = JSON.parse(v) as WorkflowSerializableValue;
|
|
202
203
|
break;
|
|
203
204
|
} catch {
|
|
204
205
|
// fall through
|
|
@@ -46,6 +46,8 @@ export const TUI_ACTION = {
|
|
|
46
46
|
inputSubmit: "tui.input.submit",
|
|
47
47
|
selectUp: "tui.select.up",
|
|
48
48
|
selectDown: "tui.select.down",
|
|
49
|
+
selectPageUp: "tui.select.pageUp",
|
|
50
|
+
selectPageDown: "tui.select.pageDown",
|
|
49
51
|
selectConfirm: "tui.select.confirm",
|
|
50
52
|
} as const;
|
|
51
53
|
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* the same ANSI shape Pi's renderer uses for every other styled run.
|
|
19
19
|
*
|
|
20
20
|
* cross-ref:
|
|
21
|
-
* - github.com/
|
|
21
|
+
* - github.com/bastani-inc/atomic packages/atomic-sdk/src/components/node-card.tsx
|
|
22
22
|
* - DESIGN.md §5 "Node Cards (orchestrator graph)"
|
|
23
23
|
* - src/tui/graph-theme.ts `deriveGraphThemeFromPiTheme` — the
|
|
24
24
|
* accent/surface tokens used below are sourced from Pi's live
|
|
@@ -120,8 +120,32 @@ function durationText(stage: StageSnapshot): string {
|
|
|
120
120
|
|
|
121
121
|
function metaText(stage: StageSnapshot): string {
|
|
122
122
|
const deps = stage.parentIds.length;
|
|
123
|
-
|
|
124
|
-
return
|
|
123
|
+
const dependencyText = deps === 0 ? "root" : deps === 1 ? "1 dep" : `${deps} deps`;
|
|
124
|
+
return stage.fastMode === true ? `${dependencyText} · fast` : dependencyText;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function shortRunId(runId: string): string {
|
|
128
|
+
return runId.length <= 8 ? runId : runId.slice(0, 8);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function workflowChildSummaryText(stage: StageSnapshot): string {
|
|
132
|
+
const child = stage.workflowChild ?? stage.workflowChildRun;
|
|
133
|
+
if (child === undefined) return durationText(stage);
|
|
134
|
+
return `↳ ${child.workflow}`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function workflowChildMetaText(stage: StageSnapshot): string {
|
|
138
|
+
const completed = stage.workflowChild;
|
|
139
|
+
if (completed !== undefined) {
|
|
140
|
+
const outputCount = Object.keys(completed.outputs).length;
|
|
141
|
+
const outputs = outputCount === 1 ? "1 out" : `${outputCount} outs`;
|
|
142
|
+
return `run ${shortRunId(completed.runId)} · ${outputs}`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const live = stage.workflowChildRun;
|
|
146
|
+
if (live !== undefined) return `run ${shortRunId(live.runId)} · live`;
|
|
147
|
+
|
|
148
|
+
return metaText(stage);
|
|
125
149
|
}
|
|
126
150
|
|
|
127
151
|
function statusLabel(status: StageStatus): string {
|
|
@@ -248,13 +272,15 @@ export function renderNodeCard(stage: StageSnapshot, opts: NodeCardOpts): string
|
|
|
248
272
|
const top = `${bg}${bc}╭${topMiddle}╮${RESET}`;
|
|
249
273
|
const bottom = `${bg}${bc}╰${"─".repeat(innerWidth)}╯${RESET}`;
|
|
250
274
|
|
|
251
|
-
// Interior — compact status + duration.
|
|
252
|
-
//
|
|
253
|
-
//
|
|
275
|
+
// Interior — compact status + duration. Child workflow boundary
|
|
276
|
+
// stages otherwise look like empty completed nodes, so use the first
|
|
277
|
+
// body row for the child workflow identity and the final row for a
|
|
278
|
+
// terse child-run summary. This keeps the graph dense while making
|
|
279
|
+
// the boundary explain what actually ran.
|
|
254
280
|
const bodyText =
|
|
255
281
|
stage.status === "blocked"
|
|
256
282
|
? blockedBadgeText(stage, opts.stages, innerWidth)
|
|
257
|
-
:
|
|
283
|
+
: workflowChildSummaryText(stage);
|
|
258
284
|
const bodyHex = durationColor(stage.status, theme);
|
|
259
285
|
const statusText = `${statusIcon(stage.status)} ${statusLabel(stage.status)}`;
|
|
260
286
|
const statusLine =
|
|
@@ -271,7 +297,7 @@ export function renderNodeCard(stage: StageSnapshot, opts: NodeCardOpts): string
|
|
|
271
297
|
`${bg}${bc}│${RESET}`;
|
|
272
298
|
const metaLine =
|
|
273
299
|
`${bg}${bc}│${RESET}` +
|
|
274
|
-
centreColored(
|
|
300
|
+
centreColored(workflowChildMetaText(stage), innerWidth, theme.dim, bg) +
|
|
275
301
|
`${bg}${bc}│${RESET}`;
|
|
276
302
|
|
|
277
303
|
const interior: string[] =
|
|
@@ -115,6 +115,8 @@ export interface BuildGraphOverlayAdapterOpts {
|
|
|
115
115
|
* inspection.
|
|
116
116
|
*/
|
|
117
117
|
onKillRun?: (runId: string) => void;
|
|
118
|
+
/** Optional clock injection for deterministic attach-pane transition tests. */
|
|
119
|
+
now?: () => number;
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
export function buildGraphOverlayAdapter(
|
|
@@ -177,21 +179,11 @@ export function buildGraphOverlayAdapter(
|
|
|
177
179
|
}
|
|
178
180
|
}
|
|
179
181
|
|
|
180
|
-
function snapshotHasAwaitingInput(snapshot: StoreSnapshot): boolean {
|
|
181
|
-
return snapshot.runs.some(
|
|
182
|
-
(run) => run.pendingPrompt !== undefined || run.stages.some(
|
|
183
|
-
(stage) => stage.status === "awaiting_input"
|
|
184
|
-
|| stage.pendingPrompt !== undefined
|
|
185
|
-
|| stage.inputRequest !== undefined,
|
|
186
|
-
),
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
182
|
function refocusVisibleOverlayForAwaitingInput(snapshot: StoreSnapshot): void {
|
|
191
|
-
if (!snapshotHasAwaitingInput(snapshot)) return;
|
|
192
183
|
if (currentHandle === null) return;
|
|
193
184
|
if (currentHandle.isHidden()) return;
|
|
194
185
|
if (currentHandle.isFocused()) return;
|
|
186
|
+
if (currentView?.wantsFocusForAwaitingInput(snapshot) !== true) return;
|
|
195
187
|
currentHandle.focus();
|
|
196
188
|
}
|
|
197
189
|
|
|
@@ -318,6 +310,7 @@ export function buildGraphOverlayAdapter(
|
|
|
318
310
|
currentHandle?.focus();
|
|
319
311
|
},
|
|
320
312
|
setMouseScrollTracking,
|
|
313
|
+
now: buildOpts.now,
|
|
321
314
|
} as ConstructorParameters<typeof WorkflowAttachPane>[0] & {
|
|
322
315
|
piTui?: PiCustomOverlayFactoryTui;
|
|
323
316
|
piTheme?: PiTheme;
|