@oh-my-pi/pi-coding-agent 16.0.4 → 16.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/dist/cli.js +2027 -1396
  3. package/dist/types/advisor/advise-tool.d.ts +31 -19
  4. package/dist/types/autoresearch/tools/init-experiment.d.ts +13 -17
  5. package/dist/types/autoresearch/tools/log-experiment.d.ts +17 -19
  6. package/dist/types/autoresearch/tools/run-experiment.d.ts +3 -4
  7. package/dist/types/autoresearch/tools/update-notes.d.ts +4 -5
  8. package/dist/types/cli/args.d.ts +1 -0
  9. package/dist/types/cli/bench-cli.d.ts +6 -0
  10. package/dist/types/cli/ttsr-cli.d.ts +39 -0
  11. package/dist/types/commands/launch.d.ts +3 -0
  12. package/dist/types/commands/ttsr.d.ts +57 -0
  13. package/dist/types/commit/agentic/tools/analyze-file.d.ts +4 -5
  14. package/dist/types/commit/agentic/tools/git-file-diff.d.ts +4 -5
  15. package/dist/types/commit/agentic/tools/git-hunk.d.ts +5 -6
  16. package/dist/types/commit/agentic/tools/git-overview.d.ts +4 -5
  17. package/dist/types/commit/agentic/tools/propose-changelog.d.ts +23 -24
  18. package/dist/types/commit/agentic/tools/propose-commit.d.ts +11 -32
  19. package/dist/types/commit/agentic/tools/recent-commits.d.ts +3 -4
  20. package/dist/types/commit/agentic/tools/schemas.d.ts +6 -27
  21. package/dist/types/commit/agentic/tools/split-commit.d.ts +28 -49
  22. package/dist/types/commit/changelog/generate.d.ts +12 -13
  23. package/dist/types/commit/shared-llm.d.ts +10 -37
  24. package/dist/types/config/config-file.d.ts +4 -4
  25. package/dist/types/config/keybindings.d.ts +5 -0
  26. package/dist/types/config/models-config-schema.d.ts +625 -990
  27. package/dist/types/config/models-config.d.ts +229 -217
  28. package/dist/types/config/settings-schema.d.ts +144 -25
  29. package/dist/types/edit/hashline/params.d.ts +7 -11
  30. package/dist/types/edit/index.d.ts +2 -1
  31. package/dist/types/edit/modes/apply-patch.d.ts +4 -5
  32. package/dist/types/edit/modes/patch.d.ts +15 -24
  33. package/dist/types/edit/modes/replace.d.ts +16 -17
  34. package/dist/types/eval/js/index.d.ts +1 -0
  35. package/dist/types/extensibility/custom-commands/types.d.ts +6 -3
  36. package/dist/types/extensibility/custom-tools/types.d.ts +8 -5
  37. package/dist/types/extensibility/extensions/runner.d.ts +5 -2
  38. package/dist/types/extensibility/extensions/types.d.ts +14 -10
  39. package/dist/types/extensibility/hooks/types.d.ts +7 -4
  40. package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +13 -5
  41. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +17 -0
  42. package/dist/types/extensibility/shared-events.d.ts +22 -1
  43. package/dist/types/extensibility/typebox.d.ts +80 -58
  44. package/dist/types/goals/tools/goal-tool.d.ts +11 -24
  45. package/dist/types/index.d.ts +2 -0
  46. package/dist/types/lsp/index.d.ts +11 -26
  47. package/dist/types/lsp/types.d.ts +12 -28
  48. package/dist/types/main.d.ts +1 -0
  49. package/dist/types/mcp/client.d.ts +8 -0
  50. package/dist/types/modes/components/btw-panel.d.ts +1 -0
  51. package/dist/types/modes/components/custom-editor.d.ts +3 -1
  52. package/dist/types/modes/components/status-line/component.d.ts +1 -1
  53. package/dist/types/modes/components/status-line/context-thresholds.d.ts +0 -1
  54. package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
  55. package/dist/types/modes/controllers/input-controller.d.ts +1 -0
  56. package/dist/types/modes/interactive-mode.d.ts +3 -0
  57. package/dist/types/modes/rpc/rpc-types.d.ts +1 -1
  58. package/dist/types/modes/setup-wizard/index.d.ts +1 -0
  59. package/dist/types/modes/setup-wizard/startup-splash.d.ts +7 -0
  60. package/dist/types/modes/theme/theme.d.ts +1 -1
  61. package/dist/types/modes/types.d.ts +3 -0
  62. package/dist/types/modes/utils/context-usage.d.ts +12 -0
  63. package/dist/types/sdk.d.ts +8 -1
  64. package/dist/types/session/agent-session.d.ts +24 -0
  65. package/dist/types/session/session-persistence.d.ts +4 -0
  66. package/dist/types/startup-splash.d.ts +12 -0
  67. package/dist/types/task/types.d.ts +47 -48
  68. package/dist/types/tools/ask.d.ts +26 -27
  69. package/dist/types/tools/ast-edit.d.ts +17 -17
  70. package/dist/types/tools/ast-grep.d.ts +12 -13
  71. package/dist/types/tools/bash.d.ts +20 -17
  72. package/dist/types/tools/browser.d.ts +46 -71
  73. package/dist/types/tools/checkpoint.d.ts +14 -15
  74. package/dist/types/tools/debug.d.ts +82 -145
  75. package/dist/types/tools/eval.d.ts +30 -40
  76. package/dist/types/tools/find.d.ts +17 -18
  77. package/dist/types/tools/gh.d.ts +49 -78
  78. package/dist/types/tools/image-gen.d.ts +20 -36
  79. package/dist/types/tools/inspect-image.d.ts +10 -11
  80. package/dist/types/tools/irc.d.ts +22 -33
  81. package/dist/types/tools/job.d.ts +11 -12
  82. package/dist/types/tools/learn.d.ts +21 -28
  83. package/dist/types/tools/manage-skill.d.ts +13 -22
  84. package/dist/types/tools/memory-edit.d.ts +15 -24
  85. package/dist/types/tools/memory-recall.d.ts +7 -8
  86. package/dist/types/tools/memory-reflect.d.ts +9 -10
  87. package/dist/types/tools/memory-retain.d.ts +13 -14
  88. package/dist/types/tools/read.d.ts +8 -8
  89. package/dist/types/tools/resolve.d.ts +11 -18
  90. package/dist/types/tools/review.d.ts +9 -15
  91. package/dist/types/tools/search-tool-bm25.d.ts +9 -10
  92. package/dist/types/tools/search.d.ts +16 -17
  93. package/dist/types/tools/ssh.d.ts +14 -15
  94. package/dist/types/tools/todo.d.ts +27 -43
  95. package/dist/types/tools/tts.d.ts +8 -9
  96. package/dist/types/tools/write.d.ts +9 -10
  97. package/dist/types/tui/code-cell.d.ts +2 -0
  98. package/dist/types/tui/index.d.ts +1 -0
  99. package/dist/types/tui/width-aware-text.d.ts +23 -0
  100. package/dist/types/utils/image-vision-fallback.d.ts +28 -0
  101. package/dist/types/utils/markit.d.ts +10 -1
  102. package/dist/types/web/search/index.d.ts +17 -28
  103. package/dist/types/web/search/providers/base.d.ts +1 -0
  104. package/dist/types/web/search/providers/gemini.d.ts +1 -0
  105. package/dist/types/web/search/providers/perplexity.d.ts +0 -2
  106. package/dist/types/web/search/types.d.ts +32 -26
  107. package/package.json +14 -13
  108. package/scripts/omp +1 -1
  109. package/src/advisor/__tests__/advisor.test.ts +103 -1
  110. package/src/advisor/advise-tool.ts +47 -11
  111. package/src/autoresearch/tools/init-experiment.ts +13 -16
  112. package/src/autoresearch/tools/log-experiment.ts +15 -18
  113. package/src/autoresearch/tools/run-experiment.ts +3 -3
  114. package/src/autoresearch/tools/update-notes.ts +4 -4
  115. package/src/cli/args.ts +1 -0
  116. package/src/cli/bench-cli.ts +30 -7
  117. package/src/cli/flag-tables.ts +8 -0
  118. package/src/cli/ttsr-cli.ts +995 -0
  119. package/src/cli-commands.ts +1 -0
  120. package/src/cli.ts +7 -1
  121. package/src/collab/host.ts +2 -2
  122. package/src/commands/launch.ts +3 -0
  123. package/src/commands/ttsr.ts +125 -0
  124. package/src/commit/agentic/tools/analyze-file.ts +4 -4
  125. package/src/commit/agentic/tools/git-file-diff.ts +4 -4
  126. package/src/commit/agentic/tools/git-hunk.ts +7 -5
  127. package/src/commit/agentic/tools/git-overview.ts +4 -4
  128. package/src/commit/agentic/tools/propose-changelog.ts +18 -15
  129. package/src/commit/agentic/tools/propose-commit.ts +6 -6
  130. package/src/commit/agentic/tools/recent-commits.ts +3 -3
  131. package/src/commit/agentic/tools/schemas.ts +8 -20
  132. package/src/commit/agentic/tools/split-commit.ts +19 -23
  133. package/src/commit/analysis/summary.ts +7 -5
  134. package/src/commit/changelog/generate.ts +15 -11
  135. package/src/commit/shared-llm.ts +17 -24
  136. package/src/config/config-file.ts +13 -15
  137. package/src/config/keybindings.ts +6 -0
  138. package/src/config/models-config-schema.ts +206 -179
  139. package/src/config/settings-schema.ts +118 -2
  140. package/src/discovery/builtin-rules/index.ts +2 -0
  141. package/src/discovery/builtin-rules/ts-import-type.md +2 -2
  142. package/src/discovery/builtin-rules/ts-no-any.md +11 -2
  143. package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +55 -0
  144. package/src/edit/hashline/params.ts +12 -11
  145. package/src/edit/index.ts +5 -4
  146. package/src/edit/modes/apply-patch.ts +4 -4
  147. package/src/edit/modes/patch.ts +15 -18
  148. package/src/edit/modes/replace.ts +13 -17
  149. package/src/edit/renderer.ts +0 -1
  150. package/src/eval/agent-bridge.ts +11 -13
  151. package/src/eval/completion-bridge.ts +25 -17
  152. package/src/eval/js/context-manager.ts +17 -2
  153. package/src/eval/js/index.ts +1 -1
  154. package/src/eval/py/executor.ts +2 -2
  155. package/src/eval/py/runner.py +44 -0
  156. package/src/extensibility/custom-commands/loader.ts +5 -3
  157. package/src/extensibility/custom-commands/types.ts +6 -3
  158. package/src/extensibility/custom-tools/loader.ts +4 -2
  159. package/src/extensibility/custom-tools/types.ts +8 -5
  160. package/src/extensibility/extensions/loader.ts +4 -2
  161. package/src/extensibility/extensions/runner.ts +20 -2
  162. package/src/extensibility/extensions/types.ts +22 -8
  163. package/src/extensibility/hooks/loader.ts +5 -2
  164. package/src/extensibility/hooks/types.ts +7 -4
  165. package/src/extensibility/legacy-pi-ai-shim.ts +42 -5
  166. package/src/extensibility/legacy-pi-coding-agent-shim.ts +113 -0
  167. package/src/extensibility/plugins/legacy-pi-compat.ts +13 -13
  168. package/src/extensibility/shared-events.ts +24 -0
  169. package/src/extensibility/tool-proxy.ts +4 -1
  170. package/src/extensibility/typebox.ts +778 -251
  171. package/src/goals/guided-setup.ts +12 -3
  172. package/src/goals/tools/goal-tool.ts +6 -6
  173. package/src/index.ts +2 -0
  174. package/src/internal-urls/docs-index.generated.ts +15 -13
  175. package/src/lsp/types.ts +13 -27
  176. package/src/main.ts +29 -21
  177. package/src/mcp/client.ts +38 -13
  178. package/src/mcp/render.ts +102 -89
  179. package/src/modes/components/agent-hub.ts +11 -4
  180. package/src/modes/components/branch-summary-message.ts +1 -0
  181. package/src/modes/components/btw-panel.ts +5 -1
  182. package/src/modes/components/collab-prompt-message.ts +9 -7
  183. package/src/modes/components/compaction-summary-message.ts +1 -0
  184. package/src/modes/components/custom-editor.ts +18 -0
  185. package/src/modes/components/custom-message.ts +1 -0
  186. package/src/modes/components/footer.ts +6 -5
  187. package/src/modes/components/hook-message.ts +1 -0
  188. package/src/modes/components/read-tool-group.ts +9 -3
  189. package/src/modes/components/skill-message.ts +1 -0
  190. package/src/modes/components/status-line/component.ts +139 -15
  191. package/src/modes/components/status-line/context-thresholds.ts +0 -1
  192. package/src/modes/components/todo-reminder.ts +1 -0
  193. package/src/modes/components/tool-execution.ts +17 -10
  194. package/src/modes/components/ttsr-notification.ts +1 -0
  195. package/src/modes/components/user-message.ts +6 -6
  196. package/src/modes/controllers/btw-controller.ts +69 -1
  197. package/src/modes/controllers/event-controller.ts +2 -7
  198. package/src/modes/controllers/input-controller.ts +29 -0
  199. package/src/modes/controllers/selector-controller.ts +10 -3
  200. package/src/modes/interactive-mode.ts +42 -10
  201. package/src/modes/rpc/rpc-types.ts +1 -1
  202. package/src/modes/setup-wizard/index.ts +1 -0
  203. package/src/modes/setup-wizard/scenes/sign-in.ts +77 -5
  204. package/src/modes/setup-wizard/startup-splash.ts +107 -0
  205. package/src/modes/theme/theme.ts +133 -143
  206. package/src/modes/types.ts +3 -0
  207. package/src/modes/utils/context-usage.ts +37 -20
  208. package/src/modes/utils/hotkeys-markdown.ts +1 -0
  209. package/src/prompts/system/system-prompt.md +1 -0
  210. package/src/prompts/tools/image-attachment-describe-system.md +8 -0
  211. package/src/prompts/tools/image-attachment-describe.md +10 -0
  212. package/src/sdk.ts +35 -22
  213. package/src/session/agent-session.ts +715 -255
  214. package/src/session/session-history-format.ts +11 -2
  215. package/src/session/session-loader.ts +19 -32
  216. package/src/session/session-persistence.ts +27 -11
  217. package/src/session/snapcompact-inline.ts +1 -1
  218. package/src/slash-commands/builtin-registry.ts +4 -11
  219. package/src/ssh/connection-manager.ts +3 -2
  220. package/src/startup-splash.ts +19 -0
  221. package/src/task/executor.ts +12 -7
  222. package/src/task/types.ts +44 -41
  223. package/src/tool-discovery/tool-index.ts +17 -4
  224. package/src/tools/ask.ts +14 -14
  225. package/src/tools/ast-edit.ts +17 -14
  226. package/src/tools/ast-grep.ts +10 -9
  227. package/src/tools/bash.ts +15 -10
  228. package/src/tools/browser/launch.ts +13 -0
  229. package/src/tools/browser.ts +26 -32
  230. package/src/tools/checkpoint.ts +7 -7
  231. package/src/tools/debug.ts +72 -69
  232. package/src/tools/eval.ts +18 -19
  233. package/src/tools/find.ts +20 -13
  234. package/src/tools/gh.ts +29 -49
  235. package/src/tools/image-gen.ts +94 -57
  236. package/src/tools/inspect-image.ts +8 -9
  237. package/src/tools/irc.ts +12 -12
  238. package/src/tools/job.ts +6 -6
  239. package/src/tools/learn.ts +11 -14
  240. package/src/tools/manage-skill.ts +19 -23
  241. package/src/tools/memory-edit.ts +8 -8
  242. package/src/tools/memory-recall.ts +4 -4
  243. package/src/tools/memory-reflect.ts +5 -5
  244. package/src/tools/memory-retain.ts +9 -11
  245. package/src/tools/puppeteer/02_stealth_hairline.txt +1 -1
  246. package/src/tools/puppeteer/04_stealth_iframe.txt +4 -4
  247. package/src/tools/puppeteer/05_stealth_webgl.txt +1 -1
  248. package/src/tools/puppeteer/10_stealth_plugins.txt +6 -4
  249. package/src/tools/puppeteer/12_stealth_codecs.txt +2 -2
  250. package/src/tools/puppeteer/13_stealth_worker.txt +1 -1
  251. package/src/tools/read.ts +197 -19
  252. package/src/tools/report-tool-issue.ts +6 -6
  253. package/src/tools/resolve.ts +6 -6
  254. package/src/tools/review.ts +10 -12
  255. package/src/tools/search-tool-bm25.ts +5 -5
  256. package/src/tools/search.ts +20 -29
  257. package/src/tools/ssh.ts +8 -8
  258. package/src/tools/todo.ts +16 -19
  259. package/src/tools/tts.ts +16 -15
  260. package/src/tools/write.ts +5 -5
  261. package/src/tui/code-cell.ts +44 -3
  262. package/src/tui/index.ts +1 -0
  263. package/src/tui/width-aware-text.ts +58 -0
  264. package/src/utils/image-vision-fallback.ts +197 -0
  265. package/src/utils/markit.ts +17 -2
  266. package/src/web/search/index.ts +21 -9
  267. package/src/web/search/providers/base.ts +1 -0
  268. package/src/web/search/providers/gemini.ts +56 -18
  269. package/src/web/search/providers/perplexity.ts +373 -126
  270. package/src/web/search/types.ts +28 -48
