@visual-z/xeno 0.2.1 → 0.4.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 +18 -0
- package/README.md +126 -34
- package/docs/app-server.md +84 -0
- package/docs/install.md +51 -13
- package/docs/providers.md +24 -3
- package/package.json +7 -5
- package/src/apps/app-server.ts +28 -0
- package/src/apps/approve.ts +3 -3
- package/src/apps/cli.ts +25 -10
- package/src/apps/commands.ts +45 -8
- package/src/apps/completions.ts +78 -22
- package/src/apps/control-transport.ts +89 -0
- package/src/apps/image-draft.ts +46 -0
- package/src/apps/model-catalog.ts +2 -1
- package/src/apps/project-trust.ts +2 -42
- package/src/apps/provider-connect.ts +346 -0
- package/src/apps/render.ts +22 -47
- package/src/apps/resources.ts +5 -2
- package/src/apps/shell.ts +333 -1542
- package/src/apps/startup-input.ts +45 -9
- package/src/apps/startup.ts +149 -94
- package/src/apps/ui/app.ts +1553 -0
- package/src/apps/ui/control-model.ts +26 -0
- package/src/apps/ui/extension-host.ts +194 -0
- package/src/apps/ui/image-cache.ts +78 -0
- package/src/apps/ui/palette.ts +88 -0
- package/src/apps/ui/project.ts +127 -0
- package/src/apps/ui/standalone.ts +146 -0
- package/src/apps/ui/status-model.ts +68 -0
- package/src/apps/ui/surface.ts +86 -0
- package/src/apps/ui/theme.ts +86 -0
- package/src/core/acceptance.ts +107 -0
- package/src/core/action-defaults.ts +47 -295
- package/src/core/benchmark.ts +2 -2
- package/src/core/branch-receipt.ts +24 -0
- package/src/core/checkpoints.ts +64 -0
- package/src/core/churn.ts +2 -0
- package/src/core/command-artifacts.ts +94 -0
- package/src/core/command-contract.ts +35 -0
- package/src/core/completion-action.ts +20 -0
- package/src/core/completion-review.ts +37 -21
- package/src/core/context-detail.ts +37 -0
- package/src/core/context-policy.ts +74 -1
- package/src/core/continuation.ts +17 -2
- package/src/core/control-protocol.ts +62 -0
- package/src/core/control-view.ts +42 -0
- package/src/core/derive.ts +29 -3
- package/src/core/draft-images.ts +56 -0
- package/src/core/environment.ts +145 -0
- package/src/core/evaluation-coverage.ts +74 -0
- package/src/core/event-validation.ts +81 -6
- package/src/core/events.ts +99 -3
- package/src/core/evidence-links.ts +7 -0
- package/src/core/evidence.ts +13 -3
- package/src/core/execution.ts +8 -2
- package/src/core/experiment.ts +133 -0
- package/src/core/export.ts +9 -0
- package/src/core/failure-recovery.ts +173 -0
- package/src/core/file-precondition.ts +72 -0
- package/src/core/handoff.ts +65 -4
- package/src/core/harness-diagnostics.ts +206 -0
- package/src/core/history-query.ts +38 -2
- package/src/core/history.ts +38 -1
- package/src/core/integration-health.ts +34 -0
- package/src/core/keymap.ts +8 -20
- package/src/core/model-picker.ts +64 -0
- package/src/core/native-eval.ts +25 -0
- package/src/core/note-guidance.ts +34 -0
- package/src/core/oauth-fields.ts +44 -0
- package/src/core/observation-view.ts +49 -8
- package/src/core/permissions.ts +24 -2
- package/src/core/process-receipt.ts +105 -0
- package/src/core/process-state.ts +37 -0
- package/src/core/program-status.ts +31 -0
- package/src/core/project.ts +70 -19
- package/src/core/prompt-policy.ts +37 -0
- package/src/core/query-eval-contract.ts +14 -0
- package/src/core/read-observation.ts +82 -0
- package/src/core/recovery-corpus.ts +17 -0
- package/src/core/recovery-evaluation.ts +60 -0
- package/src/core/recovery-sources.ts +74 -0
- package/src/core/request-progress.ts +87 -0
- package/src/core/request-state.ts +114 -0
- package/src/core/runtime-target.ts +94 -0
- package/src/core/search-observation.ts +97 -0
- package/src/core/session-tree.ts +147 -0
- package/src/core/settings.ts +33 -10
- package/src/core/skills.ts +44 -0
- package/src/core/sql-query.ts +211 -0
- package/src/core/theme.ts +36 -58
- package/src/core/tool-contract.ts +51 -2
- package/src/core/tool-discovery.ts +87 -0
- package/src/core/tool-failure.ts +60 -0
- package/src/core/tool-input.ts +6 -0
- package/src/core/tool-mode-protocol.ts +25 -0
- package/src/core/transcript.ts +19 -4
- package/src/core/trial-lifecycle.ts +14 -0
- package/src/core/tui-mode.ts +29 -0
- package/src/core/update-check.ts +97 -0
- package/src/core/version.ts +55 -0
- package/src/core/work-state.ts +49 -3
- package/src/core/working-set.ts +91 -0
- package/src/runtime/auth.ts +11 -4
- package/src/runtime/branch-jobs.ts +36 -0
- package/src/runtime/catalog.ts +13 -3
- package/src/runtime/clipboard.ts +15 -4
- package/src/runtime/command-artifacts.ts +64 -0
- package/src/runtime/control-connection.ts +104 -0
- package/src/runtime/environment-tools.ts +60 -0
- package/src/runtime/evidence-notes.ts +4 -2
- package/src/runtime/exec-sessions.ts +206 -61
- package/src/runtime/execution-control.ts +1 -0
- package/src/runtime/extension-ui.ts +22 -30
- package/src/runtime/extensions.ts +95 -16
- package/src/runtime/harness-comparison.ts +27 -7
- package/src/runtime/harness-eval.ts +462 -0
- package/src/runtime/history-cache.ts +57 -0
- package/src/runtime/history-tools.ts +62 -12
- package/src/runtime/integrations.ts +210 -0
- package/src/runtime/loop.ts +1088 -282
- package/src/runtime/model-discovery.ts +208 -0
- package/src/runtime/model.ts +9 -0
- package/src/runtime/native-eval-fixtures.ts +33 -0
- package/src/runtime/native-eval.ts +146 -0
- package/src/runtime/oauth.ts +80 -3
- package/src/runtime/pi-provider.ts +169 -12
- package/src/runtime/program-status.ts +38 -0
- package/src/runtime/provider-connect.ts +109 -0
- package/src/runtime/provider-presets.ts +87 -0
- package/src/runtime/provider-request.ts +13 -2
- package/src/runtime/provider.ts +23 -9
- package/src/runtime/query-eval-verifier.ts +28 -0
- package/src/runtime/recovery-coverage.ts +71 -0
- package/src/runtime/recovery-evidence.ts +76 -0
- package/src/runtime/recovery-fixtures.ts +116 -0
- package/src/runtime/runtime-target.ts +149 -0
- package/src/runtime/session-control.ts +198 -0
- package/src/runtime/tool-broker.ts +137 -0
- package/src/runtime/tool-discovery.ts +57 -0
- package/src/runtime/tool-mode-eval.ts +146 -0
- package/src/runtime/tool-queue.ts +77 -0
- package/src/runtime/tools.ts +628 -183
- package/src/runtime/trial-control.ts +45 -0
- package/src/runtime/update-check.ts +47 -0
- package/src/runtime/window-trial.ts +2 -1
- package/src/runtime/working-notes.ts +33 -3
- package/src/runtime/workspace-lifecycle.ts +97 -0
- package/src/store/acceptance.ts +15 -0
- package/src/store/checkpoint-history.ts +43 -0
- package/src/store/command-artifacts.ts +96 -0
- package/src/store/command.ts +65 -37
- package/src/store/file-checkpoints.ts +104 -0
- package/src/store/file-index.ts +13 -1
- package/src/store/git-checkpoints.ts +67 -0
- package/src/store/observations.ts +61 -20
- package/src/store/process-receipts.ts +34 -0
- package/src/store/query-source.ts +13 -8
- package/src/store/recovery.ts +2 -3
- package/src/store/replay.ts +16 -4
- package/src/store/runtime-target.ts +64 -0
- package/src/store/search.ts +171 -132
- package/src/store/session-archive.ts +4 -0
- package/src/store/source-cancellation.ts +6 -0
- package/src/store/update-cache.ts +36 -0
- package/src/store/workspace-observation.ts +113 -0
- package/src/tui/ansi-spans.ts +126 -0
- package/src/tui/style.ts +19 -16
- package/src/tui/ui/atomic-draft.ts +121 -0
- package/src/tui/ui/canvas.ts +96 -0
- package/src/tui/ui/colors.ts +70 -0
- package/src/tui/ui/composer.ts +200 -0
- package/src/tui/ui/control-panel.ts +23 -0
- package/src/tui/ui/dialog.ts +379 -0
- package/src/tui/ui/draft.ts +23 -0
- package/src/tui/ui/edge.ts +186 -0
- package/src/tui/ui/image-size.ts +40 -0
- package/src/tui/ui/ink.ts +210 -0
- package/src/tui/ui/items.ts +40 -0
- package/src/tui/ui/keys.ts +40 -0
- package/src/tui/ui/layout.ts +74 -0
- package/src/tui/ui/list-model.ts +86 -0
- package/src/tui/ui/list-view.ts +172 -0
- package/src/tui/ui/mascot.ts +67 -0
- package/src/tui/ui/messages.ts +518 -0
- package/src/tui/ui/motion.ts +28 -0
- package/src/tui/ui/paths.ts +10 -0
- package/src/tui/ui/pictures.ts +78 -0
- package/src/tui/ui/screen.ts +421 -0
- package/src/tui/ui/status.ts +279 -0
- package/src/tui/ui/styled.ts +85 -0
- package/src/tui/ui/testing.ts +5 -0
- package/src/tui/ui/tool-card.ts +150 -0
- package/src/apps/extension-ui.ts +0 -330
- package/src/apps/resource-browser.ts +0 -75
- package/src/apps/session-browser.ts +0 -113
- package/src/apps/session-picker.ts +0 -55
- package/src/tui/ansi.ts +0 -67
- package/src/tui/autocomplete.ts +0 -29
- package/src/tui/chat-viewport.ts +0 -47
- package/src/tui/complete.ts +0 -87
- package/src/tui/editor.ts +0 -940
- package/src/tui/host.ts +0 -264
- package/src/tui/input.ts +0 -75
- package/src/tui/keys.ts +0 -378
- package/src/tui/markdown.ts +0 -17
- package/src/tui/render.ts +0 -153
- package/src/tui/select.ts +0 -258
- package/src/tui/settings.ts +0 -44
- package/src/tui/statusline.ts +0 -162
- package/src/tui/system-theme.ts +0 -700
- package/src/tui/terminal.ts +0 -30
- package/src/tui/text.ts +0 -52
- package/src/tui/toolview.ts +0 -386
- package/src/tui/transcript.ts +0 -123
- package/src/tui/tree.ts +0 -471
- package/src/vendor/pi-tui/LICENSE.upstream +0 -21
- package/src/vendor/pi-tui/UPSTREAM.json +0 -50
- package/src/vendor/pi-tui/VENDOR.md +0 -25
- package/src/vendor/pi-tui/alt-screen-search.ts +0 -327
- package/src/vendor/pi-tui/autocomplete.ts +0 -860
- package/src/vendor/pi-tui/colors.ts +0 -367
- package/src/vendor/pi-tui/components/alt-screen-flash.ts +0 -51
- package/src/vendor/pi-tui/components/box.ts +0 -166
- package/src/vendor/pi-tui/components/cancellable-loader.ts +0 -40
- package/src/vendor/pi-tui/components/editor.ts +0 -2472
- package/src/vendor/pi-tui/components/h-stack.ts +0 -44
- package/src/vendor/pi-tui/components/image.ts +0 -127
- package/src/vendor/pi-tui/components/input.ts +0 -494
- package/src/vendor/pi-tui/components/loader.ts +0 -101
- package/src/vendor/pi-tui/components/markdown.ts +0 -1021
- package/src/vendor/pi-tui/components/mouse-region.ts +0 -33
- package/src/vendor/pi-tui/components/scroll-view.ts +0 -224
- package/src/vendor/pi-tui/components/select-list.ts +0 -273
- package/src/vendor/pi-tui/components/settings-list.ts +0 -328
- package/src/vendor/pi-tui/components/spacer.ts +0 -28
- package/src/vendor/pi-tui/components/stack.ts +0 -154
- package/src/vendor/pi-tui/components/text.ts +0 -107
- package/src/vendor/pi-tui/components/truncated-text.ts +0 -65
- package/src/vendor/pi-tui/components/v-stack.ts +0 -33
- package/src/vendor/pi-tui/editor-component.ts +0 -74
- package/src/vendor/pi-tui/fuzzy.ts +0 -138
- package/src/vendor/pi-tui/index.ts +0 -186
- package/src/vendor/pi-tui/keybindings.ts +0 -320
- package/src/vendor/pi-tui/keys.ts +0 -1401
- package/src/vendor/pi-tui/kill-ring.ts +0 -46
- package/src/vendor/pi-tui/latex.ts +0 -1506
- package/src/vendor/pi-tui/layout-node.ts +0 -51
- package/src/vendor/pi-tui/layout.ts +0 -449
- package/src/vendor/pi-tui/native/UPSTREAM.json +0 -23
- package/src/vendor/pi-tui/native/clipboard.h +0 -208
- package/src/vendor/pi-tui/native/darwin/README.md +0 -22
- package/src/vendor/pi-tui/native/darwin/build.sh +0 -69
- package/src/vendor/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
- package/src/vendor/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
- package/src/vendor/pi-tui/native/darwin/src/darwin-platform.m +0 -132
- package/src/vendor/pi-tui/native/linux/README.md +0 -23
- package/src/vendor/pi-tui/native/linux/build.sh +0 -65
- package/src/vendor/pi-tui/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
- package/src/vendor/pi-tui/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
- package/src/vendor/pi-tui/native/linux/src/clipboard-worker.h +0 -70
- package/src/vendor/pi-tui/native/linux/src/linux-platform-x11.c +0 -274
- package/src/vendor/pi-tui/native/napi.h +0 -91
- package/src/vendor/pi-tui/native/win32/README.md +0 -31
- package/src/vendor/pi-tui/native/win32/build.mjs +0 -232
- package/src/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
- package/src/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
- package/src/vendor/pi-tui/native/win32/src/win32-platform.c +0 -241
- package/src/vendor/pi-tui/native-modifiers.ts +0 -13
- package/src/vendor/pi-tui/native-module-path.ts +0 -31
- package/src/vendor/pi-tui/native-platform.ts +0 -65
- package/src/vendor/pi-tui/oklab.ts +0 -233
- package/src/vendor/pi-tui/stdin-buffer.ts +0 -444
- package/src/vendor/pi-tui/terminal-colors.ts +0 -91
- package/src/vendor/pi-tui/terminal-image.ts +0 -731
- package/src/vendor/pi-tui/terminal.ts +0 -554
- package/src/vendor/pi-tui/tui-alt-screen.ts +0 -1750
- package/src/vendor/pi-tui/tui-main-screen.ts +0 -655
- package/src/vendor/pi-tui/tui.ts +0 -1493
- package/src/vendor/pi-tui/undo-stack.ts +0 -28
- package/src/vendor/pi-tui/utils.ts +0 -1385
- package/src/vendor/pi-tui/wheel-scroll.ts +0 -82
- package/src/vendor/pi-tui/word-navigation.ts +0 -117
package/src/tui/terminal.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The only thing a renderer can be observed to do is write bytes.
|
|
3
|
-
*
|
|
4
|
-
* A real tty makes that unassertable -- tests would have to inspect a live
|
|
5
|
-
* terminal's screen, and would change its state while doing so. Injecting
|
|
6
|
-
* this interface is what makes "a frame that changed nothing writes nothing"
|
|
7
|
-
* a unit test rather than a hope.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
export interface Terminal {
|
|
11
|
-
readonly columns: number;
|
|
12
|
-
readonly rows: number;
|
|
13
|
-
write(data: string): void;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* A pty opened without TIOCSWINSZ reports `columns === 0`, not
|
|
18
|
-
* `undefined`. `n ?? 80` keeps the zero, SelectList's truncate budget
|
|
19
|
-
* goes negative, and every label becomes "" -- `/model` shows only `→`.
|
|
20
|
-
* Treat non-positive as "size unknown" and fall back to the same 80
|
|
21
|
-
* StdoutTerminal already used for a missing ioctl.
|
|
22
|
-
*/
|
|
23
|
-
export function usableColumns(n: number | undefined): number {
|
|
24
|
-
return n && n > 0 ? n : 80;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Same trap as usableColumns: a 0-row ioctl is not a 0-row screen. */
|
|
28
|
-
export function usableRows(n: number | undefined): number {
|
|
29
|
-
return n && n > 0 ? n : 24;
|
|
30
|
-
}
|
package/src/tui/text.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The one component this kernel ships. It exists to prove the renderer is
|
|
3
|
-
* actually painting something; Editor / SelectList / Markdown are a later card.
|
|
4
|
-
*
|
|
5
|
-
* Wrapping is by display width, not by string length: a CJK character that
|
|
6
|
-
* would overflow the last column starts a new line instead of being split,
|
|
7
|
-
* because a terminal cell cannot hold half of it.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { displayWidth } from "./width.ts";
|
|
11
|
-
|
|
12
|
-
export interface Component {
|
|
13
|
-
render(width: number): string[];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export class Text implements Component {
|
|
17
|
-
private readonly content: string;
|
|
18
|
-
|
|
19
|
-
constructor(content: string) {
|
|
20
|
-
this.content = content;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
render(width: number): string[] {
|
|
24
|
-
const out: string[] = [];
|
|
25
|
-
for (const paragraph of this.content.split("\n")) {
|
|
26
|
-
out.push(...wrapLine(paragraph, width));
|
|
27
|
-
}
|
|
28
|
-
return out;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function wrapLine(s: string, width: number): string[] {
|
|
33
|
-
if (width <= 0) return [s];
|
|
34
|
-
const lines: string[] = [];
|
|
35
|
-
let current = "";
|
|
36
|
-
let currentW = 0;
|
|
37
|
-
for (const ch of s) {
|
|
38
|
-
const w = displayWidth(ch);
|
|
39
|
-
// Combining marks attach to whatever is already in the line; they never
|
|
40
|
-
// start a wrap on their own.
|
|
41
|
-
if (w > 0 && currentW + w > width && current.length > 0) {
|
|
42
|
-
lines.push(current);
|
|
43
|
-
current = ch;
|
|
44
|
-
currentW = w;
|
|
45
|
-
} else {
|
|
46
|
-
current += ch;
|
|
47
|
-
currentW += w;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
lines.push(current);
|
|
51
|
-
return lines;
|
|
52
|
-
}
|
package/src/tui/toolview.ts
DELETED
|
@@ -1,386 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool-call / tool-result / thinking layout, including collapse.
|
|
3
|
-
*
|
|
4
|
-
* Pure: renderToolView(event, expanded, width, style) → string[]. Collapse
|
|
5
|
-
* is an argument, never log state — the model does not need to know which
|
|
6
|
-
* block the user folded (feat-message-rendering design).
|
|
7
|
-
*
|
|
8
|
-
* LiveTranscript owns the uncommitted tail. Toggle re-renders that tail
|
|
9
|
-
* through the line-diff renderer. Committed scrollback is the terminal's
|
|
10
|
-
* and is never rewritten; pi's `\x1b[2J\x1b[H\x1b[3J` is refused because
|
|
11
|
-
* it wipes history this process did not write.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { assertNever } from "./exhaustive.ts";
|
|
15
|
-
import { renderMarkdown } from "./markdown.ts";
|
|
16
|
-
import { renderDiff, emitPrefixed, innerWidth } from "./diff.ts";
|
|
17
|
-
import { Renderer, fitLive } from "./render.ts";
|
|
18
|
-
import {
|
|
19
|
-
ANSI,
|
|
20
|
-
PLAIN_ATTRS,
|
|
21
|
-
type Attrs,
|
|
22
|
-
type Style,
|
|
23
|
-
} from "./style.ts";
|
|
24
|
-
import { usableColumns, usableRows, type Terminal } from "./terminal.ts";
|
|
25
|
-
import { CR, ERASE_DOWN, cursorUp } from "./ansi.ts";
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Lines shown before a collapsed tool body. From pi's write renderer (10).
|
|
29
|
-
* Assumed, not measured: a taller preview fills the live region before
|
|
30
|
-
* the user can hit Ctrl+O, and a shorter one hides too much of a typical
|
|
31
|
-
* command's first useful lines.
|
|
32
|
-
*/
|
|
33
|
-
const COLLAPSED_PREVIEW_LINES = 10;
|
|
34
|
-
|
|
35
|
-
const DIM: Attrs = { ...PLAIN_ATTRS, dim: true, result: true };
|
|
36
|
-
const CYAN: Attrs = { ...PLAIN_ATTRS, tool: true };
|
|
37
|
-
const RED: Attrs = { ...PLAIN_ATTRS, error: true };
|
|
38
|
-
const THINKING: Attrs = { ...PLAIN_ATTRS, dim: true, thinking: true };
|
|
39
|
-
|
|
40
|
-
function withThinking(base: Style): Style {
|
|
41
|
-
return {
|
|
42
|
-
open(attrs) {
|
|
43
|
-
return base.open({ ...attrs, thinking: true, dim: true });
|
|
44
|
-
},
|
|
45
|
-
close(attrs) {
|
|
46
|
-
return base.close({ ...attrs, thinking: true, dim: true });
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export type ToolViewEvent =
|
|
52
|
-
| { kind: "thinking"; text: string }
|
|
53
|
-
| { kind: "tool_call"; tool: { name: string; input: unknown } }
|
|
54
|
-
| {
|
|
55
|
-
kind: "tool_result";
|
|
56
|
-
text: string;
|
|
57
|
-
ok: boolean;
|
|
58
|
-
tool?: { name: string; input: unknown };
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export function renderToolView(
|
|
62
|
-
event: ToolViewEvent,
|
|
63
|
-
expanded: boolean,
|
|
64
|
-
width: number,
|
|
65
|
-
style: Style,
|
|
66
|
-
): string[] {
|
|
67
|
-
const w = Math.max(0, Math.floor(width));
|
|
68
|
-
switch (event.kind) {
|
|
69
|
-
case "thinking":
|
|
70
|
-
return renderThinking(event.text, expanded, w, style);
|
|
71
|
-
case "tool_call":
|
|
72
|
-
return renderCall(event.tool, expanded, w, style);
|
|
73
|
-
case "tool_result":
|
|
74
|
-
return renderResult(event, expanded, w, style);
|
|
75
|
-
default:
|
|
76
|
-
return assertNever(event, "renderToolView");
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function renderThinking(text: string, expanded: boolean, width: number, style: Style): string[] {
|
|
81
|
-
if (width <= 0) return [""];
|
|
82
|
-
const indent = " ";
|
|
83
|
-
if (expanded) {
|
|
84
|
-
// Wrap to the same inner width emitPrefixed would paint into. A
|
|
85
|
-
// floor of 1 plus a two-space indent is three columns in a
|
|
86
|
-
// one-column terminal — the overflow this card exists to close.
|
|
87
|
-
const inner = innerWidth(indent, width);
|
|
88
|
-
// CommonMark folds a single newline into a space. The previous subset
|
|
89
|
-
// parser did that; pi's marked keeps the break. Folding here keeps the
|
|
90
|
-
// tool-view snapshot and the wrap of a thinking paragraph stable.
|
|
91
|
-
const folded = text.replace(/\r\n/g, "\n").replace(/(?<!\n)\n(?!\n)/g, " ");
|
|
92
|
-
const md = renderMarkdown(folded, inner, withThinking(style));
|
|
93
|
-
if (inner === width) return md;
|
|
94
|
-
return md.map((line) => `${indent}${line}`);
|
|
95
|
-
}
|
|
96
|
-
const source = text.replace(/\r\n/g, "\n").split("\n");
|
|
97
|
-
const first = source[0] ?? "";
|
|
98
|
-
const hidden = Math.max(0, source.length - 1);
|
|
99
|
-
const out = emitPrefixed(indent, first, width, style, THINKING);
|
|
100
|
-
if (hidden > 0) {
|
|
101
|
-
out.push(...emitPrefixed(indent, `… ${hidden} more lines`, width, style, DIM));
|
|
102
|
-
}
|
|
103
|
-
return out;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function renderCall(
|
|
107
|
-
tool: { name: string; input: unknown },
|
|
108
|
-
expanded: boolean,
|
|
109
|
-
width: number,
|
|
110
|
-
style: Style,
|
|
111
|
-
): string[] {
|
|
112
|
-
const input = asRecord(tool.input);
|
|
113
|
-
const name = tool.name;
|
|
114
|
-
switch (name) {
|
|
115
|
-
case "edit":
|
|
116
|
-
return renderEditCall(input, expanded, width, style);
|
|
117
|
-
case "write":
|
|
118
|
-
return renderWriteCall(input, expanded, width, style);
|
|
119
|
-
case "bash":
|
|
120
|
-
return renderBashCall(input, width, style);
|
|
121
|
-
case "read":
|
|
122
|
-
return renderReadCall(input, width, style);
|
|
123
|
-
default:
|
|
124
|
-
return emitPrefixed(" → ", headerTail(name, input), width, style, CYAN);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function renderEditCall(
|
|
129
|
-
input: Record<string, unknown>,
|
|
130
|
-
expanded: boolean,
|
|
131
|
-
width: number,
|
|
132
|
-
style: Style,
|
|
133
|
-
): string[] {
|
|
134
|
-
const path = str(input["path"]);
|
|
135
|
-
// old/new are the names logs written before old_string/new_string carry.
|
|
136
|
-
const edits = Array.isArray(input["edits"]) ? input["edits"] as Record<string, unknown>[] : [input];
|
|
137
|
-
const oldText = edits.map((edit) => str(edit["old_string"] ?? edit["old"])).join("\n");
|
|
138
|
-
const newText = edits.map((edit) => str(edit["new_string"] ?? edit["new"])).join("\n");
|
|
139
|
-
const out = emitPrefixed(" → ", `edit ${path}${edits.length > 1 ? ` (${edits.length} edits)` : ""}`, width, style, CYAN);
|
|
140
|
-
if (oldText.length === 0 && newText.length === 0) return out;
|
|
141
|
-
const diff = renderDiff(oldText, newText, width, style);
|
|
142
|
-
return out.concat(collapse(diff, expanded, COLLAPSED_PREVIEW_LINES, width, style));
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function renderWriteCall(
|
|
146
|
-
input: Record<string, unknown>,
|
|
147
|
-
expanded: boolean,
|
|
148
|
-
width: number,
|
|
149
|
-
style: Style,
|
|
150
|
-
): string[] {
|
|
151
|
-
const path = str(input["path"]);
|
|
152
|
-
const content = str(input["content"]);
|
|
153
|
-
const out = emitPrefixed(" → ", `write ${path}`, width, style, CYAN);
|
|
154
|
-
if (content.length === 0) return out;
|
|
155
|
-
const body = content.split("\n").flatMap((line) => emitPrefixed(" ", line, width, style, DIM));
|
|
156
|
-
return out.concat(collapse(body, expanded, COLLAPSED_PREVIEW_LINES, width, style));
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function renderBashCall(input: Record<string, unknown>, width: number, style: Style): string[] {
|
|
160
|
-
const command = str(input["command"]);
|
|
161
|
-
return emitPrefixed(" → ", `bash ${command}`, width, style, CYAN);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function renderReadCall(input: Record<string, unknown>, width: number, style: Style): string[] {
|
|
165
|
-
const path = str(input["path"]);
|
|
166
|
-
const range = formatReadRange(input);
|
|
167
|
-
return emitPrefixed(" → ", `read ${path}${range}`, width, style, CYAN);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function renderResult(
|
|
171
|
-
event: { text: string; ok: boolean; tool?: { name: string; input: unknown } },
|
|
172
|
-
expanded: boolean,
|
|
173
|
-
width: number,
|
|
174
|
-
style: Style,
|
|
175
|
-
): string[] {
|
|
176
|
-
const name = event.tool?.name ?? inferToolName(event.text);
|
|
177
|
-
const ok = event.ok;
|
|
178
|
-
switch (name) {
|
|
179
|
-
case "bash":
|
|
180
|
-
return renderBashResult(event.text, ok, expanded, width, style);
|
|
181
|
-
case "read":
|
|
182
|
-
return renderReadResult(event.text, ok, width, style);
|
|
183
|
-
case "write":
|
|
184
|
-
case "edit":
|
|
185
|
-
return renderStatusResult(event.text, ok, width, style);
|
|
186
|
-
default:
|
|
187
|
-
return renderGenericResult(event.text, ok, expanded, width, style);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function renderBashResult(
|
|
192
|
-
text: string,
|
|
193
|
-
ok: boolean,
|
|
194
|
-
expanded: boolean,
|
|
195
|
-
width: number,
|
|
196
|
-
style: Style,
|
|
197
|
-
): string[] {
|
|
198
|
-
const parsed = parseBashResult(text);
|
|
199
|
-
const exitLabel = parsed.label;
|
|
200
|
-
const mark = ok ? " ok " : " err ";
|
|
201
|
-
const markAttrs = ok ? DIM : RED;
|
|
202
|
-
const header = exitLabel.length > 0 ? mark + exitLabel : mark.trimEnd();
|
|
203
|
-
const out = emitPrefixed("", header, width, style, markAttrs);
|
|
204
|
-
|
|
205
|
-
const body = parsed.body;
|
|
206
|
-
if (body.length === 0) return out;
|
|
207
|
-
const stream = ok ? "stdout" : "stderr";
|
|
208
|
-
const bodyAttrs: Attrs = ok ? DIM : RED;
|
|
209
|
-
const lines = body.split("\n").flatMap((line) =>
|
|
210
|
-
emitPrefixed(` ${stream} `, line, width, style, bodyAttrs),
|
|
211
|
-
);
|
|
212
|
-
return out.concat(collapse(lines, expanded, COLLAPSED_PREVIEW_LINES, width, style));
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function renderReadResult(text: string, ok: boolean, width: number, style: Style): string[] {
|
|
216
|
-
if (!ok) return renderStatusResult(text, false, width, style);
|
|
217
|
-
const n = text.length === 0 ? 0 : text.split("\n").length;
|
|
218
|
-
return emitPrefixed(" ", `${n} lines`, width, style, DIM);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function renderStatusResult(text: string, ok: boolean, width: number, style: Style): string[] {
|
|
222
|
-
const mark = ok ? " ok " : " err ";
|
|
223
|
-
const first = text.split("\n")[0] ?? "";
|
|
224
|
-
return emitPrefixed(mark, first, width, style, ok ? DIM : RED);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function renderGenericResult(
|
|
228
|
-
text: string,
|
|
229
|
-
ok: boolean,
|
|
230
|
-
expanded: boolean,
|
|
231
|
-
width: number,
|
|
232
|
-
style: Style,
|
|
233
|
-
): string[] {
|
|
234
|
-
const mark = ok ? " ok " : " err ";
|
|
235
|
-
const lines = text.split("\n");
|
|
236
|
-
const first = emitPrefixed(mark, lines[0] ?? "", width, style, ok ? DIM : RED);
|
|
237
|
-
const rest = lines.slice(1).flatMap((line) => emitPrefixed(" ", line, width, style, DIM));
|
|
238
|
-
return first.concat(collapse(rest, expanded, COLLAPSED_PREVIEW_LINES, width, style));
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function collapse(
|
|
242
|
-
lines: readonly string[],
|
|
243
|
-
expanded: boolean,
|
|
244
|
-
preview: number,
|
|
245
|
-
width: number,
|
|
246
|
-
style: Style,
|
|
247
|
-
): string[] {
|
|
248
|
-
if (expanded || lines.length <= preview) return [...lines];
|
|
249
|
-
const shown = lines.slice(0, preview);
|
|
250
|
-
const hidden = lines.length - preview;
|
|
251
|
-
const note = paintRemainder(hidden, width, style);
|
|
252
|
-
return [...shown, ...note];
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
function paintRemainder(hidden: number, width: number, style: Style): string[] {
|
|
256
|
-
return emitPrefixed(" ", `… ${hidden} more lines`, width, style, DIM);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/** The header is `exit N` optionally followed by ` · wall · status`; all of it is the label. */
|
|
260
|
-
function parseBashResult(text: string): { exit: number | null; label: string; body: string } {
|
|
261
|
-
const m = /^exit (\d+)([^\n]*)\n?/.exec(text);
|
|
262
|
-
if (!m) return { exit: null, label: "", body: text };
|
|
263
|
-
return { exit: Number(m[1]), label: `exit ${m[1]}${m[2] ?? ""}`, body: text.slice(m[0].length) };
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function inferToolName(text: string): string {
|
|
267
|
-
if (/^exit \d+/.test(text)) return "bash";
|
|
268
|
-
if (/^wrote \d+ bytes/.test(text)) return "write";
|
|
269
|
-
if (/^replaced /.test(text)) return "edit";
|
|
270
|
-
if (/^\d+\t/.test(text)) return "read";
|
|
271
|
-
return "";
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
function formatReadRange(input: Record<string, unknown>): string {
|
|
275
|
-
const start = input["start"];
|
|
276
|
-
const end = input["end"];
|
|
277
|
-
if (typeof start !== "number" && typeof end !== "number") return "";
|
|
278
|
-
const a = typeof start === "number" ? start : 1;
|
|
279
|
-
const b = typeof end === "number" ? String(end) : "";
|
|
280
|
-
return b.length > 0 ? `:${a}-${b}` : `:${a}`;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function headerTail(name: string, input: Record<string, unknown>): string {
|
|
284
|
-
const path = str(input["path"]);
|
|
285
|
-
const command = str(input["command"]);
|
|
286
|
-
if (path.length > 0) return `${name} ${path}`;
|
|
287
|
-
if (command.length > 0) return `${name} ${command}`;
|
|
288
|
-
return name;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function asRecord(input: unknown): Record<string, unknown> {
|
|
292
|
-
if (typeof input === "object" && input !== null) return input as Record<string, unknown>;
|
|
293
|
-
return {};
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
function str(v: unknown): string {
|
|
297
|
-
return typeof v === "string" ? v : "";
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Uncommitted tool/thinking lines, plus commit/toggle against a terminal.
|
|
302
|
-
*
|
|
303
|
-
* Toggle only asks the line-diff renderer to rewrite the live tail. Overflow
|
|
304
|
-
* already written as scrollback is not reprinted, and the screen is never
|
|
305
|
-
* cleared.
|
|
306
|
-
*/
|
|
307
|
-
export class LiveTranscript {
|
|
308
|
-
toolsExpanded = false;
|
|
309
|
-
thinkingExpanded = false;
|
|
310
|
-
private pending: ToolViewEvent[] = [];
|
|
311
|
-
private flushed = 0;
|
|
312
|
-
private renderer: Renderer;
|
|
313
|
-
private liveRows = 0;
|
|
314
|
-
private readonly terminal: Terminal;
|
|
315
|
-
private readonly style: Style;
|
|
316
|
-
|
|
317
|
-
constructor(terminal: Terminal, style: Style = ANSI) {
|
|
318
|
-
this.terminal = terminal;
|
|
319
|
-
this.style = style;
|
|
320
|
-
this.renderer = new Renderer(terminal);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
push(event: ToolViewEvent): void {
|
|
324
|
-
this.pending.push(event);
|
|
325
|
-
this.paint();
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
commit(): void {
|
|
329
|
-
const lines = this.pendingLines();
|
|
330
|
-
const leftover = lines.slice(this.flushed);
|
|
331
|
-
this.eraseLive();
|
|
332
|
-
if (leftover.length > 0) this.terminal.write(leftover.join("\n") + "\n");
|
|
333
|
-
this.pending = [];
|
|
334
|
-
this.flushed = 0;
|
|
335
|
-
this.renderer = new Renderer(this.terminal);
|
|
336
|
-
this.liveRows = 0;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
toggleTools(): void {
|
|
340
|
-
this.toolsExpanded = !this.toolsExpanded;
|
|
341
|
-
this.paint();
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
toggleThinking(): void {
|
|
345
|
-
this.thinkingExpanded = !this.thinkingExpanded;
|
|
346
|
-
this.paint();
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
private expandedFor(event: ToolViewEvent): boolean {
|
|
350
|
-
return event.kind === "thinking" ? this.thinkingExpanded : this.toolsExpanded;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
private pendingLines(): string[] {
|
|
354
|
-
const width = usableColumns(this.terminal.columns);
|
|
355
|
-
return this.pending.flatMap((event) =>
|
|
356
|
-
renderToolView(event, this.expandedFor(event), width, this.style),
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
private paint(): void {
|
|
361
|
-
const rows = usableRows(this.terminal.rows);
|
|
362
|
-
const lines = this.pendingLines();
|
|
363
|
-
const fitted = fitLive(lines, rows);
|
|
364
|
-
if (fitted.overflow.length > this.flushed) {
|
|
365
|
-
const fresh = fitted.overflow.slice(this.flushed);
|
|
366
|
-
this.eraseLive();
|
|
367
|
-
this.terminal.write(fresh.join("\n") + "\n");
|
|
368
|
-
this.flushed = fitted.overflow.length;
|
|
369
|
-
this.renderer = new Renderer(this.terminal);
|
|
370
|
-
this.liveRows = 0;
|
|
371
|
-
}
|
|
372
|
-
this.renderer.render({ render: () => fitted.live });
|
|
373
|
-
this.liveRows = this.renderer.liveRowCount;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
private eraseLive(): void {
|
|
377
|
-
if (this.liveRows <= 0 && this.renderer.liveRowCount <= 0) return;
|
|
378
|
-
const rows = Math.max(this.liveRows, this.renderer.liveRowCount);
|
|
379
|
-
let out = CR;
|
|
380
|
-
if (rows > 1) out += cursorUp(rows - 1);
|
|
381
|
-
out += ERASE_DOWN;
|
|
382
|
-
this.terminal.write(out);
|
|
383
|
-
this.renderer = new Renderer(this.terminal);
|
|
384
|
-
this.liveRows = 0;
|
|
385
|
-
}
|
|
386
|
-
}
|
package/src/tui/transcript.ts
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { Container, PiText, Lines, assistantMarkdown, type MarkdownFeatures } from "./host.ts";
|
|
2
|
-
import { Image } from "../vendor/pi-tui/components/image.ts";
|
|
3
|
-
import { renderToolView } from "./toolview.ts";
|
|
4
|
-
import { PLAIN_ATTRS, paint, type Style } from "./style.ts";
|
|
5
|
-
import { applyBackgroundToLine } from "../vendor/pi-tui/utils.ts";
|
|
6
|
-
import type { Component } from "../vendor/pi-tui/tui.ts";
|
|
7
|
-
|
|
8
|
-
/** Presentation DTO; the application supplies its pure log projection. */
|
|
9
|
-
type Attachment = { sha256: string; name: string; width: number; height: number };
|
|
10
|
-
type Item = { id: string; revision: string } & (
|
|
11
|
-
| { kind: "user"; text: string; interrupted: boolean; images?: readonly Attachment[] }
|
|
12
|
-
| { kind: "assistant" | "thinking" | "notice"; text: string }
|
|
13
|
-
| { kind: "custom"; text: string; customType: string; data?: unknown; entry: boolean; timestamp: string }
|
|
14
|
-
| { kind: "tool"; callId: string; name: string; input: unknown; result: { text: string; ok: boolean; data?: unknown; images?: readonly Attachment[] } | null }
|
|
15
|
-
);
|
|
16
|
-
interface ExtensionRenderers {
|
|
17
|
-
message(type: string, text: string, data: unknown, expanded: boolean, fallback: Component, metadata: { id: string; entry: boolean; timestamp: string }): Component;
|
|
18
|
-
tool(name: string, input: unknown, result: { text: string; ok: boolean; data?: unknown } | null, expanded: boolean, call: Component, output: Component, id: string): Component;
|
|
19
|
-
hiddenThinkingLabel: string | undefined;
|
|
20
|
-
}
|
|
21
|
-
function dispose(component: Component): void {
|
|
22
|
-
if (component instanceof Container) component.children.forEach(dispose);
|
|
23
|
-
else if ("dispose" in component && typeof component.dispose === "function") component.dispose();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export class Transcript extends Container {
|
|
27
|
-
private items: readonly Item[] = [];
|
|
28
|
-
private cache = new Map<string, { revision: string; component: Container }>();
|
|
29
|
-
private style: Style | null = null;
|
|
30
|
-
private toolsExpanded = false;
|
|
31
|
-
private thinkingExpanded = false;
|
|
32
|
-
private padding = 1;
|
|
33
|
-
private features: MarkdownFeatures | undefined;
|
|
34
|
-
private images: ReadonlyMap<string, string> = new Map();
|
|
35
|
-
private showImages = true;
|
|
36
|
-
private imageWidth = 60;
|
|
37
|
-
private extensions: ExtensionRenderers | undefined;
|
|
38
|
-
setExtensions(renderers: ExtensionRenderers): void { this.extensions = renderers; }
|
|
39
|
-
private clearCache(): void { for (const entry of this.cache.values()) dispose(entry.component); this.cache.clear(); }
|
|
40
|
-
configure(features: MarkdownFeatures, images: ReadonlyMap<string, string>, showImages: boolean, width: number): void {
|
|
41
|
-
this.features = features; this.images = images; this.showImages = showImages; this.imageWidth = width;
|
|
42
|
-
this.clearCache(); this.items = [];
|
|
43
|
-
}
|
|
44
|
-
setPadding(padding: number): void { if (this.padding !== padding) { this.padding = padding; this.invalidate(); } }
|
|
45
|
-
override render(width: number): string[] {
|
|
46
|
-
const padding = Math.min(this.padding, Math.max(0, Math.floor((width - 1) / 2)));
|
|
47
|
-
return super.render(Math.max(1, width - padding * 2)).map(line => line.startsWith("\x1b]133;A\x07")
|
|
48
|
-
? "\x1b]133;A\x07" + " ".repeat(padding) + line.slice(8) : " ".repeat(padding) + line);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
update(items: readonly Item[], style: Style, toolsExpanded: boolean, thinkingExpanded: boolean): void {
|
|
52
|
-
const changed = style !== this.style || toolsExpanded !== this.toolsExpanded || thinkingExpanded !== this.thinkingExpanded;
|
|
53
|
-
if (!changed && this.items === items) return;
|
|
54
|
-
if (changed) this.clearCache();
|
|
55
|
-
this.items = items;
|
|
56
|
-
this.style = style;
|
|
57
|
-
this.toolsExpanded = toolsExpanded;
|
|
58
|
-
this.thinkingExpanded = thinkingExpanded;
|
|
59
|
-
const next = new Map<string, { revision: string; component: Container }>();
|
|
60
|
-
this.clear();
|
|
61
|
-
for (const item of items) {
|
|
62
|
-
const old = this.cache.get(item.id);
|
|
63
|
-
const component = old?.revision === item.revision ? old.component : this.component(item, style);
|
|
64
|
-
next.set(item.id, { revision: item.revision, component });
|
|
65
|
-
this.addChild(component);
|
|
66
|
-
}
|
|
67
|
-
for (const [id, entry] of this.cache) if (next.get(id)?.component !== entry.component) dispose(entry.component);
|
|
68
|
-
this.cache = next;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
private component(item: Item, style: Style): Container {
|
|
72
|
-
const component = new Container();
|
|
73
|
-
const dim = (text: string) => paint(text, { ...PLAIN_ATTRS, dim: true }, style);
|
|
74
|
-
switch (item.kind) {
|
|
75
|
-
case "user": {
|
|
76
|
-
component.addChild(new Lines(width => {
|
|
77
|
-
const transformed = this.features?.transform(item.text, width, false, style, "user") ?? item.text;
|
|
78
|
-
const text = new PiText(paint(`❯ ${transformed}`, { ...PLAIN_ATTRS, prompt: true }, style), 0, 1, text => style.token?.("userMessageBg", text) ?? text);
|
|
79
|
-
return text.render(width).map((line, index) => index === 0 ? "\x1b]133;A\x07" + line : line);
|
|
80
|
-
}));
|
|
81
|
-
this.addImages(component, item.images, dim);
|
|
82
|
-
if (item.interrupted) component.addChild(new PiText(dim(" interrupted"), 0, 0));
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
case "assistant": component.addChild(assistantMarkdown(item.text, style, this.features)); break;
|
|
86
|
-
case "thinking": {
|
|
87
|
-
const expanded = this.thinkingExpanded;
|
|
88
|
-
component.addChild(!expanded && this.extensions?.hiddenThinkingLabel !== undefined ? new PiText(this.extensions.hiddenThinkingLabel, 0, 0) : new Lines((width) => renderToolView({ kind: "thinking", text: this.features?.transform(item.text, width, false, style, "assistant-thinking") ?? item.text }, expanded, width, style)));
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
91
|
-
case "tool": {
|
|
92
|
-
const tool = { name: item.name, input: item.input };
|
|
93
|
-
const expanded = this.toolsExpanded;
|
|
94
|
-
const background = item.result === null ? "toolPendingBg" : item.result.ok ? "toolSuccessBg" : "toolErrorBg";
|
|
95
|
-
const rows: Lines[] = [new Lines((width) => renderToolView({ kind: "tool_call", tool }, expanded, width, style))];
|
|
96
|
-
if (item.result) {
|
|
97
|
-
const result = item.result;
|
|
98
|
-
rows.push(new Lines((width) => renderToolView({ kind: "tool_result", tool, text: result.text, ok: result.ok }, expanded, width, style)));
|
|
99
|
-
} else rows.push(new Lines(() => [dim(" pending")]));
|
|
100
|
-
const rendered = this.extensions?.tool(item.name, item.input, item.result, expanded, rows[0]!, rows[1]!, item.callId) ?? new Lines(width => rows.flatMap(row => row.render(width)));
|
|
101
|
-
const toolComponent = { render: (width: number) => rendered.render(width).map(line => applyBackgroundToLine(line, width, text => style.token?.(background, text) ?? text)),
|
|
102
|
-
invalidate: () => rendered.invalidate(), dispose: () => dispose(rendered) };
|
|
103
|
-
component.addChild(toolComponent);
|
|
104
|
-
this.addImages(component, item.result?.images, dim);
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
case "notice": component.addChild(new PiText(dim(item.text), 0, 0)); break;
|
|
108
|
-
case "custom": {
|
|
109
|
-
const fallback = new PiText(dim(`[${item.customType}] ${item.text}`), 0, 0);
|
|
110
|
-
component.addChild(this.extensions?.message(item.customType, item.text, item.data, this.toolsExpanded, fallback, item) ?? fallback); break;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return component;
|
|
114
|
-
}
|
|
115
|
-
private addImages(component: Container, images: readonly Attachment[] | undefined, dim: (text: string) => string): void {
|
|
116
|
-
for (const image of images ?? []) {
|
|
117
|
-
const data = this.images.get(image.sha256);
|
|
118
|
-
if (this.showImages && data) component.addChild(new Image(data, "image/png", { fallbackColor: dim },
|
|
119
|
-
{ filename: image.name, maxWidthCells: this.imageWidth }, { widthPx: image.width, heightPx: image.height }));
|
|
120
|
-
else component.addChild(new PiText(dim(`[Image: ${image.name}, ${image.width}x${image.height}]`), 0, 0));
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|