@oh-my-pi/pi-coding-agent 17.2.5 → 17.2.7

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 (201) hide show
  1. package/CHANGELOG.md +35 -1
  2. package/dist/{CHANGELOG-q2w43aw3.md → CHANGELOG-1p8yvde7.md} +35 -1
  3. package/dist/cli.js +3340 -3339
  4. package/dist/types/advisor/advise-tool.d.ts +10 -4
  5. package/dist/types/autoresearch/tools/init-experiment.d.ts +19 -7
  6. package/dist/types/autoresearch/tools/log-experiment.d.ts +19 -11
  7. package/dist/types/autoresearch/tools/run-experiment.d.ts +4 -2
  8. package/dist/types/autoresearch/tools/update-notes.d.ts +5 -2
  9. package/dist/types/cli/gc-cli.d.ts +1 -0
  10. package/dist/types/commit/agentic/tools/analyze-file.d.ts +5 -2
  11. package/dist/types/commit/agentic/tools/git-file-diff.d.ts +5 -2
  12. package/dist/types/commit/agentic/tools/git-hunk.d.ts +6 -2
  13. package/dist/types/commit/agentic/tools/git-overview.d.ts +5 -2
  14. package/dist/types/commit/agentic/tools/propose-changelog.d.ts +29 -7
  15. package/dist/types/commit/agentic/tools/propose-commit.d.ts +14 -4
  16. package/dist/types/commit/agentic/tools/recent-commits.d.ts +4 -2
  17. package/dist/types/commit/agentic/tools/schemas.d.ts +7 -3
  18. package/dist/types/commit/agentic/tools/split-commit.d.ts +37 -10
  19. package/dist/types/commit/changelog/generate.d.ts +13 -3
  20. package/dist/types/commit/shared-llm.d.ts +13 -4
  21. package/dist/types/config/config-file.d.ts +1 -1
  22. package/dist/types/config/models-config-schema-bundle.d.ts +1992 -981
  23. package/dist/types/config/models-config-schema.d.ts +1201 -580
  24. package/dist/types/config/models-config.d.ts +355 -371
  25. package/dist/types/edit/hashline/params.d.ts +4 -2
  26. package/dist/types/edit/modes/apply-patch.d.ts +4 -2
  27. package/dist/types/edit/modes/patch.d.ts +16 -5
  28. package/dist/types/edit/modes/replace.d.ts +8 -3
  29. package/dist/types/eval/bridge-timeout.d.ts +5 -2
  30. package/dist/types/eval/completion-bridge.d.ts +13 -0
  31. package/dist/types/eval/py/tool-bridge.d.ts +12 -0
  32. package/dist/types/extensibility/custom-commands/types.d.ts +4 -4
  33. package/dist/types/extensibility/custom-tools/types.d.ts +4 -4
  34. package/dist/types/extensibility/extensions/types.d.ts +4 -4
  35. package/dist/types/extensibility/hooks/types.d.ts +4 -4
  36. package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +1 -1
  37. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -1
  38. package/dist/types/extensibility/legacy-typebox.d.ts +8 -0
  39. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +6 -13
  40. package/dist/types/goals/tools/goal-tool.d.ts +12 -4
  41. package/dist/types/launch/client.d.ts +9 -1
  42. package/dist/types/launch/protocol.d.ts +17 -0
  43. package/dist/types/lsp/index.d.ts +15 -5
  44. package/dist/types/lsp/types.d.ts +15 -5
  45. package/dist/types/modes/components/btw-panel.d.ts +3 -0
  46. package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
  47. package/dist/types/modes/controllers/command-controller.d.ts +1 -0
  48. package/dist/types/modes/interactive-mode.d.ts +4 -1
  49. package/dist/types/modes/types.d.ts +3 -1
  50. package/dist/types/security/contracts/schemas.d.ts +1115 -537
  51. package/dist/types/security/publication.d.ts +92 -34
  52. package/dist/types/session/agent-session-types.d.ts +5 -0
  53. package/dist/types/session/agent-session.d.ts +26 -2
  54. package/dist/types/session/launch-completion.d.ts +10 -0
  55. package/dist/types/session/session-entries.d.ts +15 -1
  56. package/dist/types/session/session-manager.d.ts +7 -0
  57. package/dist/types/session/yield-queue.d.ts +6 -1
  58. package/dist/types/task/types.d.ts +63 -18
  59. package/dist/types/tools/ask.d.ts +34 -8
  60. package/dist/types/tools/ast-edit.d.ts +16 -4
  61. package/dist/types/tools/ast-grep.d.ts +12 -4
  62. package/dist/types/tools/bash.d.ts +20 -11
  63. package/dist/types/tools/browser.d.ts +64 -20
  64. package/dist/types/tools/checkpoint.d.ts +16 -8
  65. package/dist/types/tools/computer.d.ts +1 -1
  66. package/dist/types/tools/debug.d.ts +118 -48
  67. package/dist/types/tools/eval.d.ts +11 -5
  68. package/dist/types/tools/gh.d.ts +74 -26
  69. package/dist/types/tools/glob.d.ts +16 -6
  70. package/dist/types/tools/grep.d.ts +18 -6
  71. package/dist/types/tools/hub/index.d.ts +116 -46
  72. package/dist/types/tools/image-gen.d.ts +25 -8
  73. package/dist/types/tools/index.d.ts +9 -0
  74. package/dist/types/tools/inspect-image.d.ts +10 -4
  75. package/dist/types/tools/learn.d.ts +26 -8
  76. package/dist/types/tools/manage-skill.d.ts +16 -6
  77. package/dist/types/tools/memory-edit.d.ts +18 -6
  78. package/dist/types/tools/memory-recall.d.ts +8 -4
  79. package/dist/types/tools/memory-reflect.d.ts +10 -4
  80. package/dist/types/tools/memory-retain.d.ts +14 -4
  81. package/dist/types/tools/read.d.ts +8 -4
  82. package/dist/types/tools/security-scan.d.ts +82 -30
  83. package/dist/types/tools/todo.d.ts +30 -10
  84. package/dist/types/tools/tts.d.ts +12 -5
  85. package/dist/types/tools/vibe.d.ts +42 -18
  86. package/dist/types/tools/write.d.ts +10 -4
  87. package/dist/types/web/search/index.d.ts +20 -6
  88. package/package.json +13 -13
  89. package/scripts/legacy-pi-virtual-module.ts +2 -2
  90. package/src/advisor/advise-tool.ts +1 -1
  91. package/src/advisor/config.ts +1 -1
  92. package/src/autoresearch/tools/init-experiment.ts +1 -2
  93. package/src/autoresearch/tools/log-experiment.ts +1 -2
  94. package/src/autoresearch/tools/run-experiment.ts +1 -1
  95. package/src/autoresearch/tools/update-notes.ts +1 -1
  96. package/src/cli/gc-cli.ts +641 -21
  97. package/src/commit/agentic/tools/analyze-file.ts +1 -1
  98. package/src/commit/agentic/tools/git-file-diff.ts +1 -1
  99. package/src/commit/agentic/tools/git-hunk.ts +1 -1
  100. package/src/commit/agentic/tools/git-overview.ts +1 -1
  101. package/src/commit/agentic/tools/propose-changelog.ts +1 -1
  102. package/src/commit/agentic/tools/propose-commit.ts +1 -1
  103. package/src/commit/agentic/tools/recent-commits.ts +1 -1
  104. package/src/commit/agentic/tools/schemas.ts +1 -1
  105. package/src/commit/agentic/tools/split-commit.ts +1 -1
  106. package/src/commit/analysis/summary.ts +1 -1
  107. package/src/commit/changelog/generate.ts +1 -1
  108. package/src/config/config-file.ts +2 -2
  109. package/src/config/model-discovery.ts +14 -14
  110. package/src/config/model-registry.ts +10 -8
  111. package/src/config/models-config-schema-bundle.ts +1 -8
  112. package/src/config/settings.ts +1 -1
  113. package/src/discovery/codex.ts +22 -9
  114. package/src/edit/hashline/params.ts +1 -1
  115. package/src/edit/modes/apply-patch.ts +1 -1
  116. package/src/edit/modes/patch.ts +1 -1
  117. package/src/edit/modes/replace.ts +2 -1
  118. package/src/eval/agent-bridge.ts +4 -2
  119. package/src/eval/bridge-timeout.ts +5 -2
  120. package/src/eval/completion-bridge.ts +3 -2
  121. package/src/eval/executor-base.ts +20 -1
  122. package/src/eval/js/context-manager.ts +94 -6
  123. package/src/eval/py/tool-bridge.ts +29 -9
  124. package/src/export/html/index.ts +10 -3
  125. package/src/export/share.ts +4 -0
  126. package/src/extensibility/custom-commands/loader.ts +3 -3
  127. package/src/extensibility/custom-commands/types.ts +4 -4
  128. package/src/extensibility/custom-tools/loader.ts +3 -3
  129. package/src/extensibility/custom-tools/types.ts +5 -4
  130. package/src/extensibility/extensions/loader.ts +3 -3
  131. package/src/extensibility/extensions/types.ts +5 -4
  132. package/src/extensibility/hooks/loader.ts +3 -3
  133. package/src/extensibility/hooks/types.ts +4 -4
  134. package/src/extensibility/legacy-pi-ai-shim.ts +1 -1
  135. package/src/extensibility/legacy-pi-coding-agent-shim.ts +2 -2
  136. package/src/extensibility/legacy-typebox.ts +101 -0
  137. package/src/extensibility/plugins/legacy-pi-compat.ts +13 -22
  138. package/src/goals/tools/goal-tool.ts +1 -1
  139. package/src/launch/broker.ts +222 -6
  140. package/src/launch/client.ts +161 -9
  141. package/src/launch/protocol.ts +52 -0
  142. package/src/lsp/types.ts +1 -1
  143. package/src/main.ts +13 -6
  144. package/src/mcp/config-writer.ts +1 -1
  145. package/src/modes/components/btw-panel.ts +41 -4
  146. package/src/modes/controllers/btw-controller.ts +55 -7
  147. package/src/modes/controllers/command-controller.ts +31 -0
  148. package/src/modes/controllers/input-controller.ts +24 -7
  149. package/src/modes/interactive-mode.ts +16 -2
  150. package/src/modes/theme/theme.ts +3 -3
  151. package/src/modes/types.ts +8 -1
  152. package/src/prompts/session/launch-completion.md +1 -0
  153. package/src/sdk.ts +15 -0
  154. package/src/security/contracts/schemas.ts +202 -184
  155. package/src/security/contracts/validation.ts +6 -2
  156. package/src/security/publication.ts +1 -1
  157. package/src/security/store.ts +2 -2
  158. package/src/session/agent-session-types.ts +6 -0
  159. package/src/session/agent-session.ts +190 -14
  160. package/src/session/launch-completion.ts +37 -0
  161. package/src/session/session-context.ts +26 -0
  162. package/src/session/session-entries.ts +17 -1
  163. package/src/session/session-manager.ts +21 -0
  164. package/src/session/yield-queue.ts +121 -16
  165. package/src/slash-commands/builtin-registry.ts +10 -1
  166. package/src/task/types.ts +1 -1
  167. package/src/tools/ask.ts +1 -1
  168. package/src/tools/ast-edit.ts +1 -1
  169. package/src/tools/ast-grep.ts +1 -1
  170. package/src/tools/bash.ts +61 -3
  171. package/src/tools/browser/cmux/cmux-tab.ts +92 -4
  172. package/src/tools/browser.ts +1 -1
  173. package/src/tools/checkpoint.ts +1 -1
  174. package/src/tools/computer.ts +1 -1
  175. package/src/tools/debug.ts +1 -1
  176. package/src/tools/eval.ts +1 -1
  177. package/src/tools/gh.ts +1 -2
  178. package/src/tools/glob.ts +1 -1
  179. package/src/tools/grep.ts +1 -1
  180. package/src/tools/hub/index.ts +1 -1
  181. package/src/tools/hub/launch.ts +122 -6
  182. package/src/tools/image-gen.ts +1 -1
  183. package/src/tools/index.ts +9 -0
  184. package/src/tools/inspect-image.ts +1 -1
  185. package/src/tools/learn.ts +1 -1
  186. package/src/tools/manage-skill.ts +1 -1
  187. package/src/tools/memory-edit.ts +1 -1
  188. package/src/tools/memory-recall.ts +1 -1
  189. package/src/tools/memory-reflect.ts +1 -1
  190. package/src/tools/memory-retain.ts +1 -1
  191. package/src/tools/read.ts +1 -1
  192. package/src/tools/security-scan.ts +1 -1
  193. package/src/tools/todo.ts +1 -1
  194. package/src/tools/tts.ts +1 -1
  195. package/src/tools/vibe.ts +2 -1
  196. package/src/tools/write.ts +1 -1
  197. package/src/web/search/index.ts +2 -1
  198. package/dist/types/config/file-lock.d.ts +0 -29
  199. package/dist/types/extensibility/typebox.d.ts +0 -181
  200. package/src/config/file-lock.ts +0 -164
  201. package/src/extensibility/typebox.ts +0 -958
