@oh-my-pi/pi-coding-agent 4.1.0 → 4.2.1
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 +66 -0
- package/README.md +2 -1
- package/docs/sdk.md +0 -3
- package/package.json +6 -5
- package/src/config.ts +9 -0
- package/src/core/agent-session.ts +3 -3
- package/src/core/agent-storage.ts +450 -0
- package/src/core/auth-storage.ts +102 -183
- package/src/core/compaction/branch-summarization.ts +5 -4
- package/src/core/compaction/compaction.ts +7 -6
- package/src/core/compaction/utils.ts +6 -11
- package/src/core/custom-commands/bundled/review/index.ts +22 -94
- package/src/core/custom-share.ts +66 -0
- package/src/core/export-html/index.ts +1 -33
- package/src/core/history-storage.ts +15 -7
- package/src/core/prompt-templates.ts +271 -1
- package/src/core/sdk.ts +14 -3
- package/src/core/settings-manager.ts +100 -34
- package/src/core/slash-commands.ts +4 -1
- package/src/core/storage-migration.ts +215 -0
- package/src/core/system-prompt.ts +130 -290
- package/src/core/title-generator.ts +3 -2
- package/src/core/tools/ask.ts +2 -2
- package/src/core/tools/bash.ts +2 -1
- package/src/core/tools/calculator.ts +2 -1
- package/src/core/tools/complete.ts +5 -2
- package/src/core/tools/edit.ts +2 -1
- package/src/core/tools/find.ts +2 -1
- package/src/core/tools/gemini-image.ts +2 -1
- package/src/core/tools/git.ts +2 -2
- package/src/core/tools/grep.ts +2 -1
- package/src/core/tools/index.test.ts +0 -28
- package/src/core/tools/index.ts +0 -6
- package/src/core/tools/lsp/index.ts +2 -1
- package/src/core/tools/output.ts +2 -1
- package/src/core/tools/read.ts +4 -1
- package/src/core/tools/ssh.ts +4 -2
- package/src/core/tools/task/agents.ts +56 -30
- package/src/core/tools/task/commands.ts +5 -8
- package/src/core/tools/task/index.ts +7 -15
- package/src/core/tools/web-fetch.ts +2 -1
- package/src/core/tools/web-search/auth.ts +106 -16
- package/src/core/tools/web-search/index.ts +3 -2
- package/src/core/tools/web-search/providers/anthropic.ts +44 -6
- package/src/core/tools/write.ts +2 -1
- package/src/core/voice.ts +3 -1
- package/src/discovery/builtin.ts +9 -54
- package/src/discovery/claude.ts +16 -69
- package/src/discovery/codex.ts +11 -36
- package/src/discovery/helpers.ts +52 -1
- package/src/main.ts +1 -1
- package/src/migrations.ts +20 -20
- package/src/modes/interactive/controllers/command-controller.ts +527 -0
- package/src/modes/interactive/controllers/event-controller.ts +340 -0
- package/src/modes/interactive/controllers/extension-ui-controller.ts +600 -0
- package/src/modes/interactive/controllers/input-controller.ts +585 -0
- package/src/modes/interactive/controllers/selector-controller.ts +585 -0
- package/src/modes/interactive/interactive-mode.ts +363 -3139
- package/src/modes/interactive/theme/theme.ts +5 -5
- package/src/modes/interactive/types.ts +189 -0
- package/src/modes/interactive/utils/ui-helpers.ts +449 -0
- package/src/modes/interactive/utils/voice-manager.ts +96 -0
- package/src/prompts/{explore.md → agents/explore.md} +7 -5
- package/src/prompts/agents/frontmatter.md +7 -0
- package/src/prompts/{plan.md → agents/plan.md} +3 -3
- package/src/prompts/agents/planner.md +112 -0
- package/src/prompts/agents/task.md +15 -0
- package/src/prompts/review-request.md +44 -8
- package/src/prompts/system/custom-system-prompt.md +80 -0
- package/src/prompts/system/file-operations.md +12 -0
- package/src/prompts/system/system-prompt.md +237 -0
- package/src/prompts/system/title-system.md +2 -0
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/read.md +1 -1
- package/src/prompts/tools/task.md +34 -22
- package/src/core/tools/rulebook.ts +0 -132
- package/src/prompts/architect-plan.md +0 -10
- package/src/prompts/implement-with-critic.md +0 -11
- package/src/prompts/implement.md +0 -11
- package/src/prompts/system-prompt.md +0 -43
- package/src/prompts/task.md +0 -14
- package/src/prompts/title-system.md +0 -8
- /package/src/prompts/{init.md → agents/init.md} +0 -0
- /package/src/prompts/{reviewer.md → agents/reviewer.md} +0 -0
- /package/src/prompts/{branch-summary-preamble.md → compaction/branch-summary-preamble.md} +0 -0
- /package/src/prompts/{branch-summary.md → compaction/branch-summary.md} +0 -0
- /package/src/prompts/{compaction-summary.md → compaction/compaction-summary.md} +0 -0
- /package/src/prompts/{compaction-turn-prefix.md → compaction/compaction-turn-prefix.md} +0 -0
- /package/src/prompts/{compaction-update-summary.md → compaction/compaction-update-summary.md} +0 -0
- /package/src/prompts/{summarization-system.md → system/summarization-system.md} +0 -0
package/src/core/tools/git.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
|
2
2
|
import { type GitParams, gitTool as gitToolCore, type ToolResponse } from "@oh-my-pi/pi-git-tool";
|
|
3
3
|
import { type Static, Type } from "@sinclair/typebox";
|
|
4
4
|
import gitDescription from "../../prompts/tools/git.md" with { type: "text" };
|
|
5
|
+
import { renderPromptTemplate } from "../prompt-templates";
|
|
5
6
|
import type { ToolSession } from "./index";
|
|
6
7
|
|
|
7
8
|
const gitSchema = Type.Object({
|
|
@@ -200,7 +201,7 @@ export function createGitTool(session: ToolSession): AgentTool<typeof gitSchema,
|
|
|
200
201
|
return {
|
|
201
202
|
name: "git",
|
|
202
203
|
label: "Git",
|
|
203
|
-
description: gitDescription,
|
|
204
|
+
description: renderPromptTemplate(gitDescription),
|
|
204
205
|
parameters: gitSchema,
|
|
205
206
|
execute: async (_toolCallId, params: Static<typeof gitSchema>, _signal?: AbortSignal) => {
|
|
206
207
|
if (params.operation === "commit" && !params.message) {
|
|
@@ -224,7 +225,6 @@ export function createGitTool(session: ToolSession): AgentTool<typeof gitSchema,
|
|
|
224
225
|
export const gitTool = createGitTool({
|
|
225
226
|
cwd: process.cwd(),
|
|
226
227
|
hasUI: false,
|
|
227
|
-
rulebookRules: [],
|
|
228
228
|
getSessionFile: () => null,
|
|
229
229
|
getSessionSpawns: () => null,
|
|
230
230
|
})!;
|
package/src/core/tools/grep.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { getLanguageFromPath, type Theme } from "../../modes/interactive/theme/t
|
|
|
8
8
|
import grepDescription from "../../prompts/tools/grep.md" with { type: "text" };
|
|
9
9
|
import { ensureTool } from "../../utils/tools-manager";
|
|
10
10
|
import type { RenderResultOptions } from "../custom-tools/types";
|
|
11
|
+
import { renderPromptTemplate } from "../prompt-templates";
|
|
11
12
|
import { ScopeSignal, untilAborted } from "../utils";
|
|
12
13
|
import type { ToolSession } from "./index";
|
|
13
14
|
import { resolveToCwd } from "./path-utils";
|
|
@@ -96,7 +97,7 @@ export function createGrepTool(session: ToolSession, options?: GrepToolOptions):
|
|
|
96
97
|
return {
|
|
97
98
|
name: "grep",
|
|
98
99
|
label: "Grep",
|
|
99
|
-
description: grepDescription,
|
|
100
|
+
description: renderPromptTemplate(grepDescription),
|
|
100
101
|
parameters: grepSchema,
|
|
101
102
|
execute: async (
|
|
102
103
|
_toolCallId: string,
|
|
@@ -5,7 +5,6 @@ function createTestSession(overrides: Partial<ToolSession> = {}): ToolSession {
|
|
|
5
5
|
return {
|
|
6
6
|
cwd: "/tmp/test",
|
|
7
7
|
hasUI: false,
|
|
8
|
-
rulebookRules: [],
|
|
9
8
|
getSessionFile: () => null,
|
|
10
9
|
getSessionSpawns: () => "*",
|
|
11
10
|
...overrides,
|
|
@@ -75,32 +74,6 @@ describe("createTools", () => {
|
|
|
75
74
|
expect(names).toContain("ask");
|
|
76
75
|
});
|
|
77
76
|
|
|
78
|
-
it("excludes rulebook tool when no rules provided", async () => {
|
|
79
|
-
const session = createTestSession({ rulebookRules: [] });
|
|
80
|
-
const tools = await createTools(session);
|
|
81
|
-
const names = tools.map((t) => t.name);
|
|
82
|
-
|
|
83
|
-
expect(names).not.toContain("rulebook");
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("includes rulebook tool when rules provided", async () => {
|
|
87
|
-
const session = createTestSession({
|
|
88
|
-
rulebookRules: [
|
|
89
|
-
{
|
|
90
|
-
path: "/test/rule.md",
|
|
91
|
-
name: "Test Rule",
|
|
92
|
-
content: "Test content",
|
|
93
|
-
description: "A test rule",
|
|
94
|
-
_source: { provider: "test", providerName: "Test", path: "/test", level: "project" },
|
|
95
|
-
},
|
|
96
|
-
],
|
|
97
|
-
});
|
|
98
|
-
const tools = await createTools(session);
|
|
99
|
-
const names = tools.map((t) => t.name);
|
|
100
|
-
|
|
101
|
-
expect(names).toContain("rulebook");
|
|
102
|
-
});
|
|
103
|
-
|
|
104
77
|
it("excludes git tool when disabled in settings", async () => {
|
|
105
78
|
const session = createTestSession({
|
|
106
79
|
settings: {
|
|
@@ -174,7 +147,6 @@ describe("createTools", () => {
|
|
|
174
147
|
"notebook",
|
|
175
148
|
"output",
|
|
176
149
|
"read",
|
|
177
|
-
"rulebook",
|
|
178
150
|
"task",
|
|
179
151
|
"web_fetch",
|
|
180
152
|
"web_search",
|
package/src/core/tools/index.ts
CHANGED
|
@@ -26,7 +26,6 @@ export { createNotebookTool, type NotebookToolDetails } from "./notebook";
|
|
|
26
26
|
export { createOutputTool, type OutputToolDetails } from "./output";
|
|
27
27
|
export { createReadTool, type ReadToolDetails } from "./read";
|
|
28
28
|
export { reportFindingTool, type SubmitReviewDetails } from "./review";
|
|
29
|
-
export { filterRulebookRules, formatRulesForPrompt, type RulebookToolDetails } from "./rulebook";
|
|
30
29
|
export { createSshTool, type SSHToolDetails } from "./ssh";
|
|
31
30
|
export { BUNDLED_AGENTS, createTaskTool, taskTool } from "./task/index";
|
|
32
31
|
export {
|
|
@@ -62,7 +61,6 @@ export {
|
|
|
62
61
|
export { createWriteTool, type WriteToolDetails } from "./write";
|
|
63
62
|
|
|
64
63
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
65
|
-
import type { Rule } from "../../capability/rule";
|
|
66
64
|
import type { EventBus } from "../event-bus";
|
|
67
65
|
import type { BashInterceptorRule } from "../settings-manager";
|
|
68
66
|
import { createAskTool } from "./ask";
|
|
@@ -79,7 +77,6 @@ import { createNotebookTool } from "./notebook";
|
|
|
79
77
|
import { createOutputTool } from "./output";
|
|
80
78
|
import { createReadTool } from "./read";
|
|
81
79
|
import { reportFindingTool } from "./review";
|
|
82
|
-
import { createRulebookTool } from "./rulebook";
|
|
83
80
|
import { createSshTool } from "./ssh";
|
|
84
81
|
import { createTaskTool } from "./task/index";
|
|
85
82
|
import { createWebFetchTool } from "./web-fetch";
|
|
@@ -95,8 +92,6 @@ export interface ToolSession {
|
|
|
95
92
|
cwd: string;
|
|
96
93
|
/** Whether UI is available */
|
|
97
94
|
hasUI: boolean;
|
|
98
|
-
/** Rulebook rules */
|
|
99
|
-
rulebookRules: Rule[];
|
|
100
95
|
/** Event bus for tool/extension communication */
|
|
101
96
|
eventBus?: EventBus;
|
|
102
97
|
/** Output schema for structured completion (subagents) */
|
|
@@ -141,7 +136,6 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
|
|
|
141
136
|
notebook: createNotebookTool,
|
|
142
137
|
output: createOutputTool,
|
|
143
138
|
read: createReadTool,
|
|
144
|
-
rulebook: createRulebookTool,
|
|
145
139
|
task: createTaskTool,
|
|
146
140
|
web_fetch: createWebFetchTool,
|
|
147
141
|
web_search: createWebSearchTool,
|
|
@@ -6,6 +6,7 @@ import type { BunFile } from "bun";
|
|
|
6
6
|
import { type Theme, theme } from "../../../modes/interactive/theme/theme";
|
|
7
7
|
import lspDescription from "../../../prompts/tools/lsp.md" with { type: "text" };
|
|
8
8
|
import { logger } from "../../logger";
|
|
9
|
+
import { renderPromptTemplate } from "../../prompt-templates";
|
|
9
10
|
import { once, untilAborted } from "../../utils";
|
|
10
11
|
import type { ToolSession } from "../index";
|
|
11
12
|
import { resolveToCwd } from "../path-utils";
|
|
@@ -737,7 +738,7 @@ export function createLspTool(session: ToolSession): AgentTool<typeof lspSchema,
|
|
|
737
738
|
return {
|
|
738
739
|
name: "lsp",
|
|
739
740
|
label: "LSP",
|
|
740
|
-
description: lspDescription,
|
|
741
|
+
description: renderPromptTemplate(lspDescription),
|
|
741
742
|
parameters: lspSchema,
|
|
742
743
|
renderCall,
|
|
743
744
|
renderResult,
|
package/src/core/tools/output.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { Type } from "@sinclair/typebox";
|
|
|
14
14
|
import type { Theme } from "../../modes/interactive/theme/theme";
|
|
15
15
|
import outputDescription from "../../prompts/tools/output.md" with { type: "text" };
|
|
16
16
|
import type { RenderResultOptions } from "../custom-tools/types";
|
|
17
|
+
import { renderPromptTemplate } from "../prompt-templates";
|
|
17
18
|
import type { ToolSession } from "./index";
|
|
18
19
|
import {
|
|
19
20
|
formatCount,
|
|
@@ -236,7 +237,7 @@ export function createOutputTool(session: ToolSession): AgentTool<typeof outputS
|
|
|
236
237
|
return {
|
|
237
238
|
name: "output",
|
|
238
239
|
label: "Output",
|
|
239
|
-
description: outputDescription,
|
|
240
|
+
description: renderPromptTemplate(outputDescription),
|
|
240
241
|
parameters: outputSchema,
|
|
241
242
|
execute: async (
|
|
242
243
|
_toolCallId: string,
|
package/src/core/tools/read.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { formatDimensionNote, resizeImage } from "../../utils/image-resize";
|
|
|
12
12
|
import { detectSupportedImageMimeTypeFromFile } from "../../utils/mime";
|
|
13
13
|
import { ensureTool } from "../../utils/tools-manager";
|
|
14
14
|
import type { RenderResultOptions } from "../custom-tools/types";
|
|
15
|
+
import { renderPromptTemplate } from "../prompt-templates";
|
|
15
16
|
import type { ToolSession } from "../sdk";
|
|
16
17
|
import { ScopeSignal, untilAborted } from "../utils";
|
|
17
18
|
import { createLsTool } from "./ls";
|
|
@@ -429,7 +430,9 @@ export function createReadTool(session: ToolSession): AgentTool<typeof readSchem
|
|
|
429
430
|
return {
|
|
430
431
|
name: "read",
|
|
431
432
|
label: "Read",
|
|
432
|
-
description: readDescription
|
|
433
|
+
description: renderPromptTemplate(readDescription, {
|
|
434
|
+
DEFAULT_MAX_LINES: String(DEFAULT_MAX_LINES),
|
|
435
|
+
}),
|
|
433
436
|
parameters: readSchema,
|
|
434
437
|
execute: async (
|
|
435
438
|
toolCallId: string,
|
package/src/core/tools/ssh.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { loadSync } from "../../discovery/index";
|
|
|
8
8
|
import type { Theme } from "../../modes/interactive/theme/theme";
|
|
9
9
|
import sshDescriptionBase from "../../prompts/tools/ssh.md" with { type: "text" };
|
|
10
10
|
import type { RenderResultOptions } from "../custom-tools/types";
|
|
11
|
+
import { renderPromptTemplate } from "../prompt-templates";
|
|
11
12
|
import type { SSHHostInfo } from "../ssh/connection-manager";
|
|
12
13
|
import { ensureHostInfo, getHostInfoForHost } from "../ssh/connection-manager";
|
|
13
14
|
import { executeSSH } from "../ssh/ssh-executor";
|
|
@@ -54,11 +55,12 @@ function formatHostEntry(host: SSHHost): string {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
function formatDescription(hosts: SSHHost[]): string {
|
|
58
|
+
const baseDescription = renderPromptTemplate(sshDescriptionBase);
|
|
57
59
|
if (hosts.length === 0) {
|
|
58
|
-
return
|
|
60
|
+
return baseDescription;
|
|
59
61
|
}
|
|
60
62
|
const hostList = hosts.map(formatHostEntry).join("\n");
|
|
61
|
-
return `${
|
|
63
|
+
return `${baseDescription}\n\nAvailable hosts:\n${hostList}`;
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
function quoteRemotePath(value: string): string {
|
|
@@ -4,47 +4,73 @@
|
|
|
4
4
|
* Agents are embedded at build time via Bun's import with { type: "text" }.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import exploreMd from "../../../prompts/agents/explore.md" with { type: "text" };
|
|
7
8
|
// Embed agent markdown files at build time
|
|
8
|
-
import
|
|
9
|
-
import planMd from "../../../prompts/plan.md" with { type: "text" };
|
|
10
|
-
import reviewerMd from "../../../prompts/reviewer.md" with { type: "text" };
|
|
11
|
-
import taskMd from "../../../prompts/task.md" with { type: "text" };
|
|
9
|
+
import agentFrontmatterTemplate from "../../../prompts/agents/frontmatter.md" with { type: "text" };
|
|
10
|
+
import planMd from "../../../prompts/agents/plan.md" with { type: "text" };
|
|
11
|
+
import reviewerMd from "../../../prompts/agents/reviewer.md" with { type: "text" };
|
|
12
|
+
import taskMd from "../../../prompts/agents/task.md" with { type: "text" };
|
|
13
|
+
import { renderPromptTemplate } from "../../prompt-templates";
|
|
12
14
|
import type { AgentDefinition, AgentSource } from "./types";
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
interface AgentFrontmatter {
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
spawns?: string;
|
|
20
|
+
model?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface EmbeddedAgentDef {
|
|
24
|
+
fileName: string;
|
|
25
|
+
frontmatter?: AgentFrontmatter;
|
|
26
|
+
template: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function buildAgentContent(def: EmbeddedAgentDef): string {
|
|
30
|
+
const body = renderPromptTemplate(def.template);
|
|
31
|
+
if (!def.frontmatter) return body;
|
|
32
|
+
return renderPromptTemplate(agentFrontmatterTemplate, { ...def.frontmatter, body });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const EMBEDDED_AGENT_DEFS: EmbeddedAgentDef[] = [
|
|
36
|
+
{ fileName: "explore.md", template: exploreMd },
|
|
37
|
+
{ fileName: "plan.md", template: planMd },
|
|
38
|
+
{ fileName: "reviewer.md", template: reviewerMd },
|
|
18
39
|
{
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
name: task
|
|
22
|
-
description: General-purpose subagent with full capabilities for delegated multi-step tasks
|
|
23
|
-
spawns: explore
|
|
24
|
-
model: default
|
|
25
|
-
|
|
26
|
-
|
|
40
|
+
fileName: "task.md",
|
|
41
|
+
frontmatter: {
|
|
42
|
+
name: "task",
|
|
43
|
+
description: "General-purpose subagent with full capabilities for delegated multi-step tasks",
|
|
44
|
+
spawns: "explore",
|
|
45
|
+
model: "default",
|
|
46
|
+
},
|
|
47
|
+
template: taskMd,
|
|
27
48
|
},
|
|
28
49
|
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
name: quick_task
|
|
32
|
-
description: Quick task for fast execution
|
|
33
|
-
model: pi/smol
|
|
34
|
-
|
|
35
|
-
|
|
50
|
+
fileName: "quick_task.md",
|
|
51
|
+
frontmatter: {
|
|
52
|
+
name: "quick_task",
|
|
53
|
+
description: "Quick task for fast execution",
|
|
54
|
+
model: "pi/smol",
|
|
55
|
+
},
|
|
56
|
+
template: taskMd,
|
|
36
57
|
},
|
|
37
58
|
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
name: deep_task
|
|
41
|
-
description: Deep task for comprehensive reasoning
|
|
42
|
-
model: pi/slow
|
|
43
|
-
|
|
44
|
-
|
|
59
|
+
fileName: "deep_task.md",
|
|
60
|
+
frontmatter: {
|
|
61
|
+
name: "deep_task",
|
|
62
|
+
description: "Deep task for comprehensive reasoning",
|
|
63
|
+
model: "pi/slow",
|
|
64
|
+
},
|
|
65
|
+
template: taskMd,
|
|
45
66
|
},
|
|
46
67
|
];
|
|
47
68
|
|
|
69
|
+
const EMBEDDED_AGENTS: { name: string; content: string }[] = EMBEDDED_AGENT_DEFS.map((def) => ({
|
|
70
|
+
name: def.fileName,
|
|
71
|
+
content: buildAgentContent(def),
|
|
72
|
+
}));
|
|
73
|
+
|
|
48
74
|
/**
|
|
49
75
|
* Parse YAML frontmatter from markdown content.
|
|
50
76
|
*/
|
|
@@ -9,16 +9,13 @@ import { type SlashCommand, slashCommandCapability } from "../../../capability/s
|
|
|
9
9
|
import { loadSync } from "../../../discovery";
|
|
10
10
|
|
|
11
11
|
// Embed command markdown files at build time
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import initMd from "../../../prompts/init.md" with { type: "text" };
|
|
12
|
+
import initMd from "../../../prompts/agents/init.md" with { type: "text" };
|
|
13
|
+
import plannerMd from "../../../prompts/agents/planner.md" with { type: "text" };
|
|
14
|
+
import { renderPromptTemplate } from "../../prompt-templates";
|
|
16
15
|
|
|
17
16
|
const EMBEDDED_COMMANDS: { name: string; content: string }[] = [
|
|
18
|
-
{ name: "
|
|
19
|
-
{ name: "
|
|
20
|
-
{ name: "implement.md", content: implementMd },
|
|
21
|
-
{ name: "init.md", content: initMd },
|
|
17
|
+
{ name: "init.md", content: renderPromptTemplate(initMd) },
|
|
18
|
+
{ name: "planner.md", content: renderPromptTemplate(plannerMd) },
|
|
22
19
|
];
|
|
23
20
|
|
|
24
21
|
export const EMBEDDED_COMMAND_TEMPLATES: ReadonlyArray<{ name: string; content: string }> = EMBEDDED_COMMANDS;
|
|
@@ -17,6 +17,7 @@ import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
|
17
17
|
import type { Usage } from "@oh-my-pi/pi-ai";
|
|
18
18
|
import type { Theme } from "../../../modes/interactive/theme/theme";
|
|
19
19
|
import taskDescriptionTemplate from "../../../prompts/tools/task.md" with { type: "text" };
|
|
20
|
+
import { renderPromptTemplate } from "../../prompt-templates";
|
|
20
21
|
import { formatDuration } from "../render-utils";
|
|
21
22
|
import { cleanupTempDir, createTempArtifactsDir, getArtifactsDir } from "./artifacts";
|
|
22
23
|
import { discoverAgents, getAgent } from "./discovery";
|
|
@@ -95,21 +96,12 @@ export { taskSchema } from "./types";
|
|
|
95
96
|
async function buildDescription(cwd: string): Promise<string> {
|
|
96
97
|
const { agents } = await discoverAgents(cwd);
|
|
97
98
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
if (agents.length > MAX_AGENTS_IN_DESCRIPTION) {
|
|
105
|
-
agentLines.push(` ...and ${agents.length - MAX_AGENTS_IN_DESCRIPTION} more agents`);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Fill template placeholders
|
|
109
|
-
return taskDescriptionTemplate
|
|
110
|
-
.replace("{{AGENTS_LIST}}", agentLines.join("\n"))
|
|
111
|
-
.replace("{{MAX_PARALLEL_TASKS}}", String(MAX_PARALLEL_TASKS))
|
|
112
|
-
.replace("{{MAX_CONCURRENCY}}", String(MAX_CONCURRENCY));
|
|
99
|
+
return renderPromptTemplate(taskDescriptionTemplate, {
|
|
100
|
+
agents: agents.slice(0, MAX_AGENTS_IN_DESCRIPTION),
|
|
101
|
+
moreAgents: agents.length > MAX_AGENTS_IN_DESCRIPTION ? agents.length - MAX_AGENTS_IN_DESCRIPTION : 0,
|
|
102
|
+
MAX_PARALLEL_TASKS,
|
|
103
|
+
MAX_CONCURRENCY,
|
|
104
|
+
});
|
|
113
105
|
}
|
|
114
106
|
|
|
115
107
|
/**
|
|
@@ -10,6 +10,7 @@ import { type Theme, theme } from "../../modes/interactive/theme/theme";
|
|
|
10
10
|
import webFetchDescription from "../../prompts/tools/web-fetch.md" with { type: "text" };
|
|
11
11
|
import { ensureTool } from "../../utils/tools-manager";
|
|
12
12
|
import type { RenderResultOptions } from "../custom-tools/types";
|
|
13
|
+
import { renderPromptTemplate } from "../prompt-templates";
|
|
13
14
|
import type { ToolSession } from "./index";
|
|
14
15
|
import { specialHandlers } from "./web-scrapers/index";
|
|
15
16
|
import type { RenderResult } from "./web-scrapers/types";
|
|
@@ -836,7 +837,7 @@ export function createWebFetchTool(_session: ToolSession): AgentTool<typeof webF
|
|
|
836
837
|
return {
|
|
837
838
|
name: "web_fetch",
|
|
838
839
|
label: "Web Fetch",
|
|
839
|
-
description: webFetchDescription,
|
|
840
|
+
description: renderPromptTemplate(webFetchDescription),
|
|
840
841
|
parameters: webFetchSchema,
|
|
841
842
|
execute: async (
|
|
842
843
|
_toolCallId: string,
|
|
@@ -4,19 +4,26 @@
|
|
|
4
4
|
* 4-tier auth resolution:
|
|
5
5
|
* 1. ANTHROPIC_SEARCH_API_KEY / ANTHROPIC_SEARCH_BASE_URL env vars
|
|
6
6
|
* 2. Provider with api="anthropic-messages" in ~/.omp/agent/models.json
|
|
7
|
-
* 3. OAuth credentials in ~/.omp/agent/
|
|
7
|
+
* 3. OAuth credentials in ~/.omp/agent/agent.db (with expiry check)
|
|
8
8
|
* 4. ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL fallback
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import * as os from "node:os";
|
|
12
12
|
import * as path from "node:path";
|
|
13
13
|
import { buildBetaHeader, claudeCodeHeaders, claudeCodeVersion } from "@oh-my-pi/pi-ai";
|
|
14
|
-
import { getConfigDirPaths } from "../../../config";
|
|
15
|
-
import
|
|
14
|
+
import { getAgentDbPath, getConfigDirPaths } from "../../../config";
|
|
15
|
+
import { AgentStorage } from "../../agent-storage";
|
|
16
|
+
import type { AuthCredential, AuthCredentialEntry, AuthStorageData } from "../../auth-storage";
|
|
17
|
+
import { migrateJsonStorage } from "../../storage-migration";
|
|
18
|
+
import type { AnthropicAuthConfig, AnthropicOAuthCredential, ModelsJson } from "./types";
|
|
16
19
|
|
|
17
20
|
const DEFAULT_BASE_URL = "https://api.anthropic.com";
|
|
18
21
|
|
|
19
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Parses a .env file and extracts key-value pairs.
|
|
24
|
+
* @param filePath - Path to the .env file
|
|
25
|
+
* @returns Object containing parsed environment variables
|
|
26
|
+
*/
|
|
20
27
|
async function parseEnvFile(filePath: string): Promise<Record<string, string>> {
|
|
21
28
|
const result: Record<string, string> = {};
|
|
22
29
|
try {
|
|
@@ -47,7 +54,11 @@ async function parseEnvFile(filePath: string): Promise<Record<string, string>> {
|
|
|
47
54
|
return result;
|
|
48
55
|
}
|
|
49
56
|
|
|
50
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* Gets an environment variable from process.env or .env files.
|
|
59
|
+
* @param key - The environment variable name to look up
|
|
60
|
+
* @returns The value if found, undefined otherwise
|
|
61
|
+
*/
|
|
51
62
|
export async function getEnv(key: string): Promise<string | undefined> {
|
|
52
63
|
if (process.env[key]) return process.env[key];
|
|
53
64
|
|
|
@@ -60,7 +71,11 @@ export async function getEnv(key: string): Promise<string | undefined> {
|
|
|
60
71
|
return undefined;
|
|
61
72
|
}
|
|
62
73
|
|
|
63
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Reads and parses a JSON file safely.
|
|
76
|
+
* @param filePath - Path to the JSON file
|
|
77
|
+
* @returns Parsed JSON content, or null if file doesn't exist or parsing fails
|
|
78
|
+
*/
|
|
64
79
|
async function readJson<T>(filePath: string): Promise<T | null> {
|
|
65
80
|
try {
|
|
66
81
|
const file = Bun.file(filePath);
|
|
@@ -72,22 +87,85 @@ async function readJson<T>(filePath: string): Promise<T | null> {
|
|
|
72
87
|
}
|
|
73
88
|
}
|
|
74
89
|
|
|
75
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* Checks if a token is an OAuth token by looking for sk-ant-oat prefix.
|
|
92
|
+
* @param apiKey - The API key to check
|
|
93
|
+
* @returns True if the token is an OAuth token
|
|
94
|
+
*/
|
|
76
95
|
export function isOAuthToken(apiKey: string): boolean {
|
|
77
96
|
return apiKey.includes("sk-ant-oat");
|
|
78
97
|
}
|
|
79
98
|
|
|
80
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Converts a generic AuthCredential to AnthropicOAuthCredential if it's a valid OAuth entry.
|
|
101
|
+
* @param credential - The credential to convert
|
|
102
|
+
* @returns The converted OAuth credential, or null if not a valid OAuth type
|
|
103
|
+
*/
|
|
104
|
+
function toAnthropicOAuthCredential(credential: AuthCredential): AnthropicOAuthCredential | null {
|
|
105
|
+
if (credential.type !== "oauth") return null;
|
|
106
|
+
if (typeof credential.access !== "string" || typeof credential.expires !== "number") return null;
|
|
107
|
+
return {
|
|
108
|
+
type: "oauth",
|
|
109
|
+
access: credential.access,
|
|
110
|
+
refresh: credential.refresh,
|
|
111
|
+
expires: credential.expires,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function normalizeAuthEntry(entry: AuthCredentialEntry | undefined): AuthCredential[] {
|
|
81
116
|
if (!entry) return [];
|
|
82
117
|
return Array.isArray(entry) ? entry : [entry];
|
|
83
118
|
}
|
|
84
119
|
|
|
120
|
+
async function readLegacyAnthropicOAuthCredentials(configDir: string): Promise<AnthropicOAuthCredential[]> {
|
|
121
|
+
const authJson = await readJson<AuthStorageData>(path.join(configDir, "auth.json"));
|
|
122
|
+
if (!authJson) return [];
|
|
123
|
+
const entry = authJson.anthropic as AuthCredentialEntry | undefined;
|
|
124
|
+
const credentials = normalizeAuthEntry(entry);
|
|
125
|
+
const results: AnthropicOAuthCredential[] = [];
|
|
126
|
+
for (const credential of credentials) {
|
|
127
|
+
const mapped = toAnthropicOAuthCredential(credential);
|
|
128
|
+
if (mapped) results.push(mapped);
|
|
129
|
+
}
|
|
130
|
+
return results;
|
|
131
|
+
}
|
|
132
|
+
|
|
85
133
|
/**
|
|
86
|
-
*
|
|
134
|
+
* Reads Anthropic OAuth credentials from agent.db, migrating from legacy auth.json if needed.
|
|
135
|
+
* @param configDir - Path to the config directory containing agent.db
|
|
136
|
+
* @returns Array of valid Anthropic OAuth credentials
|
|
137
|
+
*/
|
|
138
|
+
async function readAnthropicOAuthCredentials(configDir: string): Promise<AnthropicOAuthCredential[]> {
|
|
139
|
+
await migrateJsonStorage({
|
|
140
|
+
agentDir: configDir,
|
|
141
|
+
settingsPath: path.join(configDir, "settings.json"),
|
|
142
|
+
authPaths: [path.join(configDir, "auth.json")],
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const storage = AgentStorage.open(getAgentDbPath(configDir));
|
|
146
|
+
const records = storage.listAuthCredentials("anthropic");
|
|
147
|
+
const credentials: AnthropicOAuthCredential[] = [];
|
|
148
|
+
for (const record of records) {
|
|
149
|
+
const mapped = toAnthropicOAuthCredential(record.credential);
|
|
150
|
+
if (mapped) {
|
|
151
|
+
credentials.push(mapped);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (credentials.length === 0) {
|
|
156
|
+
return readLegacyAnthropicOAuthCredentials(configDir);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return credentials;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Finds Anthropic auth config using 4-tier priority:
|
|
87
164
|
* 1. ANTHROPIC_SEARCH_API_KEY / ANTHROPIC_SEARCH_BASE_URL
|
|
88
165
|
* 2. Provider with api="anthropic-messages" in models.json
|
|
89
|
-
* 3. OAuth in
|
|
166
|
+
* 3. OAuth in agent.db (with 5-minute expiry buffer)
|
|
90
167
|
* 4. ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL fallback
|
|
168
|
+
* @returns The first valid auth configuration found, or null if none available
|
|
91
169
|
*/
|
|
92
170
|
export async function findAnthropicAuth(): Promise<AnthropicAuthConfig | null> {
|
|
93
171
|
// Get all config directories (user-level only) for fallback support
|
|
@@ -131,14 +209,13 @@ export async function findAnthropicAuth(): Promise<AnthropicAuthConfig | null> {
|
|
|
131
209
|
}
|
|
132
210
|
}
|
|
133
211
|
|
|
134
|
-
// 3. OAuth credentials in
|
|
212
|
+
// 3. OAuth credentials in agent.db (with 5-minute expiry buffer, check all config dirs)
|
|
135
213
|
const expiryBuffer = 5 * 60 * 1000; // 5 minutes
|
|
136
214
|
const now = Date.now();
|
|
137
215
|
for (const configDir of configDirs) {
|
|
138
|
-
const
|
|
139
|
-
const credentials = normalizeAnthropicOAuthCredentials(authJson?.anthropic);
|
|
216
|
+
const credentials = await readAnthropicOAuthCredentials(configDir);
|
|
140
217
|
for (const credential of credentials) {
|
|
141
|
-
if (
|
|
218
|
+
if (!credential.access) continue;
|
|
142
219
|
if (credential.expires > now + expiryBuffer) {
|
|
143
220
|
return {
|
|
144
221
|
apiKey: credential.access,
|
|
@@ -163,6 +240,11 @@ export async function findAnthropicAuth(): Promise<AnthropicAuthConfig | null> {
|
|
|
163
240
|
return null;
|
|
164
241
|
}
|
|
165
242
|
|
|
243
|
+
/**
|
|
244
|
+
* Checks if a base URL points to the official Anthropic API.
|
|
245
|
+
* @param baseUrl - The base URL to check
|
|
246
|
+
* @returns True if the URL is for api.anthropic.com over HTTPS
|
|
247
|
+
*/
|
|
166
248
|
function isAnthropicBaseUrl(baseUrl: string): boolean {
|
|
167
249
|
try {
|
|
168
250
|
const url = new URL(baseUrl);
|
|
@@ -172,7 +254,11 @@ function isAnthropicBaseUrl(baseUrl: string): boolean {
|
|
|
172
254
|
}
|
|
173
255
|
}
|
|
174
256
|
|
|
175
|
-
/**
|
|
257
|
+
/**
|
|
258
|
+
* Builds HTTP headers for Anthropic API requests.
|
|
259
|
+
* @param auth - The authentication configuration
|
|
260
|
+
* @returns Headers object ready for use in fetch requests
|
|
261
|
+
*/
|
|
176
262
|
export function buildAnthropicHeaders(auth: AnthropicAuthConfig): Record<string, string> {
|
|
177
263
|
const baseBetas = auth.isOAuth
|
|
178
264
|
? [
|
|
@@ -205,7 +291,11 @@ export function buildAnthropicHeaders(auth: AnthropicAuthConfig): Record<string,
|
|
|
205
291
|
return headers;
|
|
206
292
|
}
|
|
207
293
|
|
|
208
|
-
/**
|
|
294
|
+
/**
|
|
295
|
+
* Builds the full API URL for Anthropic messages endpoint.
|
|
296
|
+
* @param auth - The authentication configuration
|
|
297
|
+
* @returns The complete API URL with beta query parameter
|
|
298
|
+
*/
|
|
209
299
|
export function buildAnthropicUrl(auth: AnthropicAuthConfig): string {
|
|
210
300
|
const base = `${auth.baseUrl}/v1/messages`;
|
|
211
301
|
return `${base}?beta=true`;
|
|
@@ -17,6 +17,7 @@ import { Type } from "@sinclair/typebox";
|
|
|
17
17
|
import type { Theme } from "../../../modes/interactive/theme/theme";
|
|
18
18
|
import webSearchDescription from "../../../prompts/tools/web-search.md" with { type: "text" };
|
|
19
19
|
import type { CustomTool, CustomToolContext, RenderResultOptions } from "../../custom-tools/types";
|
|
20
|
+
import { renderPromptTemplate } from "../../prompt-templates";
|
|
20
21
|
import { callExaTool, findApiKey as findExaKey, formatSearchResults, isSearchResponse } from "../exa/mcp-client";
|
|
21
22
|
import { renderExaCall, renderExaResult } from "../exa/render";
|
|
22
23
|
import type { ExaRenderDetails } from "../exa/types";
|
|
@@ -332,7 +333,7 @@ async function executeWebSearch(
|
|
|
332
333
|
export const webSearchTool: AgentTool<typeof webSearchSchema> = {
|
|
333
334
|
name: "web_search",
|
|
334
335
|
label: "Web Search",
|
|
335
|
-
description: webSearchDescription,
|
|
336
|
+
description: renderPromptTemplate(webSearchDescription),
|
|
336
337
|
parameters: webSearchSchema,
|
|
337
338
|
execute: async (toolCallId, params) => {
|
|
338
339
|
return executeWebSearch(toolCallId, params as WebSearchParams);
|
|
@@ -343,7 +344,7 @@ export const webSearchTool: AgentTool<typeof webSearchSchema> = {
|
|
|
343
344
|
export const webSearchCustomTool: CustomTool<typeof webSearchSchema, WebSearchRenderDetails> = {
|
|
344
345
|
name: "web_search",
|
|
345
346
|
label: "Web Search",
|
|
346
|
-
description: webSearchDescription,
|
|
347
|
+
description: renderPromptTemplate(webSearchDescription),
|
|
347
348
|
parameters: webSearchSchema,
|
|
348
349
|
|
|
349
350
|
async execute(
|