@oh-my-pi/pi-coding-agent 15.0.2 → 15.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/CHANGELOG.md +56 -1
  2. package/examples/custom-tools/README.md +11 -7
  3. package/examples/custom-tools/hello/index.ts +2 -2
  4. package/examples/extensions/README.md +19 -8
  5. package/examples/extensions/api-demo.ts +15 -19
  6. package/examples/extensions/hello.ts +5 -6
  7. package/examples/extensions/plan-mode.ts +1 -1
  8. package/examples/extensions/reload-runtime.ts +4 -3
  9. package/examples/extensions/with-deps/index.ts +4 -3
  10. package/examples/sdk/06-extensions.ts +4 -2
  11. package/package.json +7 -17
  12. package/src/autoresearch/tools/init-experiment.ts +38 -41
  13. package/src/autoresearch/tools/log-experiment.ts +32 -41
  14. package/src/autoresearch/tools/run-experiment.ts +3 -3
  15. package/src/autoresearch/tools/update-notes.ts +11 -11
  16. package/src/commit/agentic/tools/analyze-file.ts +4 -4
  17. package/src/commit/agentic/tools/git-file-diff.ts +4 -4
  18. package/src/commit/agentic/tools/git-hunk.ts +5 -5
  19. package/src/commit/agentic/tools/git-overview.ts +4 -4
  20. package/src/commit/agentic/tools/propose-changelog.ts +13 -13
  21. package/src/commit/agentic/tools/propose-commit.ts +6 -6
  22. package/src/commit/agentic/tools/recent-commits.ts +3 -3
  23. package/src/commit/agentic/tools/schemas.ts +28 -28
  24. package/src/commit/agentic/tools/split-commit.ts +22 -21
  25. package/src/commit/analysis/summary.ts +4 -4
  26. package/src/commit/changelog/generate.ts +7 -11
  27. package/src/commit/shared-llm.ts +22 -34
  28. package/src/config/config-file.ts +35 -13
  29. package/src/config/model-registry.ts +9 -190
  30. package/src/config/models-config-schema.ts +166 -0
  31. package/src/config/settings-schema.ts +18 -0
  32. package/src/edit/index.ts +2 -2
  33. package/src/edit/modes/apply-patch.ts +7 -6
  34. package/src/edit/modes/patch.ts +18 -25
  35. package/src/edit/modes/replace.ts +18 -20
  36. package/src/eval/js/shared/rewrite-imports.ts +131 -10
  37. package/src/eval/py/executor.ts +233 -623
  38. package/src/eval/py/kernel.ts +27 -2
  39. package/src/exa/factory.ts +5 -4
  40. package/src/exa/mcp-client.ts +1 -1
  41. package/src/exa/researcher.ts +9 -20
  42. package/src/exa/search.ts +26 -52
  43. package/src/exa/types.ts +1 -1
  44. package/src/exa/websets.ts +54 -53
  45. package/src/exec/bash-executor.ts +2 -1
  46. package/src/extensibility/custom-commands/loader.ts +5 -3
  47. package/src/extensibility/custom-commands/types.ts +4 -2
  48. package/src/extensibility/custom-tools/loader.ts +5 -3
  49. package/src/extensibility/custom-tools/types.ts +7 -6
  50. package/src/extensibility/custom-tools/wrapper.ts +1 -1
  51. package/src/extensibility/extensions/loader.ts +7 -3
  52. package/src/extensibility/extensions/types.ts +9 -5
  53. package/src/extensibility/extensions/wrapper.ts +1 -2
  54. package/src/extensibility/hooks/loader.ts +3 -1
  55. package/src/extensibility/hooks/tool-wrapper.ts +1 -1
  56. package/src/extensibility/hooks/types.ts +4 -2
  57. package/src/extensibility/plugins/legacy-pi-compat.ts +30 -0
  58. package/src/extensibility/shared-events.ts +1 -1
  59. package/src/extensibility/typebox.ts +391 -0
  60. package/src/goals/tools/goal-tool.ts +6 -12
  61. package/src/hashline/types.ts +4 -4
  62. package/src/hindsight/state.ts +2 -2
  63. package/src/index.ts +0 -2
  64. package/src/internal-urls/docs-index.generated.ts +7 -7
  65. package/src/lsp/types.ts +30 -38
  66. package/src/mcp/manager.ts +1 -1
  67. package/src/mcp/tool-bridge.ts +1 -1
  68. package/src/modes/components/session-observer-overlay.ts +12 -1
  69. package/src/modes/components/status-line/segments.ts +2 -1
  70. package/src/modes/controllers/command-controller.ts +27 -2
  71. package/src/modes/controllers/event-controller.ts +3 -4
  72. package/src/modes/interactive-mode.ts +1 -1
  73. package/src/modes/rpc/host-tools.ts +1 -1
  74. package/src/modes/rpc/rpc-client.ts +1 -1
  75. package/src/modes/rpc/rpc-types.ts +1 -1
  76. package/src/modes/theme/theme.ts +111 -117
  77. package/src/modes/types.ts +1 -1
  78. package/src/modes/utils/context-usage.ts +2 -2
  79. package/src/sdk.ts +31 -8
  80. package/src/session/agent-session.ts +74 -104
  81. package/src/session/messages.ts +16 -51
  82. package/src/session/session-manager.ts +22 -2
  83. package/src/session/streaming-output.ts +16 -6
  84. package/src/task/executor.ts +208 -86
  85. package/src/task/index.ts +15 -11
  86. package/src/task/render.ts +32 -5
  87. package/src/task/types.ts +54 -39
  88. package/src/tools/ask.ts +12 -12
  89. package/src/tools/ast-edit.ts +11 -15
  90. package/src/tools/ast-grep.ts +9 -10
  91. package/src/tools/bash.ts +9 -23
  92. package/src/tools/browser.ts +39 -53
  93. package/src/tools/calculator.ts +12 -11
  94. package/src/tools/checkpoint.ts +7 -7
  95. package/src/tools/debug.ts +40 -43
  96. package/src/tools/eval.ts +6 -8
  97. package/src/tools/find.ts +10 -13
  98. package/src/tools/gh.ts +71 -128
  99. package/src/tools/hindsight-recall.ts +4 -6
  100. package/src/tools/hindsight-reflect.ts +5 -5
  101. package/src/tools/hindsight-retain.ts +15 -17
  102. package/src/tools/image-gen.ts +32 -82
  103. package/src/tools/index.ts +4 -1
  104. package/src/tools/inspect-image.ts +8 -9
  105. package/src/tools/irc.ts +15 -27
  106. package/src/tools/job.ts +14 -21
  107. package/src/tools/read.ts +7 -8
  108. package/src/tools/recipe/index.ts +7 -9
  109. package/src/tools/render-mermaid.ts +12 -12
  110. package/src/tools/report-tool-issue.ts +4 -4
  111. package/src/tools/resolve.ts +11 -11
  112. package/src/tools/review.ts +14 -26
  113. package/src/tools/search-tool-bm25.ts +7 -9
  114. package/src/tools/search.ts +19 -22
  115. package/src/tools/ssh.ts +7 -7
  116. package/src/tools/todo-write.ts +26 -34
  117. package/src/tools/vim.ts +10 -26
  118. package/src/tools/write.ts +5 -5
  119. package/src/tools/yield.ts +100 -54
  120. package/src/web/search/index.ts +9 -24
  121. package/src/prompts/compaction/branch-summary-context.md +0 -5
  122. package/src/prompts/compaction/branch-summary-preamble.md +0 -2
  123. package/src/prompts/compaction/branch-summary.md +0 -30
  124. package/src/prompts/compaction/compaction-short-summary.md +0 -9
  125. package/src/prompts/compaction/compaction-summary-context.md +0 -5
  126. package/src/prompts/compaction/compaction-summary.md +0 -38
  127. package/src/prompts/compaction/compaction-turn-prefix.md +0 -17
  128. package/src/prompts/compaction/compaction-update-summary.md +0 -45
  129. package/src/prompts/system/auto-handoff-threshold-focus.md +0 -1
  130. package/src/prompts/system/file-operations.md +0 -10
  131. package/src/prompts/system/handoff-document.md +0 -49
  132. package/src/prompts/system/summarization-system.md +0 -3
  133. package/src/session/compaction/branch-summarization.ts +0 -324
  134. package/src/session/compaction/compaction.ts +0 -1420
  135. package/src/session/compaction/errors.ts +0 -31
  136. package/src/session/compaction/index.ts +0 -8
  137. package/src/session/compaction/pruning.ts +0 -91
  138. package/src/session/compaction/utils.ts +0 -184
