@oh-my-pi/pi-coding-agent 15.10.0 → 15.10.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 +142 -1
- package/dist/types/cli/dry-balance-cli.d.ts +15 -1
- package/dist/types/cli/startup-cwd.d.ts +2 -0
- package/dist/types/commands/launch.d.ts +3 -0
- package/dist/types/commit/analysis/conventional.d.ts +2 -2
- package/dist/types/commit/analysis/summary.d.ts +2 -2
- package/dist/types/commit/changelog/generate.d.ts +2 -2
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/index.d.ts +3 -3
- package/dist/types/commit/map-reduce/map-phase.d.ts +2 -2
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +2 -2
- package/dist/types/commit/model-selection.d.ts +10 -4
- package/dist/types/config/api-key-resolver.d.ts +34 -0
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/config/model-provider-priority.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +17 -1
- package/dist/types/config/model-resolver.d.ts +4 -1
- package/dist/types/config/settings-schema.d.ts +9 -0
- package/dist/types/config/settings.d.ts +7 -2
- package/dist/types/dap/config.d.ts +14 -1
- package/dist/types/dap/types.d.ts +10 -0
- package/dist/types/debug/report-bundle.d.ts +3 -0
- package/dist/types/edit/file-snapshot-store.d.ts +18 -10
- package/dist/types/eval/py/__tests__/prelude.test.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +4 -1
- package/dist/types/lsp/client.d.ts +10 -0
- package/dist/types/lsp/utils.d.ts +3 -2
- package/dist/types/main.d.ts +3 -9
- package/dist/types/mcp/tool-bridge.d.ts +2 -0
- package/dist/types/modes/components/chat-block.d.ts +64 -0
- package/dist/types/modes/components/custom-editor.d.ts +4 -1
- package/dist/types/modes/components/overlay-box.d.ts +17 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +59 -0
- package/dist/types/modes/components/plan-toc.d.ts +41 -0
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/status-line.d.ts +2 -0
- package/dist/types/modes/components/transcript-container.d.ts +11 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +17 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +0 -1
- package/dist/types/modes/controllers/input-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +22 -0
- package/dist/types/modes/controllers/tan-command-controller.d.ts +6 -0
- package/dist/types/modes/interactive-mode.d.ts +16 -5
- package/dist/types/modes/magic-keywords.d.ts +1 -1
- package/dist/types/modes/markdown-prose.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +21 -5
- package/dist/types/modes/utils/copy-targets.d.ts +21 -1
- package/dist/types/modes/workflow.d.ts +3 -3
- package/dist/types/plan-mode/approved-plan.d.ts +27 -8
- package/dist/types/plan-mode/plan-protection.d.ts +4 -4
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +21 -0
- package/dist/types/session/auth-storage.d.ts +1 -1
- package/dist/types/session/messages.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +8 -3
- package/dist/types/slash-commands/types.d.ts +4 -6
- package/dist/types/task/executor.d.ts +17 -0
- package/dist/types/task/index.d.ts +1 -0
- package/dist/types/task/render.d.ts +3 -2
- package/dist/types/tools/archive-reader.d.ts +5 -0
- package/dist/types/tools/ast-edit.d.ts +3 -0
- package/dist/types/tools/ast-grep.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/eval.d.ts +8 -0
- package/dist/types/tools/find.d.ts +8 -4
- package/dist/types/tools/gh-cache-invalidation.d.ts +6 -0
- package/dist/types/tools/github-cache.d.ts +12 -0
- package/dist/types/tools/grouped-file-output.d.ts +95 -12
- package/dist/types/tools/memory-render.d.ts +4 -1
- package/dist/types/tools/path-utils.d.ts +8 -0
- package/dist/types/tools/plan-mode-guard.d.ts +8 -9
- package/dist/types/tools/render-utils.d.ts +5 -9
- package/dist/types/tools/search.d.ts +6 -2
- package/dist/types/tools/sqlite-reader.d.ts +1 -0
- package/dist/types/tools/todo.d.ts +3 -2
- package/dist/types/tools/write.d.ts +3 -0
- package/dist/types/tools/yield.d.ts +8 -0
- package/dist/types/tui/output-block.d.ts +16 -4
- package/dist/types/tui/status-line.d.ts +3 -0
- package/dist/types/utils/enhanced-paste.d.ts +20 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -1
- package/package.json +9 -9
- package/src/auto-thinking/classifier.ts +5 -1
- package/src/cli/args.ts +3 -1
- package/src/cli/dry-balance-cli.ts +54 -21
- package/src/cli/gallery-cli.ts +4 -1
- package/src/cli/gallery-fixtures/misc.ts +29 -0
- package/src/cli/startup-cwd.ts +68 -0
- package/src/commands/launch.ts +3 -0
- package/src/commit/analysis/conventional.ts +2 -2
- package/src/commit/analysis/summary.ts +2 -2
- package/src/commit/changelog/generate.ts +2 -2
- package/src/commit/changelog/index.ts +2 -2
- package/src/commit/map-reduce/index.ts +3 -3
- package/src/commit/map-reduce/map-phase.ts +2 -2
- package/src/commit/map-reduce/reduce-phase.ts +2 -2
- package/src/commit/model-selection.ts +36 -11
- package/src/commit/pipeline.ts +4 -4
- package/src/config/api-key-resolver.ts +58 -0
- package/src/config/model-provider-priority.ts +55 -0
- package/src/config/model-registry.ts +29 -24
- package/src/config/model-resolver.ts +39 -7
- package/src/config/settings-schema.ts +10 -0
- package/src/config/settings.ts +106 -43
- package/src/dap/config.ts +41 -2
- package/src/dap/defaults.json +1 -0
- package/src/dap/session.ts +1 -0
- package/src/dap/types.ts +10 -0
- package/src/debug/index.ts +47 -53
- package/src/debug/raw-sse-buffer.ts +7 -4
- package/src/debug/report-bundle.ts +9 -0
- package/src/edit/file-snapshot-store.ts +33 -1
- package/src/edit/hashline/filesystem.ts +2 -1
- package/src/edit/renderer.ts +82 -78
- package/src/eval/__tests__/llm-bridge.test.ts +110 -31
- package/src/eval/js/context-manager.ts +32 -15
- package/src/eval/llm-bridge.ts +22 -6
- package/src/eval/py/__tests__/prelude.test.ts +19 -0
- package/src/eval/py/executor.ts +23 -11
- package/src/eval/py/prelude.py +1 -1
- package/src/extensibility/extensions/types.ts +10 -1
- package/src/goals/tools/goal-tool.ts +36 -26
- package/src/internal-urls/docs-index.generated.ts +8 -8
- package/src/lsp/client.ts +23 -11
- package/src/lsp/config.ts +11 -1
- package/src/lsp/index.ts +61 -9
- package/src/lsp/utils.ts +3 -2
- package/src/main.ts +100 -72
- package/src/mcp/tool-bridge.ts +2 -0
- package/src/memories/index.ts +14 -7
- package/src/mnemopi/backend.ts +5 -1
- package/src/modes/acp/acp-agent.ts +33 -26
- package/src/modes/components/assistant-message.ts +2 -9
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/copy-selector.ts +1 -44
- package/src/modes/components/custom-editor.ts +164 -109
- package/src/modes/components/custom-message.ts +1 -3
- package/src/modes/components/execution-shared.ts +1 -2
- package/src/modes/components/hook-message.ts +1 -3
- package/src/modes/components/model-selector.ts +59 -13
- package/src/modes/components/oauth-selector.ts +33 -7
- package/src/modes/components/overlay-box.ts +108 -0
- package/src/modes/components/plan-review-overlay.ts +799 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/read-tool-group.ts +20 -4
- package/src/modes/components/skill-message.ts +0 -1
- package/src/modes/components/status-line.ts +19 -4
- package/src/modes/components/tips.txt +2 -1
- package/src/modes/components/todo-reminder.ts +0 -2
- package/src/modes/components/tool-execution.ts +68 -88
- package/src/modes/components/transcript-container.ts +84 -24
- package/src/modes/components/user-message.ts +2 -3
- package/src/modes/controllers/command-controller-shared.ts +7 -6
- package/src/modes/controllers/command-controller.ts +57 -55
- package/src/modes/controllers/event-controller.ts +67 -40
- package/src/modes/controllers/extension-ui-controller.ts +10 -73
- package/src/modes/controllers/input-controller.ts +170 -126
- package/src/modes/controllers/mcp-command-controller.ts +69 -60
- package/src/modes/controllers/selector-controller.ts +23 -25
- package/src/modes/controllers/streaming-reveal.ts +212 -0
- package/src/modes/controllers/tan-command-controller.ts +173 -0
- package/src/modes/interactive-mode.ts +274 -112
- package/src/modes/magic-keywords.ts +1 -1
- package/src/modes/markdown-prose.ts +1 -1
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/shimmer.ts +20 -9
- package/src/modes/theme/theme-schema.json +1 -1
- package/src/modes/theme/theme.ts +8 -4
- package/src/modes/types.ts +21 -7
- package/src/modes/utils/copy-targets.ts +133 -27
- package/src/modes/utils/ui-helpers.ts +44 -46
- package/src/modes/workflow.ts +10 -10
- package/src/plan-mode/approved-plan.ts +66 -43
- package/src/plan-mode/plan-protection.ts +4 -4
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/plan-mode-active.md +67 -58
- package/src/prompts/system/plan-mode-approved.md +1 -1
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/prompts/tools/bash.md +9 -0
- package/src/prompts/tools/browser.md +1 -1
- package/src/prompts/tools/eval.md +2 -1
- package/src/prompts/tools/read.md +2 -2
- package/src/sdk.ts +37 -46
- package/src/session/agent-session.ts +119 -18
- package/src/session/auth-storage.ts +2 -0
- package/src/session/messages.ts +26 -0
- package/src/session/session-manager.ts +109 -28
- package/src/slash-commands/builtin-registry.ts +36 -9
- package/src/slash-commands/types.ts +4 -6
- package/src/task/executor.ts +76 -38
- package/src/task/index.ts +4 -0
- package/src/task/render.ts +211 -147
- package/src/tools/archive-reader.ts +64 -0
- package/src/tools/ask.ts +119 -164
- package/src/tools/ast-edit.ts +98 -71
- package/src/tools/ast-grep.ts +37 -43
- package/src/tools/bash.ts +57 -6
- package/src/tools/browser/tab-supervisor.ts +13 -1
- package/src/tools/browser/tab-worker.ts +33 -4
- package/src/tools/debug.ts +20 -8
- package/src/tools/eval.ts +13 -2
- package/src/tools/fetch.ts +297 -7
- package/src/tools/find.ts +51 -30
- package/src/tools/gh-cache-invalidation.ts +200 -0
- package/src/tools/gh-renderer.ts +81 -42
- package/src/tools/github-cache.ts +25 -0
- package/src/tools/grouped-file-output.ts +272 -48
- package/src/tools/image-gen.ts +150 -103
- package/src/tools/inspect-image-renderer.ts +63 -41
- package/src/tools/inspect-image.ts +10 -3
- package/src/tools/job.ts +3 -4
- package/src/tools/memory-render.ts +4 -1
- package/src/tools/path-utils.ts +28 -2
- package/src/tools/plan-mode-guard.ts +66 -39
- package/src/tools/read.ts +48 -28
- package/src/tools/render-utils.ts +21 -37
- package/src/tools/resolve.ts +14 -0
- package/src/tools/search-tool-bm25.ts +36 -23
- package/src/tools/search.ts +118 -81
- package/src/tools/sqlite-reader.ts +9 -12
- package/src/tools/todo.ts +118 -52
- package/src/tools/write.ts +83 -64
- package/src/tools/yield.ts +10 -1
- package/src/tui/output-block.ts +60 -13
- package/src/tui/status-line.ts +5 -1
- package/src/utils/commit-message-generator.ts +11 -3
- package/src/utils/enhanced-paste.ts +230 -0
- package/src/utils/title-generator.ts +2 -1
- package/src/web/search/providers/anthropic.ts +25 -19
- package/src/web/search/providers/codex.ts +37 -8
- package/src/web/search/providers/exa.ts +11 -3
- package/src/web/search/providers/kimi.ts +28 -17
- package/src/web/search/providers/parallel.ts +35 -24
- package/src/web/search/providers/synthetic.ts +8 -6
- package/src/web/search/providers/tavily.ts +9 -8
- package/src/web/search/providers/zai.ts +8 -6
package/src/tools/ast-grep.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { Ellipsis, fileHyperlink, renderStatusLine, renderTreeList, truncateToWi
|
|
|
14
14
|
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
15
15
|
import type { ToolSession } from ".";
|
|
16
16
|
import { createFileRecorder, formatResultPath } from "./file-recorder";
|
|
17
|
-
import { formatGroupedFiles } from "./grouped-file-output";
|
|
17
|
+
import { classifyGroupedLines, formatGroupedFiles, groupLineIndicesByBlank } from "./grouped-file-output";
|
|
18
18
|
import { formatMatchLine } from "./match-line-format";
|
|
19
19
|
import type { OutputMeta } from "./output-meta";
|
|
20
20
|
import { resolveToolSearchScope } from "./path-utils";
|
|
@@ -29,7 +29,6 @@ import {
|
|
|
29
29
|
formatParseErrors,
|
|
30
30
|
formatParseErrorsCountLabel,
|
|
31
31
|
PREVIEW_LIMITS,
|
|
32
|
-
splitGroupsByBlankLine,
|
|
33
32
|
} from "./render-utils";
|
|
34
33
|
import { ToolError } from "./tool-errors";
|
|
35
34
|
import { toolResult } from "./tool-result";
|
|
@@ -118,6 +117,9 @@ export interface AstGrepToolDetails {
|
|
|
118
117
|
/** Absolute base directory used during search. Used by the renderer to resolve
|
|
119
118
|
* display-relative paths to absolute paths for OSC 8 hyperlinks. */
|
|
120
119
|
searchPath?: string;
|
|
120
|
+
/** Session cwd at search time. Display header/match paths are cwd-relative, so
|
|
121
|
+
* the renderer resolves them against this; `searchPath` is the scope target. */
|
|
122
|
+
cwd?: string;
|
|
121
123
|
}
|
|
122
124
|
|
|
123
125
|
export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolDetails> {
|
|
@@ -207,6 +209,7 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
|
|
|
207
209
|
...(cappedParseErrors.length > 0 ? { parseErrors: cappedParseErrors, parseErrorsTotal } : {}),
|
|
208
210
|
scopePath,
|
|
209
211
|
searchPath: resolvedSearchPath,
|
|
212
|
+
cwd: this.session.cwd,
|
|
210
213
|
files: fileList,
|
|
211
214
|
fileMatches: [],
|
|
212
215
|
};
|
|
@@ -381,15 +384,41 @@ export const astGrepToolRenderer = {
|
|
|
381
384
|
if (limitReached) meta.push(uiTheme.fg("warning", "limit reached"));
|
|
382
385
|
const description = args?.pat;
|
|
383
386
|
const header = renderStatusLine(
|
|
384
|
-
{
|
|
387
|
+
{
|
|
388
|
+
...(limitReached
|
|
389
|
+
? { icon: "warning" as const }
|
|
390
|
+
: { iconOverride: uiTheme.fg("accent", uiTheme.symbol("icon.search")) }),
|
|
391
|
+
title: "AST Grep",
|
|
392
|
+
description,
|
|
393
|
+
meta,
|
|
394
|
+
},
|
|
385
395
|
uiTheme,
|
|
386
396
|
);
|
|
387
397
|
|
|
388
398
|
const textContent = result.details?.displayContent ?? result.content?.find(c => c.type === "text")?.text ?? "";
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
);
|
|
399
|
+
const allLines = textContent.split("\n");
|
|
400
|
+
// Resolve hyperlinks over the whole output so nested directory headers
|
|
401
|
+
// reconstruct across the blank-line groups the tree list collapses by.
|
|
402
|
+
const contexts = classifyGroupedLines(allLines, details?.cwd ?? details?.searchPath, details?.searchPath);
|
|
403
|
+
const styledLines = allLines.map((line, index) => {
|
|
404
|
+
const ctx = contexts[index]!;
|
|
405
|
+
if (ctx.kind === "dir") {
|
|
406
|
+
const styled = uiTheme.fg("accent", line);
|
|
407
|
+
return ctx.headerPath ? fileHyperlink(ctx.headerPath, styled) : styled;
|
|
408
|
+
}
|
|
409
|
+
if (ctx.kind === "file") {
|
|
410
|
+
const styled = uiTheme.fg(ctx.depth === 1 ? "accent" : "dim", line);
|
|
411
|
+
return ctx.headerPath ? fileHyperlink(ctx.headerPath, styled) : styled;
|
|
412
|
+
}
|
|
413
|
+
if (line.startsWith(" meta:")) return uiTheme.fg("dim", line);
|
|
414
|
+
return uiTheme.fg("toolOutput", line);
|
|
415
|
+
});
|
|
416
|
+
const matchGroups = groupLineIndicesByBlank(allLines)
|
|
417
|
+
.filter(indices => {
|
|
418
|
+
const first = allLines[indices[0]!]!;
|
|
419
|
+
return !first.startsWith("Result limit reached") && !first.startsWith("Parse issues:");
|
|
420
|
+
})
|
|
421
|
+
.map(indices => indices.map(index => styledLines[index]!));
|
|
393
422
|
|
|
394
423
|
const extraLines: string[] = [];
|
|
395
424
|
if (limitReached) {
|
|
@@ -404,7 +433,6 @@ export const astGrepToolRenderer = {
|
|
|
404
433
|
return createCachedComponent(
|
|
405
434
|
() => options.expanded,
|
|
406
435
|
width => {
|
|
407
|
-
const searchBase = details?.searchPath;
|
|
408
436
|
const matchLines = renderTreeList(
|
|
409
437
|
{
|
|
410
438
|
items: matchGroups,
|
|
@@ -412,41 +440,7 @@ export const astGrepToolRenderer = {
|
|
|
412
440
|
maxCollapsed: matchGroups.length,
|
|
413
441
|
maxCollapsedLines: COLLAPSED_MATCH_LIMIT,
|
|
414
442
|
itemType: "match",
|
|
415
|
-
renderItem: group =>
|
|
416
|
-
let contextDir = searchBase ?? "";
|
|
417
|
-
return group.map(line => {
|
|
418
|
-
if (line.startsWith("## ")) {
|
|
419
|
-
const fileName = line
|
|
420
|
-
.slice(3)
|
|
421
|
-
.trimEnd()
|
|
422
|
-
.replace(/\s+\([^)]*\)\s*$/, "")
|
|
423
|
-
.replace(/#[0-9a-f]+$/, "");
|
|
424
|
-
const absPath = contextDir && fileName ? path.join(contextDir, fileName) : undefined;
|
|
425
|
-
const styled = uiTheme.fg("dim", line);
|
|
426
|
-
return absPath ? fileHyperlink(absPath, styled) : styled;
|
|
427
|
-
}
|
|
428
|
-
if (line.startsWith("# ")) {
|
|
429
|
-
const raw = line
|
|
430
|
-
.slice(2)
|
|
431
|
-
.trimEnd()
|
|
432
|
-
.replace(/\s+\([^)]*\)\s*$/, "");
|
|
433
|
-
const isDirectory = raw.endsWith("/");
|
|
434
|
-
const name = isDirectory ? raw.replace(/\/$/, "") : raw.replace(/#[0-9a-f]+$/, "");
|
|
435
|
-
if (isDirectory) {
|
|
436
|
-
if (searchBase) {
|
|
437
|
-
contextDir = name === "." ? searchBase : path.join(searchBase, name);
|
|
438
|
-
}
|
|
439
|
-
return uiTheme.fg("accent", line);
|
|
440
|
-
}
|
|
441
|
-
// Root-level file (single # without trailing slash) from formatGroupedFiles.
|
|
442
|
-
const absPath = searchBase && name ? path.join(searchBase, name) : undefined;
|
|
443
|
-
const styled = uiTheme.fg("accent", line);
|
|
444
|
-
return absPath ? fileHyperlink(absPath, styled) : styled;
|
|
445
|
-
}
|
|
446
|
-
if (line.startsWith(" meta:")) return uiTheme.fg("dim", line);
|
|
447
|
-
return uiTheme.fg("toolOutput", line);
|
|
448
|
-
});
|
|
449
|
-
},
|
|
443
|
+
renderItem: group => group,
|
|
450
444
|
},
|
|
451
445
|
uiTheme,
|
|
452
446
|
);
|
package/src/tools/bash.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { type BashInteractiveResult, runInteractiveBashPty } from "./bash-intera
|
|
|
29
29
|
import { checkBashInterception } from "./bash-interceptor";
|
|
30
30
|
import { canUseInteractiveBashPty } from "./bash-pty-selection";
|
|
31
31
|
import { expandInternalUrls, type InternalUrlExpansionOptions } from "./bash-skill-urls";
|
|
32
|
+
import { invalidateGithubCacheForBashCommand } from "./gh-cache-invalidation";
|
|
32
33
|
import { formatStyledTruncationWarning, type OutputMeta, stripOutputNotice } from "./output-meta";
|
|
33
34
|
import { resolveToCwd } from "./path-utils";
|
|
34
35
|
import { capPreviewLines, formatToolWorkingDirectory, replaceTabs } from "./render-utils";
|
|
@@ -287,6 +288,35 @@ function formatExitCodeNotice(exitCode: number): string {
|
|
|
287
288
|
return `Command exited with code ${exitCode}`;
|
|
288
289
|
}
|
|
289
290
|
|
|
291
|
+
const RAW_OUTPUT_ARTIFACT_PREFIX = "[raw output: artifact://";
|
|
292
|
+
const RAW_OUTPUT_ARTIFACT_SUFFIX = "]";
|
|
293
|
+
|
|
294
|
+
function stripRawOutputArtifactNotice(text: string): { text: string; artifactId?: string } {
|
|
295
|
+
const trimmed = text.trimEnd();
|
|
296
|
+
const lineStart = trimmed.lastIndexOf("\n");
|
|
297
|
+
const candidateStart = lineStart === -1 ? 0 : lineStart + 1;
|
|
298
|
+
if (
|
|
299
|
+
!trimmed.startsWith(RAW_OUTPUT_ARTIFACT_PREFIX, candidateStart) ||
|
|
300
|
+
!trimmed.endsWith(RAW_OUTPUT_ARTIFACT_SUFFIX)
|
|
301
|
+
) {
|
|
302
|
+
return { text };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const idStart = candidateStart + RAW_OUTPUT_ARTIFACT_PREFIX.length;
|
|
306
|
+
const idEnd = trimmed.length - RAW_OUTPUT_ARTIFACT_SUFFIX.length;
|
|
307
|
+
if (idStart === idEnd) return { text };
|
|
308
|
+
for (let i = idStart; i < idEnd; i++) {
|
|
309
|
+
const code = trimmed.charCodeAt(i);
|
|
310
|
+
if (code < 48 || code > 57) return { text };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const artifactId = trimmed.slice(idStart, idEnd);
|
|
314
|
+
return {
|
|
315
|
+
text: trimmed.slice(0, lineStart === -1 ? 0 : lineStart).trimEnd(),
|
|
316
|
+
artifactId,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
290
320
|
/**
|
|
291
321
|
* Strip the trailing occurrence of `notice` (plus a single surrounding newline
|
|
292
322
|
* on each side) so the TUI can echo the value via a styled footer label
|
|
@@ -692,6 +722,12 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
692
722
|
cwd = await expandInternalUrls(cwd, { ...internalUrlOptions, noEscape: true });
|
|
693
723
|
}
|
|
694
724
|
|
|
725
|
+
// Best-effort cache invalidation: drop github-cache rows for any issue/PR
|
|
726
|
+
// number touched by a mutating `gh` subcommand inside this bash call so
|
|
727
|
+
// subsequent issue:// / pr:// reads pick up the post-mutation state
|
|
728
|
+
// instead of the cached pre-mutation snapshot.
|
|
729
|
+
invalidateGithubCacheForBashCommand(command);
|
|
730
|
+
|
|
695
731
|
const commandCwd = cwd ? resolveToCwd(cwd, this.session.cwd) : this.session.cwd;
|
|
696
732
|
let cwdStat: fs.Stats;
|
|
697
733
|
try {
|
|
@@ -1028,6 +1064,7 @@ export interface ShellRendererConfig<TArgs> {
|
|
|
1028
1064
|
resolveCommand?: (args: TArgs | undefined) => string | undefined;
|
|
1029
1065
|
resolveCwd?: (args: TArgs | undefined) => string | undefined;
|
|
1030
1066
|
resolveEnv?: (args: TArgs | undefined) => Record<string, string> | undefined;
|
|
1067
|
+
showHeader?: boolean;
|
|
1031
1068
|
}
|
|
1032
1069
|
|
|
1033
1070
|
function getPartialJson<TArgs>(args: TArgs | undefined): string | undefined {
|
|
@@ -1079,9 +1116,11 @@ export function createShellRenderer<TArgs>(config: ShellRendererConfig<TArgs>) {
|
|
|
1079
1116
|
return {
|
|
1080
1117
|
renderCall(args: TArgs, options: RenderResultOptions, uiTheme: Theme): Component {
|
|
1081
1118
|
const renderArgs = toBashRenderArgs(args, config);
|
|
1082
|
-
const title = config.resolveTitle(args, options);
|
|
1083
1119
|
const cmdLines = formatBashCommandLines(renderArgs, uiTheme);
|
|
1084
|
-
const header =
|
|
1120
|
+
const header =
|
|
1121
|
+
config.showHeader === false
|
|
1122
|
+
? undefined
|
|
1123
|
+
: renderStatusLine({ icon: "pending", title: config.resolveTitle(args, options) }, uiTheme);
|
|
1085
1124
|
const outputBlock = new CachedOutputBlock();
|
|
1086
1125
|
return markFramedBlockComponent({
|
|
1087
1126
|
render: (width: number): string[] =>
|
|
@@ -1115,8 +1154,10 @@ export function createShellRenderer<TArgs>(config: ShellRendererConfig<TArgs>) {
|
|
|
1115
1154
|
const cmdLines = args ? formatBashCommandLines(renderArgs, uiTheme) : undefined;
|
|
1116
1155
|
const isError = result.isError === true;
|
|
1117
1156
|
const icon = options.isPartial ? "pending" : isError ? "error" : "success";
|
|
1118
|
-
const
|
|
1119
|
-
|
|
1157
|
+
const header =
|
|
1158
|
+
config.showHeader === false
|
|
1159
|
+
? undefined
|
|
1160
|
+
: renderStatusLine({ icon, title: config.resolveTitle(args, options) }, uiTheme);
|
|
1120
1161
|
const details = result.details;
|
|
1121
1162
|
const outputBlock = new CachedOutputBlock();
|
|
1122
1163
|
|
|
@@ -1133,7 +1174,9 @@ export function createShellRenderer<TArgs>(config: ShellRendererConfig<TArgs>) {
|
|
|
1133
1174
|
const rawOutput = renderContext?.output ?? result.content?.find(c => c.type === "text")?.text ?? "";
|
|
1134
1175
|
const strippedOutput = stripOutputNotice(rawOutput, details?.meta);
|
|
1135
1176
|
const withoutExit = stripExitCodeNotice(strippedOutput, details?.exitCode);
|
|
1136
|
-
const
|
|
1177
|
+
const withoutWall = stripWallTimeNotice(withoutExit, details?.wallTimeMs);
|
|
1178
|
+
const rawOutputArtifact = stripRawOutputArtifactNotice(withoutWall);
|
|
1179
|
+
const output = rawOutputArtifact.text;
|
|
1137
1180
|
const displayOutput = output.trimEnd();
|
|
1138
1181
|
const showingFullOutput = expanded && renderContext?.isFullOutput === true;
|
|
1139
1182
|
|
|
@@ -1152,6 +1195,9 @@ export function createShellRenderer<TArgs>(config: ShellRendererConfig<TArgs>) {
|
|
|
1152
1195
|
: `Timeout: ${timeoutSeconds}s`,
|
|
1153
1196
|
);
|
|
1154
1197
|
}
|
|
1198
|
+
if (rawOutputArtifact.artifactId) {
|
|
1199
|
+
statsParts.push(`Artifact: ${rawOutputArtifact.artifactId}`);
|
|
1200
|
+
}
|
|
1155
1201
|
if (isError && typeof details?.exitCode === "number") {
|
|
1156
1202
|
statsParts.push(`Exit: ${details.exitCode}`);
|
|
1157
1203
|
}
|
|
@@ -1215,7 +1261,11 @@ export function createShellRenderer<TArgs>(config: ShellRendererConfig<TArgs>) {
|
|
|
1215
1261
|
{ label: uiTheme.fg("toolTitle", "Output"), lines: outputLines },
|
|
1216
1262
|
],
|
|
1217
1263
|
width,
|
|
1218
|
-
animate:
|
|
1264
|
+
// Don't animate once the command has been backgrounded: the block
|
|
1265
|
+
// gets committed to scrollback and finalizes later via the async
|
|
1266
|
+
// update path, so a mid-sweep frame would freeze a stray dark
|
|
1267
|
+
// border segment.
|
|
1268
|
+
animate: options.isPartial && shimmerEnabled() && details?.async?.state !== "running",
|
|
1219
1269
|
},
|
|
1220
1270
|
uiTheme,
|
|
1221
1271
|
);
|
|
@@ -1235,4 +1285,5 @@ export const bashToolRenderer = createShellRenderer<BashRenderArgs>({
|
|
|
1235
1285
|
resolveCommand: args => args?.command,
|
|
1236
1286
|
resolveCwd: args => args?.cwd,
|
|
1237
1287
|
resolveEnv: args => args?.env,
|
|
1288
|
+
showHeader: false,
|
|
1238
1289
|
});
|
|
@@ -101,11 +101,23 @@ export async function acquireTab(
|
|
|
101
101
|
if (opts.dialogs !== undefined && opts.dialogs !== existing.dialogPolicy) {
|
|
102
102
|
await releaseTab(name, { kill: false });
|
|
103
103
|
} else {
|
|
104
|
+
const reuseSteps: string[] = [];
|
|
105
|
+
if (opts.viewport) {
|
|
106
|
+
const dsf = opts.viewport.deviceScaleFactor;
|
|
107
|
+
reuseSteps.push(
|
|
108
|
+
`await page.setViewport({ width: ${opts.viewport.width}, height: ${opts.viewport.height}, deviceScaleFactor: ${dsf === undefined ? "undefined" : String(dsf)} });`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
104
111
|
if (opts.url) {
|
|
112
|
+
reuseSteps.push(
|
|
113
|
+
`await tab.goto(${JSON.stringify(opts.url)}, { waitUntil: ${JSON.stringify(opts.waitUntil ?? "load")} });`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
if (reuseSteps.length) {
|
|
105
117
|
await runInTabWithSnapshot(
|
|
106
118
|
name,
|
|
107
119
|
{
|
|
108
|
-
code:
|
|
120
|
+
code: reuseSteps.join("\n"),
|
|
109
121
|
timeoutMs: opts.timeoutMs,
|
|
110
122
|
signal: opts.signal,
|
|
111
123
|
},
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
DEFAULT_VIEWPORT,
|
|
28
28
|
loadPuppeteerInWorker,
|
|
29
29
|
} from "./launch";
|
|
30
|
-
import { extractReadableFromHtml, type ReadableFormat
|
|
30
|
+
import { extractReadableFromHtml, type ReadableFormat } from "./readable";
|
|
31
31
|
import type {
|
|
32
32
|
Observation,
|
|
33
33
|
ObservationEntry,
|
|
@@ -97,7 +97,7 @@ interface TabApi {
|
|
|
97
97
|
): Promise<void>;
|
|
98
98
|
observe(opts?: { includeAll?: boolean; viewportOnly?: boolean }): Promise<Observation>;
|
|
99
99
|
screenshot(opts?: ScreenshotOptions): Promise<ScreenshotResult>;
|
|
100
|
-
extract(format?: ReadableFormat): Promise<
|
|
100
|
+
extract(format?: ReadableFormat): Promise<string>;
|
|
101
101
|
click(selector: string): Promise<void>;
|
|
102
102
|
type(selector: string, text: string): Promise<void>;
|
|
103
103
|
fill(selector: string, value: string): Promise<void>;
|
|
@@ -167,6 +167,25 @@ function cloneSafe(value: unknown): unknown {
|
|
|
167
167
|
return String(value);
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Strip `user:pass@` from a URL before surfacing it in tool outputs / details
|
|
172
|
+
* so Basic Auth credentials don't leak into transcripts. Returns the original
|
|
173
|
+
* string verbatim when it doesn't parse as a URL or when there are no
|
|
174
|
+
* credentials to redact.
|
|
175
|
+
*/
|
|
176
|
+
function redactUrlCredentials(url: string): string {
|
|
177
|
+
if (!url || (!url.includes("@") && !url.includes("//"))) return url;
|
|
178
|
+
try {
|
|
179
|
+
const parsed = new URL(url);
|
|
180
|
+
if (!parsed.username && !parsed.password) return url;
|
|
181
|
+
parsed.username = "";
|
|
182
|
+
parsed.password = "";
|
|
183
|
+
return parsed.toString();
|
|
184
|
+
} catch {
|
|
185
|
+
return url;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
170
189
|
function errorPayload(error: unknown): RunErrorPayload {
|
|
171
190
|
if (error instanceof ToolAbortError) {
|
|
172
191
|
return { name: error.name, message: error.message, stack: error.stack, isToolError: false, isAbort: true };
|
|
@@ -491,7 +510,7 @@ export class WorkerCore {
|
|
|
491
510
|
const targetId = this.#targetId ?? (await targetIdForPage(page));
|
|
492
511
|
this.#targetId = targetId;
|
|
493
512
|
return {
|
|
494
|
-
url: page.url(),
|
|
513
|
+
url: redactUrlCredentials(page.url()),
|
|
495
514
|
title: await page.title().catch(() => undefined),
|
|
496
515
|
viewport: page.viewport() ?? DEFAULT_VIEWPORT,
|
|
497
516
|
targetId,
|
|
@@ -677,7 +696,17 @@ export class WorkerCore {
|
|
|
677
696
|
screenshot: async opts => await this.#captureScreenshot(session, displays, screenshots, signal, opts),
|
|
678
697
|
extract: async (format = "markdown") => {
|
|
679
698
|
const html = (await untilAborted(signal, () => page.content())) as string;
|
|
680
|
-
|
|
699
|
+
const result = await extractReadableFromHtml(html, page.url(), format);
|
|
700
|
+
if (!result) {
|
|
701
|
+
throw new ToolError(`tab.extract(${JSON.stringify(format)}) found no readable content on ${page.url()}`);
|
|
702
|
+
}
|
|
703
|
+
const content = format === "markdown" ? result.markdown : result.text;
|
|
704
|
+
if (!content) {
|
|
705
|
+
throw new ToolError(
|
|
706
|
+
`tab.extract(${JSON.stringify(format)}) produced empty ${format} content for ${page.url()}`,
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
return content;
|
|
681
710
|
},
|
|
682
711
|
click: async selector => {
|
|
683
712
|
const resolved = normalizeSelector(selector);
|
package/src/tools/debug.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
type DapFunctionBreakpointRecord,
|
|
23
23
|
type DapInstructionBreakpointRecord,
|
|
24
24
|
type DapModule,
|
|
25
|
+
type DapResolvedAdapter,
|
|
25
26
|
type DapScope,
|
|
26
27
|
type DapSessionSummary,
|
|
27
28
|
type DapSource,
|
|
@@ -30,6 +31,8 @@ import {
|
|
|
30
31
|
type DapVariable,
|
|
31
32
|
dapSessionManager,
|
|
32
33
|
getAvailableAdapters,
|
|
34
|
+
type LaunchProgramKind,
|
|
35
|
+
resolveLaunchOverrides,
|
|
33
36
|
selectAttachAdapter,
|
|
34
37
|
selectLaunchAdapter,
|
|
35
38
|
} from "../dap";
|
|
@@ -489,16 +492,23 @@ function getConfiguredAdapters(cwd: string): string {
|
|
|
489
492
|
const adapters = getAvailableAdapters(cwd).map(adapter => adapter.name);
|
|
490
493
|
return adapters.length > 0 ? adapters.join(", ") : "none";
|
|
491
494
|
}
|
|
492
|
-
|
|
493
|
-
|
|
495
|
+
|
|
496
|
+
async function classifyLaunchProgram(program: string): Promise<LaunchProgramKind> {
|
|
494
497
|
try {
|
|
495
|
-
|
|
498
|
+
return (await fs.stat(program)).isDirectory() ? "directory" : "file";
|
|
496
499
|
} catch (error) {
|
|
497
|
-
if (isEnoent(error)) return;
|
|
500
|
+
if (isEnoent(error)) return "missing";
|
|
498
501
|
throw error;
|
|
499
502
|
}
|
|
500
|
-
|
|
503
|
+
}
|
|
501
504
|
|
|
505
|
+
function validateLaunchProgram(
|
|
506
|
+
program: string,
|
|
507
|
+
cwd: string,
|
|
508
|
+
programKind: LaunchProgramKind,
|
|
509
|
+
adapter: DapResolvedAdapter,
|
|
510
|
+
): void {
|
|
511
|
+
if (programKind !== "directory" || adapter.acceptsDirectoryProgram) return;
|
|
502
512
|
const displayPath = formatPathRelativeToCwd(program, cwd, { trailingSlash: true });
|
|
503
513
|
throw new ToolError(
|
|
504
514
|
`launch program resolves to a directory: ${displayPath}. Pass an executable file path, or for Python use adapter "debugpy" with program set to the .py file.`,
|
|
@@ -676,8 +686,8 @@ export class DebugTool implements AgentTool<typeof debugSchema, DebugToolDetails
|
|
|
676
686
|
}
|
|
677
687
|
const commandCwd = params.cwd ? resolveToCwd(params.cwd, this.session.cwd) : this.session.cwd;
|
|
678
688
|
const program = resolveToCwd(params.program, commandCwd);
|
|
679
|
-
await
|
|
680
|
-
const adapter = selectLaunchAdapter(program, commandCwd, params.adapter);
|
|
689
|
+
const programKind = await classifyLaunchProgram(program);
|
|
690
|
+
const adapter = selectLaunchAdapter(program, commandCwd, params.adapter, programKind);
|
|
681
691
|
if (!adapter) {
|
|
682
692
|
if (params.adapter === "debugpy") {
|
|
683
693
|
throw new ToolError("adapter 'debugpy' is not available: python not found in PATH");
|
|
@@ -686,8 +696,10 @@ export class DebugTool implements AgentTool<typeof debugSchema, DebugToolDetails
|
|
|
686
696
|
`No debugger adapter available. Installed adapters: ${getConfiguredAdapters(commandCwd)}`,
|
|
687
697
|
);
|
|
688
698
|
}
|
|
699
|
+
validateLaunchProgram(program, commandCwd, programKind, adapter);
|
|
700
|
+
const extraLaunchArguments = resolveLaunchOverrides(adapter, program, programKind);
|
|
689
701
|
const snapshot = await dapSessionManager.launch(
|
|
690
|
-
{ adapter, program, args: params.args, cwd: commandCwd },
|
|
702
|
+
{ adapter, program, args: params.args, cwd: commandCwd, extraLaunchArguments },
|
|
691
703
|
combinedSignal,
|
|
692
704
|
timeoutSec * 1000,
|
|
693
705
|
);
|
package/src/tools/eval.ts
CHANGED
|
@@ -88,12 +88,21 @@ function formatDisplayOutputsForText(outputs: EvalDisplayOutput[]): string {
|
|
|
88
88
|
export interface EvalToolDescriptionOptions {
|
|
89
89
|
py?: boolean;
|
|
90
90
|
js?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Whether `agent()` is allowed in this session. Driven by the parent's
|
|
93
|
+
* spawn policy (`getSessionSpawns`). Defaults to `true` for backward
|
|
94
|
+
* compatibility — when the session forbids spawning, the prelude doc
|
|
95
|
+
* omits the `agent()` entry so the model does not promise itself a
|
|
96
|
+
* helper that will only ever throw "spawns disabled".
|
|
97
|
+
*/
|
|
98
|
+
spawns?: boolean;
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
export function getEvalToolDescription(options: EvalToolDescriptionOptions = {}): string {
|
|
94
102
|
const py = options.py ?? true;
|
|
95
103
|
const js = options.js ?? true;
|
|
96
|
-
|
|
104
|
+
const spawns = options.spawns ?? true;
|
|
105
|
+
return prompt.render(evalDescription, { py, js, spawns });
|
|
97
106
|
}
|
|
98
107
|
|
|
99
108
|
export interface EvalToolOptions {
|
|
@@ -169,7 +178,9 @@ export class EvalTool implements AgentTool<typeof evalSchema> {
|
|
|
169
178
|
get description(): string {
|
|
170
179
|
if (!this.session) return getEvalToolDescription();
|
|
171
180
|
const backends = resolveEvalBackends(this.session);
|
|
172
|
-
|
|
181
|
+
const sessionSpawns = this.session.getSessionSpawns?.() ?? "*";
|
|
182
|
+
const spawnsAllowed = sessionSpawns !== "" && sessionSpawns !== null;
|
|
183
|
+
return getEvalToolDescription({ py: backends.python, js: backends.js, spawns: spawnsAllowed });
|
|
173
184
|
}
|
|
174
185
|
readonly parameters = evalSchema;
|
|
175
186
|
readonly concurrency = "exclusive";
|