@oh-my-pi/pi-coding-agent 16.0.4 → 16.0.6
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 -0
- package/dist/cli.js +2027 -1396
- package/dist/types/advisor/advise-tool.d.ts +31 -19
- package/dist/types/autoresearch/tools/init-experiment.d.ts +13 -17
- package/dist/types/autoresearch/tools/log-experiment.d.ts +17 -19
- package/dist/types/autoresearch/tools/run-experiment.d.ts +3 -4
- package/dist/types/autoresearch/tools/update-notes.d.ts +4 -5
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/cli/bench-cli.d.ts +6 -0
- package/dist/types/cli/ttsr-cli.d.ts +39 -0
- package/dist/types/commands/launch.d.ts +3 -0
- package/dist/types/commands/ttsr.d.ts +57 -0
- package/dist/types/commit/agentic/tools/analyze-file.d.ts +4 -5
- package/dist/types/commit/agentic/tools/git-file-diff.d.ts +4 -5
- package/dist/types/commit/agentic/tools/git-hunk.d.ts +5 -6
- package/dist/types/commit/agentic/tools/git-overview.d.ts +4 -5
- package/dist/types/commit/agentic/tools/propose-changelog.d.ts +23 -24
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +11 -32
- package/dist/types/commit/agentic/tools/recent-commits.d.ts +3 -4
- package/dist/types/commit/agentic/tools/schemas.d.ts +6 -27
- package/dist/types/commit/agentic/tools/split-commit.d.ts +28 -49
- package/dist/types/commit/changelog/generate.d.ts +12 -13
- package/dist/types/commit/shared-llm.d.ts +10 -37
- package/dist/types/config/config-file.d.ts +4 -4
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/models-config-schema.d.ts +625 -990
- package/dist/types/config/models-config.d.ts +229 -217
- package/dist/types/config/settings-schema.d.ts +144 -25
- package/dist/types/edit/hashline/params.d.ts +7 -11
- package/dist/types/edit/index.d.ts +2 -1
- package/dist/types/edit/modes/apply-patch.d.ts +4 -5
- package/dist/types/edit/modes/patch.d.ts +15 -24
- package/dist/types/edit/modes/replace.d.ts +16 -17
- package/dist/types/eval/js/index.d.ts +1 -0
- package/dist/types/extensibility/custom-commands/types.d.ts +6 -3
- package/dist/types/extensibility/custom-tools/types.d.ts +8 -5
- package/dist/types/extensibility/extensions/runner.d.ts +5 -2
- package/dist/types/extensibility/extensions/types.d.ts +14 -10
- package/dist/types/extensibility/hooks/types.d.ts +7 -4
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +13 -5
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +17 -0
- package/dist/types/extensibility/shared-events.d.ts +22 -1
- package/dist/types/extensibility/typebox.d.ts +80 -58
- package/dist/types/goals/tools/goal-tool.d.ts +11 -24
- package/dist/types/index.d.ts +2 -0
- package/dist/types/lsp/index.d.ts +11 -26
- package/dist/types/lsp/types.d.ts +12 -28
- package/dist/types/main.d.ts +1 -0
- package/dist/types/mcp/client.d.ts +8 -0
- package/dist/types/modes/components/btw-panel.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +3 -1
- package/dist/types/modes/components/status-line/component.d.ts +1 -1
- package/dist/types/modes/components/status-line/context-thresholds.d.ts +0 -1
- package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +1 -1
- package/dist/types/modes/setup-wizard/index.d.ts +1 -0
- package/dist/types/modes/setup-wizard/startup-splash.d.ts +7 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +3 -0
- package/dist/types/modes/utils/context-usage.d.ts +12 -0
- package/dist/types/sdk.d.ts +8 -1
- package/dist/types/session/agent-session.d.ts +24 -0
- package/dist/types/session/session-persistence.d.ts +4 -0
- package/dist/types/startup-splash.d.ts +12 -0
- package/dist/types/task/types.d.ts +47 -48
- package/dist/types/tools/ask.d.ts +26 -27
- package/dist/types/tools/ast-edit.d.ts +17 -17
- package/dist/types/tools/ast-grep.d.ts +12 -13
- package/dist/types/tools/bash.d.ts +20 -17
- package/dist/types/tools/browser.d.ts +46 -71
- package/dist/types/tools/checkpoint.d.ts +14 -15
- package/dist/types/tools/debug.d.ts +82 -145
- package/dist/types/tools/eval.d.ts +30 -40
- package/dist/types/tools/find.d.ts +17 -18
- package/dist/types/tools/gh.d.ts +49 -78
- package/dist/types/tools/image-gen.d.ts +20 -36
- package/dist/types/tools/inspect-image.d.ts +10 -11
- package/dist/types/tools/irc.d.ts +22 -33
- package/dist/types/tools/job.d.ts +11 -12
- package/dist/types/tools/learn.d.ts +21 -28
- package/dist/types/tools/manage-skill.d.ts +13 -22
- package/dist/types/tools/memory-edit.d.ts +15 -24
- package/dist/types/tools/memory-recall.d.ts +7 -8
- package/dist/types/tools/memory-reflect.d.ts +9 -10
- package/dist/types/tools/memory-retain.d.ts +13 -14
- package/dist/types/tools/read.d.ts +8 -8
- package/dist/types/tools/resolve.d.ts +11 -18
- package/dist/types/tools/review.d.ts +9 -15
- package/dist/types/tools/search-tool-bm25.d.ts +9 -10
- package/dist/types/tools/search.d.ts +16 -17
- package/dist/types/tools/ssh.d.ts +14 -15
- package/dist/types/tools/todo.d.ts +27 -43
- package/dist/types/tools/tts.d.ts +8 -9
- package/dist/types/tools/write.d.ts +9 -10
- package/dist/types/tui/code-cell.d.ts +2 -0
- package/dist/types/tui/index.d.ts +1 -0
- package/dist/types/tui/width-aware-text.d.ts +23 -0
- package/dist/types/utils/image-vision-fallback.d.ts +28 -0
- package/dist/types/utils/markit.d.ts +10 -1
- package/dist/types/web/search/index.d.ts +17 -28
- package/dist/types/web/search/providers/base.d.ts +1 -0
- package/dist/types/web/search/providers/gemini.d.ts +1 -0
- package/dist/types/web/search/providers/perplexity.d.ts +0 -2
- package/dist/types/web/search/types.d.ts +32 -26
- package/package.json +14 -13
- package/scripts/omp +1 -1
- package/src/advisor/__tests__/advisor.test.ts +103 -1
- package/src/advisor/advise-tool.ts +47 -11
- package/src/autoresearch/tools/init-experiment.ts +13 -16
- package/src/autoresearch/tools/log-experiment.ts +15 -18
- package/src/autoresearch/tools/run-experiment.ts +3 -3
- package/src/autoresearch/tools/update-notes.ts +4 -4
- package/src/cli/args.ts +1 -0
- package/src/cli/bench-cli.ts +30 -7
- package/src/cli/flag-tables.ts +8 -0
- package/src/cli/ttsr-cli.ts +995 -0
- package/src/cli-commands.ts +1 -0
- package/src/cli.ts +7 -1
- package/src/collab/host.ts +2 -2
- package/src/commands/launch.ts +3 -0
- package/src/commands/ttsr.ts +125 -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 +7 -5
- package/src/commit/agentic/tools/git-overview.ts +4 -4
- package/src/commit/agentic/tools/propose-changelog.ts +18 -15
- 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 +8 -20
- package/src/commit/agentic/tools/split-commit.ts +19 -23
- package/src/commit/analysis/summary.ts +7 -5
- package/src/commit/changelog/generate.ts +15 -11
- package/src/commit/shared-llm.ts +17 -24
- package/src/config/config-file.ts +13 -15
- package/src/config/keybindings.ts +6 -0
- package/src/config/models-config-schema.ts +206 -179
- package/src/config/settings-schema.ts +118 -2
- package/src/discovery/builtin-rules/index.ts +2 -0
- package/src/discovery/builtin-rules/ts-import-type.md +2 -2
- package/src/discovery/builtin-rules/ts-no-any.md +11 -2
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +55 -0
- package/src/edit/hashline/params.ts +12 -11
- package/src/edit/index.ts +5 -4
- package/src/edit/modes/apply-patch.ts +4 -4
- package/src/edit/modes/patch.ts +15 -18
- package/src/edit/modes/replace.ts +13 -17
- package/src/edit/renderer.ts +0 -1
- package/src/eval/agent-bridge.ts +11 -13
- package/src/eval/completion-bridge.ts +25 -17
- package/src/eval/js/context-manager.ts +17 -2
- package/src/eval/js/index.ts +1 -1
- package/src/eval/py/executor.ts +2 -2
- package/src/eval/py/runner.py +44 -0
- package/src/extensibility/custom-commands/loader.ts +5 -3
- package/src/extensibility/custom-commands/types.ts +6 -3
- package/src/extensibility/custom-tools/loader.ts +4 -2
- package/src/extensibility/custom-tools/types.ts +8 -5
- package/src/extensibility/extensions/loader.ts +4 -2
- package/src/extensibility/extensions/runner.ts +20 -2
- package/src/extensibility/extensions/types.ts +22 -8
- package/src/extensibility/hooks/loader.ts +5 -2
- package/src/extensibility/hooks/types.ts +7 -4
- package/src/extensibility/legacy-pi-ai-shim.ts +42 -5
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +113 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +13 -13
- package/src/extensibility/shared-events.ts +24 -0
- package/src/extensibility/tool-proxy.ts +4 -1
- package/src/extensibility/typebox.ts +778 -251
- package/src/goals/guided-setup.ts +12 -3
- package/src/goals/tools/goal-tool.ts +6 -6
- package/src/index.ts +2 -0
- package/src/internal-urls/docs-index.generated.ts +15 -13
- package/src/lsp/types.ts +13 -27
- package/src/main.ts +29 -21
- package/src/mcp/client.ts +38 -13
- package/src/mcp/render.ts +102 -89
- package/src/modes/components/agent-hub.ts +11 -4
- package/src/modes/components/branch-summary-message.ts +1 -0
- package/src/modes/components/btw-panel.ts +5 -1
- package/src/modes/components/collab-prompt-message.ts +9 -7
- package/src/modes/components/compaction-summary-message.ts +1 -0
- package/src/modes/components/custom-editor.ts +18 -0
- package/src/modes/components/custom-message.ts +1 -0
- package/src/modes/components/footer.ts +6 -5
- package/src/modes/components/hook-message.ts +1 -0
- package/src/modes/components/read-tool-group.ts +9 -3
- package/src/modes/components/skill-message.ts +1 -0
- package/src/modes/components/status-line/component.ts +139 -15
- package/src/modes/components/status-line/context-thresholds.ts +0 -1
- package/src/modes/components/todo-reminder.ts +1 -0
- package/src/modes/components/tool-execution.ts +17 -10
- package/src/modes/components/ttsr-notification.ts +1 -0
- package/src/modes/components/user-message.ts +6 -6
- package/src/modes/controllers/btw-controller.ts +69 -1
- package/src/modes/controllers/event-controller.ts +2 -7
- package/src/modes/controllers/input-controller.ts +29 -0
- package/src/modes/controllers/selector-controller.ts +10 -3
- package/src/modes/interactive-mode.ts +42 -10
- package/src/modes/rpc/rpc-types.ts +1 -1
- package/src/modes/setup-wizard/index.ts +1 -0
- package/src/modes/setup-wizard/scenes/sign-in.ts +77 -5
- package/src/modes/setup-wizard/startup-splash.ts +107 -0
- package/src/modes/theme/theme.ts +133 -143
- package/src/modes/types.ts +3 -0
- package/src/modes/utils/context-usage.ts +37 -20
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/tools/image-attachment-describe-system.md +8 -0
- package/src/prompts/tools/image-attachment-describe.md +10 -0
- package/src/sdk.ts +35 -22
- package/src/session/agent-session.ts +715 -255
- package/src/session/session-history-format.ts +11 -2
- package/src/session/session-loader.ts +19 -32
- package/src/session/session-persistence.ts +27 -11
- package/src/session/snapcompact-inline.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +4 -11
- package/src/ssh/connection-manager.ts +3 -2
- package/src/startup-splash.ts +19 -0
- package/src/task/executor.ts +12 -7
- package/src/task/types.ts +44 -41
- package/src/tool-discovery/tool-index.ts +17 -4
- package/src/tools/ask.ts +14 -14
- package/src/tools/ast-edit.ts +17 -14
- package/src/tools/ast-grep.ts +10 -9
- package/src/tools/bash.ts +15 -10
- package/src/tools/browser/launch.ts +13 -0
- package/src/tools/browser.ts +26 -32
- package/src/tools/checkpoint.ts +7 -7
- package/src/tools/debug.ts +72 -69
- package/src/tools/eval.ts +18 -19
- package/src/tools/find.ts +20 -13
- package/src/tools/gh.ts +29 -49
- package/src/tools/image-gen.ts +94 -57
- package/src/tools/inspect-image.ts +8 -9
- package/src/tools/irc.ts +12 -12
- package/src/tools/job.ts +6 -6
- package/src/tools/learn.ts +11 -14
- package/src/tools/manage-skill.ts +19 -23
- package/src/tools/memory-edit.ts +8 -8
- package/src/tools/memory-recall.ts +4 -4
- package/src/tools/memory-reflect.ts +5 -5
- package/src/tools/memory-retain.ts +9 -11
- package/src/tools/puppeteer/02_stealth_hairline.txt +1 -1
- package/src/tools/puppeteer/04_stealth_iframe.txt +4 -4
- package/src/tools/puppeteer/05_stealth_webgl.txt +1 -1
- package/src/tools/puppeteer/10_stealth_plugins.txt +6 -4
- package/src/tools/puppeteer/12_stealth_codecs.txt +2 -2
- package/src/tools/puppeteer/13_stealth_worker.txt +1 -1
- package/src/tools/read.ts +197 -19
- package/src/tools/report-tool-issue.ts +6 -6
- package/src/tools/resolve.ts +6 -6
- package/src/tools/review.ts +10 -12
- package/src/tools/search-tool-bm25.ts +5 -5
- package/src/tools/search.ts +20 -29
- package/src/tools/ssh.ts +8 -8
- package/src/tools/todo.ts +16 -19
- package/src/tools/tts.ts +16 -15
- package/src/tools/write.ts +5 -5
- package/src/tui/code-cell.ts +44 -3
- package/src/tui/index.ts +1 -0
- package/src/tui/width-aware-text.ts +58 -0
- package/src/utils/image-vision-fallback.ts +197 -0
- package/src/utils/markit.ts +17 -2
- package/src/web/search/index.ts +21 -9
- package/src/web/search/providers/base.ts +1 -0
- package/src/web/search/providers/gemini.ts +56 -18
- package/src/web/search/providers/perplexity.ts +373 -126
- package/src/web/search/types.ts +28 -48
|
@@ -45,6 +45,7 @@ const PRIMARY_ARG_KEYS = [
|
|
|
45
45
|
"query",
|
|
46
46
|
"prompt",
|
|
47
47
|
"assignment",
|
|
48
|
+
"note",
|
|
48
49
|
"message",
|
|
49
50
|
"op",
|
|
50
51
|
"name",
|
|
@@ -74,8 +75,16 @@ function lineCount(text: string): number {
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
/** Pick the most informative scalar argument of a tool call. */
|
|
77
|
-
function primaryArg(args: Record<string, unknown> | undefined): string {
|
|
78
|
+
function primaryArg(name: string, args: Record<string, unknown> | undefined): string {
|
|
78
79
|
if (!args || typeof args !== "object") return "";
|
|
80
|
+
// Advisor note is the most informative summary; preserve severity too.
|
|
81
|
+
if (name === "advise") {
|
|
82
|
+
const note = typeof args.note === "string" ? args.note : "";
|
|
83
|
+
const severity = typeof args.severity === "string" ? args.severity : "";
|
|
84
|
+
if (note && severity) return oneLine(`${severity}: ${note}`);
|
|
85
|
+
if (note) return oneLine(note);
|
|
86
|
+
if (severity) return oneLine(severity);
|
|
87
|
+
}
|
|
79
88
|
for (const key of PRIMARY_ARG_KEYS) {
|
|
80
89
|
const value = args[key];
|
|
81
90
|
if (typeof value === "string" && value.length > 0) return oneLine(value);
|
|
@@ -108,7 +117,7 @@ function toolCallLine(
|
|
|
108
117
|
result: ToolResultMessage | undefined,
|
|
109
118
|
includeToolIntent?: boolean,
|
|
110
119
|
): string {
|
|
111
|
-
const head = `→ ${name}(${primaryArg(args)})`;
|
|
120
|
+
const head = `→ ${name}(${primaryArg(name, args)})`;
|
|
112
121
|
let base: string;
|
|
113
122
|
if (!result) {
|
|
114
123
|
base = `${head} ⇒ pending`;
|
|
@@ -4,7 +4,7 @@ import { BlobStore, isBlobRef, resolveImageData, resolveImageDataUrl } from "./b
|
|
|
4
4
|
import { buildSessionContext } from "./session-context";
|
|
5
5
|
import type { FileEntry, SessionEntry, SessionHeader } from "./session-entries";
|
|
6
6
|
import { migrateToCurrentVersion } from "./session-migrations";
|
|
7
|
-
import { isImageBlock } from "./session-persistence";
|
|
7
|
+
import { isImageBlock, isImageDataPayload } from "./session-persistence";
|
|
8
8
|
import { FileSessionStorage, type SessionStorage } from "./session-storage";
|
|
9
9
|
|
|
10
10
|
/** Exported for compaction.test.ts */
|
|
@@ -44,9 +44,19 @@ function hasImageUrl(value: unknown): value is { image_url: string } {
|
|
|
44
44
|
return typeof value === "object" && value !== null && "image_url" in value && typeof value.image_url === "string";
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
function shouldResolveImagePayload(value: unknown, key: string | undefined): value is { data: string } {
|
|
48
|
+
if (!isImageDataPayload(value) || !isBlobRef(value.data)) return false;
|
|
49
|
+
return (key === "content" && isImageBlock(value)) || key === "images";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function resolvePersistedBlobRefs(value: unknown, blobStore: BlobStore, key?: string): Promise<void> {
|
|
53
|
+
if (shouldResolveImagePayload(value, key)) {
|
|
54
|
+
value.data = await resolveImageData(blobStore, value.data);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
48
58
|
if (Array.isArray(value)) {
|
|
49
|
-
await Promise.all(value.map(item =>
|
|
59
|
+
await Promise.all(value.map(item => resolvePersistedBlobRefs(item, blobStore, key)));
|
|
50
60
|
return;
|
|
51
61
|
}
|
|
52
62
|
|
|
@@ -56,38 +66,15 @@ async function resolvePersistedImageUrlRefs(value: unknown, blobStore: BlobStore
|
|
|
56
66
|
value.image_url = await resolveImageDataUrl(blobStore, value.image_url);
|
|
57
67
|
}
|
|
58
68
|
|
|
59
|
-
await Promise.all(
|
|
69
|
+
await Promise.all(
|
|
70
|
+
Object.entries(value).map(([childKey, item]) => resolvePersistedBlobRefs(item, blobStore, childKey)),
|
|
71
|
+
);
|
|
60
72
|
}
|
|
61
73
|
|
|
62
74
|
export async function resolveBlobRefsInEntries(entries: FileEntry[], blobStore: BlobStore): Promise<void> {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (entry.type === "session") continue;
|
|
67
|
-
|
|
68
|
-
let contentArray: unknown[] | undefined;
|
|
69
|
-
if (entry.type === "message" && "content" in entry.message && Array.isArray(entry.message.content)) {
|
|
70
|
-
contentArray = entry.message.content;
|
|
71
|
-
} else if (entry.type === "custom_message" && Array.isArray(entry.content)) {
|
|
72
|
-
contentArray = entry.content;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (contentArray) {
|
|
76
|
-
for (const block of contentArray) {
|
|
77
|
-
if (isImageBlock(block) && isBlobRef(block.data)) {
|
|
78
|
-
promises.push(
|
|
79
|
-
resolveImageData(blobStore, block.data).then(resolved => {
|
|
80
|
-
block.data = resolved;
|
|
81
|
-
}),
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
promises.push(resolvePersistedImageUrlRefs(entry, blobStore));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
await Promise.all(promises);
|
|
75
|
+
await Promise.all(
|
|
76
|
+
entries.filter(entry => entry.type !== "session").map(entry => resolvePersistedBlobRefs(entry, blobStore)),
|
|
77
|
+
);
|
|
91
78
|
}
|
|
92
79
|
|
|
93
80
|
/**
|
|
@@ -36,10 +36,33 @@ export function isImageBlock(value: unknown): value is { type: "image"; data: st
|
|
|
36
36
|
);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function isImageMimeType(value: unknown): value is string {
|
|
40
|
+
return typeof value === "string" && value.toLowerCase().startsWith("image/");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function isImageDataPayload(value: unknown): value is { data: string; mimeType?: string } {
|
|
44
|
+
return (
|
|
45
|
+
typeof value === "object" &&
|
|
46
|
+
value !== null &&
|
|
47
|
+
"data" in value &&
|
|
48
|
+
typeof (value as { data?: string }).data === "string" &&
|
|
49
|
+
(isImageBlock(value) || ("mimeType" in value && isImageMimeType((value as { mimeType?: unknown }).mimeType)))
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function shouldExternalizeImagePayload(
|
|
54
|
+
value: unknown,
|
|
55
|
+
key: string | undefined,
|
|
56
|
+
): value is { data: string; mimeType?: string } {
|
|
57
|
+
if (!isImageDataPayload(value)) return false;
|
|
58
|
+
if (isBlobRef(value.data) || value.data.length < BLOB_EXTERNALIZE_THRESHOLD) return false;
|
|
59
|
+
return (key === TEXT_CONTENT_KEY && isImageBlock(value)) || key === "images";
|
|
60
|
+
}
|
|
61
|
+
|
|
39
62
|
/**
|
|
40
63
|
* Recursively truncate large strings in an object for session persistence.
|
|
41
64
|
* - Truncates any oversized string fields (key-agnostic)
|
|
42
|
-
* -
|
|
65
|
+
* - Externalizes oversized image payloads to blob refs
|
|
43
66
|
* - Updates lineCount when content is truncated
|
|
44
67
|
* - Returns original object if no changes needed (structural sharing)
|
|
45
68
|
*
|
|
@@ -50,6 +73,9 @@ export function isImageBlock(value: unknown): value is { type: "image"; data: st
|
|
|
50
73
|
*/
|
|
51
74
|
function truncateForPersistence(obj: unknown, blobStore: BlobStore, key?: string): unknown {
|
|
52
75
|
if (obj === null || obj === undefined) return obj;
|
|
76
|
+
if (shouldExternalizeImagePayload(obj, key)) {
|
|
77
|
+
return { ...obj, data: externalizeImageDataSync(blobStore, obj.data, obj.mimeType) };
|
|
78
|
+
}
|
|
53
79
|
|
|
54
80
|
if (typeof obj === "string") {
|
|
55
81
|
if (key === "image_url" && isImageDataUrl(obj)) {
|
|
@@ -72,16 +98,6 @@ function truncateForPersistence(obj: unknown, blobStore: BlobStore, key?: string
|
|
|
72
98
|
const result: unknown[] = new Array(obj.length);
|
|
73
99
|
for (let i = 0; i < obj.length; i++) {
|
|
74
100
|
const item = obj[i];
|
|
75
|
-
if (
|
|
76
|
-
key === TEXT_CONTENT_KEY &&
|
|
77
|
-
isImageBlock(item) &&
|
|
78
|
-
!isBlobRef(item.data) &&
|
|
79
|
-
item.data.length >= BLOB_EXTERNALIZE_THRESHOLD
|
|
80
|
-
) {
|
|
81
|
-
changed = true;
|
|
82
|
-
result[i] = { ...item, data: externalizeImageDataSync(blobStore, item.data, item.mimeType) };
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
101
|
const newItem = truncateForPersistence(item, blobStore, key);
|
|
86
102
|
if (newItem !== item) changed = true;
|
|
87
103
|
result[i] = newItem;
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
* estimate (`estimateInlineSavings`) so the two can never disagree.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { countTokens } from "@oh-my-pi/pi-agent-core";
|
|
17
18
|
import type { Context, ImageContent, Model, TextContent, ToolResultMessage, UserMessage } from "@oh-my-pi/pi-ai";
|
|
18
|
-
import { countTokens } from "@oh-my-pi/pi-natives";
|
|
19
19
|
import * as snapcompact from "@oh-my-pi/snapcompact";
|
|
20
20
|
import contextFramesNote from "../prompts/system/snapcompact-context-frames-note.md" with { type: "text" };
|
|
21
21
|
import contextStub from "../prompts/system/snapcompact-context-stub.md" with { type: "text" };
|
|
@@ -240,13 +240,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
240
240
|
allowArgs: true,
|
|
241
241
|
handleTui: async (command, runtime) => {
|
|
242
242
|
const hadArgs = !!command.args;
|
|
243
|
-
// Capture state BEFORE the call: when plan mode is already active,
|
|
244
|
-
// handlePlanModeCommand may exit it (on confirmed exit) or leave it on (on cancel
|
|
245
|
-
// or warning). In every "already active" case the typed args are NOT consumed,
|
|
246
|
-
// so preserve them in history regardless of the user's confirm/cancel choice.
|
|
247
|
-
const wasPlanModeEnabled = runtime.ctx.planModeEnabled;
|
|
248
243
|
await runtime.ctx.handlePlanModeCommand(command.args || undefined);
|
|
249
|
-
if (hadArgs
|
|
244
|
+
if (hadArgs) {
|
|
250
245
|
runtime.ctx.editor.addToHistory(command.text);
|
|
251
246
|
}
|
|
252
247
|
runtime.ctx.editor.setText("");
|
|
@@ -275,10 +270,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
275
270
|
allowArgs: true,
|
|
276
271
|
handleTui: async (command, runtime) => {
|
|
277
272
|
const hadArgs = !!command.args;
|
|
278
|
-
// Capture state BEFORE the call (see /plan above for rationale).
|
|
279
|
-
const wasGoalModeEnabled = runtime.ctx.goalModeEnabled;
|
|
280
273
|
await runtime.ctx.handleGoalModeCommand(command.args || undefined);
|
|
281
|
-
if (hadArgs
|
|
274
|
+
if (hadArgs) {
|
|
282
275
|
runtime.ctx.editor.addToHistory(command.text);
|
|
283
276
|
}
|
|
284
277
|
runtime.ctx.editor.setText("");
|
|
@@ -308,7 +301,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
308
301
|
{
|
|
309
302
|
name: "model",
|
|
310
303
|
aliases: ["models"],
|
|
311
|
-
description: "
|
|
304
|
+
description: "Switch model for this session",
|
|
312
305
|
acpDescription: "Show current model selection",
|
|
313
306
|
handle: async (command, runtime) => {
|
|
314
307
|
if (command.args) {
|
|
@@ -341,7 +334,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
341
334
|
return commandConsumed();
|
|
342
335
|
},
|
|
343
336
|
handleTui: (_command, runtime) => {
|
|
344
|
-
runtime.ctx.showModelSelector();
|
|
337
|
+
runtime.ctx.showModelSelector({ temporaryOnly: true });
|
|
345
338
|
runtime.ctx.editor.setText("");
|
|
346
339
|
},
|
|
347
340
|
},
|
|
@@ -65,7 +65,7 @@ async function deleteHostInfoFromDisk(hostName: string): Promise<void> {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
async function validateKeyPermissions(keyPath?: string): Promise<void> {
|
|
68
|
+
async function validateKeyPermissions(keyPath?: string, platform: SshPlatform = process.platform): Promise<void> {
|
|
69
69
|
if (!keyPath) return;
|
|
70
70
|
let stats: fs.Stats;
|
|
71
71
|
try {
|
|
@@ -79,6 +79,7 @@ async function validateKeyPermissions(keyPath?: string): Promise<void> {
|
|
|
79
79
|
if (!stats.isFile()) {
|
|
80
80
|
throw new Error(`SSH key is not a file: ${keyPath}`);
|
|
81
81
|
}
|
|
82
|
+
if (platform === "win32") return;
|
|
82
83
|
const mode = stats.mode & 0o777;
|
|
83
84
|
if ((mode & 0o077) !== 0) {
|
|
84
85
|
throw new Error(`SSH key permissions must be 600 or stricter: ${keyPath}`);
|
|
@@ -402,7 +403,7 @@ export async function buildRemoteCommand(
|
|
|
402
403
|
command: string,
|
|
403
404
|
options?: SSHArgsOptions,
|
|
404
405
|
): Promise<string[]> {
|
|
405
|
-
await validateKeyPermissions(host.keyPath);
|
|
406
|
+
await validateKeyPermissions(host.keyPath, options?.platform);
|
|
406
407
|
return [...buildCommonArgs(host, options), buildSshTarget(host.username, host.host), command];
|
|
407
408
|
}
|
|
408
409
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Inputs used to decide whether the optional startup splash may run for this process. */
|
|
2
|
+
export interface StartupSplashDecisionOptions {
|
|
3
|
+
readonly configured: boolean;
|
|
4
|
+
readonly isInteractive: boolean;
|
|
5
|
+
readonly resuming: boolean;
|
|
6
|
+
readonly quiet: boolean;
|
|
7
|
+
readonly timing: boolean;
|
|
8
|
+
readonly stdinIsTTY: boolean | undefined;
|
|
9
|
+
readonly stdoutIsTTY: boolean | undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Returns true only for explicitly enabled, normal interactive TTY startup. */
|
|
13
|
+
export function shouldShowStartupSplash(options: StartupSplashDecisionOptions): boolean {
|
|
14
|
+
if (!options.configured) return false;
|
|
15
|
+
if (!options.isInteractive) return false;
|
|
16
|
+
if (options.resuming || options.quiet) return false;
|
|
17
|
+
if (options.timing) return false;
|
|
18
|
+
return options.stdinIsTTY === true && options.stdoutIsTTY === true;
|
|
19
|
+
}
|
package/src/task/executor.ts
CHANGED
|
@@ -516,6 +516,7 @@ export function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): Fi
|
|
|
516
516
|
const { yieldItems, reportFindings, doneAborted, signalAborted, outputSchema } = args;
|
|
517
517
|
let abortedViaYield = false;
|
|
518
518
|
const hasYield = Array.isArray(yieldItems) && yieldItems.length > 0;
|
|
519
|
+
const hadFailureBeforeYield = exitCode !== 0 && stderr.trim().length > 0;
|
|
519
520
|
|
|
520
521
|
if (hasYield) {
|
|
521
522
|
const lastYield = yieldItems[yieldItems.length - 1];
|
|
@@ -553,12 +554,16 @@ export function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): Fi
|
|
|
553
554
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
554
555
|
rawOutput = `{"error":"Failed to serialize yield data: ${errorMessage}"}`;
|
|
555
556
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
557
|
+
if (!hadFailureBeforeYield) {
|
|
558
|
+
exitCode = 0;
|
|
559
|
+
stderr = overridden
|
|
560
|
+
? SUBAGENT_WARNING_SCHEMA_OVERRIDDEN
|
|
561
|
+
: schemaError
|
|
562
|
+
? `invalid output schema: ${schemaError}`
|
|
563
|
+
: "";
|
|
564
|
+
} else if (!stderr) {
|
|
565
|
+
stderr = "Subagent failed after yielding a result.";
|
|
566
|
+
}
|
|
562
567
|
}
|
|
563
568
|
}
|
|
564
569
|
}
|
|
@@ -2117,8 +2122,8 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
2117
2122
|
void session.abort();
|
|
2118
2123
|
}
|
|
2119
2124
|
|
|
2125
|
+
const pendingExtensionMessages: Array<Promise<unknown>> = [];
|
|
2120
2126
|
const extensionRunner = session.extensionRunner;
|
|
2121
|
-
const pendingExtensionMessages: Promise<unknown>[] = [];
|
|
2122
2127
|
if (extensionRunner) {
|
|
2123
2128
|
extensionRunner.initialize(
|
|
2124
2129
|
{
|
package/src/task/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { Usage } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { $env } from "@oh-my-pi/pi-utils";
|
|
4
|
-
import {
|
|
4
|
+
import { type } from "arktype";
|
|
5
5
|
import type { AgentSessionEvent } from "../session/agent-session";
|
|
6
6
|
import type { NestedRepoPatch } from "./worktree";
|
|
7
7
|
|
|
@@ -78,37 +78,23 @@ export interface SubagentLifecyclePayload {
|
|
|
78
78
|
export const ROLE_LABEL_MAX = 80;
|
|
79
79
|
/** Schema bound on the raw `role` input, before it is label-normalized at every use site. */
|
|
80
80
|
export const ROLE_INPUT_MAX = 256;
|
|
81
|
+
const ROLE_INPUT_SCHEMA = `string <= ${ROLE_INPUT_MAX}` as const;
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
),
|
|
98
|
-
assignment: z.string().describe("the work; self-contained instructions"),
|
|
99
|
-
};
|
|
100
|
-
const isolatedShape = {
|
|
101
|
-
isolated: z.boolean().optional().describe("run in isolated env; returns patches"),
|
|
102
|
-
};
|
|
103
|
-
const agentShape = {
|
|
104
|
-
agent: z.string().describe("agent type to spawn"),
|
|
105
|
-
};
|
|
106
|
-
const contextShape = {
|
|
107
|
-
context: z.string().describe("shared background prepended to each assignment"),
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
export const taskItemSchema = z.object(taskItemShape);
|
|
111
|
-
const taskItemSchemaIsolated = z.object({ ...taskItemShape, ...isolatedShape });
|
|
83
|
+
export const taskItemSchema = type({
|
|
84
|
+
"id?": "string",
|
|
85
|
+
"description?": "string",
|
|
86
|
+
"role?": ROLE_INPUT_SCHEMA,
|
|
87
|
+
assignment: "string",
|
|
88
|
+
"+": "delete",
|
|
89
|
+
});
|
|
90
|
+
const taskItemSchemaIsolated = type({
|
|
91
|
+
"id?": "string",
|
|
92
|
+
"description?": "string",
|
|
93
|
+
"role?": ROLE_INPUT_SCHEMA,
|
|
94
|
+
assignment: "string",
|
|
95
|
+
"isolated?": "boolean",
|
|
96
|
+
"+": "delete",
|
|
97
|
+
});
|
|
112
98
|
|
|
113
99
|
/** Single task item. Fields are optional defensively: args stream in token by token. */
|
|
114
100
|
export interface TaskItem {
|
|
@@ -124,17 +110,34 @@ export interface TaskItem {
|
|
|
124
110
|
isolated?: boolean;
|
|
125
111
|
}
|
|
126
112
|
|
|
127
|
-
export const taskSchema =
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
113
|
+
export const taskSchema = type({
|
|
114
|
+
agent: "string",
|
|
115
|
+
"id?": "string",
|
|
116
|
+
"description?": "string",
|
|
117
|
+
"role?": ROLE_INPUT_SCHEMA,
|
|
118
|
+
assignment: "string",
|
|
119
|
+
"isolated?": "boolean",
|
|
120
|
+
"+": "delete",
|
|
121
|
+
});
|
|
122
|
+
const taskSchemaNoIsolation = type({
|
|
123
|
+
agent: "string",
|
|
124
|
+
"id?": "string",
|
|
125
|
+
"description?": "string",
|
|
126
|
+
"role?": ROLE_INPUT_SCHEMA,
|
|
127
|
+
assignment: "string",
|
|
128
|
+
"+": "delete",
|
|
129
|
+
});
|
|
130
|
+
const taskSchemaBatch = type({
|
|
131
|
+
agent: "string",
|
|
132
|
+
context: "string",
|
|
133
|
+
tasks: taskItemSchemaIsolated.array(),
|
|
134
|
+
"+": "delete",
|
|
133
135
|
});
|
|
134
|
-
const taskSchemaBatchNoIsolation =
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
tasks:
|
|
136
|
+
const taskSchemaBatchNoIsolation = type({
|
|
137
|
+
agent: "string",
|
|
138
|
+
context: "string",
|
|
139
|
+
tasks: taskItemSchema.array(),
|
|
140
|
+
"+": "delete",
|
|
138
141
|
});
|
|
139
142
|
const ALL_TASK_SCHEMAS = [taskSchema, taskSchemaNoIsolation, taskSchemaBatch, taskSchemaBatchNoIsolation] as const;
|
|
140
143
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import {
|
|
2
|
+
import type { Tool as AiTool } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import { toolWireSchema } from "@oh-my-pi/pi-ai/utils/schema";
|
|
3
4
|
|
|
4
5
|
// ─── Generic Tool Discovery Types ────────────────────────────────────────────
|
|
5
6
|
|
|
@@ -65,8 +66,13 @@ export function isMCPToolName(name: string): boolean {
|
|
|
65
66
|
return name.startsWith("mcp__");
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
function getSchemaPropertyKeys(
|
|
69
|
-
|
|
69
|
+
function getSchemaPropertyKeys(tool: Pick<AiTool, "name" | "description" | "parameters">): string[] {
|
|
70
|
+
let parameters: unknown = tool.parameters;
|
|
71
|
+
try {
|
|
72
|
+
parameters = toolWireSchema(tool as AiTool);
|
|
73
|
+
} catch {
|
|
74
|
+
// Schema may contain functions or cycles; fall back to the raw shape.
|
|
75
|
+
}
|
|
70
76
|
if (!parameters || typeof parameters !== "object" || Array.isArray(parameters)) return [];
|
|
71
77
|
const properties = (parameters as { properties?: unknown }).properties;
|
|
72
78
|
if (!properties || typeof properties !== "object" || Array.isArray(properties)) return [];
|
|
@@ -149,7 +155,14 @@ export function getDiscoverableTool(
|
|
|
149
155
|
source,
|
|
150
156
|
serverName: typeof toolRecord.mcpServerName === "string" ? toolRecord.mcpServerName : undefined,
|
|
151
157
|
mcpToolName: typeof toolRecord.mcpToolName === "string" ? toolRecord.mcpToolName : undefined,
|
|
152
|
-
schemaKeys:
|
|
158
|
+
schemaKeys:
|
|
159
|
+
toolRecord.parameters === undefined
|
|
160
|
+
? []
|
|
161
|
+
: getSchemaPropertyKeys({
|
|
162
|
+
name: tool.name,
|
|
163
|
+
description: rawDescription,
|
|
164
|
+
parameters: toolRecord.parameters as AiTool["parameters"],
|
|
165
|
+
}),
|
|
153
166
|
};
|
|
154
167
|
}
|
|
155
168
|
|
package/src/tools/ask.ts
CHANGED
|
@@ -19,7 +19,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
19
19
|
import type { ToolExample } from "@oh-my-pi/pi-ai";
|
|
20
20
|
import { type Component, Markdown, type MarkdownTheme, renderInlineMarkdown, TERMINAL, Text } from "@oh-my-pi/pi-tui";
|
|
21
21
|
import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
22
|
-
import {
|
|
22
|
+
import { type as arkType } from "arktype";
|
|
23
23
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
24
24
|
import type { ExtensionUISelectItem } from "../extensibility/extensions";
|
|
25
25
|
import { getMarkdownTheme, type Theme, theme } from "../modes/theme/theme";
|
|
@@ -34,24 +34,24 @@ import { ToolAbortError } from "./tool-errors";
|
|
|
34
34
|
// Types
|
|
35
35
|
// =============================================================================
|
|
36
36
|
|
|
37
|
-
const OptionItem =
|
|
38
|
-
label:
|
|
39
|
-
description:
|
|
37
|
+
const OptionItem = arkType({
|
|
38
|
+
label: arkType("string").describe("display label"),
|
|
39
|
+
"description?": arkType("string").describe("optional explanatory text displayed below the label"),
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
const QuestionItem =
|
|
43
|
-
id:
|
|
44
|
-
question:
|
|
45
|
-
options:
|
|
46
|
-
multi:
|
|
47
|
-
recommended:
|
|
42
|
+
const QuestionItem = arkType({
|
|
43
|
+
id: arkType("string").describe("question id"),
|
|
44
|
+
question: arkType("string").describe("question text"),
|
|
45
|
+
options: OptionItem.array().describe("available options"),
|
|
46
|
+
"multi?": arkType("boolean").describe("allow multiple selections"),
|
|
47
|
+
"recommended?": arkType("number").describe("recommended option index"),
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
const askSchema =
|
|
51
|
-
questions:
|
|
50
|
+
const askSchema = arkType({
|
|
51
|
+
questions: QuestionItem.array().atLeastLength(1).describe("questions to ask"),
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
export type AskToolInput =
|
|
54
|
+
export type AskToolInput = typeof askSchema.infer;
|
|
55
55
|
|
|
56
56
|
/** Result for a single question */
|
|
57
57
|
export interface QuestionResult {
|
|
@@ -424,7 +424,7 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
424
424
|
readonly parameters = askSchema;
|
|
425
425
|
readonly strict = true;
|
|
426
426
|
|
|
427
|
-
readonly examples: readonly ToolExample<
|
|
427
|
+
readonly examples: readonly ToolExample<typeof askSchema.infer>[] = [
|
|
428
428
|
{
|
|
429
429
|
caption: "Single question",
|
|
430
430
|
call: {
|
package/src/tools/ast-edit.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { type AstReplaceChange, type AstReplaceFileChange, astEdit } from "@oh-m
|
|
|
6
6
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
7
7
|
import { replaceTabs, Text } from "@oh-my-pi/pi-tui";
|
|
8
8
|
import { $envpos, prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
9
|
-
import {
|
|
9
|
+
import { type } from "arktype";
|
|
10
10
|
import { canonicalSnapshotKey, getFileSnapshotStore } from "../edit/file-snapshot-store";
|
|
11
11
|
import { normalizeToLF } from "../edit/normalize";
|
|
12
12
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
@@ -35,16 +35,17 @@ import { queueResolveHandler } from "./resolve";
|
|
|
35
35
|
import { ToolError } from "./tool-errors";
|
|
36
36
|
import { toolResult } from "./tool-result";
|
|
37
37
|
|
|
38
|
-
const astEditOpSchema =
|
|
39
|
-
pat:
|
|
40
|
-
out:
|
|
38
|
+
const astEditOpSchema = type({
|
|
39
|
+
pat: type("string").describe("ast pattern"),
|
|
40
|
+
out: type("string").describe("replacement template"),
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
const astEditSchema =
|
|
44
|
-
ops:
|
|
45
|
-
paths:
|
|
46
|
-
.
|
|
47
|
-
.
|
|
43
|
+
const astEditSchema = type({
|
|
44
|
+
ops: astEditOpSchema.array().atLeastLength(1).describe("rewrite ops"),
|
|
45
|
+
paths: type("string")
|
|
46
|
+
.describe("file, directory, glob, or internal URL to rewrite")
|
|
47
|
+
.array()
|
|
48
|
+
.atLeastLength(1)
|
|
48
49
|
.describe("files, directories, globs, or internal URLs to rewrite"),
|
|
49
50
|
});
|
|
50
51
|
|
|
@@ -165,16 +166,18 @@ export interface AstEditToolDetails {
|
|
|
165
166
|
cwd?: string;
|
|
166
167
|
}
|
|
167
168
|
|
|
169
|
+
type AstEditSchemaInfer = typeof astEditSchema.infer;
|
|
170
|
+
|
|
168
171
|
export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolDetails> {
|
|
169
172
|
readonly name = "ast_edit";
|
|
170
173
|
readonly approval = (args: unknown) => {
|
|
171
|
-
const paths = Array.isArray((args as Partial<
|
|
172
|
-
? ((args as Partial<
|
|
174
|
+
const paths = Array.isArray((args as Partial<AstEditSchemaInfer>).paths)
|
|
175
|
+
? ((args as Partial<AstEditSchemaInfer>).paths as string[])
|
|
173
176
|
: [];
|
|
174
177
|
return paths.length > 0 && paths.every(path => isInternalUrlPath(path)) ? "read" : "write";
|
|
175
178
|
};
|
|
176
179
|
readonly formatApprovalDetails = (args: unknown): string[] => {
|
|
177
|
-
const params = args as Partial<
|
|
180
|
+
const params = args as Partial<AstEditSchemaInfer>;
|
|
178
181
|
const lines: string[] = [];
|
|
179
182
|
const ops = Array.isArray(params.ops) ? params.ops : [];
|
|
180
183
|
const firstOp = ops[0];
|
|
@@ -196,7 +199,7 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
|
|
|
196
199
|
readonly parameters = astEditSchema;
|
|
197
200
|
readonly strict = true;
|
|
198
201
|
|
|
199
|
-
readonly examples: readonly ToolExample<
|
|
202
|
+
readonly examples: readonly ToolExample<AstEditSchemaInfer>[] = [
|
|
200
203
|
{
|
|
201
204
|
caption: "Rename a call site across TypeScript files",
|
|
202
205
|
call: {
|
|
@@ -248,7 +251,7 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
|
|
|
248
251
|
|
|
249
252
|
async execute(
|
|
250
253
|
_toolCallId: string,
|
|
251
|
-
params:
|
|
254
|
+
params: AstEditSchemaInfer,
|
|
252
255
|
signal?: AbortSignal,
|
|
253
256
|
_onUpdate?: AgentToolUpdateCallback<AstEditToolDetails>,
|
|
254
257
|
_context?: AgentToolContext,
|
package/src/tools/ast-grep.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { type AstFindMatch, astGrep } from "@oh-my-pi/pi-natives";
|
|
|
6
6
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
7
7
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
8
8
|
import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
9
|
-
import {
|
|
9
|
+
import { type } from "arktype";
|
|
10
10
|
import { recordFileSnapshot, recordSeenLinesFromBody } from "../edit/file-snapshot-store";
|
|
11
11
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
12
12
|
import type { Theme } from "../modes/theme/theme";
|
|
@@ -34,13 +34,14 @@ import {
|
|
|
34
34
|
import { ToolError } from "./tool-errors";
|
|
35
35
|
import { toolResult } from "./tool-result";
|
|
36
36
|
|
|
37
|
-
const astGrepSchema =
|
|
38
|
-
pat:
|
|
39
|
-
paths:
|
|
40
|
-
.
|
|
41
|
-
.
|
|
37
|
+
const astGrepSchema = type({
|
|
38
|
+
pat: type("string").describe("ast pattern"),
|
|
39
|
+
paths: type("string")
|
|
40
|
+
.describe("file, directory, glob, or internal URL to search")
|
|
41
|
+
.array()
|
|
42
|
+
.atLeastLength(1)
|
|
42
43
|
.describe("files, directories, globs, or internal URLs to search"),
|
|
43
|
-
skip:
|
|
44
|
+
"skip?": type("number").describe("matches to skip"),
|
|
44
45
|
});
|
|
45
46
|
|
|
46
47
|
async function runMultiTargetAstGrep(
|
|
@@ -132,7 +133,7 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
|
|
|
132
133
|
readonly parameters = astGrepSchema;
|
|
133
134
|
readonly strict = true;
|
|
134
135
|
|
|
135
|
-
readonly examples: readonly ToolExample<
|
|
136
|
+
readonly examples: readonly ToolExample<typeof astGrepSchema.inferIn>[] = [
|
|
136
137
|
{
|
|
137
138
|
caption: "Search TypeScript files under src",
|
|
138
139
|
call: { pat: "console.log($$$)", paths: ["src/**/*.ts"] },
|
|
@@ -162,7 +163,7 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
|
|
|
162
163
|
|
|
163
164
|
async execute(
|
|
164
165
|
_toolCallId: string,
|
|
165
|
-
params:
|
|
166
|
+
params: typeof astGrepSchema.infer,
|
|
166
167
|
signal?: AbortSignal,
|
|
167
168
|
_onUpdate?: AgentToolUpdateCallback<AstGrepToolDetails>,
|
|
168
169
|
_context?: AgentToolContext,
|