@oh-my-pi/pi-coding-agent 13.3.14 → 13.4.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 (54) hide show
  1. package/CHANGELOG.md +69 -9
  2. package/examples/sdk/README.md +22 -0
  3. package/package.json +7 -7
  4. package/src/capability/index.ts +1 -11
  5. package/src/commit/analysis/index.ts +4 -4
  6. package/src/config/settings-schema.ts +18 -15
  7. package/src/config/settings.ts +2 -20
  8. package/src/discovery/index.ts +1 -11
  9. package/src/exa/index.ts +1 -10
  10. package/src/extensibility/custom-commands/index.ts +2 -15
  11. package/src/extensibility/custom-tools/index.ts +3 -18
  12. package/src/extensibility/custom-tools/loader.ts +28 -5
  13. package/src/extensibility/custom-tools/types.ts +18 -1
  14. package/src/extensibility/extensions/index.ts +9 -130
  15. package/src/extensibility/extensions/types.ts +2 -1
  16. package/src/extensibility/hooks/index.ts +3 -14
  17. package/src/extensibility/plugins/index.ts +6 -31
  18. package/src/index.ts +28 -220
  19. package/src/internal-urls/docs-index.generated.ts +3 -2
  20. package/src/internal-urls/index.ts +11 -16
  21. package/src/mcp/index.ts +11 -37
  22. package/src/mcp/transports/index.ts +2 -2
  23. package/src/modes/components/extensions/index.ts +3 -3
  24. package/src/modes/components/index.ts +35 -40
  25. package/src/modes/rpc/rpc-mode.ts +1 -7
  26. package/src/patch/index.ts +4 -20
  27. package/src/prompts/system/system-prompt.md +3 -3
  28. package/src/prompts/tools/ast-edit.md +30 -0
  29. package/src/prompts/tools/{ast-find.md → ast-grep.md} +10 -12
  30. package/src/prompts/tools/bash.md +2 -2
  31. package/src/prompts/tools/hashline.md +2 -0
  32. package/src/prompts/tools/resolve.md +8 -0
  33. package/src/sdk.ts +27 -7
  34. package/src/session/agent-session.ts +24 -33
  35. package/src/session/session-manager.ts +0 -30
  36. package/src/stt/index.ts +3 -3
  37. package/src/task/types.ts +2 -2
  38. package/src/tools/ast-edit.ts +480 -0
  39. package/src/tools/{ast-find.ts → ast-grep.ts} +195 -86
  40. package/src/tools/bash.ts +3 -2
  41. package/src/tools/gemini-image.ts +3 -3
  42. package/src/tools/index.ts +55 -57
  43. package/src/tools/pending-action.ts +49 -0
  44. package/src/tools/render-utils.ts +10 -0
  45. package/src/tools/renderers.ts +6 -4
  46. package/src/tools/resolve.ts +156 -0
  47. package/src/web/search/index.ts +6 -4
  48. package/src/web/search/providers/anthropic.ts +2 -2
  49. package/src/web/search/providers/base.ts +3 -0
  50. package/src/web/search/providers/exa.ts +11 -5
  51. package/src/web/search/providers/gemini.ts +112 -24
  52. package/src/patch/normative.ts +0 -72
  53. package/src/prompts/tools/ast-replace.md +0 -37
  54. package/src/tools/ast-replace.ts +0 -300
