@oh-my-pi/pi-coding-agent 16.3.10 → 16.3.12
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 +61 -0
- package/dist/cli.js +3368 -3277
- package/dist/types/advisor/runtime.d.ts +11 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/settings-schema.d.ts +6 -0
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/discovery/helpers.d.ts +9 -0
- package/dist/types/exec/bash-executor.d.ts +1 -0
- package/dist/types/extensibility/shared-events.d.ts +2 -2
- package/dist/types/internal-urls/__tests__/agent-protocol-nested.test.d.ts +1 -0
- package/dist/types/internal-urls/registry-helpers.d.ts +7 -5
- package/dist/types/modes/components/model-selector.d.ts +2 -1
- package/dist/types/modes/github-ref-autocomplete.d.ts +35 -0
- package/dist/types/modes/utils/context-usage.d.ts +0 -12
- package/dist/types/modes/workflow.d.ts +5 -1
- package/dist/types/session/agent-session.d.ts +5 -0
- package/dist/types/system-prompt.d.ts +1 -1
- package/dist/types/tools/bash-interactive.d.ts +1 -1
- package/dist/types/tools/bash-skill-urls.d.ts +1 -0
- package/dist/types/tools/bash.d.ts +2 -1
- package/dist/types/tools/browser/launch.d.ts +1 -0
- package/dist/types/tools/grep.d.ts +2 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/path-utils.d.ts +24 -0
- package/dist/types/tools/read.d.ts +2 -0
- package/dist/types/utils/local-date.d.ts +2 -0
- package/package.json +12 -12
- package/src/advisor/__tests__/advisor.test.ts +145 -0
- package/src/advisor/runtime.ts +19 -0
- package/src/config/api-key-resolver.ts +7 -2
- package/src/config/model-discovery.ts +18 -2
- package/src/config/model-registry.ts +9 -0
- package/src/config/settings-schema.ts +11 -1
- package/src/config/settings.ts +11 -0
- package/src/discovery/builtin.ts +2 -1
- package/src/discovery/claude-plugins.ts +167 -46
- package/src/discovery/helpers.ts +16 -1
- package/src/edit/renderer.ts +20 -6
- package/src/eval/js/worker-core.ts +163 -6
- package/src/exec/bash-executor.ts +14 -9
- package/src/extensibility/plugins/legacy-pi-compat.ts +6 -2
- package/src/extensibility/plugins/marketplace/fetcher.ts +15 -14
- package/src/extensibility/shared-events.ts +2 -2
- package/src/internal-urls/__tests__/agent-protocol-nested.test.ts +68 -0
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/internal-urls/registry-helpers.ts +9 -6
- package/src/modes/components/model-selector.ts +30 -6
- package/src/modes/components/settings-defs.ts +1 -1
- package/src/modes/components/status-line/component.ts +14 -2
- package/src/modes/controllers/command-controller.ts +13 -23
- package/src/modes/controllers/event-controller.ts +12 -12
- package/src/modes/controllers/extension-ui-controller.ts +6 -35
- package/src/modes/controllers/input-controller.ts +18 -2
- package/src/modes/controllers/mcp-command-controller.ts +10 -9
- package/src/modes/controllers/selector-controller.ts +16 -5
- package/src/modes/github-ref-autocomplete.ts +75 -0
- package/src/modes/interactive-mode.ts +54 -10
- package/src/modes/prompt-action-autocomplete.ts +35 -0
- package/src/modes/utils/context-usage.ts +58 -5
- package/src/modes/utils/hotkeys-markdown.ts +2 -1
- package/src/modes/utils/ui-helpers.ts +2 -2
- package/src/modes/workflow.ts +14 -8
- package/src/prompts/system/plan-mode-active.md +5 -2
- package/src/prompts/system/system-prompt.md +1 -2
- package/src/prompts/system/title-system.md +10 -10
- package/src/prompts/system/workflow-notice.md +69 -50
- package/src/prompts/tools/bash.md +18 -7
- package/src/prompts/tools/grep.md +1 -1
- package/src/prompts/tools/read.md +4 -3
- package/src/sdk.ts +11 -0
- package/src/session/agent-session.ts +128 -14
- package/src/system-prompt.test.ts +34 -1
- package/src/system-prompt.ts +27 -10
- package/src/tools/bash-interactive.ts +1 -1
- package/src/tools/bash-skill-urls.ts +39 -7
- package/src/tools/bash.ts +69 -39
- package/src/tools/browser/launch.ts +31 -4
- package/src/tools/grep.ts +53 -14
- package/src/tools/index.ts +11 -0
- package/src/tools/path-utils.ts +46 -1
- package/src/tools/read.ts +108 -50
- package/src/utils/local-date.ts +7 -0
- package/src/utils/open.ts +36 -10
- package/src/utils/title-generator.ts +40 -65
- package/src/prompts/system/title-system-marker.md +0 -17
package/src/system-prompt.ts
CHANGED
|
@@ -24,6 +24,7 @@ import projectPromptTemplate from "./prompts/system/project-prompt.md" with { ty
|
|
|
24
24
|
import systemPromptTemplate from "./prompts/system/system-prompt.md" with { type: "text" };
|
|
25
25
|
import { shortenPath } from "./tools/render-utils";
|
|
26
26
|
import { type ActiveRepoContext, resolveActiveRepoContext } from "./utils/active-repo-context";
|
|
27
|
+
import { formatLocalCalendarDate } from "./utils/local-date";
|
|
27
28
|
import { normalizePromptPath } from "./utils/prompt-path";
|
|
28
29
|
import { AGENTS_MD_LIMIT, buildWorkspaceTree, type WorkspaceTree } from "./workspace-tree";
|
|
29
30
|
|
|
@@ -249,6 +250,21 @@ async function getCachedGpu(): Promise<string | undefined> {
|
|
|
249
250
|
await logger.time("getCachedGpu:saveGpuCache", saveGpuCache, { gpu });
|
|
250
251
|
return gpu ?? undefined;
|
|
251
252
|
}
|
|
253
|
+
|
|
254
|
+
async function getCpuModel(): Promise<string | undefined> {
|
|
255
|
+
if (process.platform !== "linux") return undefined;
|
|
256
|
+
try {
|
|
257
|
+
const cpuInfo = await Bun.file("/proc/cpuinfo").text();
|
|
258
|
+
const match = /^model name\s*:\s*(.+)$/m.exec(cpuInfo);
|
|
259
|
+
return match?.[1]?.trim() || undefined;
|
|
260
|
+
} catch (error) {
|
|
261
|
+
if (!isEnoent(error)) {
|
|
262
|
+
logger.debug("Could not read Linux CPU model", { error: String(error) });
|
|
263
|
+
}
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
252
268
|
/**
|
|
253
269
|
* Kernel identity for the workstation block. Prefers the uname build string
|
|
254
270
|
* from `os.version()`, but Bun on macOS 15+ (Darwin 24/25) returns the literal
|
|
@@ -263,13 +279,10 @@ function getKernelIdentity(): string {
|
|
|
263
279
|
return `${os.type()} ${os.release()}`.trim();
|
|
264
280
|
}
|
|
265
281
|
|
|
266
|
-
function getEnvironmentInfo(
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
} catch {
|
|
271
|
-
cpuModel = undefined;
|
|
272
|
-
}
|
|
282
|
+
function getEnvironmentInfo(
|
|
283
|
+
cpuModel: string | undefined,
|
|
284
|
+
gpu: string | undefined,
|
|
285
|
+
): Array<{ label: string; value: string }> {
|
|
273
286
|
const entries: Array<{ label: string; value: string | undefined }> = [
|
|
274
287
|
{ label: "OS", value: `${os.platform()} ${os.release()}` },
|
|
275
288
|
{ label: "Distro", value: os.type() },
|
|
@@ -388,7 +401,7 @@ export async function loadSystemPromptFiles(options: LoadContextFilesOptions = {
|
|
|
388
401
|
return userLevel?.content ?? null;
|
|
389
402
|
}
|
|
390
403
|
|
|
391
|
-
export const DEFAULT_SYSTEM_PROMPT_TOOL_NAMES = ["read", "bash", "
|
|
404
|
+
export const DEFAULT_SYSTEM_PROMPT_TOOL_NAMES = ["read", "bash", "edit", "write"] as const;
|
|
392
405
|
|
|
393
406
|
export interface SystemPromptToolMetadata {
|
|
394
407
|
label: string;
|
|
@@ -549,6 +562,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
549
562
|
agentsMdFiles: [],
|
|
550
563
|
} satisfies WorkspaceTree,
|
|
551
564
|
activeRepoContext: null as ActiveRepoContext | null,
|
|
565
|
+
cpuModel: undefined as string | undefined,
|
|
552
566
|
gpu: undefined as string | undefined,
|
|
553
567
|
};
|
|
554
568
|
|
|
@@ -619,6 +633,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
619
633
|
providedActiveRepoContext !== undefined
|
|
620
634
|
? Promise.resolve(providedActiveRepoContext)
|
|
621
635
|
: logger.time("resolveActiveRepoContext", () => resolveActiveRepoContext(resolvedCwd));
|
|
636
|
+
const cpuModelPromise = logger.time("getCpuModel", getCpuModel);
|
|
622
637
|
const gpuPromise = logger.time("getCachedGpu", getCachedGpu);
|
|
623
638
|
|
|
624
639
|
const [
|
|
@@ -629,6 +644,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
629
644
|
skills,
|
|
630
645
|
workspaceTree,
|
|
631
646
|
activeRepoContext,
|
|
647
|
+
cpuModel,
|
|
632
648
|
gpu,
|
|
633
649
|
] = await Promise.all([
|
|
634
650
|
withDeadline(
|
|
@@ -652,6 +668,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
652
668
|
withDeadline("loadSkills", skillsPromise, prepDefaults.skills),
|
|
653
669
|
withDeadline("buildWorkspaceTree", workspaceTreePromise, prepDefaults.workspaceTree),
|
|
654
670
|
withDeadline("resolveActiveRepoContext", activeRepoContextPromise, prepDefaults.activeRepoContext),
|
|
671
|
+
withDeadline("getCpuModel", cpuModelPromise, prepDefaults.cpuModel),
|
|
655
672
|
withDeadline("getCachedGpu", gpuPromise, prepDefaults.gpu),
|
|
656
673
|
]);
|
|
657
674
|
clearTimeout(deadlineTimer);
|
|
@@ -677,7 +694,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
677
694
|
}
|
|
678
695
|
}
|
|
679
696
|
|
|
680
|
-
const date =
|
|
697
|
+
const date = formatLocalCalendarDate();
|
|
681
698
|
const dateTime = date;
|
|
682
699
|
const promptCwd = shortenPath(normalizePromptPath(resolvedCwd));
|
|
683
700
|
const activeRepoContextPrompt = renderActiveRepoContextPrompt(activeRepoContext);
|
|
@@ -732,7 +749,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
732
749
|
];
|
|
733
750
|
const injectedAlwaysApplyRules = dedupeAlwaysApplyRules(alwaysApplyRules, promptSources);
|
|
734
751
|
|
|
735
|
-
const environment = getEnvironmentInfo(gpu);
|
|
752
|
+
const environment = getEnvironmentInfo(cpuModel, gpu);
|
|
736
753
|
const data = {
|
|
737
754
|
systemPromptCustomization: effectiveSystemPromptCustomization,
|
|
738
755
|
customPrompt: resolvedCustomPrompt,
|
|
@@ -140,6 +140,30 @@ function unquoteToken(token: string): string {
|
|
|
140
140
|
return token;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
function isInsideShellQuote(command: string, index: number): boolean {
|
|
144
|
+
let quote: "'" | '"' | undefined;
|
|
145
|
+
for (let i = 0; i < index; i++) {
|
|
146
|
+
const char = command[i];
|
|
147
|
+
if (char === "\\" && quote !== "'") {
|
|
148
|
+
i++;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (char === "'" && quote !== '"') {
|
|
152
|
+
quote = quote === "'" ? undefined : "'";
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (char === '"' && quote !== "'") {
|
|
156
|
+
quote = quote === '"' ? undefined : '"';
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return quote !== undefined;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function isEmbeddedInQuotedText(command: string, token: string, index: number): boolean {
|
|
163
|
+
if (token.startsWith("'") || token.startsWith('"')) return false;
|
|
164
|
+
return isInsideShellQuote(command, index);
|
|
165
|
+
}
|
|
166
|
+
|
|
143
167
|
/** Shell-escape a path using single quotes. */
|
|
144
168
|
function shellEscape(p: string): string {
|
|
145
169
|
return `'${p.replace(/'/g, "'\\''")}'`;
|
|
@@ -216,6 +240,7 @@ export function expandSkillUrls(command: string, skills: readonly Skill[]): stri
|
|
|
216
240
|
|
|
217
241
|
/**
|
|
218
242
|
* Expand supported internal URLs in a bash command string to shell-escaped absolute paths.
|
|
243
|
+
* Unresolvable URLs and literal mentions inside larger quoted text are left unchanged.
|
|
219
244
|
* Supported schemes: skill://, agent://, artifact://, memory://, rule://, local://
|
|
220
245
|
*/
|
|
221
246
|
export async function expandInternalUrls(command: string, options: InternalUrlExpansionOptions): Promise<string> {
|
|
@@ -231,15 +256,22 @@ export async function expandInternalUrls(command: string, options: InternalUrlEx
|
|
|
231
256
|
const index = match.index;
|
|
232
257
|
if (index === undefined) continue;
|
|
233
258
|
|
|
259
|
+
if (isEmbeddedInQuotedText(command, token, index)) continue;
|
|
260
|
+
|
|
234
261
|
const rawUrl = unquoteToken(token);
|
|
235
262
|
const url = normalizeLocalScheme(rawUrl);
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
263
|
+
let resolvedPath: string;
|
|
264
|
+
try {
|
|
265
|
+
resolvedPath = await resolveInternalUrlToPath(
|
|
266
|
+
url,
|
|
267
|
+
options.skills,
|
|
268
|
+
options.internalRouter,
|
|
269
|
+
options.localOptions,
|
|
270
|
+
options.ensureLocalParentDirs,
|
|
271
|
+
);
|
|
272
|
+
} catch {
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
243
275
|
const replacement = options.noEscape ? resolvedPath : shellEscape(resolvedPath);
|
|
244
276
|
expanded = `${expanded.slice(0, index)}${replacement}${expanded.slice(index + token.length)}`;
|
|
245
277
|
}
|
package/src/tools/bash.ts
CHANGED
|
@@ -27,6 +27,7 @@ import { type BashInteractiveResult, runInteractiveBashPty } from "./bash-intera
|
|
|
27
27
|
import { checkBashInterception } from "./bash-interceptor";
|
|
28
28
|
import { canUseInteractiveBashPty } from "./bash-pty-selection";
|
|
29
29
|
import { expandInternalUrls, type InternalUrlExpansionOptions } from "./bash-skill-urls";
|
|
30
|
+
import { resolveEvalBackends } from "./eval-backends";
|
|
30
31
|
import { invalidateGithubCacheForBashCommand } from "./gh-cache-invalidation";
|
|
31
32
|
import {
|
|
32
33
|
formatStyledTruncationWarning,
|
|
@@ -131,7 +132,7 @@ async function saveBashOriginalArtifact(session: ToolSession, originalText: stri
|
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
const BASH_TIMEOUT_DESCRIPTION = `timeout in seconds; clamped to ${TOOL_TIMEOUTS.bash.min}-${TOOL_TIMEOUTS.bash.max}`;
|
|
135
|
+
const BASH_TIMEOUT_DESCRIPTION = `timeout in seconds; 0 disables the command deadline; nonzero values are clamped to ${TOOL_TIMEOUTS.bash.min}-${TOOL_TIMEOUTS.bash.max}`;
|
|
135
136
|
|
|
136
137
|
const bashSchemaBase = type({
|
|
137
138
|
command: type("string").describe("command to execute"),
|
|
@@ -166,6 +167,7 @@ export interface BashToolDetails {
|
|
|
166
167
|
meta?: OutputMeta;
|
|
167
168
|
timeoutSeconds?: number;
|
|
168
169
|
requestedTimeoutSeconds?: number;
|
|
170
|
+
timeoutDisabled?: boolean;
|
|
169
171
|
wallTimeMs?: number;
|
|
170
172
|
/** Exit code of a command that ran to completion but failed (non-zero). */
|
|
171
173
|
exitCode?: number;
|
|
@@ -375,7 +377,24 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
|
|
|
375
377
|
};
|
|
376
378
|
readonly label = "Bash";
|
|
377
379
|
readonly loadMode = "essential";
|
|
378
|
-
|
|
380
|
+
get description(): string {
|
|
381
|
+
const evalBackends = resolveEvalBackends(this.session);
|
|
382
|
+
const isToolActive = (name: string, fallback: boolean): boolean => this.session.isToolActive?.(name) ?? fallback;
|
|
383
|
+
return prompt.render(bashDescription, {
|
|
384
|
+
asyncEnabled: this.#asyncEnabled,
|
|
385
|
+
autoBackgroundEnabled: this.#autoBackgroundEnabled,
|
|
386
|
+
autoBackgroundThresholdSeconds: Math.max(0, Math.floor(this.#autoBackgroundThresholdMs / 1000)),
|
|
387
|
+
hasAstGrep: isToolActive("ast_grep", this.session.settings.get("astGrep.enabled")),
|
|
388
|
+
hasAstEdit: isToolActive("ast_edit", this.session.settings.get("astEdit.enabled")),
|
|
389
|
+
hasGrep: isToolActive("grep", this.session.settings.get("grep.enabled")),
|
|
390
|
+
hasGlob: isToolActive("glob", this.session.settings.get("glob.enabled")),
|
|
391
|
+
hasRead: isToolActive("read", true),
|
|
392
|
+
hasEval: isToolActive(
|
|
393
|
+
"eval",
|
|
394
|
+
evalBackends.python || evalBackends.js || evalBackends.ruby || evalBackends.julia,
|
|
395
|
+
),
|
|
396
|
+
});
|
|
397
|
+
}
|
|
379
398
|
readonly parameters: BashToolSchema;
|
|
380
399
|
// Non-pty calls run alongside each other (the executor isolates overlapping
|
|
381
400
|
// runs on the same shell session); pty takes over the terminal UI and must
|
|
@@ -397,15 +416,6 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
|
|
|
397
416
|
),
|
|
398
417
|
);
|
|
399
418
|
this.parameters = this.#asyncEnabled ? bashSchemaWithAsync : bashSchemaBase;
|
|
400
|
-
this.description = prompt.render(bashDescription, {
|
|
401
|
-
asyncEnabled: this.#asyncEnabled,
|
|
402
|
-
autoBackgroundEnabled: this.#autoBackgroundEnabled,
|
|
403
|
-
autoBackgroundThresholdSeconds: Math.max(0, Math.floor(this.#autoBackgroundThresholdMs / 1000)),
|
|
404
|
-
hasAstGrep: this.session.settings.get("astGrep.enabled"),
|
|
405
|
-
hasAstEdit: this.session.settings.get("astEdit.enabled"),
|
|
406
|
-
hasGrep: this.session.settings.get("grep.enabled"),
|
|
407
|
-
hasGlob: this.session.settings.get("glob.enabled"),
|
|
408
|
-
});
|
|
409
419
|
}
|
|
410
420
|
|
|
411
421
|
#formatResultOutput(result: BashResult | BashInteractiveResult): string {
|
|
@@ -421,7 +431,11 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
|
|
|
421
431
|
* completed command that failed; #buildCompletedResult surfaces it as an
|
|
422
432
|
* error *result* (carrying execution details) rather than a throw.
|
|
423
433
|
*/
|
|
424
|
-
#throwIfUnfinished(
|
|
434
|
+
#throwIfUnfinished(
|
|
435
|
+
result: BashResult | BashInteractiveResult,
|
|
436
|
+
timeoutSec: number | undefined,
|
|
437
|
+
outputText: string,
|
|
438
|
+
): void {
|
|
425
439
|
if (result.cancelled) {
|
|
426
440
|
// executeBash output already carries a `[Command cancelled]` notice from
|
|
427
441
|
// the sink; PTY/bridge interactive output does not, so annotate it here.
|
|
@@ -431,11 +445,9 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
|
|
|
431
445
|
}
|
|
432
446
|
if (isInteractiveResult(result) && result.timedOut) {
|
|
433
447
|
const out = normalizeResultOutput(result);
|
|
434
|
-
|
|
435
|
-
out
|
|
436
|
-
|
|
437
|
-
: `Command timed out after ${timeoutSec} seconds`,
|
|
438
|
-
);
|
|
448
|
+
const message =
|
|
449
|
+
timeoutSec === undefined ? "Command timed out" : `Command timed out after ${timeoutSec} seconds`;
|
|
450
|
+
throw new ToolError(out ? `${out}\n\n[${message}]` : message);
|
|
439
451
|
}
|
|
440
452
|
if (result.exitCode === undefined) {
|
|
441
453
|
throw new ToolError(`${outputText}\n\nCommand failed: missing exit status`);
|
|
@@ -444,7 +456,7 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
|
|
|
444
456
|
|
|
445
457
|
async #buildCompletedResult(
|
|
446
458
|
result: BashResult | BashInteractiveResult,
|
|
447
|
-
timeoutSec: number,
|
|
459
|
+
timeoutSec: number | undefined,
|
|
448
460
|
options: {
|
|
449
461
|
requestedTimeoutSec?: number;
|
|
450
462
|
notices?: readonly string[];
|
|
@@ -472,7 +484,12 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
|
|
|
472
484
|
// Aborts / timeouts / missing-status still propagate as thrown errors.
|
|
473
485
|
this.#throwIfUnfinished(result, timeoutSec, outputText);
|
|
474
486
|
|
|
475
|
-
const details: BashToolDetails = {
|
|
487
|
+
const details: BashToolDetails = {};
|
|
488
|
+
if (timeoutSec === undefined) {
|
|
489
|
+
details.timeoutDisabled = true;
|
|
490
|
+
} else {
|
|
491
|
+
details.timeoutSeconds = timeoutSec;
|
|
492
|
+
}
|
|
476
493
|
if (options.requestedTimeoutSec !== undefined && options.requestedTimeoutSec !== timeoutSec) {
|
|
477
494
|
details.requestedTimeoutSeconds = options.requestedTimeoutSec;
|
|
478
495
|
}
|
|
@@ -503,13 +520,17 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
|
|
|
503
520
|
jobId: string,
|
|
504
521
|
label: string,
|
|
505
522
|
previewText: string,
|
|
506
|
-
timeoutSec: number,
|
|
523
|
+
timeoutSec: number | undefined,
|
|
507
524
|
options: { requestedTimeoutSec?: number; notices?: readonly string[] } = {},
|
|
508
525
|
): AgentToolResult<BashToolDetails> {
|
|
509
526
|
const details: BashToolDetails = {
|
|
510
|
-
timeoutSeconds: timeoutSec,
|
|
511
527
|
async: { state: "running", jobId, type: "bash" },
|
|
512
528
|
};
|
|
529
|
+
if (timeoutSec === undefined) {
|
|
530
|
+
details.timeoutDisabled = true;
|
|
531
|
+
} else {
|
|
532
|
+
details.timeoutSeconds = timeoutSec;
|
|
533
|
+
}
|
|
513
534
|
if (options.requestedTimeoutSec !== undefined && options.requestedTimeoutSec !== timeoutSec) {
|
|
514
535
|
details.requestedTimeoutSeconds = options.requestedTimeoutSec;
|
|
515
536
|
}
|
|
@@ -539,8 +560,8 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
|
|
|
539
560
|
#startManagedBashJob(options: {
|
|
540
561
|
command: string;
|
|
541
562
|
commandCwd: string;
|
|
542
|
-
timeoutMs: number;
|
|
543
|
-
timeoutSec: number;
|
|
563
|
+
timeoutMs: number | undefined;
|
|
564
|
+
timeoutSec: number | undefined;
|
|
544
565
|
requestedTimeoutSec?: number;
|
|
545
566
|
notices?: readonly string[];
|
|
546
567
|
|
|
@@ -569,7 +590,7 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
|
|
|
569
590
|
const result = await executeBash(options.command, {
|
|
570
591
|
cwd: options.commandCwd,
|
|
571
592
|
sessionKey: `${this.session.getSessionId?.() ?? ""}:async:${jobId}`,
|
|
572
|
-
timeout: options.timeoutMs,
|
|
593
|
+
timeout: options.timeoutMs ?? 0,
|
|
573
594
|
signal: runSignal,
|
|
574
595
|
env: options.resolvedEnv,
|
|
575
596
|
artifactPath,
|
|
@@ -661,8 +682,9 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
|
|
|
661
682
|
}
|
|
662
683
|
}
|
|
663
684
|
|
|
664
|
-
#resolveAutoBackgroundWaitMs(timeoutMs: number): number {
|
|
685
|
+
#resolveAutoBackgroundWaitMs(timeoutMs: number | undefined): number {
|
|
665
686
|
if (this.#autoBackgroundThresholdMs <= 0) return 0;
|
|
687
|
+
if (timeoutMs === undefined) return this.#autoBackgroundThresholdMs;
|
|
666
688
|
const timeoutBufferMs = 1_000;
|
|
667
689
|
return Math.max(0, Math.min(this.#autoBackgroundThresholdMs, timeoutMs - timeoutBufferMs));
|
|
668
690
|
}
|
|
@@ -765,13 +787,17 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
|
|
|
765
787
|
throw new ToolError(`Working directory is not a directory: ${commandCwd}`);
|
|
766
788
|
}
|
|
767
789
|
|
|
768
|
-
//
|
|
790
|
+
// A timeout of 0 is an explicit long-running-command contract: the user
|
|
791
|
+
// must still cancel the call or job, but OMP does not impose a deadline.
|
|
769
792
|
const requestedTimeoutSec = rawTimeout;
|
|
770
|
-
const
|
|
771
|
-
const
|
|
793
|
+
const timeoutDisabled = requestedTimeoutSec === 0;
|
|
794
|
+
const timeoutSec = timeoutDisabled ? undefined : clampTimeout("bash", requestedTimeoutSec);
|
|
795
|
+
const timeoutMs = timeoutSec === undefined ? undefined : timeoutSec * 1000;
|
|
772
796
|
const pendingNotices: string[] = [];
|
|
773
|
-
|
|
774
|
-
|
|
797
|
+
if (timeoutSec !== undefined) {
|
|
798
|
+
const timeoutClampNotice = formatTimeoutClampNotice(requestedTimeoutSec, timeoutSec);
|
|
799
|
+
if (timeoutClampNotice) pendingNotices.push(timeoutClampNotice);
|
|
800
|
+
}
|
|
775
801
|
|
|
776
802
|
if (asyncRequested) {
|
|
777
803
|
if (!this.session.asyncJobManager) {
|
|
@@ -909,14 +935,16 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
|
|
|
909
935
|
throw new ToolAbortError("Command aborted");
|
|
910
936
|
}
|
|
911
937
|
|
|
912
|
-
const timeoutPromise =
|
|
938
|
+
const timeoutPromise = timeoutMs
|
|
939
|
+
? Bun.sleep(timeoutMs).then(() => ({ kind: "timeout" as const }))
|
|
940
|
+
: undefined;
|
|
913
941
|
// Poll until the process exits, times out, or the caller aborts.
|
|
914
942
|
for (;;) {
|
|
915
943
|
const racers: Array<Promise<BridgeRaceResult>> = [
|
|
916
944
|
exitPromise.then(s => ({ kind: "exit" as const, status: s })),
|
|
917
|
-
timeoutPromise,
|
|
918
945
|
Bun.sleep(250).then(() => ({ kind: "poll" as const })),
|
|
919
946
|
];
|
|
947
|
+
if (timeoutPromise) racers.push(timeoutPromise);
|
|
920
948
|
if (signal) {
|
|
921
949
|
racers.push(abortedP.then(() => ({ kind: "aborted" as const })));
|
|
922
950
|
}
|
|
@@ -1053,7 +1081,7 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
|
|
|
1053
1081
|
: await executeBash(command, {
|
|
1054
1082
|
cwd: commandCwd,
|
|
1055
1083
|
sessionKey: this.session.getSessionId?.() ?? undefined,
|
|
1056
|
-
timeout: timeoutMs,
|
|
1084
|
+
timeout: timeoutMs ?? 0,
|
|
1057
1085
|
signal,
|
|
1058
1086
|
env: resolvedEnv,
|
|
1059
1087
|
artifactPath,
|
|
@@ -1074,11 +1102,9 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
|
|
|
1074
1102
|
}
|
|
1075
1103
|
if (isInteractiveResult(result) && result.timedOut) {
|
|
1076
1104
|
const out = normalizeResultOutput(result);
|
|
1077
|
-
|
|
1078
|
-
out
|
|
1079
|
-
|
|
1080
|
-
: `Command timed out after ${timeoutSec} seconds`,
|
|
1081
|
-
);
|
|
1105
|
+
const message =
|
|
1106
|
+
timeoutSec === undefined ? "Command timed out" : `Command timed out after ${timeoutSec} seconds`;
|
|
1107
|
+
throw new ToolError(out ? `${out}\n\n[${message}]` : message);
|
|
1082
1108
|
}
|
|
1083
1109
|
return this.#buildCompletedResult(result, timeoutSec, {
|
|
1084
1110
|
requestedTimeoutSec,
|
|
@@ -1286,13 +1312,17 @@ export function createShellRenderer<TArgs>(config: ShellRendererConfig<TArgs>) {
|
|
|
1286
1312
|
const showingFullOutput = expanded && renderContext?.isFullOutput === true;
|
|
1287
1313
|
|
|
1288
1314
|
// Build truncation warning
|
|
1289
|
-
const
|
|
1315
|
+
const timeoutDisabled = details?.timeoutDisabled === true || renderContext?.timeout === 0;
|
|
1316
|
+
const timeoutSeconds = timeoutDisabled ? undefined : (details?.timeoutSeconds ?? renderContext?.timeout);
|
|
1290
1317
|
const requestedTimeoutSeconds = details?.requestedTimeoutSeconds;
|
|
1291
1318
|
const wallTimeMs = details?.wallTimeMs;
|
|
1292
1319
|
const statsParts: string[] = [];
|
|
1293
1320
|
if (wallTimeMs !== undefined) {
|
|
1294
1321
|
statsParts.push(`Wall: ${formatWallTimeSeconds(wallTimeMs)}s`);
|
|
1295
1322
|
}
|
|
1323
|
+
if (timeoutDisabled) {
|
|
1324
|
+
statsParts.push("Timeout: disabled");
|
|
1325
|
+
}
|
|
1296
1326
|
if (typeof timeoutSeconds === "number") {
|
|
1297
1327
|
statsParts.push(
|
|
1298
1328
|
requestedTimeoutSeconds !== undefined && requestedTimeoutSeconds !== timeoutSeconds
|
|
@@ -29,15 +29,20 @@ export const DEFAULT_VIEWPORT = { width: 1365, height: 768, deviceScaleFactor: 1
|
|
|
29
29
|
* connection dropped, etc.).
|
|
30
30
|
*/
|
|
31
31
|
export const BROWSER_PROTOCOL_TIMEOUT_MS = 60_000;
|
|
32
|
+
const ENABLE_AUTOMATION_FLAG = "--enable-automation";
|
|
32
33
|
// Automation-tell launch flags that puppeteer-core adds by default. We suppress
|
|
33
34
|
// them via `ignoreDefaultArgs` (the supported escape hatch) to mirror xxxx's
|
|
34
|
-
// chromiumSwitches patch. `--enable-automation` is the loudest: it sets
|
|
35
|
+
// chromiumSwitches patch. `--enable-automation` is the loudest: it normally sets
|
|
35
36
|
// navigator.webdriver=true and shows the "controlled by automated software" infobar.
|
|
37
|
+
// Edge is the launch-stability exception: it can exit before CDP opens when this
|
|
38
|
+
// default flag is stripped, so Edge keeps Puppeteer's flag while our explicit
|
|
39
|
+
// `--disable-blink-features=AutomationControlled` launch arg still handles
|
|
40
|
+
// navigator.webdriver.
|
|
36
41
|
// `ignoreDefaultArgs` does exact-string matching, so each entry must be a flag that
|
|
37
42
|
// puppeteer emits verbatim. The default `--disable-features=...` string can't be
|
|
38
43
|
// matched this way; it is neutralized in the puppeteer-core patch (ChromeLauncher).
|
|
39
44
|
const STEALTH_IGNORE_DEFAULT_ARGS = [
|
|
40
|
-
|
|
45
|
+
ENABLE_AUTOMATION_FLAG,
|
|
41
46
|
"--disable-extensions",
|
|
42
47
|
"--disable-default-apps",
|
|
43
48
|
"--disable-component-extensions-with-background-pages",
|
|
@@ -47,6 +52,23 @@ const STEALTH_IGNORE_DEFAULT_ARGS = [
|
|
|
47
52
|
"--disable-ipc-flooding-protection",
|
|
48
53
|
"--metrics-recording-only",
|
|
49
54
|
];
|
|
55
|
+
|
|
56
|
+
function isMicrosoftEdgeExecutable(executablePath: string | undefined): boolean {
|
|
57
|
+
if (!executablePath) return false;
|
|
58
|
+
const normalizedPath = executablePath.replaceAll("\\", "/").toLowerCase();
|
|
59
|
+
const executableName = normalizedPath.slice(normalizedPath.lastIndexOf("/") + 1);
|
|
60
|
+
return (
|
|
61
|
+
executableName === "msedge.exe" ||
|
|
62
|
+
executableName === "microsoft edge" ||
|
|
63
|
+
executableName.startsWith("microsoft-edge")
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function stealthIgnoreDefaultArgs(executablePath: string | undefined): string[] {
|
|
68
|
+
if (!isMicrosoftEdgeExecutable(executablePath)) return [...STEALTH_IGNORE_DEFAULT_ARGS];
|
|
69
|
+
return STEALTH_IGNORE_DEFAULT_ARGS.filter(arg => arg !== ENABLE_AUTOMATION_FLAG);
|
|
70
|
+
}
|
|
71
|
+
|
|
50
72
|
const STEALTH_ACCEPT_LANGUAGE = "en-US,en";
|
|
51
73
|
|
|
52
74
|
const USER_AGENT_TARGET_TIMEOUT_MS = 5_000;
|
|
@@ -282,12 +304,13 @@ export async function launchHeadlessBrowser(opts: LaunchHeadlessOptions): Promis
|
|
|
282
304
|
if (ignoreCert === "true" || ignoreCert === "1" || ignoreCert === "yes" || ignoreCert === "on") {
|
|
283
305
|
launchArgs.push("--ignore-certificate-errors");
|
|
284
306
|
}
|
|
307
|
+
const executablePath = await ensureChromiumExecutable();
|
|
285
308
|
return await puppeteer.launch({
|
|
286
309
|
headless: opts.headless,
|
|
287
310
|
defaultViewport: opts.headless ? initialViewport : null,
|
|
288
|
-
executablePath
|
|
311
|
+
executablePath,
|
|
289
312
|
args: launchArgs,
|
|
290
|
-
ignoreDefaultArgs:
|
|
313
|
+
ignoreDefaultArgs: stealthIgnoreDefaultArgs(executablePath),
|
|
291
314
|
protocolTimeout: BROWSER_PROTOCOL_TIMEOUT_MS,
|
|
292
315
|
});
|
|
293
316
|
}
|
|
@@ -737,6 +760,10 @@ export async function applyStealthPatches(
|
|
|
737
760
|
await injectStealthScripts(page);
|
|
738
761
|
}
|
|
739
762
|
|
|
763
|
+
export function stealthIgnoreDefaultArgsForTest(executablePath: string | undefined): string[] {
|
|
764
|
+
return stealthIgnoreDefaultArgs(executablePath);
|
|
765
|
+
}
|
|
766
|
+
|
|
740
767
|
export function targetSupportsUserAgentOverrideForTest(target: Target): boolean {
|
|
741
768
|
return targetSupportsUserAgentOverride(target);
|
|
742
769
|
}
|
package/src/tools/grep.ts
CHANGED
|
@@ -48,12 +48,14 @@ import {
|
|
|
48
48
|
type LineRange,
|
|
49
49
|
parseLineRanges,
|
|
50
50
|
pathTargetsSsh,
|
|
51
|
+
probeLiteralPathExists,
|
|
51
52
|
type ResolvedSearchTarget,
|
|
52
53
|
resolveReadPath,
|
|
53
54
|
resolveToolSearchScope,
|
|
54
55
|
selectorLineRanges,
|
|
55
56
|
splitInternalUrlSel,
|
|
56
57
|
splitPathAndSel,
|
|
58
|
+
splitPathAndSelPreferringLiteral,
|
|
57
59
|
toPathList,
|
|
58
60
|
} from "./path-utils";
|
|
59
61
|
import {
|
|
@@ -77,6 +79,9 @@ const searchSchema = type({
|
|
|
77
79
|
"path?": searchPathEntry.describe(
|
|
78
80
|
'file, directory, glob, internal URL, or "<file>:<lines>" selector to search; pass several as a semicolon-delimited list ("src; tests"). Omitted -> searches the workspace root (".")',
|
|
79
81
|
),
|
|
82
|
+
"selector?": type("string").describe(
|
|
83
|
+
'line selector without a leading colon (e.g. "50-100", "50+10", "50-100,200-300"); keeps `path` literal when filenames contain colons',
|
|
84
|
+
),
|
|
80
85
|
"case?": type("boolean").describe("case-sensitive search"),
|
|
81
86
|
"gitignore?": type("boolean").describe("respect gitignore"),
|
|
82
87
|
"skip?": type("number")
|
|
@@ -119,6 +124,7 @@ const SEARCH_GREP_TIMEOUT_MS = 30_000;
|
|
|
119
124
|
interface GrepPathSpec {
|
|
120
125
|
original: string;
|
|
121
126
|
clean: string;
|
|
127
|
+
literalFilesystemMatch?: boolean;
|
|
122
128
|
ranges?: [LineRange, ...LineRange[]];
|
|
123
129
|
}
|
|
124
130
|
|
|
@@ -147,9 +153,38 @@ function isReadSelectorGrammar(sel: string): boolean {
|
|
|
147
153
|
return lower === "raw" || lower === "conflicts" || parseLineRanges(sel) !== null;
|
|
148
154
|
}
|
|
149
155
|
|
|
150
|
-
function parsePathSpecs(
|
|
156
|
+
async function parsePathSpecs(
|
|
157
|
+
rawEntries: readonly string[],
|
|
158
|
+
cwd: string,
|
|
159
|
+
explicitSelector?: string,
|
|
160
|
+
): Promise<GrepPathSpec[]> {
|
|
161
|
+
const explicitRanges =
|
|
162
|
+
explicitSelector === undefined || explicitSelector.length === 0 ? undefined : parseLineRanges(explicitSelector);
|
|
163
|
+
if (explicitSelector !== undefined && !explicitRanges) {
|
|
164
|
+
throw new ToolError(
|
|
165
|
+
`selector "${explicitSelector}" is invalid — use line ranges like "50-100", "50+10", or "50-100,200-300" without a leading colon`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
151
168
|
const specs: GrepPathSpec[] = [];
|
|
152
169
|
for (const entry of rawEntries) {
|
|
170
|
+
if (explicitRanges) {
|
|
171
|
+
// Separate selector parameter makes `path` deterministic: first try the
|
|
172
|
+
// exact local filesystem path (with read-path normalization), then let
|
|
173
|
+
// archive/internal/URL resolution handle non-literal structured paths.
|
|
174
|
+
const rawPathHasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(entry);
|
|
175
|
+
const probe = rawPathHasScheme ? "missing" : await probeLiteralPathExists(entry, cwd);
|
|
176
|
+
// `"unknown"` covers EACCES/IO where we cannot confirm existence — treat
|
|
177
|
+
// it as a literal so a real file such as `test:1-2` under an unreadable
|
|
178
|
+
// parent is never silently reinterpreted as `test` + selector.
|
|
179
|
+
const literalMatch = probe !== "missing";
|
|
180
|
+
specs.push({
|
|
181
|
+
original: entry,
|
|
182
|
+
clean: literalMatch && !rawPathHasScheme ? resolveReadPath(entry, cwd) : entry,
|
|
183
|
+
literalFilesystemMatch: literalMatch,
|
|
184
|
+
ranges: explicitRanges,
|
|
185
|
+
});
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
153
188
|
// Internal URLs (`artifact://`, `skill://`, …) use the URL-aware splitter,
|
|
154
189
|
// which peels selector-shaped tails only for selector-capable schemes and
|
|
155
190
|
// leaves opaque ones (`mcp://`) intact. Unlike filesystem paths, their
|
|
@@ -168,10 +203,14 @@ function parsePathSpecs(rawEntries: readonly string[]): GrepPathSpec[] {
|
|
|
168
203
|
specs.push({ original: entry, clean: internalSplit.path, ranges: selectorLineRanges(internalSplit.sel) });
|
|
169
204
|
continue;
|
|
170
205
|
}
|
|
171
|
-
|
|
172
|
-
|
|
206
|
+
// Prefer a literal filesystem match when one exists — a real file named
|
|
207
|
+
// `test:1-2` outranks the `:1-2` selector interpretation (issue #4618).
|
|
208
|
+
const strictSplit = splitPathAndSel(entry);
|
|
209
|
+
const split = await splitPathAndSelPreferringLiteral(entry, cwd);
|
|
210
|
+
const literalFilesystemMatch = strictSplit.sel !== undefined && split.sel === undefined;
|
|
211
|
+
let clean = literalFilesystemMatch ? resolveReadPath(entry, cwd) : entry;
|
|
173
212
|
let ranges: [LineRange, ...LineRange[]] | undefined;
|
|
174
|
-
if (split.sel) {
|
|
213
|
+
if (!literalFilesystemMatch && split.sel) {
|
|
175
214
|
const parsed = parseLineRanges(split.sel);
|
|
176
215
|
if (!parsed) {
|
|
177
216
|
throw new ToolError(
|
|
@@ -184,7 +223,7 @@ function parsePathSpecs(rawEntries: readonly string[]): GrepPathSpec[] {
|
|
|
184
223
|
clean = split.path;
|
|
185
224
|
ranges = parsed;
|
|
186
225
|
}
|
|
187
|
-
specs.push({ original: entry, clean, ranges });
|
|
226
|
+
specs.push({ original: entry, clean, literalFilesystemMatch, ranges });
|
|
188
227
|
}
|
|
189
228
|
return specs;
|
|
190
229
|
}
|
|
@@ -220,7 +259,7 @@ function matchAbsolutePath(matchPath: string, searchPath: string): string {
|
|
|
220
259
|
* cleanup hook the caller MUST invoke in a `finally`.
|
|
221
260
|
*/
|
|
222
261
|
async function resolveArchiveSearchPaths(
|
|
223
|
-
|
|
262
|
+
pathSpecs: readonly GrepPathSpec[],
|
|
224
263
|
cwd: string,
|
|
225
264
|
): Promise<{
|
|
226
265
|
resolvedPaths: string[];
|
|
@@ -229,17 +268,18 @@ async function resolveArchiveSearchPaths(
|
|
|
229
268
|
unreadable: string[];
|
|
230
269
|
cleanup: () => Promise<void>;
|
|
231
270
|
}> {
|
|
232
|
-
const resolvedPaths =
|
|
271
|
+
const resolvedPaths = pathSpecs.map(spec => spec.clean);
|
|
233
272
|
const displayMap = new Map<string, string>();
|
|
234
273
|
const displaySet = new Set<string>();
|
|
235
274
|
const unreadable: string[] = [];
|
|
236
275
|
let tempDir: string | undefined;
|
|
237
276
|
const archiveCache = new Map<string, ArchiveReader>();
|
|
238
277
|
|
|
239
|
-
for (let idx = 0; idx <
|
|
240
|
-
const
|
|
278
|
+
for (let idx = 0; idx < pathSpecs.length; idx++) {
|
|
279
|
+
const spec = pathSpecs[idx];
|
|
280
|
+
if (!spec || spec.literalFilesystemMatch) continue;
|
|
281
|
+
const entry = spec.clean;
|
|
241
282
|
const candidates = parseArchivePathCandidates(entry);
|
|
242
|
-
// Longest archive prefix first; we want the one whose member portion is non-empty.
|
|
243
283
|
const member = candidates.find(c => c.subPath !== "" && c.archivePath !== entry);
|
|
244
284
|
if (!member) continue;
|
|
245
285
|
|
|
@@ -879,7 +919,7 @@ export class GrepTool implements AgentTool<typeof searchSchema, GrepToolDetails>
|
|
|
879
919
|
_onUpdate?: AgentToolUpdateCallback<GrepToolDetails>,
|
|
880
920
|
_toolContext?: AgentToolContext,
|
|
881
921
|
): Promise<AgentToolResult<GrepToolDetails>> {
|
|
882
|
-
const { pattern, path: rawPath, case: caseSensitive, gitignore, skip } = params;
|
|
922
|
+
const { pattern, path: rawPath, selector, case: caseSensitive, gitignore, skip } = params;
|
|
883
923
|
|
|
884
924
|
return untilAborted(signal, async () => {
|
|
885
925
|
// Preserve the pattern verbatim — leading/trailing whitespace is
|
|
@@ -897,8 +937,7 @@ export class GrepTool implements AgentTool<typeof searchSchema, GrepToolDetails>
|
|
|
897
937
|
const scopedPaths = toPathList(rawPath);
|
|
898
938
|
const effectivePaths = scopedPaths.length > 0 ? scopedPaths : ["."];
|
|
899
939
|
const rawEntries = await expandDelimitedPathEntries(effectivePaths, this.session.cwd);
|
|
900
|
-
const pathSpecs = parsePathSpecs(rawEntries);
|
|
901
|
-
const paths = pathSpecs.map(spec => spec.clean);
|
|
940
|
+
const pathSpecs = await parsePathSpecs(rawEntries, this.session.cwd, selector);
|
|
902
941
|
const materializedExternalPaths = new Map<string, string>();
|
|
903
942
|
const materializeExternalUrlForSearch = async (rawPath: string) => {
|
|
904
943
|
const target = parseReadUrlTarget(rawPath);
|
|
@@ -917,7 +956,7 @@ export class GrepTool implements AgentTool<typeof searchSchema, GrepToolDetails>
|
|
|
917
956
|
displaySet: archiveDisplaySet,
|
|
918
957
|
unreadable: archiveUnreadable,
|
|
919
958
|
cleanup: cleanupArchiveScratch,
|
|
920
|
-
} = await resolveArchiveSearchPaths(
|
|
959
|
+
} = await resolveArchiveSearchPaths(pathSpecs, this.session.cwd);
|
|
921
960
|
try {
|
|
922
961
|
const internalResolution = await resolveInternalSearchInputs({
|
|
923
962
|
pathSpecs,
|