thoth-agents 0.2.7 → 0.2.9
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/{chunk-32672NLJ.js → chunk-6NMPKYWY.js} +1004 -166
- package/dist/{chunk-4AC2TTUO.js → chunk-75JOF37E.js} +1 -1
- package/dist/{chunk-N7GFXGFJ.js → chunk-YLRPDTU7.js} +234 -203
- package/dist/cli/claude-code-install.d.ts +7 -2
- package/dist/cli/codex-install.d.ts +6 -0
- package/dist/cli/commands.d.ts +13 -3
- package/dist/cli/index.js +83 -15
- package/dist/cli/managed-state-io.d.ts +1 -0
- package/dist/cli/model-catalog/cache.d.ts +29 -0
- package/dist/cli/model-catalog/index.d.ts +18 -0
- package/dist/cli/model-catalog/models-dev.d.ts +12 -0
- package/dist/cli/model-catalog/types.d.ts +19 -0
- package/dist/cli/model-effort.d.ts +11 -0
- package/dist/cli/opencode-effort.d.ts +10 -0
- package/dist/cli/operations/claude-code.d.ts +9 -0
- package/dist/cli/operations/codex.d.ts +9 -1
- package/dist/cli/operations/types.d.ts +4 -0
- package/dist/cli/paths.d.ts +1 -0
- package/dist/cli/providers.d.ts +9 -9
- package/dist/cli/tui/components/ModelChoiceScreen.d.ts +8 -0
- package/dist/cli/tui/components/ModelScreen.d.ts +1 -0
- package/dist/cli/tui/index.js +250 -335
- package/dist/cli/tui/model-catalog.d.ts +4 -8
- package/dist/cli/tui/operations.d.ts +1 -1
- package/dist/cli/types.d.ts +3 -1
- package/dist/config/constants.d.ts +26 -0
- package/dist/harness/writers/claude-code-subagent.d.ts +2 -0
- package/dist/index.js +2 -2
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
CLAUDE_CODE_PROMPT_DIALECT,
|
|
4
4
|
CLAUDE_CODE_SUBAGENT_NAMESPACE,
|
|
5
5
|
CODEX_PROMPT_DIALECT,
|
|
6
|
+
CONFIRMED_OPENAI_SUBAGENT_PRESET,
|
|
6
7
|
CONTEXT7_MCP_URL,
|
|
7
8
|
CUSTOM_SKILLS,
|
|
8
9
|
DEFAULT_MODELS,
|
|
@@ -26,6 +27,7 @@ import {
|
|
|
26
27
|
getCustomSkillsDir,
|
|
27
28
|
getExistingConfigPath,
|
|
28
29
|
getExistingLiteConfigPath,
|
|
30
|
+
getOpenCodeManagedModelStatePath,
|
|
29
31
|
getPrimaryModelId,
|
|
30
32
|
getSkillRegistry,
|
|
31
33
|
installCustomSkills,
|
|
@@ -35,7 +37,7 @@ import {
|
|
|
35
37
|
renderRolePrompt,
|
|
36
38
|
writeConfig,
|
|
37
39
|
writeLiteConfig
|
|
38
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-YLRPDTU7.js";
|
|
39
41
|
|
|
40
42
|
// src/cli/codex-paths.ts
|
|
41
43
|
import { homedir } from "os";
|
|
@@ -81,6 +83,18 @@ function resolveCodexTargets(options) {
|
|
|
81
83
|
};
|
|
82
84
|
}
|
|
83
85
|
|
|
86
|
+
// src/cli/model-effort.ts
|
|
87
|
+
function normalizeEffortSelection(value) {
|
|
88
|
+
const normalized = value?.trim() ?? "";
|
|
89
|
+
if (!normalized || normalized === "inherit" || normalized === "default") {
|
|
90
|
+
return { kind: "inherit" };
|
|
91
|
+
}
|
|
92
|
+
return { kind: "effort", value: normalized };
|
|
93
|
+
}
|
|
94
|
+
function isExplicitEffort(selection) {
|
|
95
|
+
return selection.kind === "effort";
|
|
96
|
+
}
|
|
97
|
+
|
|
84
98
|
// src/cli/codex-install.ts
|
|
85
99
|
import { existsSync as existsSync5, readFileSync as readFileSync5, rmSync } from "fs";
|
|
86
100
|
import { basename, dirname as dirname4, isAbsolute, join as join5, relative as relative2 } from "path";
|
|
@@ -1311,19 +1325,19 @@ function renderCodexSkillLayout(input) {
|
|
|
1311
1325
|
}
|
|
1312
1326
|
|
|
1313
1327
|
// src/harness/adapters/codex.ts
|
|
1314
|
-
function readRootPackageVersion(
|
|
1328
|
+
function readRootPackageVersion(context2) {
|
|
1315
1329
|
const packageJsonPath = findRootPackageJsonPath([
|
|
1316
|
-
...hasCodexPackageRoot(
|
|
1317
|
-
|
|
1330
|
+
...hasCodexPackageRoot(context2) ? [context2.packageRoot] : [],
|
|
1331
|
+
context2.projectRoot,
|
|
1318
1332
|
process.cwd(),
|
|
1319
1333
|
fileURLToPath(new URL(".", import.meta.url))
|
|
1320
1334
|
]);
|
|
1321
1335
|
return readPackageJsonVersion(packageJsonPath);
|
|
1322
1336
|
}
|
|
1323
|
-
function createCodexPluginPackageManifest(
|
|
1337
|
+
function createCodexPluginPackageManifest(context2) {
|
|
1324
1338
|
return {
|
|
1325
1339
|
name: "thoth-agents",
|
|
1326
|
-
version: readRootPackageVersion(
|
|
1340
|
+
version: readRootPackageVersion(context2),
|
|
1327
1341
|
description: "Delegate-first OpenCode plugin with seven agents, thoth-mem persistence, and bundled SDD skills."
|
|
1328
1342
|
};
|
|
1329
1343
|
}
|
|
@@ -1367,22 +1381,9 @@ function createCodexBuiltinMcpTomlConfig() {
|
|
|
1367
1381
|
mcp_servers: createCodexBuiltinMcpServers()
|
|
1368
1382
|
};
|
|
1369
1383
|
}
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
explorer: "gpt-5.4-mini",
|
|
1374
|
-
designer: "gpt-5.4-mini",
|
|
1375
|
-
quick: "gpt-5.4-mini",
|
|
1376
|
-
deep: "gpt-5.5"
|
|
1377
|
-
};
|
|
1378
|
-
var CODEX_SUBAGENT_REASONING_EFFORTS = {
|
|
1379
|
-
oracle: "high",
|
|
1380
|
-
explorer: "low",
|
|
1381
|
-
librarian: "medium",
|
|
1382
|
-
designer: "medium",
|
|
1383
|
-
quick: "low",
|
|
1384
|
-
deep: "medium"
|
|
1385
|
-
};
|
|
1384
|
+
function isCodexSubagentName(name) {
|
|
1385
|
+
return name in CONFIRMED_OPENAI_SUBAGENT_PRESET;
|
|
1386
|
+
}
|
|
1386
1387
|
var CODEX_ROOT_START = "<!-- thoth-agents:codex-root:start -->";
|
|
1387
1388
|
var CODEX_ROOT_END = "<!-- thoth-agents:codex-root:end -->";
|
|
1388
1389
|
var CODEX_CAPABILITIES = CODEX_PROMPT_DIALECT.capabilities.capabilities;
|
|
@@ -1491,42 +1492,36 @@ function renderCodexRootInstructions(config) {
|
|
|
1491
1492
|
"- After receiving a delegated subagent response, close that subagent session unless you will retry or intentionally keep using that exact same session; explorer and librarian sessions must always be closed immediately after their response, and retry sessions must be closed after the retry result unless explicit same-session reuse is still required.",
|
|
1492
1493
|
"- Use packaged thoth-agents plugin capabilities through Codex plugin, skill, MCP, and hook review surfaces after enabling them with /plugins and /hooks.",
|
|
1493
1494
|
"- For blocking user decisions in Codex Default mode, use request_user_input after features.default_mode_request_user_input is enabled; do not ask those questions in plain prose.",
|
|
1495
|
+
"- Whenever the root orchestrator calls `request_user_input`, it MUST NEVER set or pass `autoResolutionMs`; omit the field entirely.",
|
|
1494
1496
|
"- Memory governance, role permissions, provider-per-agent controls, and hooks are instruction-level unless the active Codex runtime documents stronger enforcement.",
|
|
1495
1497
|
"</codex-runtime>",
|
|
1496
1498
|
CODEX_ROOT_END,
|
|
1497
1499
|
""
|
|
1498
1500
|
].join("\n");
|
|
1499
1501
|
}
|
|
1500
|
-
function agentModelReasoningEffort(role) {
|
|
1501
|
-
return isCodexSubagentName(role.name) ? CODEX_SUBAGENT_REASONING_EFFORTS[role.name] : "medium";
|
|
1502
|
-
}
|
|
1503
|
-
function isCodexSubagentName(name) {
|
|
1504
|
-
return name in CODEX_SUBAGENT_DEFAULT_MODELS;
|
|
1505
|
-
}
|
|
1506
1502
|
function getCodexAgentModel(role, config) {
|
|
1507
1503
|
if (!isCodexSubagentName(role.name)) return void 0;
|
|
1508
|
-
return getPrimaryModelId(config?.agents?.[role.name]?.model) ??
|
|
1504
|
+
return getPrimaryModelId(config?.agents?.[role.name]?.model) ?? CONFIRMED_OPENAI_SUBAGENT_PRESET[role.name].model;
|
|
1509
1505
|
}
|
|
1510
|
-
function
|
|
1511
|
-
|
|
1506
|
+
function getCodexAgentEffort(role, config) {
|
|
1507
|
+
if (!isCodexSubagentName(role.name)) return void 0;
|
|
1508
|
+
if (getPrimaryModelId(config?.agents?.[role.name]?.model)) return void 0;
|
|
1509
|
+
return CONFIRMED_OPENAI_SUBAGENT_PRESET[role.name].effort;
|
|
1512
1510
|
}
|
|
1513
|
-
function hasCodexConfig(
|
|
1514
|
-
return "config" in
|
|
1511
|
+
function hasCodexConfig(context2) {
|
|
1512
|
+
return "config" in context2;
|
|
1515
1513
|
}
|
|
1516
|
-
function hasCodexPackageRoot(
|
|
1517
|
-
return "packageRoot" in
|
|
1514
|
+
function hasCodexPackageRoot(context2) {
|
|
1515
|
+
return "packageRoot" in context2 && typeof context2.packageRoot === "string" && context2.packageRoot.length > 0;
|
|
1518
1516
|
}
|
|
1519
1517
|
function renderAgentArtifacts({ config }) {
|
|
1520
1518
|
const artifacts = [];
|
|
1521
1519
|
const diagnostics = [];
|
|
1522
|
-
const supportsReasoningEffort = codexSurfaceHasField(
|
|
1523
|
-
"project-agent-toml",
|
|
1524
|
-
"model_reasoning_effort"
|
|
1525
|
-
);
|
|
1526
1520
|
for (const role of getAgentPackContract().roles.filter(
|
|
1527
1521
|
(candidate) => candidate.name !== "orchestrator"
|
|
1528
1522
|
)) {
|
|
1529
1523
|
const model = getCodexAgentModel(role, config);
|
|
1524
|
+
const effort = getCodexAgentEffort(role, config);
|
|
1530
1525
|
const toml = renderCodexToml({
|
|
1531
1526
|
surfaceId: "project-agent-toml",
|
|
1532
1527
|
values: {
|
|
@@ -1534,7 +1529,7 @@ function renderAgentArtifacts({ config }) {
|
|
|
1534
1529
|
description: role.responsibility,
|
|
1535
1530
|
developer_instructions: roleInstructions(role, config),
|
|
1536
1531
|
...model ? { model } : {},
|
|
1537
|
-
...
|
|
1532
|
+
...effort ? { model_reasoning_effort: effort } : {},
|
|
1538
1533
|
sandbox_mode: role.canMutateWorkspace ? "workspace-write" : "read-only"
|
|
1539
1534
|
}
|
|
1540
1535
|
});
|
|
@@ -1621,27 +1616,27 @@ function hookReadinessDiagnostics() {
|
|
|
1621
1616
|
}
|
|
1622
1617
|
];
|
|
1623
1618
|
}
|
|
1624
|
-
function resolveSkillOutputModes(
|
|
1625
|
-
return
|
|
1619
|
+
function resolveSkillOutputModes(context2) {
|
|
1620
|
+
return context2.options?.codexSkillOutputModes ?? ["plugin-package"];
|
|
1626
1621
|
}
|
|
1627
1622
|
var codexAdapter = {
|
|
1628
1623
|
id: "codex",
|
|
1629
1624
|
displayName: "Codex",
|
|
1630
1625
|
capabilities: CODEX_CAPABILITIES,
|
|
1631
|
-
render(
|
|
1632
|
-
const config = hasCodexConfig(
|
|
1626
|
+
render(context2) {
|
|
1627
|
+
const config = hasCodexConfig(context2) ? context2.config : void 0;
|
|
1633
1628
|
const agentArtifacts = renderAgentArtifacts({ config });
|
|
1634
1629
|
const configArtifacts = renderConfigArtifacts();
|
|
1635
|
-
const skillOutputModes = resolveSkillOutputModes(
|
|
1630
|
+
const skillOutputModes = resolveSkillOutputModes(context2);
|
|
1636
1631
|
const skillLayout = renderCodexSkillLayout({
|
|
1637
|
-
projectRoot:
|
|
1638
|
-
...hasCodexPackageRoot(
|
|
1632
|
+
projectRoot: context2.projectRoot,
|
|
1633
|
+
...hasCodexPackageRoot(context2) ? { packageRoot: context2.packageRoot } : {},
|
|
1639
1634
|
skills: getSkillRegistry(),
|
|
1640
1635
|
surfaceId: "plugin-skills-directory",
|
|
1641
1636
|
outputModes: skillOutputModes
|
|
1642
1637
|
});
|
|
1643
1638
|
const pluginPackage = renderCodexPluginPackage({
|
|
1644
|
-
manifest: createCodexPluginPackageManifest(
|
|
1639
|
+
manifest: createCodexPluginPackageManifest(context2),
|
|
1645
1640
|
assets: [
|
|
1646
1641
|
{
|
|
1647
1642
|
surfaceId: "plugin-skills-directory",
|
|
@@ -1849,6 +1844,7 @@ import {
|
|
|
1849
1844
|
writeFileSync as writeFileSync2
|
|
1850
1845
|
} from "fs";
|
|
1851
1846
|
import { dirname as dirname3 } from "path";
|
|
1847
|
+
import { z } from "zod";
|
|
1852
1848
|
function writeTextWithBackup(path4, content) {
|
|
1853
1849
|
mkdirSync2(dirname3(path4), { recursive: true });
|
|
1854
1850
|
if (existsSync4(path4) && readFileSync4(path4, "utf8") === content) return false;
|
|
@@ -1871,21 +1867,37 @@ function stringRecord(value) {
|
|
|
1871
1867
|
)
|
|
1872
1868
|
);
|
|
1873
1869
|
}
|
|
1870
|
+
var managedModelStateSchema = z.object({
|
|
1871
|
+
version: z.number(),
|
|
1872
|
+
models: z.record(z.string(), z.unknown()),
|
|
1873
|
+
configuredModels: z.unknown().optional(),
|
|
1874
|
+
configuredEfforts: z.unknown().optional()
|
|
1875
|
+
}).passthrough();
|
|
1876
|
+
function effortRecord(value) {
|
|
1877
|
+
return Object.fromEntries(
|
|
1878
|
+
Object.entries(stringRecord(value)).filter(
|
|
1879
|
+
([, effort]) => isExplicitEffort(normalizeEffortSelection(effort))
|
|
1880
|
+
)
|
|
1881
|
+
);
|
|
1882
|
+
}
|
|
1874
1883
|
function emptyManagedModelState(version) {
|
|
1875
1884
|
return { version, models: {} };
|
|
1876
1885
|
}
|
|
1877
1886
|
function parseManagedModelStateJson(text, version) {
|
|
1878
1887
|
if (!text) return emptyManagedModelState(version);
|
|
1879
1888
|
try {
|
|
1880
|
-
const
|
|
1881
|
-
|
|
1889
|
+
const input = JSON.parse(text);
|
|
1890
|
+
const result = managedModelStateSchema.safeParse(input);
|
|
1891
|
+
if (!result.success || result.data.version !== version) {
|
|
1882
1892
|
return emptyManagedModelState(version);
|
|
1883
1893
|
}
|
|
1884
|
-
const configuredModels = stringRecord(
|
|
1894
|
+
const configuredModels = stringRecord(result.data.configuredModels);
|
|
1895
|
+
const configuredEfforts = effortRecord(result.data.configuredEfforts);
|
|
1885
1896
|
return {
|
|
1886
1897
|
version,
|
|
1887
|
-
models: stringRecord(
|
|
1888
|
-
...Object.keys(configuredModels).length > 0 ? { configuredModels } : {}
|
|
1898
|
+
models: stringRecord(result.data.models),
|
|
1899
|
+
...Object.keys(configuredModels).length > 0 ? { configuredModels } : {},
|
|
1900
|
+
...Object.keys(configuredEfforts).length > 0 ? { configuredEfforts } : {}
|
|
1889
1901
|
};
|
|
1890
1902
|
} catch {
|
|
1891
1903
|
return emptyManagedModelState(version);
|
|
@@ -1963,9 +1975,33 @@ function replaceRoleTomlModel(content, model) {
|
|
|
1963
1975
|
return `${rendered}
|
|
1964
1976
|
${content}`;
|
|
1965
1977
|
}
|
|
1978
|
+
function parseRoleTomlEffort(content) {
|
|
1979
|
+
return /^model_reasoning_effort\s*=\s*"([^"\\]+)"\s*$/m.exec(content)?.[1];
|
|
1980
|
+
}
|
|
1981
|
+
function replaceRoleTomlEffort(content, effort) {
|
|
1982
|
+
const pattern = /^model_reasoning_effort\s*=\s*"[^"\\]+"\s*\r?\n?/m;
|
|
1983
|
+
if (effort === void 0) return content.replace(pattern, "");
|
|
1984
|
+
const rendered = `model_reasoning_effort = "${escapeTomlString2(effort)}"`;
|
|
1985
|
+
if (pattern.test(content)) return content.replace(pattern, `${rendered}
|
|
1986
|
+
`);
|
|
1987
|
+
const modelPattern = /^model\s*=\s*"(?:\\.|[^"\\])*"\s*\r?\n?/m;
|
|
1988
|
+
if (modelPattern.test(content)) {
|
|
1989
|
+
return content.replace(
|
|
1990
|
+
modelPattern,
|
|
1991
|
+
(modelLine) => `${modelLine}${rendered}
|
|
1992
|
+
`
|
|
1993
|
+
);
|
|
1994
|
+
}
|
|
1995
|
+
return `${rendered}
|
|
1996
|
+
${content}`;
|
|
1997
|
+
}
|
|
1966
1998
|
function roleManagedModelStateKey(path4) {
|
|
1967
1999
|
return basename(path4);
|
|
1968
2000
|
}
|
|
2001
|
+
function normalizeCodexRuntimeModel(model) {
|
|
2002
|
+
const separator = model.indexOf("/");
|
|
2003
|
+
return separator === -1 ? model : model.slice(separator + 1);
|
|
2004
|
+
}
|
|
1969
2005
|
function applyCodexManagedModelOverrides(config, overrides) {
|
|
1970
2006
|
if (config.dryRun) {
|
|
1971
2007
|
return {
|
|
@@ -1998,7 +2034,8 @@ function applyCodexManagedModelOverrides(config, overrides) {
|
|
|
1998
2034
|
const nextState = {
|
|
1999
2035
|
version: MANAGED_MODEL_STATE_VERSION,
|
|
2000
2036
|
models: { ...state.models },
|
|
2001
|
-
...state.configuredModels ? { configuredModels: { ...state.configuredModels } } : {}
|
|
2037
|
+
...state.configuredModels ? { configuredModels: { ...state.configuredModels } } : {},
|
|
2038
|
+
...state.configuredEfforts ? { configuredEfforts: { ...state.configuredEfforts } } : {}
|
|
2002
2039
|
};
|
|
2003
2040
|
try {
|
|
2004
2041
|
for (const override of overrides) {
|
|
@@ -2011,14 +2048,30 @@ function applyCodexManagedModelOverrides(config, overrides) {
|
|
|
2011
2048
|
);
|
|
2012
2049
|
}
|
|
2013
2050
|
const before = existsSync5(roleItem.targetPath) ? readFileSync5(roleItem.targetPath, "utf8") : roleItem.content;
|
|
2014
|
-
const
|
|
2051
|
+
const model = normalizeCodexRuntimeModel(override.model);
|
|
2052
|
+
let updated = replaceRoleTomlModel(before, model);
|
|
2053
|
+
if (override.clearEffort) {
|
|
2054
|
+
updated = replaceRoleTomlEffort(updated, void 0);
|
|
2055
|
+
} else if (override.effort !== void 0) {
|
|
2056
|
+
updated = replaceRoleTomlEffort(updated, override.effort);
|
|
2057
|
+
}
|
|
2015
2058
|
if (writeTextWithBackup(roleItem.targetPath, updated)) {
|
|
2016
2059
|
changed.push(roleItem.targetPath);
|
|
2017
2060
|
}
|
|
2018
2061
|
const key = roleManagedModelStateKey(roleItem.targetPath);
|
|
2019
|
-
nextState.models[key] =
|
|
2062
|
+
nextState.models[key] = normalizeCodexRuntimeModel(
|
|
2063
|
+
roleItem.renderedModel ?? model
|
|
2064
|
+
);
|
|
2020
2065
|
nextState.configuredModels ??= {};
|
|
2021
|
-
nextState.configuredModels[key] =
|
|
2066
|
+
nextState.configuredModels[key] = model;
|
|
2067
|
+
if (override.clearEffort) {
|
|
2068
|
+
if (nextState.configuredEfforts) {
|
|
2069
|
+
delete nextState.configuredEfforts[key];
|
|
2070
|
+
}
|
|
2071
|
+
} else if (override.effort !== void 0) {
|
|
2072
|
+
nextState.configuredEfforts ??= {};
|
|
2073
|
+
nextState.configuredEfforts[key] = override.effort;
|
|
2074
|
+
}
|
|
2022
2075
|
}
|
|
2023
2076
|
if (writeTextWithBackup(statePath, stableJson3(nextState))) {
|
|
2024
2077
|
changed.push(statePath);
|
|
@@ -2042,6 +2095,11 @@ function resolveRoleTomlContent(options) {
|
|
|
2042
2095
|
const key = roleManagedModelStateKey(options.targetPath);
|
|
2043
2096
|
if (!renderedModel) return options.renderedContent;
|
|
2044
2097
|
const configuredModel = options.reset ? void 0 : options.state.configuredModels?.[key];
|
|
2098
|
+
const configuredEffort = options.reset ? void 0 : options.state.configuredEfforts?.[key];
|
|
2099
|
+
if (configuredEffort !== void 0) {
|
|
2100
|
+
options.nextState.configuredEfforts ??= {};
|
|
2101
|
+
options.nextState.configuredEfforts[key] = configuredEffort;
|
|
2102
|
+
}
|
|
2045
2103
|
if (options.reset || !existsSync5(options.targetPath)) {
|
|
2046
2104
|
options.nextState.models[key] = renderedModel;
|
|
2047
2105
|
if (configuredModel !== void 0) {
|
|
@@ -2051,16 +2109,19 @@ function resolveRoleTomlContent(options) {
|
|
|
2051
2109
|
}
|
|
2052
2110
|
return options.renderedContent;
|
|
2053
2111
|
}
|
|
2054
|
-
const
|
|
2055
|
-
|
|
2056
|
-
);
|
|
2112
|
+
const installedContent = readFileSync5(options.targetPath, "utf8");
|
|
2113
|
+
const currentModel = parseRoleTomlModel(installedContent);
|
|
2114
|
+
const currentEffort = parseRoleTomlEffort(installedContent);
|
|
2057
2115
|
if (configuredModel !== void 0) {
|
|
2058
2116
|
options.nextState.models[key] = renderedModel;
|
|
2059
2117
|
options.nextState.configuredModels ??= {};
|
|
2060
2118
|
options.nextState.configuredModels[key] = configuredModel;
|
|
2061
|
-
return
|
|
2062
|
-
|
|
2063
|
-
|
|
2119
|
+
return replaceRoleTomlEffort(
|
|
2120
|
+
replaceRoleTomlModel(
|
|
2121
|
+
options.renderedContent,
|
|
2122
|
+
currentModel && currentModel !== configuredModel ? currentModel : configuredModel
|
|
2123
|
+
),
|
|
2124
|
+
currentEffort
|
|
2064
2125
|
);
|
|
2065
2126
|
}
|
|
2066
2127
|
const trackedModel = options.state.models[key];
|
|
@@ -2068,10 +2129,13 @@ function resolveRoleTomlContent(options) {
|
|
|
2068
2129
|
if (isUserOwned) {
|
|
2069
2130
|
if (trackedModel !== void 0)
|
|
2070
2131
|
options.nextState.models[key] = trackedModel;
|
|
2071
|
-
return
|
|
2132
|
+
return replaceRoleTomlEffort(
|
|
2133
|
+
replaceRoleTomlModel(options.renderedContent, currentModel),
|
|
2134
|
+
currentEffort
|
|
2135
|
+
);
|
|
2072
2136
|
}
|
|
2073
2137
|
options.nextState.models[key] = renderedModel;
|
|
2074
|
-
return options.renderedContent;
|
|
2138
|
+
return replaceRoleTomlEffort(options.renderedContent, currentEffort);
|
|
2075
2139
|
}
|
|
2076
2140
|
function mergePersonalMarketplace(existing, homeDir, personalPluginRoot) {
|
|
2077
2141
|
const parsed = existing.trim() ? JSON.parse(existing) : {};
|
|
@@ -2129,6 +2193,9 @@ function buildCodexSetupPlan(config) {
|
|
|
2129
2193
|
description: `Materialize Codex role subagent ${target.role}.`,
|
|
2130
2194
|
requiresBackup: existsSync5(target.path),
|
|
2131
2195
|
role: target.role,
|
|
2196
|
+
renderedModel: parseRoleTomlModel(
|
|
2197
|
+
roleArtifactContent(target.role, render.artifacts)
|
|
2198
|
+
),
|
|
2132
2199
|
content: resolveRoleTomlContent({
|
|
2133
2200
|
renderedContent: roleArtifactContent(target.role, render.artifacts),
|
|
2134
2201
|
targetPath: target.path,
|
|
@@ -2445,6 +2512,7 @@ function renderClaudeCodeSubagent(input) {
|
|
|
2445
2512
|
`name: ${yamlScalar(input.name)}`,
|
|
2446
2513
|
`description: ${yamlScalar(input.description)}`,
|
|
2447
2514
|
`model: ${input.model}`,
|
|
2515
|
+
...input.effort !== void 0 ? [`effort: ${input.effort}`] : [],
|
|
2448
2516
|
...input.tools !== void 0 ? [`tools: ${yamlScalar(input.tools)}`] : [],
|
|
2449
2517
|
"---"
|
|
2450
2518
|
].join("\n");
|
|
@@ -2584,25 +2652,25 @@ function stableJson5(value) {
|
|
|
2584
2652
|
return `${JSON.stringify(value, null, 2)}
|
|
2585
2653
|
`;
|
|
2586
2654
|
}
|
|
2587
|
-
function readRootPackageVersion2(
|
|
2655
|
+
function readRootPackageVersion2(context2) {
|
|
2588
2656
|
const packageJsonPath = findRootPackageJsonPath([
|
|
2589
|
-
...hasPackageRoot(
|
|
2590
|
-
|
|
2657
|
+
...hasPackageRoot(context2) ? [context2.packageRoot] : [],
|
|
2658
|
+
context2.projectRoot,
|
|
2591
2659
|
process.cwd(),
|
|
2592
2660
|
fileURLToPath3(new URL(".", import.meta.url))
|
|
2593
2661
|
]);
|
|
2594
2662
|
return readPackageJsonVersion(packageJsonPath);
|
|
2595
2663
|
}
|
|
2596
|
-
function hasConfig(
|
|
2597
|
-
return "config" in
|
|
2664
|
+
function hasConfig(context2) {
|
|
2665
|
+
return "config" in context2;
|
|
2598
2666
|
}
|
|
2599
|
-
function hasPackageRoot(
|
|
2600
|
-
return "packageRoot" in
|
|
2667
|
+
function hasPackageRoot(context2) {
|
|
2668
|
+
return "packageRoot" in context2 && typeof context2.packageRoot === "string" && context2.packageRoot.length > 0;
|
|
2601
2669
|
}
|
|
2602
|
-
function createPluginManifest(
|
|
2670
|
+
function createPluginManifest(context2) {
|
|
2603
2671
|
return {
|
|
2604
2672
|
name: "thoth-agents",
|
|
2605
|
-
version: readRootPackageVersion2(
|
|
2673
|
+
version: readRootPackageVersion2(context2),
|
|
2606
2674
|
description: "Delegate-first agent pack with seven roles, thoth-mem persistence, and bundled SDD skills, packaged for Claude Code.",
|
|
2607
2675
|
author: { name: "thoth-agents" }
|
|
2608
2676
|
};
|
|
@@ -2650,8 +2718,8 @@ var claudeCodeAdapter = {
|
|
|
2650
2718
|
id: "claude",
|
|
2651
2719
|
displayName: "Claude Code",
|
|
2652
2720
|
capabilities: CLAUDE_CODE_CAPABILITIES,
|
|
2653
|
-
render(
|
|
2654
|
-
const config = hasConfig(
|
|
2721
|
+
render(context2) {
|
|
2722
|
+
const config = hasConfig(context2) ? context2.config : void 0;
|
|
2655
2723
|
const componentArtifacts = [
|
|
2656
2724
|
...renderSubagentArtifacts(config),
|
|
2657
2725
|
renderOrchestratorArtifact(config),
|
|
@@ -2671,13 +2739,13 @@ var claudeCodeAdapter = {
|
|
|
2671
2739
|
}
|
|
2672
2740
|
];
|
|
2673
2741
|
const skillLayout = renderClaudeCodeSkillLayout({
|
|
2674
|
-
projectRoot:
|
|
2675
|
-
...hasPackageRoot(
|
|
2742
|
+
projectRoot: context2.projectRoot,
|
|
2743
|
+
...hasPackageRoot(context2) ? { packageRoot: context2.packageRoot } : {},
|
|
2676
2744
|
skills: getSkillRegistry()
|
|
2677
2745
|
});
|
|
2678
2746
|
componentArtifacts.push(...skillLayout.artifacts);
|
|
2679
2747
|
const pluginPackage = renderClaudeCodePluginPackage({
|
|
2680
|
-
manifest: createPluginManifest(
|
|
2748
|
+
manifest: createPluginManifest(context2),
|
|
2681
2749
|
componentArtifacts
|
|
2682
2750
|
});
|
|
2683
2751
|
return {
|
|
@@ -2734,6 +2802,19 @@ function replaceSubagentModel(content, model) {
|
|
|
2734
2802
|
}
|
|
2735
2803
|
return content;
|
|
2736
2804
|
}
|
|
2805
|
+
function parseSubagentEffort(content) {
|
|
2806
|
+
return /^effort:\s*(\S+)\s*$/m.exec(content)?.[1];
|
|
2807
|
+
}
|
|
2808
|
+
function replaceSubagentEffort(content, effort) {
|
|
2809
|
+
if (effort === void 0) {
|
|
2810
|
+
return content.replace(/^effort:\s*\S+\s*\n/m, "");
|
|
2811
|
+
}
|
|
2812
|
+
if (/^effort:\s*\S+\s*$/m.test(content)) {
|
|
2813
|
+
return content.replace(/^effort:\s*\S+\s*$/m, `effort: ${effort}`);
|
|
2814
|
+
}
|
|
2815
|
+
return content.replace(/^(model:\s*\S+\s*)$/m, `$1
|
|
2816
|
+
effort: ${effort}`);
|
|
2817
|
+
}
|
|
2737
2818
|
function emptyManagedModelState3() {
|
|
2738
2819
|
return emptyManagedModelState(CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION);
|
|
2739
2820
|
}
|
|
@@ -2769,15 +2850,29 @@ function roleForArtifact(artifact) {
|
|
|
2769
2850
|
const name = match?.[1];
|
|
2770
2851
|
return name && CLAUDE_CODE_ROLE_NAMES.includes(name) ? name : void 0;
|
|
2771
2852
|
}
|
|
2772
|
-
function applyConfiguredModel(content, role, state, nextState, reset) {
|
|
2853
|
+
function applyConfiguredModel(content, targetPath, role, state, nextState, reset) {
|
|
2773
2854
|
const renderedModel = parseSubagentModel(content);
|
|
2774
2855
|
if (!role || !renderedModel) return content;
|
|
2775
2856
|
nextState.models[role] = renderedModel;
|
|
2776
2857
|
const configured = reset ? void 0 : state.configuredModels?.[role];
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2858
|
+
let updated = content;
|
|
2859
|
+
if (configured !== void 0) {
|
|
2860
|
+
nextState.configuredModels ??= {};
|
|
2861
|
+
nextState.configuredModels[role] = configured;
|
|
2862
|
+
updated = replaceSubagentModel(updated, configured);
|
|
2863
|
+
}
|
|
2864
|
+
const configuredEffort = reset ? void 0 : state.configuredEfforts?.[role];
|
|
2865
|
+
if (configuredEffort !== void 0) {
|
|
2866
|
+
nextState.configuredEfforts ??= {};
|
|
2867
|
+
nextState.configuredEfforts[role] = configuredEffort;
|
|
2868
|
+
}
|
|
2869
|
+
if (!reset && existsSync6(targetPath)) {
|
|
2870
|
+
const installedEffort = parseSubagentEffort(
|
|
2871
|
+
readFileSync7(targetPath, "utf8")
|
|
2872
|
+
);
|
|
2873
|
+
updated = replaceSubagentEffort(updated, installedEffort);
|
|
2874
|
+
}
|
|
2875
|
+
return updated;
|
|
2781
2876
|
}
|
|
2782
2877
|
function buildClaudeCodeSetupPlan(config) {
|
|
2783
2878
|
const targets = resolveClaudeCodeTargets({
|
|
@@ -2795,8 +2890,15 @@ function buildClaudeCodeSetupPlan(config) {
|
|
|
2795
2890
|
const items = render.artifacts.map((artifact) => {
|
|
2796
2891
|
const role = roleForArtifact(artifact);
|
|
2797
2892
|
const rendered = String(artifact.content ?? "");
|
|
2798
|
-
const content = role !== void 0 ? applyConfiguredModel(rendered, role, state, nextState, config.reset) : rendered;
|
|
2799
2893
|
const targetPath = join8(targets.pluginRoot, artifact.path);
|
|
2894
|
+
const content = role !== void 0 ? applyConfiguredModel(
|
|
2895
|
+
rendered,
|
|
2896
|
+
targetPath,
|
|
2897
|
+
role,
|
|
2898
|
+
state,
|
|
2899
|
+
nextState,
|
|
2900
|
+
config.reset
|
|
2901
|
+
) : rendered;
|
|
2800
2902
|
return {
|
|
2801
2903
|
kind: targetKindForArtifact(artifact),
|
|
2802
2904
|
action: "write-plugin-file",
|
|
@@ -2892,11 +2994,12 @@ function applyClaudeCodeManagedModelOverrides(config, overrides) {
|
|
|
2892
2994
|
const nextState = {
|
|
2893
2995
|
version: CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION,
|
|
2894
2996
|
models: { ...state.models },
|
|
2895
|
-
...state.configuredModels ? { configuredModels: { ...state.configuredModels } } : {}
|
|
2997
|
+
...state.configuredModels ? { configuredModels: { ...state.configuredModels } } : {},
|
|
2998
|
+
...state.configuredEfforts ? { configuredEfforts: { ...state.configuredEfforts } } : {}
|
|
2896
2999
|
};
|
|
2897
3000
|
try {
|
|
2898
3001
|
for (const override of overrides) {
|
|
2899
|
-
if (!isClaudeCodeModelAlias(override.model)) {
|
|
3002
|
+
if (!isClaudeCodeModelAlias(override.model) && override.catalogId !== override.model) {
|
|
2900
3003
|
throw new Error(
|
|
2901
3004
|
`Unsupported Claude Code model "${override.model}" for ${override.role}; use sonnet, opus, haiku, or inherit.`
|
|
2902
3005
|
);
|
|
@@ -2908,12 +3011,25 @@ function applyClaudeCodeManagedModelOverrides(config, overrides) {
|
|
|
2908
3011
|
throw new Error(`Missing Claude Code subagent for ${override.role}.`);
|
|
2909
3012
|
}
|
|
2910
3013
|
const before = existsSync6(roleItem.targetPath) ? readFileSync7(roleItem.targetPath, "utf8") : roleItem.content;
|
|
2911
|
-
|
|
3014
|
+
let updated = replaceSubagentModel(before, override.model);
|
|
3015
|
+
if (override.clearEffort) {
|
|
3016
|
+
updated = replaceSubagentEffort(updated, void 0);
|
|
3017
|
+
} else if (override.effort !== void 0) {
|
|
3018
|
+
updated = replaceSubagentEffort(updated, override.effort);
|
|
3019
|
+
}
|
|
2912
3020
|
if (writeTextWithBackup(roleItem.targetPath, updated)) {
|
|
2913
3021
|
changed.push(roleItem.targetPath);
|
|
2914
3022
|
}
|
|
2915
3023
|
nextState.configuredModels ??= {};
|
|
2916
3024
|
nextState.configuredModels[override.role] = override.model;
|
|
3025
|
+
if (override.clearEffort) {
|
|
3026
|
+
if (nextState.configuredEfforts) {
|
|
3027
|
+
delete nextState.configuredEfforts[override.role];
|
|
3028
|
+
}
|
|
3029
|
+
} else if (override.effort !== void 0) {
|
|
3030
|
+
nextState.configuredEfforts ??= {};
|
|
3031
|
+
nextState.configuredEfforts[override.role] = override.effort;
|
|
3032
|
+
}
|
|
2917
3033
|
}
|
|
2918
3034
|
if (writeTextWithBackup(statePath, stableJson3(nextState))) {
|
|
2919
3035
|
changed.push(statePath);
|
|
@@ -2939,6 +3055,31 @@ function formatClaudeCodeSetupPlan(plan) {
|
|
|
2939
3055
|
var CLAUDE_CODE_DISPLAY_NAME = "Claude Code";
|
|
2940
3056
|
var claudeCodePlanSources = /* @__PURE__ */ new WeakMap();
|
|
2941
3057
|
var claudeCodeModelSources = /* @__PURE__ */ new WeakMap();
|
|
3058
|
+
var CLAUDE_CODE_EFFORTS = /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh", "max"]);
|
|
3059
|
+
function resolveClaudeCodeEffort(input) {
|
|
3060
|
+
if (!input.effort || input.effort.kind === "inherit") {
|
|
3061
|
+
return { ok: true, effort: void 0 };
|
|
3062
|
+
}
|
|
3063
|
+
const effort = input.effort.value;
|
|
3064
|
+
if (!CLAUDE_CODE_EFFORTS.has(effort)) {
|
|
3065
|
+
return {
|
|
3066
|
+
ok: false,
|
|
3067
|
+
code: "claude-code-effort-runtime-unsupported",
|
|
3068
|
+
message: `Claude Code does not support effort ${effort}.`
|
|
3069
|
+
};
|
|
3070
|
+
}
|
|
3071
|
+
if (isClaudeCodeModelAlias(input.model) && input.model !== "inherit") {
|
|
3072
|
+
return { ok: true, effort };
|
|
3073
|
+
}
|
|
3074
|
+
if (input.catalogId === input.model && input.availableEfforts?.includes(effort)) {
|
|
3075
|
+
return { ok: true, effort };
|
|
3076
|
+
}
|
|
3077
|
+
return {
|
|
3078
|
+
ok: false,
|
|
3079
|
+
code: "claude-code-effort-catalog-unsupported",
|
|
3080
|
+
message: `Effort ${effort} is not supported by ${input.catalogId ?? input.model} in the models.dev catalog.`
|
|
3081
|
+
};
|
|
3082
|
+
}
|
|
2942
3083
|
var claudeCodeActions = [
|
|
2943
3084
|
{
|
|
2944
3085
|
id: "claude-code-status",
|
|
@@ -3002,16 +3143,16 @@ var claudeCodeOperationAdapter = {
|
|
|
3002
3143
|
description: "Claude Code plugin package and managed subagent surfaces.",
|
|
3003
3144
|
actions: claudeCodeActions
|
|
3004
3145
|
};
|
|
3005
|
-
function claudeCodeConfig(
|
|
3146
|
+
function claudeCodeConfig(context2 = { cwd: process.cwd() }, dryRun) {
|
|
3006
3147
|
return {
|
|
3007
3148
|
dryRun,
|
|
3008
3149
|
reset: false,
|
|
3009
3150
|
// Match the installer (install.ts uses 'user') so post-install status,
|
|
3010
3151
|
// update, sync, and model commands resolve the same plugin root.
|
|
3011
|
-
scope:
|
|
3012
|
-
projectRoot:
|
|
3013
|
-
homeDir:
|
|
3014
|
-
packageRoot:
|
|
3152
|
+
scope: context2.scope ?? "user",
|
|
3153
|
+
projectRoot: context2.cwd,
|
|
3154
|
+
homeDir: context2.homeDir,
|
|
3155
|
+
packageRoot: context2.packageRoot
|
|
3015
3156
|
};
|
|
3016
3157
|
}
|
|
3017
3158
|
function claudeCodeDisclaimers() {
|
|
@@ -3131,10 +3272,10 @@ function statusFromSetupPlan(plan) {
|
|
|
3131
3272
|
]
|
|
3132
3273
|
};
|
|
3133
3274
|
}
|
|
3134
|
-
function getClaudeCodeStatus(
|
|
3275
|
+
function getClaudeCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
3135
3276
|
let plan;
|
|
3136
3277
|
try {
|
|
3137
|
-
plan = buildClaudeCodeSetupPlan(claudeCodeConfig(
|
|
3278
|
+
plan = buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true));
|
|
3138
3279
|
} catch (error) {
|
|
3139
3280
|
const message = error instanceof Error ? error.message : String(error);
|
|
3140
3281
|
return {
|
|
@@ -3192,53 +3333,71 @@ function planFromSetup(id, action, title, summary, setupPlan) {
|
|
|
3192
3333
|
claudeCodePlanSources.set(plan, setupPlan);
|
|
3193
3334
|
return plan;
|
|
3194
3335
|
}
|
|
3195
|
-
function buildClaudeCodeInstallPlan(
|
|
3336
|
+
function buildClaudeCodeInstallPlan(context2 = { cwd: process.cwd() }) {
|
|
3196
3337
|
return planFromSetup(
|
|
3197
3338
|
"claude-code-install-preview",
|
|
3198
3339
|
"install",
|
|
3199
3340
|
"Install Claude Code plugin package",
|
|
3200
3341
|
"Preview Claude Code plugin package install using buildClaudeCodeSetupPlan().",
|
|
3201
|
-
buildClaudeCodeSetupPlan(claudeCodeConfig(
|
|
3342
|
+
buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true))
|
|
3202
3343
|
);
|
|
3203
3344
|
}
|
|
3204
|
-
function buildClaudeCodeUpdatePlan(
|
|
3345
|
+
function buildClaudeCodeUpdatePlan(context2 = { cwd: process.cwd() }) {
|
|
3205
3346
|
return planFromSetup(
|
|
3206
3347
|
"claude-code-update-preview",
|
|
3207
3348
|
"update",
|
|
3208
3349
|
"Update Claude Code plugin package",
|
|
3209
3350
|
"Preview Claude Code managed plugin refresh using buildClaudeCodeSetupPlan().",
|
|
3210
|
-
buildClaudeCodeSetupPlan(claudeCodeConfig(
|
|
3351
|
+
buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true))
|
|
3211
3352
|
);
|
|
3212
3353
|
}
|
|
3213
|
-
function buildClaudeCodeSyncPlan(
|
|
3354
|
+
function buildClaudeCodeSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
3214
3355
|
return planFromSetup(
|
|
3215
3356
|
"claude-code-sync-preview",
|
|
3216
3357
|
"sync",
|
|
3217
3358
|
"Sync Claude Code plugin package",
|
|
3218
3359
|
"Preview Claude Code managed plugin subagents, MCP, hooks, and skills.",
|
|
3219
|
-
buildClaudeCodeSetupPlan(claudeCodeConfig(
|
|
3360
|
+
buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true))
|
|
3220
3361
|
);
|
|
3221
3362
|
}
|
|
3222
3363
|
function isClaudeCodeRole(role) {
|
|
3223
3364
|
return CLAUDE_CODE_ROLE_NAMES.includes(role);
|
|
3224
3365
|
}
|
|
3225
|
-
function buildClaudeCodeModelPlan(input,
|
|
3226
|
-
const status = getClaudeCodeStatus(
|
|
3227
|
-
const
|
|
3366
|
+
function buildClaudeCodeModelPlan(input, context2 = { cwd: process.cwd() }) {
|
|
3367
|
+
const status = getClaudeCodeStatus(context2);
|
|
3368
|
+
const resolvedRoles = input.roles.map((role) => ({
|
|
3369
|
+
role,
|
|
3370
|
+
effort: resolveClaudeCodeEffort(role),
|
|
3371
|
+
validRole: isClaudeCodeRole(role.role),
|
|
3372
|
+
validModel: isClaudeCodeModelAlias(role.model) || role.catalogId === role.model
|
|
3373
|
+
}));
|
|
3374
|
+
const supportedRoles = resolvedRoles.filter((entry) => entry.validRole && entry.validModel && entry.effort.ok).map(({ role, effort }) => ({
|
|
3228
3375
|
role: role.role,
|
|
3229
|
-
model: role.model
|
|
3376
|
+
model: role.model,
|
|
3377
|
+
...role.catalogId ? { catalogId: role.catalogId } : {},
|
|
3378
|
+
...effort.ok && effort.effort !== void 0 ? { effort: effort.effort } : {},
|
|
3379
|
+
...role.effort?.kind === "inherit" ? { clearEffort: true } : {}
|
|
3230
3380
|
}));
|
|
3231
|
-
const rejectedRoles =
|
|
3232
|
-
(
|
|
3381
|
+
const rejectedRoles = resolvedRoles.filter(
|
|
3382
|
+
(entry) => !entry.validRole || !entry.validModel
|
|
3383
|
+
);
|
|
3384
|
+
const effortErrors = resolvedRoles.filter(
|
|
3385
|
+
(entry) => entry.validRole && entry.validModel && !entry.effort.ok
|
|
3233
3386
|
);
|
|
3234
3387
|
const warnings = [
|
|
3235
3388
|
...status.diagnostics,
|
|
3236
3389
|
...input.warnings ?? [],
|
|
3237
3390
|
...rejectedRoles.map(
|
|
3238
|
-
(role) => warning(
|
|
3391
|
+
({ role }) => warning(
|
|
3239
3392
|
`Claude Code does not accept role "${role.role}" with model "${role.model}"; roles must be one of ${CLAUDE_CODE_ROLE_NAMES.join(", ")} and models must be sonnet, opus, haiku, or inherit.`,
|
|
3240
3393
|
"claude-code-unsupported-model-role"
|
|
3241
3394
|
)
|
|
3395
|
+
),
|
|
3396
|
+
...effortErrors.map(
|
|
3397
|
+
({ effort }) => warning(
|
|
3398
|
+
effort.ok ? "Unknown Claude Code effort error." : effort.message,
|
|
3399
|
+
effort.ok ? "claude-code-effort-unknown" : effort.code
|
|
3400
|
+
)
|
|
3242
3401
|
)
|
|
3243
3402
|
];
|
|
3244
3403
|
if (input.harness !== "claude") {
|
|
@@ -3269,7 +3428,7 @@ function buildClaudeCodeModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3269
3428
|
title: "Configure Claude Code subagent model lines",
|
|
3270
3429
|
summary: "Preview model changes for generated Claude Code subagent files and managed model state only.",
|
|
3271
3430
|
dryRun: true,
|
|
3272
|
-
canApply: input.harness === "claude" && supportedRoles.length > 0 && status.state !== "unknown",
|
|
3431
|
+
canApply: input.harness === "claude" && supportedRoles.length > 0 && effortErrors.length === 0 && status.state !== "unknown",
|
|
3273
3432
|
targets: [...targets, ...stateTarget ? [stateTarget] : []],
|
|
3274
3433
|
surfaces: targets.map((target) => ({
|
|
3275
3434
|
id: `claude-code-model:${target.label}`,
|
|
@@ -3282,7 +3441,7 @@ function buildClaudeCodeModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3282
3441
|
strategy: "managed-backup-file",
|
|
3283
3442
|
description: "Existing subagent files and managed model state are backed up by the managed write helper."
|
|
3284
3443
|
},
|
|
3285
|
-
items: supportedRoles.map(({ role, model }) => ({
|
|
3444
|
+
items: supportedRoles.map(({ role, model, effort }) => ({
|
|
3286
3445
|
title: `Set ${role} Claude Code subagent model line`,
|
|
3287
3446
|
target: targets.find(
|
|
3288
3447
|
(target) => target.path?.endsWith(`agents${pathSep()}${role}.md`)
|
|
@@ -3290,7 +3449,7 @@ function buildClaudeCodeModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3290
3449
|
kind: "generated-artifact",
|
|
3291
3450
|
label: `Claude Code ${role} subagent`
|
|
3292
3451
|
},
|
|
3293
|
-
preview: JSON.stringify({ role, model }),
|
|
3452
|
+
preview: JSON.stringify({ role, model, effort: effort ?? null }),
|
|
3294
3453
|
backup: { required: true, strategy: "managed-backup-file" }
|
|
3295
3454
|
})),
|
|
3296
3455
|
warnings,
|
|
@@ -3304,7 +3463,7 @@ function buildClaudeCodeModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3304
3463
|
]
|
|
3305
3464
|
};
|
|
3306
3465
|
claudeCodeModelSources.set(plan, {
|
|
3307
|
-
config: claudeCodeConfig(
|
|
3466
|
+
config: claudeCodeConfig(context2, false),
|
|
3308
3467
|
roles: supportedRoles
|
|
3309
3468
|
});
|
|
3310
3469
|
return plan;
|
|
@@ -3360,7 +3519,10 @@ function applyClaudeCodePlan(plan) {
|
|
|
3360
3519
|
source.config,
|
|
3361
3520
|
source.roles.map((role) => ({
|
|
3362
3521
|
role: role.role,
|
|
3363
|
-
model: role.model
|
|
3522
|
+
model: role.model,
|
|
3523
|
+
...role.catalogId ? { catalogId: role.catalogId } : {},
|
|
3524
|
+
...role.effort ? { effort: role.effort } : {},
|
|
3525
|
+
...role.clearEffort ? { clearEffort: true } : {}
|
|
3364
3526
|
}))
|
|
3365
3527
|
);
|
|
3366
3528
|
return {
|
|
@@ -3479,16 +3641,16 @@ var codexOperationAdapter = {
|
|
|
3479
3641
|
description: "Codex agent-pack and managed subagent surfaces.",
|
|
3480
3642
|
actions: codexActions
|
|
3481
3643
|
};
|
|
3482
|
-
function codexConfig(
|
|
3644
|
+
function codexConfig(context2 = { cwd: process.cwd() }, dryRun) {
|
|
3483
3645
|
return {
|
|
3484
3646
|
dryRun,
|
|
3485
3647
|
reset: false,
|
|
3486
|
-
scope:
|
|
3487
|
-
projectRoot:
|
|
3488
|
-
homeDir:
|
|
3489
|
-
codexHome:
|
|
3490
|
-
packageRoot:
|
|
3491
|
-
pluginId:
|
|
3648
|
+
scope: context2.scope ?? "user",
|
|
3649
|
+
projectRoot: context2.cwd,
|
|
3650
|
+
homeDir: context2.homeDir,
|
|
3651
|
+
codexHome: context2.codexHome,
|
|
3652
|
+
packageRoot: context2.packageRoot,
|
|
3653
|
+
pluginId: context2.pluginId
|
|
3492
3654
|
};
|
|
3493
3655
|
}
|
|
3494
3656
|
function codexDisclaimers() {
|
|
@@ -3654,10 +3816,10 @@ function statusSummary2(state) {
|
|
|
3654
3816
|
return "Codex managed setup could not be classified safely.";
|
|
3655
3817
|
}
|
|
3656
3818
|
}
|
|
3657
|
-
function getCodexStatus(
|
|
3819
|
+
function getCodexStatus(context2 = { cwd: process.cwd() }) {
|
|
3658
3820
|
let plan;
|
|
3659
3821
|
try {
|
|
3660
|
-
plan = buildCodexSetupPlan(codexConfig(
|
|
3822
|
+
plan = buildCodexSetupPlan(codexConfig(context2, true));
|
|
3661
3823
|
} catch (error) {
|
|
3662
3824
|
const message = error instanceof Error ? error.message : String(error);
|
|
3663
3825
|
return {
|
|
@@ -3708,8 +3870,8 @@ function planItemFromSetup2(item) {
|
|
|
3708
3870
|
backup: backupForItem2(item)
|
|
3709
3871
|
};
|
|
3710
3872
|
}
|
|
3711
|
-
function planFromSetup2(id, action, title, summary, setupPlan,
|
|
3712
|
-
const status = getCodexStatus(
|
|
3873
|
+
function planFromSetup2(id, action, title, summary, setupPlan, context2) {
|
|
3874
|
+
const status = getCodexStatus(context2);
|
|
3713
3875
|
const canApply = status.state === "installed" || status.state === "missing" || status.state === "outdated";
|
|
3714
3876
|
const plan = {
|
|
3715
3877
|
id,
|
|
@@ -3744,54 +3906,91 @@ function planFromSetup2(id, action, title, summary, setupPlan, context) {
|
|
|
3744
3906
|
codexPlanSources.set(plan, setupPlan);
|
|
3745
3907
|
return plan;
|
|
3746
3908
|
}
|
|
3747
|
-
function buildCodexUpdatePlan(
|
|
3748
|
-
const setupPlan = buildCodexSetupPlan(codexConfig(
|
|
3909
|
+
function buildCodexUpdatePlan(context2 = { cwd: process.cwd() }) {
|
|
3910
|
+
const setupPlan = buildCodexSetupPlan(codexConfig(context2, true));
|
|
3749
3911
|
return planFromSetup2(
|
|
3750
3912
|
"codex-update-preview",
|
|
3751
3913
|
"update",
|
|
3752
3914
|
"Update Codex managed setup",
|
|
3753
3915
|
"Preview Codex managed setup refresh using buildCodexSetupPlan().",
|
|
3754
3916
|
setupPlan,
|
|
3755
|
-
|
|
3917
|
+
context2
|
|
3756
3918
|
);
|
|
3757
3919
|
}
|
|
3758
|
-
function buildCodexSyncPlan(
|
|
3759
|
-
const setupPlan = buildCodexSetupPlan(codexConfig(
|
|
3920
|
+
function buildCodexSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
3921
|
+
const setupPlan = buildCodexSetupPlan(codexConfig(context2, true));
|
|
3760
3922
|
return planFromSetup2(
|
|
3761
3923
|
"codex-sync-preview",
|
|
3762
3924
|
"sync",
|
|
3763
3925
|
"Sync Codex managed configuration",
|
|
3764
3926
|
"Preview Codex managed root instructions, subagents, plugin source, marketplace entry, and feature gates.",
|
|
3765
3927
|
setupPlan,
|
|
3766
|
-
|
|
3928
|
+
context2
|
|
3767
3929
|
);
|
|
3768
3930
|
}
|
|
3769
|
-
function buildCodexInstallPlan(
|
|
3770
|
-
const setupPlan = buildCodexSetupPlan(codexConfig(
|
|
3931
|
+
function buildCodexInstallPlan(context2 = { cwd: process.cwd() }) {
|
|
3932
|
+
const setupPlan = buildCodexSetupPlan(codexConfig(context2, true));
|
|
3771
3933
|
return planFromSetup2(
|
|
3772
3934
|
"codex-install-preview",
|
|
3773
3935
|
"install",
|
|
3774
3936
|
"Install Codex managed setup",
|
|
3775
3937
|
"Preview Codex managed agent-pack setup using buildCodexSetupPlan().",
|
|
3776
3938
|
setupPlan,
|
|
3777
|
-
|
|
3939
|
+
context2
|
|
3778
3940
|
);
|
|
3779
3941
|
}
|
|
3780
3942
|
function normalizeCodexModel(input) {
|
|
3781
|
-
|
|
3782
|
-
|
|
3943
|
+
return normalizeCodexRuntimeModel(input.model);
|
|
3944
|
+
}
|
|
3945
|
+
var CODEX_DOCUMENTED_EFFORTS = /* @__PURE__ */ new Set([
|
|
3946
|
+
"none",
|
|
3947
|
+
"minimal",
|
|
3948
|
+
"low",
|
|
3949
|
+
"medium",
|
|
3950
|
+
"high",
|
|
3951
|
+
"xhigh",
|
|
3952
|
+
"max",
|
|
3953
|
+
"ultra"
|
|
3954
|
+
]);
|
|
3955
|
+
function resolveCodexEffort(input) {
|
|
3956
|
+
if (!input.effort || input.effort.kind === "inherit") {
|
|
3957
|
+
return { ok: true, value: void 0 };
|
|
3958
|
+
}
|
|
3959
|
+
const value = input.effort.value;
|
|
3960
|
+
if (!CODEX_DOCUMENTED_EFFORTS.has(value)) {
|
|
3961
|
+
return {
|
|
3962
|
+
ok: false,
|
|
3963
|
+
code: "codex-effort-undocumented",
|
|
3964
|
+
message: `Codex does not document reasoning effort "${value}".`
|
|
3965
|
+
};
|
|
3783
3966
|
}
|
|
3784
|
-
|
|
3967
|
+
if (!input.catalogId?.startsWith("openai/") || !input.availableEfforts?.includes(value)) {
|
|
3968
|
+
return {
|
|
3969
|
+
ok: false,
|
|
3970
|
+
code: "codex-effort-model-unsupported",
|
|
3971
|
+
message: `Model ${input.catalogId ?? input.model} does not publish Codex effort "${value}".`
|
|
3972
|
+
};
|
|
3973
|
+
}
|
|
3974
|
+
return { ok: true, value };
|
|
3785
3975
|
}
|
|
3786
3976
|
function isCodexRole(role) {
|
|
3787
3977
|
return CODEX_ROLE_NAMES.includes(role);
|
|
3788
3978
|
}
|
|
3789
|
-
function buildCodexModelPlan(input,
|
|
3790
|
-
const status = getCodexStatus(
|
|
3791
|
-
const
|
|
3979
|
+
function buildCodexModelPlan(input, context2 = { cwd: process.cwd() }) {
|
|
3980
|
+
const status = getCodexStatus(context2);
|
|
3981
|
+
const candidateRoles = input.roles.filter((role) => isCodexRole(role.role)).map((role) => ({
|
|
3982
|
+
role: role.role,
|
|
3983
|
+
model: normalizeCodexModel(role),
|
|
3984
|
+
effort: resolveCodexEffort(role),
|
|
3985
|
+
clearEffort: role.effort?.kind === "inherit"
|
|
3986
|
+
}));
|
|
3987
|
+
const supportedRoles = candidateRoles.filter((role) => role.effort.ok).map((role) => ({
|
|
3792
3988
|
role: role.role,
|
|
3793
|
-
model:
|
|
3989
|
+
model: role.model,
|
|
3990
|
+
...role.effort.value ? { effort: role.effort.value } : {},
|
|
3991
|
+
...role.clearEffort ? { clearEffort: true } : {}
|
|
3794
3992
|
}));
|
|
3993
|
+
const effortErrors = candidateRoles.filter((role) => !role.effort.ok);
|
|
3795
3994
|
const unsupportedRoles = input.roles.filter(
|
|
3796
3995
|
(role) => !isCodexRole(role.role)
|
|
3797
3996
|
);
|
|
@@ -3803,6 +4002,12 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3803
4002
|
`Codex does not expose a supported generated model surface for ${role.role}; this plan will not write that role.`,
|
|
3804
4003
|
"codex-unsupported-model-role"
|
|
3805
4004
|
)
|
|
4005
|
+
),
|
|
4006
|
+
...effortErrors.map(
|
|
4007
|
+
(role) => warning2(
|
|
4008
|
+
role.effort.message,
|
|
4009
|
+
role.effort.code
|
|
4010
|
+
)
|
|
3806
4011
|
)
|
|
3807
4012
|
];
|
|
3808
4013
|
if (input.harness !== "codex") {
|
|
@@ -3823,7 +4028,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3823
4028
|
state: "missing"
|
|
3824
4029
|
};
|
|
3825
4030
|
});
|
|
3826
|
-
const config = codexConfig(
|
|
4031
|
+
const config = codexConfig(context2, false);
|
|
3827
4032
|
const stateTarget = status.targets.find(
|
|
3828
4033
|
(target) => target.path?.endsWith(".thoth-agents-managed-models.json")
|
|
3829
4034
|
);
|
|
@@ -3834,7 +4039,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3834
4039
|
title: "Configure Codex subagent model lines",
|
|
3835
4040
|
summary: "Preview model changes for generated Codex subagent TOML files and .thoth-agents-managed-models.json only.",
|
|
3836
4041
|
dryRun: true,
|
|
3837
|
-
canApply: input.harness === "codex" && supportedRoles.length > 0 && status.state !== "unknown",
|
|
4042
|
+
canApply: input.harness === "codex" && supportedRoles.length > 0 && effortErrors.length === 0 && status.state !== "unknown",
|
|
3838
4043
|
targets: [...targets, ...stateTarget ? [stateTarget] : []],
|
|
3839
4044
|
surfaces: targets.map((target) => ({
|
|
3840
4045
|
id: `codex-model:${target.label}`,
|
|
@@ -3847,7 +4052,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3847
4052
|
strategy: "managed-backup-file",
|
|
3848
4053
|
description: "Existing subagent TOML and managed model state files are backed up by the managed write helper."
|
|
3849
4054
|
},
|
|
3850
|
-
items: supportedRoles.map(({ role, model }) => ({
|
|
4055
|
+
items: supportedRoles.map(({ role, model, effort }) => ({
|
|
3851
4056
|
title: `Set ${role} Codex subagent model line`,
|
|
3852
4057
|
target: targets.find(
|
|
3853
4058
|
(target) => target.path?.endsWith(`thoth-agents-${role}.toml`)
|
|
@@ -3855,7 +4060,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3855
4060
|
kind: "generated-artifact",
|
|
3856
4061
|
label: `Codex ${role} subagent`
|
|
3857
4062
|
},
|
|
3858
|
-
preview: JSON.stringify({ role, model }),
|
|
4063
|
+
preview: JSON.stringify({ role, model, effort }),
|
|
3859
4064
|
backup: {
|
|
3860
4065
|
required: true,
|
|
3861
4066
|
strategy: "managed-backup-file"
|
|
@@ -3971,6 +4176,36 @@ function applyCodexPlan(plan) {
|
|
|
3971
4176
|
import { existsSync as existsSync10 } from "fs";
|
|
3972
4177
|
import { join as join10 } from "path";
|
|
3973
4178
|
|
|
4179
|
+
// src/cli/opencode-effort.ts
|
|
4180
|
+
var OPENAI_GPT_RUNTIME_VARIANTS = /* @__PURE__ */ new Set([
|
|
4181
|
+
"none",
|
|
4182
|
+
"low",
|
|
4183
|
+
"medium",
|
|
4184
|
+
"high",
|
|
4185
|
+
"xhigh"
|
|
4186
|
+
]);
|
|
4187
|
+
function resolveOpenCodeEffort(input) {
|
|
4188
|
+
if (!input.effort || input.effort.kind === "inherit") {
|
|
4189
|
+
return { ok: true, variant: void 0 };
|
|
4190
|
+
}
|
|
4191
|
+
const variant = input.effort.value;
|
|
4192
|
+
if (!input.availableEfforts?.includes(variant)) {
|
|
4193
|
+
return {
|
|
4194
|
+
ok: false,
|
|
4195
|
+
code: "opencode-effort-catalog-unsupported",
|
|
4196
|
+
message: `Effort ${variant} is not supported by ${input.catalogId ?? input.model} in the models.dev catalog.`
|
|
4197
|
+
};
|
|
4198
|
+
}
|
|
4199
|
+
if (input.catalogId?.startsWith("openai/gpt-") && OPENAI_GPT_RUNTIME_VARIANTS.has(variant)) {
|
|
4200
|
+
return { ok: true, variant };
|
|
4201
|
+
}
|
|
4202
|
+
return {
|
|
4203
|
+
ok: false,
|
|
4204
|
+
code: "opencode-effort-runtime-unconfirmed",
|
|
4205
|
+
message: `Effort ${variant} is catalogued for ${input.catalogId ?? input.model}, but the OpenCode runtime mapping is not confirmed.`
|
|
4206
|
+
};
|
|
4207
|
+
}
|
|
4208
|
+
|
|
3974
4209
|
// src/cli/skills.ts
|
|
3975
4210
|
import { spawnSync } from "child_process";
|
|
3976
4211
|
import { existsSync as existsSync9 } from "fs";
|
|
@@ -4141,11 +4376,11 @@ function titleCaseSkillName(value) {
|
|
|
4141
4376
|
return `${part.charAt(0).toUpperCase()}${part.slice(1).toLowerCase()}`;
|
|
4142
4377
|
}).join("-");
|
|
4143
4378
|
}
|
|
4144
|
-
function homeDirFromContext(
|
|
4145
|
-
return
|
|
4379
|
+
function homeDirFromContext(context2) {
|
|
4380
|
+
return context2.env?.HOME ?? context2.env?.USERPROFILE;
|
|
4146
4381
|
}
|
|
4147
|
-
function openCodeSkillTargets(
|
|
4148
|
-
const homeDir = homeDirFromContext(
|
|
4382
|
+
function openCodeSkillTargets(context2) {
|
|
4383
|
+
const homeDir = homeDirFromContext(context2);
|
|
4149
4384
|
const recommendedTargets = RECOMMENDED_SKILLS.map(
|
|
4150
4385
|
(skill) => {
|
|
4151
4386
|
const path4 = getRecommendedSkillPath(skill, homeDir);
|
|
@@ -4226,13 +4461,13 @@ function hasSevenAgentPreset(config) {
|
|
|
4226
4461
|
function classifyApplySafety(state) {
|
|
4227
4462
|
return state === "missing" || state === "installed";
|
|
4228
4463
|
}
|
|
4229
|
-
function getOpenCodeStatus(
|
|
4464
|
+
function getOpenCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
4230
4465
|
const mainPath = getExistingConfigPath();
|
|
4231
4466
|
const litePath = getExistingLiteConfigPath();
|
|
4232
4467
|
const main = parseConfig(mainPath);
|
|
4233
4468
|
const lite = parseConfig(litePath);
|
|
4234
4469
|
const diagnostics = [];
|
|
4235
|
-
const skillStatus = openCodeSkillTargets(
|
|
4470
|
+
const skillStatus = openCodeSkillTargets(context2);
|
|
4236
4471
|
if (main.error) {
|
|
4237
4472
|
diagnostics.push({
|
|
4238
4473
|
severity: "critical",
|
|
@@ -4554,11 +4789,26 @@ function normalizeRoleModel(input) {
|
|
|
4554
4789
|
function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
4555
4790
|
const items = input.roles.map((role) => {
|
|
4556
4791
|
const model = normalizeRoleModel(role);
|
|
4792
|
+
const effort = resolveOpenCodeEffort(role);
|
|
4557
4793
|
return {
|
|
4558
4794
|
title: `Set ${role.role} OpenCode model override`,
|
|
4559
4795
|
target: targetForLiteConfig(),
|
|
4560
|
-
preview: JSON.stringify({
|
|
4561
|
-
|
|
4796
|
+
preview: JSON.stringify({
|
|
4797
|
+
[role.role]: {
|
|
4798
|
+
model,
|
|
4799
|
+
variant: effort.ok ? effort.variant ?? null : null
|
|
4800
|
+
}
|
|
4801
|
+
}),
|
|
4802
|
+
backup: defaultBackup(getExistingLiteConfigPath()),
|
|
4803
|
+
...!effort.ok ? {
|
|
4804
|
+
warnings: [
|
|
4805
|
+
{
|
|
4806
|
+
severity: "critical",
|
|
4807
|
+
code: effort.code,
|
|
4808
|
+
message: effort.message
|
|
4809
|
+
}
|
|
4810
|
+
]
|
|
4811
|
+
} : {}
|
|
4562
4812
|
};
|
|
4563
4813
|
});
|
|
4564
4814
|
const plan = planFromItems(
|
|
@@ -4568,6 +4818,11 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
|
4568
4818
|
"Preview thoth-agents role model overrides in the plugin config agents map.",
|
|
4569
4819
|
items
|
|
4570
4820
|
);
|
|
4821
|
+
const effortWarnings = items.flatMap((item) => item.warnings ?? []);
|
|
4822
|
+
if (effortWarnings.length > 0) {
|
|
4823
|
+
plan.canApply = false;
|
|
4824
|
+
plan.warnings.push(...effortWarnings);
|
|
4825
|
+
}
|
|
4571
4826
|
if (input.harness !== "opencode") {
|
|
4572
4827
|
return {
|
|
4573
4828
|
...plan,
|
|
@@ -4693,13 +4948,14 @@ function applyModelPlan(plan) {
|
|
|
4693
4948
|
}
|
|
4694
4949
|
const role = match[1] ?? "";
|
|
4695
4950
|
const model = parsed2[role]?.model;
|
|
4696
|
-
|
|
4951
|
+
const variant = parsed2[role]?.variant;
|
|
4952
|
+
if (!ROLE_NAMES.includes(role) || typeof model !== "string" || variant !== null && typeof variant !== "string") {
|
|
4697
4953
|
return rejectPlan3(
|
|
4698
4954
|
plan,
|
|
4699
4955
|
"OpenCode model plan contains an invalid role or model."
|
|
4700
4956
|
);
|
|
4701
4957
|
}
|
|
4702
|
-
roleModels.set(role, model);
|
|
4958
|
+
roleModels.set(role, { model, variant });
|
|
4703
4959
|
}
|
|
4704
4960
|
if (roleModels.size === 0) {
|
|
4705
4961
|
return rejectPlan3(
|
|
@@ -4719,14 +4975,44 @@ function applyModelPlan(plan) {
|
|
|
4719
4975
|
reset: false
|
|
4720
4976
|
});
|
|
4721
4977
|
const agents = base.agents && typeof base.agents === "object" ? { ...base.agents } : {};
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4978
|
+
const statePath = getOpenCodeManagedModelStatePath();
|
|
4979
|
+
const state = readManagedModelState(statePath, 1);
|
|
4980
|
+
const configuredEfforts = { ...state.configuredEfforts ?? {} };
|
|
4981
|
+
const warnings = [];
|
|
4982
|
+
for (const [role, override] of roleModels) {
|
|
4983
|
+
const current = {
|
|
4984
|
+
...agents[role] ?? {}
|
|
4726
4985
|
};
|
|
4986
|
+
const currentVariant = typeof current.variant === "string" ? current.variant : void 0;
|
|
4987
|
+
const trackedVariant = configuredEfforts[role];
|
|
4988
|
+
const userOwnsVariant = override.variant === null && currentVariant !== void 0 && (trackedVariant === void 0 || currentVariant !== trackedVariant);
|
|
4989
|
+
current.model = override.model;
|
|
4990
|
+
if (userOwnsVariant) {
|
|
4991
|
+
delete configuredEfforts[role];
|
|
4992
|
+
warnings.push({
|
|
4993
|
+
severity: "important",
|
|
4994
|
+
code: "opencode-effort-user-owned",
|
|
4995
|
+
message: `Preserved user-owned OpenCode variant ${currentVariant} for ${role}.`
|
|
4996
|
+
});
|
|
4997
|
+
} else if (override.variant === null) {
|
|
4998
|
+
delete current.variant;
|
|
4999
|
+
delete configuredEfforts[role];
|
|
5000
|
+
} else {
|
|
5001
|
+
current.variant = override.variant;
|
|
5002
|
+
configuredEfforts[role] = override.variant;
|
|
5003
|
+
}
|
|
5004
|
+
agents[role] = current;
|
|
4727
5005
|
}
|
|
4728
5006
|
base.agents = agents;
|
|
4729
5007
|
writeConfig(targetPath, base);
|
|
5008
|
+
writeTextWithBackup(
|
|
5009
|
+
statePath,
|
|
5010
|
+
stableJson3({
|
|
5011
|
+
version: 1,
|
|
5012
|
+
models: state.models,
|
|
5013
|
+
...Object.keys(configuredEfforts).length > 0 ? { configuredEfforts } : {}
|
|
5014
|
+
})
|
|
5015
|
+
);
|
|
4730
5016
|
return {
|
|
4731
5017
|
harness: "opencode",
|
|
4732
5018
|
action: "model-config",
|
|
@@ -4739,7 +5025,7 @@ function applyModelPlan(plan) {
|
|
|
4739
5025
|
}
|
|
4740
5026
|
],
|
|
4741
5027
|
backups: existsSync10(`${targetPath}.bak`) ? [{ path: `${targetPath}.bak`, label: "managed backup" }] : [],
|
|
4742
|
-
warnings
|
|
5028
|
+
warnings,
|
|
4743
5029
|
disclaimers: defaultDisclaimers()
|
|
4744
5030
|
};
|
|
4745
5031
|
}
|
|
@@ -4868,23 +5154,571 @@ function getOperationHarness(harness) {
|
|
|
4868
5154
|
return OPERATION_HARNESSES[harness];
|
|
4869
5155
|
}
|
|
4870
5156
|
|
|
5157
|
+
// src/cli/tui/model-catalog.ts
|
|
5158
|
+
import { execFileSync } from "child_process";
|
|
5159
|
+
|
|
5160
|
+
// src/cli/model-catalog/cache.ts
|
|
5161
|
+
import {
|
|
5162
|
+
existsSync as existsSync11,
|
|
5163
|
+
mkdirSync as mkdirSync3,
|
|
5164
|
+
readFileSync as readFileSync10,
|
|
5165
|
+
renameSync as renameSync2,
|
|
5166
|
+
unlinkSync,
|
|
5167
|
+
writeFileSync as writeFileSync3
|
|
5168
|
+
} from "fs";
|
|
5169
|
+
import { homedir as homedir4 } from "os";
|
|
5170
|
+
import { dirname as dirname5, posix, win32 } from "path";
|
|
5171
|
+
import { z as z2 } from "zod";
|
|
5172
|
+
var modelCatalogSourceSchema = z2.enum(["remote", "lkg", "manual"]);
|
|
5173
|
+
var cachedModelOptionSchema = z2.object({
|
|
5174
|
+
id: z2.string().min(1),
|
|
5175
|
+
catalogId: z2.string().min(1).optional(),
|
|
5176
|
+
label: z2.string().min(1),
|
|
5177
|
+
provider: z2.string().min(1),
|
|
5178
|
+
efforts: z2.array(z2.string().min(1)),
|
|
5179
|
+
source: modelCatalogSourceSchema
|
|
5180
|
+
}).passthrough();
|
|
5181
|
+
var modelsDevCacheSchema = z2.object({
|
|
5182
|
+
version: z2.literal(1),
|
|
5183
|
+
etag: z2.string().optional(),
|
|
5184
|
+
fetchedAt: z2.string().datetime(),
|
|
5185
|
+
catalog: z2.object({ models: z2.array(cachedModelOptionSchema) }).passthrough()
|
|
5186
|
+
}).passthrough();
|
|
5187
|
+
function resolveModelsDevCachePath(options = {}) {
|
|
5188
|
+
const platform = options.platform ?? process.platform;
|
|
5189
|
+
const env = options.env ?? process.env;
|
|
5190
|
+
const home = options.homeDir ?? homedir4();
|
|
5191
|
+
if (platform === "win32") {
|
|
5192
|
+
const path4 = win32;
|
|
5193
|
+
const base2 = env.LOCALAPPDATA ?? env.APPDATA ?? path4.join(home, "AppData", "Local");
|
|
5194
|
+
return path4.join(base2, "thoth-agents", "models-dev-v1.json");
|
|
5195
|
+
}
|
|
5196
|
+
const base = env.XDG_CACHE_HOME ?? posix.join(home, ".cache");
|
|
5197
|
+
return posix.join(base, "thoth-agents", "models-dev-v1.json");
|
|
5198
|
+
}
|
|
5199
|
+
function readModelsDevCache(cachePath) {
|
|
5200
|
+
if (!existsSync11(cachePath)) return void 0;
|
|
5201
|
+
try {
|
|
5202
|
+
const input = JSON.parse(readFileSync10(cachePath, "utf8"));
|
|
5203
|
+
const parsed = modelsDevCacheSchema.safeParse(input);
|
|
5204
|
+
return parsed.success ? parsed.data : void 0;
|
|
5205
|
+
} catch {
|
|
5206
|
+
return void 0;
|
|
5207
|
+
}
|
|
5208
|
+
}
|
|
5209
|
+
function writeModelsDevCacheAtomic(cachePath, cache) {
|
|
5210
|
+
const validated = modelsDevCacheSchema.safeParse(cache);
|
|
5211
|
+
if (!validated.success) {
|
|
5212
|
+
throw new Error(`Invalid models.dev cache: ${validated.error.message}`);
|
|
5213
|
+
}
|
|
5214
|
+
mkdirSync3(dirname5(cachePath), { recursive: true });
|
|
5215
|
+
const temporaryPath = `${cachePath}.${process.pid}.${Date.now()}.tmp`;
|
|
5216
|
+
try {
|
|
5217
|
+
writeFileSync3(
|
|
5218
|
+
temporaryPath,
|
|
5219
|
+
`${JSON.stringify(validated.data, null, 2)}
|
|
5220
|
+
`
|
|
5221
|
+
);
|
|
5222
|
+
renameSync2(temporaryPath, cachePath);
|
|
5223
|
+
} catch (error) {
|
|
5224
|
+
if (existsSync11(temporaryPath)) unlinkSync(temporaryPath);
|
|
5225
|
+
throw error;
|
|
5226
|
+
}
|
|
5227
|
+
}
|
|
5228
|
+
|
|
5229
|
+
// src/cli/model-catalog/models-dev.ts
|
|
5230
|
+
import { z as z3 } from "zod";
|
|
5231
|
+
var modelsDevModelSchema = z3.object({
|
|
5232
|
+
id: z3.string().min(1).optional(),
|
|
5233
|
+
name: z3.string().min(1).optional(),
|
|
5234
|
+
reasoning: z3.boolean().optional(),
|
|
5235
|
+
reasoning_options: z3.array(z3.unknown()).optional()
|
|
5236
|
+
}).passthrough();
|
|
5237
|
+
var modelsDevProviderSchema = z3.object({
|
|
5238
|
+
models: z3.record(z3.string(), modelsDevModelSchema)
|
|
5239
|
+
}).passthrough();
|
|
5240
|
+
var modelsDevCatalogSchema = z3.record(
|
|
5241
|
+
z3.string(),
|
|
5242
|
+
modelsDevProviderSchema
|
|
5243
|
+
);
|
|
5244
|
+
var effortOptionSchema = z3.object({
|
|
5245
|
+
type: z3.literal("effort"),
|
|
5246
|
+
values: z3.array(z3.string().min(1))
|
|
5247
|
+
}).passthrough();
|
|
5248
|
+
function normalizedCatalogId(provider, modelKey, declaredId) {
|
|
5249
|
+
const candidate = (declaredId ?? modelKey).trim().toLowerCase();
|
|
5250
|
+
return candidate.includes("/") ? candidate : `${provider}/${candidate}`;
|
|
5251
|
+
}
|
|
5252
|
+
function runtimeModelId(provider, catalogId) {
|
|
5253
|
+
const prefix = `${provider}/`;
|
|
5254
|
+
return catalogId.startsWith(prefix) ? catalogId.slice(prefix.length) : catalogId;
|
|
5255
|
+
}
|
|
5256
|
+
function effortValues(options) {
|
|
5257
|
+
const values = /* @__PURE__ */ new Set();
|
|
5258
|
+
for (const option of options ?? []) {
|
|
5259
|
+
const parsed = effortOptionSchema.safeParse(option);
|
|
5260
|
+
if (!parsed.success) continue;
|
|
5261
|
+
for (const value of parsed.data.values) {
|
|
5262
|
+
const normalized = value.trim();
|
|
5263
|
+
if (normalized) values.add(normalized);
|
|
5264
|
+
}
|
|
5265
|
+
}
|
|
5266
|
+
return [...values];
|
|
5267
|
+
}
|
|
5268
|
+
function normalizeModelsDevCatalog(input) {
|
|
5269
|
+
const parsed = modelsDevCatalogSchema.safeParse(input);
|
|
5270
|
+
if (!parsed.success) {
|
|
5271
|
+
return {
|
|
5272
|
+
ok: false,
|
|
5273
|
+
issues: parsed.error.issues.map(
|
|
5274
|
+
(issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`
|
|
5275
|
+
)
|
|
5276
|
+
};
|
|
5277
|
+
}
|
|
5278
|
+
const models = [];
|
|
5279
|
+
for (const [rawProvider, provider] of Object.entries(parsed.data)) {
|
|
5280
|
+
const providerId = rawProvider.trim().toLowerCase();
|
|
5281
|
+
for (const [modelKey, model] of Object.entries(provider.models)) {
|
|
5282
|
+
const catalogId = normalizedCatalogId(providerId, modelKey, model.id);
|
|
5283
|
+
const id = runtimeModelId(providerId, catalogId);
|
|
5284
|
+
models.push({
|
|
5285
|
+
id,
|
|
5286
|
+
catalogId,
|
|
5287
|
+
label: model.name ?? id,
|
|
5288
|
+
provider: providerId,
|
|
5289
|
+
efforts: effortValues(model.reasoning_options),
|
|
5290
|
+
source: "remote"
|
|
5291
|
+
});
|
|
5292
|
+
}
|
|
5293
|
+
}
|
|
5294
|
+
return { ok: true, catalog: { models } };
|
|
5295
|
+
}
|
|
5296
|
+
|
|
5297
|
+
// src/cli/model-catalog/index.ts
|
|
5298
|
+
var MODELS_DEV_URL = "https://models.dev/api.json";
|
|
5299
|
+
var MODELS_DEV_MAX_BYTES = 8 * 1024 * 1024;
|
|
5300
|
+
var MODELS_DEV_TIMEOUT_MS = 5e3;
|
|
5301
|
+
function ownsCatalogKey(model, key) {
|
|
5302
|
+
const separator = key.indexOf("/");
|
|
5303
|
+
return separator > 0 && model.provider === key.slice(0, separator);
|
|
5304
|
+
}
|
|
5305
|
+
function mergeModelCatalog(dynamic, manual) {
|
|
5306
|
+
const merged = /* @__PURE__ */ new Map();
|
|
5307
|
+
for (const model of dynamic) {
|
|
5308
|
+
const key = model.catalogId ?? model.id;
|
|
5309
|
+
const existing = merged.get(key);
|
|
5310
|
+
if (existing && ownsCatalogKey(existing, key) && !ownsCatalogKey(model, key)) {
|
|
5311
|
+
continue;
|
|
5312
|
+
}
|
|
5313
|
+
merged.set(key, model);
|
|
5314
|
+
}
|
|
5315
|
+
for (const model of manual) {
|
|
5316
|
+
const key = model.catalogId ?? model.id;
|
|
5317
|
+
if (!merged.has(key)) merged.set(key, model);
|
|
5318
|
+
}
|
|
5319
|
+
return [...merged.values()];
|
|
5320
|
+
}
|
|
5321
|
+
function lkgModels(models) {
|
|
5322
|
+
return models.map((model) => ({
|
|
5323
|
+
...model,
|
|
5324
|
+
efforts: [...model.efforts],
|
|
5325
|
+
source: "lkg"
|
|
5326
|
+
}));
|
|
5327
|
+
}
|
|
5328
|
+
async function loadModelsDevCatalog(options = {}) {
|
|
5329
|
+
const cachePath = options.cachePath ?? resolveModelsDevCachePath();
|
|
5330
|
+
const manual = options.manual ?? [];
|
|
5331
|
+
const fetcher = options.fetcher ?? fetch;
|
|
5332
|
+
const checkedAt = (options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
|
|
5333
|
+
const cached = readModelsDevCache(cachePath);
|
|
5334
|
+
const warnings = [];
|
|
5335
|
+
try {
|
|
5336
|
+
const headers = new Headers({ Accept: "application/json" });
|
|
5337
|
+
if (cached?.etag) headers.set("If-None-Match", cached.etag);
|
|
5338
|
+
const response = await fetcher(MODELS_DEV_URL, {
|
|
5339
|
+
headers,
|
|
5340
|
+
signal: AbortSignal.timeout(MODELS_DEV_TIMEOUT_MS)
|
|
5341
|
+
});
|
|
5342
|
+
if (response.status === 304) {
|
|
5343
|
+
if (!cached)
|
|
5344
|
+
throw new Error("models.dev returned 304 without an LKG cache");
|
|
5345
|
+
return {
|
|
5346
|
+
models: mergeModelCatalog(lkgModels(cached.catalog.models), manual),
|
|
5347
|
+
source: "lkg",
|
|
5348
|
+
stale: false,
|
|
5349
|
+
checkedAt,
|
|
5350
|
+
warnings
|
|
5351
|
+
};
|
|
5352
|
+
}
|
|
5353
|
+
if (!response.ok)
|
|
5354
|
+
throw new Error(`models.dev returned HTTP ${response.status}`);
|
|
5355
|
+
const text = await response.text();
|
|
5356
|
+
if (Buffer.byteLength(text, "utf8") > MODELS_DEV_MAX_BYTES) {
|
|
5357
|
+
throw new Error("models.dev response exceeded the maximum size");
|
|
5358
|
+
}
|
|
5359
|
+
const input = JSON.parse(text);
|
|
5360
|
+
const normalized = normalizeModelsDevCatalog(input);
|
|
5361
|
+
if (!normalized.ok) {
|
|
5362
|
+
throw new Error(
|
|
5363
|
+
`models.dev validation failed: ${normalized.issues.join("; ")}`
|
|
5364
|
+
);
|
|
5365
|
+
}
|
|
5366
|
+
const remoteModels = normalized.catalog.models.map((model) => ({
|
|
5367
|
+
...model,
|
|
5368
|
+
source: "remote"
|
|
5369
|
+
}));
|
|
5370
|
+
writeModelsDevCacheAtomic(cachePath, {
|
|
5371
|
+
version: 1,
|
|
5372
|
+
...response.headers.get("etag") ? { etag: response.headers.get("etag") ?? void 0 } : {},
|
|
5373
|
+
fetchedAt: checkedAt,
|
|
5374
|
+
catalog: { models: lkgModels(remoteModels) }
|
|
5375
|
+
});
|
|
5376
|
+
return {
|
|
5377
|
+
models: mergeModelCatalog(remoteModels, manual),
|
|
5378
|
+
source: "remote",
|
|
5379
|
+
stale: false,
|
|
5380
|
+
checkedAt,
|
|
5381
|
+
warnings
|
|
5382
|
+
};
|
|
5383
|
+
} catch (error) {
|
|
5384
|
+
warnings.push(error instanceof Error ? error.message : String(error));
|
|
5385
|
+
}
|
|
5386
|
+
if (cached) {
|
|
5387
|
+
return {
|
|
5388
|
+
models: mergeModelCatalog(lkgModels(cached.catalog.models), manual),
|
|
5389
|
+
source: "lkg",
|
|
5390
|
+
stale: true,
|
|
5391
|
+
checkedAt,
|
|
5392
|
+
warnings
|
|
5393
|
+
};
|
|
5394
|
+
}
|
|
5395
|
+
return {
|
|
5396
|
+
models: [...manual],
|
|
5397
|
+
source: "manual",
|
|
5398
|
+
stale: true,
|
|
5399
|
+
checkedAt,
|
|
5400
|
+
warnings
|
|
5401
|
+
};
|
|
5402
|
+
}
|
|
5403
|
+
|
|
5404
|
+
// src/cli/tui/model-catalog.ts
|
|
5405
|
+
var MODEL_CATALOG_TIMEOUT_MS = 5e3;
|
|
5406
|
+
var CODEX_DOCUMENTED_EFFORTS2 = /* @__PURE__ */ new Set([
|
|
5407
|
+
"none",
|
|
5408
|
+
"minimal",
|
|
5409
|
+
"low",
|
|
5410
|
+
"medium",
|
|
5411
|
+
"high",
|
|
5412
|
+
"xhigh",
|
|
5413
|
+
"max",
|
|
5414
|
+
"ultra"
|
|
5415
|
+
]);
|
|
5416
|
+
var CLAUDE_CODE_EFFORTS2 = ["low", "medium", "high", "xhigh", "max"];
|
|
5417
|
+
function parseOpenCodeModels(output) {
|
|
5418
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5419
|
+
const options = [];
|
|
5420
|
+
for (const line of output.split(/\r?\n/)) {
|
|
5421
|
+
const match = line.trim().match(/^([a-z0-9_-]+)\/([^\s{]+)/i);
|
|
5422
|
+
if (!match) continue;
|
|
5423
|
+
const id = `${match[1]}/${match[2]}`;
|
|
5424
|
+
if (seen.has(id)) continue;
|
|
5425
|
+
seen.add(id);
|
|
5426
|
+
options.push({
|
|
5427
|
+
id,
|
|
5428
|
+
catalogId: id,
|
|
5429
|
+
label: id,
|
|
5430
|
+
provider: match[1] ?? "unknown",
|
|
5431
|
+
efforts: [],
|
|
5432
|
+
source: "manual"
|
|
5433
|
+
});
|
|
5434
|
+
}
|
|
5435
|
+
return options;
|
|
5436
|
+
}
|
|
5437
|
+
function isCodexOpenAiModelId(id) {
|
|
5438
|
+
const match = id.match(/^gpt-(\d+)(?:[.-]|$)/);
|
|
5439
|
+
return match?.[1] !== void 0 && Number(match[1]) >= 5;
|
|
5440
|
+
}
|
|
5441
|
+
function writableEfforts(harness, option) {
|
|
5442
|
+
if (harness === "codex") {
|
|
5443
|
+
return option.efforts.filter(
|
|
5444
|
+
(effort) => CODEX_DOCUMENTED_EFFORTS2.has(effort)
|
|
5445
|
+
);
|
|
5446
|
+
}
|
|
5447
|
+
if (harness === "claude") {
|
|
5448
|
+
return option.efforts.filter(
|
|
5449
|
+
(effort) => CLAUDE_CODE_EFFORTS2.includes(effort)
|
|
5450
|
+
);
|
|
5451
|
+
}
|
|
5452
|
+
return option.efforts.filter(
|
|
5453
|
+
(effort) => resolveOpenCodeEffort({
|
|
5454
|
+
model: option.id,
|
|
5455
|
+
catalogId: option.catalogId,
|
|
5456
|
+
availableEfforts: option.efforts,
|
|
5457
|
+
effort: { kind: "effort", value: effort }
|
|
5458
|
+
}).ok
|
|
5459
|
+
);
|
|
5460
|
+
}
|
|
5461
|
+
function effortChoicesForModel(option) {
|
|
5462
|
+
return ["inherit", ...option?.efforts ?? []];
|
|
5463
|
+
}
|
|
5464
|
+
function getOpenCodeModelsInvocation(platform = process.platform) {
|
|
5465
|
+
const options = {
|
|
5466
|
+
encoding: "utf8",
|
|
5467
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
5468
|
+
timeout: MODEL_CATALOG_TIMEOUT_MS
|
|
5469
|
+
};
|
|
5470
|
+
if (platform === "win32") {
|
|
5471
|
+
return {
|
|
5472
|
+
command: "opencode models",
|
|
5473
|
+
args: [],
|
|
5474
|
+
options: { ...options, shell: true }
|
|
5475
|
+
};
|
|
5476
|
+
}
|
|
5477
|
+
return { command: "opencode", args: ["models"], options };
|
|
5478
|
+
}
|
|
5479
|
+
function getNativeOpenCodeOptions() {
|
|
5480
|
+
try {
|
|
5481
|
+
const invocation = getOpenCodeModelsInvocation();
|
|
5482
|
+
return parseOpenCodeModels(
|
|
5483
|
+
execFileSync(invocation.command, invocation.args, invocation.options)
|
|
5484
|
+
);
|
|
5485
|
+
} catch {
|
|
5486
|
+
return [];
|
|
5487
|
+
}
|
|
5488
|
+
}
|
|
5489
|
+
var CLAUDE_CODE_MODEL_OPTIONS = [
|
|
5490
|
+
{ id: "sonnet", label: "sonnet", provider: "anthropic" },
|
|
5491
|
+
{ id: "opus", label: "opus", provider: "anthropic" },
|
|
5492
|
+
{ id: "haiku", label: "haiku", provider: "anthropic" },
|
|
5493
|
+
{
|
|
5494
|
+
id: "inherit",
|
|
5495
|
+
label: "inherit (main session model)",
|
|
5496
|
+
provider: "anthropic"
|
|
5497
|
+
}
|
|
5498
|
+
].map((option) => ({
|
|
5499
|
+
...option,
|
|
5500
|
+
efforts: option.id === "inherit" ? [] : [...CLAUDE_CODE_EFFORTS2],
|
|
5501
|
+
source: "manual"
|
|
5502
|
+
}));
|
|
5503
|
+
async function getModelOptions(harness) {
|
|
5504
|
+
const native = harness === "opencode" ? getNativeOpenCodeOptions() : [];
|
|
5505
|
+
const loaded = await loadModelsDevCatalog({ manual: native });
|
|
5506
|
+
if (harness === "claude") {
|
|
5507
|
+
const aliases = CLAUDE_CODE_MODEL_OPTIONS.map((option) => ({
|
|
5508
|
+
...option,
|
|
5509
|
+
efforts: [...option.efforts]
|
|
5510
|
+
}));
|
|
5511
|
+
const concrete = loaded.models.filter((option) => option.provider === "anthropic").map((option) => ({
|
|
5512
|
+
...option,
|
|
5513
|
+
id: option.catalogId ?? option.id,
|
|
5514
|
+
efforts: writableEfforts(harness, option)
|
|
5515
|
+
}));
|
|
5516
|
+
return [...aliases, ...concrete];
|
|
5517
|
+
}
|
|
5518
|
+
if (harness === "codex") {
|
|
5519
|
+
return loaded.models.filter(
|
|
5520
|
+
(option) => option.provider === "openai" && isCodexOpenAiModelId(option.id)
|
|
5521
|
+
).map((option) => ({
|
|
5522
|
+
...option,
|
|
5523
|
+
efforts: writableEfforts(harness, option)
|
|
5524
|
+
}));
|
|
5525
|
+
}
|
|
5526
|
+
const catalogById = new Map(
|
|
5527
|
+
loaded.models.map((option) => [option.catalogId ?? option.id, option])
|
|
5528
|
+
);
|
|
5529
|
+
return native.map((option) => {
|
|
5530
|
+
const catalog = catalogById.get(option.catalogId ?? option.id);
|
|
5531
|
+
const enriched = catalog ? { ...catalog, id: option.id, catalogId: option.catalogId } : option;
|
|
5532
|
+
return { ...enriched, efforts: writableEfforts(harness, enriched) };
|
|
5533
|
+
});
|
|
5534
|
+
}
|
|
5535
|
+
|
|
5536
|
+
// src/cli/tui/operations.ts
|
|
5537
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
5538
|
+
var context = { cwd: process.cwd() };
|
|
5539
|
+
var codexContext = { cwd: process.cwd() };
|
|
5540
|
+
var claudeCodeContext = {
|
|
5541
|
+
cwd: process.cwd(),
|
|
5542
|
+
scope: "user"
|
|
5543
|
+
};
|
|
5544
|
+
var opencodeModelRoles = ALL_AGENT_NAMES.map(
|
|
5545
|
+
(role) => ({
|
|
5546
|
+
role,
|
|
5547
|
+
model: DEFAULT_MODELS[role] ?? "openai/gpt-5.4"
|
|
5548
|
+
})
|
|
5549
|
+
);
|
|
5550
|
+
var codexModelRoles = CODEX_ROLE_NAMES.map(
|
|
5551
|
+
(role) => ({
|
|
5552
|
+
role,
|
|
5553
|
+
model: "gpt-5.4-mini"
|
|
5554
|
+
})
|
|
5555
|
+
);
|
|
5556
|
+
var codexDefaultModels = new Map(
|
|
5557
|
+
codexModelRoles.map((role) => [role.role, role.model])
|
|
5558
|
+
);
|
|
5559
|
+
function codexInstallConfig(source, dryRun) {
|
|
5560
|
+
return {
|
|
5561
|
+
dryRun,
|
|
5562
|
+
reset: false,
|
|
5563
|
+
scope: source.scope ?? "user",
|
|
5564
|
+
projectRoot: source.cwd,
|
|
5565
|
+
homeDir: source.homeDir,
|
|
5566
|
+
codexHome: source.codexHome,
|
|
5567
|
+
packageRoot: source.packageRoot,
|
|
5568
|
+
pluginId: source.pluginId
|
|
5569
|
+
};
|
|
5570
|
+
}
|
|
5571
|
+
function getCodexModelRoles(source = codexContext) {
|
|
5572
|
+
try {
|
|
5573
|
+
const plan = buildCodexSetupPlan(codexInstallConfig(source, true));
|
|
5574
|
+
return CODEX_ROLE_NAMES.map((role) => {
|
|
5575
|
+
const item = plan.items.find(
|
|
5576
|
+
(candidate) => candidate.action === "write-role-toml" && candidate.role === role
|
|
5577
|
+
);
|
|
5578
|
+
const content = item && existsSync12(item.targetPath) ? readFileSync11(item.targetPath, "utf8") : item?.content;
|
|
5579
|
+
const effort = content ? parseRoleTomlEffort(content) : void 0;
|
|
5580
|
+
return {
|
|
5581
|
+
role,
|
|
5582
|
+
model: (content ? parseRoleTomlModel(content) : void 0) ?? codexDefaultModels.get(role) ?? "gpt-5.4-mini",
|
|
5583
|
+
effort: effort ? { kind: "effort", value: effort } : { kind: "inherit" }
|
|
5584
|
+
};
|
|
5585
|
+
});
|
|
5586
|
+
} catch {
|
|
5587
|
+
return codexModelRoles.map((role) => ({ ...role }));
|
|
5588
|
+
}
|
|
5589
|
+
}
|
|
5590
|
+
function getClaudeCodeModelRoles(source = claudeCodeContext) {
|
|
5591
|
+
try {
|
|
5592
|
+
const plan = buildClaudeCodeSetupPlan({
|
|
5593
|
+
dryRun: true,
|
|
5594
|
+
reset: false,
|
|
5595
|
+
scope: source.scope ?? "user",
|
|
5596
|
+
projectRoot: source.cwd,
|
|
5597
|
+
homeDir: source.homeDir,
|
|
5598
|
+
packageRoot: source.packageRoot
|
|
5599
|
+
});
|
|
5600
|
+
return CLAUDE_CODE_ROLE_NAMES.map((role) => {
|
|
5601
|
+
const item = plan.items.find(
|
|
5602
|
+
(candidate) => candidate.kind === "subagent" && candidate.role === role
|
|
5603
|
+
);
|
|
5604
|
+
const content = item && existsSync12(item.targetPath) ? readFileSync11(item.targetPath, "utf8") : item?.content;
|
|
5605
|
+
const model = content ? parseSubagentModel(content) : void 0;
|
|
5606
|
+
const effort = content ? parseSubagentEffort(content) : void 0;
|
|
5607
|
+
return {
|
|
5608
|
+
role,
|
|
5609
|
+
model: model ?? "inherit",
|
|
5610
|
+
effort: effort ? { kind: "effort", value: effort } : { kind: "inherit" }
|
|
5611
|
+
};
|
|
5612
|
+
});
|
|
5613
|
+
} catch {
|
|
5614
|
+
return defaultClaudeCodeModelRoles();
|
|
5615
|
+
}
|
|
5616
|
+
}
|
|
5617
|
+
function readRoleField(config, role, field) {
|
|
5618
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
5619
|
+
return void 0;
|
|
5620
|
+
}
|
|
5621
|
+
const record = config;
|
|
5622
|
+
const value = record[role];
|
|
5623
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
5624
|
+
return void 0;
|
|
5625
|
+
}
|
|
5626
|
+
const fieldValue = value[field];
|
|
5627
|
+
return typeof fieldValue === "string" && fieldValue.length > 0 ? fieldValue : void 0;
|
|
5628
|
+
}
|
|
5629
|
+
function getOpenCodeModelRoles() {
|
|
5630
|
+
const parsed = parseConfig(getExistingLiteConfigPath());
|
|
5631
|
+
const agents = parsed.config?.agents && typeof parsed.config.agents === "object" ? parsed.config.agents : void 0;
|
|
5632
|
+
const presets = parsed.config?.presets && typeof parsed.config.presets === "object" ? parsed.config.presets : {};
|
|
5633
|
+
const openaiPreset = presets.openai && typeof presets.openai === "object" ? presets.openai : void 0;
|
|
5634
|
+
return ALL_AGENT_NAMES.map((role) => {
|
|
5635
|
+
const variant = readRoleField(agents, role, "variant") ?? readRoleField(openaiPreset, role, "variant");
|
|
5636
|
+
return {
|
|
5637
|
+
role,
|
|
5638
|
+
model: readRoleField(agents, role, "model") ?? readRoleField(openaiPreset, role, "model") ?? DEFAULT_MODELS[role] ?? "openai/gpt-5.4",
|
|
5639
|
+
effort: variant ? { kind: "effort", value: variant } : { kind: "inherit" }
|
|
5640
|
+
};
|
|
5641
|
+
});
|
|
5642
|
+
}
|
|
5643
|
+
function buildTuiModelPlan(harness, roles) {
|
|
5644
|
+
if (harness === "opencode") {
|
|
5645
|
+
return buildOpenCodeModelPlan({ harness, dryRun: true, roles }, context);
|
|
5646
|
+
}
|
|
5647
|
+
if (harness === "claude") {
|
|
5648
|
+
return buildClaudeCodeModelPlan(
|
|
5649
|
+
{ harness, dryRun: true, roles },
|
|
5650
|
+
claudeCodeContext
|
|
5651
|
+
);
|
|
5652
|
+
}
|
|
5653
|
+
return buildCodexModelPlan({ harness, dryRun: true, roles }, codexContext);
|
|
5654
|
+
}
|
|
5655
|
+
var defaultTuiOperations = {
|
|
5656
|
+
status(harness) {
|
|
5657
|
+
if (harness === "opencode") return getOpenCodeStatus(context);
|
|
5658
|
+
if (harness === "claude") {
|
|
5659
|
+
return getClaudeCodeStatus(claudeCodeContext);
|
|
5660
|
+
}
|
|
5661
|
+
return getCodexStatus(codexContext);
|
|
5662
|
+
},
|
|
5663
|
+
modelRoles(harness) {
|
|
5664
|
+
if (harness === "opencode") return getOpenCodeModelRoles();
|
|
5665
|
+
if (harness === "claude") {
|
|
5666
|
+
return getClaudeCodeModelRoles(claudeCodeContext);
|
|
5667
|
+
}
|
|
5668
|
+
return getCodexModelRoles(codexContext);
|
|
5669
|
+
},
|
|
5670
|
+
modelOptions(harness) {
|
|
5671
|
+
return getModelOptions(harness);
|
|
5672
|
+
},
|
|
5673
|
+
plan(harness, action) {
|
|
5674
|
+
if (harness === "opencode") {
|
|
5675
|
+
if (action === "install") return buildOpenCodeInstallPlan(context);
|
|
5676
|
+
if (action === "update") return buildOpenCodeUpdatePlan(context);
|
|
5677
|
+
if (action === "sync") return buildOpenCodeSyncPlan(context);
|
|
5678
|
+
return buildTuiModelPlan(harness, getOpenCodeModelRoles());
|
|
5679
|
+
}
|
|
5680
|
+
if (harness === "claude") {
|
|
5681
|
+
if (action === "install") {
|
|
5682
|
+
return buildClaudeCodeInstallPlan(claudeCodeContext);
|
|
5683
|
+
}
|
|
5684
|
+
if (action === "update") {
|
|
5685
|
+
return buildClaudeCodeUpdatePlan(claudeCodeContext);
|
|
5686
|
+
}
|
|
5687
|
+
if (action === "sync") return buildClaudeCodeSyncPlan(claudeCodeContext);
|
|
5688
|
+
return buildTuiModelPlan(
|
|
5689
|
+
harness,
|
|
5690
|
+
getClaudeCodeModelRoles(claudeCodeContext)
|
|
5691
|
+
);
|
|
5692
|
+
}
|
|
5693
|
+
if (action === "install") return buildCodexInstallPlan(codexContext);
|
|
5694
|
+
if (action === "update") return buildCodexUpdatePlan(codexContext);
|
|
5695
|
+
if (action === "sync") return buildCodexSyncPlan(codexContext);
|
|
5696
|
+
return buildTuiModelPlan(harness, getCodexModelRoles(codexContext));
|
|
5697
|
+
},
|
|
5698
|
+
modelPlan(harness, roles) {
|
|
5699
|
+
return buildTuiModelPlan(harness, roles);
|
|
5700
|
+
},
|
|
5701
|
+
apply(plan) {
|
|
5702
|
+
if (plan.harness === "opencode") return applyOpenCodePlan(plan);
|
|
5703
|
+
if (plan.harness === "claude") return applyClaudeCodePlan(plan);
|
|
5704
|
+
return applyCodexPlan(plan);
|
|
5705
|
+
}
|
|
5706
|
+
};
|
|
5707
|
+
|
|
4871
5708
|
export {
|
|
4872
5709
|
claudeCodeAdapter,
|
|
4873
5710
|
codexAdapter,
|
|
4874
5711
|
CODEX_ROLE_NAMES,
|
|
4875
|
-
|
|
5712
|
+
normalizeEffortSelection,
|
|
4876
5713
|
buildCodexSetupPlan,
|
|
4877
5714
|
formatCodexSetupPlan,
|
|
4878
5715
|
applyCodexSetup,
|
|
4879
|
-
CLAUDE_CODE_ROLE_NAMES,
|
|
4880
|
-
parseSubagentModel,
|
|
4881
5716
|
buildClaudeCodeSetupPlan,
|
|
4882
5717
|
applyClaudeCodeSetup,
|
|
4883
5718
|
formatClaudeCodeSetupPlan,
|
|
4884
5719
|
RECOMMENDED_SKILLS,
|
|
4885
5720
|
installRecommendedSkill,
|
|
4886
5721
|
getClaudeCodeStatus,
|
|
4887
|
-
buildClaudeCodeInstallPlan,
|
|
4888
5722
|
buildClaudeCodeUpdatePlan,
|
|
4889
5723
|
buildClaudeCodeSyncPlan,
|
|
4890
5724
|
buildClaudeCodeModelPlan,
|
|
@@ -4893,16 +5727,20 @@ export {
|
|
|
4893
5727
|
getCodexStatus,
|
|
4894
5728
|
buildCodexUpdatePlan,
|
|
4895
5729
|
buildCodexSyncPlan,
|
|
4896
|
-
buildCodexInstallPlan,
|
|
4897
5730
|
buildCodexModelPlan,
|
|
4898
5731
|
applyCodexPlan,
|
|
4899
5732
|
getOpenCodeStatus,
|
|
4900
5733
|
buildOpenCodeUpdatePlan,
|
|
4901
5734
|
buildOpenCodeSyncPlan,
|
|
4902
|
-
buildOpenCodeInstallPlan,
|
|
4903
5735
|
buildOpenCodeModelPlan,
|
|
4904
5736
|
applyOpenCodePlan,
|
|
4905
5737
|
SUPPORTED_OPERATION_HARNESSES,
|
|
4906
5738
|
listOperationHarnesses,
|
|
4907
|
-
getOperationHarness
|
|
5739
|
+
getOperationHarness,
|
|
5740
|
+
effortChoicesForModel,
|
|
5741
|
+
getModelOptions,
|
|
5742
|
+
getCodexModelRoles,
|
|
5743
|
+
getClaudeCodeModelRoles,
|
|
5744
|
+
getOpenCodeModelRoles,
|
|
5745
|
+
defaultTuiOperations
|
|
4908
5746
|
};
|