@bastani/atomic 0.9.3-alpha.1 → 0.9.3-alpha.2
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 +9 -0
- package/dist/builtin/cursor/CHANGELOG.md +15 -0
- package/dist/builtin/cursor/README.md +2 -1
- package/dist/builtin/cursor/package.json +2 -2
- package/dist/builtin/cursor/src/cursor-models-raw.json +2 -9
- package/dist/builtin/cursor/src/model-mapper.ts +14 -3
- package/dist/builtin/cursor/src/proto/protobuf-codec-base64.ts +22 -0
- package/dist/builtin/cursor/src/proto/protobuf-codec-request.ts +53 -13
- package/dist/builtin/cursor/src/proto/protobuf-codec-wire.ts +24 -7
- package/dist/builtin/cursor/src/proto/protobuf-codec.ts +3 -2
- package/dist/builtin/cursor/src/stream.ts +5 -11
- package/dist/builtin/cursor/src/transport-types.ts +3 -0
- package/dist/builtin/cursor/src/transport.ts +1 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +9 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/fanout-child.ts +1 -0
- package/dist/builtin/subagents/src/extension/index.ts +6 -3
- package/dist/builtin/subagents/src/extension/schemas.ts +0 -5
- package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +1 -4
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +15 -1
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +35 -1
- package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +4 -2
- package/dist/builtin/subagents/src/shared/types-async.ts +1 -0
- package/dist/builtin/subagents/src/slash/prompt-template-bridge.ts +27 -5
- package/dist/builtin/subagents/src/tui/render-layout.ts +27 -4
- package/dist/builtin/subagents/src/tui/render-result-animation.ts +22 -31
- package/dist/builtin/subagents/src/tui/render-result-compact.ts +6 -6
- package/dist/builtin/subagents/src/tui/render-result.ts +20 -19
- package/dist/builtin/subagents/src/tui/render-status-progress.ts +3 -3
- package/dist/builtin/subagents/src/tui/render-widget.ts +46 -7
- package/dist/builtin/subagents/src/tui/render.ts +2 -2
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +43 -0
- package/dist/builtin/workflows/README.md +1 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/authoring.d.ts +1 -1
- package/dist/builtin/workflows/src/durable/backend.ts +343 -0
- package/dist/builtin/workflows/src/durable/child-primitive.ts +79 -0
- package/dist/builtin/workflows/src/durable/dbos-backend.ts +421 -0
- package/dist/builtin/workflows/src/durable/dbos-envelope.ts +171 -0
- package/dist/builtin/workflows/src/durable/factory.ts +96 -0
- package/dist/builtin/workflows/src/durable/file-backend.ts +433 -0
- package/dist/builtin/workflows/src/durable/index.ts +73 -0
- package/dist/builtin/workflows/src/durable/resume-catalog.ts +217 -0
- package/dist/builtin/workflows/src/durable/resume-runtime.ts +299 -0
- package/dist/builtin/workflows/src/durable/scoped-backend.ts +171 -0
- package/dist/builtin/workflows/src/durable/stage-primitive.ts +284 -0
- package/dist/builtin/workflows/src/durable/tool-primitive.ts +180 -0
- package/dist/builtin/workflows/src/durable/types.ts +168 -0
- package/dist/builtin/workflows/src/durable/ui-primitive.ts +96 -0
- package/dist/builtin/workflows/src/engine/options.ts +3 -0
- package/dist/builtin/workflows/src/engine/primitives/parallel.ts +2 -2
- package/dist/builtin/workflows/src/engine/primitives/task.ts +4 -4
- package/dist/builtin/workflows/src/engine/primitives/ui.ts +22 -8
- package/dist/builtin/workflows/src/engine/primitives/workflow.ts +8 -0
- package/dist/builtin/workflows/src/engine/run-durable-finalize.ts +69 -0
- package/dist/builtin/workflows/src/engine/run-durable-stage-session.ts +31 -0
- package/dist/builtin/workflows/src/engine/run.ts +148 -6
- package/dist/builtin/workflows/src/engine/runtime.ts +8 -2
- package/dist/builtin/workflows/src/extension/extension-factory.ts +6 -12
- package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +5 -1
- package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +3 -0
- package/dist/builtin/workflows/src/extension/runtime.ts +48 -9
- package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +143 -4
- package/dist/builtin/workflows/src/runs/background/quit.ts +61 -0
- package/dist/builtin/workflows/src/runs/background/status.ts +1 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +5 -5
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +74 -33
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-context.ts +20 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +8 -7
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-replay.ts +1 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-types.ts +1 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +19 -2
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +4 -0
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +10 -10
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-options.ts +5 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-send-user-message.ts +25 -0
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-types.ts +3 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +16 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +20 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +23 -1
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +30 -1
- package/dist/builtin/workflows/src/shared/store-public-types.ts +6 -2
- package/dist/builtin/workflows/src/shared/store-run-methods.ts +12 -6
- package/dist/builtin/workflows/src/shared/types.ts +55 -0
- package/dist/builtin/workflows/src/tui/graph-view-constants.ts +1 -1
- package/dist/builtin/workflows/src/tui/graph-view-graph-render.ts +41 -0
- package/dist/builtin/workflows/src/tui/graph-view-input.ts +82 -24
- package/dist/builtin/workflows/src/tui/graph-view-render.ts +7 -0
- package/dist/builtin/workflows/src/tui/graph-view-state.ts +22 -2
- package/dist/builtin/workflows/src/tui/graph-view-types.ts +4 -5
- package/dist/builtin/workflows/src/tui/overlay-adapter.ts +9 -11
- package/dist/builtin/workflows/src/tui/stage-chat-view-footer-status.ts +9 -3
- package/dist/builtin/workflows/src/tui/stage-chat-view-input.ts +11 -2
- package/dist/builtin/workflows/src/tui/stage-chat-view-live-events.ts +35 -0
- package/dist/builtin/workflows/src/tui/stage-chat-view-state.ts +51 -17
- package/dist/builtin/workflows/src/tui/stage-chat-view-status.ts +36 -0
- package/dist/builtin/workflows/src/tui/stage-chat-view-types.ts +5 -1
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +3 -1
- package/dist/builtin/workflows/src/tui/status-list.ts +14 -2
- package/dist/builtin/workflows/src/tui/widget.ts +23 -8
- package/dist/builtin/workflows/src/tui/workflow-attach-pane-types.ts +5 -4
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +8 -8
- package/dist/builtin/workflows/src/tui/workflow-resume-selector.ts +151 -0
- package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.js +47 -30
- package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
- package/dist/core/messages.d.ts +1 -0
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +46 -1
- package/dist/core/messages.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +12 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager-core.d.ts +15 -7
- package/dist/core/session-manager-core.d.ts.map +1 -1
- package/dist/core/session-manager-core.js +20 -9
- package/dist/core/session-manager-core.js.map +1 -1
- package/dist/core/session-manager-entries.d.ts +2 -2
- package/dist/core/session-manager-entries.d.ts.map +1 -1
- package/dist/core/session-manager-entries.js +9 -3
- 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 -1
- package/dist/core/session-manager-history.js.map +1 -1
- package/dist/core/session-manager-list.d.ts +3 -3
- package/dist/core/session-manager-list.d.ts.map +1 -1
- package/dist/core/session-manager-list.js +27 -8
- package/dist/core/session-manager-list.js.map +1 -1
- package/dist/core/session-manager-storage.d.ts +3 -1
- package/dist/core/session-manager-storage.d.ts.map +1 -1
- package/dist/core/session-manager-storage.js +55 -12
- package/dist/core/session-manager-storage.js.map +1 -1
- package/dist/core/session-manager-tool-dependencies.d.ts +10 -0
- package/dist/core/session-manager-tool-dependencies.d.ts.map +1 -0
- package/dist/core/session-manager-tool-dependencies.js +133 -0
- package/dist/core/session-manager-tool-dependencies.js.map +1 -0
- package/dist/core/session-manager-types.d.ts +22 -0
- package/dist/core/session-manager-types.d.ts.map +1 -1
- package/dist/core/session-manager-types.js.map +1 -1
- package/dist/core/session-manager.d.ts +2 -2
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +1 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-runtime.d.ts +1 -0
- package/dist/modes/interactive/components/chat-session-host-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-runtime.js +12 -0
- package/dist/modes/interactive/components/chat-session-host-runtime.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.d.ts +4 -0
- package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.d.ts.map +1 -0
- package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.js +131 -0
- package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.js.map +1 -0
- package/dist/modes/interactive/components/chat-session-host.d.ts +2 -0
- package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.js +7 -1
- package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
- package/dist/modes/interactive/components/chat-transcript.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-transcript.js +15 -4
- package/dist/modes/interactive/components/chat-transcript.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +3 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +26 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/docs/compaction.md +2 -0
- package/docs/models.md +1 -1
- package/docs/providers.md +2 -1
- package/docs/session-format.md +6 -0
- package/docs/sessions.md +6 -0
- package/docs/workflows.md +105 -3
- package/package.json +4 -3
|
@@ -16,6 +16,8 @@ export abstract class GraphViewGraphRenderer extends GraphViewRenderHelpers {
|
|
|
16
16
|
protected _renderGraph(width: number): string[] {
|
|
17
17
|
const run = this._getCurrentRun();
|
|
18
18
|
if (!run || this.cachedLayout.length === 0) {
|
|
19
|
+
this.lastGraphViewport = null;
|
|
20
|
+
this.graphNodeHitRects = [];
|
|
19
21
|
const dim = hexToAnsi(this.graphTheme.dim);
|
|
20
22
|
return [
|
|
21
23
|
this._centerCanvasContent(
|
|
@@ -40,6 +42,7 @@ export abstract class GraphViewGraphRenderer extends GraphViewRenderHelpers {
|
|
|
40
42
|
);
|
|
41
43
|
const viewportWidth = Math.max(1, width - leftMargin);
|
|
42
44
|
const fullCanvasWidth = Math.max(canvasWidth, viewportWidth);
|
|
45
|
+
this.lastGraphViewport = { leftMargin, viewportWidth };
|
|
43
46
|
this._clampGraphHorizontalScroll(fullCanvasWidth, viewportWidth);
|
|
44
47
|
if (this.pendingEnsureFocusedVisible) {
|
|
45
48
|
this._scrollFocusedColumnIntoView(viewportWidth, fullCanvasWidth);
|
|
@@ -129,6 +132,44 @@ export abstract class GraphViewGraphRenderer extends GraphViewRenderHelpers {
|
|
|
129
132
|
});
|
|
130
133
|
}
|
|
131
134
|
|
|
135
|
+
protected _recordGraphNodeHitRects(
|
|
136
|
+
graphStartRow: number,
|
|
137
|
+
visibleRowCount: number,
|
|
138
|
+
): void {
|
|
139
|
+
const viewport = this.lastGraphViewport;
|
|
140
|
+
if (!viewport || visibleRowCount <= 0) {
|
|
141
|
+
this.graphNodeHitRects = [];
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const visibleTop = graphStartRow;
|
|
146
|
+
const visibleBottom = graphStartRow + visibleRowCount;
|
|
147
|
+
const viewportLeft = viewport.leftMargin;
|
|
148
|
+
const viewportRight = viewport.leftMargin + viewport.viewportWidth;
|
|
149
|
+
const rects: typeof this.graphNodeHitRects = [];
|
|
150
|
+
|
|
151
|
+
for (let index = 0; index < this.cachedLayout.length; index++) {
|
|
152
|
+
const node = this.cachedLayout[index]!;
|
|
153
|
+
const top = graphStartRow + node.y - this.graphScrollOffset;
|
|
154
|
+
const bottom = top + NODE_H;
|
|
155
|
+
const left = viewport.leftMargin + node.x - this.graphScrollColOffset;
|
|
156
|
+
const right = left + NODE_W;
|
|
157
|
+
const clippedTop = Math.max(visibleTop, top);
|
|
158
|
+
const clippedBottom = Math.min(visibleBottom, bottom);
|
|
159
|
+
const clippedLeft = Math.max(viewportLeft, left);
|
|
160
|
+
const clippedRight = Math.min(viewportRight, right);
|
|
161
|
+
if (clippedTop >= clippedBottom || clippedLeft >= clippedRight) continue;
|
|
162
|
+
rects.push({
|
|
163
|
+
index,
|
|
164
|
+
top: clippedTop,
|
|
165
|
+
bottom: clippedBottom,
|
|
166
|
+
left: clippedLeft,
|
|
167
|
+
right: clippedRight,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
this.graphNodeHitRects = rects;
|
|
171
|
+
}
|
|
172
|
+
|
|
132
173
|
protected _visibleGraphLines(
|
|
133
174
|
graphLines: string[],
|
|
134
175
|
frameWidth: number,
|
|
@@ -11,6 +11,13 @@ import {
|
|
|
11
11
|
import { filterStages, type SwitcherState } from "./switcher.js";
|
|
12
12
|
import { Key, matchesKey } from "./text-helpers.js";
|
|
13
13
|
|
|
14
|
+
interface SgrMouseEvent {
|
|
15
|
+
buttonCode: number;
|
|
16
|
+
col: number;
|
|
17
|
+
row: number;
|
|
18
|
+
final: "M" | "m";
|
|
19
|
+
}
|
|
20
|
+
|
|
14
21
|
/** Keyboard, mouse, switcher, prompt, and focus navigation handling. */
|
|
15
22
|
export abstract class GraphViewInputController extends GraphViewRenderer {
|
|
16
23
|
/** Returns true if consumed. */
|
|
@@ -80,6 +87,15 @@ export abstract class GraphViewInputController extends GraphViewRenderer {
|
|
|
80
87
|
return true;
|
|
81
88
|
}
|
|
82
89
|
|
|
90
|
+
const clickedNodeIndex = this._graphNodeIndexForClick(data);
|
|
91
|
+
if (clickedNodeIndex !== undefined) {
|
|
92
|
+
if (clickedNodeIndex !== null) {
|
|
93
|
+
this._setFocusedIndex(clickedNodeIndex);
|
|
94
|
+
this._activateFocusedNode();
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
|
|
83
99
|
// Vertical-graph navigation: up/down step between depth levels
|
|
84
100
|
// (col), left/right step between siblings at the same depth (row).
|
|
85
101
|
// j/k preserved as a flat-order fallback for muscle memory.
|
|
@@ -115,12 +131,7 @@ export abstract class GraphViewInputController extends GraphViewRenderer {
|
|
|
115
131
|
return true;
|
|
116
132
|
}
|
|
117
133
|
if (matchesKey(data, Key.enter)) {
|
|
118
|
-
|
|
119
|
-
// attach shell swaps in the stage-chat view without remounting
|
|
120
|
-
// the overlay; without a callback, fall back to the legacy
|
|
121
|
-
// expand/collapse toggle so non-attach hosts still work.
|
|
122
|
-
if (this._attachFocusedStage()) return true;
|
|
123
|
-
this.detailsExpanded = !this.detailsExpanded;
|
|
134
|
+
this._activateFocusedNode();
|
|
124
135
|
return true;
|
|
125
136
|
}
|
|
126
137
|
// `ctrl+d` detaches the whole popup (host hides the overlay). This
|
|
@@ -133,16 +144,13 @@ export abstract class GraphViewInputController extends GraphViewRenderer {
|
|
|
133
144
|
}
|
|
134
145
|
return true;
|
|
135
146
|
}
|
|
136
|
-
// `q`
|
|
137
|
-
// the
|
|
147
|
+
// `q` quits/detaches the orchestrator view without authoritatively
|
|
148
|
+
// killing the workflow. The workflow remains resumable via
|
|
149
|
+
// `/workflow resume`; use `/workflow kill` for non-resumable disposal.
|
|
138
150
|
if (matchesKey(data, "q")) {
|
|
139
151
|
const run = this._getCurrentRun();
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
? this.currentSnapshot?.runs.find((candidate) => candidate.id === targetRunId)
|
|
143
|
-
: undefined;
|
|
144
|
-
if (targetRun && targetRun.endedAt === undefined && this.onKill) {
|
|
145
|
-
this.onKill(targetRun.id);
|
|
152
|
+
if (run && run.endedAt === undefined && this.onQuit) {
|
|
153
|
+
this.onQuit(run.id);
|
|
146
154
|
}
|
|
147
155
|
this.onClose?.();
|
|
148
156
|
return true;
|
|
@@ -265,6 +273,15 @@ export abstract class GraphViewInputController extends GraphViewRenderer {
|
|
|
265
273
|
return true;
|
|
266
274
|
}
|
|
267
275
|
|
|
276
|
+
private _activateFocusedNode(): void {
|
|
277
|
+
// Enter and direct node clicks attach the popup interior to the focused
|
|
278
|
+
// stage. The attach shell swaps in the stage-chat view without remounting
|
|
279
|
+
// the overlay; without a callback, fall back to the legacy expand/collapse
|
|
280
|
+
// toggle so non-attach hosts still work.
|
|
281
|
+
if (this._attachFocusedStage()) return;
|
|
282
|
+
this.detailsExpanded = !this.detailsExpanded;
|
|
283
|
+
}
|
|
284
|
+
|
|
268
285
|
private _attachFocusedStage(): boolean {
|
|
269
286
|
if (!this.onStageAttach) return false;
|
|
270
287
|
const node = this.cachedLayout[this.focusedIndex];
|
|
@@ -278,13 +295,6 @@ export abstract class GraphViewInputController extends GraphViewRenderer {
|
|
|
278
295
|
return true;
|
|
279
296
|
}
|
|
280
297
|
|
|
281
|
-
private _focusedStageTarget(): { runId: string; stageId: string } | undefined {
|
|
282
|
-
const node = this.cachedLayout[this.focusedIndex];
|
|
283
|
-
if (!node) return undefined;
|
|
284
|
-
const target = expandedStageTarget(this.expandedGraph, node.stage.id);
|
|
285
|
-
return target ? { runId: target.runId, stageId: target.stageId } : undefined;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
298
|
private _setFocusedIndex(index: number): void {
|
|
289
299
|
const max = Math.max(0, this.cachedLayout.length - 1);
|
|
290
300
|
const next = Math.max(0, Math.min(index, max));
|
|
@@ -298,10 +308,55 @@ export abstract class GraphViewInputController extends GraphViewRenderer {
|
|
|
298
308
|
this.graphScrollOffset = Math.max(0, this.graphScrollOffset + deltaRows);
|
|
299
309
|
}
|
|
300
310
|
|
|
311
|
+
private _graphNodeIndexForClick(data: string): number | null | undefined {
|
|
312
|
+
const click = this._sgrLeftMousePress(data);
|
|
313
|
+
if (!click) return undefined;
|
|
314
|
+
if (this.mode !== "overlay") return undefined;
|
|
315
|
+
if (this.cachedLayout.length === 0) return null;
|
|
316
|
+
|
|
317
|
+
for (const rect of this.graphNodeHitRects) {
|
|
318
|
+
if (
|
|
319
|
+
click.row >= rect.top &&
|
|
320
|
+
click.row < rect.bottom &&
|
|
321
|
+
click.col >= rect.left &&
|
|
322
|
+
click.col < rect.right
|
|
323
|
+
) {
|
|
324
|
+
return rect.index;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
private _parseSgrMouse(data: string): SgrMouseEvent | null {
|
|
331
|
+
const sgr = data.match(/^\x1b\[<(\d+);(\d+);(\d+)([Mm])$/);
|
|
332
|
+
if (!sgr) return null;
|
|
333
|
+
const oneBasedCol = Number.parseInt(sgr[2]!, 10);
|
|
334
|
+
const oneBasedRow = Number.parseInt(sgr[3]!, 10);
|
|
335
|
+
const final = sgr[4];
|
|
336
|
+
if (oneBasedCol < 1 || oneBasedRow < 1) return null;
|
|
337
|
+
if (final !== "M" && final !== "m") return null;
|
|
338
|
+
return {
|
|
339
|
+
buttonCode: Number.parseInt(sgr[1]!, 10),
|
|
340
|
+
col: oneBasedCol - 1,
|
|
341
|
+
row: oneBasedRow - 1,
|
|
342
|
+
final,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private _sgrLeftMousePress(data: string): { col: number; row: number } | null {
|
|
347
|
+
const sgr = this._parseSgrMouse(data);
|
|
348
|
+
if (!sgr || sgr.final !== "M") return null;
|
|
349
|
+
const buttonCode = sgr.buttonCode;
|
|
350
|
+
if ((buttonCode & 64) !== 0 || (buttonCode & 32) !== 0 || (buttonCode & 3) !== 0) {
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
return { col: sgr.col, row: sgr.row };
|
|
354
|
+
}
|
|
355
|
+
|
|
301
356
|
private _mouseWheelDeltaRows(data: string): number {
|
|
302
|
-
const sgr =
|
|
303
|
-
if (sgr) {
|
|
304
|
-
return this._wheelDeltaForButtonCode(
|
|
357
|
+
const sgr = this._parseSgrMouse(data);
|
|
358
|
+
if (sgr && sgr.final === "M") {
|
|
359
|
+
return this._wheelDeltaForButtonCode(sgr.buttonCode);
|
|
305
360
|
}
|
|
306
361
|
if (data.startsWith("\x1b[M") && data.length >= 6) {
|
|
307
362
|
return this._wheelDeltaForButtonCode(data.charCodeAt(3) - 32);
|
|
@@ -330,4 +385,7 @@ export abstract class GraphViewInputController extends GraphViewRenderer {
|
|
|
330
385
|
get _graphScrollOffset(): number {
|
|
331
386
|
return this.graphScrollOffset;
|
|
332
387
|
}
|
|
388
|
+
get _graphScrollColOffset(): number {
|
|
389
|
+
return this.graphScrollColOffset;
|
|
390
|
+
}
|
|
333
391
|
}
|
|
@@ -37,6 +37,7 @@ export abstract class GraphViewRenderer extends GraphViewGraphRenderer {
|
|
|
37
37
|
|
|
38
38
|
protected _renderOverlay(width: number): string[] {
|
|
39
39
|
const frameWidth = Math.max(40, width);
|
|
40
|
+
this.lastOverlayFrameWidth = frameWidth;
|
|
40
41
|
const lines: string[] = [];
|
|
41
42
|
const run = this._getCurrentRun();
|
|
42
43
|
|
|
@@ -58,6 +59,10 @@ export abstract class GraphViewRenderer extends GraphViewGraphRenderer {
|
|
|
58
59
|
frameWidth,
|
|
59
60
|
bodyTarget,
|
|
60
61
|
);
|
|
62
|
+
this._recordGraphNodeHitRects(
|
|
63
|
+
this._overlayVerticalMarginRows() + 3 + visibleGraph.topPad,
|
|
64
|
+
visibleGraph.lines.length,
|
|
65
|
+
);
|
|
61
66
|
for (let i = 0; i < visibleGraph.topPad; i++)
|
|
62
67
|
lines.push(this._blankRow(frameWidth));
|
|
63
68
|
for (const line of visibleGraph.lines) {
|
|
@@ -78,6 +83,8 @@ export abstract class GraphViewRenderer extends GraphViewGraphRenderer {
|
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
protected _renderEmptyState(width: number): string[] {
|
|
86
|
+
this.graphNodeHitRects = [];
|
|
87
|
+
this.lastGraphViewport = null;
|
|
81
88
|
const t = this.graphTheme;
|
|
82
89
|
const muted = hexToAnsi(t.textMuted);
|
|
83
90
|
const dim = hexToAnsi(t.dim);
|
|
@@ -34,6 +34,19 @@ export interface GraphStageCounts {
|
|
|
34
34
|
skipped: number;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
interface GraphNodeHitRect {
|
|
38
|
+
index: number;
|
|
39
|
+
top: number;
|
|
40
|
+
bottom: number;
|
|
41
|
+
left: number;
|
|
42
|
+
right: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface GraphViewportGeometry {
|
|
46
|
+
leftMargin: number;
|
|
47
|
+
viewportWidth: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
37
50
|
/** Expansion, focus, prompt, and store-backed layout state for GraphView. */
|
|
38
51
|
export abstract class GraphViewState {
|
|
39
52
|
protected mode: GraphViewMode;
|
|
@@ -41,7 +54,7 @@ export abstract class GraphViewState {
|
|
|
41
54
|
protected store: Store;
|
|
42
55
|
protected graphTheme: GraphTheme;
|
|
43
56
|
protected onClose?: () => void;
|
|
44
|
-
protected
|
|
57
|
+
protected onQuit?: (runId: string) => void;
|
|
45
58
|
protected onHide?: () => void;
|
|
46
59
|
protected onPromptResolve?: (runId: string, promptId: string, response: unknown) => void;
|
|
47
60
|
protected onStageAttach?: (runId: string, stageId: string) => void;
|
|
@@ -64,6 +77,9 @@ export abstract class GraphViewState {
|
|
|
64
77
|
protected currentSnapshot: StoreSnapshot | null = null;
|
|
65
78
|
protected graphScrollOffset = 0;
|
|
66
79
|
protected graphScrollColOffset = 0;
|
|
80
|
+
protected graphNodeHitRects: GraphNodeHitRect[] = [];
|
|
81
|
+
protected lastGraphViewport: GraphViewportGeometry | null = null;
|
|
82
|
+
protected lastOverlayFrameWidth = 80;
|
|
67
83
|
protected pendingEnsureFocusedVisible = true;
|
|
68
84
|
protected lastAutoFocusedAwaitingInputKey: string | null = null;
|
|
69
85
|
|
|
@@ -77,7 +93,7 @@ export abstract class GraphViewState {
|
|
|
77
93
|
this.store = opts.store;
|
|
78
94
|
this.graphTheme = opts.graphTheme;
|
|
79
95
|
this.onClose = opts.onClose;
|
|
80
|
-
this.
|
|
96
|
+
this.onQuit = opts.onQuit;
|
|
81
97
|
this.onHide = opts.onHide;
|
|
82
98
|
this.onPromptResolve = opts.onPromptResolve;
|
|
83
99
|
this.onStageAttach = opts.onStageAttach;
|
|
@@ -121,6 +137,8 @@ export abstract class GraphViewState {
|
|
|
121
137
|
this.focusedIndex = 0;
|
|
122
138
|
this.graphScrollOffset = 0;
|
|
123
139
|
this.graphScrollColOffset = 0;
|
|
140
|
+
this.graphNodeHitRects = [];
|
|
141
|
+
this.lastGraphViewport = null;
|
|
124
142
|
this.pendingEnsureFocusedVisible = true;
|
|
125
143
|
this.promptState = null;
|
|
126
144
|
return;
|
|
@@ -130,6 +148,8 @@ export abstract class GraphViewState {
|
|
|
130
148
|
const graphStages = this._graphStages(run);
|
|
131
149
|
const nextLayout = computeLayout(graphStages, { orientation: "vertical" });
|
|
132
150
|
this.cachedLayout = nextLayout;
|
|
151
|
+
this.graphNodeHitRects = [];
|
|
152
|
+
this.lastGraphViewport = null;
|
|
133
153
|
|
|
134
154
|
let focusNeedsReveal = this.pendingEnsureFocusedVisible;
|
|
135
155
|
// One-shot: if the host passed `initialFocusedStageId`, snap the
|
|
@@ -10,12 +10,11 @@ export interface GraphViewOpts {
|
|
|
10
10
|
graphTheme: GraphTheme;
|
|
11
11
|
onClose?: () => void;
|
|
12
12
|
/**
|
|
13
|
-
* Invoked when the user presses `q` inside the pane
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* current run.
|
|
13
|
+
* Invoked when the user presses `q` inside the pane. This quits/detaches
|
|
14
|
+
* the orchestrator view and leaves the workflow resumable; it must not use
|
|
15
|
+
* the `/workflow kill` terminal path.
|
|
17
16
|
*/
|
|
18
|
-
|
|
17
|
+
onQuit?: (runId: string) => void;
|
|
19
18
|
/**
|
|
20
19
|
* Invoked when the user presses `h` inside the pane. Hides without
|
|
21
20
|
* unmounting (overlay-adapter calls `setHidden(true)`). Re-open via
|
|
@@ -21,8 +21,7 @@ import type { ChatMessageRenderOptions, ReadonlyFooterDataProvider } from "@bast
|
|
|
21
21
|
import { WorkflowAttachPane } from "./workflow-attach-pane.js";
|
|
22
22
|
import { WORKFLOW_STATUS_KEY } from "./workflow-status.js";
|
|
23
23
|
import { deriveGraphThemeFromPiTheme } from "./graph-theme.js";
|
|
24
|
-
import {
|
|
25
|
-
import { cancellationRegistry } from "../runs/background/cancellation-registry.js";
|
|
24
|
+
import { quitRun as defaultQuitRun } from "../runs/background/quit.js";
|
|
26
25
|
import { stageControlRegistry as defaultStageControlRegistry } from "../runs/foreground/stage-control-registry.js";
|
|
27
26
|
import type { StageControlRegistry } from "../runs/foreground/stage-control-registry.js";
|
|
28
27
|
import type { StageUiBroker } from "../shared/stage-ui-broker.js";
|
|
@@ -100,8 +99,8 @@ const FULLSCREEN_OVERLAY_OPTIONS: PiOverlayOptions = {
|
|
|
100
99
|
margin: 0,
|
|
101
100
|
};
|
|
102
101
|
|
|
103
|
-
const MOUSE_SCROLL_TRACKING_ON = "\x1b[?1000h\x1b[?1006h";
|
|
104
|
-
const MOUSE_SCROLL_TRACKING_OFF = "\x1b[?1006l\x1b[?1000l";
|
|
102
|
+
const MOUSE_SCROLL_TRACKING_ON = "\x1b[?1000h\x1b[?1002h\x1b[?1006h";
|
|
103
|
+
const MOUSE_SCROLL_TRACKING_OFF = "\x1b[?1006l\x1b[?1002l\x1b[?1000l";
|
|
105
104
|
const MAIN_CHAT_INPUT_STATUS_KEY = `${WORKFLOW_STATUS_KEY}:main-chat-input`;
|
|
106
105
|
const MAIN_CHAT_INPUT_STATUS = "Main chat needs input — exit graph to answer.";
|
|
107
106
|
|
|
@@ -119,11 +118,10 @@ export interface BuildGraphOverlayAdapterOpts {
|
|
|
119
118
|
/** Broker used to route stage-local custom UI into attached stage chats. */
|
|
120
119
|
stageUiBroker?: StageUiBroker;
|
|
121
120
|
/**
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* inspection.
|
|
121
|
+
* Quit hook used by graph-mode `q`. This is intentionally distinct from
|
|
122
|
+
* `/workflow kill`: panel quit leaves durable-progress runs resumable.
|
|
125
123
|
*/
|
|
126
|
-
|
|
124
|
+
onQuitRun?: (runId: string) => void;
|
|
127
125
|
/** Optional clock injection for deterministic attach-pane transition tests. */
|
|
128
126
|
now?: () => number;
|
|
129
127
|
}
|
|
@@ -135,8 +133,8 @@ export function buildGraphOverlayAdapter(
|
|
|
135
133
|
): GraphOverlayPort {
|
|
136
134
|
const registry = buildOpts.stageControlRegistry ?? defaultStageControlRegistry;
|
|
137
135
|
const stageUiBroker = buildOpts.stageUiBroker;
|
|
138
|
-
const
|
|
139
|
-
|
|
136
|
+
const quitRun = buildOpts.onQuitRun ?? ((id: string): void => {
|
|
137
|
+
defaultQuitRun(id, { store, stageControlRegistry: registry });
|
|
140
138
|
});
|
|
141
139
|
let currentView: WorkflowAttachPane | null = null;
|
|
142
140
|
// pi-tui returns an OverlayHandle via `options.onHandle`. We hold onto
|
|
@@ -329,7 +327,7 @@ export function buildGraphOverlayAdapter(
|
|
|
329
327
|
uiStatus,
|
|
330
328
|
onClose: finish,
|
|
331
329
|
onHide: hideMounted,
|
|
332
|
-
|
|
330
|
+
onQuit: quitRun,
|
|
333
331
|
initialAttachStageId: stageId,
|
|
334
332
|
piTui: tui,
|
|
335
333
|
piTheme: theme,
|
|
@@ -10,7 +10,10 @@ import {
|
|
|
10
10
|
trailingWidgetBorderChar,
|
|
11
11
|
widgetHintTargetLineIndex,
|
|
12
12
|
} from "./stage-chat-view-render-helpers.js";
|
|
13
|
-
import
|
|
13
|
+
import {
|
|
14
|
+
STAGE_CHAT_MOUSE_SCROLL_TOGGLE_LABEL,
|
|
15
|
+
type StageChatViewContext,
|
|
16
|
+
} from "./stage-chat-view-types.js";
|
|
14
17
|
import type { StageSnapshot } from "../shared/store-types.js";
|
|
15
18
|
import { truncateToWidth, visibleWidth } from "./text-helpers.js";
|
|
16
19
|
|
|
@@ -101,10 +104,13 @@ function mergeOrchestratorReturnHintIntoLine(
|
|
|
101
104
|
width: number,
|
|
102
105
|
options: { preserveTrailingBorder?: boolean; rightMargin?: number } = {},
|
|
103
106
|
): string {
|
|
104
|
-
const
|
|
107
|
+
const copyModeState = ctx.mouseScrollCaptureEnabled ? "off" : "on";
|
|
108
|
+
const plain = `ctrl+d graph · ${STAGE_CHAT_MOUSE_SCROLL_TOGGLE_LABEL} copy mode ${copyModeState}`;
|
|
105
109
|
const styled =
|
|
106
110
|
paint("ctrl+d", ctx.theme.text, { bold: true }) +
|
|
107
|
-
paint("
|
|
111
|
+
paint(" graph · ", ctx.theme.textMuted) +
|
|
112
|
+
paint(STAGE_CHAT_MOUSE_SCROLL_TOGGLE_LABEL, ctx.theme.text, { bold: true }) +
|
|
113
|
+
paint(` copy mode ${copyModeState}`, ctx.theme.textMuted);
|
|
108
114
|
const trailingBorder = options.preserveTrailingBorder === true
|
|
109
115
|
? trailingWidgetBorderChar(line)
|
|
110
116
|
: "";
|
|
@@ -17,9 +17,9 @@ import { releaseMountedCustomUi } from "./stage-chat-view-custom-ui.js";
|
|
|
17
17
|
import {
|
|
18
18
|
canSubmitPrompt,
|
|
19
19
|
currentStage,
|
|
20
|
+
isAbortableStreamingSession,
|
|
20
21
|
isBlocked,
|
|
21
22
|
isReadOnlyArchive,
|
|
22
|
-
isStreaming,
|
|
23
23
|
promptPageSize,
|
|
24
24
|
recordCurrentPromptDraft,
|
|
25
25
|
resolvePromptResponse,
|
|
@@ -30,6 +30,11 @@ export function handleStageChatInput(
|
|
|
30
30
|
ctx: StageChatViewContext,
|
|
31
31
|
data: string,
|
|
32
32
|
): boolean {
|
|
33
|
+
if (matchesKey(data, Key.ctrl("t"))) {
|
|
34
|
+
ctx.mouseScrollCaptureEnabled = !ctx.mouseScrollCaptureEnabled;
|
|
35
|
+
ctx.requestRender?.();
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
33
38
|
if (ctx.mountedCustomUi) {
|
|
34
39
|
return handleMountedCustomUiInput(ctx, data);
|
|
35
40
|
}
|
|
@@ -54,12 +59,16 @@ export function handleStageChatInput(
|
|
|
54
59
|
if (ctx.chatHost.handleScrollInput(data)) return true;
|
|
55
60
|
if (matchesKey(data, Key.escape)) {
|
|
56
61
|
if (
|
|
57
|
-
|
|
62
|
+
ctx.chatHost.isCompacting() ||
|
|
58
63
|
ctx.chatHost.isBashRunning() ||
|
|
59
64
|
ctx.chatHost.isEditingBashCommand()
|
|
60
65
|
) {
|
|
61
66
|
return ctx.chatHost.handleInput(data);
|
|
62
67
|
}
|
|
68
|
+
if (isAbortableStreamingSession(ctx)) {
|
|
69
|
+
void ctx.chatHost.interrupt();
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
63
72
|
ctx.onClose();
|
|
64
73
|
return true;
|
|
65
74
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { AgentSessionEvent } from "@bastani/atomic";
|
|
2
|
+
import type { StageChatViewContext } from "./stage-chat-view-types.js";
|
|
3
|
+
import { isTerminalStageChatState } from "./stage-chat-view-status.js";
|
|
4
|
+
|
|
5
|
+
export function applyStageChatLiveHandleEvent(
|
|
6
|
+
ctx: StageChatViewContext,
|
|
7
|
+
event: AgentSessionEvent,
|
|
8
|
+
): void {
|
|
9
|
+
ctx.chatHost.applyAgentEvent(event);
|
|
10
|
+
if (!shouldCleanupAfterLiveEvent(ctx, event)) return;
|
|
11
|
+
const hadAnimationTick = ctx.chatHost.hasAnimationTick();
|
|
12
|
+
ctx.chatHost.clearBusyForTerminalWorkflowStage();
|
|
13
|
+
if (hadAnimationTick !== ctx.chatHost.hasAnimationTick()) ctx.requestRender?.();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function shouldCleanupAfterLiveEvent(
|
|
17
|
+
ctx: StageChatViewContext,
|
|
18
|
+
event: AgentSessionEvent,
|
|
19
|
+
): boolean {
|
|
20
|
+
if (!isToolExecutionLiveEvent(event)) return false;
|
|
21
|
+
if (ctx.chatHost.isStreaming()) return false;
|
|
22
|
+
return isCurrentRunOrStageTerminal(ctx);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isCurrentRunOrStageTerminal(ctx: StageChatViewContext): boolean {
|
|
26
|
+
return (
|
|
27
|
+
isTerminalStageChatState(ctx.lastObservedRunStatus) ||
|
|
28
|
+
isTerminalStageChatState(ctx.lastObservedStageStatus)
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isToolExecutionLiveEvent(event: AgentSessionEvent): boolean {
|
|
33
|
+
const type = String((event as { type?: unknown }).type ?? "");
|
|
34
|
+
return type === "tool_execution_start" || type === "tool_execution_update";
|
|
35
|
+
}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from "@bastani/atomic";
|
|
5
|
-
import { Editor } from "@earendil-works/pi-tui";
|
|
6
|
-
import type { EditorComponent } from "@earendil-works/pi-tui";
|
|
7
|
-
import type { PendingPrompt, StageSnapshot } from "../shared/store-types.js";
|
|
1
|
+
import { ChatSessionHost, type ChatSessionHostStyle } from "@bastani/atomic";
|
|
2
|
+
import { Editor, type EditorComponent } from "@earendil-works/pi-tui";
|
|
3
|
+
import type { PendingPrompt, RunSnapshot, StageSnapshot } from "../shared/store-types.js";
|
|
8
4
|
import { stageUiBroker } from "../shared/stage-ui-broker.js";
|
|
9
5
|
import { resolveStageChatViewportRows } from "./stage-chat-layout.js";
|
|
10
6
|
import { createPromptCardState } from "./prompt-card.js";
|
|
@@ -33,7 +29,13 @@ import {
|
|
|
33
29
|
type StageChatViewOpts,
|
|
34
30
|
} from "./stage-chat-view-types.js";
|
|
35
31
|
import { noticeRow, noticeSummary } from "./stage-chat-view-transcript.js";
|
|
32
|
+
import { applyStageChatLiveHandleEvent } from "./stage-chat-view-live-events.js";
|
|
36
33
|
import { hexToAnsi, RESET } from "./color-utils.js";
|
|
34
|
+
import {
|
|
35
|
+
isTerminalOrNonStreamingStageChatStatus,
|
|
36
|
+
isTerminalStageChatState,
|
|
37
|
+
isTerminalStageChatTransition,
|
|
38
|
+
} from "./stage-chat-view-status.js";
|
|
37
39
|
|
|
38
40
|
export function initializeStageChatView(
|
|
39
41
|
ctx: StageChatViewContext,
|
|
@@ -66,6 +68,9 @@ export function initializeStageChatView(
|
|
|
66
68
|
ctx.promptScrollOffset = 0;
|
|
67
69
|
ctx.promptMaxScroll = 0;
|
|
68
70
|
ctx.localPaused = false;
|
|
71
|
+
ctx.mouseScrollCaptureEnabled = true;
|
|
72
|
+
ctx.lastObservedStageStatus = undefined;
|
|
73
|
+
ctx.lastObservedRunStatus = undefined;
|
|
69
74
|
ctx.seenNoticeIds = new Set<string>();
|
|
70
75
|
ctx._unsubscribeStore = null;
|
|
71
76
|
ctx._unsubscribeHandle = null;
|
|
@@ -82,16 +87,18 @@ export function initializeStageChatView(
|
|
|
82
87
|
});
|
|
83
88
|
|
|
84
89
|
snapshotMessagesFromHandle(ctx);
|
|
85
|
-
const
|
|
90
|
+
const initialRun = currentRun(ctx);
|
|
91
|
+
const initialStage = initialRun?.stages.find((s) => s.id === ctx.stageId);
|
|
92
|
+
ctx.lastObservedRunStatus = initialRun?.status;
|
|
93
|
+
ctx.lastObservedStageStatus = initialStage?.status;
|
|
86
94
|
snapshotMessagesFromSessionFile(ctx, initialStage);
|
|
87
95
|
absorbStageNotices(ctx, initialStage);
|
|
88
96
|
syncPromptState(ctx, initialStage?.pendingPrompt);
|
|
97
|
+
if (isTerminalStageChatState(initialRun?.status) || isTerminalStageChatState(initialStage?.status)) ctx.chatHost.clearBusyForTerminalWorkflowStage();
|
|
89
98
|
ctx._unsubscribeStore = ctx.store.subscribe(() => handleStoreUpdate(ctx));
|
|
90
99
|
|
|
91
100
|
if (ctx.handle) {
|
|
92
|
-
ctx._unsubscribeHandle = ctx.handle.subscribe((event) =>
|
|
93
|
-
ctx.chatHost.applyAgentEvent(event);
|
|
94
|
-
});
|
|
101
|
+
ctx._unsubscribeHandle = ctx.handle.subscribe((event) => applyStageChatLiveHandleEvent(ctx, event));
|
|
95
102
|
}
|
|
96
103
|
ctx.chatHost.syncAnimationTick();
|
|
97
104
|
}
|
|
@@ -171,7 +178,7 @@ function createChatHost(
|
|
|
171
178
|
isBashRunning: () => liveHandle(ctx)?.agentSession?.isBashRunning === true,
|
|
172
179
|
requestRender: opts.requestRender,
|
|
173
180
|
getAgentSession: () => liveHandle(ctx)?.agentSession,
|
|
174
|
-
isStreaming: () =>
|
|
181
|
+
isStreaming: () => isLiveHandleStreaming(ctx),
|
|
175
182
|
isPaused: () => isPaused(ctx),
|
|
176
183
|
isDisabled: () => isBlocked(ctx) || !liveHandle(ctx),
|
|
177
184
|
tui: opts.piTui,
|
|
@@ -200,7 +207,10 @@ function chatHostStyle(ctx: StageChatViewContext): ChatSessionHostStyle {
|
|
|
200
207
|
}
|
|
201
208
|
|
|
202
209
|
function handleStoreUpdate(ctx: StageChatViewContext): void {
|
|
203
|
-
const
|
|
210
|
+
const run = currentRun(ctx);
|
|
211
|
+
const stage = run?.stages.find((s) => s.id === ctx.stageId);
|
|
212
|
+
const currentRunStatus = run?.status;
|
|
213
|
+
const currentStageStatus = stage?.status;
|
|
204
214
|
let changed = false;
|
|
205
215
|
if (stage && stage.status === "paused" && !ctx.localPaused) {
|
|
206
216
|
ctx.localPaused = true;
|
|
@@ -215,8 +225,15 @@ function handleStoreUpdate(ctx: StageChatViewContext): void {
|
|
|
215
225
|
if (promptChanged && ctx.promptState && canSubmitPrompt(ctx, ctx.promptState.prompt.id)) {
|
|
216
226
|
ctx.requestFocus?.();
|
|
217
227
|
}
|
|
228
|
+
if (isTerminalStageChatTransition(ctx.lastObservedStageStatus, currentStageStatus) || isTerminalStageChatTransition(ctx.lastObservedRunStatus, currentRunStatus)) {
|
|
229
|
+
ctx.chatHost.clearBusyForTerminalWorkflowStage();
|
|
230
|
+
changed = true;
|
|
231
|
+
}
|
|
232
|
+
ctx.lastObservedRunStatus = currentRunStatus;
|
|
233
|
+
ctx.lastObservedStageStatus = currentStageStatus;
|
|
234
|
+
const hadAnimationTick = ctx.chatHost.hasAnimationTick();
|
|
218
235
|
ctx.chatHost.syncAnimationTick();
|
|
219
|
-
if (changed) ctx.requestRender?.();
|
|
236
|
+
if (changed || hadAnimationTick !== ctx.chatHost.hasAnimationTick()) ctx.requestRender?.();
|
|
220
237
|
}
|
|
221
238
|
|
|
222
239
|
function snapshotMessagesFromHandle(ctx: StageChatViewContext): void {
|
|
@@ -256,10 +273,12 @@ function absorbStageNotices(
|
|
|
256
273
|
return changed;
|
|
257
274
|
}
|
|
258
275
|
|
|
276
|
+
export function currentRun(ctx: StageChatViewContext): RunSnapshot | undefined {
|
|
277
|
+
return ctx.store.snapshot().runs.find((r) => r.id === ctx.runId);
|
|
278
|
+
}
|
|
279
|
+
|
|
259
280
|
export function currentStage(ctx: StageChatViewContext): StageSnapshot | undefined {
|
|
260
|
-
|
|
261
|
-
const run = snap.runs.find((r) => r.id === ctx.runId);
|
|
262
|
-
return run?.stages.find((s) => s.id === ctx.stageId);
|
|
281
|
+
return currentRun(ctx)?.stages.find((s) => s.id === ctx.stageId);
|
|
263
282
|
}
|
|
264
283
|
|
|
265
284
|
export function syncPromptState(
|
|
@@ -386,14 +405,29 @@ export function viewLineCount(ctx: StageChatViewContext): number {
|
|
|
386
405
|
return resolveStageChatViewportRows(reported, VIEW_LINE_COUNT);
|
|
387
406
|
}
|
|
388
407
|
|
|
408
|
+
export { isTerminalOrNonStreamingStageChatStatus } from "./stage-chat-view-status.js";
|
|
409
|
+
|
|
389
410
|
export function liveHandle(ctx: StageChatViewContext) {
|
|
390
411
|
return ctx.handle?.isDisposed === true ? undefined : ctx.handle;
|
|
391
412
|
}
|
|
392
413
|
|
|
414
|
+
export function isLiveHandleStreaming(ctx: StageChatViewContext): boolean {
|
|
415
|
+
const handle = liveHandle(ctx);
|
|
416
|
+
if (!handle) return false;
|
|
417
|
+
if (isTerminalOrNonStreamingStageChatStatus(currentRun(ctx)?.status)) return false;
|
|
418
|
+
if (isTerminalOrNonStreamingStageChatStatus(currentStage(ctx)?.status)) return false;
|
|
419
|
+
if (isTerminalOrNonStreamingStageChatStatus(handle.status)) return false;
|
|
420
|
+
return handle.isStreaming === true;
|
|
421
|
+
}
|
|
422
|
+
|
|
393
423
|
export function isStreaming(ctx: StageChatViewContext): boolean {
|
|
394
424
|
return ctx.chatHost.isStreaming();
|
|
395
425
|
}
|
|
396
426
|
|
|
427
|
+
export function isAbortableStreamingSession(ctx: StageChatViewContext): boolean {
|
|
428
|
+
return isLiveHandleStreaming(ctx) || liveHandle(ctx)?.agentSession?.isStreaming === true;
|
|
429
|
+
}
|
|
430
|
+
|
|
397
431
|
export function isBlocked(ctx: StageChatViewContext): boolean {
|
|
398
432
|
return currentStage(ctx)?.status === "blocked";
|
|
399
433
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const TERMINAL_OR_NON_STREAMING_STAGE_CHAT_STATUSES = new Set<string>([
|
|
2
|
+
"success",
|
|
3
|
+
"complete",
|
|
4
|
+
"completed",
|
|
5
|
+
"failure",
|
|
6
|
+
"failed",
|
|
7
|
+
"error",
|
|
8
|
+
"cancellation",
|
|
9
|
+
"cancelled",
|
|
10
|
+
"canceled",
|
|
11
|
+
"paused",
|
|
12
|
+
"detached",
|
|
13
|
+
"killed",
|
|
14
|
+
"stopped",
|
|
15
|
+
"no-longer-running",
|
|
16
|
+
"skipped",
|
|
17
|
+
"blocked",
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
export function isTerminalOrNonStreamingStageChatStatus(
|
|
21
|
+
status: string | undefined,
|
|
22
|
+
): boolean {
|
|
23
|
+
return status !== undefined && TERMINAL_OR_NON_STREAMING_STAGE_CHAT_STATUSES.has(status);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function isTerminalStageChatTransition(
|
|
27
|
+
previousStatus: string | undefined,
|
|
28
|
+
currentStatus: string | undefined,
|
|
29
|
+
): boolean {
|
|
30
|
+
return !isTerminalOrNonStreamingStageChatStatus(previousStatus) &&
|
|
31
|
+
isTerminalOrNonStreamingStageChatStatus(currentStatus);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function isTerminalStageChatState(status: string | undefined): boolean {
|
|
35
|
+
return isTerminalOrNonStreamingStageChatStatus(status);
|
|
36
|
+
}
|