@oh-my-pi/pi-coding-agent 15.0.1 → 15.1.0

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 (168) hide show
  1. package/CHANGELOG.md +94 -1
  2. package/examples/custom-tools/README.md +11 -7
  3. package/examples/custom-tools/hello/index.ts +2 -2
  4. package/examples/extensions/README.md +19 -8
  5. package/examples/extensions/api-demo.ts +15 -19
  6. package/examples/extensions/hello.ts +5 -6
  7. package/examples/extensions/plan-mode.ts +1 -1
  8. package/examples/extensions/reload-runtime.ts +4 -3
  9. package/examples/extensions/with-deps/index.ts +4 -3
  10. package/examples/sdk/06-extensions.ts +4 -2
  11. package/package.json +8 -18
  12. package/src/autoresearch/tools/init-experiment.ts +38 -41
  13. package/src/autoresearch/tools/log-experiment.ts +32 -41
  14. package/src/autoresearch/tools/run-experiment.ts +3 -3
  15. package/src/autoresearch/tools/update-notes.ts +11 -11
  16. package/src/commands/commit.ts +10 -0
  17. package/src/commit/agentic/tools/analyze-file.ts +4 -4
  18. package/src/commit/agentic/tools/git-file-diff.ts +4 -4
  19. package/src/commit/agentic/tools/git-hunk.ts +5 -5
  20. package/src/commit/agentic/tools/git-overview.ts +4 -4
  21. package/src/commit/agentic/tools/propose-changelog.ts +13 -13
  22. package/src/commit/agentic/tools/propose-commit.ts +6 -6
  23. package/src/commit/agentic/tools/recent-commits.ts +3 -3
  24. package/src/commit/agentic/tools/schemas.ts +28 -28
  25. package/src/commit/agentic/tools/split-commit.ts +22 -21
  26. package/src/commit/analysis/summary.ts +4 -4
  27. package/src/commit/changelog/generate.ts +7 -11
  28. package/src/commit/shared-llm.ts +22 -34
  29. package/src/config/config-file.ts +35 -13
  30. package/src/config/model-registry.ts +40 -191
  31. package/src/config/models-config-schema.ts +166 -0
  32. package/src/config/settings-schema.ts +29 -0
  33. package/src/discovery/claude-plugins.ts +19 -7
  34. package/src/edit/index.ts +2 -2
  35. package/src/edit/modes/apply-patch.ts +7 -6
  36. package/src/edit/modes/patch.ts +18 -25
  37. package/src/edit/modes/replace.ts +18 -20
  38. package/src/eval/js/shared/rewrite-imports.ts +131 -10
  39. package/src/eval/py/executor.ts +233 -623
  40. package/src/eval/py/kernel.ts +27 -2
  41. package/src/eval/py/runner.py +42 -11
  42. package/src/eval/py/runtime.ts +1 -0
  43. package/src/exa/factory.ts +5 -4
  44. package/src/exa/mcp-client.ts +1 -1
  45. package/src/exa/researcher.ts +9 -20
  46. package/src/exa/search.ts +26 -52
  47. package/src/exa/types.ts +1 -1
  48. package/src/exa/websets.ts +54 -53
  49. package/src/exec/bash-executor.ts +2 -1
  50. package/src/extensibility/custom-commands/loader.ts +5 -3
  51. package/src/extensibility/custom-commands/types.ts +4 -2
  52. package/src/extensibility/custom-tools/loader.ts +5 -3
  53. package/src/extensibility/custom-tools/types.ts +7 -6
  54. package/src/extensibility/custom-tools/wrapper.ts +1 -1
  55. package/src/extensibility/extensions/get-commands-handler.ts +77 -0
  56. package/src/extensibility/extensions/loader.ts +7 -3
  57. package/src/extensibility/extensions/types.ts +9 -5
  58. package/src/extensibility/extensions/wrapper.ts +1 -2
  59. package/src/extensibility/hooks/loader.ts +3 -1
  60. package/src/extensibility/hooks/tool-wrapper.ts +1 -1
  61. package/src/extensibility/hooks/types.ts +4 -2
  62. package/src/extensibility/plugins/legacy-pi-compat.ts +78 -31
  63. package/src/extensibility/shared-events.ts +1 -1
  64. package/src/extensibility/typebox.ts +391 -0
  65. package/src/goals/tools/goal-tool.ts +6 -12
  66. package/src/hashline/input.ts +2 -1
  67. package/src/hashline/parser.ts +27 -3
  68. package/src/hashline/types.ts +4 -4
  69. package/src/hindsight/state.ts +2 -2
  70. package/src/index.ts +0 -2
  71. package/src/internal-urls/docs-index.generated.ts +15 -15
  72. package/src/internal-urls/router.ts +8 -0
  73. package/src/internal-urls/types.ts +21 -0
  74. package/src/lsp/config.ts +15 -6
  75. package/src/lsp/defaults.json +6 -2
  76. package/src/lsp/types.ts +30 -38
  77. package/src/mcp/manager.ts +1 -1
  78. package/src/mcp/tool-bridge.ts +1 -1
  79. package/src/modes/acp/acp-agent.ts +248 -50
  80. package/src/modes/components/session-observer-overlay.ts +12 -1
  81. package/src/modes/components/status-line/segments.ts +39 -4
  82. package/src/modes/controllers/command-controller.ts +27 -2
  83. package/src/modes/controllers/event-controller.ts +3 -4
  84. package/src/modes/controllers/extension-ui-controller.ts +3 -2
  85. package/src/modes/interactive-mode.ts +1 -1
  86. package/src/modes/rpc/host-tools.ts +1 -1
  87. package/src/modes/rpc/host-uris.ts +235 -0
  88. package/src/modes/rpc/rpc-client.ts +1 -1
  89. package/src/modes/rpc/rpc-mode.ts +27 -1
  90. package/src/modes/rpc/rpc-types.ts +58 -1
  91. package/src/modes/runtime-init.ts +2 -1
  92. package/src/modes/theme/defaults/dark-poimandres.json +1 -0
  93. package/src/modes/theme/defaults/light-poimandres.json +1 -0
  94. package/src/modes/theme/theme.ts +117 -117
  95. package/src/modes/types.ts +1 -1
  96. package/src/modes/utils/context-usage.ts +2 -2
  97. package/src/prompts/tools/github.md +4 -4
  98. package/src/prompts/tools/hashline.md +22 -26
  99. package/src/prompts/tools/read.md +55 -37
  100. package/src/sdk.ts +31 -8
  101. package/src/session/agent-session.ts +74 -104
  102. package/src/session/messages.ts +16 -51
  103. package/src/session/session-manager.ts +22 -2
  104. package/src/session/streaming-output.ts +16 -6
  105. package/src/task/discovery.ts +5 -2
  106. package/src/task/executor.ts +210 -87
  107. package/src/task/index.ts +15 -11
  108. package/src/task/render.ts +32 -5
  109. package/src/task/types.ts +54 -39
  110. package/src/tools/ask.ts +12 -12
  111. package/src/tools/ast-edit.ts +11 -15
  112. package/src/tools/ast-grep.ts +9 -10
  113. package/src/tools/bash-command-fixup.ts +47 -0
  114. package/src/tools/bash.ts +48 -38
  115. package/src/tools/browser/render.ts +2 -2
  116. package/src/tools/browser.ts +39 -53
  117. package/src/tools/calculator.ts +12 -11
  118. package/src/tools/checkpoint.ts +7 -7
  119. package/src/tools/debug.ts +40 -43
  120. package/src/tools/eval.ts +16 -10
  121. package/src/tools/find.ts +10 -13
  122. package/src/tools/gh.ts +108 -132
  123. package/src/tools/hindsight-recall.ts +4 -6
  124. package/src/tools/hindsight-reflect.ts +5 -5
  125. package/src/tools/hindsight-retain.ts +15 -17
  126. package/src/tools/image-gen.ts +31 -81
  127. package/src/tools/index.ts +4 -1
  128. package/src/tools/inspect-image.ts +8 -9
  129. package/src/tools/irc.ts +15 -27
  130. package/src/tools/job.ts +30 -28
  131. package/src/tools/output-meta.ts +26 -0
  132. package/src/tools/read.ts +39 -12
  133. package/src/tools/recipe/index.ts +7 -9
  134. package/src/tools/render-mermaid.ts +12 -12
  135. package/src/tools/report-tool-issue.ts +4 -4
  136. package/src/tools/resolve.ts +11 -11
  137. package/src/tools/review.ts +14 -26
  138. package/src/tools/search-tool-bm25.ts +7 -9
  139. package/src/tools/search.ts +19 -22
  140. package/src/tools/ssh.ts +10 -9
  141. package/src/tools/todo-write.ts +26 -34
  142. package/src/tools/vim.ts +10 -26
  143. package/src/tools/write.ts +25 -5
  144. package/src/tools/yield.ts +100 -54
  145. package/src/web/search/index.ts +9 -24
  146. package/src/web/search/providers/anthropic.ts +5 -0
  147. package/src/web/search/providers/exa.ts +3 -0
  148. package/src/web/search/providers/gemini.ts +5 -0
  149. package/src/web/search/providers/jina.ts +5 -2
  150. package/src/web/search/providers/zai.ts +5 -2
  151. package/src/prompts/compaction/branch-summary-context.md +0 -5
  152. package/src/prompts/compaction/branch-summary-preamble.md +0 -2
  153. package/src/prompts/compaction/branch-summary.md +0 -30
  154. package/src/prompts/compaction/compaction-short-summary.md +0 -9
  155. package/src/prompts/compaction/compaction-summary-context.md +0 -5
  156. package/src/prompts/compaction/compaction-summary.md +0 -38
  157. package/src/prompts/compaction/compaction-turn-prefix.md +0 -17
  158. package/src/prompts/compaction/compaction-update-summary.md +0 -45
  159. package/src/prompts/system/auto-handoff-threshold-focus.md +0 -1
  160. package/src/prompts/system/file-operations.md +0 -10
  161. package/src/prompts/system/handoff-document.md +0 -49
  162. package/src/prompts/system/summarization-system.md +0 -3
  163. package/src/session/compaction/branch-summarization.ts +0 -324
  164. package/src/session/compaction/compaction.ts +0 -1420
  165. package/src/session/compaction/errors.ts +0 -31
  166. package/src/session/compaction/index.ts +0 -8
  167. package/src/session/compaction/pruning.ts +0 -91
  168. package/src/session/compaction/utils.ts +0 -184
