@bastani/atomic 0.8.21-0 → 0.8.22-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -9
- package/dist/builtin/intercom/broker/broker.ts +3 -3
- package/dist/builtin/intercom/config.ts +3 -3
- package/dist/builtin/intercom/index.ts +1 -1
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/ui/compose.ts +2 -2
- package/dist/builtin/mcp/host-html-template.ts +0 -3
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +13 -4
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +9 -9
- package/dist/builtin/subagents/agents/debugger.md +6 -6
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +1 -1
- package/dist/builtin/subagents/skills/browser-use/SKILL.md +234 -0
- package/dist/builtin/subagents/skills/browser-use/references/cdp-python.md +76 -0
- package/dist/builtin/subagents/skills/browser-use/references/multi-session.md +92 -0
- package/dist/builtin/subagents/skills/subagent/SKILL.md +4 -4
- package/dist/builtin/subagents/src/agents/skills.ts +19 -1
- package/dist/builtin/subagents/src/extension/index.ts +24 -22
- package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +7 -1
- package/dist/builtin/subagents/src/runs/background/async-execution.ts +23 -7
- package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +98 -3
- package/dist/builtin/subagents/src/runs/background/async-status.ts +3 -1
- package/dist/builtin/subagents/src/runs/background/run-status.ts +1 -1
- package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +3 -0
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +37 -12
- package/dist/builtin/subagents/src/runs/foreground/chain-clarify.ts +15 -15
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +26 -2
- package/dist/builtin/subagents/src/runs/shared/nested-render.ts +1 -1
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +7 -0
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +28 -1
- package/dist/builtin/subagents/src/shared/fast-mode.ts +80 -0
- package/dist/builtin/subagents/src/shared/formatters.ts +4 -2
- package/dist/builtin/subagents/src/shared/types.ts +4 -2
- package/dist/builtin/subagents/src/shared/utils.ts +3 -61
- package/dist/builtin/subagents/src/tui/render.ts +303 -157
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +101 -35
- package/dist/builtin/workflows/README.md +228 -41
- package/dist/builtin/workflows/builtin/deep-research-codebase.ts +535 -541
- package/dist/builtin/workflows/builtin/goal.ts +39 -25
- package/dist/builtin/workflows/builtin/open-claude-design.ts +66 -69
- package/dist/builtin/workflows/builtin/ralph.ts +21 -21
- package/dist/builtin/workflows/package.json +6 -5
- package/dist/builtin/workflows/skills/research-codebase/SKILL.md +1 -1
- package/dist/builtin/workflows/src/extension/background-ui-adapter.ts +2 -2
- package/dist/builtin/workflows/src/extension/discovery.ts +25 -146
- package/dist/builtin/workflows/src/extension/dispatcher.ts +72 -24
- package/dist/builtin/workflows/src/extension/hil-answer-notifications.ts +363 -0
- package/dist/builtin/workflows/src/extension/index.ts +690 -352
- package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +99 -62
- package/dist/builtin/workflows/src/extension/render-call.ts +2 -1
- package/dist/builtin/workflows/src/extension/render-result.ts +9 -3
- package/dist/builtin/workflows/src/extension/renderers.ts +5 -3
- package/dist/builtin/workflows/src/extension/runtime.ts +68 -33
- package/dist/builtin/workflows/src/extension/status-writer.ts +1 -1
- package/dist/builtin/workflows/src/extension/wiring.ts +34 -13
- package/dist/builtin/workflows/src/extension/workflow-module-loader.ts +142 -0
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +4 -4
- package/dist/builtin/workflows/src/index.ts +2 -0
- package/dist/builtin/workflows/src/intercom/result-intercom.ts +1 -1
- package/dist/builtin/workflows/src/runs/background/runner.ts +6 -4
- package/dist/builtin/workflows/src/runs/background/status.ts +45 -21
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +624 -52
- package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +1 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +80 -24
- package/dist/builtin/workflows/src/runs/shared/validate-inputs.ts +61 -24
- package/dist/builtin/workflows/src/runs/shared/workflow-runner.ts +32 -10
- package/dist/builtin/workflows/src/sdk-surface.ts +6 -0
- package/dist/builtin/workflows/src/shared/expanded-workflow-graph.ts +178 -0
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +92 -12
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +21 -3
- package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +1 -2
- package/dist/builtin/workflows/src/shared/run-visibility.ts +9 -0
- package/dist/builtin/workflows/src/shared/schema-introspection.ts +121 -0
- package/dist/builtin/workflows/src/shared/serializable.ts +132 -0
- package/dist/builtin/workflows/src/shared/stage-ui-broker.ts +91 -9
- package/dist/builtin/workflows/src/shared/store-types.ts +31 -3
- package/dist/builtin/workflows/src/shared/store.ts +58 -14
- package/dist/builtin/workflows/src/shared/types.ts +105 -40
- package/dist/builtin/workflows/src/tui/chat-surface-message.ts +129 -13
- package/dist/builtin/workflows/src/tui/chat-surface.ts +6 -1
- package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +3 -2
- package/dist/builtin/workflows/src/tui/graph-canvas.ts +1 -1
- package/dist/builtin/workflows/src/tui/graph-view.ts +91 -65
- package/dist/builtin/workflows/src/tui/inline-form-card.ts +1 -1
- package/dist/builtin/workflows/src/tui/inline-form-overlay.ts +3 -2
- package/dist/builtin/workflows/src/tui/inputs-overlay.ts +3 -2
- package/dist/builtin/workflows/src/tui/inputs-picker.ts +8 -7
- package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +2 -0
- package/dist/builtin/workflows/src/tui/node-card.ts +34 -8
- package/dist/builtin/workflows/src/tui/overlay-adapter.ts +4 -11
- package/dist/builtin/workflows/src/tui/prompt-card.ts +98 -50
- package/dist/builtin/workflows/src/tui/session-list.ts +7 -2
- package/dist/builtin/workflows/src/tui/session-picker.ts +2 -0
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +226 -55
- package/dist/builtin/workflows/src/tui/status-helpers.ts +2 -0
- package/dist/builtin/workflows/src/tui/store-widget-installer.ts +37 -158
- package/dist/builtin/workflows/src/tui/toast.ts +2 -2
- package/dist/builtin/workflows/src/tui/widget.ts +53 -12
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +270 -19
- package/dist/builtin/workflows/src/tui/workflow-notice-card.ts +184 -0
- package/dist/builtin/workflows/src/workflows/define-workflow.ts +138 -43
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +45 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +27 -9
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +196 -17
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +2 -2
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/codex-fast-mode.d.ts +36 -0
- package/dist/core/codex-fast-mode.d.ts.map +1 -0
- package/dist/core/codex-fast-mode.js +117 -0
- package/dist/core/codex-fast-mode.js.map +1 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +1 -1
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +1 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/extensions/index.d.ts +4 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -0
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +7 -2
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +23 -8
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/reactive-widget.d.ts +58 -0
- package/dist/core/extensions/reactive-widget.d.ts.map +1 -0
- package/dist/core/extensions/reactive-widget.js +182 -0
- package/dist/core/extensions/reactive-widget.js.map +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +1 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +26 -12
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/messages.d.ts +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +8 -2
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-registry.d.ts +4 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +11 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/resource-loader.d.ts +9 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +49 -21
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +22 -13
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +7 -5
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +5 -3
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +16 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +64 -5
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +7 -4
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/ask-user-question.js +2 -2
- package/dist/core/tools/ask-user-question/ask-user-question.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +12 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -1
- package/dist/modes/interactive/chat-input-actions.js.map +1 -1
- package/dist/modes/interactive/components/diff.d.ts.map +1 -1
- package/dist/modes/interactive/components/diff.js +0 -1
- package/dist/modes/interactive/components/diff.js.map +1 -1
- package/dist/modes/interactive/components/fast-mode-selector.d.ts +27 -0
- package/dist/modes/interactive/components/fast-mode-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/fast-mode-selector.js +105 -0
- package/dist/modes/interactive/components/fast-mode-selector.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +7 -12
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +132 -30
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +53 -6
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +3 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/docs/compaction.md +1 -1
- package/docs/custom-provider.md +2 -2
- package/docs/development.md +2 -2
- package/docs/docs.json +2 -2
- package/docs/extensions.md +18 -13
- package/docs/providers.md +5 -1
- package/docs/quickstart.md +5 -3
- package/docs/rpc.md +5 -5
- package/docs/sdk.md +12 -12
- package/docs/settings.md +18 -0
- package/docs/themes.md +6 -6
- package/docs/tui.md +20 -18
- package/docs/usage.md +2 -0
- package/docs/workflows.md +403 -39
- package/examples/extensions/qna.ts +2 -2
- package/package.json +4 -4
- package/dist/builtin/subagents/skills/playwright-cli/SKILL.md +0 -392
- package/dist/builtin/subagents/skills/playwright-cli/references/element-attributes.md +0 -23
- package/dist/builtin/subagents/skills/playwright-cli/references/playwright-tests.md +0 -39
- package/dist/builtin/subagents/skills/playwright-cli/references/request-mocking.md +0 -87
- package/dist/builtin/subagents/skills/playwright-cli/references/running-code.md +0 -241
- package/dist/builtin/subagents/skills/playwright-cli/references/session-management.md +0 -225
- package/dist/builtin/subagents/skills/playwright-cli/references/spec-driven-testing.md +0 -305
- package/dist/builtin/subagents/skills/playwright-cli/references/storage-state.md +0 -275
- package/dist/builtin/subagents/skills/playwright-cli/references/test-generation.md +0 -134
- package/dist/builtin/subagents/skills/playwright-cli/references/tracing.md +0 -139
- package/dist/builtin/subagents/skills/playwright-cli/references/video-recording.md +0 -143
|
@@ -40,6 +40,15 @@
|
|
|
40
40
|
* live Atomic widgets without reintroducing a high-frequency spinner.
|
|
41
41
|
*/
|
|
42
42
|
|
|
43
|
+
import {
|
|
44
|
+
decideReactiveWidgetAction,
|
|
45
|
+
installReactiveWidget,
|
|
46
|
+
type ReactiveWidgetAction,
|
|
47
|
+
type ReactiveWidgetFactory,
|
|
48
|
+
type ReactiveWidgetRenderState,
|
|
49
|
+
type ReactiveWidgetTimerApi,
|
|
50
|
+
type ReactiveWidgetTimerHandle,
|
|
51
|
+
} from "@bastani/atomic";
|
|
43
52
|
import type { Store } from "../shared/store.js";
|
|
44
53
|
import type { StoreSnapshot } from "../shared/store-types.js";
|
|
45
54
|
import { buildThemedWidgetLines, nextWidgetRefreshDelayMs } from "./widget.js";
|
|
@@ -49,15 +58,9 @@ export interface PiTheme {
|
|
|
49
58
|
bold(text: string): string;
|
|
50
59
|
}
|
|
51
60
|
|
|
52
|
-
export type WidgetFactory =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
) => Component & { dispose?(): void };
|
|
56
|
-
|
|
57
|
-
export interface Component {
|
|
58
|
-
render(width: number): string[];
|
|
59
|
-
dispose?(): void;
|
|
60
|
-
}
|
|
61
|
+
export type WidgetFactory = ReactiveWidgetFactory<unknown>;
|
|
62
|
+
export type WidgetAction = ReactiveWidgetAction;
|
|
63
|
+
export type WidgetRenderState = ReactiveWidgetRenderState;
|
|
61
64
|
|
|
62
65
|
interface UiSlice {
|
|
63
66
|
setWidget?: (
|
|
@@ -68,14 +71,8 @@ interface UiSlice {
|
|
|
68
71
|
requestRender?: () => void;
|
|
69
72
|
}
|
|
70
73
|
|
|
71
|
-
interface
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
interface TimerApi {
|
|
76
|
-
setTimeout(handler: () => void, delayMs: number): TimerHandle;
|
|
77
|
-
clearTimeout(handle: TimerHandle): void;
|
|
78
|
-
}
|
|
74
|
+
interface TimerApi extends ReactiveWidgetTimerApi {}
|
|
75
|
+
interface TimerHandle extends ReactiveWidgetTimerHandle {}
|
|
79
76
|
|
|
80
77
|
const defaultTimerApi: TimerApi = {
|
|
81
78
|
setTimeout: (handler, delayMs) => setTimeout(handler, delayMs) as TimerHandle,
|
|
@@ -97,59 +94,11 @@ function isStale(err: unknown): boolean {
|
|
|
97
94
|
return err instanceof Error && err.message.includes(STALE_CONTEXT);
|
|
98
95
|
}
|
|
99
96
|
|
|
100
|
-
/** The four ways a refresh can reach (or skip) the terminal. */
|
|
101
|
-
export type WidgetAction = "mount" | "unmount" | "update" | "none";
|
|
102
|
-
|
|
103
|
-
export interface WidgetRenderState {
|
|
104
|
-
/** Whether the widget is currently mounted (factory installed via setWidget). */
|
|
105
|
-
readonly mounted: boolean;
|
|
106
|
-
/** Plain preview lines last rendered while mounted (empty when not mounted). */
|
|
107
|
-
readonly lines: readonly string[];
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function linesEqual(a: readonly string[], b: readonly string[]): boolean {
|
|
111
|
-
if (a.length !== b.length) return false;
|
|
112
|
-
for (let i = 0; i < a.length; i++) {
|
|
113
|
-
if (a[i] !== b[i]) return false;
|
|
114
|
-
}
|
|
115
|
-
return true;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Pure decision: given the previous mount state + previously rendered lines
|
|
120
|
-
* and the next preview lines, decide how a refresh should reach the terminal.
|
|
121
|
-
* - hidden → visible => "mount" (setWidget(factory))
|
|
122
|
-
* - visible → hidden => "unmount" (setWidget(undefined))
|
|
123
|
-
* - visible → visible, changed => "update" (requestRender only — no remount)
|
|
124
|
-
* - visible → visible, identical => "none" (skip the redundant repaint)
|
|
125
|
-
* - hidden → hidden => "none"
|
|
126
|
-
*
|
|
127
|
-
* No UI/terminal dependency, so it is unit-testable without a real terminal.
|
|
128
|
-
*/
|
|
129
97
|
export function decideWidgetAction(
|
|
130
98
|
prev: WidgetRenderState,
|
|
131
99
|
nextLines: readonly string[],
|
|
132
100
|
): WidgetAction {
|
|
133
|
-
|
|
134
|
-
if (!prev.mounted) return nextVisible ? "mount" : "none";
|
|
135
|
-
if (!nextVisible) return "unmount";
|
|
136
|
-
return linesEqual(prev.lines, nextLines) ? "none" : "update";
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Build the long-lived widget factory closure. Pi invokes the factory once
|
|
141
|
-
* with `(this.ui, theme)` at mount; the returned component is reused across
|
|
142
|
-
* renders. It closes over a *live* snapshot getter rather than a captured
|
|
143
|
-
* snapshot, so each `requestRender()` recomputes lines from the latest
|
|
144
|
-
* snapshot (and a fresh `Date.now()`) with no dispose/remount.
|
|
145
|
-
*/
|
|
146
|
-
function widgetFactory(getSnap: () => StoreSnapshot): WidgetFactory {
|
|
147
|
-
return (_tui, theme) => {
|
|
148
|
-
return {
|
|
149
|
-
render: (width: number) =>
|
|
150
|
-
buildThemedWidgetLines(getSnap(), theme as PiTheme | undefined, width),
|
|
151
|
-
};
|
|
152
|
-
};
|
|
101
|
+
return decideReactiveWidgetAction(prev, nextLines);
|
|
153
102
|
}
|
|
154
103
|
|
|
155
104
|
export function installStoreWidget(
|
|
@@ -160,96 +109,25 @@ export function installStoreWidget(
|
|
|
160
109
|
const ui = pi.ui;
|
|
161
110
|
if (!ui?.setWidget) return () => {};
|
|
162
111
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
refreshTimer = undefined;
|
|
183
|
-
rerender();
|
|
184
|
-
}, delayMs);
|
|
185
|
-
refreshTimer.unref?.();
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const rerender = (): void => {
|
|
189
|
-
if (disposed) return;
|
|
190
|
-
clearRefreshTimer();
|
|
191
|
-
try {
|
|
192
|
-
currentSnap = storeInstance.snapshot();
|
|
193
|
-
const nextLines = buildThemedWidgetLines(currentSnap, undefined);
|
|
194
|
-
const action = decideWidgetAction({ mounted, lines: lastLines }, nextLines);
|
|
195
|
-
|
|
196
|
-
switch (action) {
|
|
197
|
-
case "mount":
|
|
198
|
-
// Mount the single long-lived component. It reads `currentSnap`
|
|
199
|
-
// through the getter on every subsequent render, so we never need
|
|
200
|
-
// to re-issue setWidget to refresh content.
|
|
201
|
-
ui.setWidget?.(WIDGET_KEY, widgetFactory(() => currentSnap), {
|
|
202
|
-
// belowEditor (not aboveEditor): the widget renders a live elapsed
|
|
203
|
-
// clock that re-renders every second. pi-tui's differential
|
|
204
|
-
// renderer does a full screen+scrollback clear whenever a *changed*
|
|
205
|
-
// line sits ABOVE the viewport fold (tui.js `firstChanged <
|
|
206
|
-
// prevViewportTop -> fullRender(true)`). An aboveEditor widget is
|
|
207
|
-
// pushed above the fold when the bottom region (usage meter,
|
|
208
|
-
// multi-line editor, below-editor widgets, footer) grows tall,
|
|
209
|
-
// so each clock tick then repainted the whole screen — the resize
|
|
210
|
-
// flicker in #1109. belowEditor keeps the widget among the last
|
|
211
|
-
// rendered lines (always within the bottom viewport), so its
|
|
212
|
-
// per-second tick is a clean in-place differential redraw.
|
|
213
|
-
placement: "belowEditor",
|
|
214
|
-
});
|
|
215
|
-
ui.requestRender?.();
|
|
216
|
-
mounted = true;
|
|
217
|
-
break;
|
|
218
|
-
case "unmount":
|
|
219
|
-
ui.setWidget?.(WIDGET_KEY, undefined);
|
|
220
|
-
ui.requestRender?.();
|
|
221
|
-
mounted = false;
|
|
222
|
-
break;
|
|
223
|
-
case "update":
|
|
224
|
-
// In place — NO setWidget, NO dispose/remount (the #1109 flicker fix).
|
|
225
|
-
ui.requestRender?.();
|
|
226
|
-
break;
|
|
227
|
-
case "none":
|
|
228
|
-
break;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
lastLines = nextLines;
|
|
232
|
-
scheduleRefresh(currentSnap);
|
|
233
|
-
} catch (err) {
|
|
234
|
-
if (isStale(err)) return;
|
|
235
|
-
throw err;
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
const unsubscribe = storeInstance.subscribe(() => rerender());
|
|
240
|
-
rerender();
|
|
241
|
-
|
|
242
|
-
return () => {
|
|
243
|
-
disposed = true;
|
|
244
|
-
clearRefreshTimer();
|
|
245
|
-
unsubscribe();
|
|
246
|
-
try {
|
|
247
|
-
ui.setWidget?.(WIDGET_KEY, undefined);
|
|
248
|
-
ui.requestRender?.();
|
|
249
|
-
} catch (err) {
|
|
250
|
-
if (!isStale(err)) throw err;
|
|
251
|
-
}
|
|
252
|
-
};
|
|
112
|
+
const requestRender = ui.requestRender;
|
|
113
|
+
const controller = installReactiveWidget<StoreSnapshot, unknown>({
|
|
114
|
+
ui: {
|
|
115
|
+
setWidget: (key, factory, opts) => ui.setWidget?.(key, factory, opts),
|
|
116
|
+
...(requestRender ? { requestRender: () => requestRender.call(ui) } : {}),
|
|
117
|
+
},
|
|
118
|
+
key: WIDGET_KEY,
|
|
119
|
+
placement: "belowEditor",
|
|
120
|
+
timers,
|
|
121
|
+
getSnapshot: () => storeInstance.snapshot(),
|
|
122
|
+
subscribe: (listener) => storeInstance.subscribe(() => listener()),
|
|
123
|
+
getPreviewLines: (snap, now) => buildThemedWidgetLines(snap, undefined, 120, now),
|
|
124
|
+
render: (snap, { theme, width, now }) =>
|
|
125
|
+
buildThemedWidgetLines(snap, theme as PiTheme | undefined, width, now),
|
|
126
|
+
getNextRefreshDelayMs: (snap, now) => nextWidgetRefreshDelayMs(snap, now),
|
|
127
|
+
isStaleError: isStale,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return () => controller.dispose();
|
|
253
131
|
}
|
|
254
132
|
|
|
255
133
|
interface ToolExecutionStartPayload {
|
|
@@ -372,7 +250,8 @@ export function installToolExecutionHooks(pi: LiveWidgetAPI, storeInstance: Stor
|
|
|
372
250
|
function recordToolEnd(payload: unknown): void {
|
|
373
251
|
if (!isToolExecutionPayload(payload)) return;
|
|
374
252
|
|
|
375
|
-
const
|
|
253
|
+
const activeAskCall = findActiveAskCall(payload);
|
|
254
|
+
const ids = activeAskCall ?? resolveIds(payload, false);
|
|
376
255
|
if (!ids) return;
|
|
377
256
|
|
|
378
257
|
storeInstance.recordToolEnd(ids.runId, ids.stageId, {
|
|
@@ -382,7 +261,7 @@ export function installToolExecutionHooks(pi: LiveWidgetAPI, storeInstance: Stor
|
|
|
382
261
|
endedAt: payload.endedAt ?? payload.ended_at ?? Date.now(),
|
|
383
262
|
output: payload.output,
|
|
384
263
|
});
|
|
385
|
-
recordAskUserQuestionEnd(payload, ids);
|
|
264
|
+
recordAskUserQuestionEnd(payload, activeAskCall ?? ids);
|
|
386
265
|
}
|
|
387
266
|
|
|
388
267
|
const safeStart = safelyHandle(recordToolStart);
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Visual: solid status-coloured pill (`success`/`warning`/`info`/`error`)
|
|
5
5
|
* with `backgroundElement` foreground and bold weight — same vocabulary as
|
|
6
|
-
* the header pill (DESIGN.md §5 Mode Pills). Icon glyphs
|
|
7
|
-
*
|
|
6
|
+
* the header pill (DESIGN.md §5 Mode Pills). Icon glyphs use the same
|
|
7
|
+
* cross-platform text symbols as the rest of the workflow UI (`✓ ✗ ⚠ ℹ`).
|
|
8
8
|
*/
|
|
9
9
|
import type { GraphTheme } from "./graph-theme.js";
|
|
10
10
|
import { hexBg, hexToAnsi, RESET, BOLD } from "./color-utils.js";
|
|
@@ -27,12 +27,14 @@ import type {
|
|
|
27
27
|
RunSnapshot,
|
|
28
28
|
} from "../shared/store-types.js";
|
|
29
29
|
import { elapsedRunMs } from "../shared/timing.js";
|
|
30
|
+
import { topLevelWorkflowRuns } from "../shared/run-visibility.js";
|
|
30
31
|
import type { PiTheme } from "./store-widget-installer.js";
|
|
31
32
|
import { renderRoundedBoxLines } from "./chat-surface.js";
|
|
32
33
|
import type { FlatBandBadge } from "./chat-surface.js";
|
|
33
34
|
import { deriveGraphTheme } from "./graph-theme.js";
|
|
34
35
|
import type { GraphTheme } from "./graph-theme.js";
|
|
35
36
|
import { hexToAnsi, RESET, BOLD } from "./color-utils.js";
|
|
37
|
+
import { statusIcon } from "./status-helpers.js";
|
|
36
38
|
|
|
37
39
|
// ---------------------------------------------------------------------------
|
|
38
40
|
// Tunables
|
|
@@ -81,17 +83,36 @@ interface RunCounts {
|
|
|
81
83
|
awaiting: number;
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
function
|
|
86
|
+
function runAwaitsInput(run: RunSnapshot): boolean {
|
|
87
|
+
return (
|
|
88
|
+
run.endedAt === undefined &&
|
|
89
|
+
(run.pendingPrompt !== undefined || run.stages.some((s) => s.status === "awaiting_input"))
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* A top-level run "needs attention" when it OR any of its nested
|
|
95
|
+
* `ctx.workflow()` descendants is awaiting human input. Nested child runs are
|
|
96
|
+
* hidden from the widget, but each carries `rootRunId` pointing at the ultimate
|
|
97
|
+
* top-level run, so a hidden child's awaiting (HiL) state surfaces on the
|
|
98
|
+
* visible ancestor instead of vanishing with it.
|
|
99
|
+
*/
|
|
100
|
+
function subtreeAwaitsInput(root: RunSnapshot, allRuns: readonly RunSnapshot[]): boolean {
|
|
101
|
+
if (runAwaitsInput(root)) return true;
|
|
102
|
+
return allRuns.some((run) => run.rootRunId === root.id && runAwaitsInput(run));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function countRuns(
|
|
106
|
+
runs: readonly RunSnapshot[],
|
|
107
|
+
allRuns: readonly RunSnapshot[] = runs,
|
|
108
|
+
): RunCounts {
|
|
85
109
|
const counts: RunCounts = { active: 0, paused: 0, done: 0, failed: 0, awaiting: 0 };
|
|
86
110
|
for (const r of runs) {
|
|
87
111
|
if (r.endedAt === undefined && r.status === "paused") counts.paused++;
|
|
88
112
|
else if (r.endedAt === undefined) counts.active++;
|
|
89
113
|
else if (r.status === "completed") counts.done++;
|
|
90
114
|
else if (r.status === "failed" || r.status === "killed") counts.failed++;
|
|
91
|
-
if (
|
|
92
|
-
r.endedAt === undefined &&
|
|
93
|
-
(r.pendingPrompt !== undefined || r.stages.some((s) => s.status === "awaiting_input"))
|
|
94
|
-
) {
|
|
115
|
+
if (r.endedAt === undefined && subtreeAwaitsInput(r, allRuns)) {
|
|
95
116
|
counts.awaiting++;
|
|
96
117
|
}
|
|
97
118
|
}
|
|
@@ -120,7 +141,12 @@ export function nextWidgetRefreshDelayMs(
|
|
|
120
141
|
}
|
|
121
142
|
|
|
122
143
|
function selectDisplayRuns(snap: StoreSnapshot, now: number): RunSnapshot[] {
|
|
123
|
-
|
|
144
|
+
// Only surface top-level workflows. Nested `ctx.workflow()` child runs carry
|
|
145
|
+
// a `parentRunId`, and they are already represented inline as flattened
|
|
146
|
+
// stages of their parent's graph, so listing them here would show the same
|
|
147
|
+
// composition three times (root + parent + child). Matches the visibility
|
|
148
|
+
// rule `statusRuns`/the `status` action already apply.
|
|
149
|
+
const all = topLevelWorkflowRuns(snap.runs);
|
|
124
150
|
const active = all.filter((r) => isActive(r));
|
|
125
151
|
const recent = all.filter((r) => recentlyEnded(r, now));
|
|
126
152
|
// Most recently started first within each bucket; active runs precede recent.
|
|
@@ -223,10 +249,10 @@ function countBadges(counts: RunCounts, theme: GraphTheme): FlatBandBadge[] {
|
|
|
223
249
|
badges.push({ text: `❚❚ ${counts.paused} paused`, fg: theme.warning });
|
|
224
250
|
}
|
|
225
251
|
// Awaiting input is shown in Sky per DESIGN.md status semantics: a live
|
|
226
|
-
// human-in-the-loop request that requires attention.
|
|
227
|
-
//
|
|
252
|
+
// human-in-the-loop request that requires attention. Mirror the graph node's
|
|
253
|
+
// question-mark status glyph, then keep ↵ as the attach/respond action hint.
|
|
228
254
|
if (counts.awaiting > 0) {
|
|
229
|
-
badges.push({ text:
|
|
255
|
+
badges.push({ text: `${statusIcon("awaiting_input")} ↵ ${counts.awaiting} needs attention (attach to workflow with \`/workflow connect\`)`, fg: theme.info });
|
|
230
256
|
}
|
|
231
257
|
if (counts.done > 0) {
|
|
232
258
|
badges.push({ text: `✓ ${counts.done} complete`, fg: theme.success });
|
|
@@ -237,6 +263,20 @@ function countBadges(counts: RunCounts, theme: GraphTheme): FlatBandBadge[] {
|
|
|
237
263
|
return badges;
|
|
238
264
|
}
|
|
239
265
|
|
|
266
|
+
function formatTitleBadges(
|
|
267
|
+
badges: readonly FlatBandBadge[],
|
|
268
|
+
theme: GraphTheme,
|
|
269
|
+
themed: boolean,
|
|
270
|
+
): string {
|
|
271
|
+
if (badges.length === 0) return "";
|
|
272
|
+
if (!themed) return badges.map((b) => b.text).join(" ");
|
|
273
|
+
|
|
274
|
+
const fallbackFg = hexToAnsi(theme.border);
|
|
275
|
+
return badges
|
|
276
|
+
.map((b) => `${b.fg ? hexToAnsi(b.fg) : fallbackFg}${b.text}${RESET}${fallbackFg}`)
|
|
277
|
+
.join(" ");
|
|
278
|
+
}
|
|
279
|
+
|
|
240
280
|
// ---------------------------------------------------------------------------
|
|
241
281
|
// Themed rendering (ANSI + Catppuccin)
|
|
242
282
|
// ---------------------------------------------------------------------------
|
|
@@ -313,12 +353,12 @@ export function buildThemedWidgetLines(
|
|
|
313
353
|
snap: StoreSnapshot,
|
|
314
354
|
piTheme: PiTheme | undefined,
|
|
315
355
|
width = 120,
|
|
356
|
+
now = Date.now(),
|
|
316
357
|
): string[] {
|
|
317
|
-
const now = Date.now();
|
|
318
358
|
const display = selectDisplayRuns(snap, now);
|
|
319
359
|
if (display.length === 0) return [];
|
|
320
360
|
|
|
321
|
-
const counts = countRuns(snap.runs
|
|
361
|
+
const counts = countRuns(topLevelWorkflowRuns(snap.runs), snap.runs);
|
|
322
362
|
// Active + recently-ended dominate the badge counts so a finished run
|
|
323
363
|
// visually persists for a beat before dropping off.
|
|
324
364
|
const visibleCounts: RunCounts = {
|
|
@@ -340,7 +380,8 @@ export function buildThemedWidgetLines(
|
|
|
340
380
|
const total = counts.active + counts.paused + counts.done + counts.failed;
|
|
341
381
|
const subtitle = `${total} run${total === 1 ? "" : "s"}`;
|
|
342
382
|
|
|
343
|
-
const
|
|
383
|
+
const badgeList = countBadges(visibleCounts, graphTheme);
|
|
384
|
+
const badges = formatTitleBadges(badgeList, graphTheme, themed);
|
|
344
385
|
const title = `BACKGROUND ${subtitle}${badges ? ` ${badges}` : ""}`;
|
|
345
386
|
const body: string[] = [];
|
|
346
387
|
|