package/src/task/types.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
2
2
  import type { Usage } from "@oh-my-pi/pi-ai";
3
3
  import { $env } from "@oh-my-pi/pi-utils";
4
- import { type Static, type TSchema, Type } from "@sinclair/typebox";
4
+ import * as z from "zod/v4";
5
5
  import { getTaskSimpleModeCapabilities, type TaskSimpleMode } from "./simple-mode";
6
6
  import type { NestedRepoPatch } from "./worktree";
7
7
 
@@ -63,65 +63,65 @@ const assignmentDescriptionForContextDisabled =
63
63
  "Complete per-task instructions the subagent executes. Must follow the Target/Change/Edge Cases/Acceptance structure, and include any background that would otherwise live in `context` since shared context is disabled in this mode.";
64
64
 
65
65
  const createTaskItemSchema = (contextEnabled: boolean) =>
66
- Type.Object({
67
- id: Type.String({
68
- description: "CamelCase identifier, max 48 chars",
69
- maxLength: 48,
70
- }),
71
- description: Type.String({
72
- description: "Short one-liner for UI display only — not seen by the subagent",
73
- }),
74
- assignment: Type.String({
75
- description: contextEnabled ? assignmentDescriptionForContextEnabled : assignmentDescriptionForContextDisabled,
76
- }),
66
+ z.object({
67
+ id: z.string().max(48).describe("CamelCase identifier, max 48 chars"),
68
+ description: z.string().describe("Short one-liner for UI display only — not seen by the subagent"),
69
+ assignment: z
70
+ .string()
71
+ .describe(contextEnabled ? assignmentDescriptionForContextEnabled : assignmentDescriptionForContextDisabled),
77
72
  });
78
73
 
79
74
  /** Single task item for parallel execution (default shape with context enabled). */
80
75
  export const taskItemSchema = createTaskItemSchema(true);
81
- export type TaskItem = Static<typeof taskItemSchema>;
76
+ export type TaskItem = z.infer<typeof taskItemSchema>;
82
77
 
83
78
  const createTaskSchema = (options: { isolationEnabled: boolean; simpleMode: TaskSimpleMode }) => {
84
79
  const { contextEnabled, customSchemaEnabled } = getTaskSimpleModeCapabilities(options.simpleMode);
85
80
  const itemSchema = createTaskItemSchema(contextEnabled);
86
- const properties: Record<string, TSchema> = {
87
- agent: Type.String({ description: "Agent type for all tasks in this batch" }),
88
- tasks: Type.Array(itemSchema, {
89
- description: contextEnabled
90
- ? "Tasks to execute in parallel. Each must be small-scoped (3-5 files max) and self-contained given context + assignment."
91
- : "Tasks to execute in parallel. Each must be small-scoped (3-5 files max) and fully self-contained inside assignment because shared context is disabled.",
92
- }),
93
- };
81
+
82
+ let schema = z.object({
83
+ agent: z.string().describe("Agent type for all tasks in this batch"),
84
+ tasks: z
85
+ .array(itemSchema)
86
+ .describe(
87
+ contextEnabled
88
+ ? "Tasks to execute in parallel. Each must be small-scoped (3-5 files max) and self-contained given context + assignment."
89
+ : "Tasks to execute in parallel. Each must be small-scoped (3-5 files max) and fully self-contained inside assignment because shared context is disabled.",
90
+ ),
91
+ });
94
92
 
95
93
  if (contextEnabled) {
96
- properties.context = Type.Optional(
97
- Type.String({
98
- description:
94
+ schema = schema.extend({
95
+ context: z
96
+ .string()
97
+ .optional()
98
+ .describe(
99
99
  "Shared background prepended to every task's assignment. Put goal, non-goals, constraints, conventions, reference paths, API contracts, and global acceptance commands here once — instead of duplicating across assignments.",
100
- }),
101
- );
100
+ ),
101
+ });
102
102
  }
103
103
 
104
104
  if (customSchemaEnabled) {
105
- properties.schema = Type.Optional(
106
- Type.String({
107
- description:
105
+ schema = schema.extend({
106
+ schema: z
107
+ .string()
108
+ .optional()
109
+ .describe(
108
110
  "JSON-encoded JTD schema defining expected response structure. Output format belongs here — never in context or assignment.",
109
- }),
110
- );
111
+ ),
112
+ });
111
113
  }
112
114
 
113
115
  if (options.isolationEnabled) {
114
- return Type.Object({
115
- ...properties,
116
- isolated: Type.Optional(
117
- Type.Boolean({
118
- description: "Run in isolated environment; returns patches. Use when tasks edit overlapping files.",
119
- }),
120
- ),
116
+ schema = schema.extend({
117
+ isolated: z
118
+ .boolean()
119
+ .optional()
120
+ .describe("Run in isolated environment; returns patches. Use when tasks edit overlapping files."),
121
121
  });
122
122
  }
123
123
 
124
- return Type.Object(properties);
124
+ return schema;
125
125
  };
126
126
 
127
127
  export const taskSchema = createTaskSchema({ isolationEnabled: true, simpleMode: "default" });
@@ -141,6 +141,8 @@ const ALL_TASK_SCHEMAS = [
141
141
 
142
142
  type DynamicTaskSchema = (typeof ALL_TASK_SCHEMAS)[number];
143
143
  export type TaskSchema = typeof taskSchema;
144
+ /** Active task tool parameter schema for the current simple-mode / isolation flags */
145
+ export type TaskToolSchemaInstance = DynamicTaskSchema;
144
146
 
145
147
  export function getTaskSchema(options: { isolationEnabled: boolean; simpleMode: TaskSimpleMode }): DynamicTaskSchema {
146
148
  switch (options.simpleMode) {
@@ -219,6 +221,15 @@ export interface AgentProgress {
219
221
  toolCount: number;
220
222
  /** Cumulative input + output + cacheWrite tokens across all turns. Excludes cacheRead (re-reads cached context every turn, making cumulative sum misleading). */
221
223
  tokens: number;
224
+ /**
225
+ * Current per-turn context size: latest assistant message's `usage.totalTokens`.
226
+ * This is the number to compare against `contextWindow` — what compaction
227
+ * decides on, what the user typically reads as "how full is the context".
228
+ * Distinct from `tokens`, which is a lifetime billing-volume counter.
229
+ */
230
+ contextTokens?: number;
231
+ /** Model's context window in tokens, when known. Lets the UI render `<curr>/<window>` gauges. */
232
+ contextWindow?: number;
222
233
  /** Cumulative billing cost in USD, accumulated incrementally from message_end events. */
223
234
  cost: number;
224
235
  durationMs: number;
@@ -244,6 +255,10 @@ export interface SingleResult {
244
255
  durationMs: number;
245
256
  /** Cumulative input + output + cacheWrite tokens across all turns. Excludes cacheRead (re-reads cached context every turn, making cumulative sum misleading). */
246
257
  tokens: number;
258
+ /** Latest per-turn context size at task completion. See `AgentProgress.contextTokens`. */
259
+ contextTokens?: number;
260
+ /** Model's context window in tokens, when known. */
261
+ contextWindow?: number;
247
262
  modelOverride?: string | string[];
248
263
  error?: string;
249
264
  aborted?: boolean;
package/src/tools/ask.ts CHANGED
@@ -18,7 +18,7 @@
18
18
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
19
19
  import { type Component, Container, Markdown, renderInlineMarkdown, TERMINAL, Text } from "@oh-my-pi/pi-tui";
20
20
  import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
21
- import { type Static, Type } from "@sinclair/typebox";
21
+ import * as z from "zod/v4";
22
22
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
23
23
  import { getMarkdownTheme, type Theme, theme } from "../modes/theme/theme";
24
24
  import askDescription from "../prompts/tools/ask.md" with { type: "text" };
@@ -31,23 +31,23 @@ import { ToolAbortError } from "./tool-errors";
31
31
  // Types
32
32
  // =============================================================================
33
33
 
34
- const OptionItem = Type.Object({
35
- label: Type.String({ description: "display label" }),
34
+ const OptionItem = z.object({
35
+ label: z.string().describe("display label"),
36
36
  });
37
37
 
38
- const QuestionItem = Type.Object({
39
- id: Type.String({ description: "question id", examples: ["auth", "cache"] }),
40
- question: Type.String({ description: "question text" }),
41
- options: Type.Array(OptionItem, { description: "available options" }),
42
- multi: Type.Optional(Type.Boolean({ description: "allow multiple selections" })),
43
- recommended: Type.Optional(Type.Number({ description: "recommended option index" })),
38
+ const QuestionItem = z.object({
39
+ id: z.string().describe("question id"),
40
+ question: z.string().describe("question text"),
41
+ options: z.array(OptionItem).describe("available options"),
42
+ multi: z.boolean().describe("allow multiple selections").optional(),
43
+ recommended: z.number().describe("recommended option index").optional(),
44
44
  });
45
45
 
46
- const askSchema = Type.Object({
47
- questions: Type.Array(QuestionItem, { description: "questions to ask", minItems: 1 }),
46
+ const askSchema = z.object({
47
+ questions: z.array(QuestionItem).min(1).describe("questions to ask"),
48
48
  });
49
49
 
50
- export type AskToolInput = Static<typeof askSchema>;
50
+ export type AskToolInput = z.infer<typeof askSchema>;
51
51
 
52
52
  /** Result for a single question */
53
53
  export interface QuestionResult {
@@ -4,7 +4,7 @@ import { type AstReplaceChange, type AstReplaceFileChange, astEdit } from "@oh-m
4
4
  import type { Component } from "@oh-my-pi/pi-tui";
5
5
  import { Text } from "@oh-my-pi/pi-tui";
6
6
  import { $envpos, prompt, untilAborted } from "@oh-my-pi/pi-utils";
7
- import { type Static, Type } from "@sinclair/typebox";
7
+ import * as z from "zod/v4";
8
8
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
9
9
  import { computeLineHash, HL_BODY_SEP } from "../hashline/hash";
10
10
  import type { Theme } from "../modes/theme/theme";
@@ -33,21 +33,17 @@ import { queueResolveHandler } from "./resolve";
33
33
  import { ToolError } from "./tool-errors";
34
34
  import { toolResult } from "./tool-result";
35
35
 
36
- const astEditOpSchema = Type.Object({
37
- pat: Type.String({ description: "ast pattern", examples: ["oldFn($$$ARGS)"] }),
38
- out: Type.String({ description: "replacement template", examples: ["newFn($$$ARGS)"] }),
36
+ const astEditOpSchema = z.object({
37
+ pat: z.string().describe("ast pattern"),
38
+ out: z.string().describe("replacement template"),
39
39
  });
40
40
 
41
- const astEditSchema = Type.Object({
42
- ops: Type.Array(astEditOpSchema, {
43
- minItems: 1,
44
- description: "rewrite ops",
45
- }),
46
- paths: Type.Array(Type.String({ description: "file, directory, glob, or internal URL to rewrite" }), {
47
- minItems: 1,
48
- description: "files, directories, globs, or internal URLs to rewrite",
49
- examples: [["src/"], ["src/foo.ts"], ["src/**/*.ts"], ["src/", "packages/"]],
50
- }),
41
+ const astEditSchema = z.object({
42
+ ops: z.array(astEditOpSchema).min(1).describe("rewrite ops"),
43
+ paths: z
44
+ .array(z.string().describe("file, directory, glob, or internal URL to rewrite"))
45
+ .min(1)
46
+ .describe("files, directories, globs, or internal URLs to rewrite"),
51
47
  });
52
48
 
53
49
  interface AstEditCallOptions {
@@ -174,7 +170,7 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
174
170
 
175
171
  async execute(
176
172
  _toolCallId: string,
177
- params: Static<typeof astEditSchema>,
173
+ params: z.infer<typeof astEditSchema>,
178
174
  signal?: AbortSignal,
179
175
  _onUpdate?: AgentToolUpdateCallback<AstEditToolDetails>,
180
176
  _context?: AgentToolContext,
@@ -4,7 +4,7 @@ import { type AstFindMatch, astGrep } from "@oh-my-pi/pi-natives";
4
4
  import type { Component } from "@oh-my-pi/pi-tui";
5
5
  import { Text } from "@oh-my-pi/pi-tui";
6
6
  import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
7
- import { type Static, Type } from "@sinclair/typebox";
7
+ import * as z from "zod/v4";
8
8
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
9
9
  import type { Theme } from "../modes/theme/theme";
10
10
  import astGrepDescription from "../prompts/tools/ast-grep.md" with { type: "text" };
@@ -32,14 +32,13 @@ import {
32
32
  import { ToolError } from "./tool-errors";
33
33
  import { toolResult } from "./tool-result";
34
34
 
35
- const astGrepSchema = Type.Object({
36
- pat: Type.String({ description: "ast pattern", examples: ["console.log($$$)"] }),
37
- paths: Type.Array(Type.String({ description: "file, directory, glob, or internal URL to search" }), {
38
- minItems: 1,
39
- description: "files, directories, globs, or internal URLs to search",
40
- examples: [["src/"], ["src/foo.ts"], ["src/**/*.ts"], ["src/", "packages/"]],
41
- }),
42
- skip: Type.Optional(Type.Number({ description: "matches to skip", default: 0 })),
35
+ const astGrepSchema = z.object({
36
+ pat: z.string().describe("ast pattern"),
37
+ paths: z
38
+ .array(z.string().describe("file, directory, glob, or internal URL to search"))
39
+ .min(1)
40
+ .describe("files, directories, globs, or internal URLs to search"),
41
+ skip: z.number().default(0).describe("matches to skip").optional(),
43
42
  });
44
43
 
45
44
  async function runMultiTargetAstGrep(
@@ -129,7 +128,7 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
129
128
 
130
129
  async execute(
131
130
  _toolCallId: string,
132
- params: Static<typeof astGrepSchema>,
131
+ params: z.infer<typeof astGrepSchema>,
133
132
  signal?: AbortSignal,
134
133
  _onUpdate?: AgentToolUpdateCallback<AstGrepToolDetails>,
135
134
  _context?: AgentToolContext,
package/src/tools/bash.ts CHANGED
@@ -3,7 +3,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
3
3
  import type { Component } from "@oh-my-pi/pi-tui";
4
4
  import { ImageProtocol, TERMINAL, Text } from "@oh-my-pi/pi-tui";
5
5
  import { $env, getProjectDir, isEnoent, logger, prompt } from "@oh-my-pi/pi-utils";
6
- import { Type } from "@sinclair/typebox";
6
+ import * as z from "zod/v4";
7
7
  import { AsyncJobManager } from "../async";
8
8
  import { type BashResult, executeBash } from "../exec/bash-executor";
9
9
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
@@ -44,30 +44,16 @@ async function saveBashOriginalArtifact(session: ToolSession, originalText: stri
44
44
  }
45
45
  }
46
46
 
47
- const bashSchemaBase = Type.Object({
48
- command: Type.String({ description: "command to execute", examples: ["ls -la", "echo hi"] }),
49
- env: Type.Optional(
50
- Type.Record(Type.String({ pattern: BASH_ENV_NAME_PATTERN.source }), Type.String(), {
51
- description: "extra env vars",
52
- }),
53
- ),
54
- timeout: Type.Optional(Type.Number({ description: "timeout in seconds", default: 300 })),
55
- cwd: Type.Optional(Type.String({ description: "working directory", examples: ["src/", "/tmp"] })),
56
-
57
- pty: Type.Optional(
58
- Type.Boolean({
59
- description: "run in pty mode",
60
- }),
61
- ),
47
+ const bashSchemaBase = z.object({
48
+ command: z.string().describe("command to execute"),
49
+ env: z.record(z.string().regex(BASH_ENV_NAME_PATTERN), z.string()).optional().describe("extra env vars"),
50
+ timeout: z.number().default(300).describe("timeout in seconds").optional(),
51
+ cwd: z.string().describe("working directory").optional(),
52
+ pty: z.boolean().describe("run in pty mode").optional(),
62
53
  });
63
54
 
64
- const bashSchemaWithAsync = Type.Object({
65
- ...bashSchemaBase.properties,
66
- async: Type.Optional(
67
- Type.Boolean({
68
- description: "run in background",
69
- }),
70
- ),
55
+ const bashSchemaWithAsync = bashSchemaBase.extend({
56
+ async: z.boolean().describe("run in background").optional(),
71
57
  });
72
58
 
73
59
  type BashToolSchema = typeof bashSchemaBase | typeof bashSchemaWithAsync;
@@ -1,7 +1,6 @@
1
1
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
- import { StringEnum } from "@oh-my-pi/pi-ai";
3
2
  import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
4
- import { type Static, Type } from "@sinclair/typebox";
3
+ import * as z from "zod/v4";
5
4
  import browserDescription from "../prompts/tools/browser.md" with { type: "text" };
6
5
  import type { ToolSession } from "../sdk";
7
6
  import { acquireBrowser, type BrowserHandle, type BrowserKind, type BrowserKindTag } from "./browser/registry";
@@ -18,62 +17,49 @@ export type { Observation, ObservationEntry } from "./browser/tab-protocol";
18
17
 
19
18
  const DEFAULT_TAB_NAME = "main";
20
19
 
21
- const appSchema = Type.Object({
22
- path: Type.Optional(
23
- Type.String({
24
- description: "absolute path to a binary to spawn (single-instance reuse)",
25
- examples: ["/Applications/Cursor.app/Contents/MacOS/Cursor"],
26
- }),
27
- ),
28
- cdp_url: Type.Optional(
29
- Type.String({
30
- description: "existing CDP endpoint to connect to (e.g. http://127.0.0.1:9222)",
31
- }),
32
- ),
33
- args: Type.Optional(Type.Array(Type.String(), { description: "extra CLI args when spawning" })),
34
- target: Type.Optional(Type.String({ description: "substring matched against url+title to pick a BrowserWindow" })),
20
+ const appSchema = z.object({
21
+ path: z.string().describe("absolute path to a binary to spawn (single-instance reuse)").optional(),
22
+ cdp_url: z.string().describe("existing CDP endpoint to connect to (e.g. http://127.0.0.1:9222)").optional(),
23
+ args: z.array(z.string()).describe("extra CLI args when spawning").optional(),
24
+ target: z.string().describe("substring matched against url+title to pick a BrowserWindow").optional(),
35
25
  });
36
26
 
37
- const browserSchema = Type.Object({
38
- action: StringEnum(["open", "close", "run"], { description: "tab/browser operation" }),
39
- name: Type.Optional(
40
- Type.String({
41
- description: "tab id; default 'main'. Multiple tabs can coexist; reusable across run() calls and subagents.",
42
- examples: ["main", "docs", "gh"],
43
- }),
44
- ),
45
- url: Type.Optional(Type.String({ description: "open: navigate after acquiring tab" })),
46
- app: Type.Optional(appSchema),
47
- viewport: Type.Optional(
48
- Type.Object({
49
- width: Type.Number(),
50
- height: Type.Number(),
51
- scale: Type.Optional(Type.Number()),
52
- }),
53
- ),
54
- wait_until: Type.Optional(
55
- StringEnum(["load", "domcontentloaded", "networkidle0", "networkidle2"], {
56
- description: "navigation wait condition for url",
57
- }),
58
- ),
59
- dialogs: Type.Optional(
60
- StringEnum(["accept", "dismiss"], {
61
- description: "open: auto-handle alert/confirm/beforeunload dialogs (default: leave for caller to handle)",
62
- }),
63
- ),
64
- code: Type.Optional(
65
- Type.String({
66
- description:
67
- "run: JS body executed with `page`, `browser`, `tab`, `display`, `assert`, `wait` in scope. Treated as the body of an async function. Use `display(value)` to attach text/JSON/images; the function's return value is JSON-serialized as a final block.",
68
- }),
69
- ),
70
- timeout: Type.Optional(Type.Number({ description: "timeout in seconds", default: 30 })),
71
- all: Type.Optional(Type.Boolean({ description: "close: close every tab" })),
72
- kill: Type.Optional(Type.Boolean({ description: "close: also kill spawned-app browsers (default: leave running)" })),
27
+ const browserSchema = z.object({
28
+ action: z.enum(["open", "close", "run"] as const).describe("tab/browser operation"),
29
+ name: z
30
+ .string()
31
+ .describe("tab id; default 'main'. Multiple tabs can coexist; reusable across run() calls and subagents.")
32
+ .optional(),
33
+ url: z.string().describe("open: navigate after acquiring tab").optional(),
34
+ app: appSchema.optional(),
35
+ viewport: z
36
+ .object({
37
+ width: z.number(),
38
+ height: z.number(),
39
+ scale: z.number().optional(),
40
+ })
41
+ .optional(),
42
+ wait_until: z
43
+ .enum(["load", "domcontentloaded", "networkidle0", "networkidle2"] as const)
44
+ .describe("navigation wait condition for url")
45
+ .optional(),
46
+ dialogs: z
47
+ .enum(["accept", "dismiss"] as const)
48
+ .describe("open: auto-handle alert/confirm/beforeunload dialogs (default: leave for caller to handle)")
49
+ .optional(),
50
+ code: z
51
+ .string()
52
+ .describe(
53
+ "run: JS body executed with `page`, `browser`, `tab`, `display`, `assert`, `wait` in scope. Treated as the body of an async function. Use `display(value)` to attach text/JSON/images; the function's return value is JSON-serialized as a final block.",
54
+ )
55
+ .optional(),
56
+ timeout: z.number().default(30).describe("timeout in seconds").optional(),
57
+ all: z.boolean().describe("close: close every tab").optional(),
58
+ kill: z.boolean().describe("close: also kill spawned-app browsers (default: leave running)").optional(),
73
59
  });
74
60
 
75
61
  /** Input schema for the browser tool. */
76
- export type BrowserParams = Static<typeof browserSchema>;
62
+ export type BrowserParams = z.infer<typeof browserSchema>;
77
63
 
78
64
  /** Details describing a browser tool execution result (for renderers + transcript). */
79
65
  export interface BrowserToolDetails {
@@ -2,7 +2,7 @@ import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
2
2
  import type { Component } from "@oh-my-pi/pi-tui";
3
3
  import { Text } from "@oh-my-pi/pi-tui";
4
4
  import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
5
- import { type Static, Type } from "@sinclair/typebox";
5
+ import * as z from "zod/v4";
6
6
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
7
7
  import type { Theme } from "../modes/theme/theme";
8
8
  import calculatorDescription from "../prompts/tools/calculator.md" with { type: "text" };
@@ -28,15 +28,16 @@ type Token =
28
28
  | { type: "operator"; value: Operator }
29
29
  | { type: "paren"; value: "(" | ")" };
30
30
 
31
- const calculatorSchema = Type.Object({
32
- calculations: Type.Array(
33
- Type.Object({
34
- expression: Type.String({ description: "math expression", examples: ["2 + 2", "sqrt(16)"] }),
35
- prefix: Type.String({ description: "prefix text" }),
36
- suffix: Type.String({ description: "suffix text" }),
37
- }),
38
- { description: "calculations to evaluate" },
39
- ),
31
+ const calculatorSchema = z.object({
32
+ calculations: z
33
+ .array(
34
+ z.object({
35
+ expression: z.string().describe("math expression"),
36
+ prefix: z.string().describe("prefix text"),
37
+ suffix: z.string().describe("suffix text"),
38
+ }),
39
+ )
40
+ .describe("calculations to evaluate"),
40
41
  });
41
42
 
42
43
  export interface CalculatorToolDetails {
@@ -385,7 +386,7 @@ function formatResult(value: number): string {
385
386
  // Tool Class
386
387
  // ═══════════════════════════════════════════════════════════════════════════
387
388
 
388
- type CalculatorParams = Static<typeof calculatorSchema>;
389
+ type CalculatorParams = z.infer<typeof calculatorSchema>;
389
390
 
390
391
  /**
391
392
  * Calculator tool for evaluating mathematical expressions.
@@ -1,6 +1,6 @@
1
1
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
2
  import { prompt } from "@oh-my-pi/pi-utils";
3
- import { type Static, Type } from "@sinclair/typebox";
3
+ import * as z from "zod/v4";
4
4
  import checkpointDescription from "../prompts/tools/checkpoint.md" with { type: "text" };
5
5
  import rewindDescription from "../prompts/tools/rewind.md" with { type: "text" };
6
6
  import type { ToolSession } from ".";
@@ -17,17 +17,17 @@ export interface CheckpointState {
17
17
  startedAt: string;
18
18
  }
19
19
 
20
- const checkpointSchema = Type.Object({
21
- goal: Type.String({ description: "investigation goal", examples: ["investigate retry logic"] }),
20
+ const checkpointSchema = z.object({
21
+ goal: z.string().describe("investigation goal"),
22
22
  });
23
23
 
24
- type CheckpointParams = Static<typeof checkpointSchema>;
24
+ type CheckpointParams = z.infer<typeof checkpointSchema>;
25
25
 
26
- const rewindSchema = Type.Object({
27
- report: Type.String({ description: "investigation findings" }),
26
+ const rewindSchema = z.object({
27
+ report: z.string().describe("investigation findings"),
28
28
  });
29
29
 
30
- type RewindParams = Static<typeof rewindSchema>;
30
+ type RewindParams = z.infer<typeof rewindSchema>;
31
31
 
32
32
  export interface CheckpointToolDetails {
33
33
  goal: string;
@@ -5,10 +5,9 @@ import type {
5
5
  AgentToolUpdateCallback,
6
6
  RenderResultOptions,
7
7
  } from "@oh-my-pi/pi-agent-core";
8
- import { StringEnum } from "@oh-my-pi/pi-ai";
9
8
  import { type Component, Text } from "@oh-my-pi/pi-tui";
10
9
  import { prompt } from "@oh-my-pi/pi-utils";
11
- import { type Static, Type } from "@sinclair/typebox";
10
+ import * as z from "zod/v4";
12
11
  import {
13
12
  type DapBreakpointRecord,
14
13
  type DapCapabilities,
@@ -51,8 +50,8 @@ import { ToolError } from "./tool-errors";
51
50
  import { toolResult } from "./tool-result";
52
51
  import { clampTimeout } from "./tool-timeouts";
53
52
 
54
- const debugSchema = Type.Object({
55
- action: StringEnum([
53
+ const debugSchema = z.object({
54
+ action: z.enum([
56
55
  "launch",
57
56
  "attach",
58
57
  "set_breakpoint",
@@ -81,47 +80,45 @@ const debugSchema = Type.Object({
81
80
  "output",
82
81
  "terminate",
83
82
  "sessions",
84
- ]),
85
- program: Type.Optional(Type.String({ description: "program path" })),
86
- args: Type.Optional(Type.Array(Type.String(), { description: "program arguments" })),
87
- adapter: Type.Optional(Type.String({ description: "debugger adapter (gdb, lldb-dap, debugpy, dlv)" })),
88
- cwd: Type.Optional(Type.String()),
89
- file: Type.Optional(Type.String({ description: "source file" })),
90
- line: Type.Optional(Type.Number({ description: "source line" })),
91
- function: Type.Optional(Type.String({ description: "function name" })),
92
- name: Type.Optional(Type.String({ description: "variable or data name" })),
93
- condition: Type.Optional(Type.String({ description: "breakpoint condition" })),
94
- hit_condition: Type.Optional(Type.String()),
95
- expression: Type.Optional(Type.String({ description: "expression to evaluate" })),
96
- context: Type.Optional(
97
- Type.String({ description: "evaluate context: watch | repl | hover | variables | clipboard" }),
98
- ),
99
- frame_id: Type.Optional(Type.Number()),
100
- scope_id: Type.Optional(Type.Number({ description: "scope variables reference" })),
101
- variable_ref: Type.Optional(Type.Number({ description: "variable reference" })),
102
- pid: Type.Optional(Type.Number({ description: "process id for attach" })),
103
- port: Type.Optional(Type.Number({ description: "remote attach port" })),
104
- host: Type.Optional(Type.String({ description: "remote attach host" })),
105
- levels: Type.Optional(Type.Number({ description: "max stack frames" })),
106
- memory_reference: Type.Optional(Type.String({ description: "memory reference or address" })),
107
- instruction_reference: Type.Optional(Type.String()),
108
- instruction_count: Type.Optional(Type.Number()),
109
- instruction_offset: Type.Optional(Type.Number()),
110
- count: Type.Optional(Type.Number({ description: "bytes to read" })),
111
- data: Type.Optional(Type.String({ description: "base64 memory payload" })),
112
- data_id: Type.Optional(Type.String({ description: "data breakpoint id" })),
113
- access_type: Type.Optional(StringEnum(["read", "write", "readWrite"])),
114
- command: Type.Optional(Type.String({ description: "custom dap request command" })),
115
- arguments: Type.Optional(Type.Record(Type.String(), Type.Any(), { description: "custom request arguments" })),
116
- offset: Type.Optional(Type.Number()),
117
- resolve_symbols: Type.Optional(Type.Boolean()),
118
- allow_partial: Type.Optional(Type.Boolean()),
119
- start_module: Type.Optional(Type.Number()),
120
- module_count: Type.Optional(Type.Number()),
121
- timeout: Type.Optional(Type.Number({ description: "per-request timeout seconds" })),
83
+ ] as const),
84
+ program: z.string().describe("program path").optional(),
85
+ args: z.array(z.string()).describe("program arguments").optional(),
86
+ adapter: z.string().describe("debugger adapter (gdb, lldb-dap, debugpy, dlv)").optional(),
87
+ cwd: z.string().optional(),
88
+ file: z.string().describe("source file").optional(),
89
+ line: z.number().describe("source line").optional(),
90
+ function: z.string().describe("function name").optional(),
91
+ name: z.string().describe("variable or data name").optional(),
92
+ condition: z.string().describe("breakpoint condition").optional(),
93
+ hit_condition: z.string().optional(),
94
+ expression: z.string().describe("expression to evaluate").optional(),
95
+ context: z.string().describe("evaluate context: watch | repl | hover | variables | clipboard").optional(),
96
+ frame_id: z.number().optional(),
97
+ scope_id: z.number().describe("scope variables reference").optional(),
98
+ variable_ref: z.number().describe("variable reference").optional(),
99
+ pid: z.number().describe("process id for attach").optional(),
100
+ port: z.number().describe("remote attach port").optional(),
101
+ host: z.string().describe("remote attach host").optional(),
102
+ levels: z.number().describe("max stack frames").optional(),
103
+ memory_reference: z.string().describe("memory reference or address").optional(),
104
+ instruction_reference: z.string().optional(),
105
+ instruction_count: z.number().optional(),
106
+ instruction_offset: z.number().optional(),
107
+ count: z.number().describe("bytes to read").optional(),
108
+ data: z.string().describe("base64 memory payload").optional(),
109
+ data_id: z.string().describe("data breakpoint id").optional(),
110
+ access_type: z.enum(["read", "write", "readWrite"] as const).optional(),
111
+ command: z.string().describe("custom dap request command").optional(),
112
+ arguments: z.record(z.string(), z.any()).describe("custom request arguments").optional(),
113
+ offset: z.number().optional(),
114
+ resolve_symbols: z.boolean().optional(),
115
+ allow_partial: z.boolean().optional(),
116
+ start_module: z.number().optional(),
117
+ module_count: z.number().optional(),
118
+ timeout: z.number().describe("per-request timeout seconds").optional(),
122
119
  });
123
120
 
124
- export type DebugParams = Static<typeof debugSchema>;
121
+ export type DebugParams = z.infer<typeof debugSchema>;
125
122
  export type DebugAction = DebugParams["action"];
126
123
 
127
124
  interface DebugToolDetails {