@inhuman.tools/harness-tui 0.0.0-bootstrap.0 → 0.2.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/LICENSE.md +7 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/interpreter/ComputedExecBox.d.ts +12 -0
- package/dist/interpreter/ComputedExecBox.d.ts.map +1 -0
- package/dist/interpreter/ComputedExecBox.js +88 -0
- package/dist/interpreter/ComputedExecBox.js.map +1 -0
- package/dist/interpreter/imageCapability.d.ts +9 -0
- package/dist/interpreter/imageCapability.d.ts.map +1 -0
- package/dist/interpreter/imageCapability.js +49 -0
- package/dist/interpreter/imageCapability.js.map +1 -0
- package/dist/interpreter/rpcCapability.d.ts +11 -0
- package/dist/interpreter/rpcCapability.d.ts.map +1 -0
- package/dist/interpreter/rpcCapability.js +91 -0
- package/dist/interpreter/rpcCapability.js.map +1 -0
- package/dist/interpreter/uiCapability.d.ts +34 -0
- package/dist/interpreter/uiCapability.d.ts.map +1 -0
- package/dist/interpreter/uiCapability.js +207 -0
- package/dist/interpreter/uiCapability.js.map +1 -0
- package/dist/schemas/index.d.ts +2 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +2 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/worker-boot.d.ts +14 -0
- package/dist/schemas/worker-boot.d.ts.map +1 -0
- package/dist/schemas/worker-boot.js +15 -0
- package/dist/schemas/worker-boot.js.map +1 -0
- package/dist/tui/ack-prompt.d.ts +53 -0
- package/dist/tui/ack-prompt.d.ts.map +1 -0
- package/dist/tui/ack-prompt.js +61 -0
- package/dist/tui/ack-prompt.js.map +1 -0
- package/dist/tui/agents-drawer.d.ts +47 -0
- package/dist/tui/agents-drawer.d.ts.map +1 -0
- package/dist/tui/agents-drawer.js +314 -0
- package/dist/tui/agents-drawer.js.map +1 -0
- package/dist/tui/ask-shortcuts.d.ts +59 -0
- package/dist/tui/ask-shortcuts.d.ts.map +1 -0
- package/dist/tui/ask-shortcuts.js +96 -0
- package/dist/tui/ask-shortcuts.js.map +1 -0
- package/dist/tui/channel.d.ts +59 -0
- package/dist/tui/channel.d.ts.map +1 -0
- package/dist/tui/channel.js +27 -0
- package/dist/tui/channel.js.map +1 -0
- package/dist/tui/host-verbs.d.ts +7 -0
- package/dist/tui/host-verbs.d.ts.map +1 -0
- package/dist/tui/host-verbs.js +19 -0
- package/dist/tui/host-verbs.js.map +1 -0
- package/dist/tui/host.d.ts +25 -0
- package/dist/tui/host.d.ts.map +1 -0
- package/dist/tui/host.js +380 -0
- package/dist/tui/host.js.map +1 -0
- package/dist/tui/omnibox-seat.d.ts +132 -0
- package/dist/tui/omnibox-seat.d.ts.map +1 -0
- package/dist/tui/omnibox-seat.js +182 -0
- package/dist/tui/omnibox-seat.js.map +1 -0
- package/dist/tui/paint-controller.d.ts +170 -0
- package/dist/tui/paint-controller.d.ts.map +1 -0
- package/dist/tui/paint-controller.js +965 -0
- package/dist/tui/paint-controller.js.map +1 -0
- package/dist/tui/paint-dom.d.ts +336 -0
- package/dist/tui/paint-dom.d.ts.map +1 -0
- package/dist/tui/paint-dom.js +709 -0
- package/dist/tui/paint-dom.js.map +1 -0
- package/dist/tui/paint-source.d.ts +57 -0
- package/dist/tui/paint-source.d.ts.map +1 -0
- package/dist/tui/paint-source.js +570 -0
- package/dist/tui/paint-source.js.map +1 -0
- package/dist/tui/paint-transport.d.ts +42 -0
- package/dist/tui/paint-transport.d.ts.map +1 -0
- package/dist/tui/paint-transport.js +207 -0
- package/dist/tui/paint-transport.js.map +1 -0
- package/dist/tui/paint-types.d.ts +15 -0
- package/dist/tui/paint-types.d.ts.map +1 -0
- package/dist/tui/paint-types.js +6 -0
- package/dist/tui/paint-types.js.map +1 -0
- package/dist/tui/paint-widgets.d.ts +94 -0
- package/dist/tui/paint-widgets.d.ts.map +1 -0
- package/dist/tui/paint-widgets.js +877 -0
- package/dist/tui/paint-widgets.js.map +1 -0
- package/dist/tui/parse-args.d.ts +3 -0
- package/dist/tui/parse-args.d.ts.map +1 -0
- package/dist/tui/parse-args.js +13 -0
- package/dist/tui/parse-args.js.map +1 -0
- package/dist/tui/permissions-view.d.ts +93 -0
- package/dist/tui/permissions-view.d.ts.map +1 -0
- package/dist/tui/permissions-view.js +174 -0
- package/dist/tui/permissions-view.js.map +1 -0
- package/dist/tui/present-visual/approve.d.ts +10 -0
- package/dist/tui/present-visual/approve.d.ts.map +1 -0
- package/dist/tui/present-visual/approve.js +156 -0
- package/dist/tui/present-visual/approve.js.map +1 -0
- package/dist/tui/present-visual/dump.d.ts +47 -0
- package/dist/tui/present-visual/dump.d.ts.map +1 -0
- package/dist/tui/present-visual/dump.js +144 -0
- package/dist/tui/present-visual/dump.js.map +1 -0
- package/dist/tui/present-visual/journey-teardown.d.ts +3 -0
- package/dist/tui/present-visual/journey-teardown.d.ts.map +1 -0
- package/dist/tui/present-visual/journey-teardown.js +188 -0
- package/dist/tui/present-visual/journey-teardown.js.map +1 -0
- package/dist/tui/present-visual/journey.d.ts +15 -0
- package/dist/tui/present-visual/journey.d.ts.map +1 -0
- package/dist/tui/present-visual/journey.js +88 -0
- package/dist/tui/present-visual/journey.js.map +1 -0
- package/dist/tui/present-visual/raster.d.ts +3 -0
- package/dist/tui/present-visual/raster.d.ts.map +1 -0
- package/dist/tui/present-visual/raster.js +75 -0
- package/dist/tui/present-visual/raster.js.map +1 -0
- package/dist/tui/queue-shortcuts.d.ts +13 -0
- package/dist/tui/queue-shortcuts.d.ts.map +1 -0
- package/dist/tui/queue-shortcuts.js +23 -0
- package/dist/tui/queue-shortcuts.js.map +1 -0
- package/dist/tui/read-widen.d.ts +14 -0
- package/dist/tui/read-widen.d.ts.map +1 -0
- package/dist/tui/read-widen.js +45 -0
- package/dist/tui/read-widen.js.map +1 -0
- package/dist/tui/rewind-chrome.d.ts +29 -0
- package/dist/tui/rewind-chrome.d.ts.map +1 -0
- package/dist/tui/rewind-chrome.js +77 -0
- package/dist/tui/rewind-chrome.js.map +1 -0
- package/dist/tui/rust-present.d.ts +37 -0
- package/dist/tui/rust-present.d.ts.map +1 -0
- package/dist/tui/rust-present.js +205 -0
- package/dist/tui/rust-present.js.map +1 -0
- package/dist/tui/spinner-phase.d.ts +24 -0
- package/dist/tui/spinner-phase.d.ts.map +1 -0
- package/dist/tui/spinner-phase.js +25 -0
- package/dist/tui/spinner-phase.js.map +1 -0
- package/dist/tui/statusline-path.d.ts +10 -0
- package/dist/tui/statusline-path.d.ts.map +1 -0
- package/dist/tui/statusline-path.js +42 -0
- package/dist/tui/statusline-path.js.map +1 -0
- package/dist/tui/sugarcoat-paint.d.ts +14 -0
- package/dist/tui/sugarcoat-paint.d.ts.map +1 -0
- package/dist/tui/sugarcoat-paint.js +146 -0
- package/dist/tui/sugarcoat-paint.js.map +1 -0
- package/dist/tui/tape-spans.d.ts +64 -0
- package/dist/tui/tape-spans.d.ts.map +1 -0
- package/dist/tui/tape-spans.js +93 -0
- package/dist/tui/tape-spans.js.map +1 -0
- package/dist/tui/tea.d.ts +75 -0
- package/dist/tui/tea.d.ts.map +1 -0
- package/dist/tui/tea.js +2952 -0
- package/dist/tui/tea.js.map +1 -0
- package/dist/tui/vt-paint.d.ts +12 -0
- package/dist/tui/vt-paint.d.ts.map +1 -0
- package/dist/tui/vt-paint.js +123 -0
- package/dist/tui/vt-paint.js.map +1 -0
- package/dist/tui/wait-phrase.d.ts +31 -0
- package/dist/tui/wait-phrase.d.ts.map +1 -0
- package/dist/tui/wait-phrase.js +26 -0
- package/dist/tui/wait-phrase.js.map +1 -0
- package/dist/tui/widget-host.d.ts +34 -0
- package/dist/tui/widget-host.d.ts.map +1 -0
- package/dist/tui/widget-host.js +80 -0
- package/dist/tui/widget-host.js.map +1 -0
- package/dist/tui/work-cells.d.ts +47 -0
- package/dist/tui/work-cells.d.ts.map +1 -0
- package/dist/tui/work-cells.js +606 -0
- package/dist/tui/work-cells.js.map +1 -0
- package/dist/tui/worker-entry.d.ts +2 -0
- package/dist/tui/worker-entry.d.ts.map +1 -0
- package/dist/tui/worker-entry.js +98 -0
- package/dist/tui/worker-entry.js.map +1 -0
- package/package.json +84 -4
- package/README.md +0 -1
|
@@ -0,0 +1,877 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness adapters from Paint DOM nodes to retained Quickdraw widgets.
|
|
3
|
+
* Quickdraw stays MST-free. Reactions mark dirty; prepare is untracked.
|
|
4
|
+
*
|
|
5
|
+
* The reaction data function is the dirty set. A JSON-pointer walk is not
|
|
6
|
+
* a second one: it misses completion fields, fuse, collapse, and syntax
|
|
7
|
+
* polarity, and a correct-looking "fix" will propose it. Ancestor rebuild
|
|
8
|
+
* assembles the column; the leaf cache is the cut. Node identity is the
|
|
9
|
+
* MST object (paint-replica); there is no replicated id.
|
|
10
|
+
*
|
|
11
|
+
* A paint_patch arms one tally until the paint source's prepare returns.
|
|
12
|
+
* The line is `seq=<n> rebuilt=<kinds> cached=<kinds>` (`-` when empty).
|
|
13
|
+
* A kind is rebuilt if any prepare of that kind rebuilt; otherwise cached.
|
|
14
|
+
* paint_init is a remount and does not emit. see paint-source, CHANGE ONLY.
|
|
15
|
+
*/
|
|
16
|
+
import { isPathShapedName } from "@inhuman.tools/harness-sdk/text";
|
|
17
|
+
import { collectProgressSites, measureNode } from "@inhuman.tools/quickdraw/layout";
|
|
18
|
+
import { FgP } from "@inhuman.tools/quickdraw/lexicon";
|
|
19
|
+
import { collectBirthSites } from "@inhuman.tools/quickdraw/runtime";
|
|
20
|
+
import { ComposerFrame, completionSlots, createMarkdownExpandStream, expandWidgetPriors, hasThoughtRow, Recall, replaceSpeechBody, Speech, speechBodyNode, speechBodyWidth, syntaxPolarity as defaultSyntaxPolarity, syntaxWarm as defaultSyntaxWarm, Text, ThinkingStream, transcriptColumn, replySpeechRailWidth, userSpeechRailWidth, } from "@inhuman.tools/quickdraw/widgets";
|
|
21
|
+
import { comparer, reaction, untracked } from "mobx";
|
|
22
|
+
import { getParent, hasParent, isStateTreeNode } from "mobx-state-tree";
|
|
23
|
+
import { prepareAgentsDrawer } from "./agents-drawer.js";
|
|
24
|
+
import { failureOf, paintChildrenOf, speechOf, thinkingOf, } from "./paint-dom.js";
|
|
25
|
+
import { clusterThoughtClock, filterFusedTools, groupTapeSpans } from "./tape-spans.js";
|
|
26
|
+
import { WidgetHost } from "./widget-host.js";
|
|
27
|
+
import { askPinNode, receiptFromReplica, workDisclosure } from "./work-cells.js";
|
|
28
|
+
let patchTally = null;
|
|
29
|
+
export function armPatchTally() {
|
|
30
|
+
patchTally ??= { rebuilt: [], cached: [] };
|
|
31
|
+
}
|
|
32
|
+
export function dropPatchTally() {
|
|
33
|
+
patchTally = null;
|
|
34
|
+
}
|
|
35
|
+
export function takePatchTally() {
|
|
36
|
+
const tally = patchTally;
|
|
37
|
+
patchTally = null;
|
|
38
|
+
return tally;
|
|
39
|
+
}
|
|
40
|
+
function recordPrepare(kind, cached) {
|
|
41
|
+
if (patchTally === null)
|
|
42
|
+
return;
|
|
43
|
+
(cached ? patchTally.cached : patchTally.rebuilt).push(kind);
|
|
44
|
+
}
|
|
45
|
+
export function createPaintWidgetHost(sink, syntaxPolarity = defaultSyntaxPolarity, syntaxWarm = defaultSyntaxWarm, local = null) {
|
|
46
|
+
const ctx = { sink, syntaxPolarity, syntaxWarm, local };
|
|
47
|
+
const host = new WidgetHost((node, parent) => createPaintWidget(node, parent, ctx));
|
|
48
|
+
ctx.host = host;
|
|
49
|
+
return host;
|
|
50
|
+
}
|
|
51
|
+
function createPaintWidget(node, parent, ctx) {
|
|
52
|
+
switch (node.kind) {
|
|
53
|
+
case "speech":
|
|
54
|
+
return new SpeechWidget(node, parent, ctx);
|
|
55
|
+
case "thinking":
|
|
56
|
+
return new ThinkingWidget(node, parent, ctx);
|
|
57
|
+
case "failure":
|
|
58
|
+
return new FailureWidget(node, parent, ctx);
|
|
59
|
+
case "recall":
|
|
60
|
+
case "compact":
|
|
61
|
+
return new RecallWidget(node, parent, ctx);
|
|
62
|
+
case "completion":
|
|
63
|
+
return new CompletionWidget(node, parent, ctx);
|
|
64
|
+
case "composer":
|
|
65
|
+
return new ComposerWidget(node, parent, ctx);
|
|
66
|
+
case "agents":
|
|
67
|
+
return new AgentsWidget(node, parent, ctx);
|
|
68
|
+
case "tool":
|
|
69
|
+
case "scheme":
|
|
70
|
+
case "loop":
|
|
71
|
+
case "ask":
|
|
72
|
+
case "wait":
|
|
73
|
+
return new WorkCellWidget(node, parent, ctx);
|
|
74
|
+
case "turn":
|
|
75
|
+
case "stack":
|
|
76
|
+
case "preview":
|
|
77
|
+
case "root":
|
|
78
|
+
return new StackWidget(node, parent, ctx);
|
|
79
|
+
default:
|
|
80
|
+
return new StackWidget(node, parent, ctx);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
class RetainedAdapter {
|
|
84
|
+
node;
|
|
85
|
+
parent;
|
|
86
|
+
ctx;
|
|
87
|
+
#dirty = true;
|
|
88
|
+
#cache = null;
|
|
89
|
+
#stops = [];
|
|
90
|
+
#birthSites = [];
|
|
91
|
+
#progressSites = [];
|
|
92
|
+
disposed = false;
|
|
93
|
+
constructor(node, parent, ctx) {
|
|
94
|
+
this.node = node;
|
|
95
|
+
this.parent = parent;
|
|
96
|
+
this.ctx = ctx;
|
|
97
|
+
this.watch();
|
|
98
|
+
}
|
|
99
|
+
observe(data) {
|
|
100
|
+
this.#stops.push(reaction(data, () => this.markDirty(), { fireImmediately: false, equals: comparer.shallow }));
|
|
101
|
+
}
|
|
102
|
+
markDirty() {
|
|
103
|
+
if (this.disposed)
|
|
104
|
+
return;
|
|
105
|
+
this.#dirty = true;
|
|
106
|
+
this.parent?.markDirty();
|
|
107
|
+
this.ctx.sink.requestPaint();
|
|
108
|
+
}
|
|
109
|
+
prepare(width) {
|
|
110
|
+
return untracked(() => {
|
|
111
|
+
if (!this.#dirty && this.#cache !== null && this.#cache.width === width) {
|
|
112
|
+
recordPrepare(this.node.kind, true);
|
|
113
|
+
return this.#cache;
|
|
114
|
+
}
|
|
115
|
+
const rebuilt = this.rebuild(width);
|
|
116
|
+
const measured = rebuilt.measured ?? measureNode(rebuilt, width, Number.POSITIVE_INFINITY);
|
|
117
|
+
const stamp = { w: measured.w, h: measured.h, atW: width };
|
|
118
|
+
const root = { ...rebuilt, paintReady: true, measured: stamp };
|
|
119
|
+
const sites = this.takeSites(root);
|
|
120
|
+
this.#cache = {
|
|
121
|
+
width,
|
|
122
|
+
root,
|
|
123
|
+
measured: stamp,
|
|
124
|
+
birthSites: sites.birth,
|
|
125
|
+
progressSites: sites.progress,
|
|
126
|
+
};
|
|
127
|
+
this.#dirty = false;
|
|
128
|
+
recordPrepare(this.node.kind, false);
|
|
129
|
+
return this.#cache;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/** Stack/turn rebuild already asked child widgets; reuse their site lists. */
|
|
133
|
+
takeSites(root) {
|
|
134
|
+
if (this.#birthSites.length > 0 ||
|
|
135
|
+
this.#progressSites.length > 0 ||
|
|
136
|
+
this.node.kind === "stack" ||
|
|
137
|
+
this.node.kind === "turn" ||
|
|
138
|
+
this.node.kind === "preview" ||
|
|
139
|
+
this.node.kind === "root") {
|
|
140
|
+
return { birth: this.#birthSites, progress: this.#progressSites };
|
|
141
|
+
}
|
|
142
|
+
return { birth: collectBirthSites(root), progress: collectProgressSites(root) };
|
|
143
|
+
}
|
|
144
|
+
keepSites(birth, progress) {
|
|
145
|
+
this.#birthSites = birth;
|
|
146
|
+
this.#progressSites = progress;
|
|
147
|
+
}
|
|
148
|
+
dispose() {
|
|
149
|
+
if (this.disposed)
|
|
150
|
+
return;
|
|
151
|
+
this.disposed = true;
|
|
152
|
+
for (const stop of this.#stops)
|
|
153
|
+
stop();
|
|
154
|
+
this.#stops = [];
|
|
155
|
+
this.ctx.host.forget(this.node);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function completionOf(node) {
|
|
159
|
+
if (!isStateTreeNode(node))
|
|
160
|
+
return null;
|
|
161
|
+
let current = node;
|
|
162
|
+
for (let depth = 0; depth < 4; depth++) {
|
|
163
|
+
if (!hasParent(current))
|
|
164
|
+
return null;
|
|
165
|
+
current = getParent(current);
|
|
166
|
+
if ("kind" in current && current.kind === "completion") {
|
|
167
|
+
return current;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
export class SpeechWidget extends RetainedAdapter {
|
|
173
|
+
expands = 0;
|
|
174
|
+
#stream = createMarkdownExpandStream();
|
|
175
|
+
#expanded = null;
|
|
176
|
+
watch() {
|
|
177
|
+
this.observe(() => {
|
|
178
|
+
const speech = this.node;
|
|
179
|
+
const completion = completionOf(speech);
|
|
180
|
+
return [
|
|
181
|
+
speech.text,
|
|
182
|
+
speech.voice,
|
|
183
|
+
speech.atMs,
|
|
184
|
+
speech.recap,
|
|
185
|
+
speech.recapHotkeys,
|
|
186
|
+
speech.annotation,
|
|
187
|
+
completion?.state ?? "live",
|
|
188
|
+
this.ctx.syntaxPolarity(),
|
|
189
|
+
this.ctx.syntaxWarm(),
|
|
190
|
+
];
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
rebuild(width) {
|
|
194
|
+
const speech = this.node;
|
|
195
|
+
const key = this.ctx.host.localFor(speech).hitKey;
|
|
196
|
+
if (speech.voice === "user" && speech.recap) {
|
|
197
|
+
return ComposerFrame({
|
|
198
|
+
inputValue: speech.text,
|
|
199
|
+
annotation: emptyToUndef(speech.annotation),
|
|
200
|
+
hotkeys: speech.recapHotkeys,
|
|
201
|
+
interactive: false,
|
|
202
|
+
readOnly: true,
|
|
203
|
+
selected: speech.recap === true,
|
|
204
|
+
}, { key: speech.recap ? `${key}:recap` : key, width, plainBottom: true });
|
|
205
|
+
}
|
|
206
|
+
const completion = completionOf(speech);
|
|
207
|
+
const live = completion?.state === "live";
|
|
208
|
+
const failed = completion?.state === "failed";
|
|
209
|
+
const contentW = speech.voice === "user" ? userSpeechRailWidth(width) : replySpeechRailWidth(width);
|
|
210
|
+
const textW = speechBodyWidth(contentW);
|
|
211
|
+
const node = Speech({ text: speech.text, voice: speech.voice, live, failed, atMs: speech.atMs }, { key, width: contentW });
|
|
212
|
+
const prior = speechBodyNode(node);
|
|
213
|
+
if (failed || prior === undefined)
|
|
214
|
+
return node;
|
|
215
|
+
const polarity = this.ctx.syntaxPolarity();
|
|
216
|
+
const ended = !live;
|
|
217
|
+
if (this.#expanded === null ||
|
|
218
|
+
this.#expanded.text !== speech.text ||
|
|
219
|
+
this.#expanded.width !== textW ||
|
|
220
|
+
this.#expanded.polarity !== polarity ||
|
|
221
|
+
this.#expanded.ended !== ended) {
|
|
222
|
+
this.expands += 1;
|
|
223
|
+
if (this.#expanded !== null && this.#expanded.polarity !== polarity) {
|
|
224
|
+
this.#stream = createMarkdownExpandStream();
|
|
225
|
+
}
|
|
226
|
+
this.#expanded = {
|
|
227
|
+
text: speech.text,
|
|
228
|
+
width: textW,
|
|
229
|
+
polarity,
|
|
230
|
+
ended,
|
|
231
|
+
node: prior.type === "markdown"
|
|
232
|
+
? this.#stream.pull({
|
|
233
|
+
content: prior.content,
|
|
234
|
+
live,
|
|
235
|
+
width: textW,
|
|
236
|
+
key: prior.key,
|
|
237
|
+
style: prior.style,
|
|
238
|
+
})
|
|
239
|
+
: prior,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return replaceSpeechBody(node, this.#expanded.node);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
export class ThinkingWidget extends RetainedAdapter {
|
|
246
|
+
watch() {
|
|
247
|
+
this.observe(() => {
|
|
248
|
+
const thinking = this.node;
|
|
249
|
+
const completion = completionOf(thinking);
|
|
250
|
+
return [
|
|
251
|
+
thinking.text,
|
|
252
|
+
completion?.state ?? "live",
|
|
253
|
+
completion?.workerPresent ?? false,
|
|
254
|
+
completion?.thoughtLive ?? false,
|
|
255
|
+
completion?.retryUntilMs ?? null,
|
|
256
|
+
completion?.thoughtKind ?? "",
|
|
257
|
+
completion?.model ?? "",
|
|
258
|
+
completion?.startedAt ?? null,
|
|
259
|
+
completion?.durationMs ?? null,
|
|
260
|
+
this.ctx.host.localFor(thinking).collapsed,
|
|
261
|
+
];
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
rebuild(width) {
|
|
265
|
+
const thinking = this.node;
|
|
266
|
+
const completion = completionOf(thinking);
|
|
267
|
+
const live = completion?.thoughtLive === true;
|
|
268
|
+
const key = this.ctx.host.localFor(thinking).hitKey;
|
|
269
|
+
return ThinkingStream({
|
|
270
|
+
text: thinking.text,
|
|
271
|
+
live,
|
|
272
|
+
collapsed: this.ctx.host.localFor(thinking).collapsed,
|
|
273
|
+
seal: live ? "live" : "sealed",
|
|
274
|
+
latest: completion?.state === "live",
|
|
275
|
+
startedAt: completion?.startedAt ?? undefined,
|
|
276
|
+
durationMs: completion?.durationMs ?? undefined,
|
|
277
|
+
retryUntilMs: completion?.retryUntilMs ?? undefined,
|
|
278
|
+
bootstrapping: live && !(completion?.workerPresent ?? false),
|
|
279
|
+
thoughtKind: completion?.thoughtKind,
|
|
280
|
+
agentLabel: completion?.model !== undefined && completion.model.trim() !== "" ? completion.model : undefined,
|
|
281
|
+
}, { key: `${key}:thought`, width });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
export class FailureWidget extends RetainedAdapter {
|
|
285
|
+
watch() {
|
|
286
|
+
this.observe(() => {
|
|
287
|
+
const failure = this.node;
|
|
288
|
+
const completion = completionOf(failure);
|
|
289
|
+
return [failure.endDetail, failure.failReason, completion == null ? null : speechOf(completion).atMs];
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
rebuild(width) {
|
|
293
|
+
const failure = this.node;
|
|
294
|
+
const text = failure.endDetail.trim() || failure.failReason.trim() || "turn failed";
|
|
295
|
+
const key = this.ctx.host.localFor(failure).hitKey;
|
|
296
|
+
const completion = completionOf(failure);
|
|
297
|
+
const contentW = replySpeechRailWidth(width);
|
|
298
|
+
return Speech({ text, failed: true, atMs: completion == null ? null : speechOf(completion).atMs }, { key, width: contentW });
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
export class RecallWidget extends RetainedAdapter {
|
|
302
|
+
watch() {
|
|
303
|
+
this.observe(() => this.node.text);
|
|
304
|
+
}
|
|
305
|
+
rebuild(_width) {
|
|
306
|
+
const recall = this.node;
|
|
307
|
+
const key = this.ctx.host.localFor(recall).hitKey;
|
|
308
|
+
return Recall(recall.text, { key });
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
export class CompletionWidget extends RetainedAdapter {
|
|
312
|
+
watch() {
|
|
313
|
+
this.observe(() => {
|
|
314
|
+
const completion = this.node;
|
|
315
|
+
return [
|
|
316
|
+
completion.state,
|
|
317
|
+
completion.workerPresent,
|
|
318
|
+
completion.thoughtLive,
|
|
319
|
+
completion.retryUntilMs,
|
|
320
|
+
completion.thoughtKind,
|
|
321
|
+
completion.startedAt,
|
|
322
|
+
completion.durationMs,
|
|
323
|
+
speechOf(completion).text,
|
|
324
|
+
thinkingOf(completion).text,
|
|
325
|
+
failureOf(completion).endDetail,
|
|
326
|
+
failureOf(completion).failReason,
|
|
327
|
+
completion.debugNote,
|
|
328
|
+
];
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
rebuild(width) {
|
|
332
|
+
const completion = this.node;
|
|
333
|
+
const speech = speechOf(completion);
|
|
334
|
+
const thinking = thinkingOf(completion);
|
|
335
|
+
const failure = failureOf(completion);
|
|
336
|
+
const speechWidget = this.ctx.host.widgetFor(speech, this);
|
|
337
|
+
this.ctx.host.widgetFor(thinking, this);
|
|
338
|
+
const failureWidget = this.ctx.host.widgetFor(failure, this);
|
|
339
|
+
const failed = completion.state === "failed";
|
|
340
|
+
const live = completion.state === "live";
|
|
341
|
+
const slots = completionSlots({
|
|
342
|
+
live,
|
|
343
|
+
failed,
|
|
344
|
+
workerPresent: completion.workerPresent,
|
|
345
|
+
speech: speech.text,
|
|
346
|
+
thought: thinking.text,
|
|
347
|
+
failure: failure.endDetail.trim() || failure.failReason.trim(),
|
|
348
|
+
});
|
|
349
|
+
const key = this.ctx.host.localFor(completion).hitKey;
|
|
350
|
+
const speechNode = slots.showFailure
|
|
351
|
+
? failureWidget.prepare(width).root
|
|
352
|
+
: slots.showSpeech
|
|
353
|
+
? speechWidget.prepare(width).root
|
|
354
|
+
: undefined;
|
|
355
|
+
return (speechNode ?? {
|
|
356
|
+
type: "box",
|
|
357
|
+
key,
|
|
358
|
+
children: [],
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
export class WorkCellWidget extends RetainedAdapter {
|
|
363
|
+
watch() {
|
|
364
|
+
this.observe(() => {
|
|
365
|
+
const cell = this.node;
|
|
366
|
+
const local = this.ctx.host.localFor(cell);
|
|
367
|
+
return [
|
|
368
|
+
cell.kind,
|
|
369
|
+
cell.name,
|
|
370
|
+
cell.object,
|
|
371
|
+
cell.status,
|
|
372
|
+
cell.caption,
|
|
373
|
+
cell.state,
|
|
374
|
+
cell.indent,
|
|
375
|
+
cell.split,
|
|
376
|
+
cell.agent,
|
|
377
|
+
cell.pretty,
|
|
378
|
+
cell.prettyBlock,
|
|
379
|
+
cell.response,
|
|
380
|
+
cell.receiptKind,
|
|
381
|
+
cell.receiptText,
|
|
382
|
+
cell.diffOld,
|
|
383
|
+
cell.diffNew,
|
|
384
|
+
cell.diffPath,
|
|
385
|
+
cell.diffLineStart,
|
|
386
|
+
cell.startedAt,
|
|
387
|
+
cell.retryUntilMs,
|
|
388
|
+
local.collapsed,
|
|
389
|
+
this.ctx.syntaxPolarity(),
|
|
390
|
+
this.ctx.syntaxWarm(),
|
|
391
|
+
];
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
rebuild(width) {
|
|
395
|
+
const cell = this.node;
|
|
396
|
+
const key = this.ctx.host.localFor(cell).hitKey;
|
|
397
|
+
if (cell.kind === "wait") {
|
|
398
|
+
return waitPinNode(cell, key, width);
|
|
399
|
+
}
|
|
400
|
+
if (cell.kind === "ask") {
|
|
401
|
+
const node = askPinNode(faceFromCell(cell, key, false), width);
|
|
402
|
+
if (cell.receiptKind !== "diff")
|
|
403
|
+
return node;
|
|
404
|
+
// prepare() tree. The terminal expands the same face in qd.
|
|
405
|
+
return expandWidgetPriors(node, { width });
|
|
406
|
+
}
|
|
407
|
+
const node = workDisclosure(faceFromCell(cell, key, this.ctx.host.localFor(cell).collapsed), width);
|
|
408
|
+
// This tree is for prepare(). The terminal expands the same face in qd.
|
|
409
|
+
if (cell.receiptKind !== "diff")
|
|
410
|
+
return node;
|
|
411
|
+
return expandWidgetPriors(node, { width });
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function waitErrorText(error, key) {
|
|
415
|
+
return Text(error, {
|
|
416
|
+
key,
|
|
417
|
+
whiteSpace: "nowrap",
|
|
418
|
+
ellipsis: true,
|
|
419
|
+
lineClamp: 1,
|
|
420
|
+
style: { role: "error", prominence: FgP.primary },
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
function waitPinNode(cell, key, width) {
|
|
424
|
+
const error = cell.object.trim();
|
|
425
|
+
if (cell.name === "error") {
|
|
426
|
+
return {
|
|
427
|
+
type: "flex",
|
|
428
|
+
key: `${key}:row`,
|
|
429
|
+
style: { direction: "row", width, justifyContent: "space_between", alignItems: "center" },
|
|
430
|
+
children: [
|
|
431
|
+
{ type: "box", key: `${key}:lead`, style: { flex: 1, height: 1 } },
|
|
432
|
+
waitErrorText(error, `${key}:err`),
|
|
433
|
+
],
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
const stream = ThinkingStream({
|
|
437
|
+
text: "",
|
|
438
|
+
live: true,
|
|
439
|
+
seal: "live",
|
|
440
|
+
startedAt: cell.startedAt ?? undefined,
|
|
441
|
+
retryUntilMs: cell.retryUntilMs ?? undefined,
|
|
442
|
+
bootstrapping: cell.startedAt == null && cell.retryUntilMs == null,
|
|
443
|
+
occupancyLabel: cell.status.trim() === "" ? undefined : cell.status.trim(),
|
|
444
|
+
}, { key, width });
|
|
445
|
+
if (error === "")
|
|
446
|
+
return stream;
|
|
447
|
+
return {
|
|
448
|
+
type: "flex",
|
|
449
|
+
key: `${key}:row`,
|
|
450
|
+
style: { direction: "row", width, justifyContent: "space_between", alignItems: "center" },
|
|
451
|
+
children: [stream, waitErrorText(error, `${key}:err`)],
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
function faceFromCell(cell, key, collapsed) {
|
|
455
|
+
const receipt = receiptFromReplica(cell.receiptKind, cell.receiptText, {
|
|
456
|
+
old: cell.diffOld,
|
|
457
|
+
new: cell.diffNew,
|
|
458
|
+
path: cell.diffPath,
|
|
459
|
+
lineStart: cell.diffLineStart > 1 ? cell.diffLineStart : undefined,
|
|
460
|
+
});
|
|
461
|
+
return {
|
|
462
|
+
key,
|
|
463
|
+
state: cell.state,
|
|
464
|
+
name: cell.name,
|
|
465
|
+
object: cell.object,
|
|
466
|
+
status: cell.status,
|
|
467
|
+
caption: cell.caption,
|
|
468
|
+
pathShaped: !cell.pretty && isPathShapedName(cell.name),
|
|
469
|
+
split: cell.split,
|
|
470
|
+
agent: cell.agent,
|
|
471
|
+
pretty: cell.pretty,
|
|
472
|
+
prettyBlock: cell.prettyBlock,
|
|
473
|
+
response: cell.response,
|
|
474
|
+
indent: cell.indent,
|
|
475
|
+
collapsed,
|
|
476
|
+
receipt,
|
|
477
|
+
rimNamed: cell.split ||
|
|
478
|
+
cell.agent ||
|
|
479
|
+
cell.name === "edit_file" ||
|
|
480
|
+
cell.name === "write_file" ||
|
|
481
|
+
cell.name === "read_file" ||
|
|
482
|
+
cell.name === "list_dir" ||
|
|
483
|
+
cell.name === "grep" ||
|
|
484
|
+
cell.name === "glob" ||
|
|
485
|
+
cell.name === "fff_search",
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
export class ComposerWidget extends RetainedAdapter {
|
|
489
|
+
watch() {
|
|
490
|
+
this.observe(() => {
|
|
491
|
+
const composer = this.node;
|
|
492
|
+
const echo = this.ctx.local?.echo;
|
|
493
|
+
return [
|
|
494
|
+
composer.draft,
|
|
495
|
+
composer.cursor,
|
|
496
|
+
composer.mark,
|
|
497
|
+
composer.interactive,
|
|
498
|
+
composer.readOnly,
|
|
499
|
+
composer.mode,
|
|
500
|
+
composer.placeholder,
|
|
501
|
+
composer.annotation,
|
|
502
|
+
composer.joinAnnotation,
|
|
503
|
+
composer.hotkeys,
|
|
504
|
+
composer.errorLevel,
|
|
505
|
+
composer.errorMessage,
|
|
506
|
+
composer.statusItems,
|
|
507
|
+
composer.quotaWindows,
|
|
508
|
+
composer.contextUsed,
|
|
509
|
+
composer.contextLimit,
|
|
510
|
+
composer.contourRole,
|
|
511
|
+
composer.progress,
|
|
512
|
+
composer.progressColor,
|
|
513
|
+
composer.progressGlowAt,
|
|
514
|
+
composer.progressAnnotation,
|
|
515
|
+
composer.commandChoices,
|
|
516
|
+
composer.commandAction,
|
|
517
|
+
composer.selectedCommandKey,
|
|
518
|
+
composer.scrollOffset,
|
|
519
|
+
composer.stackedBody,
|
|
520
|
+
composer.stackedViewport,
|
|
521
|
+
composer.stackedNumbered,
|
|
522
|
+
composer.stackedLang,
|
|
523
|
+
this.ctx.syntaxPolarity(),
|
|
524
|
+
this.ctx.syntaxWarm(),
|
|
525
|
+
echo?.predicted?.draft ?? null,
|
|
526
|
+
echo?.predicted?.cursor ?? null,
|
|
527
|
+
echo?.predicted?.mark ?? null,
|
|
528
|
+
this.ctx.local?.modHeld ?? false,
|
|
529
|
+
];
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
rebuild(width) {
|
|
533
|
+
const composer = this.node;
|
|
534
|
+
const predicted = composer.interactive ? (this.ctx.local?.echo.predicted ?? null) : null;
|
|
535
|
+
const draft = predicted?.draft ?? composer.draft;
|
|
536
|
+
const cursor = predicted?.cursor ?? composer.cursor;
|
|
537
|
+
const mark = predicted === null ? composer.mark : predicted.mark;
|
|
538
|
+
return ComposerFrame({
|
|
539
|
+
inputValue: draft,
|
|
540
|
+
cursor,
|
|
541
|
+
mark,
|
|
542
|
+
interactive: composer.interactive,
|
|
543
|
+
readOnly: composer.readOnly === true ? true : undefined,
|
|
544
|
+
placeholder: emptyToUndef(composer.placeholder),
|
|
545
|
+
annotation: emptyToUndef(composer.annotation),
|
|
546
|
+
joinAnnotation: emptyToUndef(composer.joinAnnotation),
|
|
547
|
+
hotkeys: composer.hotkeys.length > 0 ? composer.hotkeys : undefined,
|
|
548
|
+
errorAnnotation: composer.errorLevel !== null && composer.errorMessage !== ""
|
|
549
|
+
? { level: composer.errorLevel, message: composer.errorMessage }
|
|
550
|
+
: undefined,
|
|
551
|
+
statusItems: composer.statusItems.length > 0 ? composer.statusItems : undefined,
|
|
552
|
+
quotaWindows: composer.quotaWindows.length > 0 ? composer.quotaWindows : undefined,
|
|
553
|
+
contextUsed: composer.contextLimit > 0 || composer.contextUsed > 0 ? composer.contextUsed : undefined,
|
|
554
|
+
contextLimit: composer.contextLimit > 0 ? composer.contextLimit : undefined,
|
|
555
|
+
contourRole: composer.contourRole === "" ? undefined : composer.contourRole,
|
|
556
|
+
progress: composer.progress ?? undefined,
|
|
557
|
+
progressColor: composer.progressColor === "" ? undefined : composer.progressColor,
|
|
558
|
+
progressGlowAt: composer.progressGlowAt ?? undefined,
|
|
559
|
+
progressAnnotation: composer.progressAnnotation === "" ? undefined : composer.progressAnnotation,
|
|
560
|
+
commandChoices: (composer.commandChoices ?? undefined),
|
|
561
|
+
commandAction: (composer.commandAction ?? undefined),
|
|
562
|
+
selectedCommandKey: composer.selectedCommandKey,
|
|
563
|
+
scrollOffset: composer.scrollOffset,
|
|
564
|
+
stackedBody: emptyToUndef(composer.stackedBody),
|
|
565
|
+
stackedViewport: composer.stackedViewport > 0 ? composer.stackedViewport : undefined,
|
|
566
|
+
stackedNumbered: composer.stackedNumbered === true ? true : undefined,
|
|
567
|
+
stackedLang: emptyToUndef(composer.stackedLang),
|
|
568
|
+
modHeld: this.ctx.local?.modHeld === true,
|
|
569
|
+
}, { key: "composer:frame", width });
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
function emptyToUndef(value) {
|
|
573
|
+
return value === "" ? undefined : value;
|
|
574
|
+
}
|
|
575
|
+
export class AgentsWidget extends RetainedAdapter {
|
|
576
|
+
watch() {
|
|
577
|
+
this.observe(() => {
|
|
578
|
+
const agents = this.node;
|
|
579
|
+
return [agents.open, agents.focusKey, agents.scroll, agents.rows, agents.previewKey];
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
rebuild(width) {
|
|
583
|
+
const agents = this.node;
|
|
584
|
+
const face = {
|
|
585
|
+
meterLeft: agents.meterLeft,
|
|
586
|
+
meterRight: agents.meterRight,
|
|
587
|
+
open: agents.open,
|
|
588
|
+
focusKey: agents.focusKey,
|
|
589
|
+
scroll: agents.scroll,
|
|
590
|
+
rows: agents.rows,
|
|
591
|
+
previewKey: agents.previewKey,
|
|
592
|
+
};
|
|
593
|
+
return prepareAgentsDrawer(face, width);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
export class StackWidget extends RetainedAdapter {
|
|
597
|
+
#partsCache = null;
|
|
598
|
+
watch() {
|
|
599
|
+
this.observe(() => paintChildrenOf(this.node).map((child) => child));
|
|
600
|
+
this.observe(() => this.ctx.local?.tapeExpanded === true);
|
|
601
|
+
// Fuse is a filter, not a child identity. A floor row appears and
|
|
602
|
+
// disappears without the node being replaced.
|
|
603
|
+
this.observe(() => paintChildrenOf(this.node).map((child) => child.kind === "tool" || child.kind === "scheme" ? child.fuse : false));
|
|
604
|
+
}
|
|
605
|
+
markDirty() {
|
|
606
|
+
this.#partsCache = null;
|
|
607
|
+
super.markDirty();
|
|
608
|
+
}
|
|
609
|
+
collectParts(width) {
|
|
610
|
+
return untracked(() => {
|
|
611
|
+
if (this.#partsCache !== null && this.#partsCache.width === width) {
|
|
612
|
+
recordPrepare(this.node.kind, true);
|
|
613
|
+
return this.#partsCache.collect;
|
|
614
|
+
}
|
|
615
|
+
const collect = this.node.kind === "turn"
|
|
616
|
+
? partsForTurnPaint(this.node, this, width)
|
|
617
|
+
: collectTapeParts(this.node, this, width);
|
|
618
|
+
this.#partsCache = { width, collect };
|
|
619
|
+
recordPrepare(this.node.kind, false);
|
|
620
|
+
return collect;
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
rebuild(width) {
|
|
624
|
+
if (this.node.kind === "root") {
|
|
625
|
+
const children = [];
|
|
626
|
+
const birth = [];
|
|
627
|
+
const progress = [];
|
|
628
|
+
for (const child of paintChildrenOf(this.node)) {
|
|
629
|
+
if (child.kind === "composer" ||
|
|
630
|
+
child.kind === "ask" ||
|
|
631
|
+
child.kind === "wait" ||
|
|
632
|
+
child.kind === "statusline" ||
|
|
633
|
+
child.kind === "agents" ||
|
|
634
|
+
child.kind === "preview" ||
|
|
635
|
+
child.kind === "rewind") {
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
children.push(takePrepared(child, this, width, birth, progress));
|
|
639
|
+
}
|
|
640
|
+
this.keepSites(birth, progress);
|
|
641
|
+
const key = this.ctx.host.localFor(this.node).hitKey;
|
|
642
|
+
return { type: "box", key, children };
|
|
643
|
+
}
|
|
644
|
+
const collected = collectTapeParts(this.node, this, width);
|
|
645
|
+
this.keepSites(collected.birth, collected.progress);
|
|
646
|
+
return transcriptColumn(collected.parts, width);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
function bandOf(node) {
|
|
650
|
+
return node.kind === "speech" && node.voice === "user" ? "input" : "work";
|
|
651
|
+
}
|
|
652
|
+
function takePrepared(child, parent, width, birth, progress) {
|
|
653
|
+
const prepared = parent.ctx.host.widgetFor(child, parent).prepare(width);
|
|
654
|
+
birth.push(...prepared.birthSites);
|
|
655
|
+
progress.push(...prepared.progressSites);
|
|
656
|
+
return prepared.root;
|
|
657
|
+
}
|
|
658
|
+
function collectTapeParts(node, parent, width) {
|
|
659
|
+
if (node.kind === "turn")
|
|
660
|
+
return partsForTurnPaint(node, parent, width);
|
|
661
|
+
const parts = [];
|
|
662
|
+
const birth = [];
|
|
663
|
+
const progress = [];
|
|
664
|
+
for (const child of paintChildrenOf(node)) {
|
|
665
|
+
if (child.kind === "composer" ||
|
|
666
|
+
child.kind === "ask" ||
|
|
667
|
+
child.kind === "wait" ||
|
|
668
|
+
child.kind === "agents" ||
|
|
669
|
+
child.kind === "preview" ||
|
|
670
|
+
child.kind === "rewind") {
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
if (child.kind === "turn" || child.kind === "stack") {
|
|
674
|
+
const childWidget = parent.ctx.host.widgetFor(child, parent);
|
|
675
|
+
const inner = childWidget.collectParts(width);
|
|
676
|
+
parts.push(...inner.parts);
|
|
677
|
+
birth.push(...inner.birth);
|
|
678
|
+
progress.push(...inner.progress);
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
parts.push({
|
|
682
|
+
band: bandOf(child),
|
|
683
|
+
key: parent.ctx.host.localFor(child).hitKey,
|
|
684
|
+
node: takePrepared(child, parent, width, birth, progress),
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
return { parts, birth, progress };
|
|
688
|
+
}
|
|
689
|
+
function partsForTurnPaint(turn, parent, width) {
|
|
690
|
+
const items = [];
|
|
691
|
+
for (const child of paintChildrenOf(turn)) {
|
|
692
|
+
if (child.kind === "composer" || child.kind === "ask" || child.kind === "wait" || child.kind === "agents") {
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
if (child.kind === "speech" && child.voice === "user") {
|
|
696
|
+
items.push({ kind: "user", node: child });
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
if (child.kind === "recall" || child.kind === "compact") {
|
|
700
|
+
items.push({ kind: "recall", node: child });
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
if (child.kind === "tool" || child.kind === "scheme" || child.kind === "loop") {
|
|
704
|
+
items.push({ kind: "tool", node: child });
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
if (child.kind !== "completion")
|
|
708
|
+
continue;
|
|
709
|
+
const completion = child;
|
|
710
|
+
parent.ctx.host.widgetFor(completion, parent);
|
|
711
|
+
const slots = slotsOf(completion);
|
|
712
|
+
const hasThought = hasThoughtRow(slots);
|
|
713
|
+
const hasSpeech = slots.showSpeech || slots.showFailure;
|
|
714
|
+
if (hasSpeech) {
|
|
715
|
+
items.push({
|
|
716
|
+
kind: "speech",
|
|
717
|
+
node: completion,
|
|
718
|
+
thought: hasThought ? completion : null,
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
else if (hasThought) {
|
|
722
|
+
items.push({ kind: "thought-only", node: completion });
|
|
723
|
+
}
|
|
724
|
+
else if (completion.debugNote.trim() !== "") {
|
|
725
|
+
// Tool-only ReACT round: no speech/thought row, still a cache mark.
|
|
726
|
+
items.push({ kind: "recall", node: completion });
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
// Peek is a viewer: identity rows stay, independent of host Ctrl-O.
|
|
730
|
+
let expanded = parent.ctx.local?.tapeExpanded === true;
|
|
731
|
+
if (!expanded) {
|
|
732
|
+
for (let cur = parent; cur !== null; cur = cur.parent instanceof RetainedAdapter ? cur.parent : null) {
|
|
733
|
+
if (cur.node.kind === "preview") {
|
|
734
|
+
expanded = true;
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
const density = expanded ? items : filterFusedTools(items, cellFuses);
|
|
740
|
+
const spans = groupTapeSpans(density);
|
|
741
|
+
let lastThought = -1;
|
|
742
|
+
for (const [i, span] of spans.entries()) {
|
|
743
|
+
if (span.kind === "thoughts")
|
|
744
|
+
lastThought = i;
|
|
745
|
+
}
|
|
746
|
+
const parts = [];
|
|
747
|
+
const birth = [];
|
|
748
|
+
const progress = [];
|
|
749
|
+
for (const [i, span_] of spans.entries()) {
|
|
750
|
+
const span = span_;
|
|
751
|
+
if (span.kind === "user" || span.kind === "recall" || span.kind === "speech") {
|
|
752
|
+
if (span.kind === "recall" && span.node.kind === "completion") {
|
|
753
|
+
pushCacheMark(parts, span.node, parent);
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
parts.push({
|
|
757
|
+
band: bandOf(span.node),
|
|
758
|
+
slot: span.kind === "speech" ? "speech" : undefined,
|
|
759
|
+
key: parent.ctx.host.localFor(span.node).hitKey,
|
|
760
|
+
node: takePrepared(span.node, parent, width, birth, progress),
|
|
761
|
+
});
|
|
762
|
+
if (span.kind === "speech") {
|
|
763
|
+
pushCacheMark(parts, span.node, parent);
|
|
764
|
+
}
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
if (span.kind === "tools") {
|
|
768
|
+
for (const cell of span.nodes) {
|
|
769
|
+
parts.push({
|
|
770
|
+
band: "work",
|
|
771
|
+
slot: "tool",
|
|
772
|
+
key: parent.ctx.host.localFor(cell).hitKey,
|
|
773
|
+
node: takePrepared(cell, parent, width, birth, progress),
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
const node = thoughtSpanNode(span.nodes, parent, width, i === lastThought);
|
|
779
|
+
if (node !== undefined) {
|
|
780
|
+
parts.push({
|
|
781
|
+
band: "work",
|
|
782
|
+
slot: "thought",
|
|
783
|
+
key: node.key ?? parent.ctx.host.localFor(span.nodes[0]).hitKey,
|
|
784
|
+
node,
|
|
785
|
+
});
|
|
786
|
+
birth.push(...collectBirthSites(node));
|
|
787
|
+
progress.push(...collectProgressSites(node));
|
|
788
|
+
}
|
|
789
|
+
for (const completion of span.nodes) {
|
|
790
|
+
const slots = slotsOf(completion);
|
|
791
|
+
if (slots.showSpeech || slots.showFailure)
|
|
792
|
+
continue;
|
|
793
|
+
pushCacheMark(parts, completion, parent);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
return { parts, birth, progress };
|
|
797
|
+
}
|
|
798
|
+
function pushCacheMark(parts, completion, parent) {
|
|
799
|
+
const note = completion.debugNote.trim();
|
|
800
|
+
if (note === "")
|
|
801
|
+
return;
|
|
802
|
+
const key = `${parent.ctx.host.localFor(completion).hitKey}:cache`;
|
|
803
|
+
parts.push({
|
|
804
|
+
band: "work",
|
|
805
|
+
key,
|
|
806
|
+
node: Recall(note, { key }),
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
function cellFuses(node) {
|
|
810
|
+
if (node.kind === "loop")
|
|
811
|
+
return false;
|
|
812
|
+
if (node.kind !== "tool" && node.kind !== "scheme")
|
|
813
|
+
return false;
|
|
814
|
+
return node.fuse === true;
|
|
815
|
+
}
|
|
816
|
+
function slotsOf(completion) {
|
|
817
|
+
const speech = speechOf(completion);
|
|
818
|
+
const thinking = thinkingOf(completion);
|
|
819
|
+
const failure = failureOf(completion);
|
|
820
|
+
return completionSlots({
|
|
821
|
+
live: completion.state === "live",
|
|
822
|
+
failed: completion.state === "failed",
|
|
823
|
+
workerPresent: completion.workerPresent,
|
|
824
|
+
speech: speech.text,
|
|
825
|
+
thought: thinking.text,
|
|
826
|
+
failure: failure.endDetail.trim() || failure.failReason.trim(),
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
function thoughtSpanNode(completions, parent, width, latestSpan) {
|
|
830
|
+
const first = completions[0];
|
|
831
|
+
if (first === undefined)
|
|
832
|
+
return undefined;
|
|
833
|
+
const texts = [];
|
|
834
|
+
let agentLabel;
|
|
835
|
+
const kinds = [];
|
|
836
|
+
const clockMembers = [];
|
|
837
|
+
let retryUntilMs;
|
|
838
|
+
for (const completion of completions) {
|
|
839
|
+
const thinking = thinkingOf(completion);
|
|
840
|
+
parent.ctx.host.widgetFor(thinking, parent.ctx.host.widgetFor(completion, parent));
|
|
841
|
+
const body = thinking.text.replaceAll(/^\s+|\s+$/gu, "");
|
|
842
|
+
if (body !== "")
|
|
843
|
+
texts.push(body);
|
|
844
|
+
kinds.push(completion.thoughtKind);
|
|
845
|
+
clockMembers.push({
|
|
846
|
+
live: completion.thoughtLive,
|
|
847
|
+
startedAt: completion.startedAt,
|
|
848
|
+
durationMs: completion.durationMs,
|
|
849
|
+
});
|
|
850
|
+
if (completion.retryUntilMs != null && Number.isFinite(completion.retryUntilMs)) {
|
|
851
|
+
retryUntilMs = completion.retryUntilMs;
|
|
852
|
+
}
|
|
853
|
+
if (agentLabel === undefined && completion.model.trim() !== "")
|
|
854
|
+
agentLabel = completion.model;
|
|
855
|
+
}
|
|
856
|
+
const clock = clusterThoughtClock(clockMembers);
|
|
857
|
+
const thinking = thinkingOf(first);
|
|
858
|
+
const local = parent.ctx.host.localFor(thinking);
|
|
859
|
+
const latest = clock.live || latestSpan;
|
|
860
|
+
const bootstrapping = clock.live &&
|
|
861
|
+
completions.some((completion) => completion.thoughtLive && !completion.workerPresent) &&
|
|
862
|
+
!completions.some((completion) => completion.thoughtLive && completion.workerPresent);
|
|
863
|
+
return ThinkingStream({
|
|
864
|
+
text: texts.join("\n\n"),
|
|
865
|
+
live: clock.live,
|
|
866
|
+
collapsed: local.collapsed,
|
|
867
|
+
seal: clock.live ? "live" : "sealed",
|
|
868
|
+
latest,
|
|
869
|
+
startedAt: clock.startedAt,
|
|
870
|
+
durationMs: clock.durationMs,
|
|
871
|
+
retryUntilMs,
|
|
872
|
+
bootstrapping,
|
|
873
|
+
thoughtKind: kinds.includes("raw") ? "raw" : kinds.includes("summary") ? "summary" : "",
|
|
874
|
+
agentLabel,
|
|
875
|
+
}, { key: `${local.hitKey}:thought`, width });
|
|
876
|
+
}
|
|
877
|
+
//# sourceMappingURL=paint-widgets.js.map
|