@jini-ai/agent-runtime 0.3.2 → 0.3.7

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 (231) hide show
  1. package/dist/anthropic-live-models.d.ts +45 -0
  2. package/dist/anthropic-live-models.js +0 -0
  3. package/dist/claude-code-models.d.ts +63 -0
  4. package/dist/claude-code-models.js +252 -0
  5. package/dist/defs/aider.d.ts +1 -0
  6. package/dist/defs/aider.js +17 -1
  7. package/dist/defs/amp.js +9 -0
  8. package/dist/defs/amr.d.ts +1 -0
  9. package/dist/defs/amr.js +7 -0
  10. package/dist/defs/claude.d.ts +2 -1
  11. package/dist/defs/claude.js +78 -8
  12. package/dist/defs/codex.d.ts +20 -1
  13. package/dist/defs/codex.js +170 -17
  14. package/dist/defs/copilot.js +14 -0
  15. package/dist/defs/cursor-agent.js +11 -0
  16. package/dist/defs/deepseek.js +12 -0
  17. package/dist/defs/grok-build.js +10 -0
  18. package/dist/defs/opencode.d.ts +1 -0
  19. package/dist/defs/opencode.js +7 -2
  20. package/dist/defs/pi.d.ts +1 -0
  21. package/dist/defs/pi.js +12 -4
  22. package/dist/defs/qoder.js +11 -0
  23. package/dist/defs/qwen.js +9 -0
  24. package/dist/detection.d.ts +21 -1
  25. package/dist/detection.js +59 -4
  26. package/dist/registry.d.ts +2 -1
  27. package/dist/registry.js +2 -1
  28. package/dist/types.d.ts +67 -1
  29. package/package.json +5 -4
  30. package/dist/acp-model-probe.d.ts.map +0 -1
  31. package/dist/acp-model-probe.js.map +0 -1
  32. package/dist/agent-protocol/acp/account-failure.d.ts.map +0 -1
  33. package/dist/agent-protocol/acp/account-failure.js.map +0 -1
  34. package/dist/agent-protocol/acp/constants.d.ts.map +0 -1
  35. package/dist/agent-protocol/acp/constants.js.map +0 -1
  36. package/dist/agent-protocol/acp/index.d.ts.map +0 -1
  37. package/dist/agent-protocol/acp/index.js.map +0 -1
  38. package/dist/agent-protocol/acp/json.d.ts.map +0 -1
  39. package/dist/agent-protocol/acp/json.js.map +0 -1
  40. package/dist/agent-protocol/acp/models.d.ts.map +0 -1
  41. package/dist/agent-protocol/acp/models.js.map +0 -1
  42. package/dist/agent-protocol/acp/rpc.d.ts.map +0 -1
  43. package/dist/agent-protocol/acp/rpc.js.map +0 -1
  44. package/dist/agent-protocol/acp/session-params.d.ts.map +0 -1
  45. package/dist/agent-protocol/acp/session-params.js.map +0 -1
  46. package/dist/agent-protocol/acp/session.d.ts.map +0 -1
  47. package/dist/agent-protocol/acp/session.js.map +0 -1
  48. package/dist/agent-protocol/acp/text-suppression.d.ts.map +0 -1
  49. package/dist/agent-protocol/acp/text-suppression.js.map +0 -1
  50. package/dist/agent-protocol/acp/types.d.ts.map +0 -1
  51. package/dist/agent-protocol/acp/types.js.map +0 -1
  52. package/dist/agent-protocol/acp/updates.d.ts.map +0 -1
  53. package/dist/agent-protocol/acp/updates.js.map +0 -1
  54. package/dist/agent-protocol/core/index.d.ts.map +0 -1
  55. package/dist/agent-protocol/core/index.js.map +0 -1
  56. package/dist/agent-protocol/core/json-line-stream.d.ts.map +0 -1
  57. package/dist/agent-protocol/core/json-line-stream.js.map +0 -1
  58. package/dist/agent-protocol/index.d.ts.map +0 -1
  59. package/dist/agent-protocol/index.js.map +0 -1
  60. package/dist/agent-protocol/pi-rpc/events.d.ts.map +0 -1
  61. package/dist/agent-protocol/pi-rpc/events.js.map +0 -1
  62. package/dist/agent-protocol/pi-rpc/index.d.ts.map +0 -1
  63. package/dist/agent-protocol/pi-rpc/index.js.map +0 -1
  64. package/dist/agent-protocol/pi-rpc/internal.d.ts.map +0 -1
  65. package/dist/agent-protocol/pi-rpc/internal.js.map +0 -1
  66. package/dist/agent-protocol/pi-rpc/models.d.ts.map +0 -1
  67. package/dist/agent-protocol/pi-rpc/models.js.map +0 -1
  68. package/dist/agent-protocol/pi-rpc/session.d.ts.map +0 -1
  69. package/dist/agent-protocol/pi-rpc/session.js.map +0 -1
  70. package/dist/amr-model-cache.d.ts.map +0 -1
  71. package/dist/amr-model-cache.js.map +0 -1
  72. package/dist/amr-profile-resolver.d.ts.map +0 -1
  73. package/dist/amr-profile-resolver.js.map +0 -1
  74. package/dist/artifact-taxonomy.d.ts.map +0 -1
  75. package/dist/artifact-taxonomy.js.map +0 -1
  76. package/dist/auth.d.ts.map +0 -1
  77. package/dist/auth.js.map +0 -1
  78. package/dist/capabilities.d.ts.map +0 -1
  79. package/dist/capabilities.js.map +0 -1
  80. package/dist/claude-stream.d.ts.map +0 -1
  81. package/dist/claude-stream.js.map +0 -1
  82. package/dist/copilot-stream.d.ts.map +0 -1
  83. package/dist/copilot-stream.js.map +0 -1
  84. package/dist/defs/aider.d.ts.map +0 -1
  85. package/dist/defs/aider.js.map +0 -1
  86. package/dist/defs/amp.d.ts.map +0 -1
  87. package/dist/defs/amp.js.map +0 -1
  88. package/dist/defs/amr.d.ts.map +0 -1
  89. package/dist/defs/amr.js.map +0 -1
  90. package/dist/defs/antigravity.d.ts.map +0 -1
  91. package/dist/defs/antigravity.js.map +0 -1
  92. package/dist/defs/claude.d.ts.map +0 -1
  93. package/dist/defs/claude.js.map +0 -1
  94. package/dist/defs/codebuddy.d.ts.map +0 -1
  95. package/dist/defs/codebuddy.js.map +0 -1
  96. package/dist/defs/codex.d.ts.map +0 -1
  97. package/dist/defs/codex.js.map +0 -1
  98. package/dist/defs/copilot.d.ts.map +0 -1
  99. package/dist/defs/copilot.js.map +0 -1
  100. package/dist/defs/cursor-agent.d.ts.map +0 -1
  101. package/dist/defs/cursor-agent.js.map +0 -1
  102. package/dist/defs/deepseek.d.ts.map +0 -1
  103. package/dist/defs/deepseek.js.map +0 -1
  104. package/dist/defs/devin.d.ts.map +0 -1
  105. package/dist/defs/devin.js.map +0 -1
  106. package/dist/defs/grok-build.d.ts.map +0 -1
  107. package/dist/defs/grok-build.js.map +0 -1
  108. package/dist/defs/hermes.d.ts.map +0 -1
  109. package/dist/defs/hermes.js.map +0 -1
  110. package/dist/defs/index.d.ts.map +0 -1
  111. package/dist/defs/index.js.map +0 -1
  112. package/dist/defs/kilo.d.ts.map +0 -1
  113. package/dist/defs/kilo.js.map +0 -1
  114. package/dist/defs/kimi.d.ts.map +0 -1
  115. package/dist/defs/kimi.js.map +0 -1
  116. package/dist/defs/kiro.d.ts.map +0 -1
  117. package/dist/defs/kiro.js.map +0 -1
  118. package/dist/defs/mimo.d.ts.map +0 -1
  119. package/dist/defs/mimo.js.map +0 -1
  120. package/dist/defs/opencode.d.ts.map +0 -1
  121. package/dist/defs/opencode.js.map +0 -1
  122. package/dist/defs/pi.d.ts.map +0 -1
  123. package/dist/defs/pi.js.map +0 -1
  124. package/dist/defs/qoder.d.ts.map +0 -1
  125. package/dist/defs/qoder.js.map +0 -1
  126. package/dist/defs/qwen.d.ts.map +0 -1
  127. package/dist/defs/qwen.js.map +0 -1
  128. package/dist/defs/reasonix.d.ts.map +0 -1
  129. package/dist/defs/reasonix.js.map +0 -1
  130. package/dist/defs/shared.d.ts.map +0 -1
  131. package/dist/defs/shared.js.map +0 -1
  132. package/dist/defs/trae-cli.d.ts.map +0 -1
  133. package/dist/defs/trae-cli.js.map +0 -1
  134. package/dist/defs/vibe.d.ts.map +0 -1
  135. package/dist/defs/vibe.js.map +0 -1
  136. package/dist/detection.d.ts.map +0 -1
  137. package/dist/detection.js.map +0 -1
  138. package/dist/diagnostics.d.ts.map +0 -1
  139. package/dist/diagnostics.js.map +0 -1
  140. package/dist/env.d.ts.map +0 -1
  141. package/dist/env.js.map +0 -1
  142. package/dist/executables.d.ts.map +0 -1
  143. package/dist/executables.js.map +0 -1
  144. package/dist/index.d.ts.map +0 -1
  145. package/dist/index.js.map +0 -1
  146. package/dist/invocation.d.ts.map +0 -1
  147. package/dist/invocation.js.map +0 -1
  148. package/dist/json-event-stream.d.ts.map +0 -1
  149. package/dist/json-event-stream.js.map +0 -1
  150. package/dist/launch.d.ts.map +0 -1
  151. package/dist/launch.js.map +0 -1
  152. package/dist/log-file.d.ts.map +0 -1
  153. package/dist/log-file.js.map +0 -1
  154. package/dist/mcp.d.ts.map +0 -1
  155. package/dist/mcp.js.map +0 -1
  156. package/dist/metadata.d.ts.map +0 -1
  157. package/dist/metadata.js.map +0 -1
  158. package/dist/mmd-routes.d.ts.map +0 -1
  159. package/dist/mmd-routes.js.map +0 -1
  160. package/dist/model-registry.d.ts.map +0 -1
  161. package/dist/model-registry.js.map +0 -1
  162. package/dist/models.d.ts.map +0 -1
  163. package/dist/models.js.map +0 -1
  164. package/dist/opencode-log.d.ts.map +0 -1
  165. package/dist/opencode-log.js.map +0 -1
  166. package/dist/paths.d.ts.map +0 -1
  167. package/dist/paths.js.map +0 -1
  168. package/dist/pi-models.d.ts.map +0 -1
  169. package/dist/pi-models.js.map +0 -1
  170. package/dist/prompt-augmenter.d.ts.map +0 -1
  171. package/dist/prompt-augmenter.js.map +0 -1
  172. package/dist/prompt-budget.d.ts.map +0 -1
  173. package/dist/prompt-budget.js.map +0 -1
  174. package/dist/prompt-file.d.ts.map +0 -1
  175. package/dist/prompt-file.js.map +0 -1
  176. package/dist/providers/aihubmix.d.ts.map +0 -1
  177. package/dist/providers/aihubmix.js.map +0 -1
  178. package/dist/providers/anthropic-messages.d.ts.map +0 -1
  179. package/dist/providers/anthropic-messages.js.map +0 -1
  180. package/dist/providers/azure-chat.d.ts.map +0 -1
  181. package/dist/providers/azure-chat.js.map +0 -1
  182. package/dist/providers/connection-guard.d.ts.map +0 -1
  183. package/dist/providers/connection-guard.js.map +0 -1
  184. package/dist/providers/connection-test.d.ts.map +0 -1
  185. package/dist/providers/connection-test.js.map +0 -1
  186. package/dist/providers/elevenlabs.d.ts.map +0 -1
  187. package/dist/providers/elevenlabs.js.map +0 -1
  188. package/dist/providers/google-messages.d.ts.map +0 -1
  189. package/dist/providers/google-messages.js.map +0 -1
  190. package/dist/providers/google.d.ts.map +0 -1
  191. package/dist/providers/google.js.map +0 -1
  192. package/dist/providers/index.d.ts.map +0 -1
  193. package/dist/providers/index.js.map +0 -1
  194. package/dist/providers/model-catalog.d.ts.map +0 -1
  195. package/dist/providers/model-catalog.js.map +0 -1
  196. package/dist/providers/oauth-callback-server.d.ts.map +0 -1
  197. package/dist/providers/oauth-callback-server.js.map +0 -1
  198. package/dist/providers/oauth-credentials.d.ts.map +0 -1
  199. package/dist/providers/oauth-credentials.js.map +0 -1
  200. package/dist/providers/oauth-provider.d.ts.map +0 -1
  201. package/dist/providers/oauth-provider.js.map +0 -1
  202. package/dist/providers/oauth-tokens.d.ts.map +0 -1
  203. package/dist/providers/oauth-tokens.js.map +0 -1
  204. package/dist/providers/ollama-chat.d.ts.map +0 -1
  205. package/dist/providers/ollama-chat.js.map +0 -1
  206. package/dist/providers/openai-chat.d.ts.map +0 -1
  207. package/dist/providers/openai-chat.js.map +0 -1
  208. package/dist/providers/pkce.d.ts.map +0 -1
  209. package/dist/providers/pkce.js.map +0 -1
  210. package/dist/providers/sse-decode.d.ts.map +0 -1
  211. package/dist/providers/sse-decode.js.map +0 -1
  212. package/dist/providers/token-params.d.ts.map +0 -1
  213. package/dist/providers/token-params.js.map +0 -1
  214. package/dist/providers/turn-end-guard.d.ts.map +0 -1
  215. package/dist/providers/turn-end-guard.js.map +0 -1
  216. package/dist/providers/types.d.ts.map +0 -1
  217. package/dist/providers/types.js.map +0 -1
  218. package/dist/qoder-stream.d.ts.map +0 -1
  219. package/dist/qoder-stream.js.map +0 -1
  220. package/dist/registry.d.ts.map +0 -1
  221. package/dist/registry.js.map +0 -1
  222. package/dist/resolution.d.ts.map +0 -1
  223. package/dist/resolution.js.map +0 -1
  224. package/dist/role-marker-guard.d.ts.map +0 -1
  225. package/dist/role-marker-guard.js.map +0 -1
  226. package/dist/telemetry-sink.d.ts.map +0 -1
  227. package/dist/telemetry-sink.js.map +0 -1
  228. package/dist/terminal-launch.d.ts.map +0 -1
  229. package/dist/terminal-launch.js.map +0 -1
  230. package/dist/types.d.ts.map +0 -1
  231. package/dist/types.js.map +0 -1
