@oh-my-pi/pi-coding-agent 15.11.6 → 15.11.7
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 +29 -1
- package/dist/cli.js +114 -71
- package/dist/types/cli/bench-cli.d.ts +78 -0
- package/dist/types/commands/bench.d.ts +29 -0
- package/dist/types/config/model-resolver.d.ts +3 -2
- package/dist/types/config/settings-schema.d.ts +72 -0
- package/dist/types/edit/renderer.d.ts +1 -0
- package/dist/types/modes/components/oauth-selector.d.ts +10 -1
- package/dist/types/modes/components/settings-selector.d.ts +8 -1
- package/dist/types/modes/components/snapcompact-shape-preview.d.ts +31 -0
- package/dist/types/modes/components/tool-execution.d.ts +13 -9
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +3 -0
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +10 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +3 -0
- package/dist/types/session/snapcompact-inline.d.ts +2 -0
- package/dist/types/tools/bash.d.ts +2 -0
- package/dist/types/tools/eval-render.d.ts +1 -0
- package/dist/types/tools/renderers.d.ts +13 -0
- package/dist/types/tools/ssh.d.ts +1 -0
- package/package.json +11 -11
- package/src/cli/bench-cli.ts +437 -0
- package/src/cli-commands.ts +1 -0
- package/src/commands/bench.ts +42 -0
- package/src/config/model-registry.ts +52 -5
- package/src/config/model-resolver.ts +36 -5
- package/src/config/settings-schema.ts +92 -0
- package/src/edit/renderer.ts +5 -0
- package/src/hindsight/client.ts +26 -1
- package/src/hindsight/state.ts +6 -2
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/mcp/transports/stdio.ts +81 -7
- package/src/modes/components/oauth-selector.ts +67 -7
- package/src/modes/components/settings-selector.ts +27 -0
- package/src/modes/components/snapcompact-shape-preview-doc.md +11 -0
- package/src/modes/components/snapcompact-shape-preview.ts +192 -0
- package/src/modes/components/tool-execution.ts +18 -10
- package/src/modes/controllers/input-controller.ts +8 -6
- package/src/modes/controllers/selector-controller.ts +4 -2
- package/src/modes/interactive-mode.ts +24 -0
- package/src/modes/setup-wizard/index.ts +1 -0
- package/src/modes/setup-wizard/scenes/glyph.ts +24 -6
- package/src/modes/setup-wizard/scenes/providers.ts +36 -2
- package/src/modes/setup-wizard/scenes/sign-in.ts +10 -1
- package/src/modes/setup-wizard/scenes/theme.ts +28 -1
- package/src/modes/setup-wizard/scenes/types.ts +10 -1
- package/src/modes/setup-wizard/scenes/web-search.ts +22 -6
- package/src/modes/setup-wizard/wizard-overlay.ts +38 -1
- package/src/modes/utils/context-usage.ts +1 -1
- package/src/prompts/bench.md +7 -0
- package/src/sdk.ts +1 -0
- package/src/session/agent-session.ts +5 -0
- package/src/session/snapcompact-inline.ts +11 -19
- package/src/tools/bash.ts +3 -0
- package/src/tools/eval-render.ts +4 -0
- package/src/tools/renderers.ts +13 -0
- package/src/tools/ssh.ts +3 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AuthStorage } from "@oh-my-pi/pi-ai";
|
|
2
2
|
import { PASTE_CODE_LOGIN_PROVIDERS } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import type { OAuthProvider } from "@oh-my-pi/pi-ai/oauth/types";
|
|
4
|
-
import { Input, matchesKey, wrapTextWithAnsi } from "@oh-my-pi/pi-tui";
|
|
4
|
+
import { Input, matchesKey, type SgrMouseEvent, wrapTextWithAnsi } from "@oh-my-pi/pi-tui";
|
|
5
5
|
import { getAgentDbPath } from "@oh-my-pi/pi-utils";
|
|
6
6
|
import { OAuthSelectorComponent } from "../../components/oauth-selector";
|
|
7
7
|
import { theme } from "../../theme/theme";
|
|
@@ -35,6 +35,8 @@ export class SignInTab implements SetupTab {
|
|
|
35
35
|
#loginAbort: AbortController | undefined;
|
|
36
36
|
#loggingInProvider: string | undefined;
|
|
37
37
|
#disposed = false;
|
|
38
|
+
/** Render line where the provider selector begins. */
|
|
39
|
+
#selectorRowStart = 2;
|
|
38
40
|
|
|
39
41
|
constructor(private readonly host: SetupSceneHost) {
|
|
40
42
|
this.#authStorage = host.ctx.session.modelRegistry.authStorage;
|
|
@@ -68,12 +70,19 @@ export class SignInTab implements SetupTab {
|
|
|
68
70
|
this.#selector.handleInput(data);
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
/** Forward mouse to the provider selector; pointer is inert during an active login or code prompt. */
|
|
74
|
+
routeMouse(event: SgrMouseEvent, line: number, col: number): void {
|
|
75
|
+
if (this.#loggingInProvider || this.#prompt) return;
|
|
76
|
+
this.#selector.routeMouse(event, line - this.#selectorRowStart, col);
|
|
77
|
+
}
|
|
78
|
+
|
|
71
79
|
render(width: number): readonly string[] {
|
|
72
80
|
const lines: string[] = [];
|
|
73
81
|
if (this.#loggingInProvider) {
|
|
74
82
|
lines.push(theme.bold(`Signing in to ${this.#loggingInProvider}`));
|
|
75
83
|
} else {
|
|
76
84
|
lines.push(theme.fg("muted", "Pick a provider to sign in — you can connect more than one."), "");
|
|
85
|
+
this.#selectorRowStart = lines.length;
|
|
77
86
|
lines.push(...this.#selector.render(width));
|
|
78
87
|
}
|
|
79
88
|
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
padding,
|
|
3
|
+
type SelectItem,
|
|
4
|
+
SelectList,
|
|
5
|
+
type SgrMouseEvent,
|
|
6
|
+
truncateToWidth,
|
|
7
|
+
visibleWidth,
|
|
8
|
+
} from "@oh-my-pi/pi-tui";
|
|
2
9
|
import {
|
|
3
10
|
enableAutoTheme,
|
|
4
11
|
getAvailableThemes,
|
|
@@ -89,6 +96,8 @@ class ThemeSceneController implements SetupSceneController {
|
|
|
89
96
|
#message: string | undefined;
|
|
90
97
|
#previewRequest = 0;
|
|
91
98
|
#disposed = false;
|
|
99
|
+
/** Render line where the select list began, or -1 while it is not shown. */
|
|
100
|
+
#listRowStart = -1;
|
|
92
101
|
readonly #originalTheme = getCurrentThemeName();
|
|
93
102
|
readonly #originalSymbolPreset: SymbolPreset;
|
|
94
103
|
readonly #originalColorBlindMode: boolean;
|
|
@@ -117,6 +126,22 @@ class ThemeSceneController implements SetupSceneController {
|
|
|
117
126
|
this.#selectList.handleInput(data);
|
|
118
127
|
}
|
|
119
128
|
|
|
129
|
+
/** Wheel moves the highlight (live preview); hover lights the row under the pointer; click confirms it. */
|
|
130
|
+
routeMouse(event: SgrMouseEvent, line: number, _col: number): void {
|
|
131
|
+
if (event.wheel !== null) {
|
|
132
|
+
this.#selectList.handleWheel(event.wheel);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const index = this.#listRowStart >= 0 ? this.#selectList.hitTest(line - this.#listRowStart) : undefined;
|
|
136
|
+
if (event.motion) {
|
|
137
|
+
this.#selectList.setHoverIndex(index ?? null);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (event.leftClick && index !== undefined) {
|
|
141
|
+
this.#selectList.clickItem(index);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
120
145
|
render(width: number): readonly string[] {
|
|
121
146
|
const lines = [
|
|
122
147
|
theme.fg("muted", "Theme changes preview live. Nothing is saved until you press Enter."),
|
|
@@ -128,8 +153,10 @@ class ThemeSceneController implements SetupSceneController {
|
|
|
128
153
|
"",
|
|
129
154
|
];
|
|
130
155
|
if (this.#loadingAllThemes) {
|
|
156
|
+
this.#listRowStart = -1;
|
|
131
157
|
lines.push(theme.fg("dim", "Loading themes…"));
|
|
132
158
|
} else {
|
|
159
|
+
this.#listRowStart = lines.length;
|
|
133
160
|
lines.push(...this.#selectList.render(width));
|
|
134
161
|
}
|
|
135
162
|
if (this.#message) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Component } from "@oh-my-pi/pi-tui";
|
|
1
|
+
import type { Component, SgrMouseEvent } from "@oh-my-pi/pi-tui";
|
|
2
2
|
import type { InteractiveModeContext } from "../../types";
|
|
3
3
|
|
|
4
4
|
export type SetupSceneResult = "done" | "skipped";
|
|
@@ -17,6 +17,13 @@ export interface SetupSceneController extends Component {
|
|
|
17
17
|
onMount?(): void | Promise<void>;
|
|
18
18
|
onUnmount?(): void;
|
|
19
19
|
dispose?(): void;
|
|
20
|
+
/**
|
|
21
|
+
* Route an SGR mouse report (tracking is on while the wizard holds the
|
|
22
|
+
* alternate screen). `line`/`col` are 0-based within this controller's
|
|
23
|
+
* last rendered output. When absent, the wizard falls back to synthesizing
|
|
24
|
+
* arrow keys from wheel notches.
|
|
25
|
+
*/
|
|
26
|
+
routeMouse?(event: SgrMouseEvent, line: number, col: number): void;
|
|
20
27
|
}
|
|
21
28
|
|
|
22
29
|
/**
|
|
@@ -36,6 +43,8 @@ export interface SetupTab {
|
|
|
36
43
|
invalidate(): void;
|
|
37
44
|
/** Called when the tab becomes active (including initial mount). */
|
|
38
45
|
onActivate?(): void;
|
|
46
|
+
/** Mouse routing at tab-local coordinates; see {@link SetupSceneController.routeMouse}. */
|
|
47
|
+
routeMouse?(event: SgrMouseEvent, line: number, col: number): void;
|
|
39
48
|
dispose(): void;
|
|
40
49
|
}
|
|
41
50
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type SelectItem, SelectList, truncateToWidth } from "@oh-my-pi/pi-tui";
|
|
1
|
+
import { type SelectItem, SelectList, type SgrMouseEvent, truncateToWidth } from "@oh-my-pi/pi-tui";
|
|
2
2
|
import { SETTINGS_SCHEMA } from "../../../config/settings-schema";
|
|
3
3
|
import { getSearchProvider, setPreferredSearchProvider } from "../../../web/search/provider";
|
|
4
4
|
import { isSearchProviderPreference, type SearchProviderId } from "../../../web/search/types";
|
|
@@ -31,6 +31,8 @@ export class WebSearchTab implements SetupTab {
|
|
|
31
31
|
#availability = new Map<SearchProviderId, Availability>();
|
|
32
32
|
#status: string[] = [];
|
|
33
33
|
#disposed = false;
|
|
34
|
+
/** Render line where the select list begins. */
|
|
35
|
+
#listRowStart = 0;
|
|
34
36
|
|
|
35
37
|
constructor(private readonly host: SetupSceneHost) {
|
|
36
38
|
this.#list = new SelectList(WEB_SEARCH_ITEMS, MAX_VISIBLE, getSelectListTheme());
|
|
@@ -55,6 +57,22 @@ export class WebSearchTab implements SetupTab {
|
|
|
55
57
|
this.#list.handleInput(data);
|
|
56
58
|
}
|
|
57
59
|
|
|
60
|
+
/** Wheel moves the highlight; hover lights the row under the pointer; click confirms it. */
|
|
61
|
+
routeMouse(event: SgrMouseEvent, line: number, _col: number): void {
|
|
62
|
+
if (event.wheel !== null) {
|
|
63
|
+
this.#list.handleWheel(event.wheel);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const index = this.#list.hitTest(line - this.#listRowStart);
|
|
67
|
+
if (event.motion) {
|
|
68
|
+
this.#list.setHoverIndex(index ?? null);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (event.leftClick && index !== undefined) {
|
|
72
|
+
this.#list.clickItem(index);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
58
76
|
invalidate(): void {
|
|
59
77
|
this.#list.invalidate();
|
|
60
78
|
}
|
|
@@ -64,11 +82,9 @@ export class WebSearchTab implements SetupTab {
|
|
|
64
82
|
}
|
|
65
83
|
|
|
66
84
|
render(width: number): readonly string[] {
|
|
67
|
-
const lines = [
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
...this.#list.render(width),
|
|
71
|
-
];
|
|
85
|
+
const lines = [theme.fg("muted", "Choose the provider the web_search tool should prefer."), ""];
|
|
86
|
+
this.#listRowStart = lines.length;
|
|
87
|
+
lines.push(...this.#list.render(width));
|
|
72
88
|
const selected = this.#list.getSelectedItem();
|
|
73
89
|
if (selected) {
|
|
74
90
|
lines.push("", ...this.#readinessLines(selected.value).map(line => truncateToWidth(line, width)));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Component, matchesKey, padding, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
1
|
+
import { type Component, matchesKey, padding, parseSgrMouse, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
2
2
|
import { APP_NAME } from "@oh-my-pi/pi-utils";
|
|
3
3
|
import { gradientLogo, PI_LOGO } from "../components/welcome";
|
|
4
4
|
import { theme } from "../theme/theme";
|
|
@@ -61,6 +61,8 @@ export class SetupWizardComponent implements Component {
|
|
|
61
61
|
#timer: NodeJS.Timeout | undefined;
|
|
62
62
|
#done = Promise.withResolvers<void>();
|
|
63
63
|
#disposed = false;
|
|
64
|
+
/** Screen row where the active scene's body began in the last rendered frame. */
|
|
65
|
+
#bodyRowStart = 0;
|
|
64
66
|
|
|
65
67
|
constructor(
|
|
66
68
|
readonly ctx: InteractiveModeContext,
|
|
@@ -87,6 +89,10 @@ export class SetupWizardComponent implements Component {
|
|
|
87
89
|
|
|
88
90
|
handleInput(data: string): void {
|
|
89
91
|
if (this.#phase === "done") return;
|
|
92
|
+
if (data.startsWith("\x1b[<")) {
|
|
93
|
+
this.#handleMouse(data);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
90
96
|
if (matchesKey(data, "ctrl+c")) {
|
|
91
97
|
this.#beginOutro();
|
|
92
98
|
return;
|
|
@@ -116,6 +122,36 @@ export class SetupWizardComponent implements Component {
|
|
|
116
122
|
this.#activeScene?.handleInput?.(data);
|
|
117
123
|
}
|
|
118
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Mouse handling for the fullscreen wizard (SGR tracking is on while the
|
|
127
|
+
* overlay holds the alternate screen). The frame paints from screen row 0,
|
|
128
|
+
* so report coordinates index directly into the last rendered lines: scene
|
|
129
|
+
* body rows start at #bodyRowStart, indented by SCENE_MARGIN_X. Scenes
|
|
130
|
+
* that implement routeMouse get hit-tested events (wheel, hover, click);
|
|
131
|
+
* for the rest a wheel notch falls back to an arrow key. A left click
|
|
132
|
+
* advances the splash/outro like Enter. Raw reports never reach scene
|
|
133
|
+
* keyboard input.
|
|
134
|
+
*/
|
|
135
|
+
#handleMouse(data: string): void {
|
|
136
|
+
const event = parseSgrMouse(data);
|
|
137
|
+
if (!event) return;
|
|
138
|
+
if (this.#phase === "splash" || this.#phase === "outro") {
|
|
139
|
+
if (!event.leftClick) return;
|
|
140
|
+
if (this.#phase === "splash") this.#beginScene();
|
|
141
|
+
else this.#complete();
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const scene = this.#activeScene;
|
|
145
|
+
if (!scene) return;
|
|
146
|
+
if (scene.routeMouse) {
|
|
147
|
+
scene.routeMouse(event, event.row - this.#bodyRowStart, event.col - SCENE_MARGIN_X);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (event.wheel !== null) {
|
|
151
|
+
scene.handleInput?.(event.wheel === -1 ? "\x1b[A" : "\x1b[B");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
119
155
|
render(width: number): readonly string[] {
|
|
120
156
|
const safeWidth = Math.max(1, width);
|
|
121
157
|
const height = Math.max(1, this.ctx.ui.terminal.rows);
|
|
@@ -163,6 +199,7 @@ export class SetupWizardComponent implements Component {
|
|
|
163
199
|
header.push(indentLine(theme.fg("muted", subtitle), width, SCENE_MARGIN_X));
|
|
164
200
|
}
|
|
165
201
|
header.push("");
|
|
202
|
+
this.#bodyRowStart = header.length;
|
|
166
203
|
|
|
167
204
|
const footer = [
|
|
168
205
|
"",
|
|
@@ -183,7 +183,7 @@ export function computeContextBreakdown(
|
|
|
183
183
|
const renderToolResults = session.settings.get("snapcompact.toolResults");
|
|
184
184
|
if (renderSystemPrompt !== "none" || renderToolResults) {
|
|
185
185
|
snapcompactSavings = estimateInlineSavings({
|
|
186
|
-
options: { renderSystemPrompt, renderToolResults },
|
|
186
|
+
options: { renderSystemPrompt, renderToolResults, shape: session.settings.get("snapcompact.shape") },
|
|
187
187
|
model,
|
|
188
188
|
systemPrompt: session.systemPrompt ?? [],
|
|
189
189
|
messages: session.messages ?? [],
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Write a continuous, plain-prose technical explanation of how a relational database executes a SQL query: lexing and parsing, semantic analysis, logical plan construction, cost-based optimization, physical operator selection, and row-by-row execution through the iterator model.
|
|
2
|
+
|
|
3
|
+
Form:
|
|
4
|
+
- Plain paragraphs only: no headings, no lists, no code fences, no preamble.
|
|
5
|
+
- Do not wrap up early or summarize; keep writing until you are cut off.
|
|
6
|
+
|
|
7
|
+
Output only the explanation.
|
package/src/sdk.ts
CHANGED
|
@@ -2168,6 +2168,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2168
2168
|
? new SnapcompactInlineTransformer({
|
|
2169
2169
|
renderSystemPrompt: snapcompactSystemPromptMode,
|
|
2170
2170
|
renderToolResults: settings.get("snapcompact.toolResults"),
|
|
2171
|
+
shape: settings.get("snapcompact.shape"),
|
|
2171
2172
|
})
|
|
2172
2173
|
: undefined;
|
|
2173
2174
|
const transformProviderContext =
|
|
@@ -6388,6 +6388,10 @@ export class AgentSession {
|
|
|
6388
6388
|
const snapcompactResult = await snapcompact.compact(preparation, {
|
|
6389
6389
|
convertToLlm,
|
|
6390
6390
|
model: this.model,
|
|
6391
|
+
shape: snapcompact.resolveShape(this.model, this.settings.get("snapcompact.shape")),
|
|
6392
|
+
// Providers with hard image caps (OpenRouter: 8) silently drop
|
|
6393
|
+
// frames past the cap — keep the archive within budget.
|
|
6394
|
+
maxFrames: snapcompact.providerFrameBudget(this.model.provider),
|
|
6391
6395
|
});
|
|
6392
6396
|
summary = snapcompactResult.summary;
|
|
6393
6397
|
shortSummary = snapcompactResult.shortSummary;
|
|
@@ -7921,6 +7925,7 @@ export class AgentSession {
|
|
|
7921
7925
|
const snapcompactResult = await snapcompact.compact(preparation, {
|
|
7922
7926
|
convertToLlm,
|
|
7923
7927
|
model: this.model,
|
|
7928
|
+
maxFrames: snapcompact.providerFrameBudget(this.model?.provider),
|
|
7924
7929
|
});
|
|
7925
7930
|
summary = snapcompactResult.summary;
|
|
7926
7931
|
shortSummary = snapcompactResult.shortSummary;
|
|
@@ -28,22 +28,15 @@ export type SnapcompactSystemPromptMode = "none" | "agents-md" | "all";
|
|
|
28
28
|
export interface SnapcompactInlineOptions {
|
|
29
29
|
renderSystemPrompt: SnapcompactSystemPromptMode;
|
|
30
30
|
renderToolResults: boolean;
|
|
31
|
+
/** Frame variant override; `"auto"`/omitted picks the provider's eval winner. */
|
|
32
|
+
shape?: snapcompact.ShapeVariantName | "auto";
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const INLINE_IMAGE_BUDGET_BY_PROVIDER: Record<string, number> = {
|
|
39
|
-
anthropic: 90,
|
|
40
|
-
"amazon-bedrock": 90,
|
|
41
|
-
openai: 200,
|
|
42
|
-
google: 200,
|
|
43
|
-
"google-vertex": 200,
|
|
44
|
-
"google-gemini-cli": 200,
|
|
45
|
-
};
|
|
46
|
-
const DEFAULT_INLINE_IMAGE_BUDGET = 5;
|
|
35
|
+
// Per-provider image-count budgets live in @oh-my-pi/snapcompact
|
|
36
|
+
// (`providerImageBudget`): snapcompact frames are 1568px (<2000px) so
|
|
37
|
+
// dimension/size limits never bind; only COUNT does. Once the budget is
|
|
38
|
+
// spent (e.g. OpenRouter's hard 8-image cap, already consumed by archive
|
|
39
|
+
// frames), tool results ship verbatim as text.
|
|
47
40
|
const MAX_SYSTEM_PROMPT_FRAMES = 6;
|
|
48
41
|
/** Tool results under this many tokens are never rasterized — the swap can't
|
|
49
42
|
* save enough to justify trading crisp text for an image. */
|
|
@@ -273,7 +266,7 @@ export function estimateInlineSavings(input: {
|
|
|
273
266
|
return { visionCapable: false, savedTokens: 0 };
|
|
274
267
|
}
|
|
275
268
|
|
|
276
|
-
const shape = snapcompact.resolveShape(model.
|
|
269
|
+
const shape = snapcompact.resolveShape(model, options.shape);
|
|
277
270
|
let existingImages = 0;
|
|
278
271
|
for (const message of input.messages) {
|
|
279
272
|
if (!Array.isArray(message.content)) continue;
|
|
@@ -281,7 +274,7 @@ export function estimateInlineSavings(input: {
|
|
|
281
274
|
if (block.type === "image") existingImages++;
|
|
282
275
|
}
|
|
283
276
|
}
|
|
284
|
-
const budget = (
|
|
277
|
+
const budget = snapcompact.providerImageBudget(model.provider) - existingImages;
|
|
285
278
|
|
|
286
279
|
const candidates: InlineToolResultCandidate[] = [];
|
|
287
280
|
if (options.renderToolResults) {
|
|
@@ -407,9 +400,8 @@ export class SnapcompactInlineTransformer {
|
|
|
407
400
|
// rendering would lose the content entirely.
|
|
408
401
|
if (!model.input.includes("image")) return context;
|
|
409
402
|
|
|
410
|
-
const shape = snapcompact.resolveShape(model.
|
|
411
|
-
const budget =
|
|
412
|
-
(INLINE_IMAGE_BUDGET_BY_PROVIDER[model.provider] ?? DEFAULT_INLINE_IMAGE_BUDGET) - countContextImages(context);
|
|
403
|
+
const shape = snapcompact.resolveShape(model, this.options.shape);
|
|
404
|
+
const budget = snapcompact.providerImageBudget(model.provider) - countContextImages(context);
|
|
413
405
|
if (budget <= 0) return context;
|
|
414
406
|
|
|
415
407
|
const messages = [...context.messages];
|
package/src/tools/bash.ts
CHANGED
|
@@ -1385,6 +1385,9 @@ export function createShellRenderer<TArgs>(config: ShellRendererConfig<TArgs>) {
|
|
|
1385
1385
|
},
|
|
1386
1386
|
mergeCallAndResult: true,
|
|
1387
1387
|
inline: true,
|
|
1388
|
+
// Pending preview caps the command to a viewport-sized tail window that
|
|
1389
|
+
// shifts while args stream; keep it out of native scrollback mid-run.
|
|
1390
|
+
provisionalPendingPreview: true,
|
|
1388
1391
|
};
|
|
1389
1392
|
}
|
|
1390
1393
|
|
package/src/tools/eval-render.ts
CHANGED
|
@@ -754,4 +754,8 @@ export const evalToolRenderer = {
|
|
|
754
754
|
|
|
755
755
|
mergeCallAndResult: true,
|
|
756
756
|
inline: true,
|
|
757
|
+
// Pending preview shows tail-window code cells; the result render
|
|
758
|
+
// interleaves each cell's output under its code, re-laying-out every row
|
|
759
|
+
// below the first cell. Keep the preview out of native scrollback mid-run.
|
|
760
|
+
provisionalPendingPreview: true,
|
|
757
761
|
};
|
package/src/tools/renderers.ts
CHANGED
|
@@ -43,6 +43,19 @@ export type ToolRenderer = {
|
|
|
43
43
|
mergeCallAndResult?: boolean;
|
|
44
44
|
/** Render without background box, inline in the response flow */
|
|
45
45
|
inline?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Collapsed pending preview is provisional — a tail-window or otherwise
|
|
48
|
+
* re-anchored view the result render replaces wholesale (an edit's
|
|
49
|
+
* streamed-diff tail, bash/ssh command caps, eval cells whose outputs
|
|
50
|
+
* interleave under each cell). Its rows must never commit to native
|
|
51
|
+
* scrollback mid-run; see
|
|
52
|
+
* `ToolExecutionComponent.isTranscriptBlockCommitStable`. Absent = the
|
|
53
|
+
* pending preview streams top-anchored append-shaped rows the result
|
|
54
|
+
* render preserves (task context/assignment, write content), which stay
|
|
55
|
+
* commit-eligible so a call taller than the viewport scrolls into history
|
|
56
|
+
* instead of reading as cut off.
|
|
57
|
+
*/
|
|
58
|
+
provisionalPendingPreview?: boolean;
|
|
46
59
|
};
|
|
47
60
|
|
|
48
61
|
export const toolRenderers: Record<string, ToolRenderer> = {
|
package/src/tools/ssh.ts
CHANGED
|
@@ -346,4 +346,7 @@ export const sshToolRenderer = {
|
|
|
346
346
|
});
|
|
347
347
|
},
|
|
348
348
|
mergeCallAndResult: true,
|
|
349
|
+
// Pending preview caps the command to a viewport-sized tail window that
|
|
350
|
+
// shifts while args stream; keep it out of native scrollback mid-run.
|
|
351
|
+
provisionalPendingPreview: true,
|
|
349
352
|
};
|