@oh-my-pi/pi-coding-agent 15.3.2 → 15.4.2
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 +110 -0
- package/dist/types/cli/file-processor.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +45 -3
- package/dist/types/config/settings.d.ts +1 -1
- package/dist/types/debug/raw-sse.d.ts +2 -0
- package/dist/types/edit/file-read-cache.d.ts +15 -4
- package/dist/types/edit/index.d.ts +3 -8
- package/dist/types/edit/renderer.d.ts +1 -2
- package/dist/types/eval/__tests__/shared-executors.test.d.ts +1 -0
- package/dist/types/eval/js/shared/local-module-loader.d.ts +16 -0
- package/dist/types/eval/js/shared/rewrite-imports.d.ts +4 -0
- package/dist/types/eval/js/shared/runtime.d.ts +14 -8
- package/dist/types/eval/py/executor.d.ts +1 -2
- package/dist/types/eval/py/kernel.d.ts +6 -0
- package/dist/types/eval/py/tool-bridge.d.ts +1 -5
- package/dist/types/eval/session-id.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +1 -3
- package/dist/types/hashline/anchors.d.ts +15 -9
- package/dist/types/hashline/constants.d.ts +0 -2
- package/dist/types/hashline/diff.d.ts +1 -2
- package/dist/types/hashline/executor.d.ts +52 -0
- package/dist/types/hashline/hash.d.ts +44 -93
- package/dist/types/hashline/index.d.ts +2 -1
- package/dist/types/hashline/input.d.ts +2 -9
- package/dist/types/hashline/recovery.d.ts +3 -9
- package/dist/types/hashline/tokenizer.d.ts +91 -0
- package/dist/types/hashline/types.d.ts +5 -7
- package/dist/types/modes/components/extensions/types.d.ts +0 -4
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +11 -15
- package/dist/types/session/agent-storage.d.ts +11 -10
- package/dist/types/slash-commands/acp-builtins.d.ts +3 -3
- package/dist/types/slash-commands/types.d.ts +0 -5
- package/dist/types/task/executor.d.ts +2 -0
- package/dist/types/tool-discovery/tool-index.d.ts +0 -50
- package/dist/types/tools/index.d.ts +2 -8
- package/dist/types/tools/match-line-format.d.ts +4 -4
- package/dist/types/tools/output-schema-validator.d.ts +64 -0
- package/dist/types/tools/review.d.ts +13 -0
- package/dist/types/tools/search-tool-bm25.d.ts +1 -1
- package/dist/types/tools/search.d.ts +4 -3
- package/dist/types/utils/edit-mode.d.ts +1 -1
- package/dist/types/web/kagi.d.ts +4 -2
- package/dist/types/web/parallel.d.ts +4 -3
- package/dist/types/web/scrapers/types.d.ts +2 -1
- package/dist/types/web/search/index.d.ts +12 -4
- package/dist/types/web/search/provider.d.ts +2 -1
- package/dist/types/web/search/providers/anthropic.d.ts +9 -4
- package/dist/types/web/search/providers/base.d.ts +34 -2
- package/dist/types/web/search/providers/brave.d.ts +8 -1
- package/dist/types/web/search/providers/codex.d.ts +13 -9
- package/dist/types/web/search/providers/exa.d.ts +10 -1
- package/dist/types/web/search/providers/gemini.d.ts +20 -23
- package/dist/types/web/search/providers/jina.d.ts +2 -1
- package/dist/types/web/search/providers/kagi.d.ts +4 -1
- package/dist/types/web/search/providers/kimi.d.ts +10 -1
- package/dist/types/web/search/providers/parallel.d.ts +3 -2
- package/dist/types/web/search/providers/perplexity.d.ts +5 -2
- package/dist/types/web/search/providers/searxng.d.ts +2 -1
- package/dist/types/web/search/providers/synthetic.d.ts +5 -8
- package/dist/types/web/search/providers/tavily.d.ts +11 -4
- package/dist/types/web/search/providers/utils.d.ts +8 -6
- package/dist/types/web/search/providers/zai.d.ts +12 -3
- package/package.json +7 -7
- package/src/cli/file-processor.ts +12 -2
- package/src/cli.ts +0 -8
- package/src/commands/commit.ts +8 -8
- package/src/config/prompt-templates.ts +6 -6
- package/src/config/settings-schema.ts +47 -3
- package/src/config/settings.ts +5 -5
- package/src/debug/raw-sse.ts +68 -3
- package/src/edit/file-read-cache.ts +68 -25
- package/src/edit/index.ts +6 -37
- package/src/edit/renderer.ts +9 -47
- package/src/edit/streaming.ts +43 -56
- package/src/eval/__tests__/shared-executors.test.ts +520 -0
- package/src/eval/js/context-manager.ts +64 -53
- package/src/eval/js/shared/local-module-loader.ts +265 -0
- package/src/eval/js/shared/prelude.txt +4 -0
- package/src/eval/js/shared/rewrite-imports.ts +85 -0
- package/src/eval/js/shared/runtime.ts +129 -86
- package/src/eval/js/worker-core.ts +23 -38
- package/src/eval/py/executor.ts +155 -84
- package/src/eval/py/kernel.ts +10 -1
- package/src/eval/py/prelude.py +22 -24
- package/src/eval/py/runner.py +203 -85
- package/src/eval/py/tool-bridge.ts +17 -10
- package/src/eval/session-id.ts +8 -0
- package/src/exec/bash-executor.ts +27 -16
- package/src/extensibility/extensions/runner.ts +0 -1
- package/src/extensibility/extensions/types.ts +1 -3
- package/src/hashline/anchors.ts +56 -65
- package/src/hashline/apply.ts +29 -31
- package/src/hashline/constants.ts +0 -3
- package/src/hashline/diff-preview.ts +4 -5
- package/src/hashline/diff.ts +30 -4
- package/src/hashline/execute.ts +91 -26
- package/src/hashline/executor.ts +239 -0
- package/src/hashline/grammar.lark +12 -10
- package/src/hashline/hash.ts +69 -114
- package/src/hashline/index.ts +2 -1
- package/src/hashline/input.ts +48 -41
- package/src/hashline/prefixes.ts +21 -11
- package/src/hashline/recovery.ts +63 -71
- package/src/hashline/stream.ts +2 -2
- package/src/hashline/tokenizer.ts +467 -0
- package/src/hashline/types.ts +6 -8
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/modes/components/extensions/types.ts +0 -5
- package/src/modes/components/session-observer-overlay.ts +11 -2
- package/src/modes/components/settings-selector.ts +10 -1
- package/src/modes/components/tree-selector.ts +10 -2
- package/src/modes/controllers/command-controller.ts +1 -3
- package/src/modes/controllers/extension-ui-controller.ts +10 -11
- package/src/modes/controllers/selector-controller.ts +5 -5
- package/src/modes/theme/theme.ts +4 -2
- package/src/modes/types.ts +4 -1
- package/src/modes/utils/ui-helpers.ts +4 -0
- package/src/prompts/agents/explore.md +1 -1
- package/src/prompts/tools/ast-edit.md +1 -1
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/eval.md +1 -1
- package/src/prompts/tools/hashline.md +73 -94
- package/src/prompts/tools/read.md +4 -4
- package/src/prompts/tools/search.md +3 -3
- package/src/sdk.ts +33 -26
- package/src/session/agent-session.ts +59 -66
- package/src/session/agent-storage.ts +13 -14
- package/src/slash-commands/acp-builtins.ts +3 -3
- package/src/slash-commands/types.ts +0 -6
- package/src/task/executor.ts +26 -57
- package/src/task/index.ts +8 -4
- package/src/tool-discovery/tool-index.ts +0 -134
- package/src/tools/ast-edit.ts +36 -13
- package/src/tools/ast-grep.ts +45 -4
- package/src/tools/browser/tab-worker.ts +3 -2
- package/src/tools/eval.ts +2 -1
- package/src/tools/fetch.ts +23 -14
- package/src/tools/index.ts +2 -8
- package/src/tools/irc.ts +59 -5
- package/src/tools/match-line-format.ts +5 -7
- package/src/tools/output-schema-validator.ts +132 -0
- package/src/tools/read.ts +142 -31
- package/src/tools/review.ts +23 -0
- package/src/tools/search-tool-bm25.ts +3 -30
- package/src/tools/search.ts +48 -16
- package/src/tools/write.ts +3 -3
- package/src/tools/yield.ts +32 -41
- package/src/utils/edit-mode.ts +1 -2
- package/src/utils/file-mentions.ts +2 -2
- package/src/web/kagi.ts +15 -6
- package/src/web/parallel.ts +9 -6
- package/src/web/scrapers/types.ts +7 -1
- package/src/web/scrapers/youtube.ts +13 -7
- package/src/web/search/index.ts +37 -11
- package/src/web/search/provider.ts +5 -3
- package/src/web/search/providers/anthropic.ts +30 -21
- package/src/web/search/providers/base.ts +35 -2
- package/src/web/search/providers/brave.ts +4 -4
- package/src/web/search/providers/codex.ts +118 -89
- package/src/web/search/providers/exa.ts +3 -2
- package/src/web/search/providers/gemini.ts +58 -155
- package/src/web/search/providers/jina.ts +4 -4
- package/src/web/search/providers/kagi.ts +17 -11
- package/src/web/search/providers/kimi.ts +29 -13
- package/src/web/search/providers/parallel.ts +171 -23
- package/src/web/search/providers/perplexity.ts +38 -37
- package/src/web/search/providers/searxng.ts +3 -1
- package/src/web/search/providers/synthetic.ts +16 -19
- package/src/web/search/providers/tavily.ts +23 -18
- package/src/web/search/providers/utils.ts +11 -17
- package/src/web/search/providers/zai.ts +16 -8
- package/dist/types/hashline/parser.d.ts +0 -7
- package/dist/types/mcp/discoverable-tool-metadata.d.ts +0 -7
- package/dist/types/tools/vim.d.ts +0 -58
- package/dist/types/vim/buffer.d.ts +0 -41
- package/dist/types/vim/commands.d.ts +0 -6
- package/dist/types/vim/engine.d.ts +0 -47
- package/dist/types/vim/parser.d.ts +0 -3
- package/dist/types/vim/render.d.ts +0 -25
- package/dist/types/vim/types.d.ts +0 -182
- package/src/hashline/parser.ts +0 -246
- package/src/mcp/discoverable-tool-metadata.ts +0 -24
- package/src/prompts/tools/vim.md +0 -98
- package/src/tools/vim.ts +0 -949
- package/src/vim/buffer.ts +0 -309
- package/src/vim/commands.ts +0 -382
- package/src/vim/engine.ts +0 -2409
- package/src/vim/parser.ts +0 -134
- package/src/vim/render.ts +0 -252
- package/src/vim/types.ts +0 -197
package/src/task/executor.ts
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import type { AgentEvent, AgentIdentity, AgentTelemetryConfig, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
9
9
|
import { recordHandoff, resolveTelemetry } from "@oh-my-pi/pi-agent-core";
|
|
10
|
-
import { type JsonSchemaValidationIssue, validateJsonSchemaValue } from "@oh-my-pi/pi-ai/utils/schema";
|
|
11
10
|
import { logger, prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
12
11
|
import { ModelRegistry } from "../config/model-registry";
|
|
13
12
|
import { resolveModelOverrideWithAuthFallback } from "../config/model-resolver";
|
|
@@ -33,7 +32,10 @@ import { SKILL_PROMPT_MESSAGE_TYPE } from "../session/messages";
|
|
|
33
32
|
import { SessionManager } from "../session/session-manager";
|
|
34
33
|
import { truncateTail } from "../session/streaming-output";
|
|
35
34
|
import type { ContextFileEntry } from "../tools";
|
|
36
|
-
import {
|
|
35
|
+
import { normalizeSchema } from "../tools/jtd-to-json-schema";
|
|
36
|
+
import { buildOutputValidator, summarizeValidationFailure } from "../tools/output-schema-validator";
|
|
37
|
+
|
|
38
|
+
import { type ReportFindingDetails, toReviewFinding } from "../tools/review";
|
|
37
39
|
import { ToolAbortError } from "../tools/tool-errors";
|
|
38
40
|
import type { EventBus } from "../utils/event-bus";
|
|
39
41
|
import { buildNamedToolChoice } from "../utils/tool-choice";
|
|
@@ -183,6 +185,8 @@ export interface ExecutorOptions {
|
|
|
183
185
|
*/
|
|
184
186
|
parentArtifactManager?: ArtifactManager;
|
|
185
187
|
parentHindsightSessionState?: HindsightSessionState;
|
|
188
|
+
/** Parent agent's eval executor session id. Subagents reuse it so eval state is shared. */
|
|
189
|
+
parentEvalSessionId?: string;
|
|
186
190
|
/**
|
|
187
191
|
* Parent agent's OpenTelemetry configuration. When defined, the subagent's
|
|
188
192
|
* loop is started with the same tracer/hooks but its own agent identity
|
|
@@ -208,51 +212,6 @@ function parseStringifiedJson(value: unknown): unknown {
|
|
|
208
212
|
}
|
|
209
213
|
}
|
|
210
214
|
|
|
211
|
-
interface OutputValidator {
|
|
212
|
-
validate: (value: unknown) => { ok: true } | { ok: false; message: string; missingRequired: string[] };
|
|
213
|
-
requiredFields: string[];
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function buildOutputValidator(schema: unknown): { validator?: OutputValidator; error?: string } {
|
|
217
|
-
const { normalized, error } = normalizeSchema(schema);
|
|
218
|
-
if (error) return { error };
|
|
219
|
-
if (normalized === undefined) return {};
|
|
220
|
-
const jsonSchema = jtdToJsonSchema(normalized);
|
|
221
|
-
const required = extractRequiredFields(jsonSchema);
|
|
222
|
-
return {
|
|
223
|
-
validator: {
|
|
224
|
-
requiredFields: required,
|
|
225
|
-
validate: value => {
|
|
226
|
-
const result = validateJsonSchemaValue(jsonSchema, value);
|
|
227
|
-
if (result.success) return { ok: true };
|
|
228
|
-
const missing = computeMissingRequired(required, value);
|
|
229
|
-
const message = formatValidationIssue(result.issues[0]) ?? "schema validation failed";
|
|
230
|
-
return { ok: false, message, missingRequired: missing };
|
|
231
|
-
},
|
|
232
|
-
},
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
function extractRequiredFields(jsonSchema: unknown): string[] {
|
|
237
|
-
if (!jsonSchema || typeof jsonSchema !== "object") return [];
|
|
238
|
-
const required = (jsonSchema as { required?: unknown }).required;
|
|
239
|
-
return Array.isArray(required) ? required.filter((k): k is string => typeof k === "string") : [];
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function computeMissingRequired(required: readonly string[], value: unknown): string[] {
|
|
243
|
-
if (required.length === 0) return [];
|
|
244
|
-
if (value === null || value === undefined) return [...required];
|
|
245
|
-
if (typeof value !== "object" || Array.isArray(value)) return [];
|
|
246
|
-
const record = value as Record<string, unknown>;
|
|
247
|
-
return required.filter(key => !(key in record) || record[key] === undefined);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function formatValidationIssue(issue: JsonSchemaValidationIssue | undefined): string | undefined {
|
|
251
|
-
if (!issue) return undefined;
|
|
252
|
-
const path = issue.path.length > 0 ? issue.path.map(String).join(".") : "(root)";
|
|
253
|
-
return `${path}: ${issue.message}`;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
215
|
function previewOffendingData(value: unknown, maxLength = 500): string {
|
|
257
216
|
let serialized: string;
|
|
258
217
|
try {
|
|
@@ -306,7 +265,7 @@ function resolveFallbackCompletion(rawOutput: string, outputSchema: unknown): {
|
|
|
306
265
|
if (candidate === undefined) return null;
|
|
307
266
|
const { validator, error } = buildOutputValidator(outputSchema);
|
|
308
267
|
if (error) return null;
|
|
309
|
-
if (validator && !validator.validate(candidate).
|
|
268
|
+
if (validator && !validator.validate(candidate).success) return null;
|
|
310
269
|
return { data: candidate };
|
|
311
270
|
}
|
|
312
271
|
|
|
@@ -393,9 +352,10 @@ export function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): Fi
|
|
|
393
352
|
stderr = `schema_violation: invalid output schema: ${schemaError}`;
|
|
394
353
|
exitCode = 1;
|
|
395
354
|
} else {
|
|
396
|
-
const
|
|
397
|
-
if (!
|
|
398
|
-
const
|
|
355
|
+
const result = validator?.validate(completeData) ?? { success: true as const };
|
|
356
|
+
if (!result.success) {
|
|
357
|
+
const summary = summarizeValidationFailure(result, completeData, validator?.requiredFields ?? []);
|
|
358
|
+
const outcome = buildSchemaViolationOutcome(summary, completeData);
|
|
399
359
|
rawOutput = outcome.rawOutput;
|
|
400
360
|
stderr = outcome.stderr;
|
|
401
361
|
exitCode = outcome.exitCode;
|
|
@@ -420,9 +380,10 @@ export function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): Fi
|
|
|
420
380
|
if (fallback) {
|
|
421
381
|
const completeData = normalizeCompleteData(fallback.data, reportFindings);
|
|
422
382
|
const { validator } = buildOutputValidator(outputSchema);
|
|
423
|
-
const
|
|
424
|
-
if (!
|
|
425
|
-
const
|
|
383
|
+
const result = validator?.validate(completeData) ?? { success: true as const };
|
|
384
|
+
if (!result.success) {
|
|
385
|
+
const summary = summarizeValidationFailure(result, completeData, validator?.requiredFields ?? []);
|
|
386
|
+
const outcome = buildSchemaViolationOutcome(summary, completeData);
|
|
426
387
|
rawOutput = outcome.rawOutput;
|
|
427
388
|
stderr = outcome.stderr;
|
|
428
389
|
exitCode = outcome.exitCode;
|
|
@@ -1277,6 +1238,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1277
1238
|
customTools: mcpProxyTools.length > 0 ? mcpProxyTools : undefined,
|
|
1278
1239
|
localProtocolOptions: options.localProtocolOptions,
|
|
1279
1240
|
telemetry: subagentTelemetry,
|
|
1241
|
+
parentEvalSessionId: options.parentEvalSessionId,
|
|
1280
1242
|
}),
|
|
1281
1243
|
);
|
|
1282
1244
|
|
|
@@ -1324,22 +1286,25 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1324
1286
|
}
|
|
1325
1287
|
|
|
1326
1288
|
const extensionRunner = session.extensionRunner;
|
|
1289
|
+
const pendingExtensionMessages: Promise<void>[] = [];
|
|
1327
1290
|
if (extensionRunner) {
|
|
1328
1291
|
extensionRunner.initialize(
|
|
1329
1292
|
{
|
|
1330
1293
|
sendMessage: (message, options) => {
|
|
1331
|
-
session.sendCustomMessage(message, options).catch(e => {
|
|
1294
|
+
const sendPromise = session.sendCustomMessage(message, options).catch(e => {
|
|
1332
1295
|
logger.error("Extension sendMessage failed", {
|
|
1333
1296
|
error: e instanceof Error ? e.message : String(e),
|
|
1334
1297
|
});
|
|
1335
1298
|
});
|
|
1299
|
+
pendingExtensionMessages.push(sendPromise);
|
|
1336
1300
|
},
|
|
1337
1301
|
sendUserMessage: (content, options) => {
|
|
1338
|
-
session.sendUserMessage(content, options).catch(e => {
|
|
1302
|
+
const sendPromise = session.sendUserMessage(content, options).catch(e => {
|
|
1339
1303
|
logger.error("Extension sendUserMessage failed", {
|
|
1340
1304
|
error: e instanceof Error ? e.message : String(e),
|
|
1341
1305
|
});
|
|
1342
1306
|
});
|
|
1307
|
+
pendingExtensionMessages.push(sendPromise);
|
|
1343
1308
|
},
|
|
1344
1309
|
appendEntry: (customType, data) => {
|
|
1345
1310
|
session.sessionManager.appendCustomEntry(customType, data);
|
|
@@ -1375,6 +1340,9 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1375
1340
|
logger.error("Extension error", { path: err.extensionPath, error: err.error });
|
|
1376
1341
|
});
|
|
1377
1342
|
await awaitAbortable(extensionRunner.emit({ type: "session_start" }));
|
|
1343
|
+
while (pendingExtensionMessages.length > 0) {
|
|
1344
|
+
await awaitAbortable(Promise.all(pendingExtensionMessages.splice(0)));
|
|
1345
|
+
}
|
|
1378
1346
|
}
|
|
1379
1347
|
|
|
1380
1348
|
const MAX_YIELD_RETRIES = 3;
|
|
@@ -1547,7 +1515,8 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1547
1515
|
// Use final output if available, otherwise accumulated output
|
|
1548
1516
|
let rawOutput = finalOutputChunks.length > 0 ? finalOutputChunks.join("") : outputChunks.join("");
|
|
1549
1517
|
const yieldItems = progress.extractedToolData?.yield as YieldItem[] | undefined;
|
|
1550
|
-
const
|
|
1518
|
+
const reportFindingDetails = progress.extractedToolData?.report_finding as ReportFindingDetails[] | undefined;
|
|
1519
|
+
const reportFindings: ReviewFinding[] | undefined = reportFindingDetails?.map(toReviewFinding);
|
|
1551
1520
|
const finalized = finalizeSubprocessOutput({
|
|
1552
1521
|
rawOutput,
|
|
1553
1522
|
exitCode,
|
package/src/task/index.ts
CHANGED
|
@@ -558,6 +558,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
558
558
|
const commitStyle = this.session.settings.get("task.isolation.commits");
|
|
559
559
|
const maxConcurrency = this.session.settings.get("task.maxConcurrency");
|
|
560
560
|
const taskDepth = this.session.taskDepth ?? 0;
|
|
561
|
+
const subagentLspEnabled = (this.session.enableLsp ?? true) && this.session.settings.get("task.enableLsp");
|
|
561
562
|
|
|
562
563
|
if (isolationMode === "none" && "isolated" in params) {
|
|
563
564
|
return {
|
|
@@ -843,6 +844,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
843
844
|
file => path.basename(file.path).toLowerCase() !== "agents.md",
|
|
844
845
|
);
|
|
845
846
|
const promptTemplates = this.session.promptTemplates;
|
|
847
|
+
const parentEvalSessionId = this.session.getEvalSessionId?.() ?? undefined;
|
|
846
848
|
|
|
847
849
|
// Initialize progress for all tasks
|
|
848
850
|
for (let i = 0; i < tasksWithUniqueIds.length; i++) {
|
|
@@ -872,7 +874,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
872
874
|
if (!isIsolated) {
|
|
873
875
|
return runSubprocess({
|
|
874
876
|
cwd: this.session.cwd,
|
|
875
|
-
agent,
|
|
877
|
+
agent: effectiveAgent,
|
|
876
878
|
task: renderSubagentUserPrompt(task.assignment, simpleMode),
|
|
877
879
|
assignment: task.assignment.trim(),
|
|
878
880
|
context: sharedContext,
|
|
@@ -888,7 +890,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
888
890
|
persistArtifacts: !!artifactsDir,
|
|
889
891
|
artifactsDir: effectiveArtifactsDir,
|
|
890
892
|
contextFile: contextFilePath,
|
|
891
|
-
enableLsp:
|
|
893
|
+
enableLsp: subagentLspEnabled,
|
|
892
894
|
signal,
|
|
893
895
|
eventBus: this.session.eventBus,
|
|
894
896
|
onProgress: progress => {
|
|
@@ -910,6 +912,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
910
912
|
parentArtifactManager,
|
|
911
913
|
parentHindsightSessionState: this.session.getHindsightSessionState?.(),
|
|
912
914
|
parentTelemetry: this.session.getTelemetry?.(),
|
|
915
|
+
parentEvalSessionId,
|
|
913
916
|
});
|
|
914
917
|
}
|
|
915
918
|
|
|
@@ -927,7 +930,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
927
930
|
const result = await runSubprocess({
|
|
928
931
|
cwd: this.session.cwd,
|
|
929
932
|
worktree: isolationDir,
|
|
930
|
-
agent,
|
|
933
|
+
agent: effectiveAgent,
|
|
931
934
|
task: renderSubagentUserPrompt(task.assignment, simpleMode),
|
|
932
935
|
assignment: task.assignment.trim(),
|
|
933
936
|
context: sharedContext,
|
|
@@ -943,7 +946,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
943
946
|
persistArtifacts: !!artifactsDir,
|
|
944
947
|
artifactsDir: effectiveArtifactsDir,
|
|
945
948
|
contextFile: contextFilePath,
|
|
946
|
-
enableLsp:
|
|
949
|
+
enableLsp: subagentLspEnabled,
|
|
947
950
|
signal,
|
|
948
951
|
eventBus: this.session.eventBus,
|
|
949
952
|
onProgress: progress => {
|
|
@@ -965,6 +968,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
965
968
|
parentArtifactManager,
|
|
966
969
|
parentHindsightSessionState: this.session.getHindsightSessionState?.(),
|
|
967
970
|
parentTelemetry: this.session.getTelemetry?.(),
|
|
971
|
+
parentEvalSessionId,
|
|
968
972
|
});
|
|
969
973
|
if (mergeMode === "branch" && result.exitCode === 0) {
|
|
970
974
|
try {
|
|
@@ -44,47 +44,6 @@ export interface DiscoverableToolSearchResult {
|
|
|
44
44
|
score: number;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
// ─── Legacy MCP-typed aliases (back-compat) ──────────────────────────────────
|
|
48
|
-
|
|
49
|
-
/** @deprecated Use DiscoverableTool with source === "mcp" */
|
|
50
|
-
export type DiscoverableMCPTool = Pick<
|
|
51
|
-
DiscoverableTool,
|
|
52
|
-
"name" | "label" | "schemaKeys" | "serverName" | "mcpToolName"
|
|
53
|
-
> & { description: string };
|
|
54
|
-
|
|
55
|
-
/** @deprecated Use DiscoverableToolServerSummary */
|
|
56
|
-
export type DiscoverableMCPToolServerSummary = DiscoverableToolServerSummary;
|
|
57
|
-
|
|
58
|
-
/** @deprecated Use DiscoverableToolSummary */
|
|
59
|
-
export type DiscoverableMCPToolSummary = DiscoverableToolSummary;
|
|
60
|
-
|
|
61
|
-
/** Tool object stored on legacy MCP index documents. Carries both legacy `description` and the
|
|
62
|
-
* generic `summary`/`source` so the legacy index is structurally assignable to
|
|
63
|
-
* DiscoverableToolSearchIndex (search functions read termFrequencies, not the tool fields). */
|
|
64
|
-
export type DiscoverableMCPSearchTool = DiscoverableTool & { description: string };
|
|
65
|
-
|
|
66
|
-
/** @deprecated Use DiscoverableToolSearchDocument */
|
|
67
|
-
export interface DiscoverableMCPSearchDocument {
|
|
68
|
-
tool: DiscoverableMCPSearchTool;
|
|
69
|
-
termFrequencies: Map<string, number>;
|
|
70
|
-
length: number;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** @deprecated Use DiscoverableToolSearchIndex.
|
|
74
|
-
* Documents on this index expose `tool.description` (legacy MCP shape) while still being
|
|
75
|
-
* searchable via `searchDiscoverableTools`. */
|
|
76
|
-
export interface DiscoverableMCPSearchIndex {
|
|
77
|
-
documents: DiscoverableMCPSearchDocument[];
|
|
78
|
-
averageLength: number;
|
|
79
|
-
documentFrequencies: Map<string, number>;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/** @deprecated Use DiscoverableToolSearchResult */
|
|
83
|
-
export interface DiscoverableMCPSearchResult {
|
|
84
|
-
tool: DiscoverableMCPSearchTool;
|
|
85
|
-
score: number;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
47
|
// ─── BM25 Constants ───────────────────────────────────────────────────────────
|
|
89
48
|
|
|
90
49
|
const BM25_K1 = 1.2;
|
|
@@ -295,96 +254,3 @@ export function searchDiscoverableTools(
|
|
|
295
254
|
.sort((left, right) => right.score - left.score || left.tool.name.localeCompare(right.tool.name))
|
|
296
255
|
.slice(0, limit);
|
|
297
256
|
}
|
|
298
|
-
|
|
299
|
-
// ─── Legacy MCP-specific shims (back-compat wrappers) ────────────────────────
|
|
300
|
-
|
|
301
|
-
/** @deprecated Use getDiscoverableTool */
|
|
302
|
-
export function getDiscoverableMCPTool(tool: AgentTool): DiscoverableMCPTool | null {
|
|
303
|
-
if (!isMCPToolName(tool.name)) return null;
|
|
304
|
-
const toolRecord = tool as AgentTool & {
|
|
305
|
-
label?: string;
|
|
306
|
-
description?: string;
|
|
307
|
-
mcpServerName?: string;
|
|
308
|
-
mcpToolName?: string;
|
|
309
|
-
parameters?: unknown;
|
|
310
|
-
};
|
|
311
|
-
return {
|
|
312
|
-
name: tool.name,
|
|
313
|
-
label: typeof toolRecord.label === "string" ? toolRecord.label : tool.name,
|
|
314
|
-
description: typeof toolRecord.description === "string" ? toolRecord.description : "",
|
|
315
|
-
serverName: typeof toolRecord.mcpServerName === "string" ? toolRecord.mcpServerName : undefined,
|
|
316
|
-
mcpToolName: typeof toolRecord.mcpToolName === "string" ? toolRecord.mcpToolName : undefined,
|
|
317
|
-
schemaKeys: getSchemaPropertyKeys(toolRecord.parameters),
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/** @deprecated Use collectDiscoverableTools with source filter */
|
|
322
|
-
export function collectDiscoverableMCPTools(tools: Iterable<AgentTool>): DiscoverableMCPTool[] {
|
|
323
|
-
const discoverable: DiscoverableMCPTool[] = [];
|
|
324
|
-
for (const tool of tools) {
|
|
325
|
-
const metadata = getDiscoverableMCPTool(tool);
|
|
326
|
-
if (metadata) {
|
|
327
|
-
discoverable.push(metadata);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
return discoverable;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/** @deprecated Use selectDiscoverableToolNamesByServer */
|
|
334
|
-
export function selectDiscoverableMCPToolNamesByServer(
|
|
335
|
-
tools: Iterable<DiscoverableMCPTool>,
|
|
336
|
-
serverNames: ReadonlySet<string>,
|
|
337
|
-
): string[] {
|
|
338
|
-
if (serverNames.size === 0) return [];
|
|
339
|
-
return Array.from(tools)
|
|
340
|
-
.filter(tool => tool.serverName !== undefined && serverNames.has(tool.serverName))
|
|
341
|
-
.map(tool => tool.name);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/** @deprecated Use summarizeDiscoverableTools */
|
|
345
|
-
export function summarizeDiscoverableMCPTools(tools: DiscoverableMCPTool[]): DiscoverableMCPToolSummary {
|
|
346
|
-
const serverToolCounts = new Map<string, number>();
|
|
347
|
-
for (const tool of tools) {
|
|
348
|
-
if (!tool.serverName) continue;
|
|
349
|
-
serverToolCounts.set(tool.serverName, (serverToolCounts.get(tool.serverName) ?? 0) + 1);
|
|
350
|
-
}
|
|
351
|
-
const servers = Array.from(serverToolCounts.entries())
|
|
352
|
-
.sort(([left], [right]) => left.localeCompare(right))
|
|
353
|
-
.map(([name, toolCount]) => ({ name, toolCount }));
|
|
354
|
-
return {
|
|
355
|
-
servers,
|
|
356
|
-
toolCount: tools.length,
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/** @deprecated Use buildDiscoverableToolSearchIndex.
|
|
361
|
-
* Builds an index whose documents preserve the legacy `description` field on each tool while
|
|
362
|
-
* also carrying the generic `summary` (set from `description`) so the index remains usable
|
|
363
|
-
* with `searchDiscoverableTools`. */
|
|
364
|
-
export function buildDiscoverableMCPSearchIndex(tools: Iterable<DiscoverableMCPTool>): DiscoverableMCPSearchIndex {
|
|
365
|
-
const adapted: DiscoverableMCPSearchTool[] = Array.from(tools).map(t => ({
|
|
366
|
-
name: t.name,
|
|
367
|
-
label: t.label,
|
|
368
|
-
description: t.description,
|
|
369
|
-
summary: t.description,
|
|
370
|
-
source: "mcp" as DiscoverableToolSource,
|
|
371
|
-
serverName: t.serverName,
|
|
372
|
-
mcpToolName: t.mcpToolName,
|
|
373
|
-
schemaKeys: t.schemaKeys,
|
|
374
|
-
}));
|
|
375
|
-
const generic = buildDiscoverableToolSearchIndex(adapted);
|
|
376
|
-
// Documents reference `adapted` tools (with `description`), so the cast is sound.
|
|
377
|
-
return generic as unknown as DiscoverableMCPSearchIndex;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/** @deprecated Use searchDiscoverableTools */
|
|
381
|
-
export function searchDiscoverableMCPTools(
|
|
382
|
-
index: DiscoverableMCPSearchIndex | DiscoverableToolSearchIndex,
|
|
383
|
-
query: string,
|
|
384
|
-
limit: number,
|
|
385
|
-
): DiscoverableMCPSearchResult[] {
|
|
386
|
-
return searchDiscoverableTools(index as DiscoverableToolSearchIndex, query, limit) as DiscoverableMCPSearchResult[];
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/** @deprecated Use formatDiscoverableToolServerSummary */
|
|
390
|
-
export const formatDiscoverableMCPToolServerSummary = formatDiscoverableToolServerSummary;
|
package/src/tools/ast-edit.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { Text } from "@oh-my-pi/pi-tui";
|
|
|
6
6
|
import { $envpos, prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
7
7
|
import * as z from "zod/v4";
|
|
8
8
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
9
|
-
import {
|
|
9
|
+
import { computeFileHash, formatHashlineHeader } from "../hashline/hash";
|
|
10
10
|
import type { Theme } from "../modes/theme/theme";
|
|
11
11
|
import astEditDescription from "../prompts/tools/ast-edit.md" with { type: "text" };
|
|
12
12
|
import { Ellipsis, fileHyperlink, renderStatusLine, renderTreeList, truncateToWidth } from "../tui";
|
|
@@ -257,12 +257,26 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
|
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
const useHashLines = resolveFileDisplayMode(this.session).hashLines;
|
|
260
|
+
const hashContexts = new Map<string, { fileHash: string }>();
|
|
261
|
+
if (useHashLines) {
|
|
262
|
+
for (const relativePath of fileList) {
|
|
263
|
+
const absolutePath = path.resolve(this.session.cwd, relativePath);
|
|
264
|
+
try {
|
|
265
|
+
const fullText = await Bun.file(absolutePath).text();
|
|
266
|
+
const fileHash = computeFileHash(fullText);
|
|
267
|
+
hashContexts.set(relativePath, { fileHash });
|
|
268
|
+
} catch {
|
|
269
|
+
// Best-effort: if a file disappears between ast-edit and rendering, emit plain line output.
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
260
273
|
const outputLines: string[] = [];
|
|
261
274
|
const displayLines: string[] = [];
|
|
262
275
|
const renderChangesForFile = (relativePath: string): { model: string[]; display: string[] } => {
|
|
263
276
|
const modelOut: string[] = [];
|
|
264
277
|
const displayOut: string[] = [];
|
|
265
278
|
const fileChanges = changesByFile.get(relativePath) ?? [];
|
|
279
|
+
const hashContext = hashContexts.get(relativePath);
|
|
266
280
|
const lineNumberWidth = fileChanges.reduce(
|
|
267
281
|
(width, change) => Math.max(width, String(change.startLine).length),
|
|
268
282
|
0,
|
|
@@ -272,13 +286,9 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
|
|
|
272
286
|
const afterFirstLine = change.after.split("\n", 1)[0] ?? "";
|
|
273
287
|
const beforeLine = beforeFirstLine.slice(0, 120);
|
|
274
288
|
const afterLine = afterFirstLine.slice(0, 120);
|
|
275
|
-
const beforeRef =
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const afterRef = useHashLines
|
|
279
|
-
? `${change.startLine}${computeLineHash(change.startLine, afterFirstLine)}`
|
|
280
|
-
: `${change.startLine}:${change.startColumn}`;
|
|
281
|
-
const lineSeparator = useHashLines ? HL_BODY_SEP : " ";
|
|
289
|
+
const beforeRef = hashContext ? `${change.startLine}` : `${change.startLine}:${change.startColumn}`;
|
|
290
|
+
const afterRef = hashContext ? `${change.startLine}` : `${change.startLine}:${change.startColumn}`;
|
|
291
|
+
const lineSeparator = hashContext ? ":" : " ";
|
|
282
292
|
modelOut.push(`-${beforeRef}${lineSeparator}${beforeLine}`);
|
|
283
293
|
modelOut.push(`+${afterRef}${lineSeparator}${afterLine}`);
|
|
284
294
|
displayOut.push(formatCodeFrameLine("-", change.startLine, beforeLine, lineNumberWidth));
|
|
@@ -291,10 +301,13 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
|
|
|
291
301
|
const grouped = formatGroupedFiles(fileList, relativePath => {
|
|
292
302
|
const rendered = renderChangesForFile(relativePath);
|
|
293
303
|
const count = fileReplacementCounts.get(relativePath) ?? 0;
|
|
304
|
+
const hashContext = hashContexts.get(relativePath);
|
|
305
|
+
const hashSuffix = hashContext ? `#${hashContext.fileHash}` : "";
|
|
294
306
|
return {
|
|
295
|
-
headerSuffix:
|
|
307
|
+
headerSuffix: `${hashSuffix} (${formatCount("replacement", count)})`,
|
|
296
308
|
modelLines: rendered.model,
|
|
297
309
|
displayLines: rendered.display,
|
|
310
|
+
skip: rendered.model.length === 0,
|
|
298
311
|
};
|
|
299
312
|
});
|
|
300
313
|
outputLines.push(...grouped.model);
|
|
@@ -302,6 +315,15 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
|
|
|
302
315
|
} else {
|
|
303
316
|
for (const relativePath of fileList) {
|
|
304
317
|
const rendered = renderChangesForFile(relativePath);
|
|
318
|
+
if (rendered.model.length === 0) continue;
|
|
319
|
+
if (outputLines.length > 0) {
|
|
320
|
+
outputLines.push("");
|
|
321
|
+
displayLines.push("");
|
|
322
|
+
}
|
|
323
|
+
const hashContext = hashContexts.get(relativePath);
|
|
324
|
+
if (hashContext) {
|
|
325
|
+
outputLines.push(formatHashlineHeader(relativePath, hashContext.fileHash));
|
|
326
|
+
}
|
|
305
327
|
outputLines.push(...rendered.model);
|
|
306
328
|
displayLines.push(...rendered.display);
|
|
307
329
|
}
|
|
@@ -499,11 +521,12 @@ export const astEditToolRenderer = {
|
|
|
499
521
|
let contextDir = searchBase ?? "";
|
|
500
522
|
return group.map(line => {
|
|
501
523
|
if (line.startsWith("## ")) {
|
|
502
|
-
// Strip ` (3 replacements)`
|
|
524
|
+
// Strip ` (3 replacements)` and `#hash` suffixes from formatGroupedFiles.
|
|
503
525
|
const fileName = line
|
|
504
526
|
.slice(3)
|
|
505
527
|
.trimEnd()
|
|
506
|
-
.replace(/\s+\([^)]*\)\s*$/, "")
|
|
528
|
+
.replace(/\s+\([^)]*\)\s*$/, "")
|
|
529
|
+
.replace(/#[0-9a-f]+$/, "");
|
|
507
530
|
const absPath = contextDir && fileName ? path.join(contextDir, fileName) : undefined;
|
|
508
531
|
const styled = uiTheme.fg("dim", line);
|
|
509
532
|
return absPath ? fileHyperlink(absPath, styled) : styled;
|
|
@@ -514,14 +537,14 @@ export const astEditToolRenderer = {
|
|
|
514
537
|
.trimEnd()
|
|
515
538
|
.replace(/\s+\([^)]*\)\s*$/, "");
|
|
516
539
|
const isDirectory = raw.endsWith("/");
|
|
517
|
-
const name = raw.replace(/\/$/, "");
|
|
540
|
+
const name = isDirectory ? raw.replace(/\/$/, "") : raw.replace(/#[0-9a-f]+$/, "");
|
|
518
541
|
if (isDirectory) {
|
|
519
542
|
if (searchBase) {
|
|
520
543
|
contextDir = name === "." ? searchBase : path.join(searchBase, name);
|
|
521
544
|
}
|
|
522
545
|
return uiTheme.fg("accent", line);
|
|
523
546
|
}
|
|
524
|
-
// Root-level file with optional
|
|
547
|
+
// Root-level file with optional `#hash` and ` (3 replacements)` suffixes.
|
|
525
548
|
const absPath = searchBase && name ? path.join(searchBase, name) : undefined;
|
|
526
549
|
const styled = uiTheme.fg("accent", line);
|
|
527
550
|
return absPath ? fileHyperlink(absPath, styled) : styled;
|
package/src/tools/ast-grep.ts
CHANGED
|
@@ -5,7 +5,9 @@ import type { Component } from "@oh-my-pi/pi-tui";
|
|
|
5
5
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
6
6
|
import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
7
7
|
import * as z from "zod/v4";
|
|
8
|
+
import { getFileReadCache } from "../edit/file-read-cache";
|
|
8
9
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
10
|
+
import { computeFileHash, formatHashlineHeader } from "../hashline/hash";
|
|
9
11
|
import type { Theme } from "../modes/theme/theme";
|
|
10
12
|
import astGrepDescription from "../prompts/tools/ast-grep.md" with { type: "text" };
|
|
11
13
|
import { Ellipsis, fileHyperlink, renderStatusLine, renderTreeList, truncateToWidth } from "../tui";
|
|
@@ -216,25 +218,43 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
|
|
|
216
218
|
}
|
|
217
219
|
|
|
218
220
|
const useHashLines = resolveFileDisplayMode(this.session).hashLines;
|
|
221
|
+
const hashContexts = new Map<string, { absolutePath: string; fileHash: string }>();
|
|
222
|
+
if (useHashLines) {
|
|
223
|
+
for (const relativePath of fileList) {
|
|
224
|
+
const absolutePath = path.resolve(this.session.cwd, relativePath);
|
|
225
|
+
try {
|
|
226
|
+
const fullText = await Bun.file(absolutePath).text();
|
|
227
|
+
const fileHash = computeFileHash(fullText);
|
|
228
|
+
hashContexts.set(relativePath, { absolutePath, fileHash });
|
|
229
|
+
} catch {
|
|
230
|
+
// Best-effort: if a file disappears between ast-grep and rendering, emit plain line output.
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
219
234
|
const outputLines: string[] = [];
|
|
220
235
|
const displayLines: string[] = [];
|
|
221
236
|
const renderMatchesForFile = (relativePath: string): { model: string[]; display: string[] } => {
|
|
222
237
|
const modelOut: string[] = [];
|
|
223
238
|
const displayOut: string[] = [];
|
|
224
239
|
const fileMatches = matchesByFile.get(relativePath) ?? [];
|
|
240
|
+
const hashContext = hashContexts.get(relativePath);
|
|
225
241
|
const lineNumberWidth = fileMatches.reduce((width, match) => {
|
|
226
242
|
const lineCount = match.text.split("\n").length;
|
|
227
243
|
const endLine = match.startLine + lineCount - 1;
|
|
228
244
|
return Math.max(width, String(match.startLine).length, String(endLine).length);
|
|
229
245
|
}, 0);
|
|
246
|
+
const cacheEntries: Array<readonly [number, string]> = [];
|
|
230
247
|
for (const match of fileMatches) {
|
|
231
248
|
const matchLines = match.text.split("\n");
|
|
232
249
|
for (let index = 0; index < matchLines.length; index++) {
|
|
233
250
|
const lineNumber = match.startLine + index;
|
|
234
251
|
const isMatch = index === 0;
|
|
235
252
|
const line = matchLines[index] ?? "";
|
|
236
|
-
modelOut.push(
|
|
253
|
+
modelOut.push(
|
|
254
|
+
formatMatchLine(lineNumber, line, isMatch, { useHashLines: hashContext !== undefined }),
|
|
255
|
+
);
|
|
237
256
|
displayOut.push(formatCodeFrameLine(isMatch ? "*" : " ", lineNumber, line, lineNumberWidth));
|
|
257
|
+
cacheEntries.push([lineNumber, line] as const);
|
|
238
258
|
}
|
|
239
259
|
if (match.metaVariables && Object.keys(match.metaVariables).length > 0) {
|
|
240
260
|
const serializedMeta = Object.entries(match.metaVariables)
|
|
@@ -246,19 +266,39 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
|
|
|
246
266
|
}
|
|
247
267
|
fileMatchCounts.set(relativePath, (fileMatchCounts.get(relativePath) ?? 0) + 1);
|
|
248
268
|
}
|
|
269
|
+
if (hashContext && cacheEntries.length > 0) {
|
|
270
|
+
getFileReadCache(this.session).recordSparse(hashContext.absolutePath, cacheEntries, {
|
|
271
|
+
fileHash: hashContext.fileHash,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
249
274
|
return { model: modelOut, display: displayOut };
|
|
250
275
|
};
|
|
251
276
|
|
|
252
277
|
if (isDirectory) {
|
|
253
278
|
const grouped = formatGroupedFiles(fileList, relativePath => {
|
|
254
279
|
const rendered = renderMatchesForFile(relativePath);
|
|
255
|
-
|
|
280
|
+
const hashContext = hashContexts.get(relativePath);
|
|
281
|
+
return {
|
|
282
|
+
modelLines: rendered.model,
|
|
283
|
+
displayLines: rendered.display,
|
|
284
|
+
headerSuffix: hashContext ? `#${hashContext.fileHash}` : "",
|
|
285
|
+
skip: rendered.model.length === 0,
|
|
286
|
+
};
|
|
256
287
|
});
|
|
257
288
|
outputLines.push(...grouped.model);
|
|
258
289
|
displayLines.push(...grouped.display);
|
|
259
290
|
} else {
|
|
260
291
|
for (const relativePath of fileList) {
|
|
261
292
|
const rendered = renderMatchesForFile(relativePath);
|
|
293
|
+
if (rendered.model.length === 0) continue;
|
|
294
|
+
if (outputLines.length > 0) {
|
|
295
|
+
outputLines.push("");
|
|
296
|
+
displayLines.push("");
|
|
297
|
+
}
|
|
298
|
+
const hashContext = hashContexts.get(relativePath);
|
|
299
|
+
if (hashContext) {
|
|
300
|
+
outputLines.push(formatHashlineHeader(relativePath, hashContext.fileHash));
|
|
301
|
+
}
|
|
262
302
|
outputLines.push(...rendered.model);
|
|
263
303
|
displayLines.push(...rendered.display);
|
|
264
304
|
}
|
|
@@ -385,7 +425,8 @@ export const astGrepToolRenderer = {
|
|
|
385
425
|
const fileName = line
|
|
386
426
|
.slice(3)
|
|
387
427
|
.trimEnd()
|
|
388
|
-
.replace(/\s+\([^)]*\)\s*$/, "")
|
|
428
|
+
.replace(/\s+\([^)]*\)\s*$/, "")
|
|
429
|
+
.replace(/#[0-9a-f]+$/, "");
|
|
389
430
|
const absPath = contextDir && fileName ? path.join(contextDir, fileName) : undefined;
|
|
390
431
|
const styled = uiTheme.fg("dim", line);
|
|
391
432
|
return absPath ? fileHyperlink(absPath, styled) : styled;
|
|
@@ -396,7 +437,7 @@ export const astGrepToolRenderer = {
|
|
|
396
437
|
.trimEnd()
|
|
397
438
|
.replace(/\s+\([^)]*\)\s*$/, "");
|
|
398
439
|
const isDirectory = raw.endsWith("/");
|
|
399
|
-
const name = raw.replace(/\/$/, "");
|
|
440
|
+
const name = isDirectory ? raw.replace(/\/$/, "") : raw.replace(/#[0-9a-f]+$/, "");
|
|
400
441
|
if (isDirectory) {
|
|
401
442
|
if (searchBase) {
|
|
402
443
|
contextDir = name === "." ? searchBase : path.join(searchBase, name);
|
|
@@ -575,8 +575,10 @@ export class WorkerCore {
|
|
|
575
575
|
if (signal.aborted) onCancel();
|
|
576
576
|
else signal.addEventListener("abort", onCancel, { once: true });
|
|
577
577
|
try {
|
|
578
|
+
const hooks = this.#hooksForActiveRun();
|
|
579
|
+
if (!hooks) throw new ToolError("Browser runtime started without an active run");
|
|
578
580
|
const returnValue = await Promise.race([
|
|
579
|
-
runtime.run(msg.code, `browser-run-${msg.id}.js
|
|
581
|
+
runtime.run(msg.code, `browser-run-${msg.id}.js`, hooks, { runId: msg.id, cwd: msg.session.cwd }),
|
|
580
582
|
cancelRejection,
|
|
581
583
|
]);
|
|
582
584
|
await this.#postReadyInfo();
|
|
@@ -601,7 +603,6 @@ export class WorkerCore {
|
|
|
601
603
|
this.#runtime = new JsRuntime({
|
|
602
604
|
initialCwd: session.cwd,
|
|
603
605
|
sessionId: `browser-tab-${this.#targetId ?? "unknown"}`,
|
|
604
|
-
getHooks: () => this.#hooksForActiveRun(),
|
|
605
606
|
});
|
|
606
607
|
return this.#runtime;
|
|
607
608
|
}
|
package/src/tools/eval.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { prompt } from "@oh-my-pi/pi-utils";
|
|
|
6
6
|
import * as z from "zod/v4";
|
|
7
7
|
import { jsBackend, pythonBackend } from "../eval";
|
|
8
8
|
import type { ExecutorBackend } from "../eval/backend";
|
|
9
|
+
import { defaultEvalSessionId } from "../eval/session-id";
|
|
9
10
|
import type { EvalCellResult, EvalDisplayOutput, EvalLanguage, EvalStatusEvent, EvalToolDetails } from "../eval/types";
|
|
10
11
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
11
12
|
import { truncateToVisualLines } from "../modes/components/visual-truncate";
|
|
@@ -347,7 +348,7 @@ export class EvalTool implements AgentTool<typeof evalSchema> {
|
|
|
347
348
|
pushUpdate();
|
|
348
349
|
},
|
|
349
350
|
});
|
|
350
|
-
const sessionId =
|
|
351
|
+
const sessionId = session.getEvalSessionId?.() ?? defaultEvalSessionId(session);
|
|
351
352
|
|
|
352
353
|
for (let i = 0; i < cells.length; i++) {
|
|
353
354
|
const cell = cells[i];
|