@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.
- package/dist/anthropic-live-models.d.ts +45 -0
- package/dist/anthropic-live-models.js +0 -0
- package/dist/claude-code-models.d.ts +63 -0
- package/dist/claude-code-models.js +252 -0
- package/dist/defs/aider.d.ts +1 -0
- package/dist/defs/aider.js +17 -1
- package/dist/defs/amp.js +9 -0
- package/dist/defs/amr.d.ts +1 -0
- package/dist/defs/amr.js +7 -0
- package/dist/defs/claude.d.ts +2 -1
- package/dist/defs/claude.js +78 -8
- package/dist/defs/codex.d.ts +20 -1
- package/dist/defs/codex.js +170 -17
- package/dist/defs/copilot.js +14 -0
- package/dist/defs/cursor-agent.js +11 -0
- package/dist/defs/deepseek.js +12 -0
- package/dist/defs/grok-build.js +10 -0
- package/dist/defs/opencode.d.ts +1 -0
- package/dist/defs/opencode.js +7 -2
- package/dist/defs/pi.d.ts +1 -0
- package/dist/defs/pi.js +12 -4
- package/dist/defs/qoder.js +11 -0
- package/dist/defs/qwen.js +9 -0
- package/dist/detection.d.ts +21 -1
- package/dist/detection.js +59 -4
- package/dist/registry.d.ts +2 -1
- package/dist/registry.js +2 -1
- package/dist/types.d.ts +67 -1
- package/package.json +5 -4
- package/dist/acp-model-probe.d.ts.map +0 -1
- package/dist/acp-model-probe.js.map +0 -1
- package/dist/agent-protocol/acp/account-failure.d.ts.map +0 -1
- package/dist/agent-protocol/acp/account-failure.js.map +0 -1
- package/dist/agent-protocol/acp/constants.d.ts.map +0 -1
- package/dist/agent-protocol/acp/constants.js.map +0 -1
- package/dist/agent-protocol/acp/index.d.ts.map +0 -1
- package/dist/agent-protocol/acp/index.js.map +0 -1
- package/dist/agent-protocol/acp/json.d.ts.map +0 -1
- package/dist/agent-protocol/acp/json.js.map +0 -1
- package/dist/agent-protocol/acp/models.d.ts.map +0 -1
- package/dist/agent-protocol/acp/models.js.map +0 -1
- package/dist/agent-protocol/acp/rpc.d.ts.map +0 -1
- package/dist/agent-protocol/acp/rpc.js.map +0 -1
- package/dist/agent-protocol/acp/session-params.d.ts.map +0 -1
- package/dist/agent-protocol/acp/session-params.js.map +0 -1
- package/dist/agent-protocol/acp/session.d.ts.map +0 -1
- package/dist/agent-protocol/acp/session.js.map +0 -1
- package/dist/agent-protocol/acp/text-suppression.d.ts.map +0 -1
- package/dist/agent-protocol/acp/text-suppression.js.map +0 -1
- package/dist/agent-protocol/acp/types.d.ts.map +0 -1
- package/dist/agent-protocol/acp/types.js.map +0 -1
- package/dist/agent-protocol/acp/updates.d.ts.map +0 -1
- package/dist/agent-protocol/acp/updates.js.map +0 -1
- package/dist/agent-protocol/core/index.d.ts.map +0 -1
- package/dist/agent-protocol/core/index.js.map +0 -1
- package/dist/agent-protocol/core/json-line-stream.d.ts.map +0 -1
- package/dist/agent-protocol/core/json-line-stream.js.map +0 -1
- package/dist/agent-protocol/index.d.ts.map +0 -1
- package/dist/agent-protocol/index.js.map +0 -1
- package/dist/agent-protocol/pi-rpc/events.d.ts.map +0 -1
- package/dist/agent-protocol/pi-rpc/events.js.map +0 -1
- package/dist/agent-protocol/pi-rpc/index.d.ts.map +0 -1
- package/dist/agent-protocol/pi-rpc/index.js.map +0 -1
- package/dist/agent-protocol/pi-rpc/internal.d.ts.map +0 -1
- package/dist/agent-protocol/pi-rpc/internal.js.map +0 -1
- package/dist/agent-protocol/pi-rpc/models.d.ts.map +0 -1
- package/dist/agent-protocol/pi-rpc/models.js.map +0 -1
- package/dist/agent-protocol/pi-rpc/session.d.ts.map +0 -1
- package/dist/agent-protocol/pi-rpc/session.js.map +0 -1
- package/dist/amr-model-cache.d.ts.map +0 -1
- package/dist/amr-model-cache.js.map +0 -1
- package/dist/amr-profile-resolver.d.ts.map +0 -1
- package/dist/amr-profile-resolver.js.map +0 -1
- package/dist/artifact-taxonomy.d.ts.map +0 -1
- package/dist/artifact-taxonomy.js.map +0 -1
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js.map +0 -1
- package/dist/capabilities.d.ts.map +0 -1
- package/dist/capabilities.js.map +0 -1
- package/dist/claude-stream.d.ts.map +0 -1
- package/dist/claude-stream.js.map +0 -1
- package/dist/copilot-stream.d.ts.map +0 -1
- package/dist/copilot-stream.js.map +0 -1
- package/dist/defs/aider.d.ts.map +0 -1
- package/dist/defs/aider.js.map +0 -1
- package/dist/defs/amp.d.ts.map +0 -1
- package/dist/defs/amp.js.map +0 -1
- package/dist/defs/amr.d.ts.map +0 -1
- package/dist/defs/amr.js.map +0 -1
- package/dist/defs/antigravity.d.ts.map +0 -1
- package/dist/defs/antigravity.js.map +0 -1
- package/dist/defs/claude.d.ts.map +0 -1
- package/dist/defs/claude.js.map +0 -1
- package/dist/defs/codebuddy.d.ts.map +0 -1
- package/dist/defs/codebuddy.js.map +0 -1
- package/dist/defs/codex.d.ts.map +0 -1
- package/dist/defs/codex.js.map +0 -1
- package/dist/defs/copilot.d.ts.map +0 -1
- package/dist/defs/copilot.js.map +0 -1
- package/dist/defs/cursor-agent.d.ts.map +0 -1
- package/dist/defs/cursor-agent.js.map +0 -1
- package/dist/defs/deepseek.d.ts.map +0 -1
- package/dist/defs/deepseek.js.map +0 -1
- package/dist/defs/devin.d.ts.map +0 -1
- package/dist/defs/devin.js.map +0 -1
- package/dist/defs/grok-build.d.ts.map +0 -1
- package/dist/defs/grok-build.js.map +0 -1
- package/dist/defs/hermes.d.ts.map +0 -1
- package/dist/defs/hermes.js.map +0 -1
- package/dist/defs/index.d.ts.map +0 -1
- package/dist/defs/index.js.map +0 -1
- package/dist/defs/kilo.d.ts.map +0 -1
- package/dist/defs/kilo.js.map +0 -1
- package/dist/defs/kimi.d.ts.map +0 -1
- package/dist/defs/kimi.js.map +0 -1
- package/dist/defs/kiro.d.ts.map +0 -1
- package/dist/defs/kiro.js.map +0 -1
- package/dist/defs/mimo.d.ts.map +0 -1
- package/dist/defs/mimo.js.map +0 -1
- package/dist/defs/opencode.d.ts.map +0 -1
- package/dist/defs/opencode.js.map +0 -1
- package/dist/defs/pi.d.ts.map +0 -1
- package/dist/defs/pi.js.map +0 -1
- package/dist/defs/qoder.d.ts.map +0 -1
- package/dist/defs/qoder.js.map +0 -1
- package/dist/defs/qwen.d.ts.map +0 -1
- package/dist/defs/qwen.js.map +0 -1
- package/dist/defs/reasonix.d.ts.map +0 -1
- package/dist/defs/reasonix.js.map +0 -1
- package/dist/defs/shared.d.ts.map +0 -1
- package/dist/defs/shared.js.map +0 -1
- package/dist/defs/trae-cli.d.ts.map +0 -1
- package/dist/defs/trae-cli.js.map +0 -1
- package/dist/defs/vibe.d.ts.map +0 -1
- package/dist/defs/vibe.js.map +0 -1
- package/dist/detection.d.ts.map +0 -1
- package/dist/detection.js.map +0 -1
- package/dist/diagnostics.d.ts.map +0 -1
- package/dist/diagnostics.js.map +0 -1
- package/dist/env.d.ts.map +0 -1
- package/dist/env.js.map +0 -1
- package/dist/executables.d.ts.map +0 -1
- package/dist/executables.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/invocation.d.ts.map +0 -1
- package/dist/invocation.js.map +0 -1
- package/dist/json-event-stream.d.ts.map +0 -1
- package/dist/json-event-stream.js.map +0 -1
- package/dist/launch.d.ts.map +0 -1
- package/dist/launch.js.map +0 -1
- package/dist/log-file.d.ts.map +0 -1
- package/dist/log-file.js.map +0 -1
- package/dist/mcp.d.ts.map +0 -1
- package/dist/mcp.js.map +0 -1
- package/dist/metadata.d.ts.map +0 -1
- package/dist/metadata.js.map +0 -1
- package/dist/mmd-routes.d.ts.map +0 -1
- package/dist/mmd-routes.js.map +0 -1
- package/dist/model-registry.d.ts.map +0 -1
- package/dist/model-registry.js.map +0 -1
- package/dist/models.d.ts.map +0 -1
- package/dist/models.js.map +0 -1
- package/dist/opencode-log.d.ts.map +0 -1
- package/dist/opencode-log.js.map +0 -1
- package/dist/paths.d.ts.map +0 -1
- package/dist/paths.js.map +0 -1
- package/dist/pi-models.d.ts.map +0 -1
- package/dist/pi-models.js.map +0 -1
- package/dist/prompt-augmenter.d.ts.map +0 -1
- package/dist/prompt-augmenter.js.map +0 -1
- package/dist/prompt-budget.d.ts.map +0 -1
- package/dist/prompt-budget.js.map +0 -1
- package/dist/prompt-file.d.ts.map +0 -1
- package/dist/prompt-file.js.map +0 -1
- package/dist/providers/aihubmix.d.ts.map +0 -1
- package/dist/providers/aihubmix.js.map +0 -1
- package/dist/providers/anthropic-messages.d.ts.map +0 -1
- package/dist/providers/anthropic-messages.js.map +0 -1
- package/dist/providers/azure-chat.d.ts.map +0 -1
- package/dist/providers/azure-chat.js.map +0 -1
- package/dist/providers/connection-guard.d.ts.map +0 -1
- package/dist/providers/connection-guard.js.map +0 -1
- package/dist/providers/connection-test.d.ts.map +0 -1
- package/dist/providers/connection-test.js.map +0 -1
- package/dist/providers/elevenlabs.d.ts.map +0 -1
- package/dist/providers/elevenlabs.js.map +0 -1
- package/dist/providers/google-messages.d.ts.map +0 -1
- package/dist/providers/google-messages.js.map +0 -1
- package/dist/providers/google.d.ts.map +0 -1
- package/dist/providers/google.js.map +0 -1
- package/dist/providers/index.d.ts.map +0 -1
- package/dist/providers/index.js.map +0 -1
- package/dist/providers/model-catalog.d.ts.map +0 -1
- package/dist/providers/model-catalog.js.map +0 -1
- package/dist/providers/oauth-callback-server.d.ts.map +0 -1
- package/dist/providers/oauth-callback-server.js.map +0 -1
- package/dist/providers/oauth-credentials.d.ts.map +0 -1
- package/dist/providers/oauth-credentials.js.map +0 -1
- package/dist/providers/oauth-provider.d.ts.map +0 -1
- package/dist/providers/oauth-provider.js.map +0 -1
- package/dist/providers/oauth-tokens.d.ts.map +0 -1
- package/dist/providers/oauth-tokens.js.map +0 -1
- package/dist/providers/ollama-chat.d.ts.map +0 -1
- package/dist/providers/ollama-chat.js.map +0 -1
- package/dist/providers/openai-chat.d.ts.map +0 -1
- package/dist/providers/openai-chat.js.map +0 -1
- package/dist/providers/pkce.d.ts.map +0 -1
- package/dist/providers/pkce.js.map +0 -1
- package/dist/providers/sse-decode.d.ts.map +0 -1
- package/dist/providers/sse-decode.js.map +0 -1
- package/dist/providers/token-params.d.ts.map +0 -1
- package/dist/providers/token-params.js.map +0 -1
- package/dist/providers/turn-end-guard.d.ts.map +0 -1
- package/dist/providers/turn-end-guard.js.map +0 -1
- package/dist/providers/types.d.ts.map +0 -1
- package/dist/providers/types.js.map +0 -1
- package/dist/qoder-stream.d.ts.map +0 -1
- package/dist/qoder-stream.js.map +0 -1
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js.map +0 -1
- package/dist/resolution.d.ts.map +0 -1
- package/dist/resolution.js.map +0 -1
- package/dist/role-marker-guard.d.ts.map +0 -1
- package/dist/role-marker-guard.js.map +0 -1
- package/dist/telemetry-sink.d.ts.map +0 -1
- package/dist/telemetry-sink.js.map +0 -1
- package/dist/terminal-launch.d.ts.map +0 -1
- package/dist/terminal-launch.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- 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
|
package/dist/defs/aider.d.ts
CHANGED
|
@@ -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;
|
package/dist/defs/aider.js
CHANGED
|
@@ -14,10 +14,19 @@ export const aiderAgentDef = {
|
|
|
14
14
|
fallbackModels: [
|
|
15
15
|
DEFAULT_MODEL_OPTION,
|
|
16
16
|
{ id: 'sonnet', label: 'sonnet' },
|
|
17
|
-
|
|
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
|
package/dist/defs/amr.d.ts
CHANGED
|
@@ -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
|
package/dist/defs/claude.d.ts
CHANGED
|
@@ -16,7 +16,8 @@ export declare const claudeAgentDef: {
|
|
|
16
16
|
'--append-system-prompt': string;
|
|
17
17
|
};
|
|
18
18
|
fallbackModels: import("../types.js").RuntimeModelOption[];
|
|
19
|
-
|
|
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;
|
package/dist/defs/claude.js
CHANGED
|
@@ -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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
85
|
-
//
|
|
86
|
-
//
|
|
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
|
-
|
|
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,
|
package/dist/defs/codex.d.ts
CHANGED
|
@@ -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;
|