package/src/tools/gh.ts CHANGED
@@ -3,9 +3,9 @@ import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import { scheduler } from "node:timers/promises";
5
5
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
6
- import { StringEnum } from "@oh-my-pi/pi-ai";
6
+
7
7
  import { getWorktreesDir, isEnoent, prompt, untilAborted } from "@oh-my-pi/pi-utils";
8
- import { type Static, Type } from "@sinclair/typebox";
8
+ import * as z from "zod/v4";
9
9
  import type { Settings } from "../config/settings";
10
10
  import githubDescription from "../prompts/tools/github.md" with { type: "text" };
11
11
  import * as git from "../utils/git";
@@ -197,135 +197,78 @@ const RUN_SUCCESS_CONCLUSIONS = new Set(["success", "neutral", "skipped"]);
197
197
  const RUN_FAILURE_CONCLUSIONS = new Set(["failure", "timed_out", "cancelled", "action_required", "startup_failure"]);
198
198
  const JOB_FAILURE_CONCLUSIONS = new Set(["failure", "timed_out", "cancelled", "action_required"]);
199
199
 
200
- const githubSchema = Type.Object({
201
- op: StringEnum(
202
- [
203
- "repo_view",
204
- "pr_create",
205
- "pr_checkout",
206
- "pr_push",
207
- "search_issues",
208
- "search_prs",
209
- "search_code",
210
- "search_commits",
211
- "search_repos",
212
- "run_watch",
213
- ],
214
- { description: "github operation" },
215
- ),
216
- repo: Type.Optional(
217
- Type.String({
218
- description: "owner/repo (any op)",
219
- examples: ["facebook/react"],
220
- }),
221
- ),
222
- branch: Type.Optional(
223
- Type.String({
224
- description: "branch (repo_view, pr_push local branch, run_watch)",
225
- examples: ["main", "develop"],
226
- }),
227
- ),
228
- pr: Type.Optional(
229
- Type.Union(
230
- [
231
- Type.String({ examples: ["123", "feature-branch"] }),
232
- Type.Array(Type.String(), {
233
- examples: [["123", "456"]],
234
- }),
235
- ],
236
- {
237
- description:
238
- "pr number, url, or branch (pr_checkout); pass an array to batch-process multiple pull requests in one call",
239
- },
240
- ),
241
- ),
242
- force: Type.Optional(Type.Boolean({ description: "reset existing local branch (pr_checkout)" })),
243
- forceWithLease: Type.Optional(Type.Boolean({ description: "force-with-lease push (pr_push)" })),
244
- title: Type.Optional(
245
- Type.String({
246
- description: "PR title (pr_create)",
247
- examples: ["Fix login bug"],
248
- }),
249
- ),
250
- body: Type.Optional(
251
- Type.String({
252
- description: "PR body markdown (pr_create); mutually exclusive with fill",
253
- }),
254
- ),
255
- base: Type.Optional(
256
- Type.String({
257
- description: "PR base branch (pr_create); defaults to repo default branch",
258
- examples: ["main"],
259
- }),
260
- ),
261
- head: Type.Optional(
262
- Type.String({
263
- description: "PR head branch (pr_create); defaults to current branch",
264
- examples: ["feature/foo"],
265
- }),
266
- ),
267
- draft: Type.Optional(Type.Boolean({ description: "open PR as draft (pr_create)" })),
268
- fill: Type.Optional(
269
- Type.Boolean({
270
- description: "auto-fill PR title/body from commits (pr_create); mutually exclusive with title/body",
271
- }),
272
- ),
273
- reviewer: Type.Optional(
274
- Type.Array(Type.String(), {
275
- description: "reviewers to request (pr_create); accepts users or org/team",
276
- examples: [["octocat", "myorg/team"]],
277
- }),
278
- ),
279
- assignee: Type.Optional(
280
- Type.Array(Type.String(), {
281
- description: "assignees (pr_create); use @me for the authenticated user",
282
- examples: [["@me"]],
283
- }),
284
- ),
285
- label: Type.Optional(
286
- Type.Array(Type.String(), {
287
- description: "labels to apply (pr_create)",
288
- examples: [["bug", "enhancement"]],
289
- }),
290
- ),
291
- query: Type.Optional(
292
- Type.String({
293
- description: "search query (search_issues, search_prs, search_code, search_commits, search_repos)",
294
- examples: ["is:open label:bug"],
295
- }),
296
- ),
297
- since: Type.Optional(
298
- Type.String({
299
- description:
200
+ const githubSchema = z
201
+ .object({
202
+ op: z
203
+ .enum([
204
+ "repo_view",
205
+ "pr_create",
206
+ "pr_checkout",
207
+ "pr_push",
208
+ "search_issues",
209
+ "search_prs",
210
+ "search_code",
211
+ "search_commits",
212
+ "search_repos",
213
+ "run_watch",
214
+ ] as const)
215
+ .describe("github operation"),
216
+ repo: z.string().describe("owner/repo (any op)").optional(),
217
+ branch: z.string().describe("branch (repo_view, pr_push local branch, run_watch)").optional(),
218
+ pr: z
219
+ .union([z.string(), z.array(z.string())])
220
+ .describe(
221
+ "pr number, url, or branch (pr_checkout); pass an array to batch-process multiple pull requests in one call",
222
+ )
223
+ .optional(),
224
+ force: z.boolean().describe("reset existing local branch (pr_checkout)").optional(),
225
+ forceWithLease: z.boolean().describe("force-with-lease push (pr_push)").optional(),
226
+ title: z.string().describe("PR title (pr_create)").optional(),
227
+ body: z.string().describe("PR body markdown (pr_create); mutually exclusive with fill").optional(),
228
+ base: z.string().describe("PR base branch (pr_create); defaults to repo default branch").optional(),
229
+ head: z.string().describe("PR head branch (pr_create); defaults to current branch").optional(),
230
+ draft: z.boolean().describe("open PR as draft (pr_create)").optional(),
231
+ fill: z
232
+ .boolean()
233
+ .describe("auto-fill PR title/body from commits (pr_create); mutually exclusive with title/body")
234
+ .optional(),
235
+ reviewer: z.array(z.string()).describe("reviewers to request (pr_create); accepts users or org/team").optional(),
236
+ assignee: z.array(z.string()).describe("assignees (pr_create); use @me for the authenticated user").optional(),
237
+ label: z.array(z.string()).describe("labels to apply (pr_create)").optional(),
238
+ query: z
239
+ .string()
240
+ .describe("search query (search_issues, search_prs, search_code, search_commits, search_repos)")
241
+ .optional(),
242
+ since: z
243
+ .string()
244
+ .describe(
300
245
  "lower-bound date for search_issues/search_prs/search_commits/search_repos. Accepts a relative duration (`<n><unit>` with unit `m`/`h`/`d`/`w`/`mo`/`y`, e.g. `3d`, `12h`, `2w`) or an ISO date (`YYYY-MM-DD`) / datetime. Translated to a `created:>=…` (or `committer-date:`/`pushed:`) qualifier; not supported by search_code.",
301
- examples: ["3d", "2w", "2026-05-01"],
302
- }),
303
- ),
304
- until: Type.Optional(
305
- Type.String({
306
- description:
246
+ )
247
+ .optional(),
248
+ until: z
249
+ .string()
250
+ .describe(
307
251
  "upper-bound date in the same format as `since`. With both, builds a `field:since..until` range qualifier.",
308
- examples: ["1d", "2026-05-09"],
309
- }),
310
- ),
311
- dateField: Type.Optional(
312
- StringEnum(["created", "updated"], {
313
- description:
252
+ )
253
+ .optional(),
254
+ dateField: z
255
+ .enum(["created", "updated"] as const)
256
+ .describe(
314
257
  "date field used by `since`/`until`. issues/prs: `created` (default) or `updated`. repos: `created` (default) or `updated` (mapped to GitHub's `pushed:`). commits: ignored — always uses `committer-date`.",
315
- default: "created",
316
- }),
317
- ),
318
- limit: Type.Optional(
319
- Type.Number({
320
- description: "max results (search_issues, search_prs, search_code, search_commits, search_repos)",
321
- default: 10,
322
- }),
323
- ),
324
- run: Type.Optional(Type.String({ description: "actions run id or url (run_watch)", examples: ["123456"] })),
325
- tail: Type.Optional(Type.Number({ description: "log lines per failed job (run_watch)", default: 15 })),
326
- });
327
-
328
- type GithubInput = Static<typeof githubSchema>;
258
+ )
259
+ .default("created")
260
+ .optional(),
261
+ limit: z
262
+ .number()
263
+ .default(10)
264
+ .describe("max results (search_issues, search_prs, search_code, search_commits, search_repos)")
265
+ .optional(),
266
+ run: z.string().describe("actions run id or url (run_watch)").optional(),
267
+ tail: z.number().default(15).describe("log lines per failed job (run_watch)").optional(),
268
+ })
269
+ .strict();
270
+
271
+ type GithubInput = z.infer<typeof githubSchema>;
329
272
 
