@mono-agent/agent-app 0.6.2 → 0.8.0
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/README.md +210 -19
- package/dist/adapter-send-proxy.d.ts +18 -0
- package/dist/adapter-send-proxy.d.ts.map +1 -0
- package/dist/adapter-send-proxy.js +229 -0
- package/dist/adapter-send-proxy.js.map +1 -0
- package/dist/adapter-send-tools-main.js +62 -4
- package/dist/adapter-send-tools-main.js.map +1 -1
- package/dist/adapter-send-tools.d.ts +33 -6
- package/dist/adapter-send-tools.d.ts.map +1 -1
- package/dist/adapter-send-tools.js +485 -87
- package/dist/adapter-send-tools.js.map +1 -1
- package/dist/app-config.d.ts.map +1 -1
- package/dist/app-config.js +8 -0
- package/dist/app-config.js.map +1 -1
- package/dist/app.d.ts +2 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +528 -135
- package/dist/app.js.map +1 -1
- package/dist/broadcast-recorder.d.ts.map +1 -1
- package/dist/broadcast-recorder.js +24 -2
- package/dist/broadcast-recorder.js.map +1 -1
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +16 -6
- package/dist/channels.js.map +1 -1
- package/dist/cli.d.ts +76 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2102 -102
- package/dist/cli.js.map +1 -1
- package/dist/config-reference.d.ts.map +1 -1
- package/dist/config-reference.js +195 -6
- package/dist/config-reference.js.map +1 -1
- package/dist/configuration-proposal-main.d.ts +3 -0
- package/dist/configuration-proposal-main.d.ts.map +1 -0
- package/dist/configuration-proposal-main.js +13 -0
- package/dist/configuration-proposal-main.js.map +1 -0
- package/dist/configuration-proposal-tool.d.ts +26 -0
- package/dist/configuration-proposal-tool.d.ts.map +1 -0
- package/dist/configuration-proposal-tool.js +139 -0
- package/dist/configuration-proposal-tool.js.map +1 -0
- package/dist/configured-agent.d.ts +12 -0
- package/dist/configured-agent.d.ts.map +1 -1
- package/dist/configured-agent.js +150 -18
- package/dist/configured-agent.js.map +1 -1
- package/dist/consumer-contract.d.ts +3 -0
- package/dist/consumer-contract.d.ts.map +1 -1
- package/dist/consumer-contract.js +44 -1
- package/dist/consumer-contract.js.map +1 -1
- package/dist/doctor.d.ts +27 -5
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +1197 -101
- package/dist/doctor.js.map +1 -1
- package/dist/first-run-managed-memory.d.ts +39 -0
- package/dist/first-run-managed-memory.d.ts.map +1 -0
- package/dist/first-run-managed-memory.js +521 -0
- package/dist/first-run-managed-memory.js.map +1 -0
- package/dist/first-run-readiness.d.ts +111 -0
- package/dist/first-run-readiness.d.ts.map +1 -0
- package/dist/first-run-readiness.js +757 -0
- package/dist/first-run-readiness.js.map +1 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +88 -3
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +1212 -23
- package/dist/init.js.map +1 -1
- package/dist/interaction-bridge.d.ts +25 -1
- package/dist/interaction-bridge.d.ts.map +1 -1
- package/dist/interaction-bridge.js +281 -19
- package/dist/interaction-bridge.js.map +1 -1
- package/dist/local-configuration.d.ts +56 -0
- package/dist/local-configuration.d.ts.map +1 -0
- package/dist/local-configuration.js +914 -0
- package/dist/local-configuration.js.map +1 -0
- package/dist/memory-command.d.ts +4 -0
- package/dist/memory-command.d.ts.map +1 -1
- package/dist/memory-command.js +751 -27
- package/dist/memory-command.js.map +1 -1
- package/dist/memory-recall.d.ts +38 -10
- package/dist/memory-recall.d.ts.map +1 -1
- package/dist/memory-recall.js +82 -25
- package/dist/memory-recall.js.map +1 -1
- package/dist/memory-retrieval.d.ts +66 -0
- package/dist/memory-retrieval.d.ts.map +1 -0
- package/dist/memory-retrieval.js +332 -0
- package/dist/memory-retrieval.js.map +1 -0
- package/dist/modules/base.d.ts +6 -5
- package/dist/modules/base.d.ts.map +1 -1
- package/dist/modules/base.js +11 -7
- package/dist/modules/base.js.map +1 -1
- package/dist/modules/catalog.d.ts.map +1 -1
- package/dist/modules/catalog.js +54 -20
- package/dist/modules/catalog.js.map +1 -1
- package/dist/modules/index.d.ts +2 -2
- package/dist/modules/index.d.ts.map +1 -1
- package/dist/modules/index.js +1 -1
- package/dist/modules/index.js.map +1 -1
- package/dist/modules/known-tools.d.ts +3 -0
- package/dist/modules/known-tools.d.ts.map +1 -1
- package/dist/modules/known-tools.js +11 -1
- package/dist/modules/known-tools.js.map +1 -1
- package/dist/modules/types.d.ts +20 -6
- package/dist/modules/types.d.ts.map +1 -1
- package/dist/modules/types.js.map +1 -1
- package/dist/package-version.d.ts +3 -0
- package/dist/package-version.d.ts.map +1 -0
- package/dist/package-version.js +14 -0
- package/dist/package-version.js.map +1 -0
- package/dist/pi-auth-store-inspection.d.ts +21 -0
- package/dist/pi-auth-store-inspection.d.ts.map +1 -0
- package/dist/pi-auth-store-inspection.js +120 -0
- package/dist/pi-auth-store-inspection.js.map +1 -0
- package/dist/pi-oauth-login-main.d.ts +3 -0
- package/dist/pi-oauth-login-main.d.ts.map +1 -0
- package/dist/pi-oauth-login-main.js +14 -0
- package/dist/pi-oauth-login-main.js.map +1 -0
- package/dist/pi-oauth-login.d.ts +22 -0
- package/dist/pi-oauth-login.d.ts.map +1 -0
- package/dist/pi-oauth-login.js +79 -0
- package/dist/pi-oauth-login.js.map +1 -0
- package/dist/project-skills.d.ts +40 -0
- package/dist/project-skills.d.ts.map +1 -0
- package/dist/project-skills.js +573 -0
- package/dist/project-skills.js.map +1 -0
- package/dist/provider-setup.d.ts +117 -2
- package/dist/provider-setup.d.ts.map +1 -1
- package/dist/provider-setup.js +1505 -49
- package/dist/provider-setup.js.map +1 -1
- package/dist/readiness-probe-worker.d.ts +12 -0
- package/dist/readiness-probe-worker.d.ts.map +1 -0
- package/dist/readiness-probe-worker.js +276 -0
- package/dist/readiness-probe-worker.js.map +1 -0
- package/dist/readiness-probe.d.ts +100 -0
- package/dist/readiness-probe.d.ts.map +1 -0
- package/dist/readiness-probe.js +780 -0
- package/dist/readiness-probe.js.map +1 -0
- package/dist/request-model-override.d.ts +43 -1
- package/dist/request-model-override.d.ts.map +1 -1
- package/dist/request-model-override.js +178 -19
- package/dist/request-model-override.js.map +1 -1
- package/dist/run-history.d.ts +32 -0
- package/dist/run-history.d.ts.map +1 -0
- package/dist/run-history.js +859 -0
- package/dist/run-history.js.map +1 -0
- package/dist/runs-health.d.ts.map +1 -1
- package/dist/runs-health.js +12 -4
- package/dist/runs-health.js.map +1 -1
- package/dist/runtime-option-extensions.d.ts +14 -0
- package/dist/runtime-option-extensions.d.ts.map +1 -0
- package/dist/runtime-option-extensions.js +98 -0
- package/dist/runtime-option-extensions.js.map +1 -0
- package/dist/runtime-routes.d.ts +9 -0
- package/dist/runtime-routes.d.ts.map +1 -0
- package/dist/runtime-routes.js +13 -0
- package/dist/runtime-routes.js.map +1 -0
- package/dist/sandbox-manager.d.ts +69 -0
- package/dist/sandbox-manager.d.ts.map +1 -0
- package/dist/sandbox-manager.js +856 -0
- package/dist/sandbox-manager.js.map +1 -0
- package/dist/supermemory-plugin.d.ts +54 -0
- package/dist/supermemory-plugin.d.ts.map +1 -0
- package/dist/supermemory-plugin.js +136 -0
- package/dist/supermemory-plugin.js.map +1 -0
- package/dist/tui-command.d.ts +15 -0
- package/dist/tui-command.d.ts.map +1 -1
- package/dist/tui-command.js +46 -6
- package/dist/tui-command.js.map +1 -1
- package/dist/web-command.d.ts +8 -0
- package/dist/web-command.d.ts.map +1 -1
- package/dist/web-command.js +174 -17
- package/dist/web-command.js.map +1 -1
- package/dist/wizard/answers.d.ts +31 -4
- package/dist/wizard/answers.d.ts.map +1 -1
- package/dist/wizard/answers.js +145 -16
- package/dist/wizard/answers.js.map +1 -1
- package/dist/wizard/from-flags.d.ts +7 -0
- package/dist/wizard/from-flags.d.ts.map +1 -1
- package/dist/wizard/from-flags.js +36 -2
- package/dist/wizard/from-flags.js.map +1 -1
- package/dist/wizard/index.d.ts +4 -4
- package/dist/wizard/index.d.ts.map +1 -1
- package/dist/wizard/index.js +2 -2
- package/dist/wizard/index.js.map +1 -1
- package/dist/wizard/model-discovery.d.ts +33 -2
- package/dist/wizard/model-discovery.d.ts.map +1 -1
- package/dist/wizard/model-discovery.js +826 -108
- package/dist/wizard/model-discovery.js.map +1 -1
- package/dist/wizard/presets.d.ts +1 -1
- package/dist/wizard/presets.d.ts.map +1 -1
- package/dist/wizard/presets.js +7 -12
- package/dist/wizard/presets.js.map +1 -1
- package/dist/wizard/prompts.d.ts +56 -9
- package/dist/wizard/prompts.d.ts.map +1 -1
- package/dist/wizard/prompts.js +172 -20
- package/dist/wizard/prompts.js.map +1 -1
- package/dist/wizard/run.d.ts +59 -3
- package/dist/wizard/run.d.ts.map +1 -1
- package/dist/wizard/run.js +1133 -191
- package/dist/wizard/run.js.map +1 -1
- package/package.json +21 -16
- package/resources/srt/package-lock.json +66 -0
- package/resources/srt/package.json +9 -0
- package/schema/mono-agent.config.schema.json +381 -17
- package/skills/mono-agent-composer/SKILL.md +4 -4
- package/skills/mono-agent-composer/references/config-blueprint.md +5 -4
- package/skills/mono-agent-composer/references/discovery-questions.md +8 -8
- package/skills/mono-agent-composer/references/feature-coverage.md +8 -6
- package/skills/mono-agent-composer/references/package-map.md +1 -1
- package/skills/mono-agent-composer/references/playbooks.md +8 -7
- package/skills/mono-agent-composer/references/validation.md +1 -1
|
@@ -1,43 +1,94 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { chmod, mkdir, mkdtemp, open, rm } from "node:fs/promises";
|
|
3
|
+
import { homedir, tmpdir } from "node:os";
|
|
3
4
|
import { join } from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import { createInterface } from "node:readline";
|
|
6
|
+
import { getSupportedThinkingLevels } from "@earendil-works/pi-ai";
|
|
7
|
+
import { getOAuthProviders } from "@earendil-works/pi-ai/oauth";
|
|
8
|
+
import { builtinModels } from "@earendil-works/pi-ai/providers/all";
|
|
9
|
+
import { discoverClaudeSdkModels } from "@mono-agent/runtime-adapter";
|
|
10
|
+
import { inspectPiAuthStore as inspectDefaultPiAuthStore, } from "../pi-auth-store-inspection.js";
|
|
11
|
+
import { credentialNeutralProviderStatusEnvironment, hasDurableProviderEnvironmentCredential, runBoundedProviderCommand, } from "../provider-setup.js";
|
|
12
|
+
/** Cloud Pi providers that guided onboarding can authenticate and prove end to end. */
|
|
13
|
+
export const GUIDED_PI_PROVIDER_IDS = [
|
|
14
|
+
"anthropic",
|
|
15
|
+
"github-copilot",
|
|
16
|
+
"openai-codex",
|
|
17
|
+
"opencode-go",
|
|
18
|
+
];
|
|
19
|
+
const GUIDED_PI_PROVIDERS = new Set(GUIDED_PI_PROVIDER_IDS);
|
|
20
|
+
const GUIDED_LOCAL_PI_PROVIDERS = new Set(["ollama", "lmstudio"]);
|
|
21
|
+
/** Reject known unsupported remote Pi integrations while leaving custom local ids available. */
|
|
22
|
+
export function guidedPiProviderProblem(provider) {
|
|
23
|
+
if (GUIDED_PI_PROVIDERS.has(provider) || GUIDED_LOCAL_PI_PROVIDERS.has(provider)) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
return builtinModels().getProvider(provider) === undefined
|
|
27
|
+
? "Custom Pi providers require a hand-authored providers.local[] entry. Guided init supports discovered Ollama and LM Studio routes."
|
|
28
|
+
: "Guided init supports Pi Anthropic, GitHub Copilot, OpenAI Codex, OpenCode-Go, Ollama, and LM Studio. Configure other Pi providers manually.";
|
|
29
|
+
}
|
|
30
|
+
function providerDiscoveryCommand(opts) {
|
|
31
|
+
if (opts.execFile !== undefined) {
|
|
32
|
+
return (file, args, commandOptions) => opts.execFile(file, args, {
|
|
33
|
+
...commandOptions,
|
|
34
|
+
env: commandOptions.env ?? safeDiscoveryProcessEnv(),
|
|
35
|
+
...(opts.abortSignal === undefined ? {} : { abortSignal: opts.abortSignal }),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return (file, args, commandOptions) => runBoundedProviderCommand(file, args, {
|
|
39
|
+
...commandOptions,
|
|
40
|
+
cwd: process.cwd(),
|
|
41
|
+
env: commandOptions.env ?? safeDiscoveryProcessEnv(),
|
|
42
|
+
...(opts.abortSignal === undefined ? {} : { abortSignal: opts.abortSignal }),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
7
45
|
const DEFAULT_DISCOVERY_TIMEOUT_MS = 1200;
|
|
46
|
+
const DEFAULT_OPENCODE_DISCOVERY_TIMEOUT_MS = 5000;
|
|
8
47
|
const PI_OPENAI_CODEX_PROVIDER = "openai-codex";
|
|
9
|
-
const
|
|
10
|
-
|
|
48
|
+
const OPENAI_CODEX_MODELS = [
|
|
49
|
+
{ id: "gpt-5.6-terra", name: "GPT-5.6 Terra", minimumCodexCliVersion: [0, 144, 0] },
|
|
50
|
+
{ id: "gpt-5.6-sol", name: "GPT-5.6 Sol", minimumCodexCliVersion: [0, 144, 0] },
|
|
51
|
+
];
|
|
11
52
|
export const STATIC_MODEL_CANDIDATES = [
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
53
|
+
...OPENAI_CODEX_MODELS.map(staticDirectCodexCandidate),
|
|
54
|
+
...[
|
|
55
|
+
["claude-sonnet-5", "Claude Sonnet 5", ["low", "medium", "high", "xhigh", "max"]],
|
|
56
|
+
["claude-opus-4-8[1m]", "Claude Opus 4.8 (1M context)", ["low", "medium", "high", "xhigh", "max"]],
|
|
57
|
+
["claude-haiku-4-5-20251001", "Claude Haiku 4.5", []],
|
|
58
|
+
].map(([model, label, efforts]) => ({
|
|
59
|
+
value: `claude:${model}`,
|
|
60
|
+
label: label,
|
|
61
|
+
hint: "SDK-versioned cached catalog; sign-in required",
|
|
62
|
+
source: "claude",
|
|
63
|
+
availability: "catalog_available",
|
|
64
|
+
authState: "auth_required",
|
|
65
|
+
supportedEfforts: efforts,
|
|
17
66
|
setupRequired: true,
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
{ value: DIRECT_CODEX, label: "Codex GPT-5.5", source: "codex", defaultEffort: "medium" },
|
|
21
|
-
{ value: "claude:claude-sonnet-4-6", label: "Claude Sonnet 4.6", source: "claude", defaultEffort: "medium" },
|
|
22
|
-
{ value: "pi:ollama:llama3.1:8b", label: "Ollama llama3.1:8b", hint: "fully local", source: "ollama", defaultEffort: "none" },
|
|
67
|
+
})),
|
|
23
68
|
];
|
|
24
69
|
export async function discoverWizardModelCandidates(opts = {}) {
|
|
25
70
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_DISCOVERY_TIMEOUT_MS;
|
|
26
|
-
const [pi, opencode, ollama, lmstudio] = await Promise.all([
|
|
27
|
-
|
|
28
|
-
|
|
71
|
+
const [codex, pi, claude, opencode, ollama, lmstudio] = await Promise.all([
|
|
72
|
+
discoverDirectCodex({ ...opts, timeoutMs }),
|
|
73
|
+
discoverPiModels({ ...opts, timeoutMs }),
|
|
74
|
+
discoverClaudeModels({ ...opts, timeoutMs }),
|
|
75
|
+
discoverOpenCodeModels({
|
|
76
|
+
...opts,
|
|
77
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_OPENCODE_DISCOVERY_TIMEOUT_MS,
|
|
78
|
+
}),
|
|
29
79
|
discoverOllamaModels({ ...opts, timeoutMs }),
|
|
30
80
|
discoverLmStudioModels({ ...opts, timeoutMs }),
|
|
31
81
|
]);
|
|
32
82
|
return {
|
|
33
83
|
candidates: rankWizardModelCandidates([
|
|
34
|
-
...
|
|
84
|
+
...codex.candidates,
|
|
35
85
|
...pi.candidates,
|
|
86
|
+
...claude.candidates,
|
|
36
87
|
...opencode.candidates,
|
|
37
88
|
...ollama.candidates,
|
|
38
89
|
...lmstudio.candidates,
|
|
39
90
|
]),
|
|
40
|
-
statuses: [pi.status, opencode.status, ollama.status, lmstudio.status],
|
|
91
|
+
statuses: [codex.status, pi.status, claude.status, opencode.status, ollama.status, lmstudio.status],
|
|
41
92
|
};
|
|
42
93
|
}
|
|
43
94
|
export function rankWizardModelCandidates(candidates) {
|
|
@@ -58,9 +109,6 @@ export function defaultEffortForModelRef(modelRef, reasoning) {
|
|
|
58
109
|
if (reasoning === false) {
|
|
59
110
|
return "none";
|
|
60
111
|
}
|
|
61
|
-
if (modelRef.startsWith("claude:") || modelRef.startsWith("codex:") || modelRef.startsWith("pi:openai-codex:")) {
|
|
62
|
-
return "medium";
|
|
63
|
-
}
|
|
64
112
|
if (!modelRef.startsWith("pi:")) {
|
|
65
113
|
return undefined;
|
|
66
114
|
}
|
|
@@ -71,66 +119,402 @@ export function defaultEffortForModelRef(modelRef, reasoning) {
|
|
|
71
119
|
}
|
|
72
120
|
return undefined;
|
|
73
121
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
122
|
+
/** Detect the direct Codex CLI and its account catalog without claiming readiness. */
|
|
123
|
+
async function discoverDirectCodex(opts) {
|
|
124
|
+
const run = providerDiscoveryCommand(opts);
|
|
125
|
+
const verified = new Set(opts.verifiedModelRefs ?? []);
|
|
126
|
+
let version = "";
|
|
77
127
|
try {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
128
|
+
const result = await run("codex", ["--version"], { timeout: opts.timeoutMs });
|
|
129
|
+
version = firstOutputLine(result.stdout);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return {
|
|
133
|
+
candidates: directCodexCandidates(curatedCodexCatalog(), "install-required", "", verified),
|
|
134
|
+
status: {
|
|
135
|
+
provider: "Codex",
|
|
136
|
+
status: "setup_available",
|
|
137
|
+
detail: "CLI not found; install Codex and sign in before the readiness check",
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
let catalog = curatedCodexCatalog();
|
|
142
|
+
try {
|
|
143
|
+
// Injected command runners are normally deterministic unit-test seams; do
|
|
144
|
+
// not escape them by starting a real app-server unless a model-list seam
|
|
145
|
+
// was also supplied.
|
|
146
|
+
const discoveredCatalog = opts.codexModelList !== undefined
|
|
147
|
+
? [...await opts.codexModelList()]
|
|
148
|
+
: opts.execFile === undefined
|
|
149
|
+
? await requestCodexModelList(opts.timeoutMs, codexModelDiscoveryEnvironment(opts.persistedEnv), opts.abortSignal)
|
|
150
|
+
: catalog;
|
|
151
|
+
if (discoveredCatalog.length > 0)
|
|
152
|
+
catalog = discoveredCatalog;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// The exact curated Sol/Terra fallback remains selectable offline.
|
|
156
|
+
}
|
|
157
|
+
const durableCredential = hasDurableProviderEnvironmentCredential("codex:gpt-5.6-terra", opts.persistedEnv ?? {});
|
|
158
|
+
const shellOnlyCredential = !durableCredential
|
|
159
|
+
&& hasDurableProviderEnvironmentCredential("codex:gpt-5.6-terra", process.env);
|
|
160
|
+
try {
|
|
161
|
+
if (!durableCredential) {
|
|
162
|
+
await run("codex", ["login", "status"], {
|
|
163
|
+
timeout: opts.timeoutMs,
|
|
164
|
+
env: credentialNeutralProviderStatusEnvironment(process.env, opts.persistedEnv),
|
|
165
|
+
});
|
|
85
166
|
}
|
|
167
|
+
const candidates = directCodexCandidates(catalog, "credential-detected", version, verified);
|
|
168
|
+
const setupModels = candidates.filter((candidate) => candidate.setupRequired === true);
|
|
169
|
+
const setupDetails = [...new Set(setupModels.map((candidate) => candidate.hint ?? `${candidate.label} setup required`))];
|
|
86
170
|
return {
|
|
87
|
-
candidates
|
|
88
|
-
status: {
|
|
171
|
+
candidates,
|
|
172
|
+
status: {
|
|
173
|
+
provider: "Codex",
|
|
174
|
+
status: setupModels.length === 0 ? "detected" : "setup_available",
|
|
175
|
+
detail: `${version.length > 0 ? `${version}; ` : ""}${durableCredential ? "durable OPENAI_API_KEY detected" : "sign-in detected"} (live readiness not yet verified)${setupModels.length === 0
|
|
176
|
+
? ""
|
|
177
|
+
: `; ${setupDetails.join("; ")}`}`,
|
|
178
|
+
},
|
|
89
179
|
};
|
|
90
180
|
}
|
|
91
|
-
catch
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
181
|
+
catch {
|
|
182
|
+
return {
|
|
183
|
+
candidates: directCodexCandidates(catalog, "login-required", version, verified),
|
|
184
|
+
status: {
|
|
185
|
+
provider: "Codex",
|
|
186
|
+
status: "setup_available",
|
|
187
|
+
detail: `${version.length > 0 ? `${version}; ` : "CLI installed; "}${shellOnlyCredential
|
|
188
|
+
? "durable sign-in required; shell-only OPENAI_API_KEY ignored"
|
|
189
|
+
: "sign-in required"}`,
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async function discoverPiModels(opts) {
|
|
195
|
+
const inspectAuthStore = opts.inspectPiAuthStore ?? inspectDefaultPiAuthStore;
|
|
196
|
+
const authPath = opts.piAuthPath ?? join(homedir(), ".pi", "agent", "auth.json");
|
|
197
|
+
let credentialProviders = new Set();
|
|
198
|
+
let status;
|
|
199
|
+
try {
|
|
200
|
+
const inspection = await inspectAuthStore(authPath);
|
|
201
|
+
if (inspection.status === "missing") {
|
|
202
|
+
status = { provider: "Pi", status: "setup_available", detail: "auth store not found; provider setup is available" };
|
|
203
|
+
}
|
|
204
|
+
else if (inspection.status === "unsafe") {
|
|
205
|
+
status = {
|
|
206
|
+
provider: "Pi",
|
|
207
|
+
status: "unavailable",
|
|
208
|
+
detail: `auth store rejected as unsafe (${inspection.reason}); catalog remains available`,
|
|
96
209
|
};
|
|
97
210
|
}
|
|
98
|
-
|
|
211
|
+
else {
|
|
212
|
+
const providers = readPiAuthProviderMap(inspection.auth);
|
|
213
|
+
credentialProviders = new Set(Object.entries(providers)
|
|
214
|
+
.filter(([provider, credential]) => GUIDED_PI_PROVIDERS.has(provider) && hasUsablePiCredential(credential))
|
|
215
|
+
.map(([provider]) => provider));
|
|
216
|
+
status = credentialProviders.size > 0
|
|
217
|
+
? {
|
|
218
|
+
provider: "Pi",
|
|
219
|
+
status: "detected",
|
|
220
|
+
detail: `${credentialProviders.size} provider credential entr${credentialProviders.size === 1 ? "y" : "ies"} detected (not yet verified)`,
|
|
221
|
+
}
|
|
222
|
+
: { provider: "Pi", status: "setup_available", detail: "credential store is empty; setup is available" };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
status = { provider: "Pi", status: "unavailable", detail: "auth store unreadable; catalog remains available" };
|
|
227
|
+
}
|
|
228
|
+
const preEnvironmentStatus = status;
|
|
229
|
+
for (const provider of GUIDED_PI_PROVIDER_IDS) {
|
|
230
|
+
if (hasDurableProviderEnvironmentCredential(`pi:${provider}:credential-check`, opts.persistedEnv ?? {})) {
|
|
231
|
+
credentialProviders.add(provider);
|
|
232
|
+
}
|
|
99
233
|
}
|
|
234
|
+
if (credentialProviders.size > 0 && preEnvironmentStatus.status !== "detected") {
|
|
235
|
+
status = {
|
|
236
|
+
provider: "Pi",
|
|
237
|
+
status: "detected",
|
|
238
|
+
detail: `${credentialProviders.size} provider credential source${credentialProviders.size === 1 ? "" : "s"} detected (not yet verified); ${preEnvironmentStatus.detail}`,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
else if (credentialProviders.size === 0
|
|
242
|
+
&& hasDurableProviderEnvironmentCredential("pi:opencode-go:credential-check", process.env)) {
|
|
243
|
+
status = {
|
|
244
|
+
provider: "Pi",
|
|
245
|
+
status: "setup_available",
|
|
246
|
+
detail: `${preEnvironmentStatus.detail}; shell-only OPENCODE_API_KEY ignored until persisted in the agent .env or Pi auth store`,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
const verified = new Set(opts.verifiedModelRefs ?? []);
|
|
250
|
+
const oauthProviders = new Set(getOAuthProviders().map((provider) => provider.id));
|
|
251
|
+
const models = builtinModels();
|
|
252
|
+
const providerNames = new Map(models.getProviders().map((provider) => [provider.id, provider.name]));
|
|
253
|
+
const candidates = models.getModels()
|
|
254
|
+
.filter((model) => GUIDED_PI_PROVIDERS.has(model.provider))
|
|
255
|
+
.map((model) => {
|
|
256
|
+
const value = `pi:${model.provider}:${model.id}`;
|
|
257
|
+
const supportedEfforts = normalizeEfforts(getSupportedThinkingLevels(model));
|
|
258
|
+
const authState = verified.has(value)
|
|
259
|
+
? "verified"
|
|
260
|
+
: credentialProviders.has(model.provider)
|
|
261
|
+
? "credential_detected"
|
|
262
|
+
: "auth_required";
|
|
263
|
+
const defaultEffort = exactDefaultEffortWhenUnambiguous(supportedEfforts);
|
|
264
|
+
const shellOnlyCredential = authState === "auth_required"
|
|
265
|
+
&& hasDurableProviderEnvironmentCredential(value, process.env);
|
|
266
|
+
const providerLabel = model.provider === "opencode-go"
|
|
267
|
+
? "OpenCode-Go"
|
|
268
|
+
: providerNames.get(model.provider) ?? model.provider;
|
|
269
|
+
return {
|
|
270
|
+
value,
|
|
271
|
+
label: `Pi ${providerLabel} · ${model.name || model.id}`,
|
|
272
|
+
hint: authState === "verified"
|
|
273
|
+
? "verified by live readiness"
|
|
274
|
+
: authState === "credential_detected"
|
|
275
|
+
? "credential detected; live readiness pending"
|
|
276
|
+
: shellOnlyCredential
|
|
277
|
+
? "shell-only credential ignored; persist it in the agent .env or provider store"
|
|
278
|
+
: oauthProviders.has(model.provider)
|
|
279
|
+
? "OAuth setup available"
|
|
280
|
+
: "API key or provider environment required",
|
|
281
|
+
source: "pi",
|
|
282
|
+
availability: "catalog_available",
|
|
283
|
+
authState,
|
|
284
|
+
supportedEfforts,
|
|
285
|
+
...(defaultEffort === undefined ? {} : { defaultEffort }),
|
|
286
|
+
...(authState === "verified" ? { discovered: true } : {}),
|
|
287
|
+
...(authState === "auth_required" ? { setupRequired: true } : {}),
|
|
288
|
+
};
|
|
289
|
+
});
|
|
290
|
+
return { candidates, status };
|
|
100
291
|
}
|
|
101
292
|
function readPiAuthProviderMap(auth) {
|
|
102
|
-
const nestedProviders = parseJsonObject(auth.providers);
|
|
103
|
-
|
|
293
|
+
const nestedProviders = auth.providers === undefined ? {} : parseJsonObject(auth.providers);
|
|
294
|
+
const { providers: _providers, ...topLevelProviders } = auth;
|
|
295
|
+
return { ...nestedProviders, ...topLevelProviders };
|
|
296
|
+
}
|
|
297
|
+
function hasUsablePiCredential(value) {
|
|
298
|
+
if (!isRecord(value))
|
|
299
|
+
return false;
|
|
300
|
+
if (value.type === "oauth") {
|
|
301
|
+
return isCredentialString(value.access) || isCredentialString(value.refresh);
|
|
302
|
+
}
|
|
303
|
+
return value.type === "api_key" && isCredentialString(value.key);
|
|
304
|
+
}
|
|
305
|
+
function isCredentialString(value) {
|
|
306
|
+
return typeof value === "string"
|
|
307
|
+
&& value.trim().length > 0
|
|
308
|
+
&& value.length <= 65_536
|
|
309
|
+
&& !value.includes("\0");
|
|
310
|
+
}
|
|
311
|
+
async function discoverClaudeModels(opts) {
|
|
312
|
+
const run = providerDiscoveryCommand(opts);
|
|
313
|
+
const verified = new Set(opts.verifiedModelRefs ?? []);
|
|
314
|
+
let credentialDetected = hasDurableProviderEnvironmentCredential("claude:claude-sonnet-5", opts.persistedEnv ?? {});
|
|
315
|
+
const durableCredential = credentialDetected;
|
|
316
|
+
const shellOnlyCredential = !durableCredential
|
|
317
|
+
&& hasDurableProviderEnvironmentCredential("claude:claude-sonnet-5", process.env);
|
|
318
|
+
if (!credentialDetected) {
|
|
319
|
+
try {
|
|
320
|
+
await run("claude", ["auth", "status", "--json"], {
|
|
321
|
+
timeout: opts.timeoutMs,
|
|
322
|
+
env: credentialNeutralProviderStatusEnvironment(process.env, opts.persistedEnv),
|
|
323
|
+
});
|
|
324
|
+
credentialDetected = true;
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
// Catalog discovery is deliberately independent from account state.
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
let catalog = [];
|
|
331
|
+
try {
|
|
332
|
+
catalog = opts.claudeModelList !== undefined
|
|
333
|
+
? await opts.claudeModelList()
|
|
334
|
+
: opts.execFile === undefined
|
|
335
|
+
? await discoverClaudeSdkModels({ timeoutMs: Math.max(opts.timeoutMs, 2_000) })
|
|
336
|
+
: await discoverClaudeSdkModels({ timeoutMs: 1 });
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
// Static SDK-versioned rows remain available through STATIC_MODEL_CANDIDATES.
|
|
340
|
+
}
|
|
341
|
+
const candidates = catalog.map((model) => {
|
|
342
|
+
const value = model.reference;
|
|
343
|
+
const authState = verified.has(value)
|
|
344
|
+
? "verified"
|
|
345
|
+
: credentialDetected
|
|
346
|
+
? "credential_detected"
|
|
347
|
+
: "auth_required";
|
|
348
|
+
const supportedEfforts = normalizeEfforts(model.supportedEfforts);
|
|
349
|
+
return {
|
|
350
|
+
value,
|
|
351
|
+
label: model.displayName,
|
|
352
|
+
hint: `${model.source === "discovered" ? "discovered from Claude SDK" : "SDK-versioned cached catalog"}; ${authState === "verified" ? "verified" : authState === "credential_detected" ? "credential detected; live readiness pending" : "sign-in required"}`,
|
|
353
|
+
source: "claude",
|
|
354
|
+
availability: "catalog_available",
|
|
355
|
+
authState,
|
|
356
|
+
supportedEfforts,
|
|
357
|
+
...(authState === "verified" ? { discovered: true } : {}),
|
|
358
|
+
...(authState === "auth_required" ? { setupRequired: true } : {}),
|
|
359
|
+
};
|
|
360
|
+
});
|
|
361
|
+
return {
|
|
362
|
+
candidates,
|
|
363
|
+
status: credentialDetected
|
|
364
|
+
? {
|
|
365
|
+
provider: "Claude",
|
|
366
|
+
status: "detected",
|
|
367
|
+
detail: `${durableCredential ? "durable provider credential" : "sign-in"} detected (live readiness not yet verified)`,
|
|
368
|
+
}
|
|
369
|
+
: {
|
|
370
|
+
provider: "Claude",
|
|
371
|
+
status: "setup_available",
|
|
372
|
+
detail: `${shellOnlyCredential
|
|
373
|
+
? "durable sign-in required; shell-only Claude credential ignored"
|
|
374
|
+
: "sign-in required"}; catalog remains available`,
|
|
375
|
+
},
|
|
376
|
+
};
|
|
104
377
|
}
|
|
105
378
|
async function discoverOpenCodeModels(opts) {
|
|
106
|
-
const run = opts
|
|
379
|
+
const run = providerDiscoveryCommand(opts);
|
|
380
|
+
let isolation;
|
|
107
381
|
try {
|
|
108
|
-
|
|
109
|
-
const
|
|
382
|
+
isolation = await createOpenCodeDiscoveryIsolation();
|
|
383
|
+
const { stdout } = await run("opencode", ["models", "opencode-go", "--pure"], {
|
|
384
|
+
timeout: opts.timeoutMs,
|
|
385
|
+
env: isolation.env,
|
|
386
|
+
});
|
|
387
|
+
const bundledModelIds = new Set(builtinModels().getModels()
|
|
388
|
+
.filter((model) => model.provider === "opencode-go")
|
|
389
|
+
.map((model) => model.id));
|
|
390
|
+
const parsedModels = parseOpenCodeGoModels(stdout);
|
|
391
|
+
const models = parsedModels.filter((model) => bundledModelIds.has(model));
|
|
392
|
+
const unsupportedCount = parsedModels.length - models.length;
|
|
110
393
|
return {
|
|
111
394
|
candidates: models.map((model) => {
|
|
112
|
-
const value =
|
|
113
|
-
const defaultEffort = defaultEffortForModelRef(value, model.reasoning);
|
|
395
|
+
const value = `pi:opencode-go:${model}`;
|
|
114
396
|
return {
|
|
115
397
|
value,
|
|
116
|
-
label: `OpenCode ${displayModelName(model
|
|
117
|
-
hint: "discovered from opencode",
|
|
398
|
+
label: `OpenCode-Go ${displayModelName(model)}`,
|
|
399
|
+
hint: "discovered from opencode; OpenCode-Go API key required",
|
|
118
400
|
source: "opencode",
|
|
119
401
|
discovered: true,
|
|
120
|
-
|
|
402
|
+
availability: "catalog_available",
|
|
403
|
+
authState: "auth_required",
|
|
404
|
+
setupRequired: true,
|
|
405
|
+
supportedEfforts: [],
|
|
121
406
|
};
|
|
122
407
|
}),
|
|
123
408
|
status: models.length > 0
|
|
124
|
-
? {
|
|
125
|
-
|
|
409
|
+
? {
|
|
410
|
+
provider: "OpenCode-Go",
|
|
411
|
+
status: "detected",
|
|
412
|
+
detail: `${models.length} runtime-supported model${models.length === 1 ? "" : "s"} found${unsupportedCount === 0
|
|
413
|
+
? ""
|
|
414
|
+
: `; ${unsupportedCount} unsupported CLI row${unsupportedCount === 1 ? "" : "s"} ignored`}`,
|
|
415
|
+
}
|
|
416
|
+
: parsedModels.length > 0
|
|
417
|
+
? {
|
|
418
|
+
provider: "OpenCode-Go",
|
|
419
|
+
status: "unavailable",
|
|
420
|
+
detail: `no runtime-supported models; ${parsedModels.length} unsupported CLI row${parsedModels.length === 1 ? "" : "s"} ignored`,
|
|
421
|
+
}
|
|
422
|
+
: { provider: "OpenCode-Go", status: "unavailable", detail: "no models returned" },
|
|
126
423
|
};
|
|
127
424
|
}
|
|
128
425
|
catch {
|
|
129
|
-
return { candidates: [], status: { provider: "OpenCode", status: "unavailable", detail: "`opencode models --
|
|
426
|
+
return { candidates: [], status: { provider: "OpenCode-Go", status: "unavailable", detail: "`opencode models opencode-go --pure` unavailable" } };
|
|
427
|
+
}
|
|
428
|
+
finally {
|
|
429
|
+
await isolation?.cleanup();
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
async function createOpenCodeDiscoveryIsolation() {
|
|
433
|
+
const root = await mkdtemp(join(tmpdir(), "mono-agent-opencode-discovery-"));
|
|
434
|
+
try {
|
|
435
|
+
await chmod(root, 0o700);
|
|
436
|
+
const home = await createPrivateDirectory(root, "home");
|
|
437
|
+
const config = await createPrivateDirectory(root, "config");
|
|
438
|
+
const opencodeConfig = await createPrivateDirectory(config, "opencode");
|
|
439
|
+
if (process.platform !== "win32")
|
|
440
|
+
await chmod(opencodeConfig, 0o500);
|
|
441
|
+
const data = await createPrivateDirectory(root, "data");
|
|
442
|
+
const state = await createPrivateDirectory(root, "state");
|
|
443
|
+
const cache = await createPrivateDirectory(root, "cache");
|
|
444
|
+
const opencodeData = await createPrivateDirectory(data, "opencode");
|
|
445
|
+
const database = join(opencodeData, "opencode.db");
|
|
446
|
+
const handle = await open(database, "wx", 0o600);
|
|
447
|
+
await handle.close();
|
|
448
|
+
await chmod(database, 0o600);
|
|
449
|
+
const env = safeDiscoveryProcessEnv();
|
|
450
|
+
Object.assign(env, {
|
|
451
|
+
OPENCODE_TEST_HOME: home,
|
|
452
|
+
XDG_CONFIG_HOME: config,
|
|
453
|
+
XDG_CONFIG_DIRS: config,
|
|
454
|
+
XDG_DATA_HOME: data,
|
|
455
|
+
XDG_DATA_DIRS: data,
|
|
456
|
+
XDG_STATE_HOME: state,
|
|
457
|
+
XDG_CACHE_HOME: cache,
|
|
458
|
+
OPENCODE_DB: database,
|
|
459
|
+
OPENCODE_CONFIG_CONTENT: JSON.stringify({ share: "disabled", autoshare: false }),
|
|
460
|
+
OPENCODE_DISABLE_PROJECT_CONFIG: "true",
|
|
461
|
+
OPENCODE_DISABLE_AUTOUPDATE: "true",
|
|
462
|
+
OPENCODE_DISABLE_SHARE: "true",
|
|
463
|
+
OPENCODE_AUTO_SHARE: "false",
|
|
464
|
+
OPENCODE_DISABLE_EXTERNAL_SKILLS: "true",
|
|
465
|
+
OPENCODE_DISABLE_LSP_DOWNLOAD: "true",
|
|
466
|
+
});
|
|
467
|
+
return {
|
|
468
|
+
env,
|
|
469
|
+
cleanup: async () => {
|
|
470
|
+
await rm(root, { recursive: true, force: true }).catch(() => undefined);
|
|
471
|
+
},
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
catch (error) {
|
|
475
|
+
await rm(root, { recursive: true, force: true }).catch(() => undefined);
|
|
476
|
+
throw error;
|
|
130
477
|
}
|
|
131
478
|
}
|
|
479
|
+
function safeDiscoveryProcessEnv(source = process.env) {
|
|
480
|
+
const env = {};
|
|
481
|
+
for (const key of [
|
|
482
|
+
"PATH",
|
|
483
|
+
"LANG",
|
|
484
|
+
"LC_ALL",
|
|
485
|
+
"LC_CTYPE",
|
|
486
|
+
"SHELL",
|
|
487
|
+
"TMPDIR",
|
|
488
|
+
"TMP",
|
|
489
|
+
"TEMP",
|
|
490
|
+
"SystemRoot",
|
|
491
|
+
"WINDIR",
|
|
492
|
+
"ComSpec",
|
|
493
|
+
"PATHEXT",
|
|
494
|
+
]) {
|
|
495
|
+
const value = source[key];
|
|
496
|
+
if (typeof value === "string" && value.length > 0)
|
|
497
|
+
env[key] = value;
|
|
498
|
+
}
|
|
499
|
+
return env;
|
|
500
|
+
}
|
|
501
|
+
/** Exact non-secret environment used by live Codex model/list discovery. */
|
|
502
|
+
export function codexModelDiscoveryEnvironment(persistedEnv = {}, shellEnv = process.env) {
|
|
503
|
+
const neutral = credentialNeutralProviderStatusEnvironment(shellEnv, persistedEnv);
|
|
504
|
+
return {
|
|
505
|
+
...safeDiscoveryProcessEnv(shellEnv),
|
|
506
|
+
HOME: neutral.HOME ?? homedir(),
|
|
507
|
+
...(neutral.CODEX_HOME === undefined ? {} : { CODEX_HOME: neutral.CODEX_HOME }),
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
async function createPrivateDirectory(parent, name) {
|
|
511
|
+
const path = join(parent, name);
|
|
512
|
+
await mkdir(path, { mode: 0o700 });
|
|
513
|
+
await chmod(path, 0o700);
|
|
514
|
+
return path;
|
|
515
|
+
}
|
|
132
516
|
async function discoverOllamaModels(opts) {
|
|
133
|
-
const run = opts
|
|
517
|
+
const run = providerDiscoveryCommand(opts);
|
|
134
518
|
try {
|
|
135
519
|
const { stdout } = await run("ollama", ["list"], { timeout: opts.timeoutMs });
|
|
136
520
|
const models = parseOllamaList(stdout);
|
|
@@ -143,7 +527,9 @@ async function discoverOllamaModels(opts) {
|
|
|
143
527
|
hint: "discovered locally",
|
|
144
528
|
source: "ollama",
|
|
145
529
|
discovered: true,
|
|
146
|
-
|
|
530
|
+
availability: "catalog_available",
|
|
531
|
+
authState: "not_required",
|
|
532
|
+
supportedEfforts: [],
|
|
147
533
|
};
|
|
148
534
|
}),
|
|
149
535
|
status: models.length > 0
|
|
@@ -159,6 +545,8 @@ async function discoverLmStudioModels(opts) {
|
|
|
159
545
|
const fetchImpl = opts.fetch ?? fetch;
|
|
160
546
|
const controller = new AbortController();
|
|
161
547
|
const timer = setTimeout(() => controller.abort(), opts.timeoutMs);
|
|
548
|
+
const onAbort = () => controller.abort();
|
|
549
|
+
opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
162
550
|
try {
|
|
163
551
|
const response = await fetchImpl("http://localhost:1234/v1/models", { signal: controller.signal });
|
|
164
552
|
if (!response.ok) {
|
|
@@ -175,7 +563,10 @@ async function discoverLmStudioModels(opts) {
|
|
|
175
563
|
hint: "discovered locally",
|
|
176
564
|
source: "lmstudio",
|
|
177
565
|
discovered: true,
|
|
178
|
-
|
|
566
|
+
availability: "catalog_available",
|
|
567
|
+
authState: "not_required",
|
|
568
|
+
supportedEfforts: model.reasoning === false ? ["none"] : [],
|
|
569
|
+
...(model.reasoning === false ? { defaultEffort: "none" } : {}),
|
|
179
570
|
};
|
|
180
571
|
}),
|
|
181
572
|
status: models.length > 0
|
|
@@ -188,31 +579,68 @@ async function discoverLmStudioModels(opts) {
|
|
|
188
579
|
}
|
|
189
580
|
finally {
|
|
190
581
|
clearTimeout(timer);
|
|
582
|
+
opts.abortSignal?.removeEventListener("abort", onAbort);
|
|
191
583
|
}
|
|
192
584
|
}
|
|
193
585
|
function mergeCandidate(left, right) {
|
|
194
|
-
const { setupRequired: leftSetupRequired, defaultEffort: leftDefaultEffort, ...leftRest } = left;
|
|
195
|
-
const { setupRequired: rightSetupRequired, defaultEffort: rightDefaultEffort, ...rightRest } = right;
|
|
196
|
-
const
|
|
586
|
+
const { setupRequired: leftSetupRequired, defaultEffort: leftDefaultEffort, supportedEfforts: leftSupportedEfforts, ...leftRest } = left;
|
|
587
|
+
const { setupRequired: rightSetupRequired, defaultEffort: rightDefaultEffort, supportedEfforts: rightSupportedEfforts, ...rightRest } = right;
|
|
588
|
+
const rightHasDiscoveryState = right.discovered === true
|
|
589
|
+
|| right.setupRequired === true
|
|
590
|
+
|| right.availability !== undefined
|
|
591
|
+
|| right.authState !== undefined;
|
|
592
|
+
const preserveBuiltinPiMetadata = left.source === "pi"
|
|
593
|
+
&& (right.source === "opencode" || right.source === "ollama" || right.source === "lmstudio");
|
|
594
|
+
const hint = preserveBuiltinPiMetadata
|
|
595
|
+
? left.hint
|
|
596
|
+
: rightHasDiscoveryState ? right.hint ?? left.hint : left.hint ?? right.hint;
|
|
197
597
|
const discovered = left.discovered === true || right.discovered === true;
|
|
198
|
-
const
|
|
598
|
+
const rightReplacesCatalogMetadata = right.availability === "catalog_available"
|
|
599
|
+
&& left.source === right.source;
|
|
600
|
+
const defaultEffort = preserveBuiltinPiMetadata
|
|
601
|
+
? leftDefaultEffort
|
|
602
|
+
: rightReplacesCatalogMetadata
|
|
603
|
+
? rightDefaultEffort
|
|
604
|
+
: rightDefaultEffort ?? leftDefaultEffort;
|
|
605
|
+
const authState = preserveBuiltinPiMetadata
|
|
606
|
+
? left.authState
|
|
607
|
+
: strongerAuthState(left.authState, right.authState);
|
|
608
|
+
const setupRequired = preserveBuiltinPiMetadata
|
|
609
|
+
? leftSetupRequired === true || authState === "auth_required"
|
|
610
|
+
: rightSetupRequired === true || authState === "auth_required"
|
|
611
|
+
|| (right.authState === undefined && !discovered && leftSetupRequired === true);
|
|
199
612
|
return {
|
|
200
613
|
...leftRest,
|
|
201
614
|
...rightRest,
|
|
615
|
+
...(preserveBuiltinPiMetadata ? { source: left.source } : {}),
|
|
616
|
+
...(authState === undefined ? {} : { authState }),
|
|
617
|
+
supportedEfforts: preserveBuiltinPiMetadata
|
|
618
|
+
? [...(leftSupportedEfforts ?? [])]
|
|
619
|
+
: [...(rightSupportedEfforts ?? leftSupportedEfforts ?? [])],
|
|
202
620
|
...(hint === undefined ? {} : { hint }),
|
|
203
621
|
...(defaultEffort === undefined ? {} : { defaultEffort }),
|
|
204
622
|
discovered,
|
|
205
|
-
...(
|
|
623
|
+
...(setupRequired ? { setupRequired: true } : {}),
|
|
206
624
|
};
|
|
207
625
|
}
|
|
208
626
|
function rank(candidate) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
627
|
+
const authRank = candidate.authState === "verified"
|
|
628
|
+
? 0
|
|
629
|
+
: candidate.authState === "credential_detected" || candidate.authState === "not_required"
|
|
630
|
+
? 1
|
|
631
|
+
: 2;
|
|
632
|
+
return authRank * 1_000 + modelRank(candidate);
|
|
633
|
+
}
|
|
634
|
+
function modelRank(candidate) {
|
|
635
|
+
if (candidate.providerDefault === true)
|
|
636
|
+
return -100;
|
|
637
|
+
const directCodexRank = OPENAI_CODEX_MODELS.findIndex((model) => candidate.value === directCodexRef(model));
|
|
638
|
+
if (directCodexRank >= 0)
|
|
639
|
+
return directCodexRank;
|
|
640
|
+
const piOpenAiCodexRank = OPENAI_CODEX_MODELS.findIndex((model) => candidate.value === piOpenAiCodexRef(model));
|
|
641
|
+
if (piOpenAiCodexRank >= 0)
|
|
642
|
+
return 10 + piOpenAiCodexRank;
|
|
643
|
+
if (candidate.source === "claude") {
|
|
216
644
|
return 20;
|
|
217
645
|
}
|
|
218
646
|
if (candidate.source === "opencode") {
|
|
@@ -226,17 +654,323 @@ function rank(candidate) {
|
|
|
226
654
|
}
|
|
227
655
|
return 90;
|
|
228
656
|
}
|
|
229
|
-
function
|
|
657
|
+
function directCodexRef(model) {
|
|
658
|
+
return `codex:${model.id}`;
|
|
659
|
+
}
|
|
660
|
+
function piOpenAiCodexRef(model) {
|
|
661
|
+
return `pi:${PI_OPENAI_CODEX_PROVIDER}:${model.id}`;
|
|
662
|
+
}
|
|
663
|
+
function staticDirectCodexCandidate(model) {
|
|
664
|
+
return {
|
|
665
|
+
value: directCodexRef(model),
|
|
666
|
+
label: `Codex ${model.name}`,
|
|
667
|
+
...(model.minimumCodexCliVersion === undefined
|
|
668
|
+
? {}
|
|
669
|
+
: { hint: `requires Codex CLI ${formatVersion(model.minimumCodexCliVersion)}+` }),
|
|
670
|
+
source: "codex",
|
|
671
|
+
availability: "catalog_available",
|
|
672
|
+
authState: "auth_required",
|
|
673
|
+
// Exact effort support/defaults come from Codex app-server model/list.
|
|
674
|
+
// Do not fabricate provider metadata when the live catalog is unavailable.
|
|
675
|
+
supportedEfforts: [],
|
|
676
|
+
providerDefault: model.id === "gpt-5.6-terra",
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
function directCodexCandidates(catalog, state, version = "", verified = new Set()) {
|
|
680
|
+
return catalog.map((model) => directCodexCandidate(model, state, version, verified));
|
|
681
|
+
}
|
|
682
|
+
function directCodexCandidate(model, state, version = "", verified = new Set()) {
|
|
683
|
+
const reference = `codex:${model.id}`;
|
|
684
|
+
const curated = OPENAI_CODEX_MODELS.find((entry) => entry.id === model.id);
|
|
685
|
+
const authState = verified.has(reference)
|
|
686
|
+
? "verified"
|
|
687
|
+
: state === "credential-detected"
|
|
688
|
+
? "credential_detected"
|
|
689
|
+
: "auth_required";
|
|
690
|
+
if (state === "install-required") {
|
|
691
|
+
const minimum = curated?.minimumCodexCliVersion;
|
|
692
|
+
return {
|
|
693
|
+
value: reference,
|
|
694
|
+
label: `Codex ${model.displayName}`,
|
|
695
|
+
hint: minimum === undefined
|
|
696
|
+
? "install Codex CLI and sign in"
|
|
697
|
+
: `install Codex CLI ${formatVersion(minimum)}+ and sign in`,
|
|
698
|
+
source: "codex",
|
|
699
|
+
setupRequired: true,
|
|
700
|
+
availability: "catalog_available",
|
|
701
|
+
authState,
|
|
702
|
+
supportedEfforts: model.supportedEfforts,
|
|
703
|
+
...(model.isDefault === undefined ? {} : { providerDefault: model.isDefault }),
|
|
704
|
+
...(model.defaultEffort === undefined ? {} : { defaultEffort: model.defaultEffort }),
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
const prerequisites = [];
|
|
708
|
+
const minimum = curated?.minimumCodexCliVersion;
|
|
709
|
+
if (minimum !== undefined && !codexVersionMeetsMinimum(version, minimum)) {
|
|
710
|
+
prerequisites.push(parseCodexVersion(version) === undefined
|
|
711
|
+
? `Codex CLI ${formatVersion(minimum)}+ required; installed version could not be verified`
|
|
712
|
+
: `update Codex CLI to ${formatVersion(minimum)}+ (found ${version})`);
|
|
713
|
+
}
|
|
714
|
+
if (state === "login-required")
|
|
715
|
+
prerequisites.push("Codex sign-in required");
|
|
716
|
+
if (prerequisites.length === 0) {
|
|
717
|
+
return {
|
|
718
|
+
value: reference,
|
|
719
|
+
label: `Codex ${model.displayName}`,
|
|
720
|
+
hint: authState === "verified"
|
|
721
|
+
? `${version.length > 0 ? `${version}; ` : ""}verified by live readiness`
|
|
722
|
+
: `${version.length > 0 ? `${version}; ` : ""}sign-in detected; live readiness pending`,
|
|
723
|
+
source: "codex",
|
|
724
|
+
discovered: true,
|
|
725
|
+
availability: "catalog_available",
|
|
726
|
+
authState,
|
|
727
|
+
supportedEfforts: model.supportedEfforts,
|
|
728
|
+
...(model.isDefault === undefined ? {} : { providerDefault: model.isDefault }),
|
|
729
|
+
...(model.defaultEffort === undefined ? {} : { defaultEffort: model.defaultEffort }),
|
|
730
|
+
};
|
|
731
|
+
}
|
|
230
732
|
return {
|
|
231
|
-
value:
|
|
232
|
-
label:
|
|
233
|
-
hint:
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
733
|
+
value: reference,
|
|
734
|
+
label: `Codex ${model.displayName}`,
|
|
735
|
+
hint: prerequisites.join("; "),
|
|
736
|
+
source: "codex",
|
|
737
|
+
setupRequired: true,
|
|
738
|
+
availability: "catalog_available",
|
|
739
|
+
authState,
|
|
740
|
+
supportedEfforts: model.supportedEfforts,
|
|
741
|
+
...(model.isDefault === undefined ? {} : { providerDefault: model.isDefault }),
|
|
742
|
+
...(model.defaultEffort === undefined ? {} : { defaultEffort: model.defaultEffort }),
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
function curatedCodexCatalog() {
|
|
746
|
+
return OPENAI_CODEX_MODELS.map((model) => ({
|
|
747
|
+
id: model.id,
|
|
748
|
+
displayName: model.name,
|
|
749
|
+
supportedEfforts: [],
|
|
750
|
+
// Offline product fallback only; live model/list replaces this flag.
|
|
751
|
+
isDefault: model.id === "gpt-5.6-terra",
|
|
752
|
+
}));
|
|
753
|
+
}
|
|
754
|
+
function strongerAuthState(left, right) {
|
|
755
|
+
const order = {
|
|
756
|
+
auth_required: 0,
|
|
757
|
+
not_required: 1,
|
|
758
|
+
credential_detected: 2,
|
|
759
|
+
verified: 3,
|
|
239
760
|
};
|
|
761
|
+
if (left === undefined)
|
|
762
|
+
return right ?? "auth_required";
|
|
763
|
+
if (right === undefined)
|
|
764
|
+
return left;
|
|
765
|
+
return order[right] > order[left] ? right : left;
|
|
766
|
+
}
|
|
767
|
+
async function requestCodexModelList(timeoutMs, environment, abortSignal) {
|
|
768
|
+
const child = spawn("codex", ["app-server", "--listen", "stdio://", "-c", "project_doc_max_bytes=0"], {
|
|
769
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
770
|
+
env: { ...environment },
|
|
771
|
+
});
|
|
772
|
+
const lines = createInterface({ input: child.stdout });
|
|
773
|
+
return await new Promise((resolveResult, rejectResult) => {
|
|
774
|
+
let settled = false;
|
|
775
|
+
let timer;
|
|
776
|
+
const ignoreStdinError = () => undefined;
|
|
777
|
+
child.stdin.on("error", ignoreStdinError);
|
|
778
|
+
const childClosedWithin = async (milliseconds) => {
|
|
779
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
780
|
+
return true;
|
|
781
|
+
return await new Promise((resolveClosed) => {
|
|
782
|
+
const onClose = () => {
|
|
783
|
+
clearTimeout(closeTimer);
|
|
784
|
+
resolveClosed(true);
|
|
785
|
+
};
|
|
786
|
+
const closeTimer = setTimeout(() => {
|
|
787
|
+
child.off("close", onClose);
|
|
788
|
+
resolveClosed(false);
|
|
789
|
+
}, milliseconds);
|
|
790
|
+
closeTimer.unref?.();
|
|
791
|
+
child.once("close", onClose);
|
|
792
|
+
});
|
|
793
|
+
};
|
|
794
|
+
const stopChild = async () => {
|
|
795
|
+
lines.removeAllListeners("line");
|
|
796
|
+
lines.close();
|
|
797
|
+
try {
|
|
798
|
+
child.stdin.end();
|
|
799
|
+
}
|
|
800
|
+
catch { /* best effort */ }
|
|
801
|
+
if (await childClosedWithin(25))
|
|
802
|
+
return;
|
|
803
|
+
try {
|
|
804
|
+
child.kill("SIGTERM");
|
|
805
|
+
}
|
|
806
|
+
catch { /* best effort */ }
|
|
807
|
+
if (await childClosedWithin(250))
|
|
808
|
+
return;
|
|
809
|
+
try {
|
|
810
|
+
child.kill("SIGKILL");
|
|
811
|
+
}
|
|
812
|
+
catch { /* best effort */ }
|
|
813
|
+
if (!await childClosedWithin(250)) {
|
|
814
|
+
child.stdin.destroy();
|
|
815
|
+
child.stdout.destroy();
|
|
816
|
+
child.unref();
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
let settle;
|
|
820
|
+
const onChildError = () => void settle(new Error("Codex app-server is unavailable."));
|
|
821
|
+
const onChildClose = () => void settle(new Error("Codex app-server closed before returning models."));
|
|
822
|
+
const onAbort = () => {
|
|
823
|
+
const error = new Error("Codex model catalog discovery was interrupted.");
|
|
824
|
+
error.name = "AbortError";
|
|
825
|
+
void settle(error);
|
|
826
|
+
};
|
|
827
|
+
settle = async (error, models = []) => {
|
|
828
|
+
if (settled)
|
|
829
|
+
return;
|
|
830
|
+
settled = true;
|
|
831
|
+
clearTimeout(timer);
|
|
832
|
+
child.off("error", onChildError);
|
|
833
|
+
child.off("close", onChildClose);
|
|
834
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
835
|
+
await stopChild();
|
|
836
|
+
child.stdin.off("error", ignoreStdinError);
|
|
837
|
+
if (error === undefined)
|
|
838
|
+
resolveResult(models);
|
|
839
|
+
else
|
|
840
|
+
rejectResult(error);
|
|
841
|
+
};
|
|
842
|
+
const writeMessage = (message) => {
|
|
843
|
+
if (settled || child.stdin.destroyed || child.stdin.writableEnded)
|
|
844
|
+
return;
|
|
845
|
+
try {
|
|
846
|
+
child.stdin.write(`${JSON.stringify(message)}\n`);
|
|
847
|
+
}
|
|
848
|
+
catch { /* bounded shutdown handles the process */ }
|
|
849
|
+
};
|
|
850
|
+
timer = setTimeout(() => void settle(new Error("Codex model catalog discovery timed out.")), Math.max(250, Math.min(10_000, timeoutMs)));
|
|
851
|
+
timer.unref?.();
|
|
852
|
+
child.once("error", onChildError);
|
|
853
|
+
child.once("close", onChildClose);
|
|
854
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
855
|
+
lines.on("line", (line) => {
|
|
856
|
+
let message;
|
|
857
|
+
try {
|
|
858
|
+
message = JSON.parse(line);
|
|
859
|
+
}
|
|
860
|
+
catch {
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
if (!isRecord(message))
|
|
864
|
+
return;
|
|
865
|
+
if (typeof message.method === "string" && (typeof message.id === "string" || typeof message.id === "number")) {
|
|
866
|
+
writeMessage({
|
|
867
|
+
id: message.id,
|
|
868
|
+
error: { code: -32601, message: "Model catalog discovery does not support server requests." },
|
|
869
|
+
});
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
if (message.id === 1 && isRecord(message.result)) {
|
|
873
|
+
writeMessage({
|
|
874
|
+
id: 2,
|
|
875
|
+
method: "model/list",
|
|
876
|
+
params: { includeHidden: false, limit: 1_000 },
|
|
877
|
+
});
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
if (message.id !== 2)
|
|
881
|
+
return;
|
|
882
|
+
if (!isRecord(message.result) || !Array.isArray(message.result.data)) {
|
|
883
|
+
void settle(new Error("Codex app-server returned an invalid model catalog."));
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
void settle(undefined, normalizeCodexCatalog(message.result.data));
|
|
887
|
+
});
|
|
888
|
+
writeMessage({
|
|
889
|
+
id: 1,
|
|
890
|
+
method: "initialize",
|
|
891
|
+
params: {
|
|
892
|
+
clientInfo: { name: "mono-agent", title: "mono-agent", version: "0" },
|
|
893
|
+
capabilities: { experimentalApi: true },
|
|
894
|
+
},
|
|
895
|
+
});
|
|
896
|
+
if (abortSignal?.aborted === true)
|
|
897
|
+
onAbort();
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
function normalizeCodexCatalog(rows) {
|
|
901
|
+
const result = [];
|
|
902
|
+
const seen = new Set();
|
|
903
|
+
for (const raw of rows.slice(0, 1_000)) {
|
|
904
|
+
if (!isRecord(raw))
|
|
905
|
+
continue;
|
|
906
|
+
const id = typeof raw.id === "string" ? raw.id.trim() : "";
|
|
907
|
+
if (id.length === 0 || id.length > 160 || seen.has(id))
|
|
908
|
+
continue;
|
|
909
|
+
const supportedRows = Array.isArray(raw.supportedReasoningEfforts)
|
|
910
|
+
? raw.supportedReasoningEfforts.map((entry) => isRecord(entry) ? entry.reasoningEffort : entry)
|
|
911
|
+
: [];
|
|
912
|
+
const supportedEfforts = normalizeEfforts(supportedRows);
|
|
913
|
+
const normalizedDefault = normalizeEffort(raw.defaultReasoningEffort);
|
|
914
|
+
seen.add(id);
|
|
915
|
+
result.push({
|
|
916
|
+
id,
|
|
917
|
+
displayName: boundedCatalogText(raw.displayName, 160) || id,
|
|
918
|
+
supportedEfforts,
|
|
919
|
+
...(normalizedDefault === undefined ? {} : { defaultEffort: normalizedDefault }),
|
|
920
|
+
...(typeof raw.isDefault === "boolean" ? { isDefault: raw.isDefault } : {}),
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
return result;
|
|
924
|
+
}
|
|
925
|
+
const SUPPORTED_EFFORTS = new Set([
|
|
926
|
+
"none",
|
|
927
|
+
"minimal",
|
|
928
|
+
"low",
|
|
929
|
+
"medium",
|
|
930
|
+
"high",
|
|
931
|
+
"xhigh",
|
|
932
|
+
"max",
|
|
933
|
+
"ultra",
|
|
934
|
+
]);
|
|
935
|
+
function normalizeEffort(value) {
|
|
936
|
+
const normalized = value === "off" ? "none" : typeof value === "string" ? value : "";
|
|
937
|
+
return SUPPORTED_EFFORTS.has(normalized) ? normalized : undefined;
|
|
938
|
+
}
|
|
939
|
+
function normalizeEfforts(values) {
|
|
940
|
+
return [...new Set(values.map(normalizeEffort).filter((value) => value !== undefined))];
|
|
941
|
+
}
|
|
942
|
+
function exactDefaultEffortWhenUnambiguous(supportedEfforts) {
|
|
943
|
+
return supportedEfforts.length === 1 ? supportedEfforts[0] : undefined;
|
|
944
|
+
}
|
|
945
|
+
function boundedCatalogText(value, limit) {
|
|
946
|
+
const normalized = typeof value === "string"
|
|
947
|
+
? value.replace(/[\u0000-\u001f\u007f]/gu, " ").replace(/\s+/gu, " ").trim()
|
|
948
|
+
: "";
|
|
949
|
+
return normalized.length <= limit ? normalized : `${normalized.slice(0, Math.max(0, limit - 1))}…`;
|
|
950
|
+
}
|
|
951
|
+
function parseCodexVersion(version) {
|
|
952
|
+
const match = /(?:^|\s)v?(\d+)\.(\d+)\.(\d+)(?:[-+\s]|$)/u.exec(version);
|
|
953
|
+
if (match === null)
|
|
954
|
+
return undefined;
|
|
955
|
+
const parts = match.slice(1, 4).map(Number);
|
|
956
|
+
if (parts.some((part) => !Number.isSafeInteger(part) || part < 0))
|
|
957
|
+
return undefined;
|
|
958
|
+
return [parts[0], parts[1], parts[2]];
|
|
959
|
+
}
|
|
960
|
+
function codexVersionMeetsMinimum(version, minimum) {
|
|
961
|
+
const parsed = parseCodexVersion(version);
|
|
962
|
+
if (parsed === undefined)
|
|
963
|
+
return false;
|
|
964
|
+
for (let index = 0; index < minimum.length; index += 1) {
|
|
965
|
+
const installedPart = parsed[index] ?? 0;
|
|
966
|
+
const minimumPart = minimum[index] ?? 0;
|
|
967
|
+
if (installedPart !== minimumPart)
|
|
968
|
+
return installedPart > minimumPart;
|
|
969
|
+
}
|
|
970
|
+
return true;
|
|
971
|
+
}
|
|
972
|
+
function formatVersion(version) {
|
|
973
|
+
return version.join(".");
|
|
240
974
|
}
|
|
241
975
|
function parseOllamaList(stdout) {
|
|
242
976
|
const lines = stdout.trim().split(/\r?\n/u).filter(Boolean);
|
|
@@ -248,29 +982,13 @@ function parseOllamaList(stdout) {
|
|
|
248
982
|
.map((line) => line.trim().split(/\s+/u)[0])
|
|
249
983
|
.filter((name) => name !== undefined && name.length > 0);
|
|
250
984
|
}
|
|
251
|
-
function
|
|
252
|
-
|
|
253
|
-
const parsed = JSON.parse(stdout);
|
|
254
|
-
if (Array.isArray(parsed)) {
|
|
255
|
-
return parsed.map(modelEntryFromUnknown).filter(isModelEntry);
|
|
256
|
-
}
|
|
257
|
-
if (isRecord(parsed)) {
|
|
258
|
-
for (const key of ["models", "data"]) {
|
|
259
|
-
const value = parsed[key];
|
|
260
|
-
if (Array.isArray(value)) {
|
|
261
|
-
return value.map(modelEntryFromUnknown).filter(isModelEntry);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
catch {
|
|
267
|
-
// Fall through to tolerant line parsing.
|
|
268
|
-
}
|
|
985
|
+
function parseOpenCodeGoModels(stdout) {
|
|
986
|
+
const prefix = "opencode-go/";
|
|
269
987
|
return stdout
|
|
270
988
|
.split(/\r?\n/u)
|
|
271
|
-
.map((line) => line.trim()
|
|
272
|
-
.filter(
|
|
273
|
-
.map((
|
|
989
|
+
.map((line) => line.trim())
|
|
990
|
+
.filter((line) => line.startsWith(prefix) && line.length > prefix.length)
|
|
991
|
+
.map((line) => line.slice(prefix.length));
|
|
274
992
|
}
|
|
275
993
|
function parseOpenAiModelEntriesBody(body) {
|
|
276
994
|
if (!isRecord(body) || !Array.isArray(body.data)) {
|
|
@@ -300,7 +1018,13 @@ function displayModelName(model) {
|
|
|
300
1018
|
}
|
|
301
1019
|
function parseJsonObject(value) {
|
|
302
1020
|
const parsed = typeof value === "string" ? JSON.parse(value) : value;
|
|
303
|
-
|
|
1021
|
+
if (!isRecord(parsed)) {
|
|
1022
|
+
throw new Error("Expected a JSON object.");
|
|
1023
|
+
}
|
|
1024
|
+
return parsed;
|
|
1025
|
+
}
|
|
1026
|
+
function firstOutputLine(value) {
|
|
1027
|
+
return value.split(/\r?\n/u).map((line) => line.trim()).find((line) => line.length > 0)?.slice(0, 120) ?? "";
|
|
304
1028
|
}
|
|
305
1029
|
function readReasoningCapability(value) {
|
|
306
1030
|
for (const field of ["reasoning", "supportsReasoning", "supports_reasoning", "thinking", "supportsThinking", "supports_thinking"]) {
|
|
@@ -331,15 +1055,9 @@ function localModelDefaultEffort(model) {
|
|
|
331
1055
|
? "medium"
|
|
332
1056
|
: "none";
|
|
333
1057
|
}
|
|
334
|
-
function isMissingFileError(error) {
|
|
335
|
-
return isRecord(error) && (error.code === "ENOENT" || error.code === "ENOTDIR");
|
|
336
|
-
}
|
|
337
1058
|
function isRecord(value) {
|
|
338
1059
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
339
1060
|
}
|
|
340
|
-
function isNonEmptyString(value) {
|
|
341
|
-
return value !== undefined && value.length > 0;
|
|
342
|
-
}
|
|
343
1061
|
function isModelEntry(value) {
|
|
344
1062
|
return value !== undefined;
|
|
345
1063
|
}
|