@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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type AgentSessionEvent, ChatSessionHost } from "@bastani/atomic";
|
|
2
2
|
import { Editor, type EditorComponent } from "@earendil-works/pi-tui";
|
|
3
3
|
import { stageUiBroker } from "../shared/stage-ui-broker.js";
|
|
4
|
+
import { readGraphStoreSnapshot, subscribeStoreInvalidation } from "../shared/store-observation.js";
|
|
4
5
|
import type { PendingPrompt, RunSnapshot, StageSnapshot } from "../shared/store-types.js";
|
|
5
6
|
import { hexToAnsi, RESET } from "./color-utils.js";
|
|
6
7
|
import { createPromptCardState } from "./prompt-card.js";
|
|
@@ -66,6 +67,7 @@ export function initializeStageChatView(ctx: StageChatViewContext, opts: StageCh
|
|
|
66
67
|
ctx.promptEditorSubmitFromEnter = false;
|
|
67
68
|
ctx.promptScrollOffset = 0;
|
|
68
69
|
ctx.promptMaxScroll = 0;
|
|
70
|
+
ctx.promptVisibleRows = 0;
|
|
69
71
|
ctx.localPaused = false;
|
|
70
72
|
ctx.mouseScrollCaptureEnabled = true;
|
|
71
73
|
ctx.lastObservedStageStatus = undefined;
|
|
@@ -101,9 +103,10 @@ export function initializeStageChatView(ctx: StageChatViewContext, opts: StageCh
|
|
|
101
103
|
snapshotMessagesFromSessionFile(ctx, initialStage);
|
|
102
104
|
absorbStageNotices(ctx, initialStage);
|
|
103
105
|
syncPromptState(ctx, initialStage?.pendingPrompt);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
const initialChatIsTerminal =
|
|
107
|
+
isTerminalStageChatState(initialRun?.status) || isTerminalStageChatState(initialStage?.status);
|
|
108
|
+
if (initialChatIsTerminal) ctx.chatHost.clearBusyForTerminalWorkflowStage();
|
|
109
|
+
ctx._unsubscribeStore = subscribeStoreInvalidation(ctx.store, () => handleStoreUpdate(ctx));
|
|
107
110
|
ctx._unsubscribeFooterData = ctx.footerData?.onBranchChange(() => ctx.requestRender?.()) ?? null;
|
|
108
111
|
|
|
109
112
|
if (ctx.handle) {
|
|
@@ -112,11 +115,47 @@ export function initializeStageChatView(ctx: StageChatViewContext, opts: StageCh
|
|
|
112
115
|
// stage can see that a live delivery authorizes it.
|
|
113
116
|
ctx._unsubscribeDeliveryActivity = subscribeStageChatDeliveryActivity(ctx);
|
|
114
117
|
ctx._unsubscribeHandle = ctx.handle.subscribe((event) => applyStageChatLiveHandleEvent(ctx, event));
|
|
118
|
+
if (!initialChatIsTerminal) rehydrateCompactionStatusAfterAttach(ctx);
|
|
115
119
|
rehydrateQueuedMessages(ctx);
|
|
116
120
|
}
|
|
117
121
|
ctx.chatHost.syncAnimationTick();
|
|
118
122
|
}
|
|
119
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Restore the factual compaction indicator into a freshly mounted host.
|
|
126
|
+
*
|
|
127
|
+
* Compaction start is an event, but the active reason lives on the session so
|
|
128
|
+
* a host rebuilt after detach can render the same label even when it missed
|
|
129
|
+
* that event. An absent reason explicitly clears the fresh host's status.
|
|
130
|
+
*/
|
|
131
|
+
function rehydrateCompactionStatus(ctx: StageChatViewContext): void {
|
|
132
|
+
const session = liveHandle(ctx)?.agentSession;
|
|
133
|
+
ctx.chatHost.hydrateCompactionStatus(session?.isCompacting === true ? session.compactionReason : undefined);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The live stage handle may create its session lazily. Re-read compaction state
|
|
138
|
+
* after that async attach so a host mounted while the session is already
|
|
139
|
+
* compacting does not stay on the generic Working row forever.
|
|
140
|
+
*/
|
|
141
|
+
function rehydrateCompactionStatusAfterAttach(ctx: StageChatViewContext): void {
|
|
142
|
+
const handle = liveHandle(ctx);
|
|
143
|
+
if (!handle) return;
|
|
144
|
+
rehydrateCompactionStatus(ctx);
|
|
145
|
+
void handle.ensureAttached().then(
|
|
146
|
+
() => {
|
|
147
|
+
if (ctx._unsubscribeHandle === null || liveHandle(ctx) === undefined) return;
|
|
148
|
+
if (isTerminalStageChatState(currentRun(ctx)?.status) || isTerminalStageChatState(currentStage(ctx)?.status)) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
rehydrateCompactionStatus(ctx);
|
|
152
|
+
ctx.chatHost.syncAnimationTick();
|
|
153
|
+
ctx.requestRender?.();
|
|
154
|
+
},
|
|
155
|
+
() => {},
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
120
159
|
/**
|
|
121
160
|
* Replay the stage's pending queue into a freshly mounted chat host.
|
|
122
161
|
*
|
|
@@ -308,7 +347,7 @@ function absorbStageNotices(ctx: StageChatViewContext, stage: StageSnapshot | un
|
|
|
308
347
|
}
|
|
309
348
|
|
|
310
349
|
export function currentRun(ctx: StageChatViewContext): RunSnapshot | undefined {
|
|
311
|
-
return ctx.store
|
|
350
|
+
return readGraphStoreSnapshot(ctx.store).runs.find((r) => r.id === ctx.runId);
|
|
312
351
|
}
|
|
313
352
|
|
|
314
353
|
export function currentStage(ctx: StageChatViewContext): StageSnapshot | undefined {
|
|
@@ -337,6 +376,7 @@ export function syncPromptState(ctx: StageChatViewContext, prompt: PendingPrompt
|
|
|
337
376
|
function resetPromptScroll(ctx: StageChatViewContext): void {
|
|
338
377
|
ctx.promptScrollOffset = 0;
|
|
339
378
|
ctx.promptMaxScroll = 0;
|
|
379
|
+
ctx.promptVisibleRows = 0;
|
|
340
380
|
}
|
|
341
381
|
|
|
342
382
|
function promptSeedText(ctx: StageChatViewContext, prompt: PendingPrompt): string {
|
|
@@ -15,7 +15,8 @@ import type { PromptCardState } from "./prompt-card.js";
|
|
|
15
15
|
|
|
16
16
|
export const VIEW_LINE_COUNT = 32;
|
|
17
17
|
export const PROMPT_SCROLL_STEP_ROWS = 4;
|
|
18
|
-
|
|
18
|
+
/** Header rows reserved by prompt paging for the normal UUID continuation shape. */
|
|
19
|
+
export const HEADER_ROWS = 2;
|
|
19
20
|
export const SEP_ROWS = 1;
|
|
20
21
|
export const STAGE_CHAT_MOUSE_SCROLL_TOGGLE_LABEL = "ctrl+t";
|
|
21
22
|
|
|
@@ -148,6 +149,7 @@ export interface StageChatViewContext {
|
|
|
148
149
|
promptEditorSubmitFromEnter: boolean;
|
|
149
150
|
promptScrollOffset: number;
|
|
150
151
|
promptMaxScroll: number;
|
|
152
|
+
promptVisibleRows: number;
|
|
151
153
|
localPaused: boolean;
|
|
152
154
|
mouseScrollCaptureEnabled: boolean;
|
|
153
155
|
lastObservedStageStatus: StageStatus | undefined;
|
|
@@ -53,7 +53,6 @@ import {
|
|
|
53
53
|
} from "./stage-chat-view-state.js";
|
|
54
54
|
import { transcriptDebugEntries } from "./stage-chat-view-transcript.js";
|
|
55
55
|
import {
|
|
56
|
-
HEADER_ROWS,
|
|
57
56
|
SEP_ROWS,
|
|
58
57
|
type StageChatViewContext,
|
|
59
58
|
type StageChatViewOpts,
|
|
@@ -99,6 +98,7 @@ export class StageChatView implements Component, Focusable {
|
|
|
99
98
|
private promptEditorSubmitFromEnter!: StageChatViewContext["promptEditorSubmitFromEnter"];
|
|
100
99
|
private promptScrollOffset!: StageChatViewContext["promptScrollOffset"];
|
|
101
100
|
private promptMaxScroll!: StageChatViewContext["promptMaxScroll"];
|
|
101
|
+
private promptVisibleRows!: StageChatViewContext["promptVisibleRows"];
|
|
102
102
|
private localPaused!: StageChatViewContext["localPaused"];
|
|
103
103
|
private mouseScrollCaptureEnabled!: StageChatViewContext["mouseScrollCaptureEnabled"];
|
|
104
104
|
private seenNoticeIds!: StageChatViewContext["seenNoticeIds"];
|
|
@@ -141,7 +141,7 @@ export class StageChatView implements Component, Focusable {
|
|
|
141
141
|
const totalRows = viewLineCount(ctx);
|
|
142
142
|
const plan = planStageChatFrame({
|
|
143
143
|
viewportRows: totalRows,
|
|
144
|
-
headerRows:
|
|
144
|
+
headerRows: headerLines.length,
|
|
145
145
|
separatorRows: SEP_ROWS,
|
|
146
146
|
pendingRows: pendingLines.length,
|
|
147
147
|
workingRows: workingLines.length,
|
|
@@ -234,6 +234,7 @@ export class StageChatView implements Component, Focusable {
|
|
|
234
234
|
void this.promptEditorSubmitFromEnter;
|
|
235
235
|
void this.promptScrollOffset;
|
|
236
236
|
void this.promptMaxScroll;
|
|
237
|
+
void this.promptVisibleRows;
|
|
237
238
|
void this.mouseScrollCaptureEnabled;
|
|
238
239
|
void this.seenNoticeIds;
|
|
239
240
|
void this._unsubscribeStore;
|
|
@@ -4,13 +4,8 @@
|
|
|
4
4
|
* Visual contract (DESIGN.md §5):
|
|
5
5
|
* - One rounded `BACKGROUND` panel with subtitle and count badges.
|
|
6
6
|
* - One rounded card per run (replaces the indented per-stage rows):
|
|
7
|
-
* title: runId · workflow · state badge
|
|
8
|
-
*
|
|
9
|
-
* - Status colour is carried by the card border and state badge semantics,
|
|
10
|
-
* never by decorative body text.
|
|
11
|
-
* - One trailing hint row pointing at `/workflow status <id>` for the
|
|
12
|
-
* most-recently-active run; full per-stage detail moves into
|
|
13
|
-
* `/workflow status <id>` ({@link renderRunDetail}).
|
|
7
|
+
* title: full runId · workflow · state badge
|
|
8
|
+
* rows: status glyph + full runId, then workflow identity and meta
|
|
14
9
|
*
|
|
15
10
|
* Plain mode (theme omitted) preserves the rounded panel/card shape without
|
|
16
11
|
* ANSI escapes, with ASCII bracket cells `[✓][●][○][✗]`.
|
|
@@ -30,14 +25,13 @@ import { effectiveRunStatus } from "../shared/returned-run-status.js";
|
|
|
30
25
|
import type { RunSnapshot, StageSnapshot, StageStatus } from "../shared/store-types.js";
|
|
31
26
|
import { elapsedRunMs, elapsedStageMs } from "../shared/timing.js";
|
|
32
27
|
import type { FlatBandBadge } from "./chat-surface.js";
|
|
33
|
-
import { chatWidth, ELLIPSIS, progressStrip,
|
|
28
|
+
import { chatWidth, ELLIPSIS, progressStrip, renderRoundedBox } from "./chat-surface.js";
|
|
34
29
|
import { BOLD, hexToAnsi, RESET } from "./color-utils.js";
|
|
35
30
|
import type { GraphTheme } from "./graph-theme.js";
|
|
31
|
+
import { wrapIdentifierLines } from "./run-identity-rows.js";
|
|
36
32
|
import { fmtDuration } from "./status-helpers.js";
|
|
37
33
|
import { truncateToWidth, visibleWidth } from "./text-helpers.js";
|
|
38
34
|
|
|
39
|
-
const SHORT_ID_LEN = 6;
|
|
40
|
-
const MIN_TITLE_BUDGET = 12;
|
|
41
35
|
const STAGE_LABEL_BUDGET = 24;
|
|
42
36
|
|
|
43
37
|
export interface RenderStatusListOpts {
|
|
@@ -83,17 +77,10 @@ export function renderStatusList(runs: readonly RunSnapshot[], opts: RenderStatu
|
|
|
83
77
|
body.push(...renderRunEntry(sorted[i]!, now, cardWidth, opts.theme));
|
|
84
78
|
}
|
|
85
79
|
}
|
|
86
|
-
|
|
87
80
|
if (opts.showDetailHint !== false && sorted.length > 0) {
|
|
88
|
-
const sid = shortId(sorted[0]!.id);
|
|
89
81
|
body.push("");
|
|
90
|
-
body.push(
|
|
91
|
-
...renderHintRows([{ command: `/workflow status ${sid}`, hint: "drill into a run" }], opts.theme)
|
|
92
|
-
.split("\n")
|
|
93
|
-
.map((line) => ` ${line} `),
|
|
94
|
-
);
|
|
82
|
+
body.push(...renderStatusHintRows(sorted[0]!.id, opts.theme, width).map((line) => ` ${line} `));
|
|
95
83
|
}
|
|
96
|
-
|
|
97
84
|
const badgeText = badges && badges.length > 0 ? ` ${badges.map((b) => b.text).join(" ")}` : "";
|
|
98
85
|
return renderRoundedBox({
|
|
99
86
|
title: `BACKGROUND ${subtitle}${badgeText}`,
|
|
@@ -108,43 +95,50 @@ export function renderStatusList(runs: readonly RunSnapshot[], opts: RenderStatu
|
|
|
108
95
|
// ---------------------------------------------------------------------------
|
|
109
96
|
|
|
110
97
|
function renderRunEntry(run: RunSnapshot, now: number, width: number, theme?: GraphTheme): string[] {
|
|
111
|
-
const sid = shortId(run.id);
|
|
112
|
-
const trailing = runTrailing(run, theme);
|
|
113
|
-
const mode = run.stages.length > 1 ? "chain " : "single";
|
|
114
98
|
const bodyWidth = effectiveWidth(width);
|
|
115
99
|
const interior = Math.max(8, bodyWidth - 4);
|
|
100
|
+
const glyph = statusIconForRun(run);
|
|
101
|
+
const glyphFg = theme ? hexToAnsi(runAccent(run, theme)) : "";
|
|
102
|
+
const accent = theme ? hexToAnsi(theme.accent) : "";
|
|
103
|
+
const text = theme ? hexToAnsi(theme.text) : "";
|
|
104
|
+
const muted = theme ? hexToAnsi(theme.textMuted) : "";
|
|
105
|
+
const dim = theme ? hexToAnsi(theme.dim) : "";
|
|
106
|
+
const reset = theme ? RESET : "";
|
|
107
|
+
|
|
108
|
+
// The identifier owns its row and is never sent through truncateToWidth.
|
|
109
|
+
// At narrow widths, continuation rows preserve every identifier character
|
|
110
|
+
// while keeping each row inside the rounded panel's interior.
|
|
111
|
+
const idRows = wrapIdentifierLines(run.id, interior, ` ${glyph} `, " ");
|
|
112
|
+
const identityRows = idRows.map(({ prefix, chunk }, index) => {
|
|
113
|
+
if (!theme) return `${prefix}${chunk}`;
|
|
114
|
+
if (index === 0) return ` ${glyphFg}${glyph}${RESET} ${accent}${chunk}${RESET}`;
|
|
115
|
+
return ` ${accent}${chunk}${RESET}`;
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const trailing = runTrailing(run, theme);
|
|
119
|
+
const trailingText = truncateToWidth(trailing?.text ?? "", Math.max(0, interior - 1), ELLIPSIS);
|
|
120
|
+
const nameBudget = Math.max(1, interior - 3 - visibleWidth(trailingText) - (trailingText ? 2 : 0));
|
|
121
|
+
const name = truncateToWidth(run.name, nameBudget, ELLIPSIS);
|
|
122
|
+
const nameSeg = theme ? `${text}${BOLD}${name}${RESET}` : name;
|
|
123
|
+
const trailingSeg =
|
|
124
|
+
theme && trailingText ? `${hexToAnsi(trailing?.fg ?? theme.dim)}${trailingText}${RESET}` : trailingText;
|
|
125
|
+
const identity = ` ${nameSeg}${trailingSeg ? ` ${trailingSeg}` : ""}`;
|
|
126
|
+
|
|
127
|
+
const mode = run.stages.length > 1 ? "chain " : "single";
|
|
116
128
|
const rawMeta = runCardMeta(run, now);
|
|
117
129
|
const modeW = mode.length + 4;
|
|
118
130
|
const maxMetaW = Math.max(0, interior - modeW - 3);
|
|
119
131
|
const meta = truncateToWidth(rawMeta, maxMetaW, ELLIPSIS);
|
|
120
132
|
const metaW = visibleWidth(meta);
|
|
121
133
|
const stripBudget = Math.max(0, interior - modeW - metaW - 2);
|
|
122
|
-
|
|
123
134
|
const strip = progressStrip(stageCells(run), stripBudget, theme);
|
|
124
135
|
const usedLeftW = modeW + visibleWidth(strip);
|
|
125
136
|
const gap = Math.max(metaW > 0 ? 1 : 0, interior - usedLeftW - metaW);
|
|
126
|
-
|
|
127
|
-
const glyph = statusIconForRun(run);
|
|
128
|
-
const glyphFg = theme ? hexToAnsi(runAccent(run, theme)) : "";
|
|
129
|
-
const accent = theme ? hexToAnsi(theme.accent) : "";
|
|
130
|
-
const text = theme ? hexToAnsi(theme.text) : "";
|
|
131
|
-
const muted = theme ? hexToAnsi(theme.textMuted) : "";
|
|
132
|
-
const dim = theme ? hexToAnsi(theme.dim) : "";
|
|
133
|
-
const reset = theme ? RESET : "";
|
|
134
|
-
|
|
135
|
-
const name = truncateToWidth(
|
|
136
|
-
run.name,
|
|
137
|
-
Math.max(MIN_TITLE_BUDGET, interior - visibleWidth(sid) - visibleWidth(trailing?.text ?? "") - 8),
|
|
138
|
-
ELLIPSIS,
|
|
139
|
-
);
|
|
140
|
-
const line1 = theme
|
|
141
|
-
? ` ${glyphFg}${glyph}${RESET} ${accent}${sid}${RESET} ${text}${BOLD}${name}${RESET} ${glyphFg}${trailing?.text ?? ""}${RESET} `
|
|
142
|
-
: ` ${glyph} ${sid} ${name} ${trailing?.text ?? ""} `;
|
|
143
137
|
const modeSeg = theme ? `${muted}${mode}${reset}` : mode;
|
|
144
138
|
const metaSeg = theme ? `${dim}${meta}${reset}` : meta;
|
|
145
|
-
const
|
|
139
|
+
const metaLine = ` ${modeSeg} ${strip}${" ".repeat(gap)}${metaSeg} `;
|
|
146
140
|
|
|
147
|
-
return [
|
|
141
|
+
return [...identityRows, identity, metaLine];
|
|
148
142
|
}
|
|
149
143
|
|
|
150
144
|
function runAccent(run: RunSnapshot, theme?: GraphTheme): string {
|
|
@@ -403,8 +397,38 @@ function sortRuns(runs: readonly RunSnapshot[]): RunSnapshot[] {
|
|
|
403
397
|
return [...[...active].sort(byStart), ...[...ended].sort(byStart)];
|
|
404
398
|
}
|
|
405
399
|
|
|
406
|
-
function
|
|
407
|
-
|
|
400
|
+
function renderStatusHintRows(id: string, theme: GraphTheme | undefined, width: number): string[] {
|
|
401
|
+
const budget = Math.max(1, width - 4);
|
|
402
|
+
const prefix = "▸ /workflow status ";
|
|
403
|
+
const continuation = " ";
|
|
404
|
+
const rows = wrapIdentifierLines(id, budget, prefix, continuation);
|
|
405
|
+
const identifierRowCount = rows.length;
|
|
406
|
+
const suffix = " drill into a run";
|
|
407
|
+
const last = rows[rows.length - 1]!;
|
|
408
|
+
if (visibleWidth(`${last.prefix}${last.chunk}${suffix}`) <= budget) {
|
|
409
|
+
last.chunk += suffix;
|
|
410
|
+
} else {
|
|
411
|
+
rows.push({
|
|
412
|
+
prefix: continuation,
|
|
413
|
+
chunk: truncateToWidth(suffix.trimStart(), Math.max(1, budget - visibleWidth(continuation)), ELLIPSIS),
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
if (!theme) return rows.map((row) => `${row.prefix}${row.chunk}`);
|
|
417
|
+
const dim = hexToAnsi(theme.dim);
|
|
418
|
+
const accent = hexToAnsi(theme.accent);
|
|
419
|
+
return rows.map((row, index) => {
|
|
420
|
+
const carriesIdentifier = index < identifierRowCount;
|
|
421
|
+
const carriesSuffix = carriesIdentifier && index === identifierRowCount - 1 && row.chunk.endsWith(suffix);
|
|
422
|
+
const idChunk = carriesSuffix ? row.chunk.slice(0, -suffix.length) : row.chunk;
|
|
423
|
+
const suffixText = carriesSuffix ? suffix : "";
|
|
424
|
+
if (index === 0) {
|
|
425
|
+
return `${dim}▸${RESET} ${accent}/workflow status ${idChunk}${RESET}${suffixText ? `${dim}${suffixText}${RESET}` : ""}`;
|
|
426
|
+
}
|
|
427
|
+
if (carriesIdentifier) {
|
|
428
|
+
return `${row.prefix}${accent}${idChunk}${RESET}${suffixText ? `${dim}${suffixText}${RESET}` : ""}`;
|
|
429
|
+
}
|
|
430
|
+
return `${row.prefix}${dim}${row.chunk}${RESET}`;
|
|
431
|
+
});
|
|
408
432
|
}
|
|
409
433
|
|
|
410
434
|
function emptyStateLine(theme?: GraphTheme): string {
|
|
@@ -418,7 +442,6 @@ function statusIconForRun(run: RunSnapshot): string {
|
|
|
418
442
|
case "completed":
|
|
419
443
|
return "✓";
|
|
420
444
|
case "skipped":
|
|
421
|
-
return "⊘";
|
|
422
445
|
case "cancelled":
|
|
423
446
|
return "⊘";
|
|
424
447
|
case "blocked":
|
|
@@ -437,4 +460,3 @@ function statusIconForRun(run: RunSnapshot): string {
|
|
|
437
460
|
}
|
|
438
461
|
|
|
439
462
|
// Re-export for callers that need to inspect width budgeting.
|
|
440
|
-
export { MIN_TITLE_BUDGET };
|
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
type ReactiveWidgetTimerHandle,
|
|
46
46
|
} from "@bastani/atomic";
|
|
47
47
|
import type { Store } from "../shared/store.js";
|
|
48
|
+
import { readGraphStoreSnapshot, subscribeStoreInvalidation } from "../shared/store-observation.js";
|
|
48
49
|
import type { StoreSnapshot } from "../shared/store-types.js";
|
|
49
50
|
import { buildThemedWidgetLines, nextWidgetRefreshDelayMs } from "./widget.js";
|
|
50
51
|
|
|
@@ -106,8 +107,8 @@ export function installStoreWidget(
|
|
|
106
107
|
key: WIDGET_KEY,
|
|
107
108
|
placement: "belowEditor",
|
|
108
109
|
timers,
|
|
109
|
-
getSnapshot: () => storeInstance
|
|
110
|
-
subscribe: (listener) => storeInstance
|
|
110
|
+
getSnapshot: () => readGraphStoreSnapshot(storeInstance),
|
|
111
|
+
subscribe: (listener) => subscribeStoreInvalidation(storeInstance, listener),
|
|
111
112
|
getPreviewLines: (snap, now) => buildThemedWidgetLines(snap, undefined, 120, now),
|
|
112
113
|
render: (snap, { theme, width, now }) => buildThemedWidgetLines(snap, theme as PiTheme | undefined, width, now),
|
|
113
114
|
getNextRefreshDelayMs: (snap, now) => nextWidgetRefreshDelayMs(snap, now),
|
|
@@ -206,12 +207,12 @@ export function installToolExecutionHooks(pi: LiveWidgetAPI, storeInstance: Stor
|
|
|
206
207
|
return call ? { key, call } : null;
|
|
207
208
|
}
|
|
208
209
|
|
|
209
|
-
storeInstance
|
|
210
|
+
subscribeStoreInvalidation(storeInstance, () => pruneActiveToolCalls(readGraphStoreSnapshot(storeInstance)));
|
|
210
211
|
|
|
211
212
|
function recordToolStart(payload: unknown): void {
|
|
212
213
|
if (!isToolExecutionPayload(payload)) return;
|
|
213
214
|
|
|
214
|
-
const snap = storeInstance
|
|
215
|
+
const snap = readGraphStoreSnapshot(storeInstance);
|
|
215
216
|
pruneActiveToolCalls(snap);
|
|
216
217
|
|
|
217
218
|
const scope = resolveExplicitStageScope(payload);
|
|
@@ -234,7 +235,7 @@ export function installToolExecutionHooks(pi: LiveWidgetAPI, storeInstance: Stor
|
|
|
234
235
|
function recordToolUpdate(payload: unknown): void {
|
|
235
236
|
if (!isToolExecutionPayload(payload)) return;
|
|
236
237
|
|
|
237
|
-
pruneActiveToolCalls(storeInstance
|
|
238
|
+
pruneActiveToolCalls(readGraphStoreSnapshot(storeInstance));
|
|
238
239
|
|
|
239
240
|
if (!activeToolCallForPayload(payload)) return;
|
|
240
241
|
// Updates are attach-only until the store has an explicit update API.
|
|
@@ -243,7 +244,7 @@ export function installToolExecutionHooks(pi: LiveWidgetAPI, storeInstance: Stor
|
|
|
243
244
|
function recordToolEnd(payload: unknown): void {
|
|
244
245
|
if (!isToolExecutionPayload(payload)) return;
|
|
245
246
|
|
|
246
|
-
pruneActiveToolCalls(storeInstance
|
|
247
|
+
pruneActiveToolCalls(readGraphStoreSnapshot(storeInstance));
|
|
247
248
|
|
|
248
249
|
const active = activeToolCallForPayload(payload);
|
|
249
250
|
if (!active) return;
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
* - One transparent rounded `BACKGROUND` panel with `N runs` and status
|
|
6
6
|
* badges (`✓ n ● n ○ n ✗ n`) in the title.
|
|
7
7
|
* - One compact rounded card per run:
|
|
8
|
-
* title: `<status glyph> <
|
|
9
|
-
* row 1: `<dim mode · progress · duration>`
|
|
10
|
-
* - Blank line between cards; trailing blank trimmed.
|
|
8
|
+
* title: `<status glyph> <full id>`
|
|
9
|
+
* row 1: `<name> · <dim mode · progress · duration>`
|
|
11
10
|
* - Collapsed single-line form below 80 cells:
|
|
12
11
|
* `▾ N background · X ●` in dim+warning.
|
|
13
12
|
*
|
|
@@ -28,9 +27,10 @@ import type { RunSnapshot, StoreSnapshot } from "../shared/store-types.js";
|
|
|
28
27
|
import { elapsedRunMs } from "../shared/timing.js";
|
|
29
28
|
import type { FlatBandBadge } from "./chat-surface.js";
|
|
30
29
|
import { renderRoundedBoxLines } from "./chat-surface.js";
|
|
31
|
-
import {
|
|
30
|
+
import { hexToAnsi, RESET } from "./color-utils.js";
|
|
32
31
|
import type { GraphTheme } from "./graph-theme.js";
|
|
33
32
|
import { deriveGraphTheme } from "./graph-theme.js";
|
|
33
|
+
import { renderRunIdentityRows } from "./run-identity-rows.js";
|
|
34
34
|
import { statusIcon } from "./status-helpers.js";
|
|
35
35
|
import type { PiTheme } from "./store-widget-installer.js";
|
|
36
36
|
|
|
@@ -38,8 +38,6 @@ import type { PiTheme } from "./store-widget-installer.js";
|
|
|
38
38
|
// Tunables
|
|
39
39
|
// ---------------------------------------------------------------------------
|
|
40
40
|
|
|
41
|
-
const SHORT_ID_LEN = 6;
|
|
42
|
-
const MAX_VISIBLE_RUNS = 4;
|
|
43
41
|
export const RECENT_ENDED_WINDOW_MS = 30_000;
|
|
44
42
|
const COLLAPSED_BREAKPOINT_COLS = 80;
|
|
45
43
|
|
|
@@ -62,16 +60,28 @@ export function formatDuration(ms: number): string {
|
|
|
62
60
|
// Run classification + selection
|
|
63
61
|
// ---------------------------------------------------------------------------
|
|
64
62
|
|
|
63
|
+
function isQuitRun(run: RunSnapshot): boolean {
|
|
64
|
+
return run.endedAt === undefined && run.status === "paused" && run.exitReason === "quit";
|
|
65
|
+
}
|
|
66
|
+
|
|
65
67
|
function isActive(run: RunSnapshot): boolean {
|
|
66
|
-
return run.endedAt === undefined;
|
|
68
|
+
return run.endedAt === undefined && !isQuitRun(run);
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
function recentlyEnded(run: RunSnapshot, now: number): boolean {
|
|
70
72
|
return run.endedAt !== undefined && now - run.endedAt <= RECENT_ENDED_WINDOW_MS;
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Returns the timestamp from which a quit card's display-only expiry is measured.
|
|
77
|
+
* Older snapshots do not have `quitAt`, so retain the bounded legacy fallbacks.
|
|
78
|
+
*/
|
|
79
|
+
function quitExpiryTimestamp(run: RunSnapshot): number {
|
|
80
|
+
return run.quitAt ?? run.pausedAt ?? run.startedAt;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function recentlyQuit(run: RunSnapshot, now: number): boolean {
|
|
84
|
+
return isQuitRun(run) && now - quitExpiryTimestamp(run) <= RECENT_ENDED_WINDOW_MS;
|
|
75
85
|
}
|
|
76
86
|
|
|
77
87
|
interface RunCounts {
|
|
@@ -123,7 +133,7 @@ function countRuns(runs: readonly RunSnapshot[], allRuns: readonly RunSnapshot[]
|
|
|
123
133
|
/**
|
|
124
134
|
* Returns the next wall-clock boundary that can change the visible widget.
|
|
125
135
|
* Running elapsed labels tick on exact one-second boundaries; paused runs stay
|
|
126
|
-
* frozen. Recently ended cards retain their independent one-shot expiry.
|
|
136
|
+
* frozen. Recently ended and quit cards retain their independent one-shot expiry.
|
|
127
137
|
* Reactive-widget updates the existing mounted component in place, so these
|
|
128
138
|
* ticks repaint the visible panel without disposing or remounting it.
|
|
129
139
|
*/
|
|
@@ -131,9 +141,14 @@ export function nextWidgetRefreshDelayMs(snap: StoreSnapshot, now = Date.now()):
|
|
|
131
141
|
const display = selectDisplayRuns(snap, now);
|
|
132
142
|
if (display.length === 0) return undefined;
|
|
133
143
|
|
|
134
|
-
const delays: number[] =
|
|
135
|
-
|
|
136
|
-
|
|
144
|
+
const delays: number[] = [];
|
|
145
|
+
for (const run of display) {
|
|
146
|
+
if (run.endedAt !== undefined) {
|
|
147
|
+
delays.push(Math.max(1, run.endedAt + RECENT_ENDED_WINDOW_MS - now + 1));
|
|
148
|
+
} else if (isQuitRun(run)) {
|
|
149
|
+
delays.push(Math.max(1, quitExpiryTimestamp(run) + RECENT_ENDED_WINDOW_MS - now + 1));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
137
152
|
for (const run of display) {
|
|
138
153
|
if (run.endedAt !== undefined || effectiveRunStatus(run) !== "running" || run.pausedAt !== undefined) continue;
|
|
139
154
|
const remainder = elapsedRunMs(run, now) % 1_000;
|
|
@@ -150,21 +165,16 @@ function selectDisplayRuns(snap: StoreSnapshot, now: number): RunSnapshot[] {
|
|
|
150
165
|
// rule `statusRuns`/the `status` action already apply.
|
|
151
166
|
const all = topLevelWorkflowRuns(snap.runs);
|
|
152
167
|
const active = all.filter((r) => isActive(r));
|
|
153
|
-
const recent = all.filter((r) => recentlyEnded(r, now));
|
|
168
|
+
const recent = all.filter((r) => recentlyEnded(r, now) || recentlyQuit(r, now));
|
|
154
169
|
// Most recently started first within each bucket; active runs precede recent.
|
|
155
170
|
const sort = (xs: RunSnapshot[]) => [...xs].sort((a, b) => (b.startedAt ?? 0) - (a.startedAt ?? 0));
|
|
156
|
-
|
|
157
|
-
return ordered.slice(0, MAX_VISIBLE_RUNS);
|
|
171
|
+
return [...sort(active), ...sort(recent)];
|
|
158
172
|
}
|
|
159
173
|
|
|
160
174
|
// ---------------------------------------------------------------------------
|
|
161
175
|
// Per-run derived strings
|
|
162
176
|
// ---------------------------------------------------------------------------
|
|
163
177
|
|
|
164
|
-
function shortId(run: RunSnapshot): string {
|
|
165
|
-
return run.id.length > SHORT_ID_LEN ? run.id.slice(0, SHORT_ID_LEN) : run.id;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
178
|
function statusGlyph(run: RunSnapshot): string {
|
|
169
179
|
if (isQuitRun(run)) return "○";
|
|
170
180
|
switch (effectiveRunStatus(run)) {
|
|
@@ -298,29 +308,28 @@ function formatTitleBadges(badges: readonly FlatBandBadge[], theme: GraphTheme,
|
|
|
298
308
|
// ---------------------------------------------------------------------------
|
|
299
309
|
|
|
300
310
|
function themedRunLines(run: RunSnapshot, now: number, theme: GraphTheme): string[] {
|
|
301
|
-
const dim = hexToAnsi(theme.dim);
|
|
302
|
-
const text = hexToAnsi(theme.text);
|
|
303
|
-
const accent = hexToAnsi(theme.accent);
|
|
304
|
-
const muted = hexToAnsi(theme.textMuted);
|
|
305
|
-
const glyphFg = hexToAnsi(statusFg(run, theme));
|
|
306
|
-
|
|
307
|
-
const glyph = statusGlyph(run);
|
|
308
|
-
const sid = shortId(run);
|
|
309
|
-
const name = run.name;
|
|
310
|
-
|
|
311
|
-
const line1 = ` ${glyphFg}${glyph}${RESET} ${accent}${sid}${RESET} ${text}${BOLD}${name}${RESET}`;
|
|
312
311
|
const meta = metaLine(run, now);
|
|
313
312
|
// Render the meta line in muted while running so the elapsed-time
|
|
314
313
|
// gradient stays readable; dim it once the run has terminated.
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
314
|
+
const metaColor = effectiveRunStatus(run) === "running" ? theme.textMuted : theme.dim;
|
|
315
|
+
return renderRunIdentityRows({
|
|
316
|
+
runId: run.id,
|
|
317
|
+
name: run.name,
|
|
318
|
+
meta,
|
|
319
|
+
glyph: statusGlyph(run),
|
|
320
|
+
glyphColor: statusFg(run, theme),
|
|
321
|
+
metaColor,
|
|
322
|
+
theme,
|
|
323
|
+
});
|
|
318
324
|
}
|
|
319
325
|
|
|
320
326
|
function plainRunLines(run: RunSnapshot, now: number): string[] {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
327
|
+
return renderRunIdentityRows({
|
|
328
|
+
runId: run.id,
|
|
329
|
+
name: run.name,
|
|
330
|
+
meta: metaLine(run, now),
|
|
331
|
+
glyph: statusGlyph(run),
|
|
332
|
+
});
|
|
324
333
|
}
|
|
325
334
|
|
|
326
335
|
// ---------------------------------------------------------------------------
|
|
@@ -371,7 +380,7 @@ export function buildThemedWidgetLines(
|
|
|
371
380
|
const display = selectDisplayRuns(snap, now);
|
|
372
381
|
if (display.length === 0) return [];
|
|
373
382
|
|
|
374
|
-
const
|
|
383
|
+
const displayCounts = countRuns(display, snap.runs);
|
|
375
384
|
// Active + recently-ended dominate the badge counts so a finished run
|
|
376
385
|
// visually persists for a beat before dropping off.
|
|
377
386
|
const visibleCounts: RunCounts = {
|
|
@@ -385,7 +394,7 @@ export function buildThemedWidgetLines(
|
|
|
385
394
|
blocked: display.filter((r) => effectiveRunStatus(r) === "blocked").length,
|
|
386
395
|
failed: display.filter((r) => r.endedAt !== undefined && ["failed", "killed"].includes(effectiveRunStatus(r)))
|
|
387
396
|
.length,
|
|
388
|
-
awaiting:
|
|
397
|
+
awaiting: displayCounts.awaiting,
|
|
389
398
|
};
|
|
390
399
|
|
|
391
400
|
const themed = piTheme !== undefined;
|
|
@@ -396,7 +405,7 @@ export function buildThemedWidgetLines(
|
|
|
396
405
|
return [themed ? themedCollapsed(visibleCounts, graphTheme) : plainCollapsed(visibleCounts)];
|
|
397
406
|
}
|
|
398
407
|
|
|
399
|
-
const total =
|
|
408
|
+
const total = display.length;
|
|
400
409
|
const subtitle = `${total} run${total === 1 ? "" : "s"}`;
|
|
401
410
|
|
|
402
411
|
const badgeList = countBadges(visibleCounts, graphTheme);
|
|
@@ -15,6 +15,7 @@ import { stageQueuedUserMessageCount } from "../runs/foreground/stage-queued-use
|
|
|
15
15
|
import { expandWorkflowGraph } from "../shared/expanded-workflow-graph.js";
|
|
16
16
|
import type { StageUiBroker } from "../shared/stage-ui-broker.js";
|
|
17
17
|
import type { Store } from "../shared/store.js";
|
|
18
|
+
import { readGraphStoreSnapshot, subscribeStoreInvalidation } from "../shared/store-observation.js";
|
|
18
19
|
import type { StageSnapshot, StoreSnapshot } from "../shared/store-types.js";
|
|
19
20
|
import type { GraphTheme } from "./graph-theme.js";
|
|
20
21
|
import { GraphView } from "./graph-view.js";
|
|
@@ -86,7 +87,9 @@ export class WorkflowAttachPane implements Component {
|
|
|
86
87
|
this.setToolsExpanded = opts.setToolsExpanded;
|
|
87
88
|
this.footerData = opts.footerData;
|
|
88
89
|
this.now = opts.now ?? Date.now;
|
|
89
|
-
this.unsubscribeStore = this.store
|
|
90
|
+
this.unsubscribeStore = subscribeStoreInvalidation(this.store, () =>
|
|
91
|
+
this._handleStoreUpdate(readGraphStoreSnapshot(this.store)),
|
|
92
|
+
);
|
|
90
93
|
this.graphView = this._buildGraphView();
|
|
91
94
|
const target =
|
|
92
95
|
opts.initialAttachStageId !== undefined && this.runId
|
|
@@ -97,7 +100,7 @@ export class WorkflowAttachPane implements Component {
|
|
|
97
100
|
if (target) {
|
|
98
101
|
this._attachToStage(target.runId, target.stageId);
|
|
99
102
|
} else {
|
|
100
|
-
this._syncAwaitingInputKeys(this.store
|
|
103
|
+
this._syncAwaitingInputKeys(readGraphStoreSnapshot(this.store));
|
|
101
104
|
this._armGraphEnterQuarantineIfRunNeedsInput();
|
|
102
105
|
this._syncMouseScrollTracking();
|
|
103
106
|
}
|
|
@@ -150,7 +153,7 @@ export class WorkflowAttachPane implements Component {
|
|
|
150
153
|
return null;
|
|
151
154
|
}
|
|
152
155
|
private _workflowName(runId: string): string {
|
|
153
|
-
const snap = this.store
|
|
156
|
+
const snap = readGraphStoreSnapshot(this.store);
|
|
154
157
|
const run = snap.runs.find((r) => r.id === runId);
|
|
155
158
|
return run?.name ?? "workflow";
|
|
156
159
|
}
|
|
@@ -160,7 +163,7 @@ export class WorkflowAttachPane implements Component {
|
|
|
160
163
|
options: { suppressInitialPromptSubmit?: boolean } = { suppressInitialPromptSubmit: true },
|
|
161
164
|
): void {
|
|
162
165
|
this.graphEnterQuarantineUntil = 0;
|
|
163
|
-
const snapshot = this.store
|
|
166
|
+
const snapshot = readGraphStoreSnapshot(this.store);
|
|
164
167
|
const graphRunId = this._resolveRunId();
|
|
165
168
|
this.lastGraphAwaitingInputKey = graphRunId ? this._runAwaitingInputKey(snapshot, graphRunId) : null;
|
|
166
169
|
this.lastStageAwaitingInputKey = this._stageAwaitingInputKey(snapshot, runId, stageId);
|
|
@@ -227,7 +230,9 @@ export class WorkflowAttachPane implements Component {
|
|
|
227
230
|
this.mode = "graph";
|
|
228
231
|
this.stagePromptEnterQuarantineUntil = 0;
|
|
229
232
|
this.lastStageAwaitingInputKey = null;
|
|
230
|
-
this.lastGraphAwaitingInputKey = this.runId
|
|
233
|
+
this.lastGraphAwaitingInputKey = this.runId
|
|
234
|
+
? this._runAwaitingInputKey(readGraphStoreSnapshot(this.store), this.runId)
|
|
235
|
+
: null;
|
|
231
236
|
this.graphEnterQuarantineUntil =
|
|
232
237
|
reason === "prompt-resolved" && metadata.suppressNextGraphSubmit === true
|
|
233
238
|
? this.now() + ENTER_TRANSITION_QUARANTINE_MS
|
|
@@ -248,7 +253,7 @@ export class WorkflowAttachPane implements Component {
|
|
|
248
253
|
this.graphEnterQuarantineUntil = 0;
|
|
249
254
|
this.stagePromptEnterQuarantineUntil = 0;
|
|
250
255
|
this.graphView = this._buildGraphView();
|
|
251
|
-
this._syncAwaitingInputKeys(this.store
|
|
256
|
+
this._syncAwaitingInputKeys(readGraphStoreSnapshot(this.store));
|
|
252
257
|
if (stageId !== undefined && runId) {
|
|
253
258
|
const target =
|
|
254
259
|
stageRunId === undefined ? this._resolveGraphStageTarget(runId, stageId) : { runId: stageRunId, stageId };
|
|
@@ -264,7 +269,7 @@ export class WorkflowAttachPane implements Component {
|
|
|
264
269
|
rootRunId: string,
|
|
265
270
|
stageId: string,
|
|
266
271
|
): { runId: string; stageId: string } | undefined {
|
|
267
|
-
const graph = expandWorkflowGraph(this.store
|
|
272
|
+
const graph = expandWorkflowGraph(readGraphStoreSnapshot(this.store), rootRunId);
|
|
268
273
|
const exact = graph.stages.find((stage) => stage.id === stageId);
|
|
269
274
|
const localMatches = graph.stages.filter((stage) => stage.workflowGraphTarget.stageId === stageId);
|
|
270
275
|
if (exact === undefined && localMatches.length > 1) return undefined;
|
|
@@ -403,7 +408,7 @@ export class WorkflowAttachPane implements Component {
|
|
|
403
408
|
runId && this._runNeedsInput(runId) ? this.now() + ENTER_TRANSITION_QUARANTINE_MS : 0;
|
|
404
409
|
}
|
|
405
410
|
private _runNeedsInput(runId: string): boolean {
|
|
406
|
-
return this._runAwaitingInputKey(this.store
|
|
411
|
+
return this._runAwaitingInputKey(readGraphStoreSnapshot(this.store), runId) !== null;
|
|
407
412
|
}
|
|
408
413
|
private _runAwaitingInputKey(snapshot: StoreSnapshot, runId: string): string | null {
|
|
409
414
|
const run = snapshot.runs.find((candidate) => candidate.id === runId);
|
|
@@ -422,7 +427,7 @@ export class WorkflowAttachPane implements Component {
|
|
|
422
427
|
return keys[0]!.key;
|
|
423
428
|
}
|
|
424
429
|
private _stageNeedsInput(runId: string, stageId: string): boolean {
|
|
425
|
-
return this._stageAwaitingInputKey(this.store
|
|
430
|
+
return this._stageAwaitingInputKey(readGraphStoreSnapshot(this.store), runId, stageId) !== null;
|
|
426
431
|
}
|
|
427
432
|
private _stageAwaitingInputKey(snapshot: StoreSnapshot, runId: string, stageId: string): string | null {
|
|
428
433
|
const run = snapshot.runs.find((candidate) => candidate.id === runId);
|
|
@@ -448,7 +453,7 @@ export class WorkflowAttachPane implements Component {
|
|
|
448
453
|
return this._stageSnapshot(runId, stageId)?.attached === true;
|
|
449
454
|
}
|
|
450
455
|
private _stageSnapshot(runId: string, stageId: string): StageSnapshot | undefined {
|
|
451
|
-
const run = this.store
|
|
456
|
+
const run = readGraphStoreSnapshot(this.store).runs.find((candidate) => candidate.id === runId);
|
|
452
457
|
return run?.stages.find((candidate) => candidate.id === stageId);
|
|
453
458
|
}
|
|
454
459
|
private _stageSnapshotNeedsInput(stage: Pick<StageSnapshot, "pendingPrompt" | "inputRequest" | "status">): boolean {
|