@@ -13,7 +13,19 @@ import { callSessionTool, type JsStatusEvent } from "../js/tool-bridge";
13
13
 
14
14
  export interface PyToolBridgeEntry {
15
15
  toolSession: ToolSession;
16
+ /**
17
+ * Turn-cancel handed to the tool implementation. Raw and never deferred, so
18
+ * delegated work — above all the subagents `agent()` spawns — stops at once.
19
+ */
16
20
  signal?: AbortSignal;
21
+ /**
22
+ * Kernel-side abort, held back while a critical `agent()` phase (isolation
23
+ * worktree setup, merge/cherry-pick) is in flight. Decides only when the host
24
+ * may stop waiting on a call and let the kernel unwind; it is never given to
25
+ * a tool. Keeping these separate is what stops a cancel from settling the
26
+ * cell on top of a still-running, abort-insensitive merge.
27
+ */
28
+ shieldedSignal?: AbortSignal;
17
29
  emitStatus?: (event: JsStatusEvent) => void;
18
30
  abortRequested?: () => boolean;
19
31
  }
@@ -32,16 +44,24 @@ const registrations = new Map<string, PyToolBridgeEntry>();
32
44
  let serverPromise: Promise<BridgeServer> | null = null;
33
45
 
34
46
  /**
35
- * Forward a bridge call to {@link callSessionTool} while respecting eval abort
36
- * shielding.
47
+ * Forward a bridge call to {@link callSessionTool}, failing fast once the cell
48
+ * has been interrupted.
37
49
  *
38
50
  * Python invokes this bridge with blocking `urllib` requests from worker threads
39
- * (each `agent()` / `tool.*` call). The base executor defers the registered
40
- * signal while a bridge call is already paused so in-flight subagents can finish
41
- * and persist output instead of being orphaned. Once an abort has been requested,
42
- * later bridge calls are rejected before starting; once the shielded signal
43
- * finally aborts, this handler still resolves the HTTP request promptly so the
44
- * kernel can unwind without being hard-killed.
51
+ * (each `agent()` / `tool.*` call). Two different aborts meet here:
52
+ *
53
+ * - {@link PyToolBridgeEntry.signal} goes to the tool, so a turn cancel tears
54
+ * down delegated work subagents included instead of leaving it running
55
+ * past the cell.
56
+ * - {@link PyToolBridgeEntry.shieldedSignal} decides when we may stop waiting.
57
+ * It is deferred across a critical `agent()` phase, so a cancel landing
58
+ * mid-merge cannot return early and let the cell settle while an
59
+ * abort-insensitive cherry-pick is still rewriting the repo.
60
+ *
61
+ * Calls arriving after an abort are rejected before starting. Otherwise the
62
+ * usual path is that the tool observes its own abort and rejects; the race only
63
+ * matters for tools that ignore the signal, keeping the kernel unwinding
64
+ * promptly instead of being hard-killed.
45
65
  */