@@ -44,6 +44,8 @@ interface PendingRun {
44
44
 
45
45
  interface JsSession {
46
46
  sessionKey: string;
47
+ sessionId: string;
48
+ cwd: string;
47
49
  worker: WorkerHandle;
48
50
  state: "alive" | "dead";
49
51
  pending: Map<string, PendingRun>;
@@ -151,7 +153,14 @@ export async function disposeAllVmContexts(): Promise<void> {
151
153
  */
152
154
  export async function smokeTestJsEvalWorker(): Promise<void> {
153
155
  const worker = spawnJsWorker();
154
- const session: JsSession = { sessionKey: "smoke", worker, state: "alive", pending: new Map() };
156
+ const session: JsSession = {
157
+ sessionKey: "smoke",
158
+ sessionId: "smoke",
159
+ cwd: process.cwd(),
160
+ worker,
161
+ state: "alive",
162
+ pending: new Map(),
163
+ };
155
164
  try {
156
165
  await initWorker(session, { cwd: process.cwd(), sessionId: "smoke" }, WORKER_INIT_TIMEOUT_MS);
157
166
  if (worker.mode !== "worker") {
@@ -219,7 +228,11 @@ async function runOnce(
219
228
 
220
229
  async function acquireSession(sessionKey: string, snapshot: SessionSnapshot, timeoutMs?: number): Promise<JsSession> {
221
230
  const existing = sessions.get(sessionKey);
222
- if (existing && existing.state === "alive") return existing;
231
+ if (existing && existing.state === "alive") {
232
+ existing.sessionId = snapshot.sessionId;
233
+ existing.cwd = snapshot.cwd;
234
+ return existing;
235
+ }
223
236
  const starting = startingSessions.get(sessionKey);
224
237
  if (starting) return await starting;
225
238
 
@@ -231,6 +244,8 @@ async function acquireSession(sessionKey: string, snapshot: SessionSnapshot, tim
231
244
  const worker = spawnJsWorker();
232
245
  const session: JsSession = {
233
246
  sessionKey,
247
+ sessionId: snapshot.sessionId,
248
+ cwd: snapshot.cwd,
234
249
  worker,
235
250
  state: "alive",
236
251
  pending: new Map(),
@@ -9,7 +9,7 @@ import { executeJs } from "./executor";
9
9
 
10
10
  const JS_SESSION_PREFIX = "js:";
11
11
 
12
- function namespaceSessionId(sessionId: string): string {
12
+ export function namespaceSessionId(sessionId: string): string {
13
13
  return sessionId.startsWith(JS_SESSION_PREFIX) ? sessionId : `${JS_SESSION_PREFIX}${sessionId}`;
14
14
  }
15
15
 
@@ -632,7 +632,7 @@ async function executePerCall(code: string, cwd: string, options: PythonExecutor
632
632
  }
633
633
  const kernel = await startKernel(cwd, options);
634
634
  try {
635
- return await executeWithKernel(kernel, code, { ...options, cwd: undefined });
635
+ return await executeWithKernel(kernel, code, { ...options, cwd });
636
636
  } finally {
637
637
  await kernel.shutdown().catch(() => undefined);
638
638
  }
@@ -682,7 +682,7 @@ async function executeOnSession(code: string, cwd: string, options: PythonExecut
682
682
  throw new PythonExecutionCancelledError(false);
683
683
  }
684
684
  }
685
- const runOptions = { ...options, cwd: undefined };
685
+ const runOptions = { ...options, cwd };
686
686
  try {
687
687
  return await executeWithKernel(session.kernel, code, runOptions);
688
688
  } catch (err) {
@@ -190,6 +190,11 @@ class _RunnerState:
190
190
 
191
191
 
192
192
  _CURRENT_RID: contextvars.ContextVar[str | None] = contextvars.ContextVar("omp_current_rid", default=None)
193
+ _CURRENT_DISPLAYED_MATPLOTLIB_FIGURE_IDS: contextvars.ContextVar[set[int] | None] = contextvars.ContextVar(
194
+ "omp_displayed_matplotlib_figure_ids",
195
+ default=None,
196
+ )
197
+
193
198
 
194
199
  _STATE = _RunnerState()
195
200
 
@@ -670,6 +675,36 @@ _REPR_MIMES = [
670
675
  ]
671
676
 
672
677
 
678
+ def _is_matplotlib_figure(value: Any) -> bool:
679
+ figure_module = sys.modules.get("matplotlib.figure")
680
+ figure_cls = getattr(figure_module, "Figure", None)
681
+ if isinstance(figure_cls, type) and isinstance(value, figure_cls):
682
+ return True
683
+
684
+ value_type = type(value)
685
+ return value_type.__module__ == "matplotlib.figure" and value_type.__name__ == "Figure"
686
+
687
+
688
+ def _matplotlib_figure_png(value: Any) -> str | None:
689
+ if not _is_matplotlib_figure(value):
690
+ return None
691
+
692
+ savefig = getattr(value, "savefig", None)
693
+ if not callable(savefig):
694
+ return None
695
+
696
+ try:
697
+ buf = io.BytesIO()
698
+ savefig(buf, format="png", bbox_inches="tight")
699
+ except Exception:
700
+ return None
701
+
702
+ displayed_ids = _CURRENT_DISPLAYED_MATPLOTLIB_FIGURE_IDS.get()
703
+ if displayed_ids is not None:
704
+ displayed_ids.add(id(value))
705
+ return base64.b64encode(buf.getvalue()).decode("ascii")
706
+
707
+
673
708
  def _coerce_image_bytes(value: Any) -> str:
674
709
  if isinstance(value, (bytes, bytearray)):
675
710
  return base64.b64encode(bytes(value)).decode("ascii")
@@ -685,6 +720,10 @@ def _mime_bundle(value: Any) -> dict:
685
720
  accessors, and always provides ``text/plain``.
686
721
  """
687
722
  bundle: dict[str, Any] = {}
723
+ matplotlib_png = _matplotlib_figure_png(value)
724
+ if matplotlib_png is not None:
725
+ bundle["image/png"] = matplotlib_png
726
+
688
727
 
689
728
  mimebundle = getattr(value, "_repr_mimebundle_", None)
690
729
  if callable(mimebundle):
@@ -758,6 +797,9 @@ def _flush_matplotlib_figures() -> None:
758
797
  for num in fignums:
759
798
  try:
760
799
  fig = plt.figure(num)
800
+ if id(fig) in (_CURRENT_DISPLAYED_MATPLOTLIB_FIGURE_IDS.get() or set()):
801
+ plt.close(fig)
802
+ continue
761
803
  buf = io.BytesIO()
762
804
  fig.savefig(buf, format="png", bbox_inches="tight")
763
805
  data = base64.b64encode(buf.getvalue()).decode("ascii")
@@ -978,6 +1020,7 @@ def _start_parent_watchdog() -> None:
978
1020
  async def _handle_request_async(req: dict) -> None:
979
1021
  rid = str(req.get("id"))
980
1022
  token = _CURRENT_RID.set(rid)
1023
+ displayed_matplotlib_token = _CURRENT_DISPLAYED_MATPLOTLIB_FIGURE_IDS.set(set())
981
1024
  _STATE.capture_rid = rid
982
1025
  _STATE.user_ns["__omp_run_id__"] = rid
983
1026
  _STATE.cancel_requested = False
@@ -1046,6 +1089,7 @@ async def _handle_request_async(req: dict) -> None:
1046
1089
  _STATE.capture_rid = None
1047
1090
  _flush_stream_proxies(rid)
1048
1091
  _CURRENT_RID.reset(token)
1092
+ _CURRENT_DISPLAYED_MATPLOTLIB_FIGURE_IDS.reset(displayed_matplotlib_token)
1049
1093
 
1050
1094
 
1051
1095
  def _emit_error(rid: str, exc: BaseException) -> None:
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * Custom command loader - loads TypeScript command modules using native Bun import.
3
3
  *
4
- * Dependencies (the zod-backed typebox shim and pi-coding-agent) are injected via the
4
+ * Dependencies (the arktype validation and pi-coding-agent) are injected via the
5
5
  * CustomCommandAPI to avoid import resolution issues with custom commands loaded from user directories.
6
6
  */
7
7
  import * as fs from "node:fs";
8
8
  import * as path from "node:path";
9
9
  import { getAgentDir, getProjectDir, isEnoent, logger } from "@oh-my-pi/pi-utils";
10
- import { z as zod } from "zod/v4";
10
+ import * as arktype from "arktype";
11
+ import * as zodModule from "zod/v4";
11
12
  import { getConfigDirs } from "../../config";
12
13
  import { execCommand } from "../../exec/exec";
13
14
  // Runtime self-reference: dereference this namespace only inside loader functions to keep the index.ts cycle safe.
@@ -186,7 +187,8 @@ export async function loadCustomCommands(options: LoadCustomCommandsOptions = {}
186
187
  exec: (command: string, args: string[], execOptions) =>
187
188
  execCommand(command, args, execOptions?.cwd ?? cwd, execOptions),
188
189
  typebox,
189
- zod,
190
+ arktype,
191
+ zod: zodModule,
190
192
  pi: PiCodingAgent,
191
193
  };
192
194
 
@@ -5,7 +5,8 @@
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 { z } from "zod/v4";
8
+ import type * as arktype from "arktype";
9
+ import type * as zod from "zod/v4";
9
10
  import type { ExecOptions, ExecResult, HookCommandContext } from "../../extensibility/hooks/types";
10
11
  import type * as PiCodingAgent from "../../index";
11
12
  import type * as TypeBox from "../typebox";
@@ -24,8 +25,10 @@ export interface CustomCommandAPI {
24
25
  exec(command: string, args: string[], options?: ExecOptions): Promise<ExecResult>;
25
26
  /** Injected zod-backed typebox shim (legacy/compat). */
26
27
  typebox: typeof TypeBox;
27
- /** Injected zod module for Zod-authored custom commands. */
28
- zod: typeof z;
28
+ /** Injected arktype module for validation in custom commands. */
29
+ arktype: typeof arktype;
30
+ /** Injected zod/v4 module for canonical command validation. */
31
+ zod: typeof zod;
29
32
  /** Injected pi-coding-agent exports */
30
33
  pi: typeof PiCodingAgent;
31
34
  }
@@ -7,7 +7,8 @@
7
7
  import * as path from "node:path";
8
8
  import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
9
9
  import { logger } from "@oh-my-pi/pi-utils";
10
- import { z } from "zod/v4";
10
+ import { type } from "arktype";
11
+ import * as zodModule from "zod/v4";
11
12
  import { toolCapability } from "../../capability/tool";
12
13
  import { type CustomTool, loadCapability } from "../../discovery";
13
14
  import type { ExecOptions } from "../../exec/exec";
@@ -108,7 +109,8 @@ export class CustomToolLoader {
108
109
  hasUI: false,
109
110
  logger,
110
111
  typebox,
111
- zod: z,
112
+ arktype: type,
113
+ zod: zodModule,
112
114
  pi,
113
115
  pushPendingAction: action => {
114
116
  if (!pushPendingAction) {
@@ -15,7 +15,8 @@ import type { CompactionResult } from "@oh-my-pi/pi-agent-core/compaction";
15
15
  import type { FetchImpl, Model, Static, TSchema } from "@oh-my-pi/pi-ai";
16
16
  import type { Component } from "@oh-my-pi/pi-tui";
17
17
  import type { logger as PiLogger } from "@oh-my-pi/pi-utils";
18
- import type { z } from "zod/v4";
18
+ import type { type as ArkType } from "arktype";
19
+ import type * as zod from "zod/v4";
19
20
  import type { Rule } from "../../capability/rule";
20
21
  import type { ModelRegistry } from "../../config/model-registry";
21
22
  import type { Settings } from "../../config/settings";
@@ -61,10 +62,12 @@ export interface CustomToolAPI {
61
62
  hasUI: boolean;
62
63
  /** File logger for error/warning/debug messages */
63
64
  logger: typeof PiLogger;
64
- /** Injected zod-backed typebox shim (legacy/compat — Zod-authored tools are preferred). */
65
+ /** Injected typebox shim (legacy/compat — arktype-authored tools are preferred). */
65
66
  typebox: typeof TypeBox;
66
- /** Injected zod module for Zod-authored custom tools. */
67
- zod: typeof z;
67
+ /** Injected arktype module for arktype-authored custom tools. */
68
+ arktype: typeof ArkType;
69
+ /** Injected zod/v4 module for canonical parameter schemas. */
70
+ zod: typeof zod;
68
71
  /** Injected pi-coding-agent exports */
69
72
  pi: typeof PiCodingAgent;
70
73
  /** Push a preview action that can later be resolved with the hidden resolve tool */
@@ -195,7 +198,7 @@ export interface CustomTool<TParams extends TSchema = TSchema, TDetails = any> {
195
198
  strict?: boolean;
196
199
  /** Description for LLM */
197
200
  description: string;
198
- /** Parameter schema (Zod or TypeBox; TypeBox is auto-lifted to Zod at registration). */
201
+ /** Parameter schema (arktype, TypeBox, or legacy formats). */
199
202
  parameters: TParams;
200
203
  /** If true, tool is excluded unless explicitly listed in --tools or agent's tools field */
201
204
  hidden?: boolean;
@@ -8,7 +8,8 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
8
8
  import type { ImageContent, Model, TextContent, TSchema } from "@oh-my-pi/pi-ai";
9
9
  import type { KeyId } from "@oh-my-pi/pi-tui";
10
10
  import { hasFsCode, isEacces, isEnoent, logger } from "@oh-my-pi/pi-utils";
11
- import { z } from "zod/v4";
11
+ import { Type } from "arktype";
12
+ import * as zodModule from "zod/v4";
12
13
  import { type ExtensionModule, extensionModuleCapability } from "../../capability/extension-module";
13
14
  import { type Hook, hookCapability } from "../../capability/hook";
14
15
  import { loadCapability } from "../../discovery";
@@ -123,7 +124,8 @@ export class ExtensionRuntime implements IExtensionRuntime {
123
124
  class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
124
125
  readonly logger = logger;
125
126
  readonly typebox = TypeBox;
126
- readonly zod = z;
127
+ readonly arktype = Type;
128
+ readonly zod = zodModule;
127
129
  readonly flagValues = new Map<string, boolean | string>();
128
130
  readonly pendingProviderRegistrations: Array<{
129
131
  name: string;
@@ -46,6 +46,8 @@ import type {
46
46
  SessionBeforeSwitchResult,
47
47
  SessionBeforeTreeResult,
48
48
  SessionCompactingResult,
49
+ SessionStopEvent,
50
+ SessionStopEventResult,
49
51
  ToolCallEvent,
50
52
  ToolCallEventResult,
51
53
  ToolResultEvent,
@@ -135,7 +137,9 @@ type RunnerEmitResult<TEvent extends RunnerEmitEvent> = TEvent extends { type: "
135
137
  ? SessionBeforeTreeResult | undefined
136
138
  : TEvent extends { type: "session.compacting" }
137
139
  ? SessionCompactingResult | undefined
138
- : undefined;
140
+ : TEvent extends { type: "session_stop" }
141
+ ? SessionStopEventResult | undefined
142
+ : undefined;
139
143
 
140
144
  export type NewSessionHandler = (options?: {
141
145
  parentSession?: string;
@@ -322,6 +326,10 @@ export class ExtensionRunner {
322
326
  await this.emit({ type: "credential_disabled", ...event });
323
327
  }
324
328
 
329
+ async emitSessionStop(event: Omit<SessionStopEvent, "type">): Promise<SessionStopEventResult | undefined> {
330
+ return await this.emit({ type: "session_stop", ...event });
331
+ }
332
+
325
333
  getUIContext(): ExtensionUIContext {
326
334
  return this.#uiContext;
327
335
  }
@@ -588,7 +596,7 @@ export class ExtensionRunner {
588
596
 
589
597
  async emit<TEvent extends RunnerEmitEvent>(event: TEvent): Promise<RunnerEmitResult<TEvent>> {
590
598
  const ctx = this.createContext();
591
- let result: SessionBeforeEventResult | SessionCompactingResult | undefined;
599
+ let result: SessionBeforeEventResult | SessionCompactingResult | SessionStopEventResult | undefined;
592
600
 
593
601
  if (this.#isSessionShutdownEvent(event)) {
594
602
  const timeoutMs = handlerTimeoutForEvent(event.type);
@@ -627,6 +635,16 @@ export class ExtensionRunner {
627
635
  if (event.type === "session.compacting" && handlerResult) {
628
636
  result = handlerResult as SessionCompactingResult;
629
637
  }
638
+
639
+ if (event.type === "session_stop" && handlerResult) {
640
+ result = handlerResult as SessionStopEventResult;
641
+ const hasContinuationContext =
642
+ (typeof result.additionalContext === "string" && result.additionalContext.length > 0) ||
643
+ (typeof result.reason === "string" && result.reason.length > 0);
644
+ if ((result.continue === true || result.decision === "block") && hasContinuationContext) {
645
+ return result as RunnerEmitResult<TEvent>;
646
+ }
647
+ }
630
648
  }
631
649
  }
632
650
 
@@ -32,7 +32,8 @@ import type {
32
32
  import type { OAuthCredentials, OAuthLoginCallbacks } from "@oh-my-pi/pi-ai/oauth/types";
33
33
  import type { AutocompleteItem, Component, EditorTheme, KeyId, TUI } from "@oh-my-pi/pi-tui";
34
34
  import type { logger as PiLogger } from "@oh-my-pi/pi-utils";
35
- import type { z } from "zod/v4";
35
+ import type { Type as arktype } from "arktype";
36
+ import type * as zod from "zod/v4";
36
37
  import type { KeybindingsManager } from "../../config/keybindings";
37
38
  import type { ModelRegistry } from "../../config/model-registry";
38
39
  import type { EditToolDetails } from "../../edit";
@@ -82,6 +83,8 @@ import type {
82
83
  SessionEvent,
83
84
  SessionShutdownEvent,
84
85
  SessionStartEvent,
86
+ SessionStopEvent,
87
+ SessionStopEventResult,
85
88
  SessionSwitchEvent,
86
89
  SessionTreeEvent,
87
90
  TodoReminderEvent,
@@ -274,11 +277,11 @@ export interface ExtensionUIContext {
274
277
  // ============================================================================
275
278
 
276
279
  export interface ContextUsage {
277
- /** Estimated context tokens, or null if unknown (e.g. right after compaction, before next LLM response). */
278
- tokens: number | null;
280
+ /** Estimated context tokens. */
281
+ tokens: number;
279
282
  contextWindow: number;
280
- /** Context usage as percentage of context window, or null if tokens is unknown. */
281
- percent: number | null;
283
+ /** Context usage as percentage of context window. */
284
+ percent: number;
282
285
  }
283
286
 
284
287
  export interface CompactOptions {
@@ -525,7 +528,14 @@ export interface BeforeAgentStartEvent {
525
528
  systemPrompt: string[];
526
529
  }
527
530
 
528
- export type { AgentEndEvent, AgentStartEvent, TurnEndEvent, TurnStartEvent } from "../shared-events";
531
+ export type {
532
+ AgentEndEvent,
533
+ AgentStartEvent,
534
+ SessionStopEvent,
535
+ SessionStopEventResult,
536
+ TurnEndEvent,
537
+ TurnStartEvent,
538
+ } from "../shared-events";
529
539
 
530
540
  /** Fired when a message starts (user, assistant, or toolResult) */
531
541
  export interface MessageStartEvent {
@@ -802,6 +812,7 @@ export type ExtensionEvent =
802
812
  | BeforeAgentStartEvent
803
813
  | AgentStartEvent
804
814
  | AgentEndEvent
815
+ | SessionStopEvent
805
816
  | TurnStartEvent
806
817
  | TurnEndEvent
807
818
  | MessageStartEvent
@@ -938,8 +949,10 @@ export interface ExtensionAPI {
938
949
  /** Injected zod-backed typebox shim for legacy `Type.Object(...)` parameter authoring. */
939
950
  typebox: typeof TypeBox;
940
951
 
941
- /** Injected zod module for Zod-authored extension tools (canonical going forward). */
942
- zod: typeof z;
952
+ /** Injected arktype module for arktype-authored extension tools (canonical going forward). */
953
+ arktype: typeof arktype;
954
+ /** Injected zod/v4 module for canonical extension tool parameter schemas. */
955
+ zod: typeof zod;
943
956
 
944
957
  /** Injected pi-coding-agent exports for accessing SDK utilities */
945
958
  pi: typeof PiCodingAgent;
@@ -978,6 +991,7 @@ export interface ExtensionAPI {
978
991
  on(event: "before_agent_start", handler: ExtensionHandler<BeforeAgentStartEvent, BeforeAgentStartEventResult>): void;
979
992
  on(event: "agent_start", handler: ExtensionHandler<AgentStartEvent>): void;
980
993
  on(event: "agent_end", handler: ExtensionHandler<AgentEndEvent>): void;
994
+ on(event: "session_stop", handler: ExtensionHandler<SessionStopEvent, SessionStopEventResult>): void;
981
995
  on(event: "turn_start", handler: ExtensionHandler<TurnStartEvent>): void;
982
996
  on(event: "turn_end", handler: ExtensionHandler<TurnEndEvent>): void;
983
997
  on(event: "message_start", handler: ExtensionHandler<MessageStartEvent>): void;
@@ -3,7 +3,8 @@
3
3
  */
4
4
  import * as path from "node:path";
5
5
  import { logger } from "@oh-my-pi/pi-utils";
6
- import { z as zod } from "zod/v4";
6
+ import * as arktype from "arktype";
7
+ import * as zodModule from "zod/v4";
7
8
  import { hookCapability } from "../../capability/hook";
8
9
  import type { Hook } from "../../discovery";
9
10
  import { loadCapability } from "../../discovery";
@@ -139,7 +140,9 @@ async function createHookAPI(
139
140
  },
140
141
  logger,
141
142
  typebox,
142
- zod,
143
+ // HookAPI.arktype is typed as the arktype `Type` constructor; expose it from the module namespace.
144
+ arktype: arktype.Type,
145
+ zod: zodModule,
143
146
  pi: PiCodingAgent,
144
147
  } as HookAPI;
145
148
 
@@ -1,7 +1,8 @@
1
1
  import type { ImageContent, Message, Model, TextContent } from "@oh-my-pi/pi-ai";
2
2
  import type { Component, TUI } from "@oh-my-pi/pi-tui";
3
3
  import type { logger as PiLogger } from "@oh-my-pi/pi-utils";
4
- import type { z } from "zod/v4";
4
+ import type { Type } from "arktype";
5
+ import type * as zod from "zod/v4";
5
6
  import type { ModelRegistry } from "../../config/model-registry";
6
7
  import type { EditToolDetails } from "../../edit";
7
8
  import type { ExecOptions, ExecResult } from "../../exec/exec";
@@ -582,10 +583,12 @@ export interface HookAPI {
582
583
 
583
584
  /** File logger for error/warning/debug messages */
584
585
  logger: typeof PiLogger;
585
- /** Injected zod-backed typebox shim (legacy/compat — prefer `zod`). */
586
+ /** Injected zod-backed typebox shim (legacy/compat — prefer `arktype`). */
586
587
  typebox: typeof TypeBox;
587
- /** Injected zod module for Zod-authored hooks. */
588
- zod: typeof z;
588
+ /** Injected arktype module for arktype-authored hooks. */
589
+ arktype: typeof Type;
590
+ /** Injected zod/v4 module for canonical hook validation. */
591
+ zod: typeof zod;
589
592
  /** Injected pi-coding-agent exports */
590
593
  pi: typeof PiCodingAgent;
591
594
  }
@@ -11,14 +11,51 @@
11
11
  * bare package root. Subpath imports (`@oh-my-pi/pi-ai/oauth`, etc.)
12
12
  * continue to resolve directly against the bundled pi-ai package.
13
13
  *
14
- * The `Type` runtime is borrowed from the Zod-backed TypeBox shim that
15
- * already serves bare `@sinclair/typebox` imports for the same extension
16
- * class, keeping the legacy-compat surface internally consistent.
14
+ * The `Type` runtime and legacy `StringEnum()` helper are borrowed from the
15
+ * Zod-backed TypeBox shim that already serves TypeBox imports for the same
16
+ * extension class, keeping the legacy-compat surface internally consistent.
17
17
  *
18
18
  * Type-level `Static` and `TSchema` continue to come from pi-ai's own
19
19
  * `types.ts` via the `export *` below — pi-ai still exports both as types,
20
- * only the runtime `Type` builder was removed.
20
+ * only the runtime `Type` builder and `StringEnum()` helper were removed.
21
21
  */
22
+ import { type TSchema, Type } from "./typebox";
23
+
24
+ export interface StringEnumOptions<T extends string> {
25
+ description?: string;
26
+ default?: T;
27
+ examples?: T[];
28
+ [key: string]: unknown;
29
+ }
30
+
31
+ function stringEnumWireSchema<T extends string>(values: readonly T[], options: StringEnumOptions<T> | undefined) {
32
+ const schema: Record<string, unknown> = {
33
+ type: "string",
34
+ enum: [...values],
35
+ };
36
+ if (!options) return schema;
37
+ for (const key in options) {
38
+ if (options[key] !== undefined) {
39
+ schema[key] = options[key];
40
+ }
41
+ }
42
+ return schema;
43
+ }
44
+
45
+ export function StringEnum<T extends string>(values: readonly T[], options?: StringEnumOptions<T>): TSchema {
46
+ const opts = {
47
+ description: options?.description ?? "Legacy string enum compatibility schema",
48
+ ...options,
49
+ };
50
+ const schema: TSchema = values.length === 0 ? Type.Never(opts) : Type.Enum(values, opts);
51
+ Object.defineProperty(schema, "toJSON", {
52
+ value: () => stringEnumWireSchema(values, options),
53
+ enumerable: false,
54
+ writable: true,
55
+ configurable: true,
56
+ });
57
+ return schema;
58
+ }
22
59
 
23
60
  export * from "@oh-my-pi/pi-ai";
24
- export { Type } from "./typebox";
61
+ export { Type };
@@ -12,4 +12,117 @@
12
12
  * the same module identity as a direct `@oh-my-pi/pi-coding-agent` import.
13
13
  */
14
14
 
15
+ import type { AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
16
+ import type { TSchema } from "@oh-my-pi/pi-ai";
17
+ import { parseFrontmatter as parseOmpFrontmatter } from "@oh-my-pi/pi-utils";
18
+ import { Settings } from "../config/settings";
19
+ import { BUILTIN_TOOLS, type Tool, type ToolSession } from "../tools";
20
+ import type { ToolDefinition } from "./extensions/types";
21
+ import { Type } from "./typebox";
22
+
23
+ const TOOL_DEFINITION_MARKER = "__isToolDefinition";
24
+ const LEGACY_BUILTIN_TOOL_MARKER = "__ompLegacyBuiltinTool";
25
+ const LEGACY_CODING_TOOL_NAMES = ["read", "bash", "edit", "write"] as const;
26
+
27
+ type LegacyCodingToolName = (typeof LEGACY_CODING_TOOL_NAMES)[number];
28
+ type LegacyBuiltinToolDefinition = ToolDefinition & { [LEGACY_BUILTIN_TOOL_MARKER]: true };
29
+
30
+ function markToolDefinition<TParams extends TSchema, TDetails>(
31
+ tool: ToolDefinition<TParams, TDetails>,
32
+ ): ToolDefinition<TParams, TDetails> {
33
+ Object.defineProperty(tool, TOOL_DEFINITION_MARKER, {
34
+ value: true,
35
+ enumerable: false,
36
+ writable: false,
37
+ configurable: true,
38
+ });
39
+ return tool;
40
+ }
41
+ function legacyToolSession(cwd: string): ToolSession {
42
+ return {
43
+ cwd,
44
+ hasUI: false,
45
+ getSessionFile: () => null,
46
+ getSessionSpawns: () => null,
47
+ settings: Settings.isolated(),
48
+ };
49
+ }
50
+
51
+ function createBuiltinTool(cwd: string, name: LegacyCodingToolName): Tool {
52
+ const tool = BUILTIN_TOOLS[name](legacyToolSession(cwd));
53
+ if (tool instanceof Promise) {
54
+ throw new Error(`Built-in ${name} tool factory unexpectedly returned a promise.`);
55
+ }
56
+ if (!tool) {
57
+ throw new Error(`Built-in ${name} tool is unavailable.`);
58
+ }
59
+ return tool;
60
+ }
61
+
62
+ async function executeBuiltinTool(
63
+ cwd: string,
64
+ name: LegacyCodingToolName,
65
+ toolCallId: string,
66
+ params: unknown,
67
+ signal: AbortSignal | undefined,
68
+ onUpdate: AgentToolUpdateCallback | undefined,
69
+ ) {
70
+ const tool = createBuiltinTool(cwd, name);
71
+ return tool.execute(toolCallId, params, signal, onUpdate);
72
+ }
73
+
74
+ function legacyBuiltinTool(cwd: string, name: LegacyCodingToolName): ToolDefinition {
75
+ const tool = createBuiltinTool(cwd, name);
76
+ const definition: LegacyBuiltinToolDefinition = {
77
+ name: tool.name,
78
+ label: tool.label,
79
+ description: tool.description,
80
+ parameters: tool.parameters,
81
+ hidden: tool.hidden,
82
+ deferrable: tool.deferrable,
83
+ approval: tool.approval,
84
+ execute: (toolCallId, params, signal, onUpdate) =>
85
+ executeBuiltinTool(cwd, name, toolCallId, params, signal, onUpdate),
86
+ [LEGACY_BUILTIN_TOOL_MARKER]: true,
87
+ };
88
+ return markToolDefinition(definition);
89
+ }
90
+
91
+ export interface ParsedFrontmatter<T extends Record<string, unknown> = Record<string, unknown>> {
92
+ frontmatter: T;
93
+ body: string;
94
+ }
95
+
96
+ export function parseFrontmatter<T extends Record<string, unknown> = Record<string, unknown>>(
97
+ content: string,
98
+ ): ParsedFrontmatter<T> {
99
+ const { frontmatter, body } = parseOmpFrontmatter(content, { level: "fatal" });
100
+ return { frontmatter: frontmatter as T, body };
101
+ }
102
+
103
+ export function stripFrontmatter(content: string): string {
104
+ return parseFrontmatter(content).body;
105
+ }
106
+
107
+ export function defineTool<TParams extends TSchema = TSchema, TDetails = unknown>(
108
+ tool: ToolDefinition<TParams, TDetails>,
109
+ ): ToolDefinition<TParams, TDetails> {
110
+ return markToolDefinition(tool);
111
+ }
112
+
113
+ export function createCodingTools(cwd: string): ToolDefinition[] {
114
+ return LEGACY_CODING_TOOL_NAMES.map(name => legacyBuiltinTool(cwd, name));
115
+ }
116
+
117
+ export const SettingsManager = {
118
+ create(cwd: string, agentDir?: string): Promise<Settings> {
119
+ return Settings.init({ cwd, agentDir });
120
+ },
121
+
122
+ inMemory(): Settings {
123
+ return Settings.isolated();
124
+ },
125
+ } as const;
126
+
15
127
  export * from "../index";
128
+ export { Type };