package/CHANGELOG.md CHANGED
@@ -2,6 +2,66 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [13.4.1] - 2026-03-01
6
+
7
+ ### Fixed
8
+
9
+ - Pending resolve reminders now trigger as soon as a preview action is queued, before the next assistant turn, with regression coverage in `agent-session-resolve-reminder` tests
10
+
11
+ ## [13.4.0] - 2026-03-01
12
+
13
+ ### Breaking Changes
14
+
15
+ - `ast_grep` parameter `pattern` (string) replaced by `patterns` (string[])
16
+ - `ast_edit` parameters `pattern` + `rewrite` replaced by `ops: Array<{ pat: string; out: string }>`
17
+
18
+ ### Added
19
+
20
+ - Added `resolve` tool to apply or discard pending preview actions with required reasoning
21
+ - AST edit now registers pending actions after preview, allowing explicit apply/discard workflow via `resolve` tool
22
+ - Custom tools can register pending actions via `pushPendingAction(action)` in `CustomToolAPI`, enabling the `resolve` workflow for custom preview-apply flows
23
+ - `deferrable?: boolean` field added to `AgentTool`, `CustomTool`, and `ToolDefinition` interfaces; tools that set it signal they may stage pending actions
24
+ - `HIDDEN_TOOLS` and `ResolveTool` exported from `@oh-my-pi/pi-coding-agent` SDK for manual tool composition
25
+ - `PendingActionStore` now uses a LIFO stack (`push`/`peek`/`pop`); multiple deferrable tools can stage actions that resolve in reverse order of registration
26
+ - Added `gemini`, `codex`, and `synthetic` as supported values for the `providers.webSearch` setting
27
+ - `ast_grep` tool now accepts a `patterns` array (replaces single `pattern`); multiple patterns run in one native pass and results are merged before offset/limit
28
+ - `ast_edit` tool now accepts an `ops` array of `{ pat, out }` entries (replaces `pattern` + `rewrite`); duplicate patterns are rejected upfront
29
+ - AST find output now uses `>>` prefix on match-start lines and pads line numbers; directory-tree grouping with `# dir` / `## └─ file` headers for directory-scoped searches
30
+ - AST replace output now renders diff-style (`-before` / `+after`) change previews grouped by directory
31
+ - Both AST tools now report `scopePath`, `files`, and per-file match/replacement counts in tool details
32
+ - Task item `id` max length raised from 32 to 48 characters
33
+ - Anthropic web search provider now uses `buildAnthropicSearchHeaders` (dedicated search header builder separate from inference headers)
34
+ - Gemini web search provider: endpoint fallback (daily → sandbox) with retry on 429/5xx
35
+ - Gemini web search now injects Antigravity system instruction and aligned request metadata (`requestType`, `userAgent`, `requestId`) for Antigravity credentials
36
+ - `buildGeminiRequestTools()` helper for composable Gemini tool configuration (googleSearch, codeExecution, urlContext)
37
+ - Web search schema exposes `max_tokens`, `temperature`, and `num_search_results` as tool parameters
38
+ - Web search provider fallback: when an explicit provider is unavailable, resolves the auto chain instead of returning empty results
39
+
40
+ ### Changed
41
+
42
+ - Simplified `resolve` tool output rendering to use inline highlighted format instead of boxed layout
43
+ - Updated `resolve` tool to parse source tool name from label using colon separator for cleaner display
44
+ - `resolve` tool is now conditionally injected: included only when at least one active tool has `deferrable: true` (previously always included)
45
+ - `discoverAndLoadCustomTools` / `loadCustomTools` accept an optional `pendingActionStore` parameter to wire `pushPendingAction` for custom tools
46
+ - AST edit tool no longer accepts `preview` parameter; all AST edit calls now return previews by default
47
+ - AST edit workflow changed: preview is always shown, then use `resolve` tool to apply or discard changes
48
+ - Agent now suggests calling `resolve` tool after AST edit preview with system reminder
49
+ - `ast_grep`: `include_meta` parameter removed; metavariable captures are now always included in output
50
+ - `ast_edit`: `dry_run` renamed to `preview`; `max_files` removed from schema and capped globally via `$PI_MAX_AST_FILES` (default 1000); `max_replacements` renamed to `limit`
51
+ - `ast_grep` and `ast_edit`: parse errors in tool output are now capped at `PARSE_ERRORS_LIMIT` (20); excess errors are summarised as `N / total parse issues` rather than flooding the context
52
+ - Updated `ast_grep` and `ast_edit` tool prompt examples to use concise, idiomatic patterns
53
+
54
+ ### Removed
55
+
56
+ - Removed `normativeRewrite` setting that rewrote tool call arguments to normalized format in session history
57
+ - Removed `buildNormativeUpdateInput()` helper and normative patch transformation logic
58
+
59
+ ### Fixed
60
+
61
+ - `ast_edit` no longer rejects empty `out` values; an empty string now deletes matched nodes
62
+ - `ast_edit` no longer trims `pat` and `out` values, preserving intentional whitespace
63
+ - `gemini_image` tool: corrected `responseModalities` values from `'Image'`/`'Text'` to uppercase `'IMAGE'`/`'TEXT'` matching the API enum
64
+
5
65
  ## [13.3.14] - 2026-02-28
6
66
 
7
67
  ### Added