46
66
  async function callSessionToolPromptOnAbort(name: string, args: unknown, entry: PyToolBridgeEntry): Promise<unknown> {
47
67
  if (entry.abortRequested?.()) {
@@ -52,7 +72,7 @@ async function callSessionToolPromptOnAbort(name: string, args: unknown, entry:
52
72
  signal: entry.signal,
53
73
  emitStatus: entry.emitStatus,
54
74
  });
55
- const signal = entry.signal;
75
+ const signal = entry.shieldedSignal ?? entry.signal;
56
76
  if (!signal) return await call;
57
77
  if (signal.aborted) {
58
78
  void call.catch(() => {});
@@ -181,10 +181,17 @@ export interface SessionData {
181
181
  subSessions?: Record<string, SubSession>;
182
182
  }
183
183
 
184
+ function sessionHeaderForExport(header: SessionHeader | null): SessionHeader | null {
185
+ if (!header) return null;
186
+ const exported = { ...header };
187
+ delete exported.previousSessionFiles;
188
+ return exported;
189
+ }
190
+
184
191
  /** Snapshot the session (plus optional agent state) into the JSON shape the viewer renders. */
185
192
  export function buildSessionData(sm: SessionManager, state?: AgentState): SessionData {
186
193
  return {
187
- header: sm.getHeader(),
194
+ header: sessionHeaderForExport(sm.getHeader()),
188
195
  entries: sm.getEntries(),
189
196
  leafId: sm.getLeafId(),
190
197
  systemPrompt: state?.systemPrompt.join("\n\n"),
@@ -231,7 +238,7 @@ async function collectSubSessionsFromDir(
231
238
  out[key] = {
232
239
  agentId,
233
240
  parent: parentKey,
234
- header,
241
+ header: sessionHeaderForExport(header),
235
242
  entries,
236
243
  leafId: entries.length > 0 ? entries[entries.length - 1].id : null,
237
244
  };
@@ -295,7 +302,7 @@ export async function exportFromFile(inputPath: string, options?: ExportOptions
295
302
  }
296
303
 
297
304
  const sessionData: SessionData = {
298
- header: sm.getHeader(),
305
+ header: sessionHeaderForExport(sm.getHeader()),
299
306
  entries: sm.getEntries(),
300
307
  leafId: sm.getLeafId(),
301
308
  };
@@ -223,6 +223,7 @@ function collectShareRegexSecretValues(o: SecretObfuscator, data: SessionData):
223
223
  if (!header) return;
224
224
  add(header.title);
225
225
  add(header.cwd);
226
+ for (const previousSessionFile of header.previousSessionFiles ?? []) add(previousSessionFile);
226
227
  };
227
228
 
228
229
  addHeader(data.header);
@@ -246,6 +247,9 @@ function redactShareHeader(
246
247
  ...header,
247
248
  title: header.title === undefined ? undefined : o.obfuscate(header.title, sharedRegexSecretValues),
248
249
  cwd: o.obfuscate(header.cwd, sharedRegexSecretValues),
250
+ previousSessionFiles: header.previousSessionFiles?.map(previousSessionFile =>
251
+ o.obfuscate(previousSessionFile, sharedRegexSecretValues),
252
+ ),
249
253
  };
250
254
  }
251
255
 
@@ -6,14 +6,14 @@
6
6
  */
7
7
  import * as fs from "node:fs";
8
8
  import * as path from "node:path";
9
+ import * as arktype from "@oh-my-pi/omptype";
10
+ import * as zodModule from "@oh-my-pi/omptype/zod";
9
11
  import { getAgentDir, getProjectDir, isEnoent, logger } from "@oh-my-pi/pi-utils";
10
- import * as arktype from "arktype";
11
- import * as zodModule from "zod/v4";
12
12
  import { getConfigDirs } from "../../config";
13
13
  import { execCommand } from "../../exec/exec";
14
14
  // Runtime self-reference: dereference this namespace only inside loader functions to keep the index.ts cycle safe.
15
15
  import * as PiCodingAgent from "../../index";
16
- import * as typebox from "../typebox";
16
+ import * as typebox from "../legacy-typebox";
17
17
  import { GreenCommand } from "./bundled/ci-green";
18
18
  import { ReviewCommand } from "./bundled/review";
19
19
  import type {
@@ -5,11 +5,11 @@
5
5
  * Unlike markdown commands which expand to prompts, custom commands can execute
6
6
  * arbitrary logic with full access to the hook context.
7
7
  */
8
- import type * as arktype from "arktype";
9
- import type * as zod from "zod/v4";
8
+ import type * as arktype from "@oh-my-pi/omptype";
9
+ import type * as TypeBox from "@oh-my-pi/omptype/typebox";
10
+ import type * as zod from "@oh-my-pi/omptype/zod";
10
11
  import type { ExecOptions, ExecResult, HookCommandContext } from "../../extensibility/hooks/types";
11
12
  import type * as PiCodingAgent from "../../index";
12
- import type * as TypeBox from "../typebox";
13
13
 
14
14
  // Re-export for custom commands to use
15
15
  export type { ExecOptions, ExecResult, HookCommandContext };
@@ -27,7 +27,7 @@ export interface CustomCommandAPI {
27
27
  typebox: typeof TypeBox;
28
28
  /** Injected arktype module for validation in custom commands. */
29
29
  arktype: typeof arktype;
30
- /** Injected zod/v4 module for canonical command validation. */
30
+ /** Injected omptype-backed zod facade for command validation. */
31
31
  zod: typeof zod;
32
32
  /** Injected pi-coding-agent exports */
33
33
  pi: typeof PiCodingAgent;
@@ -5,10 +5,10 @@
5
5
  * CustomToolAPI to avoid import resolution issues with custom tools loaded from user directories.
6
6
  */
7
7
  import * as path from "node:path";
8
+ import { type } from "@oh-my-pi/omptype";
9
+ import * as zodModule from "@oh-my-pi/omptype/zod";
8
10
  import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
9
11
  import { logger } from "@oh-my-pi/pi-utils";
10
- import { type } from "arktype";
11
- import * as zodModule from "zod/v4";
12
12
  import { toolCapability } from "../../capability/tool";
13
13
  import { type CustomTool, loadCapability } from "../../discovery";
14
14
  import type { ExecOptions } from "../../exec/exec";
@@ -17,7 +17,7 @@ import type { HookUIContext } from "../../extensibility/hooks/types";
17
17
  import { getAllPluginToolPaths } from "../../extensibility/plugins/loader";
18
18
  // Runtime self-reference: dereference this namespace only inside loader functions to keep the index.ts cycle safe.
19
19
  import * as PiCodingAgent from "../../index";
20
- import * as typebox from "../typebox";
20
+ import * as typebox from "../legacy-typebox";
21
21
  import { createNoOpUIContext, resolvePath, withHostGuard } from "../utils";
22
22
  import type { CustomToolAPI, CustomToolFactory, LoadedCustomTool, ToolLoadError } from "./types";
23
23
 
@@ -4,6 +4,10 @@
4
4
  * Custom tools are TypeScript modules that define additional tools for the agent.
5
5
  * They can provide custom rendering for tool calls and results in the TUI.
6
6
  */
7
+
8
+ import type { type as ArkType } from "@oh-my-pi/omptype";
9
+ import type * as TypeBox from "@oh-my-pi/omptype/typebox";
10
+ import type * as zod from "@oh-my-pi/omptype/zod";
7
11
  import type {
8
12
  AgentToolResult,
9
13
  AgentToolUpdateCallback,
@@ -16,8 +20,6 @@ import type { CompactionResult } from "@oh-my-pi/pi-agent-core/compaction";
16
20
  import type { FetchImpl, Model, Static, TSchema } from "@oh-my-pi/pi-ai";
17
21
  import type { Component } from "@oh-my-pi/pi-tui";
18
22
  import type { logger as PiLogger } from "@oh-my-pi/pi-utils";
19
- import type { type as ArkType } from "arktype";
20
- import type * as zod from "zod/v4";
21
23
  import type { Rule } from "../../capability/rule";
22
24
  import type { ModelRegistry } from "../../config/model-registry";
23
25
  import type { Settings } from "../../config/settings";
@@ -29,7 +31,6 @@ import type { Theme } from "../../modes/theme/theme";
29
31
  import type { ReadonlySessionManager } from "../../session/session-manager";
30
32
  import type { TodoItem } from "../../tools/todo";
31
33
  import type { RecoveredRetryError } from "../shared-events";
32
- import type * as TypeBox from "../typebox";
33
34
 
34
35
  /** Alias for clarity */
35
36
  export type CustomToolUIContext = HookUIContext;
@@ -69,7 +70,7 @@ export interface CustomToolAPI {
69
70
  typebox: typeof TypeBox;
70
71
  /** Injected arktype module for arktype-authored custom tools. */
71
72
  arktype: typeof ArkType;
72
- /** Injected zod/v4 module for canonical parameter schemas. */
73
+ /** Injected omptype-backed zod facade for parameter schemas. */
73
74
  zod: typeof zod;
74
75
  /** Injected pi-coding-agent exports */
75
76
  pi: typeof PiCodingAgent;
@@ -4,6 +4,8 @@
4
4
  import type * as fs1 from "node:fs";
5
5
  import * as fs from "node:fs/promises";
6
6
  import * as path from "node:path";
7
+ import { Type } from "@oh-my-pi/omptype";
8
+ import * as zodModule from "@oh-my-pi/omptype/zod";
7
9
  import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
8
10
  import type {
9
11
  ImageContent,
@@ -16,8 +18,6 @@ import type {
16
18
  } from "@oh-my-pi/pi-ai";
17
19
  import type { KeyId } from "@oh-my-pi/pi-tui";
18
20
  import { hasFsCode, isEacces, isEnoent, logger } from "@oh-my-pi/pi-utils";
19
- import { Type } from "arktype";
20
- import * as zodModule from "zod/v4";
21
21
  import { type ExtensionModule, extensionModuleCapability } from "../../capability/extension-module";
22
22
  import { type Hook, hookCapability } from "../../capability/hook";
23
23
  import { isServiceTierFamily, isServiceTierForFamily } from "../../config/service-tier";
@@ -29,9 +29,9 @@ import { execCommand } from "../../exec/exec";
29
29
  import * as PiCodingAgent from "../../index";
30
30
  import type { CustomMessagePayload } from "../../session/messages";
31
31
  import { EventBus } from "../../utils/event-bus";
32
+ import * as TypeBox from "../legacy-typebox";
32
33
  import { installLegacyPiSpecifierShim, loadLegacyPiModule } from "../plugins/legacy-pi-compat";
33
34
  import { getAllPluginExtensionPaths } from "../plugins/loader";
34
- import * as TypeBox from "../typebox";
35
35
 
36
36
  import { resolvePath, withHostGuard } from "../utils";
37
37
  import type {
@@ -7,6 +7,10 @@
7
7
  * - Register commands, keyboard shortcuts, and CLI flags
8
8
  * - Interact with the user via UI primitives
9
9
  */
10
+
11
+ import type { Type as arktype } from "@oh-my-pi/omptype";
12
+ import type * as TypeBox from "@oh-my-pi/omptype/typebox";
13
+ import type * as zod from "@oh-my-pi/omptype/zod";
10
14
  import type {
11
15
  AgentMessage,
12
16
  AgentToolResult,
@@ -36,8 +40,6 @@ import type {
36
40
  import type { OAuthCredentials, OAuthLoginCallbacks } from "@oh-my-pi/pi-ai/oauth/types";
37
41
  import type { AutocompleteItem, AutocompleteProvider, Component, EditorTheme, KeyId, TUI } from "@oh-my-pi/pi-tui";
38
42
  import type { logger as PiLogger } from "@oh-my-pi/pi-utils";
39
- import type { Type as arktype } from "arktype";
40
- import type * as zod from "zod/v4";
41
43
  import type { KeybindingsManager } from "../../config/keybindings";
42
44
  import type { ModelRegistry } from "../../config/model-registry";
43
45
  import type { EditToolDetails } from "../../edit";
@@ -102,7 +104,6 @@ import type {
102
104
  TurnStartEvent,
103
105
  } from "../shared-events";
104
106
  import type { SlashCommandInfo } from "../slash-commands";
105
- import type * as TypeBox from "../typebox";
106
107
 
107
108
  export type { AppKeybinding, KeybindingsManager } from "../../config/keybindings";
108
109
  export type { ExecOptions, ExecResult } from "../../exec/exec";
@@ -1113,7 +1114,7 @@ export interface ExtensionAPI {
1113
1114
 
1114
1115
  /** Injected arktype module for arktype-authored extension tools (canonical going forward). */
1115
1116
  arktype: typeof arktype;
1116
- /** Injected zod/v4 module for canonical extension tool parameter schemas. */
1117
+ /** Injected omptype-backed zod facade for extension tool parameter schemas. */
1117
1118
  zod: typeof zod;
1118
1119
 
1119
1120
  /** Injected pi-coding-agent exports for accessing SDK utilities */
@@ -2,16 +2,16 @@
2
2
  * Hook loader - loads TypeScript hook modules using native Bun import.
3
3
  */
4
4
  import * as path from "node:path";
5
+ import * as arktype from "@oh-my-pi/omptype";
6
+ import * as zodModule from "@oh-my-pi/omptype/zod";
5
7
  import { logger } from "@oh-my-pi/pi-utils";
6
- import * as arktype from "arktype";
7
- import * as zodModule from "zod/v4";
8
8
  import { hookCapability } from "../../capability/hook";
9
9
  import type { Hook } from "../../discovery";
10
10
  import { loadCapability } from "../../discovery";
11
11
  // Runtime self-reference: dereference this namespace only inside loader functions to keep the index.ts cycle safe.
12
12
  import * as PiCodingAgent from "../../index";
13
13
  import type { CustomMessagePayload } from "../../session/messages";
14
- import * as typebox from "../typebox";
14
+ import * as typebox from "../legacy-typebox";
15
15
  import { resolvePath, withHostGuard } from "../utils";
16
16
  import { execCommand } from "./runner";
17
17
  import type { ExecOptions, HookAPI, HookFactory, HookMessageRenderer, RegisteredCommand } from "./types";
@@ -1,8 +1,9 @@
1
+ import type { Type } from "@oh-my-pi/omptype";
2
+ import type * as TypeBox from "@oh-my-pi/omptype/typebox";
3
+ import type * as zod from "@oh-my-pi/omptype/zod";
1
4
  import type { ImageContent, Message, Model, TextContent } from "@oh-my-pi/pi-ai";
2
5
  import type { Component, TUI } from "@oh-my-pi/pi-tui";
3
6
  import type { logger as PiLogger } from "@oh-my-pi/pi-utils";
4
- import type { Type } from "arktype";
5
- import type * as zod from "zod/v4";
6
7
  import type { ModelRegistry } from "../../config/model-registry";
7
8
  import type { EditToolDetails } from "../../edit";
8
9
  import type { ExecOptions, ExecResult } from "../../exec/exec";
@@ -43,7 +44,6 @@ import type {
43
44
  TurnEndEvent,
44
45
  TurnStartEvent,
45
46
  } from "../shared-events";
46
- import type * as TypeBox from "../typebox";
47
47
 
48
48
  // Re-export for backward compatibility
49
49
  export type { ExecOptions, ExecResult } from "../../exec/exec";
@@ -586,7 +586,7 @@ export interface HookAPI {
586
586
  typebox: typeof TypeBox;
587
587
  /** Injected arktype module for arktype-authored hooks. */
588
588
  arktype: typeof Type;
589
- /** Injected zod/v4 module for canonical hook validation. */
589
+ /** Injected omptype-backed zod facade for hook validation. */
590
590
  zod: typeof zod;
591
591
  /** Injected pi-coding-agent exports */
592
592
  pi: typeof PiCodingAgent;
@@ -37,7 +37,7 @@ import {
37
37
  getBundledProviders,
38
38
  modelsAreEqual,
39
39
  } from "@oh-my-pi/pi-catalog/models";
40
- import { type TSchema, Type } from "./typebox";
40
+ import { type TSchema, Type } from "./legacy-typebox";
41
41
 
42
42
  export interface StringEnumOptions<T extends string> {
43
43
  description?: string;
@@ -57,10 +57,10 @@ import { EventBus } from "../utils/event-bus";
57
57
  import { discoverExtensionPaths, loadExtensionFromFactory, loadExtensions } from "./extensions";
58
58
  import { ExtensionRuntime } from "./extensions/loader";
59
59
  import type { ExtensionFactory, ToolDefinition } from "./extensions/types";
60
+ import { Type } from "./legacy-typebox";
60
61
  import { getEnabledPlugins, resolvePluginExtensionPaths, type ScopedInstalledPlugin } from "./plugins/loader";
61
62
  import type { Skill } from "./skills";
62
63
  import { loadSkillsFromDir } from "./skills";
63
- import { Type } from "./typebox";
64
64
 
65
65
  const TOOL_DEFINITION_MARKER = "__isToolDefinition";
66
66
  const LEGACY_BUILTIN_TOOL_MARKER = "__ompLegacyBuiltinTool";
@@ -1434,4 +1434,4 @@ export { parseArgs } from "../cli/args";
1434
1434
  export * from "../index";
1435
1435
  export { formatBytes as formatSize } from "../tools/render-utils";
1436
1436
  export { copyToClipboard } from "../utils/clipboard";
1437
- export { Type } from "./typebox";
1437
+ export { Type } from "./legacy-typebox";
@@ -0,0 +1,101 @@
1
+ import {
2
+ type ObjectOpts,
3
+ Type as OmpType,
4
+ type TypeBuilder as OmpTypeBuilder,
5
+ type TUnsafe,
6
+ } from "@oh-my-pi/omptype/typebox";
7
+ import { upgradeJsonSchemaTo202012, validateJsonSchemaValue } from "@oh-my-pi/pi-ai/utils/schema";
8
+
9
+ export * from "@oh-my-pi/omptype/typebox";
10
+
11
+ const VALIDATION_FAILURE = Symbol("pi.typebox.validationFailure");
12
+
13
+ interface ValidationFailure {
14
+ message: string;
15
+ readonly [VALIDATION_FAILURE]: true;
16
+ }
17
+
18
+ interface SafeParseSuccess<T> {
19
+ success: true;
20
+ data: T;
21
+ }
22
+
23
+ interface SafeParseFailure {
24
+ success: false;
25
+ error: ValidationFailure;
26
+ }
27
+
28
+ type LegacyUnsafeSchema<T> = Record<string, unknown> &
29
+ TUnsafe<T> & {
30
+ __validator(data: unknown): T | ValidationFailure;
31
+ safeParse(input: unknown): SafeParseSuccess<T> | SafeParseFailure;
32
+ };
33
+
34
+ function defineHidden(target: object, key: PropertyKey, value: unknown): void {
35
+ Object.defineProperty(target, key, {
36
+ value,
37
+ writable: true,
38
+ configurable: true,
39
+ });
40
+ }
41
+
42
+ function unsafe<T = unknown>(jsonSchema: Record<string, unknown> = {}): LegacyUnsafeSchema<T> {
43
+ const schema = { ...jsonSchema } as LegacyUnsafeSchema<T>;
44
+ const upgradedSchema = upgradeJsonSchemaTo202012(jsonSchema);
45
+ const validate = (data: unknown): T | ValidationFailure => {
46
+ const result = validateJsonSchemaValue(upgradedSchema, data);
47
+ if (result.success) return data as T;
48
+ let message = "";
49
+ for (const issue of result.issues) {
50
+ if (message) message += "; ";
51
+ message += issue.path.length > 0 ? `${issue.path.join(".")}: ${issue.message}` : issue.message;
52
+ }
53
+ const failure = { message: message || "Invalid value" } as ValidationFailure;
54
+ defineHidden(failure, VALIDATION_FAILURE, true);
55
+ return failure;
56
+ };
57
+ defineHidden(schema, "__validator", validate);
58
+ defineHidden(schema, "safeParse", (input: unknown): SafeParseSuccess<T> | SafeParseFailure => {
59
+ const result = validate(input);
60
+ return typeof result === "object" && result !== null && VALIDATION_FAILURE in result
61
+ ? { success: false, error: result }
62
+ : { success: true, data: result as T };
63
+ });
64
+ return schema;
65
+ }
66
+
67
+ const object = ((properties: Record<string, unknown>, opts?: ObjectOpts) => {
68
+ let hasRawProperty = false;
69
+ for (const key in properties) {
70
+ if (typeof properties[key] !== "function") {
71
+ hasRawProperty = true;
72
+ break;
73
+ }
74
+ }
75
+ if (!hasRawProperty) return OmpType.Object(properties as Parameters<typeof OmpType.Object>[0], opts);
76
+
77
+ const propertySchemas: Record<string, unknown> = {};
78
+ const required: string[] = [];
79
+ for (const key in properties) {
80
+ const property = properties[key];
81
+ propertySchemas[key] =
82
+ typeof property === "function" && "toJsonSchema" in property
83
+ ? (property as { toJsonSchema(): Record<string, unknown> }).toJsonSchema()
84
+ : property;
85
+ required.push(key);
86
+ }
87
+ const document: Record<string, unknown> = { type: "object", properties: propertySchemas, required };
88
+ if (opts?.additionalProperties !== undefined) {
89
+ document.additionalProperties =
90
+ typeof opts.additionalProperties === "function"
91
+ ? opts.additionalProperties.toJsonSchema()
92
+ : opts.additionalProperties;
93
+ }
94
+ return unsafe(document);
95
+ }) as typeof OmpType.Object;
96
+
97
+ export const Type: OmpTypeBuilder = { ...OmpType, Object: object, Unsafe: unsafe } as unknown as OmpTypeBuilder;
98
+ export type TypeBuilder = OmpTypeBuilder;
99
+
100
+ const legacyTypeBox: { Type: OmpTypeBuilder } = { Type };
101
+ export default legacyTypeBox;
@@ -682,13 +682,11 @@ function clearLegacyPiResolutionCaches(): void {
682
682
  registerPluginCacheInvalidator(clearLegacyPiResolutionCaches);
683
683
  const PACKAGE_IMPORT_EXCLUDED = Symbol("packageImportExcluded");
684
684
 
685
- // Extensions that imported TypeBox directly used to resolve against a real
686
- // `@sinclair/typebox` or `typebox` install. The runtime dep was replaced with
687
- // the Zod-backed shim under `extensibility/typebox.ts`; plugins still importing
688
- // either public name are redirected to that shim so existing extensions keep
689
- // working without code changes. Submodules like `@sinclair/typebox/compiler`
690
- // are intentionally not remapped — those expose TypeBox-only APIs the shim does
691
- // not provide and plugins relying on them must vendor TypeBox directly.
685
+ // Extensions importing TypeBox directly are redirected to omptype's TypeBox
686
+ // facade, keeping legacy builders while producing callable omptype schemas at
687
+ // the tool wire boundary. Submodules such as `@sinclair/typebox/compiler` are
688
+ // intentionally not remapped: plugins relying on those TypeBox-only APIs must
689
+ // vendor TypeBox directly.
692
690
  const TYPEBOX_SPECIFIER_FILTER = /^(?:@sinclair\/typebox|typebox)$/;
693
691
 
694
692
  // Compat-shim path resolution. In compiled-binary mode every bundled surface
@@ -736,20 +734,13 @@ function sourceShimPath(file: string): string {
736
734
  }
737
735
 
738
736
  /**
739
- * Resolve the path the TypeBox compatibility shim ships at, then drop it when
740
- * the source file is missing.
737
+ * Resolve the coding-agent compatibility surface that composes omptype's
738
+ * TypeBox facade with legacy `Type.Unsafe`, then drop the remap when that
739
+ * entrypoint is missing.
741
740
  *
742
- * In compiled-binary mode the shim is served through the
743
- * `omp-legacy-pi-bundled:` virtual namespace (issue #3423) bunfs paths are
744
- * unreachable on Bun 1.3.14+, so the virtual specifier is always available and
745
- * needs no filesystem probe. In dev / source-link / installed-package mode the
746
- * shim is an on-disk source file; validation mirrors
747
- * `__validateLegacyPiPackageRootOverrides` (#2168): if the computed candidate
748
- * doesn't exist (e.g. an install that dropped the source — issue #3414),
749
- * `resolveTypeBoxSpecifier` returns `undefined` and
750
- * `rewriteLegacyExtensionSource` leaves bare `typebox` / `@sinclair/typebox`
751
- * specifiers alone, so Bun falls through to native resolution against the
752
- * extension's own `node_modules`.
741
+ * In compiled-binary mode the surface is served through the
742
+ * `omp-legacy-pi-bundled:` virtual namespace (issue #3423). Dev, source-link,
743
+ * and installed-package modes use the shipped source module.
753
744
  *
754
745
  * Exported for tests; production callers use `TYPEBOX_SHIM_PATH`.
755
746
  */
@@ -764,14 +755,14 @@ export function __resolveTypeBoxShimPath(
764
755
  return pathExistsSync(sourcePath) ? sourcePath : null;
765
756
  }
766
757
 
767
- const TYPEBOX_SHIM_PATH = __resolveTypeBoxShimPath(IS_COMPILED_BINARY, sourceShimPath("typebox.ts"));
758
+ const TYPEBOX_SHIM_PATH = __resolveTypeBoxShimPath(IS_COMPILED_BINARY, sourceShimPath("legacy-typebox.ts"));
768
759
 
769
760
  // Legacy extensions historically imported `Type` (and `Static`/`TSchema`) from
770
761
  // the package root of `@(scope)/pi-ai`. pi-ai 15.1.0 removed the runtime `Type`
771
762
  // export (see `packages/ai/CHANGELOG.md`), so the bare canonical specifier no
772
763
  // longer satisfies those imports. The override below redirects only the bare
773
764
  // pi-ai package root onto a sibling shim that re-exports the canonical surface
774
- // plus the borrowed `Type` runtime from the Zod-backed TypeBox shim. Subpath
765
+ // plus the borrowed `Type` runtime from the omptype TypeBox facade. Subpath
775
766
  // imports such as `@oh-my-pi/pi-ai/oauth` continue to resolve directly
776
767
  // against the bundled pi-ai package.
777
768
  const LEGACY_PI_AI_SHIM_PATH = IS_COMPILED_BINARY
@@ -1,8 +1,8 @@
1
+ import { type } from "@oh-my-pi/omptype";
1
2
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
3
  import type { Component } from "@oh-my-pi/pi-tui";
3
4
  import { Text } from "@oh-my-pi/pi-tui";
4
5
  import { formatNumber, prompt } from "@oh-my-pi/pi-utils";
5
- import { type } from "arktype";
6
6
  import type { RenderResultOptions } from "../../extensibility/custom-tools/types";
7
7
  import type { Theme, ThemeColor } from "../../modes/theme/theme";
8
8
  import goalDescription from "../../prompts/tools/goal.md" with { type: "text" };