@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
@@ -30,7 +30,6 @@ const DEFAULT_LOCAL_TOKEN = "lm-studio-local";
30
30
  import { registerOAuthProvider, unregisterOAuthProviders } from "@oh-my-pi/pi-ai/utils/oauth";
31
31
  import type { OAuthCredentials, OAuthLoginCallbacks } from "@oh-my-pi/pi-ai/utils/oauth/types";
32
32
  import { isRecord, logger } from "@oh-my-pi/pi-utils";
33
- import { type Static, Type } from "@sinclair/typebox";
34
33
  import { parseModelString, resolveProviderModelReference } from "../config/model-resolver";
35
34
  import { isValidThemeColor, type ThemeColor } from "../modes/theme/theme";
36
35
  import type { AuthStorage, OAuthCredential } from "../session/auth-storage";
@@ -43,6 +42,13 @@ import {
43
42
  formatCanonicalVariantSelector,
44
43
  type ModelEquivalenceConfig,
45
44
  } from "./model-equivalence";
45
+ import {
46
+ type ModelOverride,
47
+ type ModelsConfig,
48
+ ModelsConfigSchema,
49
+ type ProviderAuthMode,
50
+ type ProviderDiscovery,
51
+ } from "./models-config-schema";
46
52
  import { type Settings, settings } from "./settings";
47
53
 
48
54
  export type { CanonicalModelIndex, CanonicalModelRecord, CanonicalModelVariant, ModelEquivalenceConfig };
@@ -121,194 +127,6 @@ export function getRoleInfo(role: string, settings: Settings): RoleInfo {
121
127
  return { name: role, color: "muted" };
122
128
  }
123
129
 
