@oh-my-pi/pi-coding-agent 15.0.2 → 15.1.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.
Files changed (138) hide show
  1. package/CHANGELOG.md +56 -1
  2. package/examples/custom-tools/README.md +11 -7
  3. package/examples/custom-tools/hello/index.ts +2 -2
  4. package/examples/extensions/README.md +19 -8
  5. package/examples/extensions/api-demo.ts +15 -19
  6. package/examples/extensions/hello.ts +5 -6
  7. package/examples/extensions/plan-mode.ts +1 -1
  8. package/examples/extensions/reload-runtime.ts +4 -3
  9. package/examples/extensions/with-deps/index.ts +4 -3
  10. package/examples/sdk/06-extensions.ts +4 -2
  11. package/package.json +7 -17
  12. package/src/autoresearch/tools/init-experiment.ts +38 -41
  13. package/src/autoresearch/tools/log-experiment.ts +32 -41
  14. package/src/autoresearch/tools/run-experiment.ts +3 -3
  15. package/src/autoresearch/tools/update-notes.ts +11 -11
  16. package/src/commit/agentic/tools/analyze-file.ts +4 -4
  17. package/src/commit/agentic/tools/git-file-diff.ts +4 -4
  18. package/src/commit/agentic/tools/git-hunk.ts +5 -5
  19. package/src/commit/agentic/tools/git-overview.ts +4 -4
  20. package/src/commit/agentic/tools/propose-changelog.ts +13 -13
  21. package/src/commit/agentic/tools/propose-commit.ts +6 -6
  22. package/src/commit/agentic/tools/recent-commits.ts +3 -3
  23. package/src/commit/agentic/tools/schemas.ts +28 -28
  24. package/src/commit/agentic/tools/split-commit.ts +22 -21
  25. package/src/commit/analysis/summary.ts +4 -4
  26. package/src/commit/changelog/generate.ts +7 -11
  27. package/src/commit/shared-llm.ts +22 -34
  28. package/src/config/config-file.ts +35 -13
  29. package/src/config/model-registry.ts +9 -190
  30. package/src/config/models-config-schema.ts +166 -0
  31. package/src/config/settings-schema.ts +18 -0
  32. package/src/edit/index.ts +2 -2
  33. package/src/edit/modes/apply-patch.ts +7 -6
  34. package/src/edit/modes/patch.ts +18 -25
  35. package/src/edit/modes/replace.ts +18 -20
  36. package/src/eval/js/shared/rewrite-imports.ts +131 -10
  37. package/src/eval/py/executor.ts +233 -623
  38. package/src/eval/py/kernel.ts +27 -2
  39. package/src/exa/factory.ts +5 -4
  40. package/src/exa/mcp-client.ts +1 -1
  41. package/src/exa/researcher.ts +9 -20
  42. package/src/exa/search.ts +26 -52
  43. package/src/exa/types.ts +1 -1
  44. package/src/exa/websets.ts +54 -53
  45. package/src/exec/bash-executor.ts +2 -1
  46. package/src/extensibility/custom-commands/loader.ts +5 -3
  47. package/src/extensibility/custom-commands/types.ts +4 -2
  48. package/src/extensibility/custom-tools/loader.ts +5 -3
  49. package/src/extensibility/custom-tools/types.ts +7 -6
  50. package/src/extensibility/custom-tools/wrapper.ts +1 -1
  51. package/src/extensibility/extensions/loader.ts +7 -3
  52. package/src/extensibility/extensions/types.ts +9 -5
  53. package/src/extensibility/extensions/wrapper.ts +1 -2
  54. package/src/extensibility/hooks/loader.ts +3 -1
  55. package/src/extensibility/hooks/tool-wrapper.ts +1 -1
  56. package/src/extensibility/hooks/types.ts +4 -2
  57. package/src/extensibility/plugins/legacy-pi-compat.ts +30 -0
  58. package/src/extensibility/shared-events.ts +1 -1
  59. package/src/extensibility/typebox.ts +391 -0
  60. package/src/goals/tools/goal-tool.ts +6 -12
  61. package/src/hashline/types.ts +4 -4
  62. package/src/hindsight/state.ts +2 -2
  63. package/src/index.ts +0 -2
  64. package/src/internal-urls/docs-index.generated.ts +7 -7
  65. package/src/lsp/types.ts +30 -38
  66. package/src/mcp/manager.ts +1 -1
  67. package/src/mcp/tool-bridge.ts +1 -1
  68. package/src/modes/components/session-observer-overlay.ts +12 -1
  69. package/src/modes/components/status-line/segments.ts +2 -1
  70. package/src/modes/controllers/command-controller.ts +27 -2
  71. package/src/modes/controllers/event-controller.ts +3 -4
  72. package/src/modes/interactive-mode.ts +1 -1
  73. package/src/modes/rpc/host-tools.ts +1 -1
  74. package/src/modes/rpc/rpc-client.ts +1 -1
  75. package/src/modes/rpc/rpc-types.ts +1 -1
  76. package/src/modes/theme/theme.ts +111 -117
  77. package/src/modes/types.ts +1 -1
  78. package/src/modes/utils/context-usage.ts +2 -2
  79. package/src/sdk.ts +31 -8
  80. package/src/session/agent-session.ts +74 -104
  81. package/src/session/messages.ts +16 -51
  82. package/src/session/session-manager.ts +22 -2
  83. package/src/session/streaming-output.ts +16 -6
  84. package/src/task/executor.ts +208 -86
  85. package/src/task/index.ts +15 -11
  86. package/src/task/render.ts +32 -5
  87. package/src/task/types.ts +54 -39
  88. package/src/tools/ask.ts +12 -12
  89. package/src/tools/ast-edit.ts +11 -15
  90. package/src/tools/ast-grep.ts +9 -10
  91. package/src/tools/bash.ts +9 -23
  92. package/src/tools/browser.ts +39 -53
  93. package/src/tools/calculator.ts +12 -11
  94. package/src/tools/checkpoint.ts +7 -7
  95. package/src/tools/debug.ts +40 -43
  96. package/src/tools/eval.ts +6 -8
  97. package/src/tools/find.ts +10 -13
  98. package/src/tools/gh.ts +71 -128
  99. package/src/tools/hindsight-recall.ts +4 -6
  100. package/src/tools/hindsight-reflect.ts +5 -5
  101. package/src/tools/hindsight-retain.ts +15 -17
  102. package/src/tools/image-gen.ts +32 -82
  103. package/src/tools/index.ts +4 -1
  104. package/src/tools/inspect-image.ts +8 -9
  105. package/src/tools/irc.ts +15 -27
  106. package/src/tools/job.ts +14 -21
  107. package/src/tools/read.ts +7 -8
  108. package/src/tools/recipe/index.ts +7 -9
  109. package/src/tools/render-mermaid.ts +12 -12
  110. package/src/tools/report-tool-issue.ts +4 -4
  111. package/src/tools/resolve.ts +11 -11
  112. package/src/tools/review.ts +14 -26
  113. package/src/tools/search-tool-bm25.ts +7 -9
  114. package/src/tools/search.ts +19 -22
  115. package/src/tools/ssh.ts +7 -7
  116. package/src/tools/todo-write.ts +26 -34
  117. package/src/tools/vim.ts +10 -26
  118. package/src/tools/write.ts +5 -5
  119. package/src/tools/yield.ts +100 -54
  120. package/src/web/search/index.ts +9 -24
  121. package/src/prompts/compaction/branch-summary-context.md +0 -5
  122. package/src/prompts/compaction/branch-summary-preamble.md +0 -2
  123. package/src/prompts/compaction/branch-summary.md +0 -30
  124. package/src/prompts/compaction/compaction-short-summary.md +0 -9
  125. package/src/prompts/compaction/compaction-summary-context.md +0 -5
  126. package/src/prompts/compaction/compaction-summary.md +0 -38
  127. package/src/prompts/compaction/compaction-turn-prefix.md +0 -17
  128. package/src/prompts/compaction/compaction-update-summary.md +0 -45
  129. package/src/prompts/system/auto-handoff-threshold-focus.md +0 -1
  130. package/src/prompts/system/file-operations.md +0 -10
  131. package/src/prompts/system/handoff-document.md +0 -49
  132. package/src/prompts/system/summarization-system.md +0 -3
  133. package/src/session/compaction/branch-summarization.ts +0 -324
  134. package/src/session/compaction/compaction.ts +0 -1420
  135. package/src/session/compaction/errors.ts +0 -31
  136. package/src/session/compaction/index.ts +0 -8
  137. package/src/session/compaction/pruning.ts +0 -91
  138. package/src/session/compaction/utils.ts +0 -184
