@oh-my-pi/pi-coding-agent 15.0.1 → 15.1.0
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 +94 -1
- package/examples/custom-tools/README.md +11 -7
- package/examples/custom-tools/hello/index.ts +2 -2
- package/examples/extensions/README.md +19 -8
- package/examples/extensions/api-demo.ts +15 -19
- package/examples/extensions/hello.ts +5 -6
- package/examples/extensions/plan-mode.ts +1 -1
- package/examples/extensions/reload-runtime.ts +4 -3
- package/examples/extensions/with-deps/index.ts +4 -3
- package/examples/sdk/06-extensions.ts +4 -2
- package/package.json +8 -18
- package/src/autoresearch/tools/init-experiment.ts +38 -41
- package/src/autoresearch/tools/log-experiment.ts +32 -41
- package/src/autoresearch/tools/run-experiment.ts +3 -3
- package/src/autoresearch/tools/update-notes.ts +11 -11
- package/src/commands/commit.ts +10 -0
- package/src/commit/agentic/tools/analyze-file.ts +4 -4
- package/src/commit/agentic/tools/git-file-diff.ts +4 -4
- package/src/commit/agentic/tools/git-hunk.ts +5 -5
- package/src/commit/agentic/tools/git-overview.ts +4 -4
- package/src/commit/agentic/tools/propose-changelog.ts +13 -13
- package/src/commit/agentic/tools/propose-commit.ts +6 -6
- package/src/commit/agentic/tools/recent-commits.ts +3 -3
- package/src/commit/agentic/tools/schemas.ts +28 -28
- package/src/commit/agentic/tools/split-commit.ts +22 -21
- package/src/commit/analysis/summary.ts +4 -4
- package/src/commit/changelog/generate.ts +7 -11
- package/src/commit/shared-llm.ts +22 -34
- package/src/config/config-file.ts +35 -13
- package/src/config/model-registry.ts +40 -191
- package/src/config/models-config-schema.ts +166 -0
- package/src/config/settings-schema.ts +29 -0
- package/src/discovery/claude-plugins.ts +19 -7
- package/src/edit/index.ts +2 -2
- package/src/edit/modes/apply-patch.ts +7 -6
- package/src/edit/modes/patch.ts +18 -25
- package/src/edit/modes/replace.ts +18 -20
- package/src/eval/js/shared/rewrite-imports.ts +131 -10
- package/src/eval/py/executor.ts +233 -623
- package/src/eval/py/kernel.ts +27 -2
- package/src/eval/py/runner.py +42 -11
- package/src/eval/py/runtime.ts +1 -0
- package/src/exa/factory.ts +5 -4
- package/src/exa/mcp-client.ts +1 -1
- package/src/exa/researcher.ts +9 -20
- package/src/exa/search.ts +26 -52
- package/src/exa/types.ts +1 -1
- package/src/exa/websets.ts +54 -53
- package/src/exec/bash-executor.ts +2 -1
- package/src/extensibility/custom-commands/loader.ts +5 -3
- package/src/extensibility/custom-commands/types.ts +4 -2
- package/src/extensibility/custom-tools/loader.ts +5 -3
- package/src/extensibility/custom-tools/types.ts +7 -6
- package/src/extensibility/custom-tools/wrapper.ts +1 -1
- package/src/extensibility/extensions/get-commands-handler.ts +77 -0
- package/src/extensibility/extensions/loader.ts +7 -3
- package/src/extensibility/extensions/types.ts +9 -5
- package/src/extensibility/extensions/wrapper.ts +1 -2
- package/src/extensibility/hooks/loader.ts +3 -1
- package/src/extensibility/hooks/tool-wrapper.ts +1 -1
- package/src/extensibility/hooks/types.ts +4 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +78 -31
- package/src/extensibility/shared-events.ts +1 -1
- package/src/extensibility/typebox.ts +391 -0
- package/src/goals/tools/goal-tool.ts +6 -12
- package/src/hashline/input.ts +2 -1
- package/src/hashline/parser.ts +27 -3
- package/src/hashline/types.ts +4 -4
- package/src/hindsight/state.ts +2 -2
- package/src/index.ts +0 -2
- package/src/internal-urls/docs-index.generated.ts +15 -15
- package/src/internal-urls/router.ts +8 -0
- package/src/internal-urls/types.ts +21 -0
- package/src/lsp/config.ts +15 -6
- package/src/lsp/defaults.json +6 -2
- package/src/lsp/types.ts +30 -38
- package/src/mcp/manager.ts +1 -1
- package/src/mcp/tool-bridge.ts +1 -1
- package/src/modes/acp/acp-agent.ts +248 -50
- package/src/modes/components/session-observer-overlay.ts +12 -1
- package/src/modes/components/status-line/segments.ts +39 -4
- package/src/modes/controllers/command-controller.ts +27 -2
- package/src/modes/controllers/event-controller.ts +3 -4
- package/src/modes/controllers/extension-ui-controller.ts +3 -2
- package/src/modes/interactive-mode.ts +1 -1
- package/src/modes/rpc/host-tools.ts +1 -1
- package/src/modes/rpc/host-uris.ts +235 -0
- package/src/modes/rpc/rpc-client.ts +1 -1
- package/src/modes/rpc/rpc-mode.ts +27 -1
- package/src/modes/rpc/rpc-types.ts +58 -1
- package/src/modes/runtime-init.ts +2 -1
- package/src/modes/theme/defaults/dark-poimandres.json +1 -0
- package/src/modes/theme/defaults/light-poimandres.json +1 -0
- package/src/modes/theme/theme.ts +117 -117
- package/src/modes/types.ts +1 -1
- package/src/modes/utils/context-usage.ts +2 -2
- package/src/prompts/tools/github.md +4 -4
- package/src/prompts/tools/hashline.md +22 -26
- package/src/prompts/tools/read.md +55 -37
- package/src/sdk.ts +31 -8
- package/src/session/agent-session.ts +74 -104
- package/src/session/messages.ts +16 -51
- package/src/session/session-manager.ts +22 -2
- package/src/session/streaming-output.ts +16 -6
- package/src/task/discovery.ts +5 -2
- package/src/task/executor.ts +210 -87
- package/src/task/index.ts +15 -11
- package/src/task/render.ts +32 -5
- package/src/task/types.ts +54 -39
- package/src/tools/ask.ts +12 -12
- package/src/tools/ast-edit.ts +11 -15
- package/src/tools/ast-grep.ts +9 -10
- package/src/tools/bash-command-fixup.ts +47 -0
- package/src/tools/bash.ts +48 -38
- package/src/tools/browser/render.ts +2 -2
- package/src/tools/browser.ts +39 -53
- package/src/tools/calculator.ts +12 -11
- package/src/tools/checkpoint.ts +7 -7
- package/src/tools/debug.ts +40 -43
- package/src/tools/eval.ts +16 -10
- package/src/tools/find.ts +10 -13
- package/src/tools/gh.ts +108 -132
- package/src/tools/hindsight-recall.ts +4 -6
- package/src/tools/hindsight-reflect.ts +5 -5
- package/src/tools/hindsight-retain.ts +15 -17
- package/src/tools/image-gen.ts +31 -81
- package/src/tools/index.ts +4 -1
- package/src/tools/inspect-image.ts +8 -9
- package/src/tools/irc.ts +15 -27
- package/src/tools/job.ts +30 -28
- package/src/tools/output-meta.ts +26 -0
- package/src/tools/read.ts +39 -12
- package/src/tools/recipe/index.ts +7 -9
- package/src/tools/render-mermaid.ts +12 -12
- package/src/tools/report-tool-issue.ts +4 -4
- package/src/tools/resolve.ts +11 -11
- package/src/tools/review.ts +14 -26
- package/src/tools/search-tool-bm25.ts +7 -9
- package/src/tools/search.ts +19 -22
- package/src/tools/ssh.ts +10 -9
- package/src/tools/todo-write.ts +26 -34
- package/src/tools/vim.ts +10 -26
- package/src/tools/write.ts +25 -5
- package/src/tools/yield.ts +100 -54
- package/src/web/search/index.ts +9 -24
- package/src/web/search/providers/anthropic.ts +5 -0
- package/src/web/search/providers/exa.ts +3 -0
- package/src/web/search/providers/gemini.ts +5 -0
- package/src/web/search/providers/jina.ts +5 -2
- package/src/web/search/providers/zai.ts +5 -2
- package/src/prompts/compaction/branch-summary-context.md +0 -5
- package/src/prompts/compaction/branch-summary-preamble.md +0 -2
- package/src/prompts/compaction/branch-summary.md +0 -30
- package/src/prompts/compaction/compaction-short-summary.md +0 -9
- package/src/prompts/compaction/compaction-summary-context.md +0 -5
- package/src/prompts/compaction/compaction-summary.md +0 -38
- package/src/prompts/compaction/compaction-turn-prefix.md +0 -17
- package/src/prompts/compaction/compaction-update-summary.md +0 -45
- package/src/prompts/system/auto-handoff-threshold-focus.md +0 -1
- package/src/prompts/system/file-operations.md +0 -10
- package/src/prompts/system/handoff-document.md +0 -49
- package/src/prompts/system/summarization-system.md +0 -3
- package/src/session/compaction/branch-summarization.ts +0 -324
- package/src/session/compaction/compaction.ts +0 -1420
- package/src/session/compaction/errors.ts +0 -31
- package/src/session/compaction/index.ts +0 -8
- package/src/session/compaction/pruning.ts +0 -91
- package/src/session/compaction/utils.ts +0 -184
package/src/tools/job.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
2
2
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
3
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import
|
|
5
|
+
import * as z from "zod/v4";
|
|
6
6
|
import { type AsyncJob, AsyncJobManager, isBackgroundJobSupportEnabled } from "../async";
|
|
7
7
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
8
8
|
import type { Theme } from "../modes/theme/theme";
|
|
@@ -22,28 +22,21 @@ import {
|
|
|
22
22
|
} from "./render-utils";
|
|
23
23
|
import { ToolError } from "./tool-errors";
|
|
24
24
|
|
|
25
|
-
const jobSchema =
|
|
26
|
-
poll:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
),
|
|
38
|
-
list: Type.Optional(
|
|
39
|
-
Type.Boolean({
|
|
40
|
-
description:
|
|
41
|
-
"Return an immediate snapshot of every job spawned by this agent (running + completed within retention). Read-only \u2014 cannot be combined with `poll` or `cancel`.",
|
|
42
|
-
}),
|
|
43
|
-
),
|
|
25
|
+
const jobSchema = z.object({
|
|
26
|
+
poll: z
|
|
27
|
+
.array(z.string())
|
|
28
|
+
.optional()
|
|
29
|
+
.describe("background job ids to wait for; omit (with no `cancel`) to wait on all running jobs"),
|
|
30
|
+
cancel: z.array(z.string()).optional().describe("background job ids to cancel"),
|
|
31
|
+
list: z
|
|
32
|
+
.boolean()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe(
|
|
35
|
+
"Return an immediate snapshot of every job spawned by this agent (running + completed within retention). Read-only \u2014 cannot be combined with `poll` or `cancel`.",
|
|
36
|
+
),
|
|
44
37
|
});
|
|
45
38
|
|
|
46
|
-
type JobParams =
|
|
39
|
+
type JobParams = z.infer<typeof jobSchema>;
|
|
47
40
|
|
|
48
41
|
const WAIT_DURATION_MS: Record<string, number> = {
|
|
49
42
|
"5s": 5_000,
|
|
@@ -362,6 +355,8 @@ const COLLAPSED_LIST_LIMIT = PREVIEW_LIMITS.COLLAPSED_ITEMS;
|
|
|
362
355
|
const LABEL_MAX_WIDTH = 60;
|
|
363
356
|
const PREVIEW_LINES_COLLAPSED = 1;
|
|
364
357
|
const PREVIEW_LINES_EXPANDED = 4;
|
|
358
|
+
const LABEL_LINES_COLLAPSED = 1;
|
|
359
|
+
const LABEL_LINES_EXPANDED = 3;
|
|
365
360
|
const PREVIEW_LINE_WIDTH = 80;
|
|
366
361
|
|
|
367
362
|
function statusToIcon(status: JobSnapshot["status"]): ToolUIStatus {
|
|
@@ -488,14 +483,21 @@ export const jobToolRenderer = {
|
|
|
488
483
|
);
|
|
489
484
|
const typeBadge = formatBadge(job.type, statusToColor(job.status), uiTheme);
|
|
490
485
|
const idText = uiTheme.fg("muted", job.id);
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
486
|
+
const rawLabelLines = (job.label || "(no label)").split(/\r?\n/);
|
|
487
|
+
const maxLabelLines = expanded ? LABEL_LINES_EXPANDED : LABEL_LINES_COLLAPSED;
|
|
488
|
+
const visibleLabelLines = rawLabelLines
|
|
489
|
+
.slice(0, maxLabelLines)
|
|
490
|
+
.map(l => truncateToWidth(replaceTabs(l), LABEL_MAX_WIDTH, Ellipsis.Unicode));
|
|
491
|
+
if (rawLabelLines.length > maxLabelLines && visibleLabelLines.length > 0) {
|
|
492
|
+
const last = visibleLabelLines[visibleLabelLines.length - 1]!;
|
|
493
|
+
visibleLabelLines[visibleLabelLines.length - 1] = `${last} …`;
|
|
494
|
+
}
|
|
497
495
|
const durationText = uiTheme.fg("dim", formatDuration(job.durationMs));
|
|
498
|
-
|
|
496
|
+
const headLabel = uiTheme.fg("toolOutput", visibleLabelLines[0] ?? "");
|
|
497
|
+
lines.push(`${icon} ${idText} ${typeBadge} ${headLabel} ${durationText}`);
|
|
498
|
+
for (let i = 1; i < visibleLabelLines.length; i++) {
|
|
499
|
+
lines.push(` ${uiTheme.fg("toolOutput", visibleLabelLines[i]!)}`);
|
|
500
|
+
}
|
|
499
501
|
|
|
500
502
|
const preview = job.errorText?.trim() || job.resultText?.trim();
|
|
501
503
|
if (preview) {
|
package/src/tools/output-meta.ts
CHANGED
|
@@ -489,6 +489,32 @@ export function formatStyledTruncationWarning(meta: OutputMeta | undefined, them
|
|
|
489
489
|
return theme.fg("warning", wrapBrackets(message, theme));
|
|
490
490
|
}
|
|
491
491
|
|
|
492
|
+
/**
|
|
493
|
+
* Strip the trailing notice that {@link appendOutputNotice} bakes into the
|
|
494
|
+
* LLM-facing content body. Renderers should call this before printing
|
|
495
|
+
* `result.content` text in the TUI, because they emit a styled warning line of
|
|
496
|
+
* their own; without this, users see the same `[Showing lines …]` string twice
|
|
497
|
+
* (once verbatim from the body, once as the styled `⟨…⟩` warning).
|
|
498
|
+
*
|
|
499
|
+
* Safe to call eagerly: returns the input unchanged when no notice is present
|
|
500
|
+
* (e.g. during streaming, before {@link wrappedExecute} runs).
|
|
501
|
+
*/
|
|
502
|
+
export function stripOutputNotice(text: string, meta: OutputMeta | undefined): string {
|
|
503
|
+
const notice = formatOutputNotice(meta);
|
|
504
|
+
if (!notice) return text;
|
|
505
|
+
// Trim trailing whitespace from `text` and from the notice itself so we
|
|
506
|
+
// match regardless of whether: (a) the caller already trimEnd()'d, (b)
|
|
507
|
+
// extra blank lines slipped in after the notice (diagnostics blocks add
|
|
508
|
+
// `\n\n` between sections, OutputSink may pad), or (c) neither. Returns
|
|
509
|
+
// the prefix before the notice so the caller can re-trim as needed.
|
|
510
|
+
const trimmedText = text.trimEnd();
|
|
511
|
+
const trimmedNotice = notice.trimEnd();
|
|
512
|
+
if (trimmedText.endsWith(trimmedNotice)) {
|
|
513
|
+
return trimmedText.slice(0, -trimmedNotice.length);
|
|
514
|
+
}
|
|
515
|
+
return text;
|
|
516
|
+
}
|
|
517
|
+
|
|
492
518
|
// =============================================================================
|
|
493
519
|
// Tool wrapper
|
|
494
520
|
// =============================================================================
|
package/src/tools/read.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { glob, type SummaryResult, summarizeCode } from "@oh-my-pi/pi-natives";
|
|
|
7
7
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
8
8
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
9
9
|
import { getRemoteDir, logger, prompt, readImageMetadata, untilAborted } from "@oh-my-pi/pi-utils";
|
|
10
|
-
import
|
|
10
|
+
import * as z from "zod/v4";
|
|
11
11
|
import { getFileReadCache } from "../edit/file-read-cache";
|
|
12
12
|
import { isNotebookPath, readEditableNotebookText } from "../edit/notebook";
|
|
13
13
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
formatStyledTruncationWarning,
|
|
61
61
|
type OutputMeta,
|
|
62
62
|
resolveOutputMaxColumns,
|
|
63
|
+
stripOutputNotice,
|
|
63
64
|
} from "./output-meta";
|
|
64
65
|
import { expandPath, formatPathRelativeToCwd, resolveReadPath, splitPathAndSel } from "./path-utils";
|
|
65
66
|
import { formatBytes, replaceTabs, shortenPath, wrapBrackets } from "./render-utils";
|
|
@@ -172,6 +173,19 @@ function countTextLines(text: string): number {
|
|
|
172
173
|
if (text.length === 0) return 0;
|
|
173
174
|
return text.split("\n").length;
|
|
174
175
|
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Footer appended to summarized reads telling the model how to recover the
|
|
179
|
+
* elided body. Without this hint, agents either ignore the `...`/`{ .. }`
|
|
180
|
+
* markers or burn a turn guessing the right selector (see issue #1046).
|
|
181
|
+
*/
|
|
182
|
+
function formatSummaryElisionFooter(readPath: string, elidedSpans: number, elidedLines: number): string {
|
|
183
|
+
if (elidedSpans <= 0) return "";
|
|
184
|
+
const spanWord = elidedSpans === 1 ? "region" : "regions";
|
|
185
|
+
const lineWord = elidedLines === 1 ? "line" : "lines";
|
|
186
|
+
const linePart = elidedLines > 0 ? `${elidedLines} ${lineWord} across ` : "";
|
|
187
|
+
return `[${linePart}${elidedSpans} elided ${spanWord}; read ${readPath}:raw or a line range like ${readPath}:1-9999 for verbatim content]`;
|
|
188
|
+
}
|
|
175
189
|
const READ_CHUNK_SIZE = 8 * 1024;
|
|
176
190
|
|
|
177
191
|
/**
|
|
@@ -459,14 +473,13 @@ function prependSuffixResolutionNotice(text: string, suffixResolution?: { from:
|
|
|
459
473
|
return text ? `${notice}\n${text}` : notice;
|
|
460
474
|
}
|
|
461
475
|
|
|
462
|
-
const readSchema =
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
});
|
|
476
|
+
const readSchema = z
|
|
477
|
+
.object({
|
|
478
|
+
path: z.string().describe('path or url; append :<sel> for line ranges or raw mode (e.g. "src/foo.ts:50-100")'),
|
|
479
|
+
})
|
|
480
|
+
.strict();
|
|
468
481
|
|
|
469
|
-
export type ReadToolInput =
|
|
482
|
+
export type ReadToolInput = z.infer<typeof readSchema>;
|
|
470
483
|
|
|
471
484
|
export interface ReadToolDetails {
|
|
472
485
|
kind?: "file" | "url";
|
|
@@ -484,7 +497,7 @@ export interface ReadToolDetails {
|
|
|
484
497
|
* Mirrors the same lines the model receives but without hashline/line-number prefixes,
|
|
485
498
|
* so the TUI can render the file content with its own gutter without re-parsing the formatted text. */
|
|
486
499
|
displayContent?: { text: string; startLine: number };
|
|
487
|
-
summary?: { lines: number; elidedSpans: number };
|
|
500
|
+
summary?: { lines: number; elidedSpans: number; elidedLines: number };
|
|
488
501
|
/** Number of unresolved git conflicts surfaced by this read (TUI uses for inline `⚠ N` badge). */
|
|
489
502
|
conflictCount?: number;
|
|
490
503
|
}
|
|
@@ -1317,6 +1330,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1317
1330
|
text: string;
|
|
1318
1331
|
displayText: string;
|
|
1319
1332
|
elidedSpans: number;
|
|
1333
|
+
elidedLines: number;
|
|
1320
1334
|
} {
|
|
1321
1335
|
const displayMode = resolveFileDisplayMode(this.session);
|
|
1322
1336
|
const shouldAddHashLines = displayMode.hashLines;
|
|
@@ -1377,11 +1391,13 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1377
1391
|
const modelParts: string[] = [];
|
|
1378
1392
|
const displayParts: string[] = [];
|
|
1379
1393
|
let elidedSpans = 0;
|
|
1394
|
+
let elidedLines = 0;
|
|
1380
1395
|
for (const unit of units) {
|
|
1381
1396
|
if (unit.kind === "elided") {
|
|
1382
1397
|
modelParts.push("...");
|
|
1383
1398
|
displayParts.push("...");
|
|
1384
1399
|
elidedSpans++;
|
|
1400
|
+
elidedLines += unit.endLine - unit.startLine + 1;
|
|
1385
1401
|
continue;
|
|
1386
1402
|
}
|
|
1387
1403
|
if (unit.kind === "merged") {
|
|
@@ -1396,13 +1412,15 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1396
1412
|
modelParts.push(formatted.model);
|
|
1397
1413
|
displayParts.push(formatted.display);
|
|
1398
1414
|
elidedSpans++;
|
|
1415
|
+
// Merged brace pair encloses (start+1)..(end-1) as elided.
|
|
1416
|
+
elidedLines += Math.max(0, unit.endLine - unit.startLine - 1);
|
|
1399
1417
|
continue;
|
|
1400
1418
|
}
|
|
1401
1419
|
modelParts.push(formatSingleLine(unit.line, unit.text, shouldAddHashLines, shouldAddLineNumbers));
|
|
1402
1420
|
displayParts.push(unit.text);
|
|
1403
1421
|
}
|
|
1404
1422
|
|
|
1405
|
-
return { text: modelParts.join("\n"), displayText: displayParts.join("\n"), elidedSpans };
|
|
1423
|
+
return { text: modelParts.join("\n"), displayText: displayParts.join("\n"), elidedSpans, elidedLines };
|
|
1406
1424
|
}
|
|
1407
1425
|
|
|
1408
1426
|
async execute(
|
|
@@ -1646,16 +1664,23 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1646
1664
|
const summary = await this.#trySummarize(absolutePath, fileSize, signal);
|
|
1647
1665
|
if (summary?.parsed && summary.elided) {
|
|
1648
1666
|
const renderedSummary = this.#renderSummary(summary);
|
|
1667
|
+
const footer = formatSummaryElisionFooter(
|
|
1668
|
+
localReadPath,
|
|
1669
|
+
renderedSummary.elidedSpans,
|
|
1670
|
+
renderedSummary.elidedLines,
|
|
1671
|
+
);
|
|
1672
|
+
const modelText = footer ? `${renderedSummary.text}\n\n${footer}` : renderedSummary.text;
|
|
1649
1673
|
details = {
|
|
1650
1674
|
displayContent: { text: renderedSummary.displayText, startLine: 1 },
|
|
1651
1675
|
summary: {
|
|
1652
1676
|
lines: countTextLines(renderedSummary.text),
|
|
1653
1677
|
elidedSpans: renderedSummary.elidedSpans,
|
|
1678
|
+
elidedLines: renderedSummary.elidedLines,
|
|
1654
1679
|
},
|
|
1655
1680
|
};
|
|
1656
1681
|
|
|
1657
1682
|
sourcePath = absolutePath;
|
|
1658
|
-
content = [{ type: "text", text:
|
|
1683
|
+
content = [{ type: "text", text: modelText }];
|
|
1659
1684
|
}
|
|
1660
1685
|
}
|
|
1661
1686
|
|
|
@@ -2169,7 +2194,9 @@ export const readToolRenderer = {
|
|
|
2169
2194
|
const rawText = result.content?.find(c => c.type === "text")?.text ?? "";
|
|
2170
2195
|
// Prefer structured `displayContent` from details when available so the TUI
|
|
2171
2196
|
// shows clean file content (no model-only hashline anchors) without parsing the formatted text.
|
|
2172
|
-
|
|
2197
|
+
// Fall back to the raw text, but strip the LLM-facing notice so it doesn't
|
|
2198
|
+
// echo next to the styled warning line below.
|
|
2199
|
+
const contentText = details?.displayContent?.text ?? stripOutputNotice(rawText, details?.meta);
|
|
2173
2200
|
const imageContent = result.content?.find(c => c.type === "image");
|
|
2174
2201
|
const rawPath = args?.file_path || args?.path || "";
|
|
2175
2202
|
const filePath = shortenPath(rawPath);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
3
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
4
|
-
import
|
|
4
|
+
import * as z from "zod/v4";
|
|
5
5
|
import type { RenderResultOptions } from "../../extensibility/custom-tools/types";
|
|
6
6
|
import type { Theme } from "../../modes/theme/theme";
|
|
7
7
|
import recipeDescription from "../../prompts/tools/recipe.md" with { type: "text" };
|
|
@@ -11,14 +11,12 @@ import { createRecipeToolRenderer, type RecipeRenderArgs } from "./render";
|
|
|
11
11
|
import { buildPromptModel, type DetectedRunner, resolveCommand } from "./runner";
|
|
12
12
|
import { RUNNERS } from "./runners";
|
|
13
13
|
|
|
14
|
-
const recipeSchema =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
type RecipeParams = Static<typeof recipeSchema>;
|
|
14
|
+
const recipeSchema = z
|
|
15
|
+
.object({
|
|
16
|
+
op: z.string().describe('task name and args, e.g. "test" or "build --release"'),
|
|
17
|
+
})
|
|
18
|
+
.strict();
|
|
19
|
+
type RecipeParams = z.infer<typeof recipeSchema>;
|
|
22
20
|
|
|
23
21
|
type RecipeRenderResult = {
|
|
24
22
|
content: Array<{ type: string; text?: string }>;
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import { type MermaidAsciiRenderOptions, prompt, renderMermaidAscii } from "@oh-my-pi/pi-utils";
|
|
3
|
-
import
|
|
3
|
+
import * as z from "zod/v4";
|
|
4
4
|
import renderMermaidDescription from "../prompts/tools/render-mermaid.md" with { type: "text" };
|
|
5
5
|
import type { ToolSession } from "./index";
|
|
6
6
|
|
|
7
|
-
const renderMermaidSchema =
|
|
8
|
-
mermaid:
|
|
9
|
-
config:
|
|
10
|
-
|
|
11
|
-
useAscii:
|
|
12
|
-
paddingX:
|
|
13
|
-
paddingY:
|
|
14
|
-
boxBorderPadding:
|
|
15
|
-
})
|
|
16
|
-
|
|
7
|
+
const renderMermaidSchema = z.object({
|
|
8
|
+
mermaid: z.string().describe("mermaid source"),
|
|
9
|
+
config: z
|
|
10
|
+
.object({
|
|
11
|
+
useAscii: z.boolean().optional(),
|
|
12
|
+
paddingX: z.number().optional(),
|
|
13
|
+
paddingY: z.number().optional(),
|
|
14
|
+
boxBorderPadding: z.number().optional(),
|
|
15
|
+
})
|
|
16
|
+
.optional(),
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
type RenderMermaidParams =
|
|
19
|
+
type RenderMermaidParams = z.infer<typeof renderMermaidSchema>;
|
|
20
20
|
|
|
21
21
|
function sanitizeRenderConfig(config: MermaidAsciiRenderOptions | undefined): MermaidAsciiRenderOptions | undefined {
|
|
22
22
|
if (!config) return undefined;
|
|
@@ -9,13 +9,13 @@ import { Database } from "bun:sqlite";
|
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
11
11
|
import { $flag, getAgentDir, logger, VERSION } from "@oh-my-pi/pi-utils";
|
|
12
|
-
import
|
|
12
|
+
import * as z from "zod/v4";
|
|
13
13
|
import type { Settings } from "..";
|
|
14
14
|
import type { ToolSession } from "./index";
|
|
15
15
|
|
|
16
|
-
const ReportToolIssueParams =
|
|
17
|
-
tool:
|
|
18
|
-
report:
|
|
16
|
+
const ReportToolIssueParams = z.object({
|
|
17
|
+
tool: z.string().describe("tool name"),
|
|
18
|
+
report: z.string().describe("unexpected behavior"),
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
export function isAutoQaEnabled(settings?: Settings): boolean {
|
package/src/tools/resolve.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
2
2
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
3
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import
|
|
5
|
+
import * as z from "zod/v4";
|
|
6
6
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
7
7
|
import type { Theme } from "../modes/theme/theme";
|
|
8
8
|
import resolveDescription from "../prompts/tools/resolve.md" with { type: "text" };
|
|
@@ -11,18 +11,18 @@ import type { ToolSession } from ".";
|
|
|
11
11
|
import { replaceTabs } from "./render-utils";
|
|
12
12
|
import { ToolError } from "./tool-errors";
|
|
13
13
|
|
|
14
|
-
const resolveSchema =
|
|
15
|
-
action:
|
|
16
|
-
reason:
|
|
17
|
-
extra:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
const resolveSchema = z.object({
|
|
15
|
+
action: z.union([z.literal("apply"), z.literal("discard")]),
|
|
16
|
+
reason: z.string().describe("reason for action"),
|
|
17
|
+
extra: z
|
|
18
|
+
.record(z.string(), z.unknown())
|
|
19
|
+
.optional()
|
|
20
|
+
.describe(
|
|
21
|
+
'Free-form metadata interpreted by the resolving tool (e.g. plan-mode approval requires `{ title: "<PLAN_TITLE>" }`).',
|
|
22
|
+
),
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
type ResolveParams =
|
|
25
|
+
type ResolveParams = z.infer<typeof resolveSchema>;
|
|
26
26
|
|
|
27
27
|
export interface ResolveToolDetails {
|
|
28
28
|
action: "apply" | "discard";
|
package/src/tools/review.ts
CHANGED
|
@@ -6,18 +6,15 @@
|
|
|
6
6
|
* Reviewers finish via `yield` tool with SubmitReviewDetails schema.
|
|
7
7
|
*/
|
|
8
8
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
9
|
-
|
|
10
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
11
10
|
import path from "node:path";
|
|
12
11
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
13
|
-
import { StringEnum } from "@oh-my-pi/pi-ai";
|
|
14
12
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
15
13
|
import { Container, Text } from "@oh-my-pi/pi-tui";
|
|
16
14
|
import { isRecord } from "@oh-my-pi/pi-utils";
|
|
17
|
-
import
|
|
15
|
+
import * as z from "zod/v4";
|
|
18
16
|
import type { Theme, ThemeColor } from "../modes/theme/theme";
|
|
19
17
|
import { subprocessToolRegistry } from "../task/subprocess-tool-registry";
|
|
20
|
-
|
|
21
18
|
export type FindingPriority = "P0" | "P1" | "P2" | "P3";
|
|
22
19
|
|
|
23
20
|
export interface FindingPriorityInfo {
|
|
@@ -53,27 +50,18 @@ function getPriorityDisplay(
|
|
|
53
50
|
}
|
|
54
51
|
|
|
55
52
|
// report_finding schema
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
minimum: 0,
|
|
69
|
-
maximum: 1,
|
|
70
|
-
description: "confidence score",
|
|
71
|
-
examples: [0.0, 0.5, 1.0],
|
|
72
|
-
}),
|
|
73
|
-
file_path: Type.String({ description: "file path" }),
|
|
74
|
-
line_start: Type.Number({ description: "start line" }),
|
|
75
|
-
line_end: Type.Number({ description: "end line" }),
|
|
76
|
-
});
|
|
53
|
+
// report_finding schema
|
|
54
|
+
const ReportFindingParams = z
|
|
55
|
+
.object({
|
|
56
|
+
title: z.string().describe("prefixed imperative title"),
|
|
57
|
+
body: z.string().describe("problem explanation"),
|
|
58
|
+
priority: z.enum(["P0", "P1", "P2", "P3"] as const).describe("priority 0-3"),
|
|
59
|
+
confidence: z.number().min(0).max(1).describe("confidence score"),
|
|
60
|
+
file_path: z.string().describe("file path"),
|
|
61
|
+
line_start: z.number().describe("start line"),
|
|
62
|
+
line_end: z.number().describe("end line"),
|
|
63
|
+
})
|
|
64
|
+
.strict();
|
|
77
65
|
|
|
78
66
|
interface ReportFindingDetails {
|
|
79
67
|
title: string;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import {
|
|
2
|
+
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
|
+
import { Text } from "@oh-my-pi/pi-tui";
|
|
3
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
4
|
-
import
|
|
5
|
+
import * as z from "zod/v4";
|
|
5
6
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
6
7
|
import type { Theme } from "../modes/theme/theme";
|
|
7
8
|
import searchToolBm25Description from "../prompts/tools/search-tool-bm25.md" with { type: "text" };
|
|
@@ -30,15 +31,12 @@ const COLLAPSED_MATCH_LIMIT = 5;
|
|
|
30
31
|
const MATCH_LABEL_LEN = 72;
|
|
31
32
|
const MATCH_DESCRIPTION_LEN = 96;
|
|
32
33
|
|
|
33
|
-
const searchToolBm25Schema =
|
|
34
|
-
query:
|
|
35
|
-
|
|
36
|
-
examples: ["kubernetes pod", "image processing", "git commit"],
|
|
37
|
-
}),
|
|
38
|
-
limit: Type.Optional(Type.Integer({ description: "max matches", minimum: 1 })),
|
|
34
|
+
const searchToolBm25Schema = z.object({
|
|
35
|
+
query: z.string().describe("tool search query"),
|
|
36
|
+
limit: z.number().int().min(1).optional().describe("max matches"),
|
|
39
37
|
});
|
|
40
38
|
|
|
41
|
-
type SearchToolBm25Params =
|
|
39
|
+
type SearchToolBm25Params = z.infer<typeof searchToolBm25Schema>;
|
|
42
40
|
|
|
43
41
|
interface SearchToolBm25Match {
|
|
44
42
|
name: string;
|
package/src/tools/search.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
3
|
-
|
|
4
3
|
import { type GrepMatch, GrepOutputMode, type GrepResult, grep } from "@oh-my-pi/pi-natives";
|
|
5
4
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
6
5
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
7
6
|
import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
8
|
-
import
|
|
7
|
+
import * as z from "zod/v4";
|
|
9
8
|
import { getFileReadCache } from "../edit/file-read-cache";
|
|
10
9
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
11
10
|
import type { Theme } from "../modes/theme/theme";
|
|
@@ -31,25 +30,23 @@ import {
|
|
|
31
30
|
import { ToolError } from "./tool-errors";
|
|
32
31
|
import { toolResult } from "./tool-result";
|
|
33
32
|
|
|
34
|
-
const searchSchema =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
export type SearchToolInput = Static<typeof searchSchema>;
|
|
33
|
+
const searchSchema = z
|
|
34
|
+
.object({
|
|
35
|
+
pattern: z.string().describe("regex pattern"),
|
|
36
|
+
paths: z
|
|
37
|
+
.array(z.string().describe("file, directory, glob, or internal URL to search"))
|
|
38
|
+
.min(1)
|
|
39
|
+
.describe("files, directories, globs, or internal URLs to search"),
|
|
40
|
+
i: z.boolean().optional().describe("case-insensitive search"),
|
|
41
|
+
gitignore: z.boolean().optional().describe("respect gitignore"),
|
|
42
|
+
skip: z
|
|
43
|
+
.number()
|
|
44
|
+
.optional()
|
|
45
|
+
.describe("files to skip before collecting results — use to paginate when the prior call hit the file limit"),
|
|
46
|
+
})
|
|
47
|
+
.strict();
|
|
48
|
+
|
|
49
|
+
export type SearchToolInput = z.infer<typeof searchSchema>;
|
|
53
50
|
|
|
54
51
|
/** Maximum number of distinct files surfaced in a single response. The
|
|
55
52
|
* agent paginates further pages via `skip`. */
|
|
@@ -89,7 +86,7 @@ export interface SearchToolDetails {
|
|
|
89
86
|
missingPaths?: string[];
|
|
90
87
|
}
|
|
91
88
|
|
|
92
|
-
type SearchParams =
|
|
89
|
+
type SearchParams = z.infer<typeof searchSchema>;
|
|
93
90
|
|
|
94
91
|
export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDetails> {
|
|
95
92
|
readonly name = "search";
|
package/src/tools/ssh.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
2
2
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
3
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import
|
|
5
|
+
import * as z from "zod/v4";
|
|
6
6
|
import type { SSHHost } from "../capability/ssh";
|
|
7
7
|
import { sshCapability } from "../capability/ssh";
|
|
8
8
|
import { loadCapability } from "../discovery";
|
|
@@ -16,16 +16,16 @@ import { executeSSH } from "../ssh/ssh-executor";
|
|
|
16
16
|
import { renderStatusLine } from "../tui";
|
|
17
17
|
import { CachedOutputBlock } from "../tui/output-block";
|
|
18
18
|
import type { ToolSession } from ".";
|
|
19
|
-
import { formatStyledTruncationWarning, type OutputMeta } from "./output-meta";
|
|
19
|
+
import { formatStyledTruncationWarning, type OutputMeta, stripOutputNotice } from "./output-meta";
|
|
20
20
|
import { ToolError } from "./tool-errors";
|
|
21
21
|
import { toolResult } from "./tool-result";
|
|
22
22
|
import { clampTimeout } from "./tool-timeouts";
|
|
23
23
|
|
|
24
|
-
const sshSchema =
|
|
25
|
-
host:
|
|
26
|
-
command:
|
|
27
|
-
cwd:
|
|
28
|
-
timeout:
|
|
24
|
+
const sshSchema = z.object({
|
|
25
|
+
host: z.string().describe("ssh host"),
|
|
26
|
+
command: z.string().describe("remote command"),
|
|
27
|
+
cwd: z.string().optional().describe("remote working directory"),
|
|
28
|
+
timeout: z.number().optional().describe("timeout in seconds").default(60),
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
export interface SSHToolDetails {
|
|
@@ -116,7 +116,7 @@ async function loadHosts(session: ToolSession): Promise<{
|
|
|
116
116
|
return { hostNames, hostsByName };
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
type SshToolParams =
|
|
119
|
+
type SshToolParams = z.infer<typeof sshSchema>;
|
|
120
120
|
|
|
121
121
|
export class SshTool implements AgentTool<typeof sshSchema, SSHToolDetails> {
|
|
122
122
|
readonly name = "ssh";
|
|
@@ -253,7 +253,8 @@ export const sshToolRenderer = {
|
|
|
253
253
|
render: (width: number): string[] => {
|
|
254
254
|
// REACTIVE: read mutable options at render time
|
|
255
255
|
const { expanded, renderContext } = options;
|
|
256
|
-
|
|
256
|
+
// Strip LLM-facing notice so we don't echo it next to the styled warning.
|
|
257
|
+
const output = stripOutputNotice(textContent, details?.meta).trimEnd();
|
|
257
258
|
const outputLines: string[] = [];
|
|
258
259
|
|
|
259
260
|
if (output) {
|