@oh-my-pi/pi-coding-agent 16.1.15 → 16.1.16
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 +48 -0
- package/dist/cli.js +3725 -4029
- package/dist/types/cli/args.d.ts +2 -5
- package/dist/types/cli/flag-tables.d.ts +2 -2
- package/dist/types/cli/session-picker.d.ts +4 -2
- package/dist/types/commands/launch.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +12 -1
- package/dist/types/eval/agent-bridge.d.ts +19 -0
- package/dist/types/eval/js/shared/helpers.d.ts +1 -13
- package/dist/types/eval/js/shared/types.d.ts +1 -1
- package/dist/types/eval/js/worker-protocol.d.ts +1 -1
- package/dist/types/eval/py/executor.d.ts +1 -1
- package/dist/types/internal-urls/local-protocol.d.ts +18 -1
- package/dist/types/main.d.ts +2 -0
- package/dist/types/modes/components/plugin-settings.d.ts +5 -0
- package/dist/types/modes/components/session-selector.d.ts +25 -0
- package/dist/types/task/isolation-runner.d.ts +128 -0
- package/dist/types/task/worktree.d.ts +14 -1
- package/dist/types/thinking.d.ts +15 -0
- package/dist/types/tools/eval-render.d.ts +3 -0
- package/dist/types/tools/eval.d.ts +11 -17
- package/dist/types/tools/todo.d.ts +26 -28
- package/dist/types/tui/output-block.d.ts +8 -0
- package/dist/types/utils/image-resize.d.ts +2 -0
- package/dist/types/web/search/providers/exa.d.ts +2 -0
- package/package.json +12 -12
- package/scripts/build-binary.ts +18 -4
- package/src/cli/args.ts +4 -5
- package/src/cli/flag-tables.ts +3 -3
- package/src/cli/gallery-fixtures/interaction.ts +6 -9
- package/src/cli/gallery-fixtures/shell.ts +15 -23
- package/src/cli/session-picker.ts +17 -3
- package/src/commands/launch.ts +3 -3
- package/src/config/settings-schema.ts +13 -1
- package/src/edit/renderer.ts +34 -12
- package/src/eval/__tests__/agent-bridge.test.ts +462 -3
- package/src/eval/__tests__/helpers-local-roots.test.ts +2 -5
- package/src/eval/__tests__/julia-prelude.test.ts +1 -30
- package/src/eval/__tests__/prelude-agent.test.ts +42 -8
- package/src/eval/agent-bridge.ts +301 -71
- package/src/eval/jl/prelude.jl +32 -227
- package/src/eval/jl/runner.jl +38 -12
- package/src/eval/js/shared/helpers.ts +1 -114
- package/src/eval/js/shared/prelude.txt +13 -27
- package/src/eval/js/shared/runtime.ts +0 -6
- package/src/eval/js/shared/types.ts +1 -1
- package/src/eval/js/worker-protocol.ts +1 -1
- package/src/eval/py/__tests__/prelude.test.ts +13 -0
- package/src/eval/py/executor.ts +1 -1
- package/src/eval/py/prelude.py +47 -105
- package/src/eval/py/runner.py +0 -6
- package/src/eval/rb/prelude.rb +21 -189
- package/src/eval/rb/runner.rb +116 -9
- package/src/export/html/tool-views.generated.js +29 -29
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/internal-urls/local-protocol.ts +100 -53
- package/src/main.ts +15 -4
- package/src/modes/acp/acp-event-mapper.ts +7 -2
- package/src/modes/components/plugin-settings.ts +7 -1
- package/src/modes/components/session-selector.ts +143 -29
- package/src/modes/controllers/command-controller.ts +5 -0
- package/src/modes/rpc/rpc-mode.ts +6 -0
- package/src/modes/utils/copy-targets.ts +7 -2
- package/src/prompts/system/system-prompt.md +3 -3
- package/src/prompts/system/workflow-notice.md +3 -3
- package/src/prompts/tools/bash.md +16 -0
- package/src/prompts/tools/eval.md +19 -19
- package/src/prompts/tools/todo.md +1 -1
- package/src/session/agent-session.ts +231 -50
- package/src/task/index.ts +61 -207
- package/src/task/isolation-runner.ts +354 -0
- package/src/task/worktree.ts +46 -9
- package/src/thinking.ts +20 -0
- package/src/tools/ask.ts +44 -38
- package/src/tools/bash.ts +9 -2
- package/src/tools/browser/tab-worker.ts +1 -1
- package/src/tools/eval-render.ts +34 -27
- package/src/tools/eval.ts +100 -103
- package/src/tools/index.ts +8 -1
- package/src/tools/read.ts +136 -60
- package/src/tools/todo.ts +60 -64
- package/src/tui/code-cell.ts +1 -1
- package/src/tui/output-block.ts +11 -0
- package/src/utils/image-resize.ts +30 -0
- package/src/web/search/providers/exa.ts +85 -1
|
@@ -10,6 +10,8 @@ import { type AuthStorage, type FetchImpl } from "@oh-my-pi/pi-ai";
|
|
|
10
10
|
import type { SearchResponse } from "../../../web/search/types";
|
|
11
11
|
import type { SearchParams } from "./base";
|
|
12
12
|
import { SearchProvider } from "./base";
|
|
13
|
+
/** Reset Exa request pacing state for isolated provider tests. */
|
|
14
|
+
export declare function resetExaSearchThrottleForTest(): void;
|
|
13
15
|
type ExaSearchType = "neural" | "fast" | "auto" | "deep";
|
|
14
16
|
type ExaSearchParamType = ExaSearchType | "keyword";
|
|
15
17
|
export interface ExaSearchParams {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "16.1.
|
|
4
|
+
"version": "16.1.16",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -48,17 +48,17 @@
|
|
|
48
48
|
"@agentclientprotocol/sdk": "0.25.0",
|
|
49
49
|
"@babel/parser": "^7.29.7",
|
|
50
50
|
"@mozilla/readability": "^0.6.0",
|
|
51
|
-
"@oh-my-pi/hashline": "16.1.
|
|
52
|
-
"@oh-my-pi/omp-stats": "16.1.
|
|
53
|
-
"@oh-my-pi/pi-agent-core": "16.1.
|
|
54
|
-
"@oh-my-pi/pi-ai": "16.1.
|
|
55
|
-
"@oh-my-pi/pi-catalog": "16.1.
|
|
56
|
-
"@oh-my-pi/pi-mnemopi": "16.1.
|
|
57
|
-
"@oh-my-pi/pi-natives": "16.1.
|
|
58
|
-
"@oh-my-pi/pi-tui": "16.1.
|
|
59
|
-
"@oh-my-pi/pi-utils": "16.1.
|
|
60
|
-
"@oh-my-pi/pi-wire": "16.1.
|
|
61
|
-
"@oh-my-pi/snapcompact": "16.1.
|
|
51
|
+
"@oh-my-pi/hashline": "16.1.16",
|
|
52
|
+
"@oh-my-pi/omp-stats": "16.1.16",
|
|
53
|
+
"@oh-my-pi/pi-agent-core": "16.1.16",
|
|
54
|
+
"@oh-my-pi/pi-ai": "16.1.16",
|
|
55
|
+
"@oh-my-pi/pi-catalog": "16.1.16",
|
|
56
|
+
"@oh-my-pi/pi-mnemopi": "16.1.16",
|
|
57
|
+
"@oh-my-pi/pi-natives": "16.1.16",
|
|
58
|
+
"@oh-my-pi/pi-tui": "16.1.16",
|
|
59
|
+
"@oh-my-pi/pi-utils": "16.1.16",
|
|
60
|
+
"@oh-my-pi/pi-wire": "16.1.16",
|
|
61
|
+
"@oh-my-pi/snapcompact": "16.1.16",
|
|
62
62
|
"@opentelemetry/api": "^1.9.1",
|
|
63
63
|
"@opentelemetry/context-async-hooks": "^2.7.1",
|
|
64
64
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
|
package/scripts/build-binary.ts
CHANGED
|
@@ -5,7 +5,15 @@ import * as path from "node:path";
|
|
|
5
5
|
|
|
6
6
|
const packageDir = path.join(import.meta.dir, "..");
|
|
7
7
|
const repoRoot = path.join(packageDir, "..", "..");
|
|
8
|
-
|
|
8
|
+
// Optional cross-compile target, e.g. CROSS_TARGET=linux-arm64 → bun build
|
|
9
|
+
// --target=bun-linux-arm64, embeds the matching native, outputs dist/omp-<target>.
|
|
10
|
+
const crossTarget = Bun.env.CROSS_TARGET || null;
|
|
11
|
+
const [crossPlatform, crossArch] = crossTarget ? crossTarget.split("-") : [null, null];
|
|
12
|
+
// x64 uses the baseline bun runtime so it runs under Rosetta / pre-AVX2 CPUs
|
|
13
|
+
// (the modern bun-linux-x64 target SIGILLs under Apple-Silicon Rosetta).
|
|
14
|
+
const bunTarget = crossTarget ? (crossTarget === "linux-x64" ? "bun-linux-x64-baseline" : `bun-${crossTarget}`) : null;
|
|
15
|
+
const outName = crossTarget ? `omp-${crossTarget}` : "omp";
|
|
16
|
+
const outputPath = path.join(packageDir, "dist", outName);
|
|
9
17
|
|
|
10
18
|
// Transformers.js is an optional, native-heavy dependency that is never bundled
|
|
11
19
|
// into the binary; the tiny-model worker `bun install`s it into a runtime cache
|
|
@@ -17,7 +25,7 @@ const transformersVersion = (
|
|
|
17
25
|
).version;
|
|
18
26
|
|
|
19
27
|
function shouldAdhocSignDarwinBinary(): boolean {
|
|
20
|
-
return process.platform === "darwin";
|
|
28
|
+
return process.platform === "darwin" && !crossTarget;
|
|
21
29
|
}
|
|
22
30
|
|
|
23
31
|
async function runCommand(
|
|
@@ -43,7 +51,12 @@ async function main(): Promise<void> {
|
|
|
43
51
|
try {
|
|
44
52
|
await runCommand(["bun", "--cwd=../stats", "scripts/generate-client-bundle.ts", "--generate"]);
|
|
45
53
|
await runCommand(["bun", "scripts/generate-docs-index.ts", "--generate"]);
|
|
46
|
-
await runCommand(
|
|
54
|
+
await runCommand(
|
|
55
|
+
["bun", "--cwd=../natives", "run", "embed:native"],
|
|
56
|
+
crossTarget
|
|
57
|
+
? { ...Bun.env, TARGET_PLATFORM: crossPlatform as string, TARGET_ARCH: crossArch as string }
|
|
58
|
+
: Bun.env,
|
|
59
|
+
);
|
|
47
60
|
await runCommand(["bun", "scripts/embed-mupdf-wasm.ts", "--generate"]);
|
|
48
61
|
try {
|
|
49
62
|
const buildEnv = shouldAdhocSignDarwinBinary() ? { ...Bun.env, BUN_NO_CODESIGN_MACHO_BINARY: "1" } : Bun.env;
|
|
@@ -52,6 +65,7 @@ async function main(): Promise<void> {
|
|
|
52
65
|
"bun",
|
|
53
66
|
"build",
|
|
54
67
|
"--compile",
|
|
68
|
+
...(bunTarget ? ["--target", bunTarget] : []),
|
|
55
69
|
"--no-compile-autoload-bunfig",
|
|
56
70
|
"--no-compile-autoload-dotenv",
|
|
57
71
|
"--no-compile-autoload-tsconfig",
|
|
@@ -85,7 +99,7 @@ async function main(): Promise<void> {
|
|
|
85
99
|
"./packages/coding-agent/src/extensibility/legacy-pi-ai-shim.ts",
|
|
86
100
|
"./packages/coding-agent/src/extensibility/legacy-pi-coding-agent-shim.ts",
|
|
87
101
|
"--outfile",
|
|
88
|
-
|
|
102
|
+
`packages/coding-agent/dist/${outName}`,
|
|
89
103
|
],
|
|
90
104
|
buildEnv,
|
|
91
105
|
repoRoot,
|
package/src/cli/args.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CLI argument parsing and help display
|
|
3
3
|
*/
|
|
4
|
-
import { type Effort, THINKING_EFFORTS } from "@oh-my-pi/pi-catalog/effort";
|
|
5
4
|
import { APP_NAME, CONFIG_DIR_NAME, logger } from "@oh-my-pi/pi-utils";
|
|
6
5
|
import chalk from "chalk";
|
|
7
|
-
import {
|
|
6
|
+
import { CLI_THINKING_LEVELS, type ConfiguredThinkingLevel, parseCliThinkingLevel } from "../thinking";
|
|
8
7
|
import { BUILTIN_TOOL_NAMES } from "../tools/builtin-names";
|
|
9
8
|
import {
|
|
10
9
|
OPTIONAL_FLAGS,
|
|
@@ -32,7 +31,7 @@ export interface Args {
|
|
|
32
31
|
apiKey?: string;
|
|
33
32
|
systemPrompt?: string;
|
|
34
33
|
appendSystemPrompt?: string;
|
|
35
|
-
thinking?:
|
|
34
|
+
thinking?: ConfiguredThinkingLevel;
|
|
36
35
|
hideThinking?: boolean;
|
|
37
36
|
advisor?: boolean;
|
|
38
37
|
continue?: boolean;
|
|
@@ -89,9 +88,9 @@ export interface Args {
|
|
|
89
88
|
*/
|
|
90
89
|
const PARSE_DEPS: ParseDeps = {
|
|
91
90
|
logger,
|
|
92
|
-
|
|
91
|
+
parseThinking: parseCliThinkingLevel,
|
|
93
92
|
builtinToolNames: BUILTIN_TOOL_NAMES,
|
|
94
|
-
thinkingEfforts:
|
|
93
|
+
thinkingEfforts: CLI_THINKING_LEVELS,
|
|
95
94
|
};
|
|
96
95
|
|
|
97
96
|
export function parseArgs(inputArgs: string[], extensionFlags?: Map<string, { type: "boolean" | "string" }>): Args {
|
package/src/cli/flag-tables.ts
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* real implementations at the dispatch site.
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
|
-
import type {
|
|
33
|
+
import type { ConfiguredThinkingLevel } from "../thinking";
|
|
34
34
|
import type { Args } from "./args";
|
|
35
35
|
|
|
36
36
|
/**
|
|
@@ -44,7 +44,7 @@ import type { Args } from "./args";
|
|
|
44
44
|
*/
|
|
45
45
|
export interface ParseDeps {
|
|
46
46
|
logger: { warn: (message: string, meta?: Record<string, unknown>) => void };
|
|
47
|
-
|
|
47
|
+
parseThinking: (value: string | null | undefined) => ConfiguredThinkingLevel | undefined;
|
|
48
48
|
builtinToolNames: readonly string[];
|
|
49
49
|
thinkingEfforts: readonly string[];
|
|
50
50
|
}
|
|
@@ -165,7 +165,7 @@ export const STRING_SETTERS: Record<string, StringSetter> = {
|
|
|
165
165
|
result.tools = valid;
|
|
166
166
|
},
|
|
167
167
|
"--thinking": (result, value, deps) => {
|
|
168
|
-
const thinking = deps.
|
|
168
|
+
const thinking = deps.parseThinking(value);
|
|
169
169
|
if (thinking !== undefined) {
|
|
170
170
|
result.thinking = thinking;
|
|
171
171
|
} else {
|
|
@@ -5,17 +5,14 @@ export const interactionFixtures: Record<string, GalleryFixture> = {
|
|
|
5
5
|
todo: {
|
|
6
6
|
label: "Todo",
|
|
7
7
|
streamingArgs: {
|
|
8
|
-
|
|
8
|
+
op: "init",
|
|
9
|
+
list: [{ phase: "Foundation", items: ["Scaffold crate"] }],
|
|
9
10
|
},
|
|
10
11
|
args: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
{ phase: "Foundation", items: ["Scaffold crate", "Wire workspace"] },
|
|
16
|
-
{ phase: "Auth", items: ["Port credential store", "Wire OAuth providers"] },
|
|
17
|
-
],
|
|
18
|
-
},
|
|
12
|
+
op: "init",
|
|
13
|
+
list: [
|
|
14
|
+
{ phase: "Foundation", items: ["Scaffold crate", "Wire workspace"] },
|
|
15
|
+
{ phase: "Auth", items: ["Port credential store", "Wire OAuth providers"] },
|
|
19
16
|
],
|
|
20
17
|
},
|
|
21
18
|
result: {
|
|
@@ -59,31 +59,23 @@ export const shellFixtures: Record<string, GalleryFixture> = {
|
|
|
59
59
|
eval: {
|
|
60
60
|
label: "Eval",
|
|
61
61
|
streamingArgs: {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
code: 'import json\nfrom pathlib import Path\n\ndata = json.loads(Path("package.js',
|
|
66
|
-
title: "load config",
|
|
67
|
-
},
|
|
68
|
-
],
|
|
62
|
+
language: "py",
|
|
63
|
+
code: 'import json\nfrom pathlib import Path\n\ndata = json.loads(Path("package.js',
|
|
64
|
+
title: "load config",
|
|
69
65
|
},
|
|
70
66
|
args: {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"display(sorted(deps)[:3])",
|
|
84
|
-
].join("\n"),
|
|
85
|
-
},
|
|
86
|
-
],
|
|
67
|
+
language: "py",
|
|
68
|
+
title: "load config",
|
|
69
|
+
code: [
|
|
70
|
+
"import json",
|
|
71
|
+
"from pathlib import Path",
|
|
72
|
+
"",
|
|
73
|
+
'data = json.loads(Path("package.json").read_text())',
|
|
74
|
+
'deps = data.get("dependencies", {})',
|
|
75
|
+
'print(f"{data[\\"name\\"]} v{data[\\"version\\"]}")',
|
|
76
|
+
'print(f"{len(deps)} dependencies")',
|
|
77
|
+
"display(sorted(deps)[:3])",
|
|
78
|
+
].join("\n"),
|
|
87
79
|
},
|
|
88
80
|
result: {
|
|
89
81
|
content: [
|
|
@@ -8,8 +8,10 @@ import { FileSessionStorage } from "../session/session-storage";
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Show the TUI session selector and return the selected session, or null if
|
|
11
|
-
* cancelled.
|
|
12
|
-
*
|
|
11
|
+
* cancelled. Rendered as a fullscreen overlay on the terminal's alternate
|
|
12
|
+
* screen, so the list scrolls and rows are clickable with the mouse. Tab
|
|
13
|
+
* toggles between current-folder and all-projects scope; the all-projects list
|
|
14
|
+
* is loaded lazily via `SessionManager.listAll`.
|
|
13
15
|
*/
|
|
14
16
|
export async function selectSession(
|
|
15
17
|
sessions: SessionInfo[],
|
|
@@ -65,6 +67,7 @@ export async function selectSession(
|
|
|
65
67
|
loadAllSessions: () => SessionManager.listAll(storage),
|
|
66
68
|
allSessions: options?.allSessions,
|
|
67
69
|
getTerminalRows: () => ui.terminal.rows,
|
|
70
|
+
fillHeight: true,
|
|
68
71
|
},
|
|
69
72
|
);
|
|
70
73
|
return selector;
|
|
@@ -72,7 +75,18 @@ export async function selectSession(
|
|
|
72
75
|
|
|
73
76
|
const selector = showSelector();
|
|
74
77
|
selector.setOnRequestRender(() => ui.requestRender());
|
|
75
|
-
|
|
78
|
+
// Present as a fullscreen overlay so the picker borrows the terminal's
|
|
79
|
+
// alternate screen buffer (vim/less idiom): the list scrolls and rows are
|
|
80
|
+
// clickable via the mouse tracking the overlay enables for its lifetime.
|
|
81
|
+
// Anchored top-left at full size so a mouse row maps directly to a rendered
|
|
82
|
+
// line (the overlay paints from screen row 0).
|
|
83
|
+
ui.showOverlay(selector, {
|
|
84
|
+
anchor: "top-left",
|
|
85
|
+
width: "100%",
|
|
86
|
+
maxHeight: "100%",
|
|
87
|
+
margin: 0,
|
|
88
|
+
fullscreen: true,
|
|
89
|
+
});
|
|
76
90
|
ui.setFocus(selector);
|
|
77
91
|
ui.start();
|
|
78
92
|
return promise;
|
package/src/commands/launch.ts
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Root command for the coding agent CLI.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { THINKING_EFFORTS } from "@oh-my-pi/pi-catalog/effort";
|
|
6
5
|
import { APP_NAME } from "@oh-my-pi/pi-utils";
|
|
7
6
|
import { Args, Command, Flags } from "@oh-my-pi/pi-utils/cli";
|
|
8
7
|
import { parseArgs } from "../cli/args";
|
|
9
8
|
import { runRootCommand } from "../main";
|
|
10
9
|
import { prepareAcpTerminalAuthArgs } from "../modes/acp/terminal-auth";
|
|
10
|
+
import { CLI_THINKING_LEVELS } from "../thinking";
|
|
11
11
|
|
|
12
12
|
export default class Index extends Command {
|
|
13
13
|
static description = "AI coding assistant";
|
|
@@ -100,8 +100,8 @@ export default class Index extends Command {
|
|
|
100
100
|
description: "Comma-separated list of tools to enable (default: all)",
|
|
101
101
|
}),
|
|
102
102
|
thinking: Flags.string({
|
|
103
|
-
description: `Set thinking level: ${
|
|
104
|
-
options: [...
|
|
103
|
+
description: `Set thinking level: ${CLI_THINKING_LEVELS.join(", ")}`,
|
|
104
|
+
options: [...CLI_THINKING_LEVELS],
|
|
105
105
|
}),
|
|
106
106
|
"hide-thinking": Flags.boolean({
|
|
107
107
|
description: "Hide thinking blocks in TUI output (display only, does not disable model thinking)",
|
|
@@ -3572,7 +3572,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
3572
3572
|
group: "Discovery & MCP",
|
|
3573
3573
|
label: "Essential Tools Override",
|
|
3574
3574
|
description:
|
|
3575
|
-
"Override the always-loaded built-in tools (default: read, bash, edit). Leave empty to use defaults.",
|
|
3575
|
+
"Override the always-loaded built-in tools (default: read, bash, edit, write, find, eval). Leave empty to use defaults.",
|
|
3576
3576
|
},
|
|
3577
3577
|
},
|
|
3578
3578
|
|
|
@@ -4479,6 +4479,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
4479
4479
|
},
|
|
4480
4480
|
},
|
|
4481
4481
|
|
|
4482
|
+
"exa.searchDelayMs": {
|
|
4483
|
+
type: "number",
|
|
4484
|
+
default: 1_000,
|
|
4485
|
+
ui: {
|
|
4486
|
+
tab: "providers",
|
|
4487
|
+
group: "Services",
|
|
4488
|
+
label: "Exa Search Delay",
|
|
4489
|
+
description: "Minimum delay between Exa web search requests in milliseconds; set 0 to disable pacing",
|
|
4490
|
+
},
|
|
4491
|
+
},
|
|
4492
|
+
|
|
4482
4493
|
"exa.enableResearcher": {
|
|
4483
4494
|
type: "boolean",
|
|
4484
4495
|
default: false,
|
|
@@ -4785,6 +4796,7 @@ export interface TtsrSettings {
|
|
|
4785
4796
|
export interface ExaSettings {
|
|
4786
4797
|
enabled: boolean;
|
|
4787
4798
|
enableSearch: boolean;
|
|
4799
|
+
searchDelayMs: number;
|
|
4788
4800
|
enableResearcher: boolean;
|
|
4789
4801
|
enableWebsets: boolean;
|
|
4790
4802
|
}
|
package/src/edit/renderer.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
invalidateRenderedStringCache,
|
|
23
23
|
type LspBatchRequest,
|
|
24
24
|
PREVIEW_LIMITS,
|
|
25
|
+
previewWindowRows,
|
|
25
26
|
type RenderedStringCache,
|
|
26
27
|
replaceTabs,
|
|
27
28
|
shortenPath,
|
|
@@ -340,6 +341,7 @@ function renderPlainTextPreview(text: string, uiTheme: Theme, _filePath?: string
|
|
|
340
341
|
function formatStreamingDiff(
|
|
341
342
|
diff: string,
|
|
342
343
|
rawPath: string,
|
|
344
|
+
width: number,
|
|
343
345
|
uiTheme: Theme,
|
|
344
346
|
expanded: boolean,
|
|
345
347
|
label = "streaming",
|
|
@@ -347,15 +349,32 @@ function formatStreamingDiff(
|
|
|
347
349
|
cache?: RenderedStringCache,
|
|
348
350
|
): string {
|
|
349
351
|
if (!diff) return "";
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
352
|
+
// Clamp the collapsed tail to the viewport so a tall or fast-growing diff
|
|
353
|
+
// cannot outgrow the live window. Otherwise its mutating tail scrolls above
|
|
354
|
+
// the native-scrollback commit boundary and the engine re-commits a fresh
|
|
355
|
+
// snapshot every streamed frame, stacking duplicate "… more lines above"
|
|
356
|
+
// previews in history. The budget is VISUAL rows (a long wrapped line counts
|
|
357
|
+
// for more than one) at the framed block's inner width (border only —
|
|
358
|
+
// contentPaddingLeft is 0); only the visible suffix is syntax-colored, so the
|
|
359
|
+
// cheap raw-line wrap walk keeps the per-chunk cost bounded. innerWidth/budget
|
|
360
|
+
// are in the cache salt so a resize re-slices.
|
|
361
|
+
const innerWidth = Math.max(1, width - 2);
|
|
362
|
+
const budget = expanded ? Number.POSITIVE_INFINITY : Math.min(EDIT_STREAMING_PREVIEW_LINES, previewWindowRows());
|
|
363
|
+
let text = cachedRenderedString(cache, uiTheme, expanded, `${rawPath}:${innerWidth}:${budget}`, diff, () => {
|
|
364
|
+
// "Cursor" tail window: pin the last rows to the bottom so freshly streamed
|
|
365
|
+
// changes stay on screen. The whole-file diff is recomputed every chunk and
|
|
366
|
+
// its Myers alignment is not monotonic in payload length, so a hunk-aware
|
|
367
|
+
// window stutters as rows move between hunks. Expanded lifts the cap.
|
|
357
368
|
const allLines = diff.replace(/\n+$/u, "").split("\n");
|
|
358
|
-
|
|
369
|
+
let visualUsed = 0;
|
|
370
|
+
let cut = allLines.length;
|
|
371
|
+
for (let i = allLines.length - 1; i >= 0; i--) {
|
|
372
|
+
const lineRows = Math.max(1, wrapTextWithAnsi(replaceTabs(allLines[i]!), innerWidth).length);
|
|
373
|
+
if (visualUsed + lineRows > budget && visualUsed > 0) break;
|
|
374
|
+
visualUsed += lineRows;
|
|
375
|
+
cut = i;
|
|
376
|
+
}
|
|
377
|
+
const hiddenLines = cut;
|
|
359
378
|
const visible = hiddenLines > 0 ? allLines.slice(hiddenLines) : allLines;
|
|
360
379
|
let rendered = "\n\n";
|
|
361
380
|
if (hiddenLines > 0) {
|
|
@@ -384,6 +403,7 @@ function formatStreamingDiff(
|
|
|
384
403
|
|
|
385
404
|
function formatMultiFileStreamingDiff(
|
|
386
405
|
previews: PerFileDiffPreview[],
|
|
406
|
+
width: number,
|
|
387
407
|
uiTheme: Theme,
|
|
388
408
|
expanded: boolean,
|
|
389
409
|
spinnerFrame?: number,
|
|
@@ -405,7 +425,7 @@ function formatMultiFileStreamingDiff(
|
|
|
405
425
|
const isLast = index === previews.length - 1;
|
|
406
426
|
const cache = previewCacheAt(caches, index);
|
|
407
427
|
parts.push(
|
|
408
|
-
`${header}${formatStreamingDiff(preview.diff, preview.path, uiTheme, expanded, "preview", isLast ? spinnerFrame : undefined, cache)}`,
|
|
428
|
+
`${header}${formatStreamingDiff(preview.diff, preview.path, width, uiTheme, expanded, "preview", isLast ? spinnerFrame : undefined, cache)}`,
|
|
409
429
|
);
|
|
410
430
|
}
|
|
411
431
|
}
|
|
@@ -415,6 +435,7 @@ function formatMultiFileStreamingDiff(
|
|
|
415
435
|
function getCallPreview(
|
|
416
436
|
args: EditRenderArgs,
|
|
417
437
|
rawPath: string,
|
|
438
|
+
width: number,
|
|
418
439
|
uiTheme: Theme,
|
|
419
440
|
renderContext: EditRenderContext | undefined,
|
|
420
441
|
expanded: boolean,
|
|
@@ -423,14 +444,14 @@ function getCallPreview(
|
|
|
423
444
|
): string {
|
|
424
445
|
const multi = renderContext?.perFileDiffPreview;
|
|
425
446
|
if (multi && multi.length > 1 && multi.some(p => p.diff || p.error)) {
|
|
426
|
-
return formatMultiFileStreamingDiff(multi, uiTheme, expanded, spinnerFrame, caches);
|
|
447
|
+
return formatMultiFileStreamingDiff(multi, width, uiTheme, expanded, spinnerFrame, caches);
|
|
427
448
|
}
|
|
428
449
|
const cache = previewCacheAt(caches, 0);
|
|
429
450
|
if (args.previewDiff) {
|
|
430
|
-
return formatStreamingDiff(args.previewDiff, rawPath, uiTheme, expanded, "preview", spinnerFrame, cache);
|
|
451
|
+
return formatStreamingDiff(args.previewDiff, rawPath, width, uiTheme, expanded, "preview", spinnerFrame, cache);
|
|
431
452
|
}
|
|
432
453
|
if (args.diff && args.op) {
|
|
433
|
-
return formatStreamingDiff(args.diff, rawPath, uiTheme, expanded, "streaming", spinnerFrame, cache);
|
|
454
|
+
return formatStreamingDiff(args.diff, rawPath, width, uiTheme, expanded, "streaming", spinnerFrame, cache);
|
|
434
455
|
}
|
|
435
456
|
if (args.diff) {
|
|
436
457
|
return renderPlainTextPreview(args.diff, uiTheme, rawPath);
|
|
@@ -628,6 +649,7 @@ export const editToolRenderer = {
|
|
|
628
649
|
let body = getCallPreview(
|
|
629
650
|
editArgs,
|
|
630
651
|
rawPath,
|
|
652
|
+
width,
|
|
631
653
|
uiTheme,
|
|
632
654
|
renderContext,
|
|
633
655
|
options.expanded,
|