@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
|
@@ -25,8 +25,10 @@
|
|
|
25
25
|
|
|
26
26
|
import type { PiCustomComponent, PiCustomOverlayFactoryTui, PiCustomOverlayFunction } from "../extension/wiring.js";
|
|
27
27
|
import type { Store } from "../shared/store.js";
|
|
28
|
+
import { readGraphStoreSnapshot, subscribeStoreInvalidation } from "../shared/store-observation.js";
|
|
28
29
|
import type { GraphTheme } from "./graph-theme.js";
|
|
29
30
|
import {
|
|
31
|
+
createSessionPickerResumeCandidateCache,
|
|
30
32
|
createSessionPickerState,
|
|
31
33
|
handleSessionPickerInput,
|
|
32
34
|
renderSessionPicker,
|
|
@@ -80,6 +82,8 @@ export function openSessionPicker(
|
|
|
80
82
|
let settled = false;
|
|
81
83
|
let unsubscribe: (() => void) | null = null;
|
|
82
84
|
|
|
85
|
+
const resumeCandidateCache = createSessionPickerResumeCandidateCache();
|
|
86
|
+
|
|
83
87
|
const factory = (
|
|
84
88
|
tui: PiCustomOverlayFactoryTui,
|
|
85
89
|
_theme: unknown,
|
|
@@ -96,14 +100,31 @@ export function openSessionPicker(
|
|
|
96
100
|
};
|
|
97
101
|
// Re-render on store changes so newly-started runs appear and
|
|
98
102
|
// status icons refresh without the user having to press a key.
|
|
99
|
-
|
|
103
|
+
// Read one memoized payload-free projection per render rather than
|
|
104
|
+
// caching a snapshot from the subscription callback: the
|
|
105
|
+
// invalidation channel carries no snapshot, so a cached one would
|
|
106
|
+
// go stale and newly-started runs would never appear.
|
|
107
|
+
const selectRows = (): ReturnType<typeof selectRunsForPicker> => {
|
|
108
|
+
const snapshot = readGraphStoreSnapshot(store);
|
|
109
|
+
const resumeCandidateLookup =
|
|
110
|
+
intent === "resume" ? resumeCandidateCache({ ...snapshot, runs: store.runs() }) : undefined;
|
|
111
|
+
return selectRunsForPicker(
|
|
112
|
+
snapshot.runs,
|
|
113
|
+
state.query,
|
|
114
|
+
state.includeAll,
|
|
115
|
+
Date.now(),
|
|
116
|
+
intent,
|
|
117
|
+
resumeCandidateLookup,
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
unsubscribe = subscribeStoreInvalidation(store, () => tui.requestRender?.());
|
|
100
121
|
return {
|
|
101
122
|
render: (width: number) => {
|
|
102
|
-
const rows =
|
|
123
|
+
const rows = selectRows();
|
|
103
124
|
return renderSessionPicker({ width, theme, rows, state });
|
|
104
125
|
},
|
|
105
126
|
handleInput: (data: string) => {
|
|
106
|
-
const rows =
|
|
127
|
+
const rows = selectRows();
|
|
107
128
|
const action = handleSessionPickerInput(data, state, rows);
|
|
108
129
|
if (action.kind === "noop") {
|
|
109
130
|
tui.requestRender?.();
|
|
@@ -19,11 +19,14 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import { keyText } from "@bastani/atomic";
|
|
22
|
+
import { isWorkflowRunResumable, type WorkflowRunResumeCandidate } from "../durable/resume-eligibility.js";
|
|
22
23
|
import { isTopLevelWorkflowRun } from "../shared/run-visibility.js";
|
|
23
|
-
import type { RunSnapshot } from "../shared/store-types.js";
|
|
24
|
+
import type { RunSnapshot, StoreSnapshot } from "../shared/store-types.js";
|
|
24
25
|
import { elapsedRunMs } from "../shared/timing.js";
|
|
26
|
+
import { workflowRunResumeCandidate } from "../shared/workflow-artifacts.js";
|
|
25
27
|
import { BOLD, hexBg, hexToAnsi, RESET } from "./color-utils.js";
|
|
26
28
|
import type { GraphTheme } from "./graph-theme.js";
|
|
29
|
+
import { type IdentifierLine, wrapIdentifierLines } from "./run-identity-rows.js";
|
|
27
30
|
import { fmtDuration, statusColor, statusIcon } from "./status-helpers.js";
|
|
28
31
|
import { Key, matchesKey, truncateToWidth, visibleWidth } from "./text-helpers.js";
|
|
29
32
|
|
|
@@ -55,23 +58,54 @@ export interface PickerRow {
|
|
|
55
58
|
readonly bucket: "active" | "terminal";
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
|
|
61
|
+
export type ResumeCandidateProbe = (run: RunSnapshot) => WorkflowRunResumeCandidate;
|
|
62
|
+
export type ResumeCandidateLookup = (run: RunSnapshot) => WorkflowRunResumeCandidate;
|
|
63
|
+
|
|
64
|
+
/** Cache resume probes for one store revision so render and input share the same filesystem/backend work. */
|
|
65
|
+
export function createSessionPickerResumeCandidateCache(
|
|
66
|
+
probe: ResumeCandidateProbe = workflowRunResumeCandidate,
|
|
67
|
+
): (snapshot: StoreSnapshot) => ResumeCandidateLookup {
|
|
68
|
+
let cachedVersion: number | undefined;
|
|
69
|
+
let cachedRuns: readonly RunSnapshot[] | undefined;
|
|
70
|
+
let sourceRuns = new Map<string, RunSnapshot>();
|
|
71
|
+
let candidates = new Map<string, WorkflowRunResumeCandidate>();
|
|
72
|
+
|
|
73
|
+
return (snapshot: StoreSnapshot): ResumeCandidateLookup => {
|
|
74
|
+
if (cachedVersion !== snapshot.version || cachedRuns !== snapshot.runs) {
|
|
75
|
+
cachedVersion = snapshot.version;
|
|
76
|
+
cachedRuns = snapshot.runs;
|
|
77
|
+
sourceRuns = new Map(snapshot.runs.map((run) => [run.id, run]));
|
|
78
|
+
candidates = new Map();
|
|
79
|
+
}
|
|
80
|
+
return (run: RunSnapshot): WorkflowRunResumeCandidate => {
|
|
81
|
+
const cached = candidates.get(run.id);
|
|
82
|
+
if (cached !== undefined) return cached;
|
|
83
|
+
const candidate = probe(sourceRuns.get(run.id) ?? run);
|
|
84
|
+
candidates.set(run.id, candidate);
|
|
85
|
+
return candidate;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
}
|
|
59
89
|
|
|
90
|
+
const RECENT_WINDOW_MS = 60 * 60 * 1000;
|
|
60
91
|
/**
|
|
61
92
|
* Slice runs into picker rows. Active = `endedAt === undefined`. Terminal =
|
|
62
|
-
* retained terminal runs.
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* Sort: active first (newest start last in the list = bottom-of-pane),
|
|
66
|
-
* then terminal newest-end first.
|
|
93
|
+
* retained terminal runs. Resume intent uses the shared resumability predicate;
|
|
94
|
+
* connect/attach and the legacy status list retain their broader listing.
|
|
95
|
+
* With `includeAll` false, terminal rows are limited to the recent-ended window.
|
|
67
96
|
*/
|
|
68
97
|
export function selectRunsForPicker(
|
|
69
98
|
runs: readonly RunSnapshot[],
|
|
70
99
|
query: string,
|
|
71
100
|
includeAll: boolean,
|
|
72
101
|
now: number = Date.now(),
|
|
102
|
+
intent: "connect" | "resume" | "pause" = "connect",
|
|
103
|
+
resumeCandidateLookup?: ResumeCandidateLookup,
|
|
73
104
|
): PickerRow[] {
|
|
74
105
|
const q = query.trim().toLowerCase();
|
|
106
|
+
// Incremental search, not id resolution. The user narrows a list and then
|
|
107
|
+
// selects a row, and the selected run's full id is what gets acted on, so
|
|
108
|
+
// prefix matching here never targets a run by a truncated id.
|
|
75
109
|
const matches = (r: RunSnapshot): boolean => {
|
|
76
110
|
if (!q) return true;
|
|
77
111
|
return r.name.toLowerCase().includes(q) || r.id.startsWith(q);
|
|
@@ -82,7 +116,10 @@ export function selectRunsForPicker(
|
|
|
82
116
|
for (const r of runs) {
|
|
83
117
|
if (!isTopLevelWorkflowRun(r)) continue;
|
|
84
118
|
if (!matches(r)) continue;
|
|
85
|
-
|
|
119
|
+
if (intent === "resume") {
|
|
120
|
+
const candidate = resumeCandidateLookup?.(r) ?? workflowRunResumeCandidate(r);
|
|
121
|
+
if (!isWorkflowRunResumable(candidate)) continue;
|
|
122
|
+
}
|
|
86
123
|
const endedAt = r.endedAt;
|
|
87
124
|
if (endedAt === undefined) {
|
|
88
125
|
active.push({ run: r, bucket: "active" });
|
|
@@ -124,7 +161,8 @@ function renderHeader(width: number, theme: GraphTheme): string {
|
|
|
124
161
|
const inner = Math.max(4, width - 2);
|
|
125
162
|
const border = hexToAnsi(theme.border);
|
|
126
163
|
const accent = hexToAnsi(theme.accent);
|
|
127
|
-
const
|
|
164
|
+
const title = truncateToWidth(TITLE, Math.max(1, inner - 2), "…");
|
|
165
|
+
const padded = ` ${title} `;
|
|
128
166
|
const padLen = Math.max(0, inner - visibleWidth(padded));
|
|
129
167
|
const left = Math.min(2, padLen);
|
|
130
168
|
const right = padLen - left;
|
|
@@ -219,45 +257,60 @@ function stageProgress(run: RunSnapshot): string {
|
|
|
219
257
|
return `${done}/${total} stages`;
|
|
220
258
|
}
|
|
221
259
|
|
|
222
|
-
function renderRunRow(row: PickerRow, isSelected: boolean, inner: number, theme: GraphTheme, now: number): string {
|
|
260
|
+
function renderRunRow(row: PickerRow, isSelected: boolean, inner: number, theme: GraphTheme, now: number): string[] {
|
|
223
261
|
const border = hexToAnsi(theme.border);
|
|
262
|
+
const panelBg = hexBg(theme.bg);
|
|
224
263
|
const run = row.run;
|
|
225
264
|
const icon = statusIcon(run.status);
|
|
226
|
-
const
|
|
265
|
+
const iconColor = hexToAnsi(statusColor(run.status, theme));
|
|
266
|
+
const dim = hexToAnsi(theme.dim);
|
|
267
|
+
const text = hexToAnsi(theme.text);
|
|
268
|
+
const muted = hexToAnsi(theme.textMuted);
|
|
269
|
+
|
|
270
|
+
// The full identifier owns its own row. It is never ellipsized; narrow
|
|
271
|
+
// overlays wrap it into continuation rows so the box remains intact.
|
|
272
|
+
const idRows = wrapIdentifierLines(run.id, inner, ` ${icon} `, " ");
|
|
273
|
+
const renderIdRow = ({ prefix, chunk }: IdentifierLine, index: number): string => {
|
|
274
|
+
if (isSelected) {
|
|
275
|
+
return `${border}│${RESET}${hexBg(theme.accent)}${hexToAnsi(theme.backgroundElement)}${BOLD}${padTo(
|
|
276
|
+
`${prefix}${chunk}`,
|
|
277
|
+
inner,
|
|
278
|
+
)}${RESET}${border}│${RESET}`;
|
|
279
|
+
}
|
|
280
|
+
const content =
|
|
281
|
+
index === 0
|
|
282
|
+
? ` ${iconColor}${icon}${RESET}${panelBg} ${dim}${chunk}${RESET}${panelBg}`
|
|
283
|
+
: ` ${dim}${chunk}${RESET}${panelBg}`;
|
|
284
|
+
return `${border}│${RESET}${panelBg}${padTo(content, inner)}${border}│${RESET}`;
|
|
285
|
+
};
|
|
286
|
+
|
|
227
287
|
const elapsed = fmtElapsed(run, now);
|
|
228
288
|
const progress = stageProgress(run);
|
|
229
|
-
|
|
230
|
-
// Layout columns: glyph(1) idShort(8) name(flex) elapsed(R) progress(R).
|
|
231
|
-
// Name budgeting is done by visible cell width so wide workflow names
|
|
232
|
-
// cannot push the elapsed/progress columns through the right border.
|
|
233
289
|
const elapsedCol = elapsed.padStart(8, " ");
|
|
234
290
|
const progressCol = progress.padStart(10, " ");
|
|
235
291
|
const rightPlain = `${elapsedCol} ${progressCol} `;
|
|
236
|
-
const
|
|
237
|
-
const
|
|
292
|
+
const rightBudget = Math.max(1, inner - 5);
|
|
293
|
+
const rightVisible = truncateToWidth(rightPlain, rightBudget, "…");
|
|
294
|
+
const right =
|
|
295
|
+
visibleWidth(rightVisible) < visibleWidth(rightPlain)
|
|
296
|
+
? `${dim}${rightVisible}${RESET}${panelBg}`
|
|
297
|
+
: `${muted}${elapsedCol}${RESET}${panelBg} ${dim}${progressCol}${RESET}${panelBg} `;
|
|
298
|
+
const nameBudget = Math.max(1, inner - 3 - visibleWidth(rightVisible) - 1);
|
|
238
299
|
const name = truncateToWidth(run.name, nameBudget, "…");
|
|
239
300
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const text = hexToAnsi(theme.text);
|
|
254
|
-
const muted = hexToAnsi(theme.textMuted);
|
|
255
|
-
|
|
256
|
-
const left = ` ${iconColor}${icon}${RESET}${panelBg} ${dim}${idShort}${RESET}${panelBg} ${text}${name}${RESET}${panelBg}`;
|
|
257
|
-
const right = `${muted}${elapsedCol}${RESET}${panelBg} ${dim}${progressCol}${RESET}${panelBg} `;
|
|
258
|
-
const gap = Math.max(1, inner - visibleWidth(left) - visibleWidth(right));
|
|
259
|
-
const content = `${left}${" ".repeat(gap)}${right}`;
|
|
260
|
-
return `${border}│${RESET}${panelBg}${padTo(content, inner)}${RESET}${border}│${RESET}`;
|
|
301
|
+
const nameRow = isSelected
|
|
302
|
+
? `${border}│${RESET}${hexBg(theme.accent)}${hexToAnsi(theme.backgroundElement)}${BOLD}${padTo(
|
|
303
|
+
` ${name}${" ".repeat(Math.max(1, inner - 3 - visibleWidth(name) - visibleWidth(rightVisible)))}${rightVisible}`,
|
|
304
|
+
inner,
|
|
305
|
+
)}${RESET}${border}│${RESET}`
|
|
306
|
+
: `${border}│${RESET}${panelBg}${padTo(
|
|
307
|
+
` ${text}${name}${RESET}${panelBg}${" ".repeat(
|
|
308
|
+
Math.max(1, inner - 3 - visibleWidth(name) - visibleWidth(right)),
|
|
309
|
+
)}${right}`,
|
|
310
|
+
inner,
|
|
311
|
+
)}${border}│${RESET}`;
|
|
312
|
+
|
|
313
|
+
return [...idRows.map(renderIdRow), nameRow];
|
|
261
314
|
}
|
|
262
315
|
|
|
263
316
|
function renderEmptyState(inner: number, theme: GraphTheme): string {
|
|
@@ -269,12 +322,12 @@ function renderEmptyState(inner: number, theme: GraphTheme): string {
|
|
|
269
322
|
return `${border}│${RESET}${panelBg}${padTo(content, inner)}${RESET}${border}│${RESET}`;
|
|
270
323
|
}
|
|
271
324
|
|
|
272
|
-
const VIEWPORT =
|
|
325
|
+
const VIEWPORT = 5;
|
|
273
326
|
|
|
274
327
|
export function renderSessionPicker(opts: SessionPickerRenderOpts): string[] {
|
|
275
328
|
const { width, theme, rows, state } = opts;
|
|
276
329
|
const now = opts.now ?? Date.now();
|
|
277
|
-
const inner = Math.max(
|
|
330
|
+
const inner = Math.max(4, width - 2);
|
|
278
331
|
|
|
279
332
|
const lines: string[] = [];
|
|
280
333
|
lines.push(renderHeader(width, theme));
|
|
@@ -303,7 +356,7 @@ export function renderSessionPicker(opts: SessionPickerRenderOpts): string[] {
|
|
|
303
356
|
prevBucket = row.bucket;
|
|
304
357
|
}
|
|
305
358
|
const absIndex = Math.max(0, start) + i;
|
|
306
|
-
lines.push(renderRunRow(row, absIndex === sel, inner, theme, now));
|
|
359
|
+
lines.push(...renderRunRow(row, absIndex === sel, inner, theme, now));
|
|
307
360
|
}
|
|
308
361
|
lines.push(renderBlankRow(inner, theme));
|
|
309
362
|
lines.push(renderBottomBorder(width, theme));
|
|
@@ -2,7 +2,12 @@ import { Box, Text } from "@earendil-works/pi-tui";
|
|
|
2
2
|
import type { PendingPrompt, StageSnapshot } from "../shared/store-types.js";
|
|
3
3
|
import { renderRoundedBoxLines } from "./chat-surface.js";
|
|
4
4
|
import { hexToAnsi, RESET } from "./color-utils.js";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
type PromptCardLayout,
|
|
7
|
+
renderPromptCardLayout,
|
|
8
|
+
renderPromptIdentityBanner,
|
|
9
|
+
renderPromptRunIdBanner,
|
|
10
|
+
} from "./prompt-card-render.js";
|
|
6
11
|
import { bannerLines, embedOrchestratorReturnHintInWidget } from "./stage-chat-view-footer-status.js";
|
|
7
12
|
import {
|
|
8
13
|
blankLine,
|
|
@@ -205,24 +210,46 @@ export function renderBlockedBody(
|
|
|
205
210
|
}
|
|
206
211
|
|
|
207
212
|
export function renderPromptBody(ctx: StageChatViewContext, width: number, budget: number): string[] {
|
|
208
|
-
const
|
|
209
|
-
if (
|
|
210
|
-
return fitPromptBodyLines(
|
|
213
|
+
const primitiveLayout = renderPrimitivePromptBody(ctx, width, budget);
|
|
214
|
+
if (primitiveLayout) {
|
|
215
|
+
return fitPromptBodyLines(
|
|
216
|
+
ctx,
|
|
217
|
+
embedPromptReturnHint(ctx, primitiveLayout.lines, width),
|
|
218
|
+
width,
|
|
219
|
+
budget,
|
|
220
|
+
Math.max(0, primitiveLayout.totalQuestionRows - primitiveLayout.visibleQuestionRows),
|
|
221
|
+
true,
|
|
222
|
+
primitiveLayout.visibleQuestionRows,
|
|
223
|
+
);
|
|
211
224
|
}
|
|
212
225
|
|
|
213
226
|
const state = ctx.promptState;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
:
|
|
222
|
-
|
|
227
|
+
if (!state) return fitPromptBodyLines(ctx, [], width, budget);
|
|
228
|
+
const layout = renderPromptCardLayout({
|
|
229
|
+
state,
|
|
230
|
+
theme: ctx.theme,
|
|
231
|
+
width,
|
|
232
|
+
cursorOn: ctx.focused,
|
|
233
|
+
identity: { runId: ctx.runId, name: ctx.workflowName },
|
|
234
|
+
maxRows: budget,
|
|
235
|
+
messageOffset: ctx.promptScrollOffset,
|
|
236
|
+
});
|
|
237
|
+
return fitPromptBodyLines(
|
|
238
|
+
ctx,
|
|
239
|
+
embedPromptReturnHint(ctx, layout.lines, width),
|
|
240
|
+
width,
|
|
241
|
+
budget,
|
|
242
|
+
Math.max(0, layout.totalQuestionRows - layout.visibleQuestionRows),
|
|
243
|
+
true,
|
|
244
|
+
layout.visibleQuestionRows,
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function embedPromptReturnHint(ctx: StageChatViewContext, lines: readonly string[], width: number): string[] {
|
|
249
|
+
return lines.length < 3 ? [...lines] : embedOrchestratorReturnHintInWidget(ctx, lines, width);
|
|
223
250
|
}
|
|
224
251
|
|
|
225
|
-
function renderPrimitivePromptBody(ctx: StageChatViewContext, width: number):
|
|
252
|
+
function renderPrimitivePromptBody(ctx: StageChatViewContext, width: number, budget: number): PromptCardLayout | null {
|
|
226
253
|
const state = ctx.promptState;
|
|
227
254
|
const editor = ctx.promptEditor;
|
|
228
255
|
if (!state || !editor) return null;
|
|
@@ -230,19 +257,151 @@ function renderPrimitivePromptBody(ctx: StageChatViewContext, width: number): st
|
|
|
230
257
|
setEditorBorderColor(editor, (text) => hexToAnsi(ctx.theme.accent) + text + RESET);
|
|
231
258
|
|
|
232
259
|
const innerWidth = Math.max(2, width - 2);
|
|
233
|
-
const
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
260
|
+
const messageLines = new Text(paint(state.prompt.message, ctx.theme.text), 2, 0).render(innerWidth);
|
|
261
|
+
const responseLines = new Text(paint("response", ctx.theme.textMuted, { bold: true }), 2, 0).render(innerWidth);
|
|
262
|
+
const editorLines = editor.render(Math.max(20, innerWidth - 4)).map((line) => ` ${line}`);
|
|
263
|
+
const hintLines = new Text(renderHintsForPrompt(state.prompt.kind, ctx.theme), 2, 0).render(innerWidth);
|
|
264
|
+
const identity = { runId: ctx.runId, name: ctx.workflowName };
|
|
265
|
+
const unattributed = renderPrimitivePromptBlockLayout(
|
|
266
|
+
ctx,
|
|
267
|
+
width,
|
|
268
|
+
budget,
|
|
269
|
+
"AWAITING INPUT",
|
|
270
|
+
messageLines,
|
|
271
|
+
responseLines,
|
|
272
|
+
editorLines,
|
|
273
|
+
hintLines,
|
|
274
|
+
);
|
|
275
|
+
const minimumPromptRows = 2 + editorLines.length + hintLines.length + (messageLines.length > 0 ? 1 : 0);
|
|
276
|
+
// Same degradation ladder as the standard prompt surface: two identity rows,
|
|
277
|
+
// then the run id alone, then no banner. The middle rung matters most here,
|
|
278
|
+
// because the editor and hint rows leave the least room for attribution.
|
|
279
|
+
const banners = [
|
|
280
|
+
renderPromptIdentityBanner(identity, ctx.theme, width),
|
|
281
|
+
renderPromptRunIdBanner(identity, ctx.theme, width),
|
|
282
|
+
];
|
|
283
|
+
for (const banner of banners) {
|
|
284
|
+
const promptBudget = Math.max(0, budget - banner.length);
|
|
285
|
+
const attributed = renderPrimitivePromptBlockLayout(
|
|
286
|
+
ctx,
|
|
287
|
+
width,
|
|
288
|
+
promptBudget,
|
|
289
|
+
"",
|
|
290
|
+
messageLines,
|
|
291
|
+
responseLines,
|
|
292
|
+
editorLines,
|
|
293
|
+
hintLines,
|
|
294
|
+
);
|
|
295
|
+
if (
|
|
296
|
+
banner.length + minimumPromptRows > budget ||
|
|
297
|
+
attributed.visibleQuestionRows === 0 ||
|
|
298
|
+
attributed.visibleQuestionRows < unattributed.visibleQuestionRows
|
|
299
|
+
) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
lines: [...banner, ...attributed.lines],
|
|
304
|
+
totalQuestionRows: attributed.totalQuestionRows,
|
|
305
|
+
visibleQuestionRows: attributed.visibleQuestionRows,
|
|
306
|
+
};
|
|
240
307
|
}
|
|
241
|
-
|
|
242
|
-
|
|
308
|
+
return unattributed;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function renderPrimitivePromptBlockLayout(
|
|
312
|
+
ctx: StageChatViewContext,
|
|
313
|
+
width: number,
|
|
314
|
+
maxRows: number,
|
|
315
|
+
title: string,
|
|
316
|
+
messageLines: readonly string[],
|
|
317
|
+
responseLines: readonly string[],
|
|
318
|
+
editorLines: readonly string[],
|
|
319
|
+
hintLines: readonly string[],
|
|
320
|
+
): PromptCardLayout {
|
|
321
|
+
const totalQuestionRows = messageLines.length;
|
|
322
|
+
if (maxRows === 0) return { lines: [], totalQuestionRows, visibleQuestionRows: 0 };
|
|
323
|
+
const fixedRows = 2 + editorLines.length + hintLines.length;
|
|
324
|
+
if (maxRows < fixedRows + (totalQuestionRows > 0 ? 1 : 0)) {
|
|
325
|
+
return renderReducedPrimitivePrompt(ctx, width, maxRows, messageLines, title);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
let remaining = maxRows - fixedRows;
|
|
329
|
+
let messageCount = totalQuestionRows > 0 ? 1 : 0;
|
|
330
|
+
remaining -= messageCount;
|
|
331
|
+
const includeResponse = remaining >= responseLines.length;
|
|
332
|
+
if (includeResponse) remaining -= responseLines.length;
|
|
333
|
+
const extraMessageRows = Math.min(remaining, Math.max(0, totalQuestionRows - messageCount));
|
|
334
|
+
messageCount += extraMessageRows;
|
|
335
|
+
remaining -= extraMessageRows;
|
|
336
|
+
const includeBlank = remaining > 0;
|
|
337
|
+
const safeOffset = Math.min(ctx.promptScrollOffset, Math.max(0, totalQuestionRows - messageCount));
|
|
338
|
+
const bodyLines = [
|
|
339
|
+
...messageLines.slice(safeOffset, safeOffset + messageCount),
|
|
340
|
+
...(includeResponse ? responseLines : []),
|
|
341
|
+
...editorLines,
|
|
342
|
+
...(includeBlank ? [""] : []),
|
|
343
|
+
...hintLines,
|
|
344
|
+
];
|
|
345
|
+
return {
|
|
346
|
+
lines: renderPrimitivePromptBlock(ctx, width, title, bodyLines),
|
|
347
|
+
totalQuestionRows,
|
|
348
|
+
visibleQuestionRows: messageCount,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
243
351
|
|
|
352
|
+
function renderReducedPrimitivePrompt(
|
|
353
|
+
ctx: StageChatViewContext,
|
|
354
|
+
width: number,
|
|
355
|
+
maxRows: number,
|
|
356
|
+
messageLines: readonly string[],
|
|
357
|
+
title: string,
|
|
358
|
+
): PromptCardLayout {
|
|
359
|
+
const totalQuestionRows = messageLines.length;
|
|
360
|
+
const compactTitle = "AWAITING INPUT";
|
|
361
|
+
if (maxRows === 1) {
|
|
362
|
+
return {
|
|
363
|
+
lines: [paint(`${compactTitle} · enter Submit`, ctx.theme.textMuted, { bold: true })],
|
|
364
|
+
totalQuestionRows,
|
|
365
|
+
visibleQuestionRows: 0,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
if (maxRows === 2) {
|
|
369
|
+
const closed = renderPrimitivePromptBlock(ctx, width, compactTitle, []);
|
|
370
|
+
return {
|
|
371
|
+
lines: [closed[0]!, closed.at(-1)!],
|
|
372
|
+
totalQuestionRows,
|
|
373
|
+
visibleQuestionRows: 0,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const availableBodyRows = maxRows - 2;
|
|
378
|
+
const supportingRows = availableBodyRows >= 3 ? 2 : Math.max(0, availableBodyRows - 1);
|
|
379
|
+
const messageCount = Math.min(totalQuestionRows, Math.max(1, availableBodyRows - supportingRows));
|
|
380
|
+
const safeOffset = Math.min(ctx.promptScrollOffset, Math.max(0, totalQuestionRows - messageCount));
|
|
381
|
+
const responseLine = paint(
|
|
382
|
+
availableBodyRows >= 3 ? " response" : " response · enter Submit",
|
|
383
|
+
ctx.theme.textMuted,
|
|
384
|
+
{ bold: true },
|
|
385
|
+
);
|
|
386
|
+
const hintLine = renderHintsForPrompt(ctx.promptState?.prompt.kind ?? "input", ctx.theme);
|
|
387
|
+
return {
|
|
388
|
+
lines: renderPrimitivePromptBlock(ctx, width, maxRows < 5 ? compactTitle : title || "AWAITING INPUT", [
|
|
389
|
+
...messageLines.slice(safeOffset, safeOffset + messageCount),
|
|
390
|
+
...(availableBodyRows >= 2 ? [responseLine] : []),
|
|
391
|
+
...(availableBodyRows >= 3 ? [hintLine] : []),
|
|
392
|
+
]),
|
|
393
|
+
totalQuestionRows,
|
|
394
|
+
visibleQuestionRows: messageCount,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
function renderPrimitivePromptBlock(
|
|
398
|
+
ctx: StageChatViewContext,
|
|
399
|
+
width: number,
|
|
400
|
+
title: string,
|
|
401
|
+
bodyLines: readonly string[],
|
|
402
|
+
): string[] {
|
|
244
403
|
return renderRoundedBoxLines({
|
|
245
|
-
title
|
|
404
|
+
title,
|
|
246
405
|
bodyLines,
|
|
247
406
|
width,
|
|
248
407
|
theme: ctx.theme,
|
|
@@ -255,10 +414,15 @@ export function fitPromptBodyLines(
|
|
|
255
414
|
lines: readonly string[],
|
|
256
415
|
width: number,
|
|
257
416
|
budget: number,
|
|
417
|
+
maxScroll = Math.max(0, lines.length - budget),
|
|
418
|
+
scrollApplied = false,
|
|
419
|
+
visibleRows = Math.min(lines.length, budget),
|
|
258
420
|
): string[] {
|
|
259
|
-
ctx.promptMaxScroll = Math.max(0,
|
|
421
|
+
ctx.promptMaxScroll = Math.max(0, maxScroll);
|
|
422
|
+
ctx.promptVisibleRows = Math.max(0, visibleRows);
|
|
260
423
|
ctx.promptScrollOffset = Math.max(0, Math.min(ctx.promptScrollOffset, ctx.promptMaxScroll));
|
|
261
|
-
const
|
|
424
|
+
const start = scrollApplied ? 0 : ctx.promptScrollOffset;
|
|
425
|
+
const framed = lines.slice(start, start + budget);
|
|
262
426
|
while (framed.length < budget) framed.push(blankLine(width));
|
|
263
427
|
return framed;
|
|
264
428
|
}
|
|
@@ -2,6 +2,7 @@ import type { AgentSession } from "@bastani/atomic";
|
|
|
2
2
|
import { Box, Text } from "@earendil-works/pi-tui";
|
|
3
3
|
import type { StageSnapshot } from "../shared/store-types.js";
|
|
4
4
|
import { hexToAnsi, RESET } from "./color-utils.js";
|
|
5
|
+
import { wrapIdentifierLines } from "./run-identity-rows.js";
|
|
5
6
|
import {
|
|
6
7
|
bgFn,
|
|
7
8
|
blendBg,
|
|
@@ -17,30 +18,63 @@ import { truncateToWidth, visibleWidth } from "./text-helpers.js";
|
|
|
17
18
|
export function renderHeader(ctx: StageChatViewContext, width: number, stage: StageSnapshot | undefined): string[] {
|
|
18
19
|
const t = ctx.theme;
|
|
19
20
|
const stageName = stage?.name ?? "stage";
|
|
20
|
-
const
|
|
21
|
+
const sid = ctx.handle?.sessionId ?? stage?.sessionId;
|
|
22
|
+
const prefixWidth = visibleWidth(" STAGE ");
|
|
23
|
+
const separatorWidth = visibleWidth(" / ");
|
|
24
|
+
const meta = sid ? `session ${sid}` : "";
|
|
25
|
+
const rightWidth = meta ? visibleWidth(meta) + 1 : 0;
|
|
26
|
+
const singleRowNameBudget = width - prefixWidth - separatorWidth - rightWidth - (meta ? 1 : 0);
|
|
27
|
+
|
|
28
|
+
const fullNameWidth = visibleWidth(ctx.workflowName) + visibleWidth(stageName);
|
|
29
|
+
if (!sid || singleRowNameBudget >= fullNameWidth) {
|
|
30
|
+
const names = fitHeaderNames(ctx.workflowName, stageName, Math.max(2, singleRowNameBudget));
|
|
31
|
+
const left = renderHeaderLeft(ctx, names.workflow, names.stage);
|
|
32
|
+
const right = meta ? `${paint(meta, t.dim)} ` : "";
|
|
33
|
+
const gap = Math.max(0, width - visibleWidth(left) - visibleWidth(right));
|
|
34
|
+
return [left + " ".repeat(gap) + right];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const names = fitHeaderNames(ctx.workflowName, stageName, Math.max(2, width - prefixWidth - separatorWidth));
|
|
38
|
+
const left = renderHeaderLeft(ctx, names.workflow, names.stage);
|
|
39
|
+
const lines = [left + " ".repeat(Math.max(0, width - visibleWidth(left)))];
|
|
40
|
+
if (visibleWidth(meta) + 1 <= width) {
|
|
41
|
+
lines.push(`${" ".repeat(Math.max(0, width - visibleWidth(meta) - 1))}${paint(meta, t.dim)} `);
|
|
42
|
+
return lines;
|
|
43
|
+
}
|
|
44
|
+
for (const row of wrapIdentifierLines(sid, width, " ", " ")) {
|
|
45
|
+
const value = `${row.prefix}${paint(row.chunk, t.dim)}`;
|
|
46
|
+
lines.push(value + " ".repeat(Math.max(0, width - visibleWidth(value))));
|
|
47
|
+
}
|
|
48
|
+
return lines;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function renderHeaderLeft(ctx: StageChatViewContext, workflowName: string, stageName: string): string {
|
|
52
|
+
const t = ctx.theme;
|
|
53
|
+
return (
|
|
21
54
|
paint(" ", t.mauve, { bold: true }) +
|
|
22
55
|
paint("STAGE", t.textMuted, { bold: true }) +
|
|
23
56
|
" " +
|
|
24
|
-
paint(
|
|
57
|
+
paint(workflowName, t.textMuted) +
|
|
25
58
|
paint(" / ", t.dim) +
|
|
26
|
-
paint(stageName, t.text, { bold: true })
|
|
27
|
-
|
|
28
|
-
const right = meta ? `${paint(meta, t.dim)} ` : "";
|
|
29
|
-
const leftW = visibleWidth(ctx.workflowName) + visibleWidth(stageName) + visibleWidth(" STAGE / ") + 1;
|
|
30
|
-
const rightW = visibleWidth(meta) + (meta ? 1 : 0);
|
|
31
|
-
const gap = Math.max(1, width - leftW - rightW);
|
|
32
|
-
return [left + " ".repeat(gap) + right];
|
|
59
|
+
paint(stageName, t.text, { bold: true })
|
|
60
|
+
);
|
|
33
61
|
}
|
|
34
62
|
|
|
35
|
-
function
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
63
|
+
function fitHeaderNames(workflowName: string, stageName: string, budget: number): { workflow: string; stage: string } {
|
|
64
|
+
const available = Math.max(2, budget);
|
|
65
|
+
const workflowWidth = visibleWidth(workflowName);
|
|
66
|
+
const stageWidth = visibleWidth(stageName);
|
|
67
|
+
let workflowBudget = Math.min(workflowWidth, Math.max(1, Math.ceil(available / 2)));
|
|
68
|
+
let stageBudget = Math.min(stageWidth, Math.max(1, available - workflowBudget));
|
|
69
|
+
let remaining = available - workflowBudget - stageBudget;
|
|
70
|
+
const workflowExtra = Math.min(remaining, Math.max(0, workflowWidth - workflowBudget));
|
|
71
|
+
workflowBudget += workflowExtra;
|
|
72
|
+
remaining -= workflowExtra;
|
|
73
|
+
stageBudget += Math.min(remaining, Math.max(0, stageWidth - stageBudget));
|
|
74
|
+
return {
|
|
75
|
+
workflow: truncateToWidth(workflowName, workflowBudget, "…"),
|
|
76
|
+
stage: truncateToWidth(stageName, stageBudget, "…"),
|
|
77
|
+
};
|
|
44
78
|
}
|
|
45
79
|
|
|
46
80
|
export function sepRule(ctx: StageChatViewContext, width: number): string {
|
|
@@ -56,7 +56,7 @@ export function handleStageChatInput(ctx: StageChatViewContext, data: string): b
|
|
|
56
56
|
return ctx.chatHost.handleInput(data);
|
|
57
57
|
}
|
|
58
58
|
if (isAbortableStreamingSession(ctx)) {
|
|
59
|
-
void ctx.chatHost.interrupt();
|
|
59
|
+
void ctx.chatHost.interrupt({ restoreQueuedMessages: true });
|
|
60
60
|
return true;
|
|
61
61
|
}
|
|
62
62
|
ctx.onClose();
|
|
@@ -160,8 +160,8 @@ function handlePromptInput(ctx: StageChatViewContext, data: string): void {
|
|
|
160
160
|
function handlePromptScrollInput(ctx: StageChatViewContext, data: string, includeKeyboard = true): boolean {
|
|
161
161
|
const mouse = parseTerminalMouseInput(data);
|
|
162
162
|
const wheelDirection = mouse ? terminalMouseWheelDirection(mouse) : null;
|
|
163
|
-
const
|
|
164
|
-
|
|
163
|
+
const wheelStep = promptScrollStep(ctx, PROMPT_SCROLL_STEP_ROWS);
|
|
164
|
+
const wheelDeltaRows = wheelDirection === "up" ? -wheelStep : wheelDirection === "down" ? wheelStep : 0;
|
|
165
165
|
if (wheelDeltaRows !== 0) {
|
|
166
166
|
scrollPromptBy(ctx, wheelDeltaRows);
|
|
167
167
|
return true;
|
|
@@ -169,11 +169,11 @@ function handlePromptScrollInput(ctx: StageChatViewContext, data: string, includ
|
|
|
169
169
|
if (mouse) return true;
|
|
170
170
|
if (!includeKeyboard) return false;
|
|
171
171
|
if (matchesKey(data, "pageUp")) {
|
|
172
|
-
scrollPromptBy(ctx, -promptPageSize(ctx));
|
|
172
|
+
scrollPromptBy(ctx, -promptScrollStep(ctx, promptPageSize(ctx)));
|
|
173
173
|
return true;
|
|
174
174
|
}
|
|
175
175
|
if (matchesKey(data, "pageDown")) {
|
|
176
|
-
scrollPromptBy(ctx, promptPageSize(ctx));
|
|
176
|
+
scrollPromptBy(ctx, promptScrollStep(ctx, promptPageSize(ctx)));
|
|
177
177
|
return true;
|
|
178
178
|
}
|
|
179
179
|
if (!ctx.promptEditor && matchesKey(data, "home")) {
|
|
@@ -189,6 +189,10 @@ function handlePromptScrollInput(ctx: StageChatViewContext, data: string, includ
|
|
|
189
189
|
return false;
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
function promptScrollStep(ctx: StageChatViewContext, requestedRows: number): number {
|
|
193
|
+
return Math.max(1, Math.min(requestedRows, Math.max(1, ctx.promptVisibleRows)));
|
|
194
|
+
}
|
|
195
|
+
|
|
192
196
|
function scrollPromptBy(ctx: StageChatViewContext, deltaRows: number): void {
|
|
193
197
|
ctx.promptScrollOffset = Math.max(0, Math.min(ctx.promptMaxScroll, ctx.promptScrollOffset + deltaRows));
|
|
194
198
|
ctx.requestRender?.();
|