@@ -0,0 +1,45 @@
1
+ import type { RuntimeEnv, RuntimeModelOption } from './types.js';
2
+ /** Test-only reset — this module-scope cache would otherwise leak a stubbed `fetch`'s result across
3
+ * test cases. */
4
+ export declare function resetAnthropicLiveModelCacheForTesting(): void;
5
+ export type AnthropicCredential = {
6
+ apiKey: string;
7
+ baseUrl: string;
8
+ };
9
+ /**
10
+ * Reads an Anthropic API credential out of the environment handed to the def's `fetchModels`.
11
+ *
12
+ * Reads ONLY the passed `env`, never `process.env` directly: detection composes a per-agent
13
+ * environment (host env + def env + operator overrides) and that composed value is the one a spawn
14
+ * would actually see, so consulting the ambient process would let this disagree with the runtime.
15
+ *
16
+ * @param env - The composed environment for this agent.
17
+ * @returns The credential, or `null` when no usable key is present — the caller's signal to make no
18
+ * network call whatsoever.
19
+ * @complexity O(1).
20
+ */
21
+ export declare function resolveAnthropicCredential(env: RuntimeEnv): AnthropicCredential | null;
22
+ /**
23
+ * Appends live-discovered models to the static fallback list.
24
+ *
25
+ * UNION, never replace. `fallback` is always fully present, in its original order — nothing is ever
26
+ * removed from it, so the `default` sentinel and the bare CLI aliases survive by construction rather
27
+ * than by a guard that could be edited away. Live ids already present are skipped.
28
+ *
29
+ * @complexity O(f + l) — one `Set` over the fallback, one pass over the live list.
30
+ */
31
+ export declare function mergeLiveModels(fallback: readonly RuntimeModelOption[], live: readonly RuntimeModelOption[]): RuntimeModelOption[];
32
+ /**
33
+ * Returns the Claude model list with the account's live catalog merged in, or `null` when there is
34
+ * nothing to merge.
35
+ *
36
+ * @param env - The composed environment for the `claude` agent.
37
+ * @param fallbackModels - The def's static list; always fully present in a non-null result.
38
+ * @param now - Injectable clock for TTL tests.
39
+ * @returns The merged list, or `null` for every no-credential / failed-call outcome — which the
40
+ * caller treats as "keep the static list", never as an error.
41
+ * @complexity O(1) plus, at most, one cached outbound HTTPS call bounded by
42
+ * {@link LIVE_MODEL_TIMEOUT_MS}.
43
+ */
44
+ export declare function loadAnthropicLiveModels(env: RuntimeEnv, fallbackModels: readonly RuntimeModelOption[], now?: () => number): Promise<RuntimeModelOption[] | null>;
45
+ //# sourceMappingURL=anthropic-live-models.d.ts.map
Binary file
@@ -0,0 +1,63 @@
1
+ import type { RuntimeEnv, RuntimeModelOption } from './types.js';
2
+ /** The single stdin line that asks the CLI for its initialize payload (which carries `models`). */
3
+ export declare const CLAUDE_CODE_INITIALIZE_REQUEST = "{\"type\":\"control_request\",\"request_id\":\"jini-model-probe\",\"request\":{\"subtype\":\"initialize\"}}\n";
4
+ /**
5
+ * argv for the initialize probe.
6
+ *
7
+ * `--no-session-persistence`: no transcript is written. `--strict-mcp-config` with no `--mcp-config`:
8
+ * none of the operator's MCP servers are started. `disableAllHooks`: the operator's SessionStart
9
+ * hooks do not fire for a metadata probe (verified: without it, two user hooks ran). User settings
10
+ * are otherwise kept, because they can change what the picker offers (e.g. `availableModels`).
11
+ */
12
+ export declare const CLAUDE_CODE_INITIALIZE_ARGS: readonly string[];
13
+ /** Strips the bracketed context-window variant marker (`claude-fable-5-1[1m]`) —
14
+ * `models.ts#sanitizeCustomModel` rejects brackets, and `--model` accepts the bare name. */
15
+ export declare function normalizeClaudePickerModelId(raw: string): string;
16
+ /** Outcome of parsing `~/.claude.json`. The failure reasons are distinct so the freshness guard can
17
+ * say WHY it skipped; the runtime treats every failure as "nothing to add". */
18
+ export type ClaudeCodePickerCacheParse = {
19
+ readonly ok: true;
20
+ readonly ids: readonly string[];
21
+ } | {
22
+ readonly ok: false;
23
+ readonly reason: 'invalid-json' | 'no-cache' | 'no-usable-values';
24
+ };
25
+ /**
26
+ * Parses the raw text of `~/.claude.json` down to the model ids in `additionalModelOptionsCache`.
27
+ *
28
+ * @returns Normalized ids (bracket suffix stripped), or a typed reason. Never throws.
29
+ * @complexity O(n) over the cache entries (plus the JSON parse of the whole file).
30
+ */
31
+ export declare function parseClaudeCodePickerCache(raw: string): ClaudeCodePickerCacheParse;
32
+ /**
33
+ * Parses the CLI's stream-json stdout for the initialize `control_response` and returns its model
34
+ * rows (aliases and concrete ids, bracket suffixes stripped, deduped, `default` excluded).
35
+ *
36
+ * @returns The rows, or `null` when no well-formed response with at least one usable model exists.
37
+ * @complexity O(s + n) over the stdout length and the n models listed.
38
+ */
39
+ export declare function parseClaudeInitializeModels(stdout: string): RuntimeModelOption[] | null;
40
+ /** Where Claude Code keeps `.claude.json` for this env: `$CLAUDE_CONFIG_DIR` when set, else HOME. */
41
+ export declare function resolveClaudeConfigPath(env: RuntimeEnv): string;
42
+ /** The two I/O edges, injectable so tests never spawn a real CLI or read the real home dir. */
43
+ export interface ClaudeCodeModelIo {
44
+ /** Runs the initialize probe; resolves to whatever stdout was captured, or `null`. Never rejects. */
45
+ runInitialize(bin: string, env: RuntimeEnv): Promise<string | null>;
46
+ /** Reads a file as UTF-8, or `null` when missing/unreadable. Never rejects. */
47
+ readConfigFile(path: string): Promise<string | null>;
48
+ }
49
+ /** Test-only: install fake I/O (or `null` to restore the real one) and clear the process cache. */
50
+ export declare function setClaudeCodeModelIoForTesting(io: ClaudeCodeModelIo | null): void;
51
+ /**
52
+ * Returns the `claude` model list with the CLI's own picker catalog merged in, or `null` when there
53
+ * is nothing to merge. Needs no API key.
54
+ *
55
+ * @param bin - The resolved `claude` (or fork) binary detection found.
56
+ * @param env - The composed environment for the agent — what a spawn would actually see.
57
+ * @param fallbackModels - The def's static list; always fully present, in order, in a non-null result.
58
+ * @param now - Injectable clock for TTL tests.
59
+ * @complexity O(f + l) plus, at most once per {@link CLAUDE_CODE_MODEL_CACHE_TTL_MS} per cache key,
60
+ * one CLI spawn bounded by {@link CLAUDE_CODE_INIT_TIMEOUT_MS} and one file read.
61
+ */
62
+ export declare function loadClaudeCodeModels(bin: string, env: RuntimeEnv, fallbackModels: readonly RuntimeModelOption[], now?: () => number): Promise<RuntimeModelOption[] | null>;
63
+ //# sourceMappingURL=claude-code-models.d.ts.map
@@ -0,0 +1,252 @@
1
+ /**
2
+ * @module claude-code-models
3
+ *
4
+ * Credential-free live model discovery for the `claude` runtime def — the `claude` analogue of
5
+ * `codex debug models`.
6
+ *
7
+ * **Why this exists.** `claude` has no list-models subcommand, so without an `ANTHROPIC_API_KEY` the
8
+ * picker used to render `defs/claude.ts`'s hand-written `CLAUDE_FALLBACK_MODELS` and nothing else. A
9
+ * subscription-only install therefore never saw a new model (e.g. `claude-opus-5-5`) until someone
10
+ * edited that array and republished.
11
+ *
12
+ * **The two sources, both needing no API key** (verified live against Claude Code 2.1.280):
13
+ *
14
+ * 1. The CLI's own `/model` picker list, via the stream-json control protocol. Writing ONE
15
+ * `{"type":"control_request","request":{"subtype":"initialize"}}` line to
16
+ * `claude -p --input-format stream-json --output-format stream-json --verbose` and closing stdin
17
+ * makes the CLI answer with a `control_response` whose `response.models` is exactly the rows its
18
+ * interactive `/model` picker renders — the binary's built-in models PLUS the server-fetched
19
+ * "additional" options — then exit 0. No user message is ever sent, so no model call is made
20
+ * and nothing is billed; it authenticates with whatever the CLI itself uses (subscription OAuth
21
+ * included). This is the same `initialize` handshake the Claude Agent SDK's `supportedModels()`
22
+ * rides on.
23
+ * 2. `~/.claude.json`'s `additionalModelOptionsCache` — the server-fetched "additional" picker rows
24
+ * Claude Code persists on every session start's bootstrap fetch (headless `-p` runs included;
25
+ * it only rewrites the file when the payload changed). Only the server's extras, NOT the
26
+ * built-in rows, so it is strictly weaker than (1); it is read only when (1) yields nothing — an
27
+ * older CLI without the control protocol, a fork bin (`openclaude`), a timeout.
28
+ *
29
+ * **Enrichment, never a gate.** Every failure resolves to `null` ("nothing to add"), and a non-null
30
+ * result is always `mergeLiveModels(fallback, live)` — the static list is fully present, in order.
31
+ *
32
+ * The cache-file parser ({@link parseClaudeCodePickerCache}) is shared with
33
+ * `scripts/check-model-fallback-freshness.ts` so the freshness guard and the runtime can never
34
+ * disagree about what that cache says.
35
+ */
36
+ import { execFile } from 'node:child_process';
37
+ import { createHash } from 'node:crypto';
38
+ import { readFile } from 'node:fs/promises';
39
+ import os from 'node:os';
40
+ import { join } from 'node:path';
41
+ import { createCommandInvocation } from '@jini-ai/platform';
42
+ import { mergeLiveModels } from './anthropic-live-models.js';
43
+ import { sanitizeCustomModel } from './models.js';
44
+ /** A spawn costs ~2.5s and the picker list only moves when Claude Code updates or the server's
45
+ * "additional" options change, so one answer is trusted for this long per process. Failures are
46
+ * cached for the same window so a broken CLI is not re-spawned on every detection pass. */
47
+ const CLAUDE_CODE_MODEL_CACHE_TTL_MS = 30 * 60_000;
48
+ /** Measured ~2.5s on a warm machine (the CLI performs its bootstrap fetch before answering). */
49
+ const CLAUDE_CODE_INIT_TIMEOUT_MS = 8_000;
50
+ /** The single stdin line that asks the CLI for its initialize payload (which carries `models`). */
51
+ export const CLAUDE_CODE_INITIALIZE_REQUEST = '{"type":"control_request","request_id":"jini-model-probe","request":{"subtype":"initialize"}}\n';
52
+ /**
53
+ * argv for the initialize probe.
54
+ *
55
+ * `--no-session-persistence`: no transcript is written. `--strict-mcp-config` with no `--mcp-config`:
56
+ * none of the operator's MCP servers are started. `disableAllHooks`: the operator's SessionStart
57
+ * hooks do not fire for a metadata probe (verified: without it, two user hooks ran). User settings
58
+ * are otherwise kept, because they can change what the picker offers (e.g. `availableModels`).
59
+ */
60
+ export const CLAUDE_CODE_INITIALIZE_ARGS = [
61
+ '-p',
62
+ '--input-format', 'stream-json',
63
+ '--output-format', 'stream-json',
64
+ '--verbose',
65
+ '--no-session-persistence',
66
+ '--strict-mcp-config',
67
+ '--settings', '{"disableAllHooks":true}',
68
+ ];
69
+ /** Strips the bracketed context-window variant marker (`claude-fable-5-1[1m]`) —
70
+ * `models.ts#sanitizeCustomModel` rejects brackets, and `--model` accepts the bare name. */
71
+ export function normalizeClaudePickerModelId(raw) {
72
+ return raw.replace(/\[[^\]]*\]$/, '').trim();
73
+ }
74
+ /**
75
+ * Parses the raw text of `~/.claude.json` down to the model ids in `additionalModelOptionsCache`.
76
+ *
77
+ * @returns Normalized ids (bracket suffix stripped), or a typed reason. Never throws.
78
+ * @complexity O(n) over the cache entries (plus the JSON parse of the whole file).
79
+ */
80
+ export function parseClaudeCodePickerCache(raw) {
81
+ let parsed;
82
+ try {
83
+ parsed = JSON.parse(raw);
84
+ }
85
+ catch {
86
+ return { ok: false, reason: 'invalid-json' };
87
+ }
88
+ const cached = parsed && typeof parsed === 'object'
89
+ ? parsed.additionalModelOptionsCache
90
+ : undefined;
91
+ if (!Array.isArray(cached) || cached.length === 0)
92
+ return { ok: false, reason: 'no-cache' };
93
+ const ids = cached
94
+ .map((entry) => (entry && typeof entry === 'object' ? entry.value : null))
95
+ .filter((value) => typeof value === 'string')
96
+ .map(normalizeClaudePickerModelId)
97
+ .filter((id) => id.length > 0);
98
+ return ids.length > 0 ? { ok: true, ids } : { ok: false, reason: 'no-usable-values' };
99
+ }
100
+ /** Pulls `response.response.models` out of the one `control_response` line, or `null`. */
101
+ function findInitializeModels(stdout) {
102
+ for (const line of stdout.split('\n')) {
103
+ if (!line.includes('control_response'))
104
+ continue;
105
+ try {
106
+ const msg = JSON.parse(line);
107
+ if (msg.type !== 'control_response' || msg.response?.subtype !== 'success')
108
+ continue;
109
+ const models = msg.response.response?.models;
110
+ if (Array.isArray(models))
111
+ return models;
112
+ }
113
+ catch {
114
+ // Not a JSON line (or a truncated one on timeout) — keep scanning.
115
+ }
116
+ }
117
+ return null;
118
+ }
119
+ /** The picker rows one initialize entry contributes: its selection value (an alias like `sonnet`,
120
+ * or a full id) and the concrete model it resolves to. `default` is the sentinel the fallback list
121
+ * already carries; anything `sanitizeCustomModel` would refuse is dropped rather than rendered. */
122
+ function initializeEntryRows(entry) {
123
+ const rows = [];
124
+ const displayName = typeof entry.displayName === 'string' ? entry.displayName : '';
125
+ for (const raw of [entry.value, entry.resolvedModel]) {
126
+ if (typeof raw !== 'string')
127
+ continue;
128
+ const id = normalizeClaudePickerModelId(raw);
129
+ if (id === 'default' || sanitizeCustomModel(id) !== id)
130
+ continue;
131
+ const isAlias = !id.startsWith('claude-');
132
+ rows.push({ id, label: isAlias && displayName ? `${displayName} (alias)` : id });
133
+ }
134
+ return rows;
135
+ }
136
+ /**
137
+ * Parses the CLI's stream-json stdout for the initialize `control_response` and returns its model
138
+ * rows (aliases and concrete ids, bracket suffixes stripped, deduped, `default` excluded).
139
+ *
140
+ * @returns The rows, or `null` when no well-formed response with at least one usable model exists.
141
+ * @complexity O(s + n) over the stdout length and the n models listed.
142
+ */
143
+ export function parseClaudeInitializeModels(stdout) {
144
+ const models = findInitializeModels(String(stdout || ''));
145
+ if (!models)
146
+ return null;
147
+ const seen = new Set();
148
+ const out = [];
149
+ for (const raw of models) {
150
+ if (!raw || typeof raw !== 'object')
151
+ continue;
152
+ for (const row of initializeEntryRows(raw)) {
153
+ if (seen.has(row.id))
154
+ continue;
155
+ seen.add(row.id);
156
+ out.push(row);
157
+ }
158
+ }
159
+ return out.length > 0 ? out : null;
160
+ }
161
+ /** Where Claude Code keeps `.claude.json` for this env: `$CLAUDE_CONFIG_DIR` when set, else HOME. */
162
+ export function resolveClaudeConfigPath(env) {
163
+ const configDir = typeof env.CLAUDE_CONFIG_DIR === 'string' ? env.CLAUDE_CONFIG_DIR.trim() : '';
164
+ if (configDir)
165
+ return join(configDir, '.claude.json');
166
+ const home = typeof env.HOME === 'string' && env.HOME.trim() ? env.HOME.trim() : os.homedir();
167
+ return join(home, '.claude.json');
168
+ }
169
+ function runInitializeProbe(bin, env) {
170
+ return new Promise((resolve) => {
171
+ try {
172
+ const invocation = createCommandInvocation({ command: bin, args: [...CLAUDE_CODE_INITIALIZE_ARGS], env });
173
+ const child = execFile(invocation.command, invocation.args, {
174
+ env: env,
175
+ // Neutral cwd, same reason as `invocation.ts#execAgentFile`: a metadata probe must never
176
+ // pick up (or write into) a project directory.
177
+ cwd: os.tmpdir(),
178
+ timeout: CLAUDE_CODE_INIT_TIMEOUT_MS,
179
+ maxBuffer: 8 * 1024 * 1024,
180
+ windowsVerbatimArguments: invocation.windowsVerbatimArguments,
181
+ },
182
+ // A timeout or non-zero exit still hands back what was captured; the parser decides.
183
+ (_err, stdout) => resolve(typeof stdout === 'string' ? stdout : String(stdout ?? '')));
184
+ child.stdin?.on('error', () => { });
185
+ child.stdin?.end(CLAUDE_CODE_INITIALIZE_REQUEST);
186
+ }
187
+ catch {
188
+ resolve(null);
189
+ }
190
+ });
191
+ }
192
+ const defaultIo = {
193
+ runInitialize: runInitializeProbe,
194
+ readConfigFile: (path) => readFile(path, 'utf8').catch(() => null),
195
+ };
196
+ let activeIo = defaultIo;
197
+ const cache = new Map();
198
+ /** Test-only: install fake I/O (or `null` to restore the real one) and clear the process cache. */
199
+ export function setClaudeCodeModelIoForTesting(io) {
200
+ activeIo = io ?? defaultIo;
201
+ cache.clear();
202
+ }
203
+ /** One cache slot per binary + config location + auth-affecting env, fingerprinted so no secret is
204
+ * retained as a `Map` key. Two accounts on one host must not share a picker list. */
205
+ function cacheKeyFor(bin, env) {
206
+ const auth = ['ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_BASE_URL', 'CLAUDE_CODE_OAUTH_TOKEN']
207
+ .map((name) => (typeof env[name] === 'string' ? env[name] : ''))
208
+ .join('\u0000');
209
+ const fingerprint = createHash('sha256').update(auth).digest('hex').slice(0, 16);
210
+ return `${bin}\u0000${resolveClaudeConfigPath(env)}\u0000${fingerprint}`;
211
+ }
212
+ /** Source (1), then source (2). Never rejects. */
213
+ async function discoverClaudeCodeModels(bin, env) {
214
+ try {
215
+ const stdout = await activeIo.runInitialize(bin, env);
216
+ const fromCli = stdout ? parseClaudeInitializeModels(stdout) : null;
217
+ if (fromCli)
218
+ return fromCli;
219
+ const raw = await activeIo.readConfigFile(resolveClaudeConfigPath(env));
220
+ const cached = raw === null ? null : parseClaudeCodePickerCache(raw);
221
+ if (!cached?.ok)
222
+ return null;
223
+ return cached.ids
224
+ .filter((id) => sanitizeCustomModel(id) === id)
225
+ .map((id) => ({ id, label: id }));
226
+ }
227
+ catch {
228
+ return null;
229
+ }
230
+ }
231
+ /**
232
+ * Returns the `claude` model list with the CLI's own picker catalog merged in, or `null` when there
233
+ * is nothing to merge. Needs no API key.
234
+ *
235
+ * @param bin - The resolved `claude` (or fork) binary detection found.
236
+ * @param env - The composed environment for the agent — what a spawn would actually see.
237
+ * @param fallbackModels - The def's static list; always fully present, in order, in a non-null result.
238
+ * @param now - Injectable clock for TTL tests.
239
+ * @complexity O(f + l) plus, at most once per {@link CLAUDE_CODE_MODEL_CACHE_TTL_MS} per cache key,
240
+ * one CLI spawn bounded by {@link CLAUDE_CODE_INIT_TIMEOUT_MS} and one file read.
241
+ */
242
+ export async function loadClaudeCodeModels(bin, env, fallbackModels, now = Date.now) {
243
+ const key = cacheKeyFor(bin, env);
244
+ const cached = cache.get(key);
245
+ const pending = cached && cached.expiresAt > now() ? cached.value : null;
246
+ const value = pending ?? discoverClaudeCodeModels(bin, env);
247
+ if (!pending)
248
+ cache.set(key, { expiresAt: now() + CLAUDE_CODE_MODEL_CACHE_TTL_MS, value });
249
+ const live = await value;
250
+ return live && live.length > 0 ? mergeLiveModels(fallbackModels, live) : null;
251
+ }
252
+ //# sourceMappingURL=claude-code-models.js.map
@@ -4,6 +4,7 @@ export declare const aiderAgentDef: {
4
4
  bin: string;
5
5
  versionArgs: string[];
6
6
  fallbackModels: import("../types.js").RuntimeModelOption[];
7
+ fallbackModelsAssertedAt: string;
7
8
  buildArgs: (prompt: string, _imagePaths: string[], _extra: string[] | undefined, options?: import("../types.js").RuntimeBuildOptions | undefined) => string[];
8
9
  maxPromptArgBytes: number;
9
10
  streamFormat: string;
@@ -14,10 +14,19 @@ export const aiderAgentDef = {
14
14
  fallbackModels: [
15
15
  DEFAULT_MODEL_OPTION,
16
16
  { id: 'sonnet', label: 'sonnet' },
17
- { id: 'gpt-4o', label: 'gpt-4o' },
17
+ // `gpt-4o` is stale — OpenAI's GPT-5.6 family replaced the old flagship/mini/nano naming
18
+ // with named tiers (sol/terra/luna); `sol` is the flagship `gpt-4o` mapped to, bare (no
19
+ // `openai/` prefix) to match this list's existing LiteLLM-provider-default spelling. See
20
+ // `packages/ui/src/features/execution/constants.ts`'s OpenAI preset and
21
+ // `scripts/check-model-fallback-freshness.ts`.
22
+ { id: 'gpt-5.6-sol', label: 'gpt-5.6-sol' },
18
23
  { id: 'deepseek/deepseek-chat', label: 'deepseek/deepseek-chat' },
19
24
  { id: 'gemini/gemini-2.0-flash', label: 'gemini/gemini-2.0-flash' },
20
25
  ],
26
+ // Asserted against `packages/ui/src/features/execution/constants.ts`'s current OpenAI preset
27
+ // (gpt-5.6-sol/terra/luna). See `RuntimeAgentDef.fallbackModelsAssertedAt` and
28
+ // `scripts/check-model-fallback-freshness.ts`.
29
+ fallbackModelsAssertedAt: '2026-09-16',
21
30
  // Aider's one-shot mode requires the prompt as `--message <text>` on
22
31
  // argv; neither `--message` nor `--message-file` accept `-` as a stdin
23
32
  // sentinel (it is treated as a literal filename), so we cannot pipe
@@ -64,5 +73,12 @@ export const aiderAgentDef = {
64
73
  streamFormat: 'plain',
65
74
  installUrl: 'https://aider.chat/docs/install.html',
66
75
  docsUrl: 'https://aider.chat',
76
+ // No `externalMcpInjection`: Aider has no native MCP client. Its published
77
+ // config reference (aider.chat/docs) documents no MCP options as of mid-2026,
78
+ // and the upstream MCP-support pull requests were closed unmerged — there is
79
+ // no `--mcp-config`-equivalent surface to write into. It proxies to whatever
80
+ // LiteLLM-routed model the user configures for chat completions only; there
81
+ // is nowhere for a bridged tool call to land. Revisit if upstream ships a
82
+ // client.
67
83
  };
68
84
  //# sourceMappingURL=aider.js.map
package/dist/defs/amp.js CHANGED
@@ -58,5 +58,14 @@ export const ampAgentDef = {
58
58
  },
59
59
  promptViaStdin: true,
60
60
  streamFormat: 'claude-stream-json',
61
+ // No `externalMcpInjection` yet, though Amp does have real native MCP support:
62
+ // `.amp/settings.json`'s `amp.mcpServers` key (workspace- or global-scoped),
63
+ // plus an `amp mcp add [--workspace]` CLI mutator. It doesn't fit any of the
64
+ // four wired strategies — the file/schema differ from `.mcp.json`
65
+ // (`'claude-mcp-json'`), Amp isn't ACP, and it isn't an env-var-content format
66
+ // (`'opencode-env-content'`/`'mimo-env-content'`). Workspace MCP servers also
67
+ // "require explicit approval before they can run" with no confirmed
68
+ // non-interactive bypass, so a new strategy here would still need that
69
+ // verified against a live headless `amp -x` run before it's safe to wire.
61
70
  };
62
71
  //# sourceMappingURL=amp.js.map
@@ -28,6 +28,7 @@ export declare const amrAgentDef: {
28
28
  fallbackModels: RuntimeModelOption[];
29
29
  buildArgs: () => string[];
30
30
  streamFormat: string;
31
+ externalMcpInjection: "acp-merge";
31
32
  resumesSessionViaAcpLoad: true;
32
33
  supportsCustomModel: false;
33
34
  imageDelivery: "native";
package/dist/defs/amr.js CHANGED
@@ -280,6 +280,13 @@ export const amrAgentDef = {
280
280
  fallbackModels: [],
281
281
  buildArgs: () => ['agent', 'run', '--runtime', 'opencode'],
282
282
  streamFormat: 'acp-json-rpc',
283
+ // AMR is a genuine ACP-native def (same `acp-json-rpc` transport as devin/hermes/kilo/kimi/
284
+ // kiro/reasonix/trae-cli/vibe), so it gets the caller's external MCP servers the same way those
285
+ // 8 do: merged into the `session/new` `mcpServers` array. `buildMcpBridgeDelivery` dispatches on
286
+ // this declared strategy alone, never on `def.id`, so this one field is the whole fix — no
287
+ // `agent-executor.ts` change needed. Previously undeclared here only because this def was ported
288
+ // after the other 8 and the field was missed, not because vela lacks the transport.
289
+ externalMcpInjection: 'acp-merge',
283
290
  // vela resumes the upstream OpenCode session via ACP session/load across
284
291
  // turns (the OpenCode session store persists per conversation), so the daemon
285
292
  // captures the durable handle, skips the transcript resend on resume, and
@@ -16,7 +16,8 @@ export declare const claudeAgentDef: {
16
16
  '--append-system-prompt': string;
17
17
  };
18
18
  fallbackModels: import("../types.js").RuntimeModelOption[];
19
- fetchModels: (_resolvedBin: string, env: import("../types.js").RuntimeEnv) => Promise<import("../types.js").RuntimeModelOption[] | null>;
19
+ fallbackModelsAssertedAt: string;
20
+ fetchModels: (resolvedBin: string, env: import("../types.js").RuntimeEnv) => Promise<import("../types.js").RuntimeModelOption[] | null>;
20
21
  reasoningOptions: {
21
22
  id: string;
22
23
  label: string;
@@ -30,6 +30,8 @@
30
30
  import { agentCapabilities } from '../capabilities.js';
31
31
  import { buildClaudeMcpConfigArgs, DEFAULT_MODEL_OPTION } from './shared.js';
32
32
  import { loadMmdRouteModels } from '../mmd-routes.js';
33
+ import { loadAnthropicLiveModels, mergeLiveModels } from '../anthropic-live-models.js';
34
+ import { loadClaudeCodeModels } from '../claude-code-models.js';
33
35
  /**
34
36
  * The levels `claude --effort` accepts, as the CLI itself reports them when
35
37
  * given an unknown one. Anything outside this set is dropped rather than
@@ -40,18 +42,44 @@ import { loadMmdRouteModels } from '../mmd-routes.js';
40
42
  const CLAUDE_EFFORT_LEVELS = new Set([
41
43
  'low', 'medium', 'high', 'xhigh', 'max',
42
44
  ]);
43
- // Current models first, then the still-active-but-superseded 4.5 opus/sonnet generation kept as
44
- // older options rather than dropped — an installed CLI may still be pinned to one via its own
45
- // config, and removing a working selection out from under a user is worse than listing it last.
46
- // `claude-haiku-4-5` is unchanged: Haiku 4.5 is still the current Haiku model, nothing superseded it.
45
+ /**
46
+ * The list a picker renders when nothing live is available — no `~/.config/mms/model-routes.json`,
47
+ * the CLI's own picker catalog unreadable (see `claude-code-models.ts`), and no `ANTHROPIC_API_KEY`
48
+ * in the agent's environment or a failed live call. Not the primary source: see `fetchModels` below.
49
+ * Every live source is unioned ON TOP of this list, so it is also the floor that never shrinks.
50
+ *
51
+ * Ordered current-first, then the still-active-but-superseded generations, which are KEPT rather
52
+ * than dropped — an installed CLI may be pinned to one via its own config, and removing a working
53
+ * selection out from under a user is worse than listing it last.
54
+ *
55
+ * Every id below is verified present in the installed Claude Code binary's own embedded model
56
+ * table (2.1.261), and `claude-fable-5-1` is additionally the value in `~/.claude.json`'s
57
+ * server-fetched `additionalModelOptionsCache`. `claude-fable-5-mythos-5` appears in the binary but
58
+ * NOT in that server-fetched picker list, so it is deliberately omitted: an id in the binary is not
59
+ * evidence it is a selectable model.
60
+ *
61
+ * The `[1m]` long-context suffix the server-fetched cache carries on `claude-fable-5-1` is dropped
62
+ * here on purpose — `models.ts#sanitizeCustomModel` rejects brackets, and `claude --help` documents
63
+ * the bare full name as the accepted `--model` form.
64
+ *
65
+ * **This list WILL go stale, and that is now a detectable event rather than a silent one** — see
66
+ * `fallbackModelsAssertedAt` on the def below and `scripts/check-model-fallback-freshness.ts`.
67
+ */
47
68
  const CLAUDE_FALLBACK_MODELS = [
48
69
  DEFAULT_MODEL_OPTION,
70
+ { id: 'fable', label: 'Fable (alias)' },
49
71
  { id: 'sonnet', label: 'Sonnet (alias)' },
50
72
  { id: 'opus', label: 'Opus (alias)' },
51
73
  { id: 'haiku', label: 'Haiku (alias)' },
74
+ { id: 'claude-fable-5-1', label: 'claude-fable-5-1' },
75
+ { id: 'claude-fable-5', label: 'claude-fable-5' },
52
76
  { id: 'claude-opus-5', label: 'claude-opus-5' },
53
77
  { id: 'claude-sonnet-5', label: 'claude-sonnet-5' },
54
78
  { id: 'claude-haiku-4-5', label: 'claude-haiku-4-5' },
79
+ { id: 'claude-opus-4-8', label: 'claude-opus-4-8' },
80
+ { id: 'claude-opus-4-7', label: 'claude-opus-4-7' },
81
+ { id: 'claude-opus-4-6', label: 'claude-opus-4-6' },
82
+ { id: 'claude-sonnet-4-6', label: 'claude-sonnet-4-6' },
55
83
  { id: 'claude-opus-4-5', label: 'claude-opus-4-5' },
56
84
  { id: 'claude-sonnet-4-5', label: 'claude-sonnet-4-5' },
57
85
  ];
@@ -81,11 +109,40 @@ export const claudeAgentDef = {
81
109
  '--effort': 'effort',
82
110
  '--append-system-prompt': 'appendSystemPrompt',
83
111
  },
84
- // `claude` has no list-models subcommand. Prefer local mmd/MMS routes
85
- // when present so proxy-backed Claude-compatible models appear in the
86
- // picker, then keep the built-in aliases as fallback hints.
112
+ // `claude` has no list-models subcommand, so the model list is answered from the best source
113
+ // available, in this order:
114
+ //
115
+ // 1. Local mmd/MMS routes, when `~/.config/mms/model-routes.json` exists — an explicit local
116
+ // routing config is the operator's own authoritative statement of what this CLI can reach,
117
+ // including proxy-backed Claude-compatible models the vendor API knows nothing about.
118
+ // 2. Otherwise, the UNION of two live sources, run in parallel:
119
+ // a. The CLI's own `/model` picker catalog — NO credential needed (subscription auth
120
+ // works). See `claude-code-models.ts`: the stream-json `initialize` control request,
121
+ // falling back to `~/.claude.json`'s `additionalModelOptionsCache`. Cached per process.
122
+ // b. The account's live Anthropic catalog, when `ANTHROPIC_API_KEY` is in this agent's
123
+ // environment (makes no network call at all without a key).
124
+ // Both are merged ON TOP of the static list, never in place of it; neither shadows the other.
125
+ // 3. The static list, when neither live source answers.
126
+ //
127
+ // Returning `null` from any step is "nothing to add", and `detection.ts#fetchModels` renders
128
+ // `fallbackModels` for it — so a live-discovery failure can never empty or shrink the picker.
87
129
  fallbackModels: CLAUDE_FALLBACK_MODELS,
88
- fetchModels: async (_resolvedBin, env) => loadMmdRouteModels(env, CLAUDE_FALLBACK_MODELS),
130
+ // Asserted against the installed Claude Code 2.1.261 binary's embedded model table and
131
+ // `~/.claude.json`'s server-fetched `additionalModelOptionsCache`. See
132
+ // `RuntimeAgentDef.fallbackModelsAssertedAt` and `scripts/check-model-fallback-freshness.ts`.
133
+ fallbackModelsAssertedAt: '2026-09-05',
134
+ fetchModels: async (resolvedBin, env) => {
135
+ const routed = await loadMmdRouteModels(env, CLAUDE_FALLBACK_MODELS);
136
+ if (routed)
137
+ return routed;
138
+ const [fromCli, fromApi] = await Promise.all([
139
+ loadClaudeCodeModels(resolvedBin, env, CLAUDE_FALLBACK_MODELS),
140
+ loadAnthropicLiveModels(env, CLAUDE_FALLBACK_MODELS),
141
+ ]);
142
+ if (!fromCli)
143
+ return fromApi;
144
+ return fromApi ? mergeLiveModels(fromCli, fromApi) : fromCli;
145
+ },
89
146
  // `claude --effort <level>`. The set differs from codex's on both ends — it
90
147
  // has `max` and has no `none`/`minimal` — so it is spelled out rather than
91
148
  // shared, and `CLAUDE_EFFORT_LEVELS` below is what `buildArgs` validates
@@ -168,6 +225,19 @@ export const claudeAgentDef = {
168
225
  // implementation rather than a copy per def. A no-op (`[]`) whenever the caller staged no
169
226
  // file, so a host that never configured MCP injection sees no argv change.
170
227
  args.push(...buildClaudeMcpConfigArgs(runtimeContext));
228
+ // Finding 2 (SEC-assistant-env-isolation-2026-09-07): `RuntimeBuildOptions.disallowedTools`/
229
+ // `allowedTools` are a `@jini-ai/agent-runtime`-level *mechanism* only — no product policy is
230
+ // baked in here (see `types.ts`'s own doc on those fields). Verified against installed Claude
231
+ // Code 2.1.263's own `-p --help`: `--disallowedTools, --disallowed-tools <tools...>` /
232
+ // `--allowedTools, --allowed-tools <tools...>`. Placed last, after every other flag, so an
233
+ // omitted (default) value is a true no-op — every existing call site that never passes these
234
+ // options sees byte-identical argv to before this change.
235
+ if (options.disallowedTools && options.disallowedTools.length > 0) {
236
+ args.push('--disallowedTools', ...options.disallowedTools);
237
+ }
238
+ if (options.allowedTools && options.allowedTools.length > 0) {
239
+ args.push('--allowedTools', ...options.allowedTools);
240
+ }
171
241
  return args;
172
242
  },
173
243
  promptViaStdin: true,
@@ -1,5 +1,22 @@
1
- import type { RuntimeModelOption } from '../types.js';
1
+ import type { RuntimeModelOption, RuntimeReasoningOption } from '../types.js';
2
2
  export declare function parseCodexDebugModels(stdout: string): RuntimeModelOption[] | null;
3
+ /**
4
+ * Collapses the per-model effort levels on a fetched model list into the single agent-level list a
5
+ * picker renders, in first-seen catalog order, behind the `default` sentinel.
6
+ *
7
+ * A UNION rather than an intersection: the alternative would offer only the levels EVERY model
8
+ * supports, which on the live 0.153.4 catalog is `low..xhigh` — precisely the truncated list this
9
+ * exists to replace, since `gpt-5.5` would veto `max`/`ultra` for `gpt-6-astra`. Over-offering is
10
+ * the same failure mode the previous hardcoded list already had (it listed `none`/`minimal`, which
11
+ * no catalog model supports), so this is strictly better; narrowing the offer per SELECTED model is
12
+ * a picker-side concern, and the data for it rides along on each row's `reasoning` field.
13
+ *
14
+ * @param models - A fetched model list; rows without `reasoning` contribute nothing.
15
+ * @returns The union, or `null` when no row carried levels — the caller's signal to keep its own
16
+ * static list rather than replace it with an empty one.
17
+ * @complexity O(n·m) over n models and their m levels.
18
+ */
19
+ export declare function unionModelReasoningOptions(models: readonly RuntimeModelOption[]): RuntimeReasoningOption[] | null;
3
20
  export declare function codexNeedsDangerFullAccessSandbox(platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): boolean;
4
21
  export declare const codexAgentDef: {
5
22
  id: string;
@@ -16,10 +33,12 @@ export declare const codexAgentDef: {
16
33
  timeoutMs: number;
17
34
  };
18
35
  fallbackModels: RuntimeModelOption[];
36
+ fallbackModelsAssertedAt: string;
19
37
  reasoningOptions: {
20
38
  id: string;
21
39
  label: string;
22
40
  }[];
41
+ deriveReasoningOptions: typeof unionModelReasoningOptions;
23
42
  buildArgs: (_prompt: string, imagePaths: string[], extraAllowedDirs?: string[] | undefined, options?: import("../types.js").RuntimeBuildOptions | undefined, runtimeContext?: import("../types.js").RuntimeContext | undefined) => string[];
24
43
  promptViaStdin: true;
25
44
  resumesSessionViaCli: true;