124
- const OpenRouterRoutingSchema = Type.Object({
125
- only: Type.Optional(Type.Array(Type.String())),
126
- order: Type.Optional(Type.Array(Type.String())),
127
- });
128
-
129
- // Schema for Vercel AI Gateway routing preferences
130
- const VercelGatewayRoutingSchema = Type.Object({
131
- only: Type.Optional(Type.Array(Type.String())),
132
- order: Type.Optional(Type.Array(Type.String())),
133
- });
134
-
135
- // Schema for OpenAI compatibility settings
136
- const ReasoningEffortMapSchema = Type.Object({
137
- minimal: Type.Optional(Type.String()),
138
- low: Type.Optional(Type.String()),
139
- medium: Type.Optional(Type.String()),
140
- high: Type.Optional(Type.String()),
141
- xhigh: Type.Optional(Type.String()),
142
- });
143
-
144
- const OpenAICompatSchema = Type.Object({
145
- supportsStore: Type.Optional(Type.Boolean()),
146
- supportsDeveloperRole: Type.Optional(Type.Boolean()),
147
- supportsReasoningEffort: Type.Optional(Type.Boolean()),
148
- reasoningEffortMap: Type.Optional(ReasoningEffortMapSchema),
149
- maxTokensField: Type.Optional(Type.Union([Type.Literal("max_completion_tokens"), Type.Literal("max_tokens")])),
150
- supportsUsageInStreaming: Type.Optional(Type.Boolean()),
151
- requiresToolResultName: Type.Optional(Type.Boolean()),
152
- requiresMistralToolIds: Type.Optional(Type.Boolean()),
153
- requiresAssistantAfterToolResult: Type.Optional(Type.Boolean()),
154
- requiresThinkingAsText: Type.Optional(Type.Boolean()),
155
- reasoningContentField: Type.Optional(
156
- Type.Union([Type.Literal("reasoning_content"), Type.Literal("reasoning"), Type.Literal("reasoning_text")]),
157
- ),
158
- requiresReasoningContentForToolCalls: Type.Optional(Type.Boolean()),
159
- requiresAssistantContentForToolCalls: Type.Optional(Type.Boolean()),
160
- supportsToolChoice: Type.Optional(Type.Boolean()),
161
- disableReasoningOnForcedToolChoice: Type.Optional(Type.Boolean()),
162
- thinkingFormat: Type.Optional(
163
- Type.Union([
164
- Type.Literal("openai"),
165
- Type.Literal("openrouter"),
166
- Type.Literal("zai"),
167
- Type.Literal("qwen"),
168
- Type.Literal("qwen-chat-template"),
169
- ]),
170
- ),
171
- openRouterRouting: Type.Optional(OpenRouterRoutingSchema),
172
- vercelGatewayRouting: Type.Optional(VercelGatewayRoutingSchema),
173
- extraBody: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
174
- supportsStrictMode: Type.Optional(Type.Boolean()),
175
- toolStrictMode: Type.Optional(Type.Union([Type.Literal("all_strict"), Type.Literal("none")])),
176
- });
177
-
178
- const EffortSchema = Type.Union([
179
- Type.Literal("minimal"),
180
- Type.Literal("low"),
181
- Type.Literal("medium"),
182
- Type.Literal("high"),
183
- Type.Literal("xhigh"),
184
- ]);
185
-
186
- const ThinkingControlModeSchema = Type.Union([
187
- Type.Literal("effort"),
188
- Type.Literal("budget"),
189
- Type.Literal("google-level"),
190
- Type.Literal("anthropic-adaptive"),
191
- Type.Literal("anthropic-budget-effort"),
192
- ]);
193
-
194
- const ModelThinkingSchema = Type.Object({
195
- minLevel: EffortSchema,
196
- maxLevel: EffortSchema,
197
- mode: ThinkingControlModeSchema,
198
- defaultLevel: Type.Optional(EffortSchema),
199
- });
200
-
201
- // Schema for custom model definition
202
- // Most fields are optional with sensible defaults for local models (Ollama, LM Studio, etc.)
203
- const ModelDefinitionSchema = Type.Object({
204
- id: Type.String({ minLength: 1 }),
205
- name: Type.Optional(Type.String({ minLength: 1 })),
206
- api: Type.Optional(
207
- Type.Union([
208
- Type.Literal("openai-completions"),
209
- Type.Literal("openai-responses"),
210
- Type.Literal("openai-codex-responses"),
211
- Type.Literal("azure-openai-responses"),
212
- Type.Literal("anthropic-messages"),
213
- Type.Literal("google-generative-ai"),
214
- Type.Literal("google-vertex"),
215
- ]),
216
- ),
217
- baseUrl: Type.Optional(Type.String({ minLength: 1 })),
218
- reasoning: Type.Optional(Type.Boolean()),
219
- thinking: Type.Optional(ModelThinkingSchema),
220
- input: Type.Optional(Type.Array(Type.Union([Type.Literal("text"), Type.Literal("image")]))),
221
- cost: Type.Optional(
222
- Type.Object({
223
- input: Type.Number(),
224
- output: Type.Number(),
225
- cacheRead: Type.Number(),
226
- cacheWrite: Type.Number(),
227
- }),
228
- ),
229
- premiumMultiplier: Type.Optional(Type.Number()),
230
- contextWindow: Type.Optional(Type.Number()),
231
- maxTokens: Type.Optional(Type.Number()),
232
- headers: Type.Optional(Type.Record(Type.String(), Type.String())),
233
- compat: Type.Optional(OpenAICompatSchema),
234
- contextPromotionTarget: Type.Optional(Type.String({ minLength: 1 })),
235
- });
236
-
237
- // Schema for per-model overrides (all fields optional, merged with built-in model)
238
- const ModelOverrideSchema = Type.Object({
239
- name: Type.Optional(Type.String({ minLength: 1 })),
240
- reasoning: Type.Optional(Type.Boolean()),
241
- thinking: Type.Optional(ModelThinkingSchema),
242
- input: Type.Optional(Type.Array(Type.Union([Type.Literal("text"), Type.Literal("image")]))),
243
- cost: Type.Optional(
244
- Type.Object({
245
- input: Type.Optional(Type.Number()),
246
- output: Type.Optional(Type.Number()),
247
- cacheRead: Type.Optional(Type.Number()),
248
- cacheWrite: Type.Optional(Type.Number()),
249
- }),
250
- ),
251
- premiumMultiplier: Type.Optional(Type.Number()),
252
- contextWindow: Type.Optional(Type.Number()),
253
- maxTokens: Type.Optional(Type.Number()),
254
- headers: Type.Optional(Type.Record(Type.String(), Type.String())),
255
- compat: Type.Optional(OpenAICompatSchema),
256
- contextPromotionTarget: Type.Optional(Type.String({ minLength: 1 })),
257
- });
258
-
259
- type ModelOverride = Static<typeof ModelOverrideSchema>;
260
-
261
- const ProviderDiscoverySchema = Type.Object({
262
- type: Type.Union([
263
- Type.Literal("ollama"),
264
- Type.Literal("llama.cpp"),
265
- Type.Literal("lm-studio"),
266
- Type.Literal("openai-models-list"),
267
- ]),
268
- });
269
-
270
- const ProviderAuthSchema = Type.Union([Type.Literal("apiKey"), Type.Literal("none"), Type.Literal("oauth")]);
271
-
272
- const ProviderConfigSchema = Type.Object({
273
- baseUrl: Type.Optional(Type.String({ minLength: 1 })),
274
- apiKey: Type.Optional(Type.String({ minLength: 1 })),
275
- api: Type.Optional(
276
- Type.Union([
277
- Type.Literal("openai-completions"),
278
- Type.Literal("openai-responses"),
279
- Type.Literal("openai-codex-responses"),
280
- Type.Literal("azure-openai-responses"),
281
- Type.Literal("anthropic-messages"),
282
- Type.Literal("google-generative-ai"),
283
- Type.Literal("google-vertex"),
284
- ]),
285
- ),
286
- headers: Type.Optional(Type.Record(Type.String(), Type.String())),
287
- compat: Type.Optional(OpenAICompatSchema),
288
- authHeader: Type.Optional(Type.Boolean()),
289
- auth: Type.Optional(ProviderAuthSchema),
290
- discovery: Type.Optional(ProviderDiscoverySchema),
291
- models: Type.Optional(Type.Array(ModelDefinitionSchema)),
292
- modelOverrides: Type.Optional(Type.Record(Type.String(), ModelOverrideSchema)),
293
- /** When true, disables strict tool schemas for this provider (for third-party Anthropic-compatible endpoints that reject the strict field). */
294
- disableStrictTools: Type.Optional(Type.Boolean()),
295
- });
296
-
297
- const EquivalenceConfigSchema = Type.Object({
298
- overrides: Type.Optional(Type.Record(Type.String(), Type.String({ minLength: 1 }))),
299
- exclude: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
300
- });
301
-
302
- const ModelsConfigSchema = Type.Object({
303
- providers: Type.Optional(Type.Record(Type.String(), ProviderConfigSchema)),
304
- equivalence: Type.Optional(EquivalenceConfigSchema),
305
- });
306
-
307
- type ModelsConfig = Static<typeof ModelsConfigSchema>;
308
-
309
- type ProviderAuthMode = Static<typeof ProviderAuthSchema>;
310
- type ProviderDiscovery = Static<typeof ProviderDiscoverySchema>;
311
-
312
130
  type ProviderValidationMode = "models-config" | "runtime-register";