@@ -1,31 +0,0 @@
1
- /**
2
- * Compaction error types.
3
- *
4
- * `CompactionCancelledError` is the canonical signal raised when a compaction
5
- * is explicitly aborted — operator Esc, extension hook returning `cancel`,
6
- * programmatic `session.abortCompaction()` call, or any other deliberate
7
- * abort source. Downstream callers (e.g. `executeCompaction`) discriminate
8
- * cancellation from other failures via `instanceof CompactionCancelledError`
9
- * rather than introspecting error messages or `name` fields — the typed
10
- * sentinel makes classification source-agnostic and refactor-stable.
11
- */
12
-
13
- export class CompactionCancelledError extends Error {
14
- readonly name = "CompactionCancelledError" as const;
15
-
16
- constructor(message = "Compaction cancelled") {
17
- super(message);
18
- }
19
- }
20
-
21
- /**
22
- * Outcome of a compaction attempt, surfaced by `CommandController.executeCompaction`
23
- * so callers (e.g. the plan-mode approval flow) can distinguish a deliberate abort
24
- * from an unrelated failure.
25
- *
26
- * "ok" — compaction completed; transcript was summarized.
27
- * "cancelled" — `CompactionCancelledError` was raised. Operator Esc, extension
28
- * hook, programmatic abort — all source-agnostic.
29
- * "failed" — any other rejection from `session.compact()`.
30
- */
31
- export type CompactionOutcome = "ok" | "cancelled" | "failed";
@@ -1,8 +0,0 @@
1
- /**
2
- * Compaction and summarization utilities.
3
- */
4
-
5
- export * from "./branch-summarization";
6
- export * from "./compaction";
7
- export * from "./errors";
8
- export * from "./utils";
@@ -1,91 +0,0 @@
1
- /**
2
- * Tool output pruning utilities for compaction.
3
- */
4
- import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
5
- import type { ToolResultMessage } from "@oh-my-pi/pi-ai";
6
- import type { SessionEntry, SessionMessageEntry } from "../session-manager";
7
- import { estimateTokens } from "./compaction";
8
-
9
- export interface PruneConfig {
10
- /** Keep the most recent tool output tokens intact. */
11
- protectTokens: number;
12
- /** Only prune if total savings meets this threshold. */
13
- minimumSavings: number;
14
- /** Tool names that should never be pruned. */
15
- protectedTools: string[];
16
- }
17
-
18
- export const DEFAULT_PRUNE_CONFIG: PruneConfig = {
19
- protectTokens: 40_000,
20
- minimumSavings: 20_000,
21
- protectedTools: ["skill", "read"],
22
- };
23
-
24
- export interface PruneResult {
25
- prunedCount: number;
26
- tokensSaved: number;
27
- }
28
-
29
- function createPrunedNotice(tokens: number): string {
30
- return `[Output truncated - ${tokens} tokens]`;
31
- }
32
-
33
- function getToolResultMessage(entry: SessionEntry): ToolResultMessage | undefined {
34
- if (entry.type !== "message") return undefined;
35
- const message = entry.message as AgentMessage;
36
- if (message.role !== "toolResult") return undefined;
37
- return message as ToolResultMessage;
38
- }
39
-
40
- function estimatePrunedSavings(tokens: number): number {
41
- const noticeTokens = Math.ceil(createPrunedNotice(tokens).length / 4);
42
- return Math.max(0, tokens - noticeTokens);
43
- }
44
-
45
- export function pruneToolOutputs(entries: SessionEntry[], config: PruneConfig = DEFAULT_PRUNE_CONFIG): PruneResult {
46
- let accumulatedTokens = 0;
47
- let tokensSaved = 0;
48
- let prunedCount = 0;
49
-
50
- const candidates: Array<{ entry: SessionMessageEntry; tokens: number }> = [];
51
-
52
- for (let i = entries.length - 1; i >= 0; i--) {
53
- const entry = entries[i];
54
- const message = getToolResultMessage(entry);
55
- if (!message) continue;
56
-
57
- const tokens = estimateTokens(message as AgentMessage);
58
- const isProtected = config.protectedTools.includes(message.toolName);
59
-
60
- if (message.prunedAt !== undefined) {
61
- accumulatedTokens += tokens;
62
- continue;
63
- }
64
-
65
- if (accumulatedTokens < config.protectTokens || isProtected) {
66
- accumulatedTokens += tokens;
67
- continue;
68
- }
69
-
70
- candidates.push({ entry: entry as SessionMessageEntry, tokens });
71
- accumulatedTokens += tokens;
72
- }
73
-
74
- for (const candidate of candidates) {
75
- tokensSaved += estimatePrunedSavings(candidate.tokens);
76
- }
77
-
78
- if (tokensSaved < config.minimumSavings || candidates.length === 0) {
79
- return { prunedCount: 0, tokensSaved: 0 };
80
- }
81
-
82
- const prunedAt = Date.now();
83
- for (const candidate of candidates) {
84
- const message = candidate.entry.message as ToolResultMessage;
85
- message.content = [{ type: "text", text: createPrunedNotice(candidate.tokens) }];
86
- message.prunedAt = prunedAt;
87
- prunedCount++;
88
- }
89
-
90
- return { prunedCount, tokensSaved };
91
- }
@@ -1,184 +0,0 @@
1
- /**
2
- * Shared utilities for compaction and branch summarization.
3
- */
4
- import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
5
- import type { Message } from "@oh-my-pi/pi-ai";
6
- import { prompt } from "@oh-my-pi/pi-utils";
7
- import fileOperationsTemplate from "../../prompts/system/file-operations.md" with { type: "text" };
8
- import summarizationSystemPrompt from "../../prompts/system/summarization-system.md" with { type: "text" };
9
-
10
- // ============================================================================
11
- // File Operation Tracking
12
- // ============================================================================
13
-
14
- export interface FileOperations {
15
- read: Set<string>;
16
- written: Set<string>;
17
- edited: Set<string>;
18
- }
19
-
20
- export function createFileOps(): FileOperations {
21
- return {
22
- read: new Set(),
23
- written: new Set(),
24
- edited: new Set(),
25
- };
26
- }
27
-
28
- /**
29
- * Extract file operations from tool calls in an assistant message.
30
- */
31
- export function extractFileOpsFromMessage(message: AgentMessage, fileOps: FileOperations): void {
32
- if (message.role !== "assistant") return;
33
- if (!("content" in message) || !Array.isArray(message.content)) return;
34
-
35
- for (const block of message.content) {
36
- if (typeof block !== "object" || block === null) continue;
37
- if (!("type" in block) || block.type !== "toolCall") continue;
38
- if (!("arguments" in block) || !("name" in block)) continue;
39
-
40
- const args = block.arguments as Record<string, unknown> | undefined;
41
- if (!args) continue;
42
-
43
- const path = typeof args.path === "string" ? args.path : undefined;
44
- if (!path) continue;
45
-
46
- switch (block.name) {
47
- case "read":
48
- fileOps.read.add(path);
49
- break;
50
- case "write":
51
- fileOps.written.add(path);
52
- break;
53
- case "edit":
54
- fileOps.edited.add(path);
55
- break;
56
- }
57
- }
58
- }
59
-
60
- /**
61
- * Compute final file lists from file operations.
62
- * Returns readFiles (files only read, not modified) and modifiedFiles.
63
- */
64
- export function computeFileLists(fileOps: FileOperations): { readFiles: string[]; modifiedFiles: string[] } {
65
- const modified = new Set([...fileOps.edited, ...fileOps.written]);
66
- const readOnly = [...fileOps.read].filter(f => !modified.has(f)).sort();
67
- const modifiedFiles = [...modified].sort();
68
- return { readFiles: readOnly, modifiedFiles };
69
- }
70
-
71
- /**
72
- * Format file operations as XML tags for summary.
73
- */
74
- const FILE_OPERATION_SUMMARY_LIMIT = 20;
75
-
76
- function truncateFileList(files: string[]): string[] {
77
- if (files.length <= FILE_OPERATION_SUMMARY_LIMIT) return files;
78
- const omitted = files.length - FILE_OPERATION_SUMMARY_LIMIT;
79
- return [...files.slice(0, FILE_OPERATION_SUMMARY_LIMIT), `… (${omitted} more files omitted)`];
80
- }
81
-
82
- function stripFileOperationTags(summary: string): string {
83
- const withoutReadFiles = summary.replace(/<read-files>[\s\S]*?<\/read-files>\s*/g, "");
84
- const withoutModifiedFiles = withoutReadFiles.replace(/<modified-files>[\s\S]*?<\/modified-files>\s*/g, "");
85
- return withoutModifiedFiles.trimEnd();
86
- }
87
- export function formatFileOperations(readFiles: string[], modifiedFiles: string[]): string {
88
- if (readFiles.length === 0 && modifiedFiles.length === 0) return "";
89
- return prompt.render(fileOperationsTemplate, {
90
- readFiles: truncateFileList(readFiles),
91
- modifiedFiles: truncateFileList(modifiedFiles),
92
- });
93
- }
94
-
95
- export function upsertFileOperations(summary: string, readFiles: string[], modifiedFiles: string[]): string {
96
- const baseSummary = stripFileOperationTags(summary);
97
- const fileOperations = formatFileOperations(readFiles, modifiedFiles);
98
- if (!fileOperations) return baseSummary;
99
- if (!baseSummary) return fileOperations;
100
- return `${baseSummary}\n\n${fileOperations}`;
101
- }
102
-
103
- // ============================================================================
104
- // Message Serialization
105
- // ============================================================================
106
-
107
- /** Maximum characters for a tool result in serialized summaries. */
108
- const TOOL_RESULT_MAX_CHARS = 2000;
109
-
110
- /**
111
- * Truncate text to a maximum character length for summarization.
112
- * Keeps the beginning and appends a truncation marker.
113
- */
114
- function truncateForSummary(text: string, maxChars: number): string {
115
- if (text.length <= maxChars) return text;
116
- const truncatedChars = text.length - maxChars;
117
- return `${text.slice(0, maxChars)}\n\n[... ${truncatedChars} more characters truncated]`;
118
- }
119
-
120
- /**
121
- * Serialize LLM messages to text for summarization.
122
- * This prevents the model from treating it as a conversation to continue.
123
- * Call convertToLlm() first to handle custom message types.
124
- */
125
- export function serializeConversation(messages: Message[]): string {
126
- const parts: string[] = [];
127
-
128
- for (const msg of messages) {
129
- if (msg.role === "user") {
130
- const content =
131
- typeof msg.content === "string"
132
- ? msg.content
133
- : msg.content
134
- .filter((c): c is { type: "text"; text: string } => c.type === "text")
135
- .map(c => c.text)
136
- .join("");
137
- if (content) parts.push(`[User]: ${content}`);
138
- } else if (msg.role === "assistant") {
139
- const textParts: string[] = [];
140
- const thinkingParts: string[] = [];
141
- const toolCalls: string[] = [];
142
-
143
- for (const block of msg.content) {
144
- if (block.type === "text") {
145
- textParts.push(block.text);
146
- } else if (block.type === "thinking") {
147
- thinkingParts.push(block.thinking);
148
- } else if (block.type === "toolCall") {
149
- const args = block.arguments as Record<string, unknown>;
150
- const argsStr = Object.entries(args)
151
- .map(([k, v]) => `${k}=${JSON.stringify(v)}`)
152
- .join(", ");
153
- toolCalls.push(`${block.name}(${argsStr})`);
154
- }
155
- }
156
-
157
- if (thinkingParts.length > 0) {
158
- parts.push(`[Assistant thinking]: ${thinkingParts.join("\n")}`);
159
- }
160
- if (textParts.length > 0) {
161
- parts.push(`[Assistant]: ${textParts.join("\n")}`);
162
- }
163
- if (toolCalls.length > 0) {
164
- parts.push(`[Assistant tool calls]: ${toolCalls.join("; ")}`);
165
- }
166
- } else if (msg.role === "toolResult") {
167
- const content = msg.content
168
- .filter((c): c is { type: "text"; text: string } => c.type === "text")
169
- .map(c => c.text)
170
- .join("");
171
- if (content) {
172
- parts.push(`[Tool result]: ${truncateForSummary(content, TOOL_RESULT_MAX_CHARS)}`);
173
- }
174
- }
175
- }
176
-
177
- return parts.join("\n\n");
178
- }
179
-
180
- // ============================================================================
181
- // Summarization System Prompt
182
- // ============================================================================
183
-
184
- export const SUMMARIZATION_SYSTEM_PROMPT = prompt.render(summarizationSystemPrompt);