@@ -9,7 +69,7 @@
9
69
  - Expanded AST tool language support from 7 to all 25 ast-grep tree-sitter languages (Bash, C, C++, C#, CSS, Elixir, Go, Haskell, HCL, HTML, Java, JavaScript, JSON, Kotlin, Lua, Nix, PHP, Python, Ruby, Rust, Scala, Solidity, Swift, TSX, TypeScript, YAML)
10
70
  - AST find now emits all lines of multiline matches with hashline tags (LINE#HASH:content) consistent with read/grep output
11
71
  - Added AST pattern syntax reference (metavariables, wildcards, variadics) to system prompt
12
- - Added examples and scoping guidance to ast-find and ast-replace tool prompts
72
+ - Added examples and scoping guidance to ast-grep and ast-edit tool prompts
13
73
  - Added `provider-schema-compatibility.test.ts`: integration test that instantiates every builtin and hidden tool, runs their parameter schemas through `adaptSchemaForStrict`, `sanitizeSchemaForGoogle`, and `prepareSchemaForCCA`, and asserts zero violations against each provider's compatibility rules
14
74
 
15
75
  ### Fixed
@@ -28,9 +88,9 @@
28
88
 
29
89
  ### Added
30
90
 
31
- - Added `ast_find` tool for structural code search using AST matching via ast-grep, enabling syntax-aware pattern discovery across codebases
32
- - Added `ast_replace` tool for structural AST-aware rewrites via ast-grep, enabling safe syntax-level codemods without text-based fragility
33
- - Added `astFind.enabled` and `astReplace.enabled` settings to control availability of AST tools
91
+ - Added `ast_grep` tool for structural code search using AST matching via ast-grep, enabling syntax-aware pattern discovery across codebases
92
+ - Added `ast_edit` tool for structural AST-aware rewrites via ast-grep, enabling safe syntax-level codemods without text-based fragility
93
+ - Added `astGrep.enabled` and `astEdit.enabled` settings to control availability of AST tools
34
94
  - Added system prompt guidance to prefer AST tools over bash text manipulation (grep/sed/awk/perl) for syntax-aware operations
35
95
  - Extracted prompt formatting logic into reusable `formatPromptContent()` utility with configurable render phases and formatting options
36
96
  - Added `type_definition` action to navigate to symbol type definitions with source context
@@ -57,10 +117,10 @@
57
117
  - Updated HTML parsing API calls from `node-html-parser` to `linkedom` across all web scrapers (arXiv, IACR, Go pkg, Read the Docs, Twitter, Wikipedia)
58
118
  - Changed element text extraction from `.text` property to `.textContent` property for compatibility with linkedom DOM API
59
119
  - Optimized document link extraction to use regex-based parsing with deduplication and a 20-link limit instead of full DOM traversal
60
- - Unified `path` parameter in ast_find and ast_replace tools to accept files, directories, or glob patterns directly, eliminating the separate `glob` parameter
61
- - Removed `strictness` parameter from ast_find and ast_replace tools
62
- - Removed `fail_on_parse_error` parameter from ast_replace tool (now always false)
63
- - Updated ast_find and ast_replace prompt guidance to clarify that `path` accepts glob patterns and no longer requires separate glob specification
120
+ - Unified `path` parameter in ast_grep and ast_edit tools to accept files, directories, or glob patterns directly, eliminating the separate `glob` parameter
121
+ - Removed `strictness` parameter from ast_grep and ast_edit tools
122
+ - Removed `fail_on_parse_error` parameter from ast_edit tool (now always false)
123
+ - Updated ast_grep and ast_edit prompt guidance to clarify that `path` accepts glob patterns and no longer requires separate glob specification
64
124
  - Refactored prompt template rendering to use unified `formatPromptContent()` function with phase-aware formatting (pre-render vs post-render)
65
125
  - Updated `format-prompts.ts` script to use centralized prompt formatting utility instead of inline implementation
66
126
  - Replaced `column` parameter with `symbol` parameter for more intuitive position specification
@@ -5362,4 +5422,4 @@ Initial public release.
5362
5422
  - Git branch display in footer
5363
5423
  - Message queueing during streaming responses
5364
5424
  - OAuth integration for Gmail and Google Calendar access
5365
- - HTML export with syntax highlighting and collapsible sections
5425
+ - HTML export with syntax highlighting and collapsible sections
@@ -45,7 +45,9 @@ import {
45
45
  ModelRegistry,
46
46
  SessionManager,
47
47
  BUILTIN_TOOLS,
48
+ HIDDEN_TOOLS,
48
49
  createTools,
50
+ ResolveTool,
49
51
  } from "@oh-my-pi/pi-coding-agent";
50
52
 
51
53
  // Auth and models setup
@@ -104,6 +106,26 @@ session.subscribe((event) => {
104
106
  await session.prompt("Hello");
105
107
  ```
106
108
 
109
+ ## Resolve preview workflow (AST edit apply/discard)
110
+
111
+ `ast_edit` now always returns a preview. To finalize, call hidden `resolve` with a required reason.
112
+
113
+ - `action: "apply"` → commit pending preview changes
114
+ - `action: "discard"` → drop pending preview changes
115
+ - `reason: string` is required for both paths
116
+
117
+ `createAgentSession()` / `createTools()` include `resolve` automatically, even when filtering `toolNames`.
118
+ If you are composing tools manually, use `HIDDEN_TOOLS.resolve` (or `ResolveTool`) and wire the same `pendingActionStore`.
119
+
120
+ ```typescript
121
+ const tools = await createTools(toolSession, ["ast_edit"]); // resolve is auto-included
122
+ const resolveTool = tools.find(t => t.name === "resolve") as ResolveTool;
123
+
124
+ await resolveTool.execute("call-1", {
125
+ action: "apply",
126
+ reason: "Preview matches expected replacements",
127
+ });
128
+ ```
107
129
  ## Options
108
130
 
109
131
  | Option | Default | Description |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-coding-agent",
4
- "version": "13.3.14",
4
+ "version": "13.4.1",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/can1357/oh-my-pi",
7
7
  "author": "Can Boluk",
@@ -41,12 +41,12 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@mozilla/readability": "^0.6",
44
- "@oh-my-pi/omp-stats": "13.3.14",
45
- "@oh-my-pi/pi-agent-core": "13.3.14",
46
- "@oh-my-pi/pi-ai": "13.3.14",
47
- "@oh-my-pi/pi-natives": "13.3.14",
48
- "@oh-my-pi/pi-tui": "13.3.14",
49
- "@oh-my-pi/pi-utils": "13.3.14",
44
+ "@oh-my-pi/omp-stats": "13.4.1",
45
+ "@oh-my-pi/pi-agent-core": "13.4.1",
46
+ "@oh-my-pi/pi-ai": "13.4.1",
47
+ "@oh-my-pi/pi-natives": "13.4.1",
48
+ "@oh-my-pi/pi-tui": "13.4.1",
49
+ "@oh-my-pi/pi-utils": "13.4.1",
50
50
  "@sinclair/typebox": "^0.34",
51
51
  "@xterm/headless": "^6.0",
52
52
  "ajv": "^8.18",
@@ -419,14 +419,4 @@ export function cacheStats(): { content: number; dir: number } {
419
419
  // Re-exports
420
420
  // =============================================================================
421
421
 
422
- export type {
423
- Capability,
424
- CapabilityInfo,
425
- CapabilityResult,
426
- LoadContext,
427
- LoadOptions,
428
- LoadResult,
429
- Provider,
430
- ProviderInfo,
431
- SourceMeta,
432
- } from "./types";
422
+ export type * from "./types";
@@ -1,4 +1,4 @@
1
- export { generateConventionalAnalysis } from "./conventional";
2
- export { extractScopeCandidates } from "./scope";
3
- export { generateSummary, stripTypePrefix } from "./summary";
4
- export { validateAnalysis, validateScope, validateSummary } from "./validation";
1
+ export * from "./conventional";
2
+ export * from "./scope";
3
+ export * from "./summary";
4
+ export * from "./validation";
@@ -254,15 +254,6 @@ export const SETTINGS_SCHEMA = {
254
254
  submenu: true,
255
255
  },
256
256
  },
257
- normativeRewrite: {
258
- type: "boolean",
259
- default: false,
260
- ui: {
261
- tab: "agent",
262
- label: "Normative rewrite",
263
- description: "Rewrite tool call arguments to normalized format in session history",
264
- },
265
- },
266
257
  repeatToolDescriptions: {
267
258
  type: "boolean",
268
259
  default: false,
@@ -455,18 +446,18 @@ export const SETTINGS_SCHEMA = {
455
446
  submenu: true,
456
447
  },
457
448
  },
458
- "astFind.enabled": {
449
+ "astGrep.enabled": {
459
450
  type: "boolean",
460
451
  default: true,
461
- ui: { tab: "tools", label: "Enable AST Find", description: "Enable the ast_find tool for structural AST search" },
452
+ ui: { tab: "tools", label: "Enable AST Grep", description: "Enable the ast_grep tool for structural AST search" },
462
453
  },
463
- "astReplace.enabled": {
454
+ "astEdit.enabled": {
464
455
  type: "boolean",
465
456
  default: true,
466
457
  ui: {
467
458
  tab: "tools",
468
- label: "Enable AST Replace",
469
- description: "Enable the ast_replace tool for structural AST rewrites",
459
+ label: "Enable AST Edit",
460
+ description: "Enable the ast_edit tool for structural AST rewrites",
470
461
  },
471
462
  },
472
463
  "notebook.enabled": {
@@ -737,7 +728,19 @@ export const SETTINGS_SCHEMA = {
737
728
  // ─────────────────────────────────────────────────────────────────────────
738
729
  "providers.webSearch": {
739
730
  type: "enum",
740
- values: ["auto", "exa", "brave", "jina", "kimi", "zai", "perplexity", "anthropic"] as const,
731
+ values: [
732
+ "auto",
733
+ "exa",
734
+ "brave",
735
+ "jina",
736
+ "kimi",
737
+ "zai",
738
+ "perplexity",
739
+ "anthropic",
740
+ "gemini",
741
+ "codex",
742
+ "synthetic",
743
+ ] as const,
741
744
  default: "auto",
742
745
  ui: { tab: "services", label: "Web search provider", description: "Provider for web search tool", submenu: true },
743
746
  },
@@ -41,26 +41,8 @@ import {
41
41
  } from "./settings-schema";
42
42
 
43
43
  // Re-export types that callers need
44
- export type {
45
- BashInterceptorRule,
46
- BranchSummarySettings,
47
- CommitSettings,
48
- CompactionSettings,
49
- ContextPromotionSettings,
50
- ExaSettings,
51
- GroupPrefix,
52
- GroupTypeMap,
53
- MemoriesSettings,
54
- RetrySettings,
55
- SettingPath,
56
- SettingValue,
57
- SkillsSettings,
58
- StatusLineSettings,
59
- ThinkingBudgetsSettings,
60
- TodoCompletionSettings,
61
- TtsrSettings,
62
- } from "./settings-schema";
63
- export { getDefault, getEnumValues, getPathsForTab, getType, getUi, hasUi } from "./settings-schema";
44
+ export type * from "./settings-schema";
45
+ export * from "./settings-schema";
64
46
 
65
47
  // ═══════════════════════════════════════════════════════════════════════════
66
48
  // Types
@@ -76,14 +76,4 @@ export type { SSHHost } from "../capability/ssh";
76
76
  export type { SystemPrompt } from "../capability/system-prompt";
77
77
  export type { CustomTool } from "../capability/tool";
78
78
  // Re-export types
79
- export type {
80
- Capability,
81
- CapabilityInfo,
82
- CapabilityResult,
83
- LoadContext,
84
- LoadOptions,
85
- LoadResult,
86
- Provider,
87
- ProviderInfo,
88
- SourceMeta,
89
- } from "../capability/types";
79
+ export type * from "../capability/types";
package/src/exa/index.ts CHANGED
@@ -27,16 +27,7 @@ export const exaTools: CustomTool<any, ExaRenderDetails>[] = [
27
27
 
28
28
  export { companyTool } from "./company";
29
29
  export { linkedinTool } from "./linkedin";
30
- export {
31
- callExaTool,
32
- callWebsetsTool,
33
- createMCPToolFromServer,
34
- fetchMCPToolSchema,
35
- findApiKey,
36
- formatSearchResults,
37
- isSearchResponse,
38
- MCPWrappedTool,
39
- } from "./mcp-client";
30
+ export * from "./mcp-client";
40
31
  export { renderExaCall, renderExaResult } from "./render";
41
32
  export { researcherTools } from "./researcher";
42
33
  // Re-export individual modules for selective importing
@@ -1,15 +1,2 @@
1
- export {
2
- type DiscoverCustomCommandsOptions,
3
- type DiscoverCustomCommandsResult,
4
- discoverCustomCommands,
5
- type LoadCustomCommandsOptions,
6
- loadCustomCommands,
7
- } from "./loader";
8
- export type {
9
- CustomCommand,
10
- CustomCommandAPI,
11
- CustomCommandFactory,
12
- CustomCommandSource,
13
- CustomCommandsLoadResult,
14
- LoadedCustomCommand,
15
- } from "./types";
1
+ export * from "./loader";
2
+ export type * from "./types";
@@ -2,21 +2,6 @@
2
2
  * Custom tools module.
3
3
  */
4
4
 
5
- export { CustomToolLoader, discoverAndLoadCustomTools, loadCustomTools } from "./loader";
6
- export type {
7
- AgentToolResult,
8
- AgentToolUpdateCallback,
9
- CustomTool,
10
- CustomToolAPI,
11
- CustomToolContext,
12
- CustomToolFactory,
13
- CustomToolResult,
14
- CustomToolSessionEvent,
15
- CustomToolsLoadResult,
16
- CustomToolUIContext,
17
- ExecResult,
18
- LoadedCustomTool,
19
- RenderResultOptions,
20
- ToolLoadError,
21
- } from "./types";
22
- export { CustomToolAdapter } from "./wrapper";
5
+ export * from "./loader";
6
+ export type * from "./types";
7
+ export * from "./wrapper";
@@ -14,6 +14,7 @@ import type { ExecOptions } from "../../exec/exec";
14
14
  import { execCommand } from "../../exec/exec";
15
15
  import type { HookUIContext } from "../../extensibility/hooks/types";
16
16
  import { getAllPluginToolPaths } from "../../extensibility/plugins/loader";
17
+ import type { PendingActionStore } from "../../tools/pending-action";
17
18
  import { createNoOpUIContext, resolvePath } from "../utils";
18
19
  import type { CustomToolAPI, CustomToolFactory, LoadedCustomTool, ToolLoadError } from "./types";
19
20
 
@@ -84,7 +85,7 @@ export class CustomToolLoader {
84
85
  #sharedApi: CustomToolAPI;
85
86
  #seenNames: Set<string>;
86
87
 
87
- constructor(cwd: string, builtInToolNames: string[]) {
88
+ constructor(cwd: string, builtInToolNames: string[], pendingActionStore?: PendingActionStore) {
88
89
  this.#sharedApi = {
89
90
  cwd,
90
91
  exec: (command: string, args: string[], options?: ExecOptions) =>
@@ -94,6 +95,18 @@ export class CustomToolLoader {
94
95
  logger,
95
96
  typebox,
96
97
  pi: piCodingAgent,
98
+ pushPendingAction: action => {
99
+ if (!pendingActionStore) {
100
+ throw new Error("Pending action store unavailable for custom tools in this runtime.");
101
+ }
102
+ pendingActionStore.push({
103
+ label: action.label,
104
+ sourceToolName: action.sourceToolName ?? "custom_tool",
105
+ apply: action.apply,
106
+ reject: action.reject,
107
+ details: action.details,
108
+ });
109
+ },
97
110
  };
98
111
  this.#seenNames = new Set<string>(builtInToolNames);
99
112
  }
@@ -138,8 +151,13 @@ export class CustomToolLoader {
138
151
  * @param cwd - Current working directory for resolving relative paths
139
152
  * @param builtInToolNames - Names of built-in tools to check for conflicts
140
153
  */
141
- export async function loadCustomTools(pathsWithSources: ToolPathWithSource[], cwd: string, builtInToolNames: string[]) {
142
- const loader = new CustomToolLoader(cwd, builtInToolNames);
154
+ export async function loadCustomTools(
155
+ pathsWithSources: ToolPathWithSource[],
156
+ cwd: string,
157
+ builtInToolNames: string[],
158
+ pendingActionStore?: PendingActionStore,
159
+ ) {
160
+ const loader = new CustomToolLoader(cwd, builtInToolNames, pendingActionStore);
143
161
  await loader.load(pathsWithSources);
144
162
  return {
145
163
  tools: loader.tools,
@@ -160,7 +178,12 @@ export async function loadCustomTools(pathsWithSources: ToolPathWithSource[], cw
160
178
  * @param cwd - Current working directory
161
179
  * @param builtInToolNames - Names of built-in tools to check for conflicts
162
180
  */
163
- export async function discoverAndLoadCustomTools(configuredPaths: string[], cwd: string, builtInToolNames: string[]) {
181
+ export async function discoverAndLoadCustomTools(
182
+ configuredPaths: string[],
183
+ cwd: string,
184
+ builtInToolNames: string[],
185
+ pendingActionStore?: PendingActionStore,
186
+ ) {
164
187
  const allPathsWithSources: ToolPathWithSource[] = [];
165
188
  const seen = new Set<string>();
166
189
 
@@ -193,5 +216,5 @@ export async function discoverAndLoadCustomTools(configuredPaths: string[], cwd:
193
216
  addPath(resolvePath(configPath, cwd), { provider: "config", providerName: "Config", level: "project" });
194
217
  }
195
218
 
196
- return loadCustomTools(allPathsWithSources, cwd, builtInToolNames);
219
+ return loadCustomTools(allPathsWithSources, cwd, builtInToolNames, pendingActionStore);
197
220
  }
@@ -26,6 +26,20 @@ export type { AgentToolResult, AgentToolUpdateCallback };
26
26
  // Re-export for backward compatibility
27
27
  export type { ExecOptions, ExecResult } from "../../exec/exec";
28
28
 
29
+ /** Pending action entry consumed by the hidden resolve tool */
30
+ export interface CustomToolPendingAction {
31
+ /** Human-readable preview label shown in resolve flow */
32
+ label: string;
33
+ /** Apply callback invoked when resolve(action="apply") is called */
34
+ apply(reason: string): Promise<AgentToolResult<unknown>>;
35
+ /** Optional reject callback invoked when resolve(action="discard") is called */
36
+ reject?(reason: string): Promise<AgentToolResult<unknown> | undefined>;
37
+ /** Optional details metadata stored with the pending action */
38
+ details?: unknown;
39
+ /** Optional source tool name shown by resolve renderer (defaults to "custom_tool") */
40
+ sourceToolName?: string;
41
+ }
42
+
29
43
  /** API passed to custom tool factory (stable across session changes) */
30
44
  export interface CustomToolAPI {
31
45
  /** Current working directory */
@@ -42,6 +56,8 @@ export interface CustomToolAPI {
42
56
  typebox: typeof import("@sinclair/typebox");
43
57
  /** Injected pi-coding-agent exports */
44
58
  pi: typeof import("../..");
59
+ /** Push a preview action that can later be resolved with the hidden resolve tool */
60
+ pushPendingAction(action: CustomToolPendingAction): void;
45
61
  }
46
62
 
47
63
  /**
@@ -162,7 +178,8 @@ export interface CustomTool<TParams extends TSchema = TSchema, TDetails = any> {
162
178
  parameters: TParams;
163
179
  /** If true, tool is excluded unless explicitly listed in --tools or agent's tools field */
164
180
  hidden?: boolean;
165
-
181
+ /** If true, tool may stage deferred changes that require explicit resolve/discard. */
182
+ deferrable?: boolean;
166
183
  /**
167
184
  * Execute the tool.
168
185
  * @param toolCallId - Unique ID for this tool call
@@ -3,134 +3,13 @@
3
3
  */
4
4
 
5
5
  export type { SlashCommandInfo, SlashCommandLocation, SlashCommandSource } from "../slash-commands";
6
- export { discoverAndLoadExtensions, ExtensionRuntime, loadExtensionFromFactory, loadExtensions } from "./loader";
7
- export type {
8
- BranchHandler,
9
- ExtensionErrorListener,
10
- NavigateTreeHandler,
11
- NewSessionHandler,
12
- ShutdownHandler,
13
- SwitchSessionHandler,
14
- } from "./runner";
15
- export { ExtensionRunner } from "./runner";
16
- export type {
17
- AgentEndEvent,
18
- AgentStartEvent,
19
- // Re-exports
20
- AgentToolResult,
21
- AgentToolUpdateCallback,
22
- AppAction,
23
- AppendEntryHandler,
24
- // Events - Tool (ToolCallEvent types)
25
- BashToolCallEvent,
26
- BashToolResultEvent,
27
- BeforeAgentStartEvent,
28
- BeforeAgentStartEventResult,
29
- // Events - Agent
30
- ContextEvent,
31
- // Event Results
32
- ContextEventResult,
33
- ContextUsage,
34
- CustomToolCallEvent,
35
- CustomToolResultEvent,
36
- EditToolCallEvent,
37
- EditToolResultEvent,
38
- ExecOptions,
39
- ExecResult,
40
- Extension,
41
- ExtensionActions,
42
- ExtensionAPI,
43
- ExtensionCommandContext,
44
- ExtensionCommandContextActions,
45
- // Context
46
- ExtensionContext,
47
- ExtensionContextActions,
48
- // Errors
49
- ExtensionError,
50
- ExtensionEvent,
51
- ExtensionFactory,
52
- ExtensionFlag,
53
- ExtensionHandler,
54
- ExtensionShortcut,
55
- ExtensionUIContext,
56
- ExtensionUIDialogOptions,
57
- FindToolCallEvent,
58
- FindToolResultEvent,
59
- GetActiveToolsHandler,
60
- GetAllToolsHandler,
61
- GetCommandsHandler,
62
- GetThinkingLevelHandler,
63
- GrepToolCallEvent,
64
- GrepToolResultEvent,
65
- // Events - Input
66
- InputEvent,
67
- InputEventResult,
68
- KeybindingsManager,
69
- LoadExtensionsResult,
70
- // Events - Message
71
- MessageEndEvent,
72
- // Message Rendering
73
- MessageRenderer,
74
- MessageRenderOptions,
75
- MessageStartEvent,
76
- MessageUpdateEvent,
77
- // Provider Registration
78
- ProviderConfig,
79
- ProviderModelConfig,
80
- ReadToolCallEvent,
81
- ReadToolResultEvent,
82
- // Commands
83
- RegisteredCommand,
84
- RegisteredTool,
85
- // Events - Resources
86
- ResourcesDiscoverEvent,
87
- ResourcesDiscoverResult,
88
- SendMessageHandler,
89
- SendUserMessageHandler,
90
- SessionBeforeBranchEvent,
91
- SessionBeforeBranchResult,
92
- SessionBeforeCompactEvent,
93
- SessionBeforeCompactResult,
94
- SessionBeforeSwitchEvent,
95
- SessionBeforeSwitchResult,
96
- SessionBeforeTreeEvent,
97
- SessionBeforeTreeResult,
98
- SessionBranchEvent,
99
- SessionCompactEvent,
100
- SessionCompactingEvent,
101
- SessionCompactingResult,
102
- SessionEvent,
103
- SessionShutdownEvent,
104
- // Events - Session
105
- SessionStartEvent,
106
- SessionSwitchEvent,
107
- SessionTreeEvent,
108
- SetActiveToolsHandler,
109
- SetModelHandler,
110
- SetThinkingLevelHandler,
111
- TerminalInputHandler,
112
- // Events - Tool
113
- ToolCallEvent,
114
- ToolCallEventResult,
115
- // Tools
116
- ToolDefinition,
117
- // Events - Tool Execution
118
- ToolExecutionEndEvent,
119
- ToolExecutionStartEvent,
120
- ToolExecutionUpdateEvent,
121
- ToolRenderResultOptions,
122
- ToolResultEvent,
123
- ToolResultEventResult,
124
- TreePreparation,
125
- TurnEndEvent,
126
- TurnStartEvent,
127
- UserBashEvent,
128
- UserBashEventResult,
129
- UserPythonEvent,
130
- UserPythonEventResult,
131
- WriteToolCallEvent,
132
- WriteToolResultEvent,
133
- } from "./types";
6
+ export {
7
+ discoverAndLoadExtensions,
8
+ ExtensionRuntimeNotInitializedError,
9
+ loadExtensionFromFactory,
10
+ loadExtensions,
11
+ } from "./loader";
12
+ export * from "./runner";
134
13
  // Type guards
135
- export { isToolCallEventType } from "./types";
136
- export { ExtensionToolWrapper, RegisteredToolAdapter, wrapRegisteredTool, wrapRegisteredTools } from "./wrapper";
14
+ export * from "./types";
15
+ export * from "./wrapper";
@@ -290,7 +290,8 @@ export interface ToolDefinition<TParams extends TSchema = TSchema, TDetails = un
290
290
  parameters: TParams;
291
291
  /** If true, tool is excluded unless explicitly listed in --tools or agent's tools field */
292
292
  hidden?: boolean;
293
-
293
+ /** If true, tool may stage deferred changes that require explicit resolve/discard. */
294
+ deferrable?: boolean;
294
295
  /** Execute the tool. */
295
296
  execute(
296
297
  toolCallId: string,
@@ -1,16 +1,5 @@
1
- // biome-ignore assist/source/organizeImports: biome is not smart
2
1
  export type { ReadonlySessionManager, UsageStatistics } from "../../session/session-manager";
3
- export {
4
- discoverAndLoadHooks,
5
- loadHooks,
6
- type AppendEntryHandler,
7
- type BranchHandler,
8
- type LoadedHook,
9
- type LoadHooksResult,
10
- type NavigateTreeHandler,
11
- type NewSessionHandler,
12
- type SendMessageHandler,
13
- } from "./loader";
14
- export { execCommand, HookRunner, type HookErrorListener } from "./runner";
15
- export { HookToolWrapper } from "./tool-wrapper";
2
+ export * from "./loader";
3
+ export * from "./runner";
4
+ export * from "./tool-wrapper";
16
5
  export * from "./types";