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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/dist/cli.js +2027 -1396
  3. package/dist/types/advisor/advise-tool.d.ts +31 -19
  4. package/dist/types/autoresearch/tools/init-experiment.d.ts +13 -17
  5. package/dist/types/autoresearch/tools/log-experiment.d.ts +17 -19
  6. package/dist/types/autoresearch/tools/run-experiment.d.ts +3 -4
  7. package/dist/types/autoresearch/tools/update-notes.d.ts +4 -5
  8. package/dist/types/cli/args.d.ts +1 -0
  9. package/dist/types/cli/bench-cli.d.ts +6 -0
  10. package/dist/types/cli/ttsr-cli.d.ts +39 -0
  11. package/dist/types/commands/launch.d.ts +3 -0
  12. package/dist/types/commands/ttsr.d.ts +57 -0
  13. package/dist/types/commit/agentic/tools/analyze-file.d.ts +4 -5
  14. package/dist/types/commit/agentic/tools/git-file-diff.d.ts +4 -5
  15. package/dist/types/commit/agentic/tools/git-hunk.d.ts +5 -6
  16. package/dist/types/commit/agentic/tools/git-overview.d.ts +4 -5
  17. package/dist/types/commit/agentic/tools/propose-changelog.d.ts +23 -24
  18. package/dist/types/commit/agentic/tools/propose-commit.d.ts +11 -32
  19. package/dist/types/commit/agentic/tools/recent-commits.d.ts +3 -4
  20. package/dist/types/commit/agentic/tools/schemas.d.ts +6 -27
  21. package/dist/types/commit/agentic/tools/split-commit.d.ts +28 -49
  22. package/dist/types/commit/changelog/generate.d.ts +12 -13
  23. package/dist/types/commit/shared-llm.d.ts +10 -37
  24. package/dist/types/config/config-file.d.ts +4 -4
  25. package/dist/types/config/keybindings.d.ts +5 -0
  26. package/dist/types/config/models-config-schema.d.ts +625 -990
  27. package/dist/types/config/models-config.d.ts +229 -217
  28. package/dist/types/config/settings-schema.d.ts +144 -25
  29. package/dist/types/edit/hashline/params.d.ts +7 -11
  30. package/dist/types/edit/index.d.ts +2 -1
  31. package/dist/types/edit/modes/apply-patch.d.ts +4 -5
  32. package/dist/types/edit/modes/patch.d.ts +15 -24
  33. package/dist/types/edit/modes/replace.d.ts +16 -17
  34. package/dist/types/eval/js/index.d.ts +1 -0
  35. package/dist/types/extensibility/custom-commands/types.d.ts +6 -3
  36. package/dist/types/extensibility/custom-tools/types.d.ts +8 -5
  37. package/dist/types/extensibility/extensions/runner.d.ts +5 -2
  38. package/dist/types/extensibility/extensions/types.d.ts +14 -10
  39. package/dist/types/extensibility/hooks/types.d.ts +7 -4
  40. package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +13 -5
  41. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +17 -0
  42. package/dist/types/extensibility/shared-events.d.ts +22 -1
  43. package/dist/types/extensibility/typebox.d.ts +80 -58
  44. package/dist/types/goals/tools/goal-tool.d.ts +11 -24
  45. package/dist/types/index.d.ts +2 -0
  46. package/dist/types/lsp/index.d.ts +11 -26
  47. package/dist/types/lsp/types.d.ts +12 -28
  48. package/dist/types/main.d.ts +1 -0
  49. package/dist/types/mcp/client.d.ts +8 -0
  50. package/dist/types/modes/components/btw-panel.d.ts +1 -0
  51. package/dist/types/modes/components/custom-editor.d.ts +3 -1
  52. package/dist/types/modes/components/status-line/component.d.ts +1 -1
  53. package/dist/types/modes/components/status-line/context-thresholds.d.ts +0 -1
  54. package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
  55. package/dist/types/modes/controllers/input-controller.d.ts +1 -0
  56. package/dist/types/modes/interactive-mode.d.ts +3 -0
  57. package/dist/types/modes/rpc/rpc-types.d.ts +1 -1
  58. package/dist/types/modes/setup-wizard/index.d.ts +1 -0
  59. package/dist/types/modes/setup-wizard/startup-splash.d.ts +7 -0
  60. package/dist/types/modes/theme/theme.d.ts +1 -1
  61. package/dist/types/modes/types.d.ts +3 -0
  62. package/dist/types/modes/utils/context-usage.d.ts +12 -0
  63. package/dist/types/sdk.d.ts +8 -1
  64. package/dist/types/session/agent-session.d.ts +24 -0
  65. package/dist/types/session/session-persistence.d.ts +4 -0
  66. package/dist/types/startup-splash.d.ts +12 -0
  67. package/dist/types/task/types.d.ts +47 -48
  68. package/dist/types/tools/ask.d.ts +26 -27
  69. package/dist/types/tools/ast-edit.d.ts +17 -17
  70. package/dist/types/tools/ast-grep.d.ts +12 -13
  71. package/dist/types/tools/bash.d.ts +20 -17
  72. package/dist/types/tools/browser.d.ts +46 -71
  73. package/dist/types/tools/checkpoint.d.ts +14 -15
  74. package/dist/types/tools/debug.d.ts +82 -145
  75. package/dist/types/tools/eval.d.ts +30 -40
  76. package/dist/types/tools/find.d.ts +17 -18
  77. package/dist/types/tools/gh.d.ts +49 -78
  78. package/dist/types/tools/image-gen.d.ts +20 -36
  79. package/dist/types/tools/inspect-image.d.ts +10 -11
  80. package/dist/types/tools/irc.d.ts +22 -33
  81. package/dist/types/tools/job.d.ts +11 -12
  82. package/dist/types/tools/learn.d.ts +21 -28
  83. package/dist/types/tools/manage-skill.d.ts +13 -22
  84. package/dist/types/tools/memory-edit.d.ts +15 -24
  85. package/dist/types/tools/memory-recall.d.ts +7 -8
  86. package/dist/types/tools/memory-reflect.d.ts +9 -10
  87. package/dist/types/tools/memory-retain.d.ts +13 -14
  88. package/dist/types/tools/read.d.ts +8 -8
  89. package/dist/types/tools/resolve.d.ts +11 -18
  90. package/dist/types/tools/review.d.ts +9 -15
  91. package/dist/types/tools/search-tool-bm25.d.ts +9 -10
  92. package/dist/types/tools/search.d.ts +16 -17
  93. package/dist/types/tools/ssh.d.ts +14 -15
  94. package/dist/types/tools/todo.d.ts +27 -43
  95. package/dist/types/tools/tts.d.ts +8 -9
  96. package/dist/types/tools/write.d.ts +9 -10
  97. package/dist/types/tui/code-cell.d.ts +2 -0
  98. package/dist/types/tui/index.d.ts +1 -0
  99. package/dist/types/tui/width-aware-text.d.ts +23 -0
  100. package/dist/types/utils/image-vision-fallback.d.ts +28 -0
  101. package/dist/types/utils/markit.d.ts +10 -1
  102. package/dist/types/web/search/index.d.ts +17 -28
  103. package/dist/types/web/search/providers/base.d.ts +1 -0
  104. package/dist/types/web/search/providers/gemini.d.ts +1 -0
  105. package/dist/types/web/search/providers/perplexity.d.ts +0 -2
  106. package/dist/types/web/search/types.d.ts +32 -26
  107. package/package.json +14 -13
  108. package/scripts/omp +1 -1
  109. package/src/advisor/__tests__/advisor.test.ts +103 -1
  110. package/src/advisor/advise-tool.ts +47 -11
  111. package/src/autoresearch/tools/init-experiment.ts +13 -16
  112. package/src/autoresearch/tools/log-experiment.ts +15 -18
  113. package/src/autoresearch/tools/run-experiment.ts +3 -3
  114. package/src/autoresearch/tools/update-notes.ts +4 -4
  115. package/src/cli/args.ts +1 -0
  116. package/src/cli/bench-cli.ts +30 -7
  117. package/src/cli/flag-tables.ts +8 -0
  118. package/src/cli/ttsr-cli.ts +995 -0
  119. package/src/cli-commands.ts +1 -0
  120. package/src/cli.ts +7 -1
  121. package/src/collab/host.ts +2 -2
  122. package/src/commands/launch.ts +3 -0
  123. package/src/commands/ttsr.ts +125 -0
  124. package/src/commit/agentic/tools/analyze-file.ts +4 -4
  125. package/src/commit/agentic/tools/git-file-diff.ts +4 -4
  126. package/src/commit/agentic/tools/git-hunk.ts +7 -5
  127. package/src/commit/agentic/tools/git-overview.ts +4 -4
  128. package/src/commit/agentic/tools/propose-changelog.ts +18 -15
  129. package/src/commit/agentic/tools/propose-commit.ts +6 -6
  130. package/src/commit/agentic/tools/recent-commits.ts +3 -3
  131. package/src/commit/agentic/tools/schemas.ts +8 -20
  132. package/src/commit/agentic/tools/split-commit.ts +19 -23
  133. package/src/commit/analysis/summary.ts +7 -5
  134. package/src/commit/changelog/generate.ts +15 -11
  135. package/src/commit/shared-llm.ts +17 -24
  136. package/src/config/config-file.ts +13 -15
  137. package/src/config/keybindings.ts +6 -0
  138. package/src/config/models-config-schema.ts +206 -179
  139. package/src/config/settings-schema.ts +118 -2
  140. package/src/discovery/builtin-rules/index.ts +2 -0
  141. package/src/discovery/builtin-rules/ts-import-type.md +2 -2
  142. package/src/discovery/builtin-rules/ts-no-any.md +11 -2
  143. package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +55 -0
  144. package/src/edit/hashline/params.ts +12 -11
  145. package/src/edit/index.ts +5 -4
  146. package/src/edit/modes/apply-patch.ts +4 -4
  147. package/src/edit/modes/patch.ts +15 -18
  148. package/src/edit/modes/replace.ts +13 -17
  149. package/src/edit/renderer.ts +0 -1
  150. package/src/eval/agent-bridge.ts +11 -13
  151. package/src/eval/completion-bridge.ts +25 -17
  152. package/src/eval/js/context-manager.ts +17 -2
  153. package/src/eval/js/index.ts +1 -1
  154. package/src/eval/py/executor.ts +2 -2
  155. package/src/eval/py/runner.py +44 -0
  156. package/src/extensibility/custom-commands/loader.ts +5 -3
  157. package/src/extensibility/custom-commands/types.ts +6 -3
  158. package/src/extensibility/custom-tools/loader.ts +4 -2
  159. package/src/extensibility/custom-tools/types.ts +8 -5
  160. package/src/extensibility/extensions/loader.ts +4 -2
  161. package/src/extensibility/extensions/runner.ts +20 -2
  162. package/src/extensibility/extensions/types.ts +22 -8
  163. package/src/extensibility/hooks/loader.ts +5 -2
  164. package/src/extensibility/hooks/types.ts +7 -4
  165. package/src/extensibility/legacy-pi-ai-shim.ts +42 -5
  166. package/src/extensibility/legacy-pi-coding-agent-shim.ts +113 -0
  167. package/src/extensibility/plugins/legacy-pi-compat.ts +13 -13
  168. package/src/extensibility/shared-events.ts +24 -0
  169. package/src/extensibility/tool-proxy.ts +4 -1
  170. package/src/extensibility/typebox.ts +778 -251
  171. package/src/goals/guided-setup.ts +12 -3
  172. package/src/goals/tools/goal-tool.ts +6 -6
  173. package/src/index.ts +2 -0
  174. package/src/internal-urls/docs-index.generated.ts +15 -13
  175. package/src/lsp/types.ts +13 -27
  176. package/src/main.ts +29 -21
  177. package/src/mcp/client.ts +38 -13
  178. package/src/mcp/render.ts +102 -89
  179. package/src/modes/components/agent-hub.ts +11 -4
  180. package/src/modes/components/branch-summary-message.ts +1 -0
  181. package/src/modes/components/btw-panel.ts +5 -1
  182. package/src/modes/components/collab-prompt-message.ts +9 -7
  183. package/src/modes/components/compaction-summary-message.ts +1 -0
  184. package/src/modes/components/custom-editor.ts +18 -0
  185. package/src/modes/components/custom-message.ts +1 -0
  186. package/src/modes/components/footer.ts +6 -5
  187. package/src/modes/components/hook-message.ts +1 -0
  188. package/src/modes/components/read-tool-group.ts +9 -3
  189. package/src/modes/components/skill-message.ts +1 -0
  190. package/src/modes/components/status-line/component.ts +139 -15
  191. package/src/modes/components/status-line/context-thresholds.ts +0 -1
  192. package/src/modes/components/todo-reminder.ts +1 -0
  193. package/src/modes/components/tool-execution.ts +17 -10
  194. package/src/modes/components/ttsr-notification.ts +1 -0
  195. package/src/modes/components/user-message.ts +6 -6
  196. package/src/modes/controllers/btw-controller.ts +69 -1
  197. package/src/modes/controllers/event-controller.ts +2 -7
  198. package/src/modes/controllers/input-controller.ts +29 -0
  199. package/src/modes/controllers/selector-controller.ts +10 -3
  200. package/src/modes/interactive-mode.ts +42 -10
  201. package/src/modes/rpc/rpc-types.ts +1 -1
  202. package/src/modes/setup-wizard/index.ts +1 -0
  203. package/src/modes/setup-wizard/scenes/sign-in.ts +77 -5
  204. package/src/modes/setup-wizard/startup-splash.ts +107 -0
  205. package/src/modes/theme/theme.ts +133 -143
  206. package/src/modes/types.ts +3 -0
  207. package/src/modes/utils/context-usage.ts +37 -20
  208. package/src/modes/utils/hotkeys-markdown.ts +1 -0
  209. package/src/prompts/system/system-prompt.md +1 -0
  210. package/src/prompts/tools/image-attachment-describe-system.md +8 -0
  211. package/src/prompts/tools/image-attachment-describe.md +10 -0
  212. package/src/sdk.ts +35 -22
  213. package/src/session/agent-session.ts +715 -255
  214. package/src/session/session-history-format.ts +11 -2
  215. package/src/session/session-loader.ts +19 -32
  216. package/src/session/session-persistence.ts +27 -11
  217. package/src/session/snapcompact-inline.ts +1 -1
  218. package/src/slash-commands/builtin-registry.ts +4 -11
  219. package/src/ssh/connection-manager.ts +3 -2
  220. package/src/startup-splash.ts +19 -0
  221. package/src/task/executor.ts +12 -7
  222. package/src/task/types.ts +44 -41
  223. package/src/tool-discovery/tool-index.ts +17 -4
  224. package/src/tools/ask.ts +14 -14
  225. package/src/tools/ast-edit.ts +17 -14
  226. package/src/tools/ast-grep.ts +10 -9
  227. package/src/tools/bash.ts +15 -10
  228. package/src/tools/browser/launch.ts +13 -0
  229. package/src/tools/browser.ts +26 -32
  230. package/src/tools/checkpoint.ts +7 -7
  231. package/src/tools/debug.ts +72 -69
  232. package/src/tools/eval.ts +18 -19
  233. package/src/tools/find.ts +20 -13
  234. package/src/tools/gh.ts +29 -49
  235. package/src/tools/image-gen.ts +94 -57
  236. package/src/tools/inspect-image.ts +8 -9
  237. package/src/tools/irc.ts +12 -12
  238. package/src/tools/job.ts +6 -6
  239. package/src/tools/learn.ts +11 -14
  240. package/src/tools/manage-skill.ts +19 -23
  241. package/src/tools/memory-edit.ts +8 -8
  242. package/src/tools/memory-recall.ts +4 -4
  243. package/src/tools/memory-reflect.ts +5 -5
  244. package/src/tools/memory-retain.ts +9 -11
  245. package/src/tools/puppeteer/02_stealth_hairline.txt +1 -1
  246. package/src/tools/puppeteer/04_stealth_iframe.txt +4 -4
  247. package/src/tools/puppeteer/05_stealth_webgl.txt +1 -1
  248. package/src/tools/puppeteer/10_stealth_plugins.txt +6 -4
  249. package/src/tools/puppeteer/12_stealth_codecs.txt +2 -2
  250. package/src/tools/puppeteer/13_stealth_worker.txt +1 -1
  251. package/src/tools/read.ts +197 -19
  252. package/src/tools/report-tool-issue.ts +6 -6
  253. package/src/tools/resolve.ts +6 -6
  254. package/src/tools/review.ts +10 -12
  255. package/src/tools/search-tool-bm25.ts +5 -5
  256. package/src/tools/search.ts +20 -29
  257. package/src/tools/ssh.ts +8 -8
  258. package/src/tools/todo.ts +16 -19
  259. package/src/tools/tts.ts +16 -15
  260. package/src/tools/write.ts +5 -5
  261. package/src/tui/code-cell.ts +44 -3
  262. package/src/tui/index.ts +1 -0
  263. package/src/tui/width-aware-text.ts +58 -0
  264. package/src/utils/image-vision-fallback.ts +197 -0
  265. package/src/utils/markit.ts +17 -2
  266. package/src/web/search/index.ts +21 -9
  267. package/src/web/search/providers/base.ts +1 -0
  268. package/src/web/search/providers/gemini.ts +56 -18
  269. package/src/web/search/providers/perplexity.ts +373 -126
  270. package/src/web/search/types.ts +28 -48
