@oh-my-pi/pi-coding-agent 15.9.67 → 15.10.1
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 +136 -0
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/dry-balance-cli.d.ts +15 -1
- package/dist/types/cli/gallery-cli.d.ts +43 -0
- package/dist/types/cli/gallery-fixtures/agentic.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/codeintel.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/edit.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/fs.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/index.d.ts +4 -0
- package/dist/types/cli/gallery-fixtures/interaction.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/memory.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/misc.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/search.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/shell.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/types.d.ts +44 -0
- package/dist/types/cli/gallery-fixtures/web.d.ts +2 -0
- package/dist/types/cli/gallery-screenshot.d.ts +35 -0
- package/dist/types/commands/gallery.d.ts +47 -0
- package/dist/types/commit/analysis/conventional.d.ts +2 -2
- package/dist/types/commit/analysis/summary.d.ts +2 -2
- package/dist/types/commit/changelog/generate.d.ts +2 -2
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/index.d.ts +3 -3
- package/dist/types/commit/map-reduce/map-phase.d.ts +2 -2
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +2 -2
- package/dist/types/commit/model-selection.d.ts +10 -4
- package/dist/types/config/api-key-resolver.d.ts +34 -0
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/model-id-affixes.d.ts +2 -0
- package/dist/types/config/model-registry.d.ts +25 -2
- package/dist/types/config/settings-schema.d.ts +41 -6
- package/dist/types/dap/config.d.ts +14 -1
- package/dist/types/dap/types.d.ts +10 -0
- package/dist/types/extensibility/plugins/marketplace-auto-update.d.ts +8 -0
- package/dist/types/lsp/types.d.ts +10 -0
- package/dist/types/lsp/utils.d.ts +3 -2
- package/dist/types/main.d.ts +3 -2
- package/dist/types/memory-backend/index.d.ts +2 -1
- package/dist/types/memory-backend/resolve.d.ts +1 -1
- package/dist/types/memory-backend/types.d.ts +1 -1
- package/dist/types/modes/components/chat-block.d.ts +64 -0
- package/dist/types/modes/components/custom-editor.d.ts +5 -1
- package/dist/types/modes/components/overlay-box.d.ts +17 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +59 -0
- package/dist/types/modes/components/plan-toc.d.ts +41 -0
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +18 -0
- package/dist/types/modes/components/transcript-container.d.ts +11 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +0 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +0 -1
- package/dist/types/modes/controllers/input-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +22 -0
- package/dist/types/modes/controllers/tan-command-controller.d.ts +6 -0
- package/dist/types/modes/index.d.ts +5 -4
- package/dist/types/modes/interactive-mode.d.ts +16 -6
- package/dist/types/modes/setup-version.d.ts +11 -0
- package/dist/types/modes/setup-wizard/index.d.ts +2 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +2 -1
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +19 -6
- package/dist/types/modes/utils/copy-targets.d.ts +21 -1
- package/dist/types/plan-mode/approved-plan.d.ts +27 -8
- package/dist/types/plan-mode/plan-protection.d.ts +4 -4
- package/dist/types/sdk.d.ts +3 -1
- package/dist/types/session/agent-session.d.ts +21 -0
- package/dist/types/session/messages.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/slash-commands/types.d.ts +4 -6
- package/dist/types/task/executor.d.ts +14 -0
- package/dist/types/task/index.d.ts +1 -0
- package/dist/types/task/render.d.ts +3 -2
- package/dist/types/telemetry-export.d.ts +1 -1
- package/dist/types/tools/archive-reader.d.ts +5 -0
- package/dist/types/tools/ast-edit.d.ts +3 -0
- package/dist/types/tools/ast-grep.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/eval-render.d.ts +1 -8
- package/dist/types/tools/fetch.d.ts +15 -7
- package/dist/types/tools/find.d.ts +8 -4
- package/dist/types/tools/grouped-file-output.d.ts +95 -12
- package/dist/types/tools/memory-render.d.ts +4 -1
- package/dist/types/tools/plan-mode-guard.d.ts +8 -9
- package/dist/types/tools/render-utils.d.ts +13 -9
- package/dist/types/tools/renderers.d.ts +16 -2
- package/dist/types/tools/search.d.ts +5 -1
- package/dist/types/tools/sqlite-reader.d.ts +1 -0
- package/dist/types/tools/todo.d.ts +3 -2
- package/dist/types/tools/write.d.ts +5 -0
- package/dist/types/tui/output-block.d.ts +16 -4
- package/dist/types/tui/status-line.d.ts +3 -0
- package/dist/types/utils/enhanced-paste.d.ts +20 -0
- package/dist/types/web/scrapers/github.d.ts +22 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -1
- package/dist/types/web/search/providers/perplexity.d.ts +8 -1
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +9 -9
- package/scripts/dev-launch +42 -0
- package/scripts/dev-launch-preload.ts +19 -0
- package/src/auto-thinking/classifier.ts +5 -1
- package/src/cli/args.ts +2 -2
- package/src/cli/dry-balance-cli.ts +52 -17
- package/src/cli/gallery-cli.ts +226 -0
- package/src/cli/gallery-fixtures/agentic.ts +292 -0
- package/src/cli/gallery-fixtures/codeintel.ts +188 -0
- package/src/cli/gallery-fixtures/edit.ts +194 -0
- package/src/cli/gallery-fixtures/fs.ts +153 -0
- package/src/cli/gallery-fixtures/index.ts +40 -0
- package/src/cli/gallery-fixtures/interaction.ts +49 -0
- package/src/cli/gallery-fixtures/memory.ts +81 -0
- package/src/cli/gallery-fixtures/misc.ts +250 -0
- package/src/cli/gallery-fixtures/search.ts +213 -0
- package/src/cli/gallery-fixtures/shell.ts +167 -0
- package/src/cli/gallery-fixtures/types.ts +41 -0
- package/src/cli/gallery-fixtures/web.ts +158 -0
- package/src/cli/gallery-screenshot.ts +279 -0
- package/src/cli-commands.ts +1 -0
- package/src/commands/gallery.ts +52 -0
- package/src/commands/launch.ts +1 -1
- package/src/commit/analysis/conventional.ts +2 -2
- package/src/commit/analysis/summary.ts +2 -2
- package/src/commit/changelog/generate.ts +2 -2
- package/src/commit/changelog/index.ts +2 -2
- package/src/commit/map-reduce/index.ts +3 -3
- package/src/commit/map-reduce/map-phase.ts +2 -2
- package/src/commit/map-reduce/reduce-phase.ts +2 -2
- package/src/commit/model-selection.ts +33 -9
- package/src/commit/pipeline.ts +4 -4
- package/src/config/api-key-resolver.ts +58 -0
- package/src/config/keybindings.ts +15 -6
- package/src/config/model-equivalence.ts +35 -12
- package/src/config/model-id-affixes.ts +39 -22
- package/src/config/model-registry.ts +41 -18
- package/src/config/settings-schema.ts +28 -5
- package/src/config/settings.ts +31 -2
- package/src/dap/client.ts +14 -16
- package/src/dap/config.ts +41 -2
- package/src/dap/defaults.json +1 -0
- package/src/dap/session.ts +1 -0
- package/src/dap/types.ts +10 -0
- package/src/debug/index.ts +40 -54
- package/src/edit/renderer.ts +111 -119
- package/src/eval/__tests__/agent-bridge.test.ts +75 -32
- package/src/eval/__tests__/llm-bridge.test.ts +90 -31
- package/src/eval/agent-bridge.ts +34 -7
- package/src/eval/llm-bridge.ts +8 -3
- package/src/extensibility/extensions/runner.ts +1 -0
- package/src/extensibility/plugins/doctor.ts +0 -1
- package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
- package/src/goals/tools/goal-tool.ts +37 -27
- package/src/internal-urls/docs-index.generated.ts +10 -10
- package/src/lsp/client.ts +104 -55
- package/src/lsp/types.ts +10 -0
- package/src/lsp/utils.ts +3 -2
- package/src/main.ts +53 -56
- package/src/memories/index.ts +12 -5
- package/src/memory-backend/index.ts +13 -1
- package/src/memory-backend/resolve.ts +3 -5
- package/src/memory-backend/types.ts +1 -1
- package/src/mnemopi/backend.ts +5 -1
- package/src/modes/acp/acp-agent.ts +33 -26
- package/src/modes/components/assistant-message.ts +2 -9
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/copy-selector.ts +1 -44
- package/src/modes/components/custom-editor.ts +33 -1
- package/src/modes/components/custom-message.ts +1 -3
- package/src/modes/components/execution-shared.ts +1 -2
- package/src/modes/components/hook-message.ts +1 -3
- package/src/modes/components/overlay-box.ts +108 -0
- package/src/modes/components/plan-review-overlay.ts +799 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/read-tool-group.ts +20 -4
- package/src/modes/components/skill-message.ts +0 -1
- package/src/modes/components/status-line.ts +3 -5
- package/src/modes/components/tips.txt +1 -0
- package/src/modes/components/todo-reminder.ts +0 -2
- package/src/modes/components/tool-execution.ts +115 -90
- package/src/modes/components/transcript-container.ts +84 -24
- package/src/modes/components/user-message.ts +1 -2
- package/src/modes/controllers/command-controller-shared.ts +7 -6
- package/src/modes/controllers/command-controller.ts +70 -57
- package/src/modes/controllers/event-controller.ts +41 -40
- package/src/modes/controllers/extension-ui-controller.ts +10 -73
- package/src/modes/controllers/input-controller.ts +135 -122
- package/src/modes/controllers/mcp-command-controller.ts +69 -60
- package/src/modes/controllers/selector-controller.ts +25 -27
- package/src/modes/controllers/streaming-reveal.ts +212 -0
- package/src/modes/controllers/tan-command-controller.ts +173 -0
- package/src/modes/index.ts +5 -4
- package/src/modes/interactive-mode.ts +171 -82
- package/src/modes/setup-version.ts +11 -0
- package/src/modes/setup-wizard/index.ts +3 -2
- package/src/modes/setup-wizard/scenes/web-search.ts +3 -2
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/theme-schema.json +1 -1
- package/src/modes/theme/theme.ts +8 -4
- package/src/modes/types.ts +19 -8
- package/src/modes/utils/context-usage.ts +10 -6
- package/src/modes/utils/copy-targets.ts +133 -27
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +44 -46
- package/src/plan-mode/approved-plan.ts +66 -43
- package/src/plan-mode/plan-protection.ts +4 -4
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/plan-mode-active.md +67 -58
- package/src/prompts/system/plan-mode-approved.md +1 -1
- package/src/sdk.ts +32 -60
- package/src/session/agent-session.ts +89 -13
- package/src/session/messages.ts +26 -0
- package/src/session/session-manager.ts +13 -5
- package/src/slash-commands/builtin-registry.ts +37 -10
- package/src/slash-commands/helpers/usage-report.ts +2 -0
- package/src/slash-commands/types.ts +4 -6
- package/src/task/executor.ts +25 -4
- package/src/task/index.ts +4 -0
- package/src/task/render.ts +212 -148
- package/src/telemetry-export.ts +25 -7
- package/src/tools/archive-reader.ts +64 -0
- package/src/tools/ask.ts +119 -164
- package/src/tools/ast-edit.ts +98 -71
- package/src/tools/ast-grep.ts +37 -43
- package/src/tools/bash.ts +50 -6
- package/src/tools/debug.ts +20 -8
- package/src/tools/eval-backends.ts +6 -17
- package/src/tools/eval-render.ts +21 -18
- package/src/tools/eval.ts +5 -4
- package/src/tools/fetch.ts +391 -91
- package/src/tools/find.ts +44 -30
- package/src/tools/gh-renderer.ts +81 -42
- package/src/tools/grouped-file-output.ts +272 -48
- package/src/tools/image-gen.ts +150 -103
- package/src/tools/inspect-image-renderer.ts +63 -41
- package/src/tools/inspect-image.ts +8 -1
- package/src/tools/job.ts +3 -4
- package/src/tools/memory-render.ts +4 -1
- package/src/tools/plan-mode-guard.ts +21 -39
- package/src/tools/read.ts +23 -16
- package/src/tools/render-utils.ts +38 -40
- package/src/tools/renderers.ts +16 -1
- package/src/tools/report-tool-issue.ts +1 -1
- package/src/tools/resolve.ts +14 -0
- package/src/tools/search-tool-bm25.ts +36 -23
- package/src/tools/search.ts +189 -95
- package/src/tools/sqlite-reader.ts +9 -12
- package/src/tools/todo.ts +138 -59
- package/src/tools/write.ts +100 -60
- package/src/tui/output-block.ts +60 -13
- package/src/tui/status-line.ts +5 -1
- package/src/utils/commit-message-generator.ts +9 -1
- package/src/utils/enhanced-paste.ts +202 -0
- package/src/utils/title-generator.ts +2 -1
- package/src/web/scrapers/github.ts +255 -3
- package/src/web/scrapers/youtube.ts +3 -2
- package/src/web/search/providers/anthropic.ts +25 -19
- package/src/web/search/providers/exa.ts +11 -3
- package/src/web/search/providers/kimi.ts +28 -17
- package/src/web/search/providers/parallel.ts +35 -24
- package/src/web/search/providers/perplexity.ts +199 -51
- package/src/web/search/providers/synthetic.ts +8 -6
- package/src/web/search/providers/tavily.ts +9 -8
- package/src/web/search/providers/zai.ts +8 -6
- package/src/web/search/render.ts +39 -54
- package/src/web/search/types.ts +5 -1
- package/dist/types/eval/__tests__/shared-executors.test.d.ts +0 -1
- package/src/eval/__tests__/shared-executors.test.ts +0 -609
package/src/tui/output-block.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
5
5
|
import { ImageProtocol, padding, TERMINAL, visibleWidth, wrapTextWithAnsi } from "@oh-my-pi/pi-tui";
|
|
6
|
-
import type { Theme } from "../modes/theme/theme";
|
|
6
|
+
import type { Theme, ThemeColor } from "../modes/theme/theme";
|
|
7
7
|
import { getSixelLineMask } from "../utils/sixel";
|
|
8
8
|
import type { State } from "./types";
|
|
9
9
|
import type { RenderCache } from "./utils";
|
|
@@ -13,11 +13,15 @@ export interface OutputBlockOptions {
|
|
|
13
13
|
header?: string;
|
|
14
14
|
headerMeta?: string;
|
|
15
15
|
state?: State;
|
|
16
|
-
sections?: Array<{ label?: string; lines: string[] }>;
|
|
16
|
+
sections?: Array<{ label?: string; lines: string[]; separator?: boolean }>;
|
|
17
17
|
width: number;
|
|
18
18
|
applyBg?: boolean;
|
|
19
|
+
contentPaddingLeft?: number;
|
|
19
20
|
/** Animate the border with a sweeping dark segment (pending/running state). */
|
|
20
21
|
animate?: boolean;
|
|
22
|
+
/** Override the state-derived border color. Used for muted "legacy" tool
|
|
23
|
+
* frames that should not visually compete with framed-output tools. */
|
|
24
|
+
borderColor?: ThemeColor;
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
const FRAMED_BLOCK_COMPONENT = Symbol("framedBlockComponent");
|
|
@@ -33,7 +37,7 @@ export function isFramedBlockComponent(component: Component): boolean {
|
|
|
33
37
|
return (component as FramedBlockComponent)[FRAMED_BLOCK_COMPONENT] === true;
|
|
34
38
|
}
|
|
35
39
|
|
|
36
|
-
const BORDER_SHIMMER_TICK_MS =
|
|
40
|
+
const BORDER_SHIMMER_TICK_MS = 1000 / 30;
|
|
37
41
|
/** Duration of one full left↔right↔left bounce of the bottom-edge segment, in
|
|
38
42
|
* ms. Position is derived from the wall clock against this fixed cycle so a
|
|
39
43
|
* resize only nudges the segment proportionally instead of teleporting it. */
|
|
@@ -42,9 +46,9 @@ const BORDER_BOUNCE_MS = 3000;
|
|
|
42
46
|
const BORDER_SEGMENT_LEN = 8;
|
|
43
47
|
|
|
44
48
|
/**
|
|
45
|
-
* Monotonic frame counter for animated borders, quantized to the TUI's ~
|
|
46
|
-
* render cap so the cache key advances once per
|
|
47
|
-
* smooth segment sweep, coarse enough to coalesce multiple render passes
|
|
49
|
+
* Monotonic frame counter for animated borders, quantized to the TUI's ~30fps
|
|
50
|
+
* render cap so the cache key advances once per animation frame — fine enough
|
|
51
|
+
* for a smooth segment sweep, coarse enough to coalesce multiple render passes
|
|
48
52
|
* land inside the same frame.
|
|
49
53
|
*/
|
|
50
54
|
export function borderShimmerTick(): number {
|
|
@@ -92,6 +96,11 @@ type BlockRow =
|
|
|
92
96
|
| { kind: "content"; inner: string }
|
|
93
97
|
| { kind: "sixel"; raw: string };
|
|
94
98
|
|
|
99
|
+
function normalizeContentPaddingLeft(value: number | undefined): number {
|
|
100
|
+
if (value === undefined || !Number.isFinite(value)) return 1;
|
|
101
|
+
return Math.max(0, Math.floor(value));
|
|
102
|
+
}
|
|
103
|
+
|
|
95
104
|
export function renderOutputBlock(options: OutputBlockOptions, theme: Theme): string[] {
|
|
96
105
|
const { header, headerMeta, state, sections = [], width, applyBg = true } = options;
|
|
97
106
|
const h = theme.boxSharp.horizontal;
|
|
@@ -99,14 +108,15 @@ export function renderOutputBlock(options: OutputBlockOptions, theme: Theme): st
|
|
|
99
108
|
const cap = h.repeat(3);
|
|
100
109
|
const lineWidth = Math.max(0, width);
|
|
101
110
|
// Border colors: running/pending use accent, success uses dim (gray), error/warning keep their colors
|
|
102
|
-
const borderColor:
|
|
103
|
-
|
|
111
|
+
const borderColor: ThemeColor =
|
|
112
|
+
options.borderColor ??
|
|
113
|
+
(state === "error"
|
|
104
114
|
? "error"
|
|
105
115
|
: state === "warning"
|
|
106
116
|
? "warning"
|
|
107
117
|
: state === "running" || state === "pending"
|
|
108
118
|
? "accent"
|
|
109
|
-
: "dim";
|
|
119
|
+
: "dim");
|
|
110
120
|
const border = (text: string) => theme.fg(borderColor, text);
|
|
111
121
|
const bgFn = (() => {
|
|
112
122
|
if (!state || !applyBg) return undefined;
|
|
@@ -121,7 +131,9 @@ export function renderOutputBlock(options: OutputBlockOptions, theme: Theme): st
|
|
|
121
131
|
};
|
|
122
132
|
})();
|
|
123
133
|
|
|
124
|
-
const
|
|
134
|
+
const contentPaddingLeft = normalizeContentPaddingLeft(options.contentPaddingLeft);
|
|
135
|
+
const contentWidth = Math.max(0, lineWidth - visibleWidth(v) - contentPaddingLeft - visibleWidth(v));
|
|
136
|
+
const contentLeftPadding = contentPaddingLeft > 0 ? padding(contentPaddingLeft) : "";
|
|
125
137
|
|
|
126
138
|
// ── Layout pass: collect row descriptors so the border perimeter length is
|
|
127
139
|
// known before the moving segment is positioned. ──
|
|
@@ -135,7 +147,11 @@ export function renderOutputBlock(options: OutputBlockOptions, theme: Theme): st
|
|
|
135
147
|
});
|
|
136
148
|
|
|
137
149
|
const normalizedSections = sections.length > 0 ? sections : [{ lines: [] as string[] }];
|
|
138
|
-
for (
|
|
150
|
+
for (let sectionIndex = 0; sectionIndex < normalizedSections.length; sectionIndex++) {
|
|
151
|
+
const section = normalizedSections[sectionIndex]!;
|
|
152
|
+
// A labeled section always draws its titled separator bar. A label-less
|
|
153
|
+
// section can still request a plain divider via `separator`, but only
|
|
154
|
+
// between sections — leading with one would just double the header bar.
|
|
139
155
|
if (section.label) {
|
|
140
156
|
rows.push({
|
|
141
157
|
kind: "bar",
|
|
@@ -143,6 +159,12 @@ export function renderOutputBlock(options: OutputBlockOptions, theme: Theme): st
|
|
|
143
159
|
rightChar: theme.boxSharp.teeLeft,
|
|
144
160
|
label: section.label,
|
|
145
161
|
});
|
|
162
|
+
} else if (section.separator && sectionIndex > 0) {
|
|
163
|
+
rows.push({
|
|
164
|
+
kind: "bar",
|
|
165
|
+
leftChar: theme.boxSharp.teeRight,
|
|
166
|
+
rightChar: theme.boxSharp.teeLeft,
|
|
167
|
+
});
|
|
146
168
|
}
|
|
147
169
|
const allLines = section.lines.flatMap(l => l.split("\n"));
|
|
148
170
|
const sixelLineMask = TERMINAL.imageProtocol === ImageProtocol.Sixel ? getSixelLineMask(allLines) : undefined;
|
|
@@ -202,7 +224,12 @@ export function renderOutputBlock(options: OutputBlockOptions, theme: Theme): st
|
|
|
202
224
|
const rightGlyph = row.rightChar;
|
|
203
225
|
if (lineWidth <= 0) return border(leftGlyphs) + border(rightGlyph);
|
|
204
226
|
const labelText = [row.label, row.meta].filter(Boolean).join(theme.sep.dot);
|
|
205
|
-
|
|
227
|
+
if (!labelText) {
|
|
228
|
+
// No header: draw a clean, continuous top/separator bar (no 1-col gap).
|
|
229
|
+
const fillCount = Math.max(0, lineWidth - visibleWidth(leftGlyphs) - visibleWidth(rightGlyph));
|
|
230
|
+
return `${border(leftGlyphs)}${border(h.repeat(fillCount))}${border(rightGlyph)}`;
|
|
231
|
+
}
|
|
232
|
+
const rawLabel = ` ${labelText} `;
|
|
206
233
|
const leftWidth = visibleWidth(leftGlyphs);
|
|
207
234
|
const rightWidth = visibleWidth(rightGlyph);
|
|
208
235
|
const maxLabelWidth = Math.max(0, lineWidth - leftWidth - rightWidth);
|
|
@@ -225,7 +252,7 @@ export function renderOutputBlock(options: OutputBlockOptions, theme: Theme): st
|
|
|
225
252
|
return `${leftStr}${fillStr}${rightStr}`;
|
|
226
253
|
};
|
|
227
254
|
|
|
228
|
-
const renderContent = (inner: string): string => `${border(
|
|
255
|
+
const renderContent = (inner: string): string => `${border(v)}${contentLeftPadding}${inner}${border(v)}`;
|
|
229
256
|
|
|
230
257
|
const lines: string[] = [];
|
|
231
258
|
for (let r = 0; r < H; r++) {
|
|
@@ -269,15 +296,18 @@ export class CachedOutputBlock {
|
|
|
269
296
|
#buildKey(options: OutputBlockOptions): bigint {
|
|
270
297
|
const h = new Hasher();
|
|
271
298
|
h.u32(options.width);
|
|
299
|
+
h.u32(normalizeContentPaddingLeft(options.contentPaddingLeft));
|
|
272
300
|
h.optional(options.header);
|
|
273
301
|
h.optional(options.headerMeta);
|
|
274
302
|
h.optional(options.state);
|
|
303
|
+
h.optional(options.borderColor);
|
|
275
304
|
h.bool(options.applyBg ?? true);
|
|
276
305
|
h.bool(options.animate ?? false);
|
|
277
306
|
if (options.animate) h.u32(borderShimmerTick());
|
|
278
307
|
if (options.sections) {
|
|
279
308
|
for (const s of options.sections) {
|
|
280
309
|
h.optional(s.label);
|
|
310
|
+
h.bool(s.separator ?? false);
|
|
281
311
|
for (const line of s.lines) {
|
|
282
312
|
h.str(line);
|
|
283
313
|
}
|
|
@@ -286,3 +316,20 @@ export class CachedOutputBlock {
|
|
|
286
316
|
return h.digest();
|
|
287
317
|
}
|
|
288
318
|
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Build a self-framing tool component backed by a cached output block. The
|
|
322
|
+
* `build` callback returns the block options for a given width; the cache
|
|
323
|
+
* dedupes re-renders. Pass `borderColor: "borderMuted"` for the dim "legacy"
|
|
324
|
+
* look that does not compete with the state-colored framed tools.
|
|
325
|
+
*/
|
|
326
|
+
export function framedBlock(theme: Theme, build: (width: number) => OutputBlockOptions): Component {
|
|
327
|
+
const block = new CachedOutputBlock();
|
|
328
|
+
// Marked so the tool-execution container treats it as self-framing (renders
|
|
329
|
+
// flush, no extra padding/background) the same way `markFramedBlockComponent`
|
|
330
|
+
// blocks are treated.
|
|
331
|
+
return markFramedBlockComponent({
|
|
332
|
+
render: (width: number): string[] => block.render(build(width), theme),
|
|
333
|
+
invalidate: () => block.invalidate(),
|
|
334
|
+
});
|
|
335
|
+
}
|
package/src/tui/status-line.ts
CHANGED
|
@@ -7,6 +7,9 @@ import { formatStatusIcon } from "../tools/render-utils";
|
|
|
7
7
|
|
|
8
8
|
export interface StatusLineOptions {
|
|
9
9
|
icon?: ToolUIStatus;
|
|
10
|
+
/** Pre-rendered glyph that replaces the status icon (e.g. a magnifier for
|
|
11
|
+
* search-family tools). Takes precedence over `icon`. */
|
|
12
|
+
iconOverride?: string;
|
|
10
13
|
spinnerFrame?: number;
|
|
11
14
|
title: string;
|
|
12
15
|
titleColor?: ThemeColor;
|
|
@@ -27,7 +30,8 @@ function flattenForHeader(text: string): string {
|
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
export function renderStatusLine(options: StatusLineOptions, theme: Theme): string {
|
|
30
|
-
const icon =
|
|
33
|
+
const icon =
|
|
34
|
+
options.iconOverride ?? (options.icon ? formatStatusIcon(options.icon, theme, options.spinnerFrame) : "");
|
|
31
35
|
const titleColor = options.titleColor ?? "accent";
|
|
32
36
|
const title = theme.fg(titleColor, flattenForHeader(options.title));
|
|
33
37
|
let line = icon ? `${icon} ${title}` : title;
|
|
@@ -6,6 +6,7 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
|
6
6
|
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
7
7
|
import { completeSimple } from "@oh-my-pi/pi-ai";
|
|
8
8
|
import { logger, prompt } from "@oh-my-pi/pi-utils";
|
|
9
|
+
|
|
9
10
|
import type { ModelRegistry } from "../config/model-registry";
|
|
10
11
|
import { resolveModelRoleValue } from "../config/model-resolver";
|
|
11
12
|
import type { Settings } from "../config/settings";
|
|
@@ -110,7 +111,14 @@ export async function generateCommitMessage(
|
|
|
110
111
|
systemPrompt: [COMMIT_SYSTEM_PROMPT],
|
|
111
112
|
messages: [{ role: "user", content: userMessage, timestamp: Date.now() }],
|
|
112
113
|
},
|
|
113
|
-
{
|
|
114
|
+
{
|
|
115
|
+
apiKey: registry.resolver(candidate.model.provider, {
|
|
116
|
+
sessionId,
|
|
117
|
+
baseUrl: candidate.model.baseUrl,
|
|
118
|
+
}),
|
|
119
|
+
maxTokens,
|
|
120
|
+
reasoning: toReasoningEffort(candidate.thinkingLevel),
|
|
121
|
+
},
|
|
114
122
|
);
|
|
115
123
|
|
|
116
124
|
if (response.stopReason === "error") {
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
2
|
+
|
|
3
|
+
const OSC5522_PREFIX = "\x1b]5522;";
|
|
4
|
+
const OSC_TERMINATOR_ST = "\x1b\\";
|
|
5
|
+
const OSC_TERMINATOR_BEL = "\x07";
|
|
6
|
+
const PASTE_EVENT_NAME_BASE64 = Buffer.from("Paste event", "utf8").toString("base64");
|
|
7
|
+
|
|
8
|
+
const IMAGE_MIME_PRIORITY = ["image/png", "image/jpeg", "image/webp", "image/gif"] as const;
|
|
9
|
+
const TEXT_MIME_TYPE = "text/plain";
|
|
10
|
+
|
|
11
|
+
type PasteReadKind = "image" | "text";
|
|
12
|
+
|
|
13
|
+
export interface Osc5522Packet {
|
|
14
|
+
metadata: Map<string, string>;
|
|
15
|
+
payload: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface PasteListingState {
|
|
19
|
+
phase: "listing";
|
|
20
|
+
mimes: string[];
|
|
21
|
+
pw?: string;
|
|
22
|
+
loc?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface PasteReadState {
|
|
26
|
+
phase: "reading";
|
|
27
|
+
kind: PasteReadKind;
|
|
28
|
+
mimeType: string;
|
|
29
|
+
chunks: string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type PasteState = PasteListingState | PasteReadState;
|
|
33
|
+
|
|
34
|
+
export interface EnhancedPasteHandlers {
|
|
35
|
+
write(data: string): void;
|
|
36
|
+
pasteText(text: string): void;
|
|
37
|
+
pasteImage(image: ImageContent): void | Promise<void>;
|
|
38
|
+
showStatus(message: string): void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function isOsc5522Packet(data: string): boolean {
|
|
42
|
+
return data.startsWith(OSC5522_PREFIX) && (data.endsWith(OSC_TERMINATOR_ST) || data.endsWith(OSC_TERMINATOR_BEL));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function decodeBase64Utf8(value: string): string | undefined {
|
|
46
|
+
try {
|
|
47
|
+
return Buffer.from(value, "base64").toString("utf8");
|
|
48
|
+
} catch {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function parseMetadata(raw: string): Map<string, string> {
|
|
54
|
+
const metadata = new Map<string, string>();
|
|
55
|
+
for (const part of raw.split(":")) {
|
|
56
|
+
const eq = part.indexOf("=");
|
|
57
|
+
if (eq <= 0) continue;
|
|
58
|
+
metadata.set(part.slice(0, eq), part.slice(eq + 1));
|
|
59
|
+
}
|
|
60
|
+
return metadata;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function parseOsc5522Packet(data: string): Osc5522Packet | undefined {
|
|
64
|
+
if (!isOsc5522Packet(data)) return undefined;
|
|
65
|
+
const bodyEnd = data.endsWith(OSC_TERMINATOR_BEL) ? data.length - 1 : data.length - OSC_TERMINATOR_ST.length;
|
|
66
|
+
const body = data.slice(OSC5522_PREFIX.length, bodyEnd);
|
|
67
|
+
const separator = body.indexOf(";");
|
|
68
|
+
const metadataRaw = separator === -1 ? body : body.slice(0, separator);
|
|
69
|
+
const payload = separator === -1 ? "" : body.slice(separator + 1);
|
|
70
|
+
return { metadata: parseMetadata(metadataRaw), payload };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function choosePasteMime(mimes: readonly string[]): { kind: PasteReadKind; mimeType: string } | undefined {
|
|
74
|
+
for (const mimeType of IMAGE_MIME_PRIORITY) {
|
|
75
|
+
if (mimes.includes(mimeType)) return { kind: "image", mimeType };
|
|
76
|
+
}
|
|
77
|
+
return mimes.includes(TEXT_MIME_TYPE) ? { kind: "text", mimeType: TEXT_MIME_TYPE } : undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export class EnhancedPasteController {
|
|
81
|
+
#state: PasteState | undefined;
|
|
82
|
+
#handlers: EnhancedPasteHandlers;
|
|
83
|
+
|
|
84
|
+
constructor(handlers: EnhancedPasteHandlers) {
|
|
85
|
+
this.#handlers = handlers;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
enable(): void {
|
|
89
|
+
this.#handlers.write("\x1b[?5522h");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
disable(): void {
|
|
93
|
+
this.#handlers.write("\x1b[?5522l");
|
|
94
|
+
this.#state = undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
handleInput(data: string): boolean {
|
|
98
|
+
const packet = parseOsc5522Packet(data);
|
|
99
|
+
if (!packet) return false;
|
|
100
|
+
void this.#handlePacket(packet);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async #handlePacket(packet: Osc5522Packet): Promise<void> {
|
|
105
|
+
const type = packet.metadata.get("type");
|
|
106
|
+
if (type !== "read") return;
|
|
107
|
+
|
|
108
|
+
const status = packet.metadata.get("status");
|
|
109
|
+
if (status === "OK") {
|
|
110
|
+
this.#handleOk(packet);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (status === "DATA") {
|
|
114
|
+
this.#handleData(packet);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (status === "DONE") {
|
|
118
|
+
await this.#handleDone();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (status) {
|
|
122
|
+
this.#state = undefined;
|
|
123
|
+
this.#handlers.showStatus(`Enhanced paste failed: ${status}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
#handleOk(packet: Osc5522Packet): void {
|
|
128
|
+
if (this.#state?.phase === "reading") return;
|
|
129
|
+
const loc = packet.metadata.get("loc");
|
|
130
|
+
this.#state = {
|
|
131
|
+
phase: "listing",
|
|
132
|
+
mimes: [],
|
|
133
|
+
pw: packet.metadata.get("pw"),
|
|
134
|
+
loc: loc === "primary" ? loc : undefined,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
#handleData(packet: Osc5522Packet): void {
|
|
139
|
+
const state = this.#state;
|
|
140
|
+
if (!state) return;
|
|
141
|
+
const encodedMime = packet.metadata.get("mime");
|
|
142
|
+
if (!encodedMime) return;
|
|
143
|
+
const mimeType = decodeBase64Utf8(encodedMime);
|
|
144
|
+
if (!mimeType) return;
|
|
145
|
+
|
|
146
|
+
if (state.phase === "listing") {
|
|
147
|
+
state.mimes.push(mimeType);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (state.mimeType === mimeType && packet.payload) {
|
|
152
|
+
state.chunks.push(packet.payload);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async #handleDone(): Promise<void> {
|
|
157
|
+
const state = this.#state;
|
|
158
|
+
if (!state) return;
|
|
159
|
+
if (state.phase === "listing") {
|
|
160
|
+
this.#finishListing(state);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
this.#state = undefined;
|
|
164
|
+
const bytes = Buffer.concat(state.chunks.map(chunk => Buffer.from(chunk, "base64")));
|
|
165
|
+
if (bytes.byteLength === 0) {
|
|
166
|
+
this.#handlers.showStatus("Clipboard paste was empty");
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (state.kind === "text") {
|
|
170
|
+
this.#handlers.pasteText(bytes.toString("utf8"));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
await this.#handlers.pasteImage({
|
|
174
|
+
type: "image",
|
|
175
|
+
data: bytes.toString("base64"),
|
|
176
|
+
mimeType: state.mimeType,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
#finishListing(state: PasteListingState): void {
|
|
181
|
+
const selected = choosePasteMime(state.mimes);
|
|
182
|
+
if (!selected) {
|
|
183
|
+
this.#state = undefined;
|
|
184
|
+
this.#handlers.showStatus("Clipboard paste has no supported text or image data");
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
this.#state = {
|
|
189
|
+
phase: "reading",
|
|
190
|
+
kind: selected.kind,
|
|
191
|
+
mimeType: selected.mimeType,
|
|
192
|
+
chunks: [],
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const metadata = [`type=read`, `mime=${Buffer.from(selected.mimeType, "utf8").toString("base64")}`];
|
|
196
|
+
if (state.loc) metadata.push(`loc=${state.loc}`);
|
|
197
|
+
if (state.pw) {
|
|
198
|
+
metadata.push(`pw=${state.pw}`, `name=${PASTE_EVENT_NAME_BASE64}`);
|
|
199
|
+
}
|
|
200
|
+
this.#handlers.write(`${OSC5522_PREFIX}${metadata.join(":")}${OSC_TERMINATOR_ST}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -6,6 +6,7 @@ import * as path from "node:path";
|
|
|
6
6
|
import { type Api, type AssistantMessage, completeSimple, type Model, type Tool } from "@oh-my-pi/pi-ai";
|
|
7
7
|
import { logger, prompt } from "@oh-my-pi/pi-utils";
|
|
8
8
|
import type { ModelRegistry } from "../config/model-registry";
|
|
9
|
+
|
|
9
10
|
import { resolveRoleSelection } from "../config/model-resolver";
|
|
10
11
|
import type { Settings } from "../config/settings";
|
|
11
12
|
import titleSystemPrompt from "../prompts/system/title-system.md" with { type: "text" };
|
|
@@ -238,7 +239,7 @@ export async function generateTitleOnline(
|
|
|
238
239
|
tools: [setTitleTool],
|
|
239
240
|
},
|
|
240
241
|
{
|
|
241
|
-
apiKey,
|
|
242
|
+
apiKey: registry.resolver(model.provider, { sessionId, baseUrl: model.baseUrl }),
|
|
242
243
|
maxTokens,
|
|
243
244
|
disableReasoning: true,
|
|
244
245
|
toolChoice: { type: "tool", name: SET_TITLE_TOOL_NAME },
|