313
131
 
314
132
  interface ProviderValidationModel {
@@ -347,12 +165,13 @@ function validateProviderConfiguration(
347
165
  !config.baseUrl &&
348
166
  !config.headers &&
349
167
  !config.compat &&
168
+ !config.apiKey &&
350
169
  !config.disableStrictTools &&
351
170
  !hasModelOverrides &&
352
171
  !config.discovery
353
172
  ) {
354
173
  throw new Error(
355
- `Provider ${providerName}: must specify "baseUrl", "headers", "compat", "disableStrictTools", "modelOverrides", "discovery", or "models"`,
174
+ `Provider ${providerName}: must specify "baseUrl", "headers", "apiKey", "compat", "disableStrictTools", "modelOverrides", "discovery", or "models"`,
356
175
  );
357
176
  }
358
177
  }
@@ -0,0 +1,166 @@
1
+ import * as z from "zod/v4";
2
+
3
+ const OpenRouterRoutingSchema = z.object({
4
+ only: z.array(z.string()).optional(),
5
+ order: z.array(z.string()).optional(),
6
+ });
7
+
8
+ const VercelGatewayRoutingSchema = z.object({
9
+ only: z.array(z.string()).optional(),
10
+ order: z.array(z.string()).optional(),
11
+ });
12
+
13
+ const ReasoningEffortMapSchema = z.object({
14
+ minimal: z.string().optional(),
15
+ low: z.string().optional(),
16
+ medium: z.string().optional(),
17
+ high: z.string().optional(),
18
+ xhigh: z.string().optional(),
19
+ });
20
+
21
+ export const OpenAICompatSchema = z.object({
22
+ supportsStore: z.boolean().optional(),
23
+ supportsDeveloperRole: z.boolean().optional(),
24
+ supportsMultipleSystemMessages: z.boolean().optional(),
25
+ supportsReasoningEffort: z.boolean().optional(),
26
+ reasoningEffortMap: ReasoningEffortMapSchema.optional(),
27
+ maxTokensField: z.enum(["max_completion_tokens", "max_tokens"]).optional(),
28
+ supportsUsageInStreaming: z.boolean().optional(),
29
+ requiresToolResultName: z.boolean().optional(),
30
+ requiresMistralToolIds: z.boolean().optional(),
31
+ requiresAssistantAfterToolResult: z.boolean().optional(),
32
+ requiresThinkingAsText: z.boolean().optional(),
33
+ reasoningContentField: z.enum(["reasoning_content", "reasoning", "reasoning_text"]).optional(),
34
+ requiresReasoningContentForToolCalls: z.boolean().optional(),
35
+ allowsSyntheticReasoningContentForToolCalls: z.boolean().optional(),
36
+ requiresAssistantContentForToolCalls: z.boolean().optional(),
37
+ supportsToolChoice: z.boolean().optional(),
38
+ disableReasoningOnForcedToolChoice: z.boolean().optional(),
39
+ disableReasoningOnToolChoice: z.boolean().optional(),
40
+ thinkingFormat: z.enum(["openai", "openrouter", "zai", "qwen", "qwen-chat-template"]).optional(),
41
+ openRouterRouting: OpenRouterRoutingSchema.optional(),
42
+ vercelGatewayRouting: VercelGatewayRoutingSchema.optional(),
43
+ extraBody: z.record(z.string(), z.unknown()).optional(),
44
+ supportsStrictMode: z.boolean().optional(),
45
+ toolStrictMode: z.enum(["all_strict", "none"]).optional(),
46
+ });
47
+
48
+ const EffortSchema = z.enum(["minimal", "low", "medium", "high", "xhigh"]);
49
+
50
+ const ThinkingControlModeSchema = z.enum([
51
+ "effort",
52
+ "budget",
53
+ "google-level",
54
+ "anthropic-adaptive",
55
+ "anthropic-budget-effort",
56
+ ]);
57
+
58
+ const ModelThinkingSchema = z.object({
59
+ minLevel: EffortSchema,
60
+ maxLevel: EffortSchema,
61
+ mode: ThinkingControlModeSchema,
62
+ defaultLevel: EffortSchema.optional(),
63
+ levels: z.array(EffortSchema).optional(),
64
+ });
65
+
66
+ const ModelDefinitionSchema = z.object({
67
+ id: z.string().min(1),
68
+ name: z.string().min(1).optional(),
69
+ api: z
70
+ .enum([
71
+ "openai-completions",
72
+ "openai-responses",
73
+ "openai-codex-responses",
74
+ "azure-openai-responses",
75
+ "anthropic-messages",
76
+ "google-generative-ai",
77
+ "google-vertex",
78
+ ])
79
+ .optional(),
80
+ baseUrl: z.string().min(1).optional(),
81
+ reasoning: z.boolean().optional(),
82
+ thinking: ModelThinkingSchema.optional(),
83
+ input: z.array(z.enum(["text", "image"])).optional(),
84
+ cost: z
85
+ .object({
86
+ input: z.number(),
87
+ output: z.number(),
88
+ cacheRead: z.number(),
89
+ cacheWrite: z.number(),
90
+ })
91
+ .optional(),
92
+ premiumMultiplier: z.number().optional(),
93
+ contextWindow: z.number().optional(),
94
+ maxTokens: z.number().optional(),
95
+ headers: z.record(z.string(), z.string()).optional(),
96
+ compat: OpenAICompatSchema.optional(),
97
+ contextPromotionTarget: z.string().min(1).optional(),
98
+ });
99
+
100
+ export const ModelOverrideSchema = z.object({
101
+ name: z.string().min(1).optional(),
102
+ reasoning: z.boolean().optional(),
103
+ thinking: ModelThinkingSchema.optional(),
104
+ input: z.array(z.enum(["text", "image"])).optional(),
105
+ cost: z
106
+ .object({
107
+ input: z.number().optional(),
108
+ output: z.number().optional(),
109
+ cacheRead: z.number().optional(),
110
+ cacheWrite: z.number().optional(),
111
+ })
112
+ .optional(),
113
+ premiumMultiplier: z.number().optional(),
114
+ contextWindow: z.number().optional(),
115
+ maxTokens: z.number().optional(),
116
+ headers: z.record(z.string(), z.string()).optional(),
117
+ compat: OpenAICompatSchema.optional(),
118
+ contextPromotionTarget: z.string().min(1).optional(),
119
+ });
120
+
121
+ export type ModelOverride = z.infer<typeof ModelOverrideSchema>;
122
+
123
+ export const ProviderDiscoverySchema = z.object({
124
+ type: z.enum(["ollama", "llama.cpp", "lm-studio", "openai-models-list"]),
125
+ });
126
+
127
+ export const ProviderAuthSchema = z.enum(["apiKey", "none", "oauth"]);
128
+
129
+ export type ProviderAuthMode = z.infer<typeof ProviderAuthSchema>;
130
+ export type ProviderDiscovery = z.infer<typeof ProviderDiscoverySchema>;
131
+
132
+ const ProviderConfigSchema = z.object({
133
+ baseUrl: z.string().min(1).optional(),
134
+ apiKey: z.string().min(1).optional(),
135
+ api: z
136
+ .enum([
137
+ "openai-completions",
138
+ "openai-responses",
139
+ "openai-codex-responses",
140
+ "azure-openai-responses",
141
+ "anthropic-messages",
142
+ "google-generative-ai",
143
+ "google-vertex",
144
+ ])
145
+ .optional(),
146
+ headers: z.record(z.string(), z.string()).optional(),
147
+ compat: OpenAICompatSchema.optional(),
148
+ authHeader: z.boolean().optional(),
149
+ auth: ProviderAuthSchema.optional(),
150
+ discovery: ProviderDiscoverySchema.optional(),
151
+ models: z.array(ModelDefinitionSchema).optional(),
152
+ modelOverrides: z.record(z.string(), ModelOverrideSchema).optional(),
153
+ disableStrictTools: z.boolean().optional(),
154
+ });
155
+
156
+ const EquivalenceConfigSchema = z.object({
157
+ overrides: z.record(z.string(), z.string().min(1)).optional(),
158
+ exclude: z.array(z.string().min(1)).optional(),
159
+ });
160
+
161
+ export const ModelsConfigSchema = z.object({
162
+ providers: z.record(z.string(), ProviderConfigSchema).optional(),
163
+ equivalence: EquivalenceConfigSchema.optional(),
164
+ });
165
+
166
+ export type ModelsConfig = z.infer<typeof ModelsConfigSchema>;
@@ -2329,6 +2329,24 @@ export const SETTINGS_SCHEMA = {
2329
2329
  },