330
273
  export interface GhToolDetails {
331
274
  meta?: OutputMeta;
@@ -1774,6 +1717,39 @@ export async function resolveDefaultRepoMemoized(cwd: string, signal?: AbortSign
1774
1717
  return untilAborted(signal, pending);
1775
1718
  }
1776
1719
 
1720
+ /**
1721
+ * Matches search-query qualifiers that already scope to a repository, org, or
1722
+ * user. When present, callers should avoid layering a default `repo:<current>`
1723
+ * on top — the user has already expressed an explicit scope.
1724
+ *
1725
+ * Only the leading `repo:`/`org:`/`user:`/`owner:` token is treated as a
1726
+ * scope marker; arbitrary substrings (e.g. inside quoted text) are ignored.
1727
+ */
1728
+ const REPO_SCOPE_QUALIFIER_PATTERN = /(?:^|\s)-?(?:repo|org|user|owner):\S/i;
1729
+
1730
+ /**
1731
+ * Resolve the effective `repo:` scope for a search op. Returns the explicit
1732
+ * `repo` when set, `undefined` when the query already carries a scoping
1733
+ * qualifier, and otherwise the current checkout's `owner/repo` via
1734
+ * `resolveDefaultRepoMemoized`. Resolution failures (no git/gh context, no
1735
+ * configured remote) silently fall back to `undefined` so the search proceeds
1736
+ * across all of GitHub instead of throwing.
1737
+ */
1738
+ async function resolveSearchRepoScope(
1739
+ cwd: string,
1740
+ repo: string | undefined,
1741
+ query: string | undefined,
1742
+ signal: AbortSignal | undefined,
1743
+ ): Promise<string | undefined> {
1744
+ if (repo) return repo;
1745
+ if (query && REPO_SCOPE_QUALIFIER_PATTERN.test(query)) return undefined;
1746
+ try {
1747
+ return await resolveDefaultRepoMemoized(cwd, signal);
1748
+ } catch {
1749
+ return undefined;
1750
+ }
1751
+ }
1752
+
1777
1753
  async function resolveGitHubBranchHead(
1778
1754
  cwd: string,
1779
1755
  repo: string,
@@ -3267,11 +3243,11 @@ async function executeSearchIssues(
3267
3243
  params: GithubInput,
3268
3244
  signal: AbortSignal | undefined,
3269
3245
  ): Promise<AgentToolResult<GhToolDetails>> {
3270
- const repo = normalizeOptionalString(params.repo);
3271
3246
  const limit = resolveSearchLimit(params.limit);
3272
3247
  const dateField = resolveSearchDateField("issues", params.dateField);
3273
3248
  const dateQualifier = buildSearchDateQualifier(dateField, params.since, params.until);
3274
3249
  const displayQuery = composeSearchQuery([params.query, dateQualifier]);
3250
+ const repo = await resolveSearchRepoScope(session.cwd, normalizeOptionalString(params.repo), displayQuery, signal);
3275
3251
  const apiQuery = composeSearchQuery([displayQuery, repo ? `repo:${repo}` : undefined, "is:issue"]);
3276
3252
  const args = buildGhApiSearchArgs("issues", apiQuery, limit);
3277
3253
 
@@ -3285,11 +3261,11 @@ async function executeSearchPrs(
3285
3261
  params: GithubInput,
3286
3262
  signal: AbortSignal | undefined,
3287
3263
  ): Promise<AgentToolResult<GhToolDetails>> {
3288
- const repo = normalizeOptionalString(params.repo);
3289
3264
  const limit = resolveSearchLimit(params.limit);
3290
3265
  const dateField = resolveSearchDateField("prs", params.dateField);
3291
3266
  const dateQualifier = buildSearchDateQualifier(dateField, params.since, params.until);
3292
3267
  const displayQuery = composeSearchQuery([params.query, dateQualifier]);
3268
+ const repo = await resolveSearchRepoScope(session.cwd, normalizeOptionalString(params.repo), displayQuery, signal);
3293
3269
  const apiQuery = composeSearchQuery([displayQuery, repo ? `repo:${repo}` : undefined, "is:pr"]);
3294
3270
  const args = buildGhApiSearchArgs("issues", apiQuery, limit);
3295
3271
 
@@ -3307,8 +3283,8 @@ async function executeSearchCode(
3307
3283
  if (params.since !== undefined || params.until !== undefined) {
3308
3284
  throw new ToolError("search_code does not support since/until; GitHub code search has no date qualifier.");
3309
3285
  }
3310
- const repo = normalizeOptionalString(params.repo);
3311
3286
  const limit = resolveSearchLimit(params.limit);
3287
+ const repo = await resolveSearchRepoScope(session.cwd, normalizeOptionalString(params.repo), query, signal);
3312
3288
  const apiQuery = composeSearchQuery([query, repo ? `repo:${repo}` : undefined]);
3313
3289
  const args = buildGhApiSearchArgs("code", apiQuery, limit, ["Accept: application/vnd.github.text-match+json"]);
3314
3290
 
@@ -3322,11 +3298,11 @@ async function executeSearchCommits(
3322
3298
  params: GithubInput,
3323
3299
  signal: AbortSignal | undefined,
3324
3300
  ): Promise<AgentToolResult<GhToolDetails>> {
3325
- const repo = normalizeOptionalString(params.repo);
3326
3301
  const limit = resolveSearchLimit(params.limit);
3327
3302
  const dateField = resolveSearchDateField("commits", params.dateField);
3328
3303
  const dateQualifier = buildSearchDateQualifier(dateField, params.since, params.until);
3329
3304
  const displayQuery = composeSearchQuery([params.query, dateQualifier]);
3305
+ const repo = await resolveSearchRepoScope(session.cwd, normalizeOptionalString(params.repo), displayQuery, signal);
3330
3306
  const apiQuery = composeSearchQuery([displayQuery, repo ? `repo:${repo}` : undefined]);
3331
3307
  const args = buildGhApiSearchArgs("commits", apiQuery, limit);
3332
3308
 
@@ -1,17 +1,15 @@
1
1
  import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
2
2
  import { logger, untilAborted } from "@oh-my-pi/pi-utils";
3
- import { type Static, Type } from "@sinclair/typebox";
3
+ import * as z from "zod/v4";
4
4
  import { formatCurrentTime, formatMemories } from "../hindsight/content";
5
5
  import recallDescription from "../prompts/tools/recall.md" with { type: "text" };
6
6
  import type { ToolSession } from ".";
7
7
 
8
- const hindsightRecallSchema = Type.Object({
9
- query: Type.String({
10
- description: "Natural language search query. Be specific about what you need to know.",
11
- }),
8
+ const hindsightRecallSchema = z.object({
9
+ query: z.string().describe("Natural language search query. Be specific about what you need to know."),
12
10
  });
13
11
 
14
- export type HindsightRecallParams = Static<typeof hindsightRecallSchema>;
12
+ export type HindsightRecallParams = z.infer<typeof hindsightRecallSchema>;
15
13
 
16
14
  export class HindsightRecallTool implements AgentTool<typeof hindsightRecallSchema> {
17
15
  readonly name = "recall";
@@ -1,16 +1,16 @@
1
1
  import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
2
2
  import { logger, untilAborted } from "@oh-my-pi/pi-utils";
3
- import { type Static, Type } from "@sinclair/typebox";
3
+ import * as z from "zod/v4";
4
4
  import { ensureBankMission } from "../hindsight/bank";
5
5
  import reflectDescription from "../prompts/tools/reflect.md" with { type: "text" };
6
6
  import type { ToolSession } from ".";
7
7
 
8
- const hindsightReflectSchema = Type.Object({
9
- query: Type.String({ description: "The question to answer using long-term memory." }),
10
- context: Type.Optional(Type.String({ description: "Optional additional context to guide the reflection." })),
8
+ const hindsightReflectSchema = z.object({
9
+ query: z.string().describe("The question to answer using long-term memory."),
10
+ context: z.string().describe("Optional additional context to guide the reflection.").optional(),
11
11
  });
12
12
 
13
- export type HindsightReflectParams = Static<typeof hindsightReflectSchema>;
13
+ export type HindsightReflectParams = z.infer<typeof hindsightReflectSchema>;
14
14
 
15
15
  export class HindsightReflectTool implements AgentTool<typeof hindsightReflectSchema> {
16
16
  readonly name = "reflect";
@@ -1,27 +1,25 @@
1
1
  import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
2
- import { type Static, Type } from "@sinclair/typebox";
2
+ import * as z from "zod/v4";
3
3
  import retainDescription from "../prompts/tools/retain.md" with { type: "text" };
4
4
  import type { ToolSession } from ".";
5
5
 
6
- const hindsightRetainSchema = Type.Object({
7
- items: Type.Array(
8
- Type.Object({
9
- content: Type.String({
10
- description: "The information to remember. Be specific and self-contained — include who, what, when, why.",
6
+ const hindsightRetainSchema = z.object({
7
+ items: z
8
+ .array(
9
+ z.object({
10
+ content: z
11
+ .string()
12
+ .describe("The information to remember. Be specific and self-contained — include who, what, when, why."),
13
+ context: z.string().describe("Optional context describing where this information came from.").optional(),
11
14
  }),
12
- context: Type.Optional(
13
- Type.String({ description: "Optional context describing where this information came from." }),
14
- ),
15
- }),
16
- {
17
- minItems: 1,
18
- description:
19
- "One or more memories to retain. Batch related facts in a single call rather than calling retain repeatedly — they are deduplicated and consolidated together.",
20
- },
21
- ),
15
+ )
16
+ .min(1)
17
+ .describe(
18
+ "One or more memories to retain. Batch related facts in a single call rather than calling retain repeatedly — they are deduplicated and consolidated together.",
19
+ ),
22
20
  });
23
21
 
24
- export type HindsightRetainParams = Static<typeof hindsightRetainSchema>;
22
+ export type HindsightRetainParams = z.infer<typeof hindsightRetainSchema>;
25
23
  export class HindsightRetainTool implements AgentTool<typeof hindsightRetainSchema> {
26
24
  readonly name = "retain";
27
25
  readonly label = "Retain";
@@ -1,6 +1,6 @@
1
1
  import * as os from "node:os";
2
2
  import * as path from "node:path";
3
- import { getAntigravityUserAgent, getEnvApiKey, type Model, StringEnum } from "@oh-my-pi/pi-ai";
3
+ import { getAntigravityUserAgent, getEnvApiKey, type Model } from "@oh-my-pi/pi-ai";
4
4
  import {
5
5
  CODEX_BASE_URL,
6
6
  getCodexAccountId,
@@ -18,7 +18,7 @@ import {
18
18
  Snowflake,
19
19
  untilAborted,
20
20
  } from "@oh-my-pi/pi-utils";
21
- import { type Static, Type } from "@sinclair/typebox";
21
+ import * as z from "zod/v4";
22
22
  import packageJson from "../../package.json" with { type: "json" };
23
23
  import { isAuthenticated, type ModelRegistry } from "../config/model-registry";
24
24
  import type { CustomTool } from "../extensibility/custom-tools/types";
@@ -46,87 +46,37 @@ interface ImageApiKey {
46
46
  model?: Model;
47
47
  }
48
48
 
49
- const responseModalitySchema = StringEnum(["IMAGE", "TEXT"]);
50
- const aspectRatioSchema = StringEnum(["1:1", "3:4", "4:3", "9:16", "16:9"], {
51
- description: "aspect ratio",
52
- examples: ["1:1", "3:4", "16:9"],
53
- });
54
- const imageSizeSchema = StringEnum(["1024x1024", "1536x1024", "1024x1536"], {
55
- description: "image size",
56
- examples: ["1024x1024", "1536x1024"],
57
- });
58
-
59
- const inputImageSchema = Type.Object(
60
- {
61
- path: Type.Optional(Type.String({ description: "input image path" })),
62
- data: Type.Optional(Type.String({ description: "base64 image data" })),
63
- mime_type: Type.Optional(Type.String({ description: "mime type" })),
64
- },
65
- { additionalProperties: false },
66
- );
67
-
68
- const baseImageSchema = Type.Object(
69
- {
70
- subject: Type.String({
71
- description: "main subject",
72
- examples: ["a stoic robot barista", "a weathered lighthouse"],
73
- }),
74
- action: Type.Optional(
75
- Type.String({
76
- description: "what subject is doing",
77
- examples: ["pouring latte art", "standing against waves"],
78
- }),
79
- ),
80
- scene: Type.Optional(
81
- Type.String({
82
- description: "location or environment",
83
- examples: ["futuristic café on mars", "thunderstorm at dusk"],
84
- }),
85
- ),
86
- composition: Type.Optional(
87
- Type.String({
88
- description: "camera angle and framing",
89
- examples: ["low-angle close-up", "wide establishing shot"],
90
- }),
91
- ),
92
- lighting: Type.Optional(
93
- Type.String({
94
- description: "lighting setup",
95
- examples: ["warm rim lighting", "golden hour backlight"],
96
- }),
97
- ),
98
- style: Type.Optional(
99
- Type.String({
100
- description: "artistic style",
101
- examples: ["film noir", "studio ghibli watercolor", "photorealistic"],
102
- }),
103
- ),
104
- text: Type.Optional(
105
- Type.String({
106
- description: "text to render",
107
- examples: ["headline 'urban explorer' top center"],
108
- }),
109
- ),
110
- changes: Type.Optional(
111
- Type.Array(Type.String(), {
112
- description: "edits to make",
113
- examples: [["change tie to green", "remove car"]],
114
- }),
115
- ),
116
- aspect_ratio: Type.Optional(aspectRatioSchema),
117
- image_size: Type.Optional(imageSizeSchema),
118
- input: Type.Optional(
119
- Type.Array(inputImageSchema, {
120
- description: "input images",
121
- }),
122
- ),
123
- },
124
- { additionalProperties: false },
125
- );
49
+ const responseModalitySchema = z.enum(["IMAGE", "TEXT"] as const);
50
+ const aspectRatioSchema = z.enum(["1:1", "3:4", "4:3", "9:16", "16:9"] as const).describe("aspect ratio");
51
+ const imageSizeSchema = z.enum(["1024x1024", "1536x1024", "1024x1536"] as const).describe("image size");
52
+
53
+ const inputImageSchema = z
54
+ .object({
55
+ path: z.string().describe("input image path").optional(),
56
+ data: z.string().describe("base64 image data").optional(),
57
+ mime_type: z.string().describe("mime type").optional(),
58
+ })
59
+ .strict();
60
+
61
+ const baseImageSchema = z
62
+ .object({
63
+ subject: z.string().describe("main subject"),
64
+ action: z.string().describe("what subject is doing").optional(),
65
+ scene: z.string().describe("location or environment").optional(),
66
+ composition: z.string().describe("camera angle and framing").optional(),
67
+ lighting: z.string().describe("lighting setup").optional(),
68
+ style: z.string().describe("artistic style").optional(),
69
+ text: z.string().describe("text to render").optional(),
70
+ changes: z.array(z.string()).describe("edits to make").optional(),
71
+ aspect_ratio: aspectRatioSchema.optional(),
72
+ image_size: imageSizeSchema.optional(),
73
+ input: z.array(inputImageSchema).describe("input images").optional(),
74
+ })
75
+ .strict();
126
76
 
127
77
  export const imageGenSchema = baseImageSchema;
128
- export type ImageGenParams = Static<typeof imageGenSchema>;
129
- export type GeminiResponseModality = Static<typeof responseModalitySchema>;
78
+ export type ImageGenParams = z.infer<typeof imageGenSchema>;
79
+ export type GeminiResponseModality = z.infer<typeof responseModalitySchema>;
130
80
 
131
81
  /**
132
82
  * Assembles a structured prompt from the provided parameters.
@@ -1,4 +1,4 @@
1
- import type { AgentTool } from "@oh-my-pi/pi-agent-core";
1
+ import type { AgentTelemetryConfig, AgentTool } from "@oh-my-pi/pi-agent-core";
2
2
  import type { ToolChoice } from "@oh-my-pi/pi-ai";
3
3
  import { $env, $flag, logger } from "@oh-my-pi/pi-utils";
4
4
  import type { PromptTemplate } from "../config/prompt-templates";
@@ -250,6 +250,9 @@ export interface ToolSession {
250
250
 
251
251
  /** Queue a hidden message to be injected at the next agent turn. */
252
252
  queueDeferredMessage?(message: CustomMessage): void;
253
+ /** Get the active OpenTelemetry config so subagent dispatch can forward
254
+ * the parent's tracer/hooks with the subagent's own identity stamped. */
255
+ getTelemetry?: () => AgentTelemetryConfig | undefined;
253
256
  }
254
257
 
255
258
  export type ToolFactory = (session: ToolSession) => Tool | null | Promise<Tool | null>;
@@ -1,7 +1,7 @@
1
1
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
2
  import { type Api, completeSimple, type Model } from "@oh-my-pi/pi-ai";
3
3
  import { prompt } from "@oh-my-pi/pi-utils";
4
- import { type Static, Type } from "@sinclair/typebox";
4
+ import * as z from "zod/v4";
5
5
  import { extractTextContent } from "../commit/utils";
6
6
  import { expandRoleAlias, resolveModelFromString } from "../config/model-resolver";
7
7
  import inspectImageDescription from "../prompts/tools/inspect-image.md" with { type: "text" };
@@ -15,15 +15,14 @@ import {
15
15
  import type { ToolSession } from "./index";
16
16
  import { ToolError } from "./tool-errors";
17
17
 
18
- const inspectImageSchema = Type.Object(
19
- {
20
- path: Type.String({ description: "image path", examples: ["image.png"] }),
21
- question: Type.String({ description: "question about image", examples: ["What is in this image?"] }),
22
- },
23
- { additionalProperties: false },
24
- );
18
+ const inspectImageSchema = z
19
+ .object({
20
+ path: z.string().describe("image path"),
21
+ question: z.string().describe("question about image"),
22
+ })
23
+ .strict();
25
24
 
26
- export type InspectImageParams = Static<typeof inspectImageSchema>;
25
+ export type InspectImageParams = z.infer<typeof inspectImageSchema>;
27
26
 
28
27
  export interface InspectImageToolDetails {
29
28
  model: string;
package/src/tools/irc.ts CHANGED
@@ -20,39 +20,27 @@
20
20
 
21
21
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
22
22
  import { prompt } from "@oh-my-pi/pi-utils";
23
- import { type Static, Type } from "@sinclair/typebox";
23
+ import * as z from "zod/v4";
24
24
  import ircDescription from "../prompts/tools/irc.md" with { type: "text" };
25
25
  import type { AgentRef, AgentRegistry } from "../registry/agent-registry";
26
26
  import type { ToolSession } from ".";
27
27
 
28
- const ircSchema = Type.Object({
29
- op: Type.Union(
30
- [
31
- Type.Literal("send", { description: "Send a message to one peer or to all peers" }),
32
- Type.Literal("list", { description: "List currently visible peers" }),
33
- ],
34
- { description: "IRC operation" },
35
- ),
36
- to: Type.Optional(
37
- Type.String({
38
- description: 'Recipient agent id (e.g. "0-Main", "0-AuthLoader") or "all" to broadcast',
39
- examples: ["0-Main", "all"],
40
- }),
41
- ),
42
- message: Type.Optional(
43
- Type.String({
44
- description: "Message body to deliver",
45
- examples: ["Should we use JWT or session cookies?"],
46
- }),
47
- ),
48
- awaitReply: Type.Optional(
49
- Type.Boolean({
50
- description: "Wait for the recipient's prose reply (default: true for DM, false for broadcast)",
51
- }),
52
- ),
28
+ const ircSchema = z.object({
29
+ op: z
30
+ .union([
31
+ z.literal("send").describe("Send a message to one peer or to all peers"),
32
+ z.literal("list").describe("List currently visible peers"),
33
+ ])
34
+ .describe("IRC operation"),
35
+ to: z.string().optional().describe('Recipient agent id (e.g. "0-Main", "0-AuthLoader") or "all" to broadcast'),
36
+ message: z.string().optional().describe("Message body to deliver"),
37
+ awaitReply: z
38
+ .boolean()
39
+ .optional()
40
+ .describe("Wait for the recipient's prose reply (default: true for DM, false for broadcast)"),
53
41
  });
54
42
 
55
- type IrcParams = Static<typeof ircSchema>;
43
+ type IrcParams = z.infer<typeof ircSchema>;
56
44
 
57
45
  interface IrcReply {
58
46
  from: string;