@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
|
@@ -5,17 +5,18 @@ import {
|
|
|
5
5
|
type ExpandedWorkflowStageTarget,
|
|
6
6
|
expandedStageTarget,
|
|
7
7
|
expandWorkflowGraph,
|
|
8
|
+
refreshExpandedWorkflowGraph,
|
|
9
|
+
sameExpandedWorkflowTopology,
|
|
8
10
|
} from "../shared/expanded-workflow-graph.js";
|
|
9
11
|
import type { Store } from "../shared/store.js";
|
|
12
|
+
import { readGraphStoreSnapshot, subscribeStoreInvalidation } from "../shared/store-observation.js";
|
|
10
13
|
import type { PendingPrompt, RunSnapshot, StageSnapshot, StoreSnapshot } from "../shared/store-types.js";
|
|
11
14
|
import type { GraphTheme } from "./graph-theme.js";
|
|
12
15
|
import { ANIMATION_TICK_MS } from "./graph-view-constants.js";
|
|
13
16
|
import type { GraphViewMode, GraphViewOpts } from "./graph-view-types.js";
|
|
14
|
-
import type
|
|
15
|
-
import { computeLayout } from "./layout.js";
|
|
17
|
+
import { computeLayout, type LayoutNode, NODE_H, NODE_W } from "./layout.js";
|
|
16
18
|
import { createPromptCardState, type PromptCardState } from "./prompt-card.js";
|
|
17
19
|
import type { SwitcherState } from "./switcher.js";
|
|
18
|
-
import { createToastManager } from "./toast.js";
|
|
19
20
|
|
|
20
21
|
export interface GraphStageCounts {
|
|
21
22
|
pending: number;
|
|
@@ -41,6 +42,30 @@ interface GraphViewportGeometry {
|
|
|
41
42
|
viewportWidth: number;
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
interface GraphLayoutBand {
|
|
46
|
+
top: number;
|
|
47
|
+
bottom: number;
|
|
48
|
+
nodeIndices: number[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface GraphRenderEdge {
|
|
52
|
+
parentX: number;
|
|
53
|
+
parentY: number;
|
|
54
|
+
childX: number;
|
|
55
|
+
childY: number;
|
|
56
|
+
top: number;
|
|
57
|
+
bottom: number;
|
|
58
|
+
left: number;
|
|
59
|
+
right: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface GraphRenderGeometry {
|
|
63
|
+
canvasWidth: number;
|
|
64
|
+
totalRows: number;
|
|
65
|
+
bands: GraphLayoutBand[];
|
|
66
|
+
edges: GraphRenderEdge[];
|
|
67
|
+
}
|
|
68
|
+
|
|
44
69
|
/** Expansion, focus, prompt, and store-backed layout state for GraphView. */
|
|
45
70
|
export abstract class GraphViewState {
|
|
46
71
|
protected mode: GraphViewMode;
|
|
@@ -66,9 +91,10 @@ export abstract class GraphViewState {
|
|
|
66
91
|
protected focusedIndex = 0;
|
|
67
92
|
protected switcherOpen = false;
|
|
68
93
|
protected switcherState: SwitcherState = { query: "", selectedIndex: 0 };
|
|
69
|
-
protected toastManager = createToastManager();
|
|
70
94
|
protected detailsExpanded = true;
|
|
71
95
|
protected cachedLayout: LayoutNode[] = [];
|
|
96
|
+
/** Stages mirrored from `cachedLayout` — shared across card renders. */
|
|
97
|
+
protected cachedDisplayStages: StageSnapshot[] = [];
|
|
72
98
|
protected expandedGraph: ExpandedWorkflowGraph = {
|
|
73
99
|
stages: [],
|
|
74
100
|
renderStages: [],
|
|
@@ -81,9 +107,14 @@ export abstract class GraphViewState {
|
|
|
81
107
|
protected graphScrollColOffset = 0;
|
|
82
108
|
protected graphNodeHitRects: GraphNodeHitRect[] = [];
|
|
83
109
|
protected lastGraphViewport: GraphViewportGeometry | null = null;
|
|
110
|
+
protected lastGraphTotalRows = 0;
|
|
84
111
|
protected lastOverlayFrameWidth = 80;
|
|
85
112
|
protected pendingEnsureFocusedVisible = true;
|
|
86
113
|
protected lastAutoFocusedAwaitingInputKey: string | null = null;
|
|
114
|
+
protected lastBuiltSnapshotVersion: number | null = null;
|
|
115
|
+
protected topologySnapshot: StoreSnapshot | null = null;
|
|
116
|
+
protected hasAnimatingStages = false;
|
|
117
|
+
protected cachedRenderGeometry: GraphRenderGeometry = { canvasWidth: 0, totalRows: 0, bands: [], edges: [] };
|
|
87
118
|
|
|
88
119
|
protected _intervalId: ReturnType<typeof setInterval> | null = null;
|
|
89
120
|
protected _lastGTime: number | null = null;
|
|
@@ -107,11 +138,11 @@ export abstract class GraphViewState {
|
|
|
107
138
|
this.footerData = opts.footerData;
|
|
108
139
|
this.getStageQueuedMessageCount = opts.getStageQueuedMessageCount;
|
|
109
140
|
|
|
110
|
-
this._unsubscribe = this.store
|
|
111
|
-
this.currentSnapshot =
|
|
141
|
+
this._unsubscribe = subscribeStoreInvalidation(this.store, () => {
|
|
142
|
+
this.currentSnapshot = readGraphStoreSnapshot(this.store);
|
|
112
143
|
this._rebuildLayout();
|
|
113
144
|
});
|
|
114
|
-
this.currentSnapshot = this.store
|
|
145
|
+
this.currentSnapshot = readGraphStoreSnapshot(this.store);
|
|
115
146
|
this._rebuildLayout();
|
|
116
147
|
|
|
117
148
|
// Animation tick: while the overlay is mounted, fire a render
|
|
@@ -125,18 +156,35 @@ export abstract class GraphViewState {
|
|
|
125
156
|
// gate in place the previous tmux scrollback "ghost overlay"
|
|
126
157
|
// failure mode does not apply: pi-tui owns the screen buffer
|
|
127
158
|
// and diff-blits frames in place.
|
|
159
|
+
//
|
|
160
|
+
// Skip ticks when nothing is animating (#2100): a large completed
|
|
161
|
+
// or pending-only graph must not burn ~10 full paints/sec.
|
|
128
162
|
if (this.mode === "overlay" && this.requestRender) {
|
|
129
163
|
this._intervalId = setInterval(() => {
|
|
164
|
+
if (!this._needsAnimationTick()) return;
|
|
130
165
|
this.requestRender?.();
|
|
131
166
|
}, ANIMATION_TICK_MS);
|
|
132
167
|
(this._intervalId as { unref?: () => void }).unref?.();
|
|
133
168
|
}
|
|
134
169
|
}
|
|
135
170
|
|
|
171
|
+
protected _needsAnimationTick(): boolean {
|
|
172
|
+
return this.promptState !== null || this.hasAnimatingStages;
|
|
173
|
+
}
|
|
174
|
+
|
|
136
175
|
protected _rebuildLayout(): void {
|
|
176
|
+
const version = this.currentSnapshot?.version ?? null;
|
|
177
|
+
// Overlay adapter calls `invalidate()` after GraphView's own store
|
|
178
|
+
// subscriber already rebuilt for this snapshot — skip the duplicate.
|
|
179
|
+
if (version !== null && version === this.lastBuiltSnapshotVersion) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
137
183
|
const run = this._getCurrentRun();
|
|
138
184
|
if (!run) {
|
|
139
185
|
this.cachedLayout = [];
|
|
186
|
+
this.cachedDisplayStages = [];
|
|
187
|
+
this.cachedRenderGeometry = { canvasWidth: 0, totalRows: 0, bands: [], edges: [] };
|
|
140
188
|
this.expandedGraph = { stages: [], renderStages: [], tools: [], nodes: [], targets: new Map() };
|
|
141
189
|
this.focusedIndex = 0;
|
|
142
190
|
this.graphScrollOffset = 0;
|
|
@@ -145,16 +193,109 @@ export abstract class GraphViewState {
|
|
|
145
193
|
this.lastGraphViewport = null;
|
|
146
194
|
this.pendingEnsureFocusedVisible = true;
|
|
147
195
|
this.promptState = null;
|
|
196
|
+
this.topologySnapshot = this.currentSnapshot;
|
|
197
|
+
this.hasAnimatingStages = false;
|
|
198
|
+
this.lastBuiltSnapshotVersion = version;
|
|
148
199
|
return;
|
|
149
200
|
}
|
|
150
201
|
|
|
151
202
|
const previousFocusedStageId = this.cachedLayout[this.focusedIndex]?.stage.id;
|
|
152
203
|
const graphStages = this._graphStages(run);
|
|
204
|
+
const sameTopology =
|
|
205
|
+
this.cachedLayout.length === graphStages.length &&
|
|
206
|
+
this.cachedLayout.length > 0 &&
|
|
207
|
+
this.cachedLayout.every((node, index) => {
|
|
208
|
+
const next = graphStages[index];
|
|
209
|
+
if (!next || node.stage.id !== next.id || node.stage.nodeKind !== next.nodeKind) return false;
|
|
210
|
+
if (node.stage.parentIds.length !== next.parentIds.length) return false;
|
|
211
|
+
return node.stage.parentIds.every((parentId, parentIndex) => parentId === next.parentIds[parentIndex]);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
if (sameTopology) {
|
|
215
|
+
for (let index = 0; index < this.cachedLayout.length; index++) {
|
|
216
|
+
this.cachedLayout[index]!.stage = graphStages[index]!;
|
|
217
|
+
}
|
|
218
|
+
this.cachedDisplayStages = [...graphStages];
|
|
219
|
+
this.hasAnimatingStages = graphStages.some(
|
|
220
|
+
(stage) => stage.status === "running" || stage.status === "awaiting_input",
|
|
221
|
+
);
|
|
222
|
+
this._finalizeFocusAndPrompt(run, previousFocusedStageId, true);
|
|
223
|
+
this.lastBuiltSnapshotVersion = version;
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
153
227
|
const nextLayout = computeLayout(graphStages, { orientation: "vertical" });
|
|
154
228
|
this.cachedLayout = nextLayout;
|
|
229
|
+
this.cachedDisplayStages = nextLayout.map((node) => node.stage);
|
|
230
|
+
this.cachedRenderGeometry = this._buildRenderGeometry(nextLayout);
|
|
231
|
+
this.hasAnimatingStages = graphStages.some(
|
|
232
|
+
(stage) => stage.status === "running" || stage.status === "awaiting_input",
|
|
233
|
+
);
|
|
155
234
|
this.graphNodeHitRects = [];
|
|
156
235
|
this.lastGraphViewport = null;
|
|
236
|
+
this._finalizeFocusAndPrompt(run, previousFocusedStageId, false);
|
|
237
|
+
this.lastBuiltSnapshotVersion = version;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private _buildRenderGeometry(layout: readonly LayoutNode[]): GraphRenderGeometry {
|
|
241
|
+
let canvasWidth = 0;
|
|
242
|
+
let totalRows = 0;
|
|
243
|
+
const nodeByStageId = new Map<string, LayoutNode>();
|
|
244
|
+
const bandsByTop = new Map<number, GraphLayoutBand>();
|
|
245
|
+
for (let index = 0; index < layout.length; index++) {
|
|
246
|
+
const node = layout[index]!;
|
|
247
|
+
canvasWidth = Math.max(canvasWidth, node.x + NODE_W);
|
|
248
|
+
totalRows = Math.max(totalRows, node.y + NODE_H);
|
|
249
|
+
nodeByStageId.set(node.stage.id, node);
|
|
250
|
+
const band = bandsByTop.get(node.y);
|
|
251
|
+
if (band) band.nodeIndices.push(index);
|
|
252
|
+
else bandsByTop.set(node.y, { top: node.y, bottom: node.y + NODE_H, nodeIndices: [index] });
|
|
253
|
+
}
|
|
157
254
|
|
|
255
|
+
const edges: GraphRenderEdge[] = [];
|
|
256
|
+
for (const node of layout) {
|
|
257
|
+
for (const parentId of node.stage.parentIds) {
|
|
258
|
+
const parent = nodeByStageId.get(parentId);
|
|
259
|
+
if (!parent) continue;
|
|
260
|
+
const parentCol = parent.x + Math.floor(NODE_W / 2);
|
|
261
|
+
const childCol = node.x + Math.floor(NODE_W / 2);
|
|
262
|
+
edges.push({
|
|
263
|
+
parentX: parent.x,
|
|
264
|
+
parentY: parent.y,
|
|
265
|
+
childX: node.x,
|
|
266
|
+
childY: node.y,
|
|
267
|
+
top: parent.y + NODE_H,
|
|
268
|
+
bottom: node.y,
|
|
269
|
+
left: Math.min(parentCol, childCol),
|
|
270
|
+
right: Math.max(parentCol, childCol) + 1,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
canvasWidth,
|
|
276
|
+
totalRows,
|
|
277
|
+
bands: [...bandsByTop.values()].sort((left, right) => left.top - right.top),
|
|
278
|
+
edges,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
protected _firstVisibleLayoutBand(viewportTop: number): number {
|
|
283
|
+
const bands = this.cachedRenderGeometry.bands;
|
|
284
|
+
let low = 0;
|
|
285
|
+
let high = bands.length;
|
|
286
|
+
while (low < high) {
|
|
287
|
+
const middle = Math.floor((low + high) / 2);
|
|
288
|
+
if (bands[middle]!.bottom <= viewportTop) low = middle + 1;
|
|
289
|
+
else high = middle;
|
|
290
|
+
}
|
|
291
|
+
return low;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
protected _finalizeFocusAndPrompt(
|
|
295
|
+
run: RunSnapshot,
|
|
296
|
+
previousFocusedStageId: string | undefined,
|
|
297
|
+
preserveHitRects: boolean,
|
|
298
|
+
): void {
|
|
158
299
|
let focusNeedsReveal = this.pendingEnsureFocusedVisible;
|
|
159
300
|
// One-shot: if the host passed `initialFocusedStageId`, snap the
|
|
160
301
|
// cursor to that stage now that the layout exists. The attach shell
|
|
@@ -206,6 +347,10 @@ export abstract class GraphViewState {
|
|
|
206
347
|
focusNeedsReveal = true;
|
|
207
348
|
}
|
|
208
349
|
this.pendingEnsureFocusedVisible = focusNeedsReveal;
|
|
350
|
+
if (!preserveHitRects && focusNeedsReveal) {
|
|
351
|
+
this.graphNodeHitRects = [];
|
|
352
|
+
this.lastGraphViewport = null;
|
|
353
|
+
}
|
|
209
354
|
this._syncPromptState(run.pendingPrompt);
|
|
210
355
|
}
|
|
211
356
|
|
|
@@ -247,9 +392,13 @@ export abstract class GraphViewState {
|
|
|
247
392
|
}
|
|
248
393
|
|
|
249
394
|
protected _graphStages(run: RunSnapshot): ExpandedWorkflowStage[] {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
395
|
+
const snapshot = this.currentSnapshot;
|
|
396
|
+
if (!snapshot) return [];
|
|
397
|
+
const canRefresh =
|
|
398
|
+
this.topologySnapshot !== null && sameExpandedWorkflowTopology(this.topologySnapshot, snapshot);
|
|
399
|
+
const refreshed = canRefresh ? refreshExpandedWorkflowGraph(this.expandedGraph, snapshot) : undefined;
|
|
400
|
+
this.expandedGraph = refreshed ?? expandWorkflowGraph(snapshot, run.id);
|
|
401
|
+
this.topologySnapshot = snapshot;
|
|
253
402
|
const stages = [...this.expandedGraph.renderStages];
|
|
254
403
|
const hasStagePrompt = stages.some(
|
|
255
404
|
(stage) =>
|
|
@@ -258,8 +407,6 @@ export abstract class GraphViewState {
|
|
|
258
407
|
);
|
|
259
408
|
if (!hasStagePrompt) return stages;
|
|
260
409
|
return stages.filter((stage) => {
|
|
261
|
-
// Prompt-node injection can leave unstarted author stages in the store
|
|
262
|
-
// while the prompt node owns focus; hide only these inert placeholders.
|
|
263
410
|
const isUnstartedPlaceholder =
|
|
264
411
|
stage.status === "pending" &&
|
|
265
412
|
stage.startedAt === undefined &&
|
|
@@ -324,7 +471,7 @@ export abstract class GraphViewState {
|
|
|
324
471
|
}
|
|
325
472
|
|
|
326
473
|
protected _displayStages(run: RunSnapshot): StageSnapshot[] {
|
|
327
|
-
return this.
|
|
474
|
+
return this.cachedDisplayStages.length > 0 ? this.cachedDisplayStages : [...run.stages];
|
|
328
475
|
}
|
|
329
476
|
|
|
330
477
|
protected _counts(stages: readonly StageSnapshot[]): GraphStageCounts {
|
|
@@ -129,11 +129,17 @@ export function computeLayout(stages: readonly StageSnapshot[], opts: LayoutOpts
|
|
|
129
129
|
centreShift.set(depth, Math.round(widestCentre - centre));
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
const rowIndex = new Map<string, number>();
|
|
133
|
+
for (const ids of colGroups.values()) {
|
|
134
|
+
for (let i = 0; i < ids.length; i++) {
|
|
135
|
+
rowIndex.set(ids[i]!, i);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
132
139
|
const nodes: LayoutNode[] = [];
|
|
133
140
|
for (const s of stages) {
|
|
134
141
|
const col = colMap.get(s.id) ?? 0;
|
|
135
|
-
const
|
|
136
|
-
const row = group.indexOf(s.id);
|
|
142
|
+
const row = rowIndex.get(s.id) ?? 0;
|
|
137
143
|
const x =
|
|
138
144
|
orientation === "vertical" ? row * (NODE_W + colGap) + (centreShift.get(col) ?? 0) : col * (NODE_W + colGap);
|
|
139
145
|
const y = orientation === "vertical" ? col * (NODE_H + rowGap) : row * (NODE_H + rowGap);
|
|
@@ -30,6 +30,7 @@ import { elapsedStageMs } from "../shared/timing.js";
|
|
|
30
30
|
import { BOLD, hexBg, hexToAnsi, lerpColor, paint, RESET } from "./color-utils.js";
|
|
31
31
|
import type { GraphTheme } from "./graph-theme.js";
|
|
32
32
|
import { NODE_H, NODE_W } from "./layout.js";
|
|
33
|
+
import { wrapIdentifierLines } from "./run-identity-rows.js";
|
|
33
34
|
import { fmtDuration, statusIcon } from "./status-helpers.js";
|
|
34
35
|
import { truncateToWidth, visibleWidth } from "./text-helpers.js";
|
|
35
36
|
|
|
@@ -131,28 +132,29 @@ function metaText(stage: StageSnapshot): string {
|
|
|
131
132
|
return stage.fastMode === true ? `${dependencyText} · fast` : dependencyText;
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
function
|
|
135
|
-
return runId.length <= 8 ? runId : runId.slice(0, 8);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function workflowChildSummaryText(stage: StageSnapshot): string {
|
|
135
|
+
function workflowChildRunRows(stage: StageSnapshot, width: number): string[] {
|
|
139
136
|
const child = stage.workflowChild ?? stage.workflowChildRun;
|
|
140
|
-
if (child === undefined) return
|
|
141
|
-
return
|
|
137
|
+
if (child === undefined) return [];
|
|
138
|
+
return wrapIdentifierLines(child.runId, Math.max(1, width), "run ", "").map((row) => `${row.prefix}${row.chunk}`);
|
|
142
139
|
}
|
|
143
140
|
|
|
144
|
-
function workflowChildMetaText(stage: StageSnapshot): string {
|
|
141
|
+
function workflowChildMetaText(stage: StageSnapshot): string | undefined {
|
|
145
142
|
const completed = stage.workflowChild;
|
|
146
143
|
if (completed !== undefined) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
144
|
+
// #2140 moved the child run id onto its own wrapped row, so the meta
|
|
145
|
+
// line carries only the output count. Prefer the payload-free
|
|
146
|
+
// `outputCount` the compact graph projection supplies; fall back to
|
|
147
|
+
// counting a full `outputs` map on the legacy snapshot path.
|
|
148
|
+
const outputCount = completed.outputCount ?? Object.keys(completed.outputs).length;
|
|
149
|
+
return outputCount === 1 ? "1 out" : `${outputCount} outs`;
|
|
150
150
|
}
|
|
151
|
+
if (stage.workflowChildRun !== undefined) return "live";
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
151
154
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return metaText(stage);
|
|
155
|
+
function joinCompactStatusMeta(status: string, meta: string, width: number): string {
|
|
156
|
+
const candidates = [`${status} · ${meta}`, `${status} ·${meta}`, `${status}· ${meta}`, `${status}·${meta}`];
|
|
157
|
+
return candidates.find((candidate) => visibleWidth(candidate) <= width) ?? meta;
|
|
156
158
|
}
|
|
157
159
|
|
|
158
160
|
function statusLabel(status: StageStatus): string {
|
|
@@ -210,22 +212,23 @@ function buildTitleSlot(
|
|
|
210
212
|
focused: boolean,
|
|
211
213
|
theme: GraphTheme,
|
|
212
214
|
cardBg: string,
|
|
215
|
+
compact = false,
|
|
213
216
|
): { slot: string; visibleWidth: number } {
|
|
214
|
-
const maxName = Math.max(2, innerWidth - 4);
|
|
217
|
+
const maxName = Math.max(2, compact ? innerWidth - 1 : innerWidth - 4);
|
|
215
218
|
const safeName = truncate(name, maxName);
|
|
216
219
|
if (focused) {
|
|
217
220
|
// Flanking spaces sit on the accent tab so the pill reads as a
|
|
218
221
|
// single coloured run. Use `paint` to combine bg + fg + bold +
|
|
219
222
|
// RESET in one ANSI sequence, then re-prime the card stratum so
|
|
220
223
|
// the dashes outside the slot stay on the body bg.
|
|
221
|
-
const tabText = ` ${safeName} `;
|
|
224
|
+
const tabText = compact ? safeName : ` ${safeName} `;
|
|
222
225
|
const styled = `${paint(tabText, theme.surface, {
|
|
223
226
|
bg: theme.accent,
|
|
224
227
|
bold: true,
|
|
225
228
|
})}${cardBg}`;
|
|
226
229
|
return { slot: styled, visibleWidth: visibleWidth(tabText) };
|
|
227
230
|
}
|
|
228
|
-
const titleRaw = ` ${safeName} `;
|
|
231
|
+
const titleRaw = compact ? safeName : ` ${safeName} `;
|
|
229
232
|
const styled = `${BOLD}${titleRaw}${RESET}${cardBg}`;
|
|
230
233
|
return { slot: styled, visibleWidth: visibleWidth(titleRaw) };
|
|
231
234
|
}
|
|
@@ -251,14 +254,16 @@ export function renderNodeCard(stage: StageSnapshot, opts: NodeCardOpts): string
|
|
|
251
254
|
const bg = hexBg(theme.bg);
|
|
252
255
|
const innerWidth = Math.max(2, width - 2);
|
|
253
256
|
|
|
254
|
-
//
|
|
255
|
-
//
|
|
257
|
+
// Child workflow boundaries use the compact title path so their workflow
|
|
258
|
+
// identity remains visible without changing the fixed card geometry.
|
|
259
|
+
const child = stage.workflowChild ?? stage.workflowChildRun;
|
|
256
260
|
const { slot: titleSlot, visibleWidth: titleVisibleWidth } = buildTitleSlot(
|
|
257
|
-
stage.name
|
|
261
|
+
child === undefined ? stage.name : `↳ ${child.workflow}`,
|
|
258
262
|
innerWidth,
|
|
259
263
|
focused,
|
|
260
264
|
theme,
|
|
261
265
|
bg,
|
|
266
|
+
child !== undefined,
|
|
262
267
|
);
|
|
263
268
|
const titleStart = Math.max(1, Math.floor((innerWidth - titleVisibleWidth) / 2));
|
|
264
269
|
const titleEnd = titleStart + titleVisibleWidth;
|
|
@@ -267,17 +272,12 @@ export function renderNodeCard(stage: StageSnapshot, opts: NodeCardOpts): string
|
|
|
267
272
|
const top = `${bg}${bc}╭${topMiddle}╮${RESET}`;
|
|
268
273
|
const bottom = `${bg}${bc}╰${"─".repeat(innerWidth)}╯${RESET}`;
|
|
269
274
|
|
|
270
|
-
// Interior — compact status + duration. Child workflow boundary
|
|
271
|
-
// stages otherwise look like empty completed nodes, so use the first
|
|
272
|
-
// body row for the child workflow identity and the final row for a
|
|
273
|
-
// terse child-run summary. This keeps the graph dense while making
|
|
274
|
-
// the boundary explain what actually ran.
|
|
275
275
|
const bodyText =
|
|
276
276
|
stage.nodeKind === "tool"
|
|
277
277
|
? (stage.error ?? stage.result ?? "durable tool")
|
|
278
278
|
: stage.status === "blocked"
|
|
279
279
|
? blockedBadgeText(stage, opts.stages, innerWidth)
|
|
280
|
-
:
|
|
280
|
+
: durationText(stage);
|
|
281
281
|
const bodyHex = durationColor(stage.status, theme);
|
|
282
282
|
const statusText = `${statusIcon(stage.status)} ${stage.toolStatus ?? statusLabel(stage.status)}`;
|
|
283
283
|
const statusLine =
|
|
@@ -292,10 +292,26 @@ export function renderNodeCard(stage: StageSnapshot, opts: NodeCardOpts): string
|
|
|
292
292
|
bold: stage.status === "blocked",
|
|
293
293
|
}) +
|
|
294
294
|
`${bg}${bc}│${RESET}`;
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
295
|
+
|
|
296
|
+
const contentRows = Math.max(0, height - 2);
|
|
297
|
+
const metaLine = `${bg}${bc}│${RESET}${centreColored(metaText(stage), innerWidth, theme.dim, bg)}${bg}${bc}│${RESET}`;
|
|
298
|
+
const childRunLines = workflowChildRunRows(stage, innerWidth).map(
|
|
299
|
+
(row) => `${bg}${bc}│${RESET}${centreColored(row, innerWidth, theme.dim, bg)}${bg}${bc}│${RESET}`,
|
|
300
|
+
);
|
|
301
|
+
const queuedCount = queuedBadgeCount(opts.queuedMessageCount);
|
|
302
|
+
const childMeta = workflowChildMetaText(stage);
|
|
303
|
+
const childSummary =
|
|
304
|
+
childMeta === undefined
|
|
305
|
+
? undefined
|
|
306
|
+
: joinCompactStatusMeta(statusText, queuedCount > 0 ? queuedBadgeText(queuedCount) : childMeta, innerWidth);
|
|
307
|
+
const childSummaryLine =
|
|
308
|
+
childSummary === undefined
|
|
309
|
+
? undefined
|
|
310
|
+
: `${bg}${bc}│${RESET}` +
|
|
311
|
+
centreColored(childSummary, innerWidth, bodyHex, bg, {
|
|
312
|
+
bold: stage.status === "running" || stage.status === "awaiting_input",
|
|
313
|
+
}) +
|
|
314
|
+
`${bg}${bc}│${RESET}`;
|
|
299
315
|
|
|
300
316
|
const interior: string[] =
|
|
301
317
|
stage.status === "awaiting_input"
|
|
@@ -308,29 +324,25 @@ export function renderNodeCard(stage: StageSnapshot, opts: NodeCardOpts): string
|
|
|
308
324
|
centreColored("↵ enter to respond", innerWidth, theme.dim, bg) +
|
|
309
325
|
`${bg}${bc}│${RESET}`,
|
|
310
326
|
]
|
|
311
|
-
:
|
|
327
|
+
: childSummaryLine === undefined
|
|
328
|
+
? [durLine, statusLine, metaLine]
|
|
329
|
+
: [...childRunLines, childSummaryLine];
|
|
312
330
|
|
|
313
331
|
// A queued steer/follow-up is invisible once the user leaves the stage chat,
|
|
314
332
|
// so it claims one existing body row rather than competing for space inside a
|
|
315
|
-
// line that would truncate.
|
|
316
|
-
//
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
const queuedCount = queuedBadgeCount(opts.queuedMessageCount);
|
|
320
|
-
const preferredBadgeRow = stage.status === "awaiting_input" ? 1 : interior.length - 1;
|
|
333
|
+
// line that would truncate. Child boundaries pack it beside status except when
|
|
334
|
+
// the awaiting-input interior leaves its redundant response row available.
|
|
335
|
+
const preferredBadgeRow =
|
|
336
|
+
stage.status === "awaiting_input" ? 1 : childSummaryLine === undefined ? interior.length - 1 : -1;
|
|
321
337
|
|
|
322
338
|
// Pad / clip to exactly `height` lines.
|
|
323
|
-
const contentRows = Math.max(0, height - 2);
|
|
324
339
|
while (interior.length < contentRows) {
|
|
325
340
|
interior.push(`${bg}${bc}│${RESET}${bg}${" ".repeat(innerWidth)}${bg}${bc}│${RESET}`);
|
|
326
341
|
}
|
|
327
|
-
if (interior.length > contentRows)
|
|
328
|
-
interior.length = contentRows;
|
|
329
|
-
}
|
|
342
|
+
if (interior.length > contentRows) interior.length = contentRows;
|
|
330
343
|
|
|
331
|
-
if (queuedCount > 0 && interior.length > 0) {
|
|
332
|
-
const badgeRow =
|
|
333
|
-
preferredBadgeRow >= 0 && preferredBadgeRow < interior.length ? preferredBadgeRow : interior.length - 1;
|
|
344
|
+
if (queuedCount > 0 && interior.length > 0 && preferredBadgeRow >= 0) {
|
|
345
|
+
const badgeRow = preferredBadgeRow < interior.length ? preferredBadgeRow : interior.length - 1;
|
|
334
346
|
interior[badgeRow] =
|
|
335
347
|
`${bg}${bc}│${RESET}` +
|
|
336
348
|
centreColored(queuedBadgeText(queuedCount), innerWidth, theme.info, bg, { bold: true }) +
|
|
@@ -34,6 +34,7 @@ import type { StageControlRegistry } from "../runs/foreground/stage-control-regi
|
|
|
34
34
|
import { stageControlRegistry as defaultStageControlRegistry } from "../runs/foreground/stage-control-registry.js";
|
|
35
35
|
import type { StageUiBroker } from "../shared/stage-ui-broker.js";
|
|
36
36
|
import type { Store } from "../shared/store.js";
|
|
37
|
+
import { readGraphStoreSnapshot, subscribeStoreInvalidation } from "../shared/store-observation.js";
|
|
37
38
|
import type { StoreSnapshot } from "../shared/store-types.js";
|
|
38
39
|
import { deriveGraphThemeFromPiTheme } from "./graph-theme.js";
|
|
39
40
|
import type { OverlayTerminalOutput } from "./overlay-terminal-modes.js";
|
|
@@ -274,7 +275,8 @@ export function buildGraphOverlayAdapter(
|
|
|
274
275
|
|
|
275
276
|
function makeComponent(view: WorkflowAttachPane, tui: PiCustomOverlayFactoryTui): PiCustomComponent {
|
|
276
277
|
requestMountedRender = () => tui.requestRender?.();
|
|
277
|
-
const onStoreUpdate = (
|
|
278
|
+
const onStoreUpdate = (): void => {
|
|
279
|
+
const snapshot = readGraphStoreSnapshot(store);
|
|
278
280
|
// Always invalidate retained view state so a later reopen renders the
|
|
279
281
|
// current snapshot — but while the overlay is hidden, never ask the
|
|
280
282
|
// host to render (#1856): each hidden-overlay render request became
|
|
@@ -285,7 +287,7 @@ export function buildGraphOverlayAdapter(
|
|
|
285
287
|
refocusVisibleOverlayForAwaitingInput(snapshot);
|
|
286
288
|
tui.requestRender?.();
|
|
287
289
|
};
|
|
288
|
-
const unsubscribe = store
|
|
290
|
+
const unsubscribe = subscribeStoreInvalidation(store, onStoreUpdate);
|
|
289
291
|
return {
|
|
290
292
|
render: (width: number) => view.render(width),
|
|
291
293
|
handleInput: (data: string) => {
|