@oh-my-pi/pi-tui 18.3.1 → 18.3.3
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 +22 -0
- package/dist/types/app-keybindings.d.ts +2 -17
- package/dist/types/autocomplete.d.ts +18 -3
- package/dist/types/chrome/keybinding-hints.d.ts +16 -10
- package/dist/types/components/editor.d.ts +11 -0
- package/dist/types/key-hint-format.d.ts +29 -0
- package/dist/types/keys.d.ts +2 -1
- package/dist/types/overlays/agent-hub.d.ts +1 -1
- package/dist/types/overlays/agent-transcript-viewer.d.ts +1 -1
- package/dist/types/overlays/model-picker.d.ts +1 -3
- package/dist/types/overlays/rewind-selector.d.ts +4 -2
- package/dist/types/prompt/composer-hints.d.ts +42 -0
- package/dist/types/prompt/composer.d.ts +2 -1
- package/dist/types/prompt/custom-editor.d.ts +8 -2
- package/dist/types/prompt/macos-spelling.d.ts +7 -16
- package/dist/types/prompt/prompt-action-autocomplete.d.ts +4 -1
- package/dist/types/prompt/prose-gate.d.ts +24 -0
- package/dist/types/prompt/word-completion.d.ts +46 -0
- package/dist/types/render/render-utils.d.ts +1 -1
- package/dist/types/status-line/host.d.ts +1 -1
- package/dist/types/theme/active-symbols.d.ts +12 -0
- package/dist/types/theme/symbols.d.ts +1 -1
- package/dist/types/theme/theme-class.d.ts +0 -1
- package/dist/types/tools/eval.d.ts +1 -1
- package/dist/types/tools/mcp.d.ts +3 -1
- package/dist/types/tools/task-yield-assembly.d.ts +10 -2
- package/dist/types/tools/task.d.ts +4 -0
- package/package.json +11 -10
- package/src/app-keybindings.ts +11 -104
- package/src/apps/autoresearch-dashboard.ts +10 -3
- package/src/apps/debug/log-viewer.ts +3 -2
- package/src/apps/debug/raw-sse.ts +2 -1
- package/src/apps/git/git-tui.ts +9 -5
- package/src/apps/live-visualizer.ts +2 -1
- package/src/apps/ps-top.ts +13 -4
- package/src/autocomplete.ts +42 -7
- package/src/chat/compaction-summary-message.ts +2 -1
- package/src/chat/execution-shared.ts +4 -2
- package/src/chat/tool-execution.ts +3 -1
- package/src/chat/ttsr-notification.ts +4 -3
- package/src/chat/user-message.ts +2 -2
- package/src/chrome/keybinding-hints.ts +38 -13
- package/src/components/editor.ts +180 -58
- package/src/components/settings-list.ts +10 -3
- package/src/components/tab-bar.ts +2 -1
- package/src/debug-server.ts +4 -1
- package/src/hotkeys-markdown.ts +14 -17
- package/src/key-hint-format.ts +122 -0
- package/src/keys.ts +2 -1
- package/src/overlays/advisor-config.ts +28 -6
- package/src/overlays/agent-hub.ts +8 -5
- package/src/overlays/agent-transcript-viewer.ts +6 -4
- package/src/overlays/agents-hub.ts +22 -8
- package/src/overlays/annotation-overlay.ts +31 -17
- package/src/overlays/ask-dialog.ts +14 -19
- package/src/overlays/bordered-loader.ts +2 -1
- package/src/overlays/btw-history-panel.ts +15 -12
- package/src/overlays/btw-panel.ts +12 -7
- package/src/overlays/cleanse-panel.ts +9 -6
- package/src/overlays/codex-reset-fireworks.ts +5 -3
- package/src/overlays/copy-selector.ts +13 -5
- package/src/overlays/extensions/extension-dashboard.ts +6 -1
- package/src/overlays/history-search.ts +3 -2
- package/src/overlays/hook-editor.ts +7 -2
- package/src/overlays/hook-input.ts +2 -1
- package/src/overlays/hook-selector.ts +5 -1
- package/src/overlays/login-dialog.ts +17 -4
- package/src/overlays/logout-account-selector.ts +10 -1
- package/src/overlays/mcp-add-wizard.ts +32 -20
- package/src/overlays/model-hub.ts +66 -27
- package/src/overlays/model-picker.ts +27 -12
- package/src/overlays/move-overlay.ts +5 -1
- package/src/overlays/omfg-panel.ts +8 -5
- package/src/overlays/pause-screen.ts +7 -3
- package/src/overlays/plan-review-overlay.ts +40 -14
- package/src/overlays/plan-save-overlay.ts +3 -1
- package/src/overlays/plugin-settings.ts +15 -8
- package/src/overlays/reset-usage-selector.ts +10 -3
- package/src/overlays/rewind-selector.ts +193 -13
- package/src/overlays/session-info-overlay.ts +4 -3
- package/src/overlays/session-selector.ts +12 -2
- package/src/overlays/settings-selector.ts +18 -11
- package/src/overlays/tree-selector.ts +27 -4
- package/src/overlays/usage-dashboard.ts +8 -2
- package/src/prompt/composer-cache.ts +2 -1
- package/src/prompt/composer-hints.ts +75 -0
- package/src/prompt/composer.ts +3 -2
- package/src/prompt/custom-editor.ts +22 -4
- package/src/prompt/macos-spelling.ts +17 -158
- package/src/prompt/prompt-action-autocomplete.ts +9 -2
- package/src/prompt/prose-gate.ts +87 -0
- package/src/prompt/tips.txt +6 -6
- package/src/prompt/welcome.ts +35 -2
- package/src/prompt/word-completion.ts +205 -0
- package/src/render/render-utils.ts +4 -4
- package/src/setup/scenes/composer.ts +5 -1
- package/src/setup/scenes/model.ts +3 -1
- package/src/setup/scenes/providers.ts +4 -1
- package/src/setup/scenes/sign-in.ts +4 -2
- package/src/setup/scenes/splash.ts +9 -4
- package/src/setup/scenes/theme.ts +18 -5
- package/src/setup/wizard-overlay.ts +5 -4
- package/src/status-line/host.ts +1 -1
- package/src/status-line/segments.ts +2 -2
- package/src/theme/active-symbols.ts +20 -0
- package/src/theme/glyph-bundle.json +28 -1
- package/src/theme/symbols.ts +88 -6
- package/src/theme/theme-class.ts +0 -1
- package/src/theme/theme.ts +2 -0
- package/src/tools/bash-interactive.ts +2 -1
- package/src/tools/eval.ts +3 -3
- package/src/tools/mcp.ts +3 -1
- package/src/tools/task-yield-assembly.ts +34 -8
- package/src/tools/task.ts +14 -5
- package/dist/types/overlays/tiny-title-download-progress.d.ts +0 -21
- package/src/overlays/tiny-title-download-progress.ts +0 -102
package/src/theme/symbols.ts
CHANGED
|
@@ -99,7 +99,6 @@ export type SymbolKey =
|
|
|
99
99
|
| "icon.advisorClosed"
|
|
100
100
|
| "icon.time"
|
|
101
101
|
| "icon.omp"
|
|
102
|
-
| "icon.esc"
|
|
103
102
|
| "icon.ghost"
|
|
104
103
|
| "icon.agents"
|
|
105
104
|
| "icon.job"
|
|
@@ -285,7 +284,28 @@ export type SymbolKey =
|
|
|
285
284
|
| "tool.goal"
|
|
286
285
|
| "tool.irc"
|
|
287
286
|
| "tool.delete"
|
|
288
|
-
| "tool.move"
|
|
287
|
+
| "tool.move"
|
|
288
|
+
// Keyboard keys (rendered by formatKeyHint). `*Mac` variants replace the
|
|
289
|
+
// generic modifier on macOS, whose keycaps are labelled with ⌃ ⌥ ⌘.
|
|
290
|
+
| "key.ctrl"
|
|
291
|
+
| "key.ctrlMac"
|
|
292
|
+
| "key.shift"
|
|
293
|
+
| "key.alt"
|
|
294
|
+
| "key.altMac"
|
|
295
|
+
| "key.super"
|
|
296
|
+
| "key.superMac"
|
|
297
|
+
// Gap after a glyph modifier (`⌃⇧C`); nerd icons render too small to abut
|
|
298
|
+
| "key.joiner"
|
|
299
|
+
| "key.enter"
|
|
300
|
+
| "key.esc"
|
|
301
|
+
| "key.tab"
|
|
302
|
+
| "key.backspace"
|
|
303
|
+
| "key.delete"
|
|
304
|
+
| "key.space"
|
|
305
|
+
| "key.up"
|
|
306
|
+
| "key.down"
|
|
307
|
+
| "key.left"
|
|
308
|
+
| "key.right";
|
|
289
309
|
|
|
290
310
|
export type SymbolMap = Record<SymbolKey, string>;
|
|
291
311
|
/**
|
|
@@ -459,7 +479,6 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
459
479
|
"icon.advisorClosed": "🙈",
|
|
460
480
|
"icon.time": "⏱",
|
|
461
481
|
"icon.omp": "π",
|
|
462
|
-
"icon.esc": "⎋",
|
|
463
482
|
"icon.ghost": "👻",
|
|
464
483
|
"icon.agents": "👥",
|
|
465
484
|
"icon.job": "⚙",
|
|
@@ -646,6 +665,25 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
646
665
|
"tool.irc": "✉",
|
|
647
666
|
"tool.delete": "🗑",
|
|
648
667
|
"tool.move": "➜",
|
|
668
|
+
// Keys: word modifiers off macOS (⌃/⌥/⌘ name no key on a PC keyboard)
|
|
669
|
+
"key.ctrl": "Ctrl",
|
|
670
|
+
"key.ctrlMac": "⌃",
|
|
671
|
+
"key.shift": "⇧",
|
|
672
|
+
"key.alt": "Alt",
|
|
673
|
+
"key.altMac": "⌥",
|
|
674
|
+
"key.super": "Super",
|
|
675
|
+
"key.superMac": "⌘",
|
|
676
|
+
"key.joiner": "",
|
|
677
|
+
"key.enter": "⏎",
|
|
678
|
+
"key.esc": "⎋",
|
|
679
|
+
"key.tab": "⇥",
|
|
680
|
+
"key.backspace": "⌫",
|
|
681
|
+
"key.delete": "⌦",
|
|
682
|
+
"key.space": "␣",
|
|
683
|
+
"key.up": "↑",
|
|
684
|
+
"key.down": "↓",
|
|
685
|
+
"key.left": "←",
|
|
686
|
+
"key.right": "→",
|
|
649
687
|
};
|
|
650
688
|
|
|
651
689
|
const NERD_SYMBOLS: SymbolMap = {
|
|
@@ -822,8 +860,6 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
822
860
|
// U+F03FF before (2ec52b8bdd) and reverted because it renders the wrong glyph;
|
|
823
861
|
// do not swap it again.
|
|
824
862
|
"icon.omp": "\u{f0d57}",
|
|
825
|
-
// pick: (nf-md-keyboard_esc) | alt: ⎋
|
|
826
|
-
"icon.esc": "\u{f12b7}",
|
|
827
863
|
// pick: (nf-md-ghost) | alt: 👻
|
|
828
864
|
"icon.ghost": "\u{f02a0}",
|
|
829
865
|
// pick: | alt:
|
|
@@ -1098,6 +1134,35 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
1098
1134
|
"tool.irc": "\uF086",
|
|
1099
1135
|
"tool.delete": "\uf12d",
|
|
1100
1136
|
"tool.move": "\uf061",
|
|
1137
|
+
// Keys: word modifiers off macOS; arrows stay Unicode (nerd arrows add nothing)
|
|
1138
|
+
"key.ctrl": "Ctrl",
|
|
1139
|
+
// pick: (nf-md-apple_keyboard_control) | alt: ⌃
|
|
1140
|
+
"key.ctrlMac": "\u{f0634}",
|
|
1141
|
+
// pick: (nf-md-apple_keyboard_shift) | alt: ⇧
|
|
1142
|
+
"key.shift": "\u{f0636}",
|
|
1143
|
+
"key.alt": "Alt",
|
|
1144
|
+
// pick: (nf-md-apple_keyboard_option) | alt: ⌥
|
|
1145
|
+
"key.altMac": "\u{f0635}",
|
|
1146
|
+
"key.super": "Super",
|
|
1147
|
+
// pick: (nf-md-apple_keyboard_command) | alt: ⌘
|
|
1148
|
+
"key.superMac": "\u{f0633}",
|
|
1149
|
+
"key.joiner": " ",
|
|
1150
|
+
// pick: (nf-md-keyboard_return) | alt: ⏎
|
|
1151
|
+
"key.enter": "\u{f0311}",
|
|
1152
|
+
// pick: (nf-md-keyboard_esc) | alt: ⎋
|
|
1153
|
+
"key.esc": "\u{f12b7}",
|
|
1154
|
+
// pick: (nf-md-keyboard_tab) | alt: ⇥
|
|
1155
|
+
"key.tab": "\u{f0312}",
|
|
1156
|
+
// pick: (nf-md-backspace) | alt: ⌫
|
|
1157
|
+
"key.backspace": "\u{f006e}",
|
|
1158
|
+
// pick: (nf-md-backspace_reverse) | alt: ⌦
|
|
1159
|
+
"key.delete": "\u{f0e7e}",
|
|
1160
|
+
// pick: (nf-md-keyboard_space) | alt: ␣
|
|
1161
|
+
"key.space": "\u{f1050}",
|
|
1162
|
+
"key.up": "↑",
|
|
1163
|
+
"key.down": "↓",
|
|
1164
|
+
"key.left": "←",
|
|
1165
|
+
"key.right": "→",
|
|
1101
1166
|
};
|
|
1102
1167
|
|
|
1103
1168
|
const ASCII_SYMBOLS: SymbolMap = {
|
|
@@ -1192,7 +1257,6 @@ const ASCII_SYMBOLS: SymbolMap = {
|
|
|
1192
1257
|
"icon.advisorClosed": "(adv)",
|
|
1193
1258
|
"icon.time": "t:",
|
|
1194
1259
|
"icon.omp": "pi",
|
|
1195
|
-
"icon.esc": "esc",
|
|
1196
1260
|
"icon.ghost": "@",
|
|
1197
1261
|
"icon.agents": "AG",
|
|
1198
1262
|
"icon.job": "bg",
|
|
@@ -1374,6 +1438,24 @@ const ASCII_SYMBOLS: SymbolMap = {
|
|
|
1374
1438
|
"tool.irc": "#",
|
|
1375
1439
|
"tool.delete": "rm",
|
|
1376
1440
|
"tool.move": "mv",
|
|
1441
|
+
"key.ctrl": "Ctrl",
|
|
1442
|
+
"key.ctrlMac": "Ctrl",
|
|
1443
|
+
"key.shift": "Shift",
|
|
1444
|
+
"key.alt": "Alt",
|
|
1445
|
+
"key.altMac": "Option",
|
|
1446
|
+
"key.super": "Super",
|
|
1447
|
+
"key.superMac": "Cmd",
|
|
1448
|
+
"key.joiner": "+",
|
|
1449
|
+
"key.enter": "Enter",
|
|
1450
|
+
"key.esc": "Esc",
|
|
1451
|
+
"key.tab": "Tab",
|
|
1452
|
+
"key.backspace": "Backspace",
|
|
1453
|
+
"key.delete": "Delete",
|
|
1454
|
+
"key.space": "Space",
|
|
1455
|
+
"key.up": "Up",
|
|
1456
|
+
"key.down": "Down",
|
|
1457
|
+
"key.left": "Left",
|
|
1458
|
+
"key.right": "Right",
|
|
1377
1459
|
};
|
|
1378
1460
|
|
|
1379
1461
|
export const SYMBOL_PRESETS: Record<SymbolPreset, SymbolMap> = {
|
package/src/theme/theme-class.ts
CHANGED
|
@@ -592,7 +592,6 @@ export class Theme {
|
|
|
592
592
|
advisorClosed: this.#symbols["icon.advisorClosed"],
|
|
593
593
|
time: this.#symbols["icon.time"],
|
|
594
594
|
omp: this.#symbols["icon.omp"],
|
|
595
|
-
esc: this.#symbols["icon.esc"],
|
|
596
595
|
ghost: this.#symbols["icon.ghost"],
|
|
597
596
|
agents: this.#symbols["icon.agents"],
|
|
598
597
|
job: this.#symbols["icon.job"],
|
package/src/theme/theme.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { Terminal, TerminalAppearance } from "../terminal";
|
|
|
5
5
|
import { colorLuma } from "@oh-my-pi/pi-utils/color";
|
|
6
6
|
import { getCustomThemesDir } from "@oh-my-pi/pi-utils/dirs";
|
|
7
7
|
import * as logger from "@oh-my-pi/pi-utils/logger";
|
|
8
|
+
import { setActiveSymbolTheme } from "./active-symbols";
|
|
8
9
|
import { ansi256ToHex, resolveThemeColors, resolveVarRefs } from "./color";
|
|
9
10
|
import { type CreateThemeOptions, getBuiltinThemes, loadTheme, loadThemeJson, loadThemeSync } from "./loader";
|
|
10
11
|
import type { ThemeColor, ThemeJson } from "./schema";
|
|
@@ -99,6 +100,7 @@ export function bindTheme(binding: ThemeBinding): () => void {
|
|
|
99
100
|
|
|
100
101
|
function assignTheme(value: Theme): void {
|
|
101
102
|
theme = value;
|
|
103
|
+
setActiveSymbolTheme(value);
|
|
102
104
|
if (themeBindings) {
|
|
103
105
|
for (const binding of themeBindings) binding(value);
|
|
104
106
|
}
|
|
@@ -4,6 +4,7 @@ import type { Component } from "../tui";
|
|
|
4
4
|
import { extractPrintableText, matchesKey, parseKey, parseKittySequence } from "../keys";
|
|
5
5
|
import { padding, truncateToWidth, visibleWidth } from "../utils";
|
|
6
6
|
import type { Theme } from "../theme/theme";
|
|
7
|
+
import { formatKeyHint } from "../app-keybindings";
|
|
7
8
|
import { formatStatusIcon, replaceTabs } from "../render/render-utils";
|
|
8
9
|
import { readTerminalRows, styleTerminalRow } from "./terminal-output";
|
|
9
10
|
|
|
@@ -267,7 +268,7 @@ export class BashInteractiveOverlayComponent implements Component {
|
|
|
267
268
|
const footer =
|
|
268
269
|
this.#state === "running"
|
|
269
270
|
? truncateToWidth(
|
|
270
|
-
`${this.#uiTheme.fg("warning", "
|
|
271
|
+
`${this.#uiTheme.fg("warning", formatKeyHint("escape"))} ${this.#uiTheme.fg("dim", "force-kill")} ${this.#uiTheme.fg("dim", "· input forwarded to PTY")}`,
|
|
271
272
|
innerWidth,
|
|
272
273
|
)
|
|
273
274
|
: truncateToWidth(this.#uiTheme.fg("dim", "session finished"), innerWidth);
|
package/src/tools/eval.ts
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
} from "../render/render-utils";
|
|
31
31
|
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
32
32
|
import type { OutputMeta } from "./output-meta";
|
|
33
|
-
import type
|
|
33
|
+
import { type ConfiguredThinkingLevel, expandKeyHint } from "../render/render-utils";
|
|
34
34
|
|
|
35
35
|
/** Runtime backend that an eval cell dispatches to. */
|
|
36
36
|
export type EvalLanguage = "python" | "js";
|
|
@@ -677,7 +677,7 @@ export const evalToolRenderer = {
|
|
|
677
677
|
const outputLines = [...outputContent.lines];
|
|
678
678
|
if (!expanded && outputContent.hiddenCount > 0) {
|
|
679
679
|
outputLines.push(
|
|
680
|
-
uiTheme.fg("dim", `… ${outputContent.hiddenCount} more lines (
|
|
680
|
+
uiTheme.fg("dim", `… ${outputContent.hiddenCount} more lines (${expandKeyHint()} to expand)`),
|
|
681
681
|
);
|
|
682
682
|
}
|
|
683
683
|
if (statusLines.length > 0) {
|
|
@@ -811,7 +811,7 @@ export const evalToolRenderer = {
|
|
|
811
811
|
outputLines.push("");
|
|
812
812
|
const skippedLine = uiTheme.fg(
|
|
813
813
|
"dim",
|
|
814
|
-
`… (${cachedSkipped} earlier lines, showing ${cachedLines.length} of ${cachedSkipped + cachedLines.length}) (
|
|
814
|
+
`… (${cachedSkipped} earlier lines, showing ${cachedLines.length} of ${cachedSkipped + cachedLines.length}) (${expandKeyHint()} to expand)`,
|
|
815
815
|
);
|
|
816
816
|
outputLines.push(truncateToWidth(skippedLine, width));
|
|
817
817
|
}
|
package/src/tools/mcp.ts
CHANGED
|
@@ -266,7 +266,7 @@ export interface MCPResourceContent {
|
|
|
266
266
|
/** Supported MCP result content blocks retained in display metadata. */
|
|
267
267
|
export type MCPContent = MCPTextContent | MCPImageContent | MCPResourceContent;
|
|
268
268
|
|
|
269
|
-
/**
|
|
269
|
+
/** MCP result details shared by renderers and programmatic tool consumers. */
|
|
270
270
|
export interface MCPToolDetails {
|
|
271
271
|
/** Server name */
|
|
272
272
|
serverName: string;
|
|
@@ -276,6 +276,8 @@ export interface MCPToolDetails {
|
|
|
276
276
|
isError?: boolean;
|
|
277
277
|
/** Raw content from MCP response */
|
|
278
278
|
rawContent?: MCPContent[];
|
|
279
|
+
/** Server-supplied structured data, independent of the model-facing text rendering. */
|
|
280
|
+
structuredContent?: Record<string, unknown>;
|
|
279
281
|
/** Structured metadata from the MCP response */
|
|
280
282
|
mcpMeta?: Record<string, unknown>;
|
|
281
283
|
/** Provider ID (e.g., "claude", "mcp-json") */
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import type { YieldItem } from "./task";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Output-schema shape of each declared top-level property, keyed by incremental yield label.
|
|
5
|
+
* `array` sections accumulate into a list (even a lone yield); `scalar` sections keep the
|
|
6
|
+
* latest yield, since the schema admits exactly one value. Undeclared labels accumulate
|
|
7
|
+
* into a list only once repeated.
|
|
8
|
+
*/
|
|
9
|
+
export type YieldSectionShapes = ReadonlyMap<string, "array" | "scalar">;
|
|
10
|
+
|
|
2
11
|
/** Outcome of folding a run's yield calls into one payload, with provenance flags. */
|
|
3
12
|
interface AssembledYieldResult {
|
|
4
13
|
data: unknown;
|
|
@@ -52,12 +61,14 @@ function appendYieldSection(
|
|
|
52
61
|
sectionCounts: Map<string, number>,
|
|
53
62
|
label: string,
|
|
54
63
|
value: unknown,
|
|
55
|
-
|
|
64
|
+
shape: "array" | "scalar" | undefined,
|
|
56
65
|
): void {
|
|
57
66
|
const count = sectionCounts.get(label) ?? 0;
|
|
58
67
|
const existing = sections[label];
|
|
59
|
-
if (
|
|
60
|
-
sections[label] =
|
|
68
|
+
if (shape === "scalar") {
|
|
69
|
+
sections[label] = value;
|
|
70
|
+
} else if (count === 0) {
|
|
71
|
+
sections[label] = shape === "array" ? [value] : value;
|
|
61
72
|
} else if (Array.isArray(existing)) {
|
|
62
73
|
existing.push(value);
|
|
63
74
|
} else {
|
|
@@ -74,12 +85,13 @@ function appendYieldSection(
|
|
|
74
85
|
* assistant turn the raw terminal result. Other string-typed yields contribute
|
|
75
86
|
* the terminal labelled section. Untyped terminal yields keep the historical
|
|
76
87
|
* "last yield wins" behavior unless no terminal yield exists, in which case
|
|
77
|
-
* accumulated typed sections finalize on idle.
|
|
88
|
+
* accumulated typed sections finalize on idle. Repeated sections merge per
|
|
89
|
+
* `sectionShapes` (see {@link YieldSectionShapes}).
|
|
78
90
|
*/
|
|
79
91
|
export function assembleYieldResult(
|
|
80
92
|
yieldItems: YieldItem[],
|
|
81
93
|
lastAssistantText?: string,
|
|
82
|
-
|
|
94
|
+
sectionShapes?: YieldSectionShapes,
|
|
83
95
|
): AssembledYieldResult | undefined {
|
|
84
96
|
if (yieldItems.length === 0) return undefined;
|
|
85
97
|
|
|
@@ -101,18 +113,27 @@ export function assembleYieldResult(
|
|
|
101
113
|
// level deep and made output-schema validation report every field missing.
|
|
102
114
|
const sections: Record<string, unknown> = {};
|
|
103
115
|
const sectionCounts = new Map<string, number>();
|
|
116
|
+
const overriddenScalars = new Set<string>();
|
|
104
117
|
let schemaOverridden = false;
|
|
105
118
|
let missingData = false;
|
|
106
119
|
let hasSections = false;
|
|
107
120
|
for (const item of yieldItems) {
|
|
108
121
|
if (item.status === "aborted") continue;
|
|
109
122
|
if (!isIncrementalYieldType(item.type)) continue;
|
|
110
|
-
|
|
123
|
+
const overridden = item.schemaOverridden === true;
|
|
111
124
|
const labels = getYieldLabels(item.type);
|
|
112
125
|
const resolved = resolveYieldPayload(item, lastAssistantText, labels);
|
|
113
126
|
missingData ||= resolved.missingData;
|
|
127
|
+
if (labels.length === 0) schemaOverridden ||= overridden;
|
|
114
128
|
for (const label of labels) {
|
|
115
|
-
|
|
129
|
+
const shape = sectionShapes?.get(label);
|
|
130
|
+
appendYieldSection(sections, sectionCounts, label, resolved.value, shape);
|
|
131
|
+
if (shape === "scalar") {
|
|
132
|
+
if (overridden) overriddenScalars.add(label);
|
|
133
|
+
else overriddenScalars.delete(label);
|
|
134
|
+
} else {
|
|
135
|
+
schemaOverridden ||= overridden;
|
|
136
|
+
}
|
|
116
137
|
hasSections = true;
|
|
117
138
|
}
|
|
118
139
|
}
|
|
@@ -133,7 +154,12 @@ export function assembleYieldResult(
|
|
|
133
154
|
// A data-less terminal finalize keeps accumulated sections; only when none
|
|
134
155
|
// exist does the last assistant turn become the raw result.
|
|
135
156
|
if (hasSections) {
|
|
136
|
-
return {
|
|
157
|
+
return {
|
|
158
|
+
data: sections,
|
|
159
|
+
schemaOverridden: schemaOverridden || overriddenScalars.size > 0,
|
|
160
|
+
rawText: false,
|
|
161
|
+
missingData,
|
|
162
|
+
};
|
|
137
163
|
}
|
|
138
164
|
|
|
139
165
|
if (!terminalItem) return undefined;
|
package/src/tools/task.ts
CHANGED
|
@@ -42,7 +42,7 @@ import { framedToolCard } from "../render/tool-card";
|
|
|
42
42
|
import { formatOutputInline, renderJsonTreeLines } from "./json-tree";
|
|
43
43
|
import { repairDoubleEncodedJsonString } from "./task-repair-args";
|
|
44
44
|
import { getSubprocessToolRenderer } from "./subprocess";
|
|
45
|
-
import { assembleYieldResult } from "./task-yield-assembly";
|
|
45
|
+
import { assembleYieldResult, type YieldSectionShapes } from "./task-yield-assembly";
|
|
46
46
|
|
|
47
47
|
/** Render context threaded in from `ToolExecutionComponent.#buildRenderContext`. */
|
|
48
48
|
interface TaskRenderContext {
|
|
@@ -98,8 +98,13 @@ function normalizeFindings(value: unknown): FindingDetails[] {
|
|
|
98
98
|
return findings;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
/** Reviewer output
|
|
102
|
-
const
|
|
101
|
+
/** Reviewer output shapes: `findings` is an array (a lone finding still assembles as a list); the verdict fields are scalars. */
|
|
102
|
+
const REVIEWER_SECTION_SHAPES: YieldSectionShapes = new Map([
|
|
103
|
+
["findings", "array"],
|
|
104
|
+
["overall_correctness", "scalar"],
|
|
105
|
+
["explanation", "scalar"],
|
|
106
|
+
["confidence", "scalar"],
|
|
107
|
+
]);
|
|
103
108
|
|
|
104
109
|
function extractIncrementalReviewResult(
|
|
105
110
|
items: RenderYieldItem[],
|
|
@@ -110,7 +115,7 @@ function extractIncrementalReviewResult(
|
|
|
110
115
|
status: item.status === "aborted" ? "aborted" : item.status === "success" ? "success" : undefined,
|
|
111
116
|
useLastTurn: item.useLastTurn,
|
|
112
117
|
}));
|
|
113
|
-
const assembled = assembleYieldResult(yieldItems, undefined,
|
|
118
|
+
const assembled = assembleYieldResult(yieldItems, undefined, REVIEWER_SECTION_SHAPES);
|
|
114
119
|
const data = assembled?.data;
|
|
115
120
|
if (!data || typeof data !== "object" || Array.isArray(data)) return undefined;
|
|
116
121
|
const record = data as Record<string, unknown>;
|
|
@@ -553,7 +558,7 @@ function createAssignmentSectionRenderer(
|
|
|
553
558
|
}
|
|
554
559
|
|
|
555
560
|
/**
|
|
556
|
-
* Build the shared-context section (the `# Goal / #
|
|
561
|
+
* Build the shared-context section (the `# Goal / # Contract` background a
|
|
557
562
|
* batch call hands every subagent). Rendered like the assignment brief so the
|
|
558
563
|
* shared background stays visible for the whole task lifecycle.
|
|
559
564
|
*/
|
|
@@ -1726,6 +1731,8 @@ export interface TaskItem {
|
|
|
1726
1731
|
agent?: string;
|
|
1727
1732
|
/** The work; required by the schema. */
|
|
1728
1733
|
task?: string;
|
|
1734
|
+
/** Caller's terse rationale for why the work is simple or complex; required by the schema and fed to the child's `auto` thinking classifier. */
|
|
1735
|
+
complexity?: string;
|
|
1729
1736
|
/** Per-spawn thinking effort: lowest/middle/highest level the resolved model supports. Overrides the agent's default selector (e.g. `auto`). */
|
|
1730
1737
|
effort?: "lo" | "med" | "hi";
|
|
1731
1738
|
/** Caller-provided output schema; its presence overrides the selected agent's schema. */
|
|
@@ -1751,6 +1758,8 @@ export interface TaskParams {
|
|
|
1751
1758
|
agent?: string;
|
|
1752
1759
|
/** The work (flat form). */
|
|
1753
1760
|
task?: string;
|
|
1761
|
+
/** Caller's difficulty rationale (flat form); see {@link TaskItem.complexity}. */
|
|
1762
|
+
complexity?: string;
|
|
1754
1763
|
/** Per-spawn thinking effort (flat form): lowest/middle/highest level the resolved model supports. */
|
|
1755
1764
|
effort?: "lo" | "med" | "hi";
|
|
1756
1765
|
/** Caller-provided output schema; its presence overrides the selected agent's schema. */
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { type Component } from "../index.js";
|
|
2
|
-
/** Download progress fields consumed by the status display. */
|
|
3
|
-
export interface TinyTitleDownloadProgress {
|
|
4
|
-
status: "initiate" | "download" | "progress" | "progress_total" | "done" | "ready" | "error";
|
|
5
|
-
file?: string;
|
|
6
|
-
progress?: number;
|
|
7
|
-
loaded?: number;
|
|
8
|
-
total?: number;
|
|
9
|
-
files?: Record<string, {
|
|
10
|
-
loaded: number;
|
|
11
|
-
total: number;
|
|
12
|
-
}>;
|
|
13
|
-
}
|
|
14
|
-
export declare class TinyTitleDownloadProgressComponent implements Component {
|
|
15
|
-
#private;
|
|
16
|
-
constructor(modelLabel: string);
|
|
17
|
-
update(event: TinyTitleDownloadProgress): void;
|
|
18
|
-
isComplete(): boolean;
|
|
19
|
-
invalidate(): void;
|
|
20
|
-
render(width: number): readonly string[];
|
|
21
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { type Component, truncateToWidth, visibleWidth } from "../index";
|
|
2
|
-
import { formatBytes } from "@oh-my-pi/pi-utils";
|
|
3
|
-
import { ProgressBar } from "../components/progress-bar";
|
|
4
|
-
/** Download progress fields consumed by the status display. */
|
|
5
|
-
export interface TinyTitleDownloadProgress {
|
|
6
|
-
status: "initiate" | "download" | "progress" | "progress_total" | "done" | "ready" | "error";
|
|
7
|
-
file?: string;
|
|
8
|
-
progress?: number;
|
|
9
|
-
loaded?: number;
|
|
10
|
-
total?: number;
|
|
11
|
-
files?: Record<string, { loaded: number; total: number }>;
|
|
12
|
-
}
|
|
13
|
-
import { theme } from "../theme/theme";
|
|
14
|
-
|
|
15
|
-
const DEFAULT_BAR_WIDTH = 24;
|
|
16
|
-
|
|
17
|
-
function padLine(line: string, width: number): string {
|
|
18
|
-
const visible = visibleWidth(line);
|
|
19
|
-
return visible >= width ? truncateToWidth(line, width) : `${line}${" ".repeat(width - visible)}`;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function currentFile(event: TinyTitleDownloadProgress | undefined): string | undefined {
|
|
23
|
-
if (!event) return undefined;
|
|
24
|
-
if (event.file) return event.file.split("/").at(-1) ?? event.file;
|
|
25
|
-
if (event.files) {
|
|
26
|
-
let largestFile: string | undefined;
|
|
27
|
-
let largestLoaded = -1;
|
|
28
|
-
for (const file in event.files) {
|
|
29
|
-
const state = event.files[file];
|
|
30
|
-
if (state.loaded <= largestLoaded || state.loaded >= state.total) continue;
|
|
31
|
-
largestFile = file;
|
|
32
|
-
largestLoaded = state.loaded;
|
|
33
|
-
}
|
|
34
|
-
return largestFile?.split("/").at(-1) ?? largestFile;
|
|
35
|
-
}
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function statusLabel(event: TinyTitleDownloadProgress | undefined): string {
|
|
40
|
-
if (!event) return "Preparing";
|
|
41
|
-
if (event.status === "error") return "Failed";
|
|
42
|
-
if (event.status === "ready") return "Ready";
|
|
43
|
-
if (event.status === "done") return "Downloaded";
|
|
44
|
-
if (event.status === "download") return "Downloading";
|
|
45
|
-
if (event.status === "progress" || event.status === "progress_total") return "Downloading";
|
|
46
|
-
return "Preparing";
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function byteLabel(event: TinyTitleDownloadProgress | undefined): string | undefined {
|
|
50
|
-
if (!event?.loaded || !event.total) return undefined;
|
|
51
|
-
return `${formatBytes(event.loaded)} / ${formatBytes(event.total)}`;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export class TinyTitleDownloadProgressComponent implements Component {
|
|
55
|
-
#modelLabel: string;
|
|
56
|
-
#event: TinyTitleDownloadProgress | undefined;
|
|
57
|
-
#bar = new ProgressBar(undefined, {
|
|
58
|
-
min: 0,
|
|
59
|
-
max: 100,
|
|
60
|
-
minWidth: 8,
|
|
61
|
-
maxWidth: DEFAULT_BAR_WIDTH,
|
|
62
|
-
style: {
|
|
63
|
-
filled: "█",
|
|
64
|
-
empty: "░",
|
|
65
|
-
styleFilled: text => theme.fg("accent", text),
|
|
66
|
-
styleEmpty: text => theme.fg("muted", text),
|
|
67
|
-
styleIndeterminate: text => theme.fg("muted", text),
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
constructor(modelLabel: string) {
|
|
72
|
-
this.#modelLabel = modelLabel;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
update(event: TinyTitleDownloadProgress): void {
|
|
76
|
-
this.#event = event;
|
|
77
|
-
this.#bar.setValue(event.progress);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
isComplete(): boolean {
|
|
81
|
-
return this.#event?.status === "ready" || this.#event?.status === "error";
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
invalidate(): void {
|
|
85
|
-
this.#bar.invalidate();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
render(width: number): readonly string[] {
|
|
89
|
-
width = Math.max(1, width);
|
|
90
|
-
const border = theme.fg("border", theme.boxRound.horizontal.repeat(width));
|
|
91
|
-
const status = statusLabel(this.#event);
|
|
92
|
-
const file = currentFile(this.#event);
|
|
93
|
-
const pct =
|
|
94
|
-
this.#event?.progress === undefined ? "" : `${Math.floor(this.#event.progress).toString().padStart(3, " ")}%`;
|
|
95
|
-
const bytes = byteLabel(this.#event);
|
|
96
|
-
const title = `${theme.fg("accent", "Tiny model")} ${theme.fg("muted", status)} ${this.#modelLabel}`;
|
|
97
|
-
const bar = this.#bar.render(Math.max(8, width - 36))[0] ?? "";
|
|
98
|
-
const details = [bar, pct, bytes, file].filter((part): part is string => Boolean(part)).join(" ");
|
|
99
|
-
|
|
100
|
-
return [border, padLine(` ${title}`, width), padLine(` ${details}`, width), border];
|
|
101
|
-
}
|
|
102
|
-
}
|