@@ -1,8 +1,8 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { getAgentDir, isEnoent, logger } from "@oh-my-pi/pi-utils";
4
+ import type { Type } from "arktype";
4
5
  import { JSONC, YAML } from "bun";
5
- import type { ZodType } from "zod/v4";
6
6
 
7
7
  /** Minimal subset of the AJV ConfigSchemaError shape this module actually relies on. */
8
8
  interface ConfigSchemaError {
@@ -55,7 +55,7 @@ function migrateJsonToYml(jsonPath: string, ymlPath: string) {
55
55
 
56
56
  export interface IConfigFile<T> {
57
57
  readonly id: string;
58
- readonly schema: ZodType<T>;
58
+ readonly schema: Type;
59
59
  path?(): string;
60
60
  load(): T | null;
61
61
  invalidate?(): void;
@@ -129,7 +129,7 @@ export class ConfigFile<T> implements IConfigFile<T> {
129
129
 
130
130
  constructor(
131
131
  readonly id: string,
132
- readonly schema: ZodType<T>,
132
+ readonly schema: Type,
133
133
  configPath: string = path.join(getAgentDir(), `${id}.yml`),
134
134
  ) {
135
135
  this.#basePath = configPath;
@@ -193,10 +193,10 @@ export class ConfigFile<T> implements IConfigFile<T> {
193
193
  }
194
194
 
195
195
  createDefault(): T {
196
- const parsed = this.schema.safeParse({});
197
- if (parsed.success) return parsed.data;
198
- const fallback = this.schema.safeParse(undefined);
199
- if (fallback.success) return fallback.data;
196
+ const parsed = this.schema({});
197
+ if (!(parsed instanceof Error)) return parsed as T;
198
+ const fallback = this.schema(undefined);
199
+ if (!(fallback instanceof Error)) return fallback as T;
200
200
  throw new ConfigError(this.id, undefined, {
201
201
  err: new Error("Schema produced no default value"),
202
202
  stage: "createDefault",
@@ -219,19 +219,17 @@ export class ConfigFile<T> implements IConfigFile<T> {
219
219
  throw new Error(`Invalid config file path: ${this.#basePath}`);
220
220
  }
221
221
 
222
- const checked = this.schema.safeParse(parsed);
223
- if (!checked.success) {
222
+ const checked = this.schema(parsed);
223
+ if (checked instanceof Error) {
224
224
  const schemaErrors: ConfigSchemaError[] = [];
225
- for (const issue of checked.error.issues) {
226
- const instancePath = issue.path.length === 0 ? "" : `/${issue.path.map(String).join("/")}`;
227
- schemaErrors.push({ instancePath, message: issue.message });
228
- if (schemaErrors.length >= 50) break;
229
- }
225
+ // arktype errors are Error instances with a message property
226
+ // Extract the error message as a single schema error
227
+ schemaErrors.push({ instancePath: "root", message: checked.message });
230
228
  const error = new ConfigError(this.id, schemaErrors);
231
229
  logger.warn("Failed to parse config file", { path: this.path(), error });
232
230
  return this.#storeCache({ error, status: "error" });
233
231
  }
234
- const value = checked.data;
232
+ const value = checked as T;
235
233
  try {
236
234
  this.#auxValidate?.(value);
237
235
  } catch (error) {
@@ -31,6 +31,7 @@ interface AppKeybindings {
31
31
  "app.tools.expand": true;
32
32
  "app.editor.external": true;
33
33
  "app.message.followUp": true;
34
+ "app.retry": true;
34
35
  "app.message.dequeue": true;
35
36
  "app.clipboard.pasteImage": true;
36
37
  "app.clipboard.pasteTextRaw": true;
@@ -131,6 +132,10 @@ export const KEYBINDINGS = {
131
132
  defaultKeys: ["ctrl+q", "ctrl+enter"],
132
133
  description: "Send follow-up message",
133
134
  },
135
+ "app.retry": {
136
+ defaultKeys: "alt+r",
137
+ description: "Retry last failed assistant turn",
138
+ },
134
139
  "app.message.dequeue": {
135
140
  defaultKeys: "alt+up",
136
141
  description: "Dequeue message",
@@ -238,6 +243,7 @@ const KEYBINDING_NAME_MIGRATIONS = {
238
243
  toggleThinking: "app.thinking.toggle",
239
244
  externalEditor: "app.editor.external",
240
245
  followUp: "app.message.followUp",
246
+ retry: "app.retry",
241
247
  dequeue: "app.message.dequeue",
242
248
  pasteImage: "app.clipboard.pasteImage",
243
249
  pasteTextRaw: "app.clipboard.pasteTextRaw",
@@ -1,73 +1,72 @@
1
- import { z } from "zod/v4";
1
+ import { type } from "arktype";
2
2
 
3
- const OpenRouterRoutingSchema = z.object({
4
- only: z.array(z.string()).optional(),
5
- order: z.array(z.string()).optional(),
3
+ const OpenRouterRoutingSchema = type({
4
+ "only?": "string[]",
5
+ "order?": "string[]",
6
6
  });
7
7
 
8
- const VercelGatewayRoutingSchema = z.object({
9
- only: z.array(z.string()).optional(),
10
- order: z.array(z.string()).optional(),
8
+ const VercelGatewayRoutingSchema = type({
9
+ "only?": "string[]",
10
+ "order?": "string[]",
11
11
  });
12
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(),
13
+ const ReasoningEffortMapSchema = type({
14
+ "minimal?": "string",
15
+ "low?": "string",
16
+ "medium?": "string",
17
+ "high?": "string",
18
+ "xhigh?": "string",
19
19
  });
20
20
 
21
- const OpenAICompatFieldsSchema = 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
- supportsForcedToolChoice: z.boolean().optional(),
39
- disableReasoningOnForcedToolChoice: z.boolean().optional(),
40
- disableReasoningOnToolChoice: z.boolean().optional(),
41
- thinkingFormat: z.enum(["openai", "openrouter", "zai", "qwen", "qwen-chat-template"]).optional(),
42
- openRouterRouting: OpenRouterRoutingSchema.optional(),
43
- vercelGatewayRouting: VercelGatewayRoutingSchema.optional(),
44
- extraBody: z.record(z.string(), z.unknown()).optional(),
45
- cacheControlFormat: z.enum(["anthropic"]).optional(),
46
- supportsStrictMode: z.boolean().optional(),
47
- toolStrictMode: z.enum(["all_strict", "none"]).optional(),
48
- streamIdleTimeoutMs: z.number().nonnegative().optional(),
49
- supportsLongPromptCacheRetention: z.boolean().optional(),
50
- supportsReasoningParams: z.boolean().optional(),
51
- alwaysSendMaxTokens: z.boolean().optional(),
52
- strictResponsesPairing: z.boolean().optional(),
21
+ const OpenAICompatFields = {
22
+ "supportsStore?": "boolean",
23
+ "supportsDeveloperRole?": "boolean",
24
+ "supportsMultipleSystemMessages?": "boolean",
25
+ "supportsReasoningEffort?": "boolean",
26
+ "reasoningEffortMap?": ReasoningEffortMapSchema,
27
+ "maxTokensField?": '"max_completion_tokens" | "max_tokens"',
28
+ "supportsUsageInStreaming?": "boolean",
29
+ "requiresToolResultName?": "boolean",
30
+ "requiresMistralToolIds?": "boolean",
31
+ "requiresAssistantAfterToolResult?": "boolean",
32
+ "requiresThinkingAsText?": "boolean",
33
+ "reasoningContentField?": '"reasoning_content" | "reasoning" | "reasoning_text"',
34
+ "requiresReasoningContentForToolCalls?": "boolean",
35
+ "allowsSyntheticReasoningContentForToolCalls?": "boolean",
36
+ "requiresAssistantContentForToolCalls?": "boolean",
37
+ "supportsToolChoice?": "boolean",
38
+ "supportsForcedToolChoice?": "boolean",
39
+ "disableReasoningOnForcedToolChoice?": "boolean",
40
+ "disableReasoningOnToolChoice?": "boolean",
41
+ "thinkingFormat?": '"openai" | "openrouter" | "zai" | "qwen" | "qwen-chat-template"',
42
+ "openRouterRouting?": OpenRouterRoutingSchema,
43
+ "vercelGatewayRouting?": VercelGatewayRoutingSchema,
44
+ "extraBody?": { "[string]": "unknown" },
45
+ "cacheControlFormat?": '"anthropic"',
46
+ "supportsStrictMode?": "boolean",
47
+ "toolStrictMode?": '"all_strict" | "none"',
48
+ "streamIdleTimeoutMs?": "number >= 0",
49
+ "supportsLongPromptCacheRetention?": "boolean",
50
+ "supportsReasoningParams?": "boolean",
51
+ "alwaysSendMaxTokens?": "boolean",
52
+ "strictResponsesPairing?": "boolean",
53
53
  // anthropic-messages compat flags (same `compat` slot, per-api interpretation)
54
- requiresToolResultId: z.boolean().optional(),
55
- replayUnsignedThinking: z.boolean().optional(),
56
- });
54
+ "requiresToolResultId?": "boolean",
55
+ "replayUnsignedThinking?": "boolean",
56
+ } as const;
57
+
58
+ const OpenAICompatFieldsSchema = type(OpenAICompatFields);
57
59
 
58
- export const OpenAICompatSchema = OpenAICompatFieldsSchema.extend({
59
- whenThinking: OpenAICompatFieldsSchema.optional(),
60
+ export const OpenAICompatSchema = type({
61
+ ...OpenAICompatFields,
62
+ "whenThinking?": OpenAICompatFieldsSchema,
60
63
  });
61
64
 
62
- const EffortSchema = z.enum(["minimal", "low", "medium", "high", "xhigh"]);
65
+ const EffortSchema = type('"minimal" | "low" | "medium" | "high" | "xhigh"');
63
66
 
64
- const ThinkingControlModeSchema = z.enum([
65
- "effort",
66
- "budget",
67
- "google-level",
68
- "anthropic-adaptive",
69
- "anthropic-budget-effort",
70
- ]);
67
+ const ThinkingControlModeSchema = type(
68
+ '"effort" | "budget" | "google-level" | "anthropic-adaptive" | "anthropic-budget-effort"',
69
+ );
71
70
 
72
71
  const EFFORT_ORDER = ["minimal", "low", "medium", "high", "xhigh"] as const;
73
72
 
@@ -77,137 +76,141 @@ const EFFORT_ORDER = ["minimal", "low", "medium", "high", "xhigh"] as const;
77
76
  * `ThinkingConfig` (ordered `efforts`, never empty). Precedence mirrors the
78
77
  * old runtime: explicit `levels` beat the min..max range; `efforts` beats both.
79
78
  */
80
- const ModelThinkingSchema = z
81
- .object({
82
- mode: ThinkingControlModeSchema,
83
- efforts: z.array(EffortSchema).min(1).optional(),
84
- defaultLevel: EffortSchema.optional(),
85
- effortMap: ReasoningEffortMapSchema.optional(),
86
- supportsDisplay: z.boolean().optional(),
87
- // Legacy range vocabulary (pre-efforts configs).
88
- minLevel: EffortSchema.optional(),
89
- maxLevel: EffortSchema.optional(),
90
- levels: z.array(EffortSchema).min(1).optional(),
91
- })
92
- .refine(
93
- value =>
79
+ const ModelThinkingSchema = type({
80
+ mode: ThinkingControlModeSchema,
81
+ "efforts?": EffortSchema.array(),
82
+ "defaultLevel?": EffortSchema,
83
+ "effortMap?": ReasoningEffortMapSchema,
84
+ "supportsDisplay?": "boolean",
85
+ // Legacy range vocabulary (pre-efforts configs).
86
+ "minLevel?": EffortSchema,
87
+ "maxLevel?": EffortSchema,
88
+ "levels?": EffortSchema.array(),
89
+ })
90
+ .narrow(
91
+ (value, ctx) =>
94
92
  value.efforts !== undefined ||
95
93
  value.levels !== undefined ||
96
- (value.minLevel !== undefined && value.maxLevel !== undefined),
97
- {
98
- message: "thinking requires `efforts` (or legacy `levels`/`minLevel`+`maxLevel`)",
99
- },
94
+ (value.minLevel !== undefined && value.maxLevel !== undefined) ||
95
+ ctx.mustBe("thinking with `efforts` (or legacy `levels`/`minLevel`+`maxLevel`)"),
100
96
  )
101
- .transform(({ efforts, levels, minLevel, maxLevel, mode, defaultLevel, effortMap, supportsDisplay }) => {
102
- let resolved = efforts ?? levels;
97
+ .pipe((value: any) => {
98
+ let resolved = value.efforts ?? value.levels;
103
99
  if (!resolved) {
104
- const minIndex = EFFORT_ORDER.indexOf(minLevel!);
105
- const maxIndex = EFFORT_ORDER.indexOf(maxLevel!);
100
+ const minIndex = EFFORT_ORDER.indexOf(value.minLevel!);
101
+ const maxIndex = EFFORT_ORDER.indexOf(value.maxLevel!);
106
102
  resolved = EFFORT_ORDER.slice(minIndex, Math.max(minIndex, maxIndex) + 1);
107
103
  }
108
104
  return {
109
- mode,
105
+ mode: value.mode,
110
106
  efforts: resolved,
111
- ...(defaultLevel !== undefined && { defaultLevel }),
112
- ...(effortMap !== undefined && { effortMap }),
113
- ...(supportsDisplay !== undefined && { supportsDisplay }),
107
+ ...(value.defaultLevel !== undefined && { defaultLevel: value.defaultLevel }),
108
+ ...(value.effortMap !== undefined && { effortMap: value.effortMap }),
109
+ ...(value.supportsDisplay !== undefined && { supportsDisplay: value.supportsDisplay }),
114
110
  };
115
111
  });
116
112
 
117
- const ModelDefinitionSchema = z.object({
118
- id: z.string().min(1),
119
- name: z.string().min(1).optional(),
120
- api: z
121
- .enum([
122
- "openai-completions",
123
- "openai-responses",
124
- "openai-codex-responses",
125
- "azure-openai-responses",
126
- "anthropic-messages",
127
- "google-generative-ai",
128
- "google-gemini-cli",
129
- "google-vertex",
130
- ])
131
- .optional(),
132
- baseUrl: z.string().min(1).optional(),
133
- reasoning: z.boolean().optional(),
134
- thinking: ModelThinkingSchema.optional(),
135
- input: z.array(z.enum(["text", "image"])).optional(),
136
- supportsTools: z.boolean().optional(),
137
- cost: z
138
- .object({
139
- input: z.number(),
140
- output: z.number(),
141
- cacheRead: z.number(),
142
- cacheWrite: z.number(),
143
- })
144
- .optional(),
145
- premiumMultiplier: z.number().optional(),
146
- contextWindow: z.number().optional(),
147
- maxTokens: z.number().optional(),
148
- omitMaxOutputTokens: z.boolean().optional(),
149
- headers: z.record(z.string(), z.string()).optional(),
150
- compat: OpenAICompatSchema.optional(),
151
- contextPromotionTarget: z.string().min(1).optional(),
113
+ const ModelDefinitionSchema = type({
114
+ id: "string",
115
+ "name?": "string",
116
+ "api?":
117
+ '"openai-completions" | "openai-responses" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "google-generative-ai" | "google-gemini-cli" | "google-vertex"',
118
+ "baseUrl?": "string",
119
+ "reasoning?": "boolean",
120
+ "thinking?": ModelThinkingSchema,
121
+ "input?": '("text" | "image")[]',
122
+ "supportsTools?": "boolean",
123
+ "cost?": {
124
+ input: "number",
125
+ output: "number",
126
+ cacheRead: "number",
127
+ cacheWrite: "number",
128
+ },
129
+ "premiumMultiplier?": "number",
130
+ "contextWindow?": "number",
131
+ "maxTokens?": "number",
132
+ "omitMaxOutputTokens?": "boolean",
133
+ "headers?": { "[string]": "string" },
134
+ "compat?": OpenAICompatSchema,
135
+ "contextPromotionTarget?": "string",
136
+ }).narrow((value, ctx) => {
137
+ // Enforce id non-empty
138
+ if (typeof value.id === "string" && value.id.length === 0) {
139
+ return ctx.mustBe("id a non-empty string");
140
+ }
141
+ if (value.name !== undefined && typeof value.name === "string" && value.name.length === 0) {
142
+ return ctx.mustBe("name a non-empty string");
143
+ }
144
+ if (value.baseUrl !== undefined && typeof value.baseUrl === "string" && value.baseUrl.length === 0) {
145
+ return ctx.mustBe("baseUrl a non-empty string");
146
+ }
147
+ if (
148
+ value.contextPromotionTarget !== undefined &&
149
+ typeof value.contextPromotionTarget === "string" &&
150
+ value.contextPromotionTarget.length === 0
151
+ ) {
152
+ return ctx.mustBe("contextPromotionTarget a non-empty string");
153
+ }
154
+ return true;
152
155
  });
153
156
 
154
- export const ModelOverrideSchema = z.object({
155
- name: z.string().min(1).optional(),
156
- reasoning: z.boolean().optional(),
157
- thinking: ModelThinkingSchema.optional(),
158
- input: z.array(z.enum(["text", "image"])).optional(),
159
- supportsTools: z.boolean().optional(),
160
- cost: z
161
- .object({
162
- input: z.number().optional(),
163
- output: z.number().optional(),
164
- cacheRead: z.number().optional(),
165
- cacheWrite: z.number().optional(),
166
- })
167
- .optional(),
168
- premiumMultiplier: z.number().optional(),
169
- contextWindow: z.number().optional(),
170
- maxTokens: z.number().optional(),
171
- omitMaxOutputTokens: z.boolean().optional(),
172
- headers: z.record(z.string(), z.string()).optional(),
173
- compat: OpenAICompatSchema.optional(),
174
- contextPromotionTarget: z.string().min(1).optional(),
157
+ export const ModelOverrideSchema = type({
158
+ "name?": "string",
159
+ "reasoning?": "boolean",
160
+ "thinking?": ModelThinkingSchema,
161
+ "input?": '("text" | "image")[]',
162
+ "supportsTools?": "boolean",
163
+ "cost?": {
164
+ "input?": "number",
165
+ "output?": "number",
166
+ "cacheRead?": "number",
167
+ "cacheWrite?": "number",
168
+ },
169
+ "premiumMultiplier?": "number",
170
+ "contextWindow?": "number",
171
+ "maxTokens?": "number",
172
+ "omitMaxOutputTokens?": "boolean",
173
+ "headers?": { "[string]": "string" },
174
+ "compat?": OpenAICompatSchema,
175
+ "contextPromotionTarget?": "string",
176
+ }).narrow((value, ctx) => {
177
+ if (value.name !== undefined && typeof value.name === "string" && value.name.length === 0) {
178
+ return ctx.mustBe("name a non-empty string");
179
+ }
180
+ if (
181
+ value.contextPromotionTarget !== undefined &&
182
+ typeof value.contextPromotionTarget === "string" &&
183
+ value.contextPromotionTarget.length === 0
184
+ ) {
185
+ return ctx.mustBe("contextPromotionTarget a non-empty string");
186
+ }
187
+ return true;
175
188
  });
176
189
 
177
- export type ModelOverride = z.infer<typeof ModelOverrideSchema>;
190
+ export type ModelOverride = typeof ModelOverrideSchema.infer;
178
191
 
179
- export const ProviderDiscoverySchema = z.object({
180
- type: z.enum(["ollama", "llama.cpp", "lm-studio", "openai-models-list", "proxy"]),
192
+ export const ProviderDiscoverySchema = type({
193
+ type: '"ollama" | "llama.cpp" | "lm-studio" | "openai-models-list" | "proxy"',
181
194
  });
182
195
 
183
- export const ProviderAuthSchema = z.enum(["apiKey", "none", "oauth"]);
184
-
185
- export type ProviderAuthMode = z.infer<typeof ProviderAuthSchema>;
186
- export type ProviderDiscovery = z.infer<typeof ProviderDiscoverySchema>;
187
-
188
- const ProviderConfigSchema = z.object({
189
- baseUrl: z.string().min(1).optional(),
190
- apiKey: z.string().min(1).optional(),
191
- api: z
192
- .enum([
193
- "openai-completions",
194
- "openai-responses",
195
- "openai-codex-responses",
196
- "azure-openai-responses",
197
- "anthropic-messages",
198
- "google-generative-ai",
199
- "google-gemini-cli",
200
- "google-vertex",
201
- ])
202
- .optional(),
203
- headers: z.record(z.string(), z.string()).optional(),
204
- compat: OpenAICompatSchema.optional(),
205
- authHeader: z.boolean().optional(),
206
- auth: ProviderAuthSchema.optional(),
207
- discovery: ProviderDiscoverySchema.optional(),
208
- models: z.array(ModelDefinitionSchema).optional(),
209
- modelOverrides: z.record(z.string(), ModelOverrideSchema).optional(),
210
- disableStrictTools: z.boolean().optional(),
196
+ export const ProviderAuthSchema = type('"apiKey" | "none" | "oauth"');
197
+
198
+ export type ProviderAuthMode = typeof ProviderAuthSchema.infer;
199
+ export type ProviderDiscovery = typeof ProviderDiscoverySchema.infer;
200
+
201
+ const ProviderConfigSchema = type({
202
+ "baseUrl?": "string",
203
+ "apiKey?": "string",
204
+ "api?":
205
+ '"openai-completions" | "openai-responses" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "google-generative-ai" | "google-gemini-cli" | "google-vertex"',
206
+ "headers?": { "[string]": "string" },
207
+ "compat?": OpenAICompatSchema,
208
+ "authHeader?": "boolean",
209
+ "auth?": ProviderAuthSchema,
210
+ "discovery?": ProviderDiscoverySchema,
211
+ "models?": ModelDefinitionSchema.array(),
212
+ "modelOverrides?": { "[string]": ModelOverrideSchema },
213
+ "disableStrictTools?": "boolean",
211
214
  /**
212
215
  * Streaming transport override. When set to `"pi-native"`, omp dispatches
213
216
  * every model under this provider via the auth-gateway's
@@ -215,17 +218,41 @@ const ProviderConfigSchema = z.object({
215
218
  * provider's `baseUrl` must point at a compatible `omp auth-gateway`
216
219
  * and `apiKey` must carry the gateway bearer.
217
220
  */
218
- transport: z.literal("pi-native").optional(),
221
+ "transport?": '"pi-native"',
222
+ }).narrow((value, ctx) => {
223
+ if (value.baseUrl !== undefined && typeof value.baseUrl === "string" && value.baseUrl.length === 0) {
224
+ return ctx.mustBe("baseUrl a non-empty string");
225
+ }
226
+ if (value.apiKey !== undefined && typeof value.apiKey === "string" && value.apiKey.length === 0) {
227
+ return ctx.mustBe("apiKey a non-empty string");
228
+ }
229
+ return true;
219
230
  });
220
231
 
221
- const EquivalenceConfigSchema = z.object({
222
- overrides: z.record(z.string(), z.string().min(1)).optional(),
223
- exclude: z.array(z.string().min(1)).optional(),
232
+ const EquivalenceConfigSchema = type({
233
+ "overrides?": { "[string]": "string" },
234
+ "exclude?": "string[]",
235
+ }).narrow((value, ctx) => {
236
+ if (value.overrides !== undefined) {
237
+ for (const [, v] of Object.entries(value.overrides)) {
238
+ if (typeof v === "string" && v.length === 0) {
239
+ return ctx.mustBe("overrides values non-empty strings");
240
+ }
241
+ }
242
+ }
243
+ if (value.exclude !== undefined && Array.isArray(value.exclude)) {
244
+ for (const item of value.exclude) {
245
+ if (typeof item === "string" && item.length === 0) {
246
+ return ctx.mustBe("exclude items non-empty strings");
247
+ }
248
+ }
249
+ }
250
+ return true;
224
251
  });
225
252
 
226
- export const ModelsConfigSchema = z.object({
227
- providers: z.record(z.string(), ProviderConfigSchema).optional(),
228
- equivalence: EquivalenceConfigSchema.optional(),
253
+ export const ModelsConfigSchema = type({
254
+ "providers?": { "[string]": ProviderConfigSchema },
255
+ "equivalence?": EquivalenceConfigSchema,
229
256
  });
230
257
 
231
- export type ModelsConfig = z.infer<typeof ModelsConfigSchema>;
258
+ export type ModelsConfig = typeof ModelsConfigSchema.infer;