2330
2330
  },
2331
2331
 
2332
+ "task.maxRuntimeMs": {
2333
+ type: "number",
2334
+ default: 0,
2335
+ ui: {
2336
+ tab: "tasks",
2337
+ label: "Max Subagent Runtime",
2338
+ description:
2339
+ "Hard wall-clock limit per subagent (ms). 0 disables it. Defense-in-depth against provider-side stream hangs that escape the inference-layer watchdog; triggers a normal subagent abort with a 'timed out' reason.",
2340
+ options: [
2341
+ { value: "0", label: "Unlimited", description: "Default" },
2342
+ { value: "300000", label: "5 minutes" },
2343
+ { value: "900000", label: "15 minutes" },
2344
+ { value: "1800000", label: "30 minutes" },
2345
+ { value: "3600000", label: "1 hour" },
2346
+ ],
2347
+ },
2348
+ },
2349
+
2332
2350
  "task.disabledAgents": {
2333
2351
  type: "array",
2334
2352
  default: [] as string[],
package/src/edit/index.ts CHANGED
@@ -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 } from "@sinclair/typebox";
3
+ import type * as z from "zod/v4";
4
4
  import {
5
5
  executeHashlineSingle,
6
6
  HashlineMismatchError,
@@ -53,7 +53,7 @@ type TInput =
53
53
  | typeof vimSchema
54
54
  | typeof applyPatchSchema;
55
55
 
56
- type VimParams = Static<typeof vimSchema>;
56
+ type VimParams = z.infer<typeof vimSchema>;
57
57
  type EditParams = ReplaceParams | PatchParams | HashlineParams | VimParams | ApplyPatchParams;
58
58
  type EditToolResultDetails = EditToolDetails | VimToolDetails;
59
59
 
@@ -8,19 +8,20 @@
8
8
  * the `patch` mode.
9
9
  */
10
10
 
11
- import { type Static, Type } from "@sinclair/typebox";
11
+ import * as z from "zod/v4";
12
12
  import { parseApplyPatch, parseApplyPatchStreaming } from "../apply-patch/parser";
13
13
  import { ApplyPatchError } from "../diff";
14
14
  import type { PatchEditEntry } from "./patch";
15
15
 
16
- export const applyPatchSchema = Type.Object({
17
- input: Type.String({
18
- description:
16
+ export const applyPatchSchema = z.object({
17
+ input: z
18
+ .string()
19
+ .describe(
19
20
  "Full Codex apply_patch envelope, including '*** Begin Patch' and '*** End Patch'. Contains any mix of Add/Delete/Update (with optional Move to) file operations.",
20
- }),
21
+ ),
21
22
  });
22
23
 
23
- export type ApplyPatchParams = Static<typeof applyPatchSchema>;
24
+ export type ApplyPatchParams = z.infer<typeof applyPatchSchema>;
24
25
 
25
26
  export type ApplyPatchEntry = PatchEditEntry & { path: string };
26
27
 
@@ -8,9 +8,8 @@
8
8
  import * as fs from "node:fs";
9
9
  import * as path from "node:path";
10
10
  import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
11
- import { StringEnum } from "@oh-my-pi/pi-ai";
12
11
  import { isEnoent } from "@oh-my-pi/pi-utils";
13
- import { type Static, Type } from "@sinclair/typebox";
12
+ import * as z from "zod/v4";
14
13
  import {
15
14
  type FileDiagnosticsResult,
16
15
  flushLspWritethroughBatch,
@@ -1577,29 +1576,23 @@ export async function computePatchDiff(
1577
1576
  }
1578
1577
  }
1579
1578
 
1580
- export const patchEditEntrySchema = Type.Object(
1581
- {
1582
- op: Type.Optional(
1583
- StringEnum(["create", "delete", "update"], {
1584
- description: "Operation (default: update)",
1585
- }),
1586
- ),
1587
- rename: Type.Optional(Type.String({ description: "New path for move" })),
1588
- diff: Type.Optional(Type.String({ description: "Diff hunks (update) or full content (create)" })),
1589
- },
1590
- { additionalProperties: false },
1591
- );
1592
-
1593
- export const patchEditSchema = Type.Object(
1594
- {
1595
- path: Type.String({ description: "file path for edits" }),
1596
- edits: Type.Array(patchEditEntrySchema, { description: "Patch operations", minItems: 1 }),
1597
- },
1598
- { additionalProperties: false },
1599
- );
1600
-
1601
- export type PatchEditEntry = Static<typeof patchEditEntrySchema>;
1602
- export type PatchParams = Static<typeof patchEditSchema>;
1579
+ export const patchEditEntrySchema = z
1580
+ .object({
1581
+ op: z.enum(["create", "delete", "update"]).optional().describe("Operation (default: update)"),
1582
+ rename: z.string().describe("New path for move").optional(),
1583
+ diff: z.string().describe("Diff hunks (update) or full content (create)").optional(),
1584
+ })
1585
+ .strict();
1586
+
1587
+ export const patchEditSchema = z
1588
+ .object({
1589
+ path: z.string().describe("file path for edits"),
1590
+ edits: z.array(patchEditEntrySchema).min(1).describe("Patch operations"),
1591
+ })
1592
+ .strict();
1593
+
1594
+ export type PatchEditEntry = z.infer<typeof patchEditEntrySchema>;
1595
+ export type PatchParams = z.infer<typeof patchEditSchema>;
1603
1596
 
1604
1597
  export interface ExecutePatchSingleOptions {
1605
1598
  session: ToolSession;
@@ -5,7 +5,7 @@
5
5
  * fallback strategies for finding text in files.
6
6
  */
7
7
  import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
8
- import { type Static, Type } from "@sinclair/typebox";
8
+ import * as z from "zod/v4";
9
9
  import type { WritethroughCallback, WritethroughDeferredHandle } from "../../lsp";
10
10
  import type { ToolSession } from "../../tools";
11
11
  import { invalidateFsScanAfterWrite } from "../../tools/fs-cache-invalidation";
@@ -976,25 +976,23 @@ export function findContextLine(
976
976
  return { index: undefined, confidence: bestScore };
977
977
  }
978
978
 
979
- export const replaceEditEntrySchema = Type.Object(
980
- {
981
- old_text: Type.String({ description: "Text to find (fuzzy whitespace matching enabled)" }),
982
- new_text: Type.String({ description: "Replacement text" }),
983
- all: Type.Optional(Type.Boolean({ description: "Replace all occurrences (default: unique match required)" })),
984
- },
985
- { additionalProperties: false },
986
- );
987
-
988
- export const replaceEditSchema = Type.Object(
989
- {
990
- path: Type.String({ description: "file path for edits" }),
991
- edits: Type.Array(replaceEditEntrySchema, { description: "Replacements", minItems: 1 }),
992
- },
993
- { additionalProperties: false },
994
- );
995
-
996
- export type ReplaceEditEntry = Static<typeof replaceEditEntrySchema>;
997
- export type ReplaceParams = Static<typeof replaceEditSchema>;
979
+ export const replaceEditEntrySchema = z
980
+ .object({
981
+ old_text: z.string().describe("Text to find (fuzzy whitespace matching enabled)"),
982
+ new_text: z.string().describe("Replacement text"),
983
+ all: z.boolean().describe("Replace all occurrences (default: unique match required)").optional(),
984
+ })
985
+ .strict();
986
+
987
+ export const replaceEditSchema = z
988
+ .object({
989
+ path: z.string().describe("file path for edits"),
990
+ edits: z.array(replaceEditEntrySchema).min(1).describe("Replacements"),
991
+ })
992
+ .strict();
993
+
994
+ export type ReplaceEditEntry = z.infer<typeof replaceEditEntrySchema>;
995
+ export type ReplaceParams = z.infer<typeof replaceEditSchema>;
998
996
 
999
997
  export interface ExecuteReplaceSingleOptions {
1000
998
  session: ToolSession;