thoth-agents 0.2.7 → 0.2.8
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-4AC2TTUO.js → chunk-75JOF37E.js} +1 -1
- package/dist/{chunk-32672NLJ.js → chunk-FM7HCZSW.js} +992 -161
- 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 +4 -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,6 +1975,26 @@ 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
|
}
|
|
@@ -1998,7 +2030,8 @@ function applyCodexManagedModelOverrides(config, overrides) {
|
|
|
1998
2030
|
const nextState = {
|
|
1999
2031
|
version: MANAGED_MODEL_STATE_VERSION,
|
|
2000
2032
|
models: { ...state.models },
|
|
2001
|
-
...state.configuredModels ? { configuredModels: { ...state.configuredModels } } : {}
|
|
2033
|
+
...state.configuredModels ? { configuredModels: { ...state.configuredModels } } : {},
|
|
2034
|
+
...state.configuredEfforts ? { configuredEfforts: { ...state.configuredEfforts } } : {}
|
|
2002
2035
|
};
|
|
2003
2036
|
try {
|
|
2004
2037
|
for (const override of overrides) {
|
|
@@ -2011,7 +2044,12 @@ function applyCodexManagedModelOverrides(config, overrides) {
|
|
|
2011
2044
|
);
|
|
2012
2045
|
}
|
|
2013
2046
|
const before = existsSync5(roleItem.targetPath) ? readFileSync5(roleItem.targetPath, "utf8") : roleItem.content;
|
|
2014
|
-
|
|
2047
|
+
let updated = replaceRoleTomlModel(before, override.model);
|
|
2048
|
+
if (override.clearEffort) {
|
|
2049
|
+
updated = replaceRoleTomlEffort(updated, void 0);
|
|
2050
|
+
} else if (override.effort !== void 0) {
|
|
2051
|
+
updated = replaceRoleTomlEffort(updated, override.effort);
|
|
2052
|
+
}
|
|
2015
2053
|
if (writeTextWithBackup(roleItem.targetPath, updated)) {
|
|
2016
2054
|
changed.push(roleItem.targetPath);
|
|
2017
2055
|
}
|
|
@@ -2019,6 +2057,14 @@ function applyCodexManagedModelOverrides(config, overrides) {
|
|
|
2019
2057
|
nextState.models[key] = parseRoleTomlModel(roleItem.content) ?? override.model;
|
|
2020
2058
|
nextState.configuredModels ??= {};
|
|
2021
2059
|
nextState.configuredModels[key] = override.model;
|
|
2060
|
+
if (override.clearEffort) {
|
|
2061
|
+
if (nextState.configuredEfforts) {
|
|
2062
|
+
delete nextState.configuredEfforts[key];
|
|
2063
|
+
}
|
|
2064
|
+
} else if (override.effort !== void 0) {
|
|
2065
|
+
nextState.configuredEfforts ??= {};
|
|
2066
|
+
nextState.configuredEfforts[key] = override.effort;
|
|
2067
|
+
}
|
|
2022
2068
|
}
|
|
2023
2069
|
if (writeTextWithBackup(statePath, stableJson3(nextState))) {
|
|
2024
2070
|
changed.push(statePath);
|
|
@@ -2042,6 +2088,11 @@ function resolveRoleTomlContent(options) {
|
|
|
2042
2088
|
const key = roleManagedModelStateKey(options.targetPath);
|
|
2043
2089
|
if (!renderedModel) return options.renderedContent;
|
|
2044
2090
|
const configuredModel = options.reset ? void 0 : options.state.configuredModels?.[key];
|
|
2091
|
+
const configuredEffort = options.reset ? void 0 : options.state.configuredEfforts?.[key];
|
|
2092
|
+
if (configuredEffort !== void 0) {
|
|
2093
|
+
options.nextState.configuredEfforts ??= {};
|
|
2094
|
+
options.nextState.configuredEfforts[key] = configuredEffort;
|
|
2095
|
+
}
|
|
2045
2096
|
if (options.reset || !existsSync5(options.targetPath)) {
|
|
2046
2097
|
options.nextState.models[key] = renderedModel;
|
|
2047
2098
|
if (configuredModel !== void 0) {
|
|
@@ -2051,16 +2102,19 @@ function resolveRoleTomlContent(options) {
|
|
|
2051
2102
|
}
|
|
2052
2103
|
return options.renderedContent;
|
|
2053
2104
|
}
|
|
2054
|
-
const
|
|
2055
|
-
|
|
2056
|
-
);
|
|
2105
|
+
const installedContent = readFileSync5(options.targetPath, "utf8");
|
|
2106
|
+
const currentModel = parseRoleTomlModel(installedContent);
|
|
2107
|
+
const currentEffort = parseRoleTomlEffort(installedContent);
|
|
2057
2108
|
if (configuredModel !== void 0) {
|
|
2058
2109
|
options.nextState.models[key] = renderedModel;
|
|
2059
2110
|
options.nextState.configuredModels ??= {};
|
|
2060
2111
|
options.nextState.configuredModels[key] = configuredModel;
|
|
2061
|
-
return
|
|
2062
|
-
|
|
2063
|
-
|
|
2112
|
+
return replaceRoleTomlEffort(
|
|
2113
|
+
replaceRoleTomlModel(
|
|
2114
|
+
options.renderedContent,
|
|
2115
|
+
currentModel && currentModel !== configuredModel ? currentModel : configuredModel
|
|
2116
|
+
),
|
|
2117
|
+
currentEffort
|
|
2064
2118
|
);
|
|
2065
2119
|
}
|
|
2066
2120
|
const trackedModel = options.state.models[key];
|
|
@@ -2068,10 +2122,13 @@ function resolveRoleTomlContent(options) {
|
|
|
2068
2122
|
if (isUserOwned) {
|
|
2069
2123
|
if (trackedModel !== void 0)
|
|
2070
2124
|
options.nextState.models[key] = trackedModel;
|
|
2071
|
-
return
|
|
2125
|
+
return replaceRoleTomlEffort(
|
|
2126
|
+
replaceRoleTomlModel(options.renderedContent, currentModel),
|
|
2127
|
+
currentEffort
|
|
2128
|
+
);
|
|
2072
2129
|
}
|
|
2073
2130
|
options.nextState.models[key] = renderedModel;
|
|
2074
|
-
return options.renderedContent;
|
|
2131
|
+
return replaceRoleTomlEffort(options.renderedContent, currentEffort);
|
|
2075
2132
|
}
|
|
2076
2133
|
function mergePersonalMarketplace(existing, homeDir, personalPluginRoot) {
|
|
2077
2134
|
const parsed = existing.trim() ? JSON.parse(existing) : {};
|
|
@@ -2445,6 +2502,7 @@ function renderClaudeCodeSubagent(input) {
|
|
|
2445
2502
|
`name: ${yamlScalar(input.name)}`,
|
|
2446
2503
|
`description: ${yamlScalar(input.description)}`,
|
|
2447
2504
|
`model: ${input.model}`,
|
|
2505
|
+
...input.effort !== void 0 ? [`effort: ${input.effort}`] : [],
|
|
2448
2506
|
...input.tools !== void 0 ? [`tools: ${yamlScalar(input.tools)}`] : [],
|
|
2449
2507
|
"---"
|
|
2450
2508
|
].join("\n");
|
|
@@ -2584,25 +2642,25 @@ function stableJson5(value) {
|
|
|
2584
2642
|
return `${JSON.stringify(value, null, 2)}
|
|
2585
2643
|
`;
|
|
2586
2644
|
}
|
|
2587
|
-
function readRootPackageVersion2(
|
|
2645
|
+
function readRootPackageVersion2(context2) {
|
|
2588
2646
|
const packageJsonPath = findRootPackageJsonPath([
|
|
2589
|
-
...hasPackageRoot(
|
|
2590
|
-
|
|
2647
|
+
...hasPackageRoot(context2) ? [context2.packageRoot] : [],
|
|
2648
|
+
context2.projectRoot,
|
|
2591
2649
|
process.cwd(),
|
|
2592
2650
|
fileURLToPath3(new URL(".", import.meta.url))
|
|
2593
2651
|
]);
|
|
2594
2652
|
return readPackageJsonVersion(packageJsonPath);
|
|
2595
2653
|
}
|
|
2596
|
-
function hasConfig(
|
|
2597
|
-
return "config" in
|
|
2654
|
+
function hasConfig(context2) {
|
|
2655
|
+
return "config" in context2;
|
|
2598
2656
|
}
|
|
2599
|
-
function hasPackageRoot(
|
|
2600
|
-
return "packageRoot" in
|
|
2657
|
+
function hasPackageRoot(context2) {
|
|
2658
|
+
return "packageRoot" in context2 && typeof context2.packageRoot === "string" && context2.packageRoot.length > 0;
|
|
2601
2659
|
}
|
|
2602
|
-
function createPluginManifest(
|
|
2660
|
+
function createPluginManifest(context2) {
|
|
2603
2661
|
return {
|
|
2604
2662
|
name: "thoth-agents",
|
|
2605
|
-
version: readRootPackageVersion2(
|
|
2663
|
+
version: readRootPackageVersion2(context2),
|
|
2606
2664
|
description: "Delegate-first agent pack with seven roles, thoth-mem persistence, and bundled SDD skills, packaged for Claude Code.",
|
|
2607
2665
|
author: { name: "thoth-agents" }
|
|
2608
2666
|
};
|
|
@@ -2650,8 +2708,8 @@ var claudeCodeAdapter = {
|
|
|
2650
2708
|
id: "claude",
|
|
2651
2709
|
displayName: "Claude Code",
|
|
2652
2710
|
capabilities: CLAUDE_CODE_CAPABILITIES,
|
|
2653
|
-
render(
|
|
2654
|
-
const config = hasConfig(
|
|
2711
|
+
render(context2) {
|
|
2712
|
+
const config = hasConfig(context2) ? context2.config : void 0;
|
|
2655
2713
|
const componentArtifacts = [
|
|
2656
2714
|
...renderSubagentArtifacts(config),
|
|
2657
2715
|
renderOrchestratorArtifact(config),
|
|
@@ -2671,13 +2729,13 @@ var claudeCodeAdapter = {
|
|
|
2671
2729
|
}
|
|
2672
2730
|
];
|
|
2673
2731
|
const skillLayout = renderClaudeCodeSkillLayout({
|
|
2674
|
-
projectRoot:
|
|
2675
|
-
...hasPackageRoot(
|
|
2732
|
+
projectRoot: context2.projectRoot,
|
|
2733
|
+
...hasPackageRoot(context2) ? { packageRoot: context2.packageRoot } : {},
|
|
2676
2734
|
skills: getSkillRegistry()
|
|
2677
2735
|
});
|
|
2678
2736
|
componentArtifacts.push(...skillLayout.artifacts);
|
|
2679
2737
|
const pluginPackage = renderClaudeCodePluginPackage({
|
|
2680
|
-
manifest: createPluginManifest(
|
|
2738
|
+
manifest: createPluginManifest(context2),
|
|
2681
2739
|
componentArtifacts
|
|
2682
2740
|
});
|
|
2683
2741
|
return {
|
|
@@ -2734,6 +2792,19 @@ function replaceSubagentModel(content, model) {
|
|
|
2734
2792
|
}
|
|
2735
2793
|
return content;
|
|
2736
2794
|
}
|
|
2795
|
+
function parseSubagentEffort(content) {
|
|
2796
|
+
return /^effort:\s*(\S+)\s*$/m.exec(content)?.[1];
|
|
2797
|
+
}
|
|
2798
|
+
function replaceSubagentEffort(content, effort) {
|
|
2799
|
+
if (effort === void 0) {
|
|
2800
|
+
return content.replace(/^effort:\s*\S+\s*\n/m, "");
|
|
2801
|
+
}
|
|
2802
|
+
if (/^effort:\s*\S+\s*$/m.test(content)) {
|
|
2803
|
+
return content.replace(/^effort:\s*\S+\s*$/m, `effort: ${effort}`);
|
|
2804
|
+
}
|
|
2805
|
+
return content.replace(/^(model:\s*\S+\s*)$/m, `$1
|
|
2806
|
+
effort: ${effort}`);
|
|
2807
|
+
}
|
|
2737
2808
|
function emptyManagedModelState3() {
|
|
2738
2809
|
return emptyManagedModelState(CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION);
|
|
2739
2810
|
}
|
|
@@ -2769,15 +2840,29 @@ function roleForArtifact(artifact) {
|
|
|
2769
2840
|
const name = match?.[1];
|
|
2770
2841
|
return name && CLAUDE_CODE_ROLE_NAMES.includes(name) ? name : void 0;
|
|
2771
2842
|
}
|
|
2772
|
-
function applyConfiguredModel(content, role, state, nextState, reset) {
|
|
2843
|
+
function applyConfiguredModel(content, targetPath, role, state, nextState, reset) {
|
|
2773
2844
|
const renderedModel = parseSubagentModel(content);
|
|
2774
2845
|
if (!role || !renderedModel) return content;
|
|
2775
2846
|
nextState.models[role] = renderedModel;
|
|
2776
2847
|
const configured = reset ? void 0 : state.configuredModels?.[role];
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2848
|
+
let updated = content;
|
|
2849
|
+
if (configured !== void 0) {
|
|
2850
|
+
nextState.configuredModels ??= {};
|
|
2851
|
+
nextState.configuredModels[role] = configured;
|
|
2852
|
+
updated = replaceSubagentModel(updated, configured);
|
|
2853
|
+
}
|
|
2854
|
+
const configuredEffort = reset ? void 0 : state.configuredEfforts?.[role];
|
|
2855
|
+
if (configuredEffort !== void 0) {
|
|
2856
|
+
nextState.configuredEfforts ??= {};
|
|
2857
|
+
nextState.configuredEfforts[role] = configuredEffort;
|
|
2858
|
+
}
|
|
2859
|
+
if (!reset && existsSync6(targetPath)) {
|
|
2860
|
+
const installedEffort = parseSubagentEffort(
|
|
2861
|
+
readFileSync7(targetPath, "utf8")
|
|
2862
|
+
);
|
|
2863
|
+
updated = replaceSubagentEffort(updated, installedEffort);
|
|
2864
|
+
}
|
|
2865
|
+
return updated;
|
|
2781
2866
|
}
|
|
2782
2867
|
function buildClaudeCodeSetupPlan(config) {
|
|
2783
2868
|
const targets = resolveClaudeCodeTargets({
|
|
@@ -2795,8 +2880,15 @@ function buildClaudeCodeSetupPlan(config) {
|
|
|
2795
2880
|
const items = render.artifacts.map((artifact) => {
|
|
2796
2881
|
const role = roleForArtifact(artifact);
|
|
2797
2882
|
const rendered = String(artifact.content ?? "");
|
|
2798
|
-
const content = role !== void 0 ? applyConfiguredModel(rendered, role, state, nextState, config.reset) : rendered;
|
|
2799
2883
|
const targetPath = join8(targets.pluginRoot, artifact.path);
|
|
2884
|
+
const content = role !== void 0 ? applyConfiguredModel(
|
|
2885
|
+
rendered,
|
|
2886
|
+
targetPath,
|
|
2887
|
+
role,
|
|
2888
|
+
state,
|
|
2889
|
+
nextState,
|
|
2890
|
+
config.reset
|
|
2891
|
+
) : rendered;
|
|
2800
2892
|
return {
|
|
2801
2893
|
kind: targetKindForArtifact(artifact),
|
|
2802
2894
|
action: "write-plugin-file",
|
|
@@ -2892,11 +2984,12 @@ function applyClaudeCodeManagedModelOverrides(config, overrides) {
|
|
|
2892
2984
|
const nextState = {
|
|
2893
2985
|
version: CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION,
|
|
2894
2986
|
models: { ...state.models },
|
|
2895
|
-
...state.configuredModels ? { configuredModels: { ...state.configuredModels } } : {}
|
|
2987
|
+
...state.configuredModels ? { configuredModels: { ...state.configuredModels } } : {},
|
|
2988
|
+
...state.configuredEfforts ? { configuredEfforts: { ...state.configuredEfforts } } : {}
|
|
2896
2989
|
};
|
|
2897
2990
|
try {
|
|
2898
2991
|
for (const override of overrides) {
|
|
2899
|
-
if (!isClaudeCodeModelAlias(override.model)) {
|
|
2992
|
+
if (!isClaudeCodeModelAlias(override.model) && override.catalogId !== override.model) {
|
|
2900
2993
|
throw new Error(
|
|
2901
2994
|
`Unsupported Claude Code model "${override.model}" for ${override.role}; use sonnet, opus, haiku, or inherit.`
|
|
2902
2995
|
);
|
|
@@ -2908,12 +3001,25 @@ function applyClaudeCodeManagedModelOverrides(config, overrides) {
|
|
|
2908
3001
|
throw new Error(`Missing Claude Code subagent for ${override.role}.`);
|
|
2909
3002
|
}
|
|
2910
3003
|
const before = existsSync6(roleItem.targetPath) ? readFileSync7(roleItem.targetPath, "utf8") : roleItem.content;
|
|
2911
|
-
|
|
3004
|
+
let updated = replaceSubagentModel(before, override.model);
|
|
3005
|
+
if (override.clearEffort) {
|
|
3006
|
+
updated = replaceSubagentEffort(updated, void 0);
|
|
3007
|
+
} else if (override.effort !== void 0) {
|
|
3008
|
+
updated = replaceSubagentEffort(updated, override.effort);
|
|
3009
|
+
}
|
|
2912
3010
|
if (writeTextWithBackup(roleItem.targetPath, updated)) {
|
|
2913
3011
|
changed.push(roleItem.targetPath);
|
|
2914
3012
|
}
|
|
2915
3013
|
nextState.configuredModels ??= {};
|
|
2916
3014
|
nextState.configuredModels[override.role] = override.model;
|
|
3015
|
+
if (override.clearEffort) {
|
|
3016
|
+
if (nextState.configuredEfforts) {
|
|
3017
|
+
delete nextState.configuredEfforts[override.role];
|
|
3018
|
+
}
|
|
3019
|
+
} else if (override.effort !== void 0) {
|
|
3020
|
+
nextState.configuredEfforts ??= {};
|
|
3021
|
+
nextState.configuredEfforts[override.role] = override.effort;
|
|
3022
|
+
}
|
|
2917
3023
|
}
|
|
2918
3024
|
if (writeTextWithBackup(statePath, stableJson3(nextState))) {
|
|
2919
3025
|
changed.push(statePath);
|
|
@@ -2939,6 +3045,31 @@ function formatClaudeCodeSetupPlan(plan) {
|
|
|
2939
3045
|
var CLAUDE_CODE_DISPLAY_NAME = "Claude Code";
|
|
2940
3046
|
var claudeCodePlanSources = /* @__PURE__ */ new WeakMap();
|
|
2941
3047
|
var claudeCodeModelSources = /* @__PURE__ */ new WeakMap();
|
|
3048
|
+
var CLAUDE_CODE_EFFORTS = /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh", "max"]);
|
|
3049
|
+
function resolveClaudeCodeEffort(input) {
|
|
3050
|
+
if (!input.effort || input.effort.kind === "inherit") {
|
|
3051
|
+
return { ok: true, effort: void 0 };
|
|
3052
|
+
}
|
|
3053
|
+
const effort = input.effort.value;
|
|
3054
|
+
if (!CLAUDE_CODE_EFFORTS.has(effort)) {
|
|
3055
|
+
return {
|
|
3056
|
+
ok: false,
|
|
3057
|
+
code: "claude-code-effort-runtime-unsupported",
|
|
3058
|
+
message: `Claude Code does not support effort ${effort}.`
|
|
3059
|
+
};
|
|
3060
|
+
}
|
|
3061
|
+
if (isClaudeCodeModelAlias(input.model) && input.model !== "inherit") {
|
|
3062
|
+
return { ok: true, effort };
|
|
3063
|
+
}
|
|
3064
|
+
if (input.catalogId === input.model && input.availableEfforts?.includes(effort)) {
|
|
3065
|
+
return { ok: true, effort };
|
|
3066
|
+
}
|
|
3067
|
+
return {
|
|
3068
|
+
ok: false,
|
|
3069
|
+
code: "claude-code-effort-catalog-unsupported",
|
|
3070
|
+
message: `Effort ${effort} is not supported by ${input.catalogId ?? input.model} in the models.dev catalog.`
|
|
3071
|
+
};
|
|
3072
|
+
}
|
|
2942
3073
|
var claudeCodeActions = [
|
|
2943
3074
|
{
|
|
2944
3075
|
id: "claude-code-status",
|
|
@@ -3002,16 +3133,16 @@ var claudeCodeOperationAdapter = {
|
|
|
3002
3133
|
description: "Claude Code plugin package and managed subagent surfaces.",
|
|
3003
3134
|
actions: claudeCodeActions
|
|
3004
3135
|
};
|
|
3005
|
-
function claudeCodeConfig(
|
|
3136
|
+
function claudeCodeConfig(context2 = { cwd: process.cwd() }, dryRun) {
|
|
3006
3137
|
return {
|
|
3007
3138
|
dryRun,
|
|
3008
3139
|
reset: false,
|
|
3009
3140
|
// Match the installer (install.ts uses 'user') so post-install status,
|
|
3010
3141
|
// update, sync, and model commands resolve the same plugin root.
|
|
3011
|
-
scope:
|
|
3012
|
-
projectRoot:
|
|
3013
|
-
homeDir:
|
|
3014
|
-
packageRoot:
|
|
3142
|
+
scope: context2.scope ?? "user",
|
|
3143
|
+
projectRoot: context2.cwd,
|
|
3144
|
+
homeDir: context2.homeDir,
|
|
3145
|
+
packageRoot: context2.packageRoot
|
|
3015
3146
|
};
|
|
3016
3147
|
}
|
|
3017
3148
|
function claudeCodeDisclaimers() {
|
|
@@ -3131,10 +3262,10 @@ function statusFromSetupPlan(plan) {
|
|
|
3131
3262
|
]
|
|
3132
3263
|
};
|
|
3133
3264
|
}
|
|
3134
|
-
function getClaudeCodeStatus(
|
|
3265
|
+
function getClaudeCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
3135
3266
|
let plan;
|
|
3136
3267
|
try {
|
|
3137
|
-
plan = buildClaudeCodeSetupPlan(claudeCodeConfig(
|
|
3268
|
+
plan = buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true));
|
|
3138
3269
|
} catch (error) {
|
|
3139
3270
|
const message = error instanceof Error ? error.message : String(error);
|
|
3140
3271
|
return {
|
|
@@ -3192,53 +3323,71 @@ function planFromSetup(id, action, title, summary, setupPlan) {
|
|
|
3192
3323
|
claudeCodePlanSources.set(plan, setupPlan);
|
|
3193
3324
|
return plan;
|
|
3194
3325
|
}
|
|
3195
|
-
function buildClaudeCodeInstallPlan(
|
|
3326
|
+
function buildClaudeCodeInstallPlan(context2 = { cwd: process.cwd() }) {
|
|
3196
3327
|
return planFromSetup(
|
|
3197
3328
|
"claude-code-install-preview",
|
|
3198
3329
|
"install",
|
|
3199
3330
|
"Install Claude Code plugin package",
|
|
3200
3331
|
"Preview Claude Code plugin package install using buildClaudeCodeSetupPlan().",
|
|
3201
|
-
buildClaudeCodeSetupPlan(claudeCodeConfig(
|
|
3332
|
+
buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true))
|
|
3202
3333
|
);
|
|
3203
3334
|
}
|
|
3204
|
-
function buildClaudeCodeUpdatePlan(
|
|
3335
|
+
function buildClaudeCodeUpdatePlan(context2 = { cwd: process.cwd() }) {
|
|
3205
3336
|
return planFromSetup(
|
|
3206
3337
|
"claude-code-update-preview",
|
|
3207
3338
|
"update",
|
|
3208
3339
|
"Update Claude Code plugin package",
|
|
3209
3340
|
"Preview Claude Code managed plugin refresh using buildClaudeCodeSetupPlan().",
|
|
3210
|
-
buildClaudeCodeSetupPlan(claudeCodeConfig(
|
|
3341
|
+
buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true))
|
|
3211
3342
|
);
|
|
3212
3343
|
}
|
|
3213
|
-
function buildClaudeCodeSyncPlan(
|
|
3344
|
+
function buildClaudeCodeSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
3214
3345
|
return planFromSetup(
|
|
3215
3346
|
"claude-code-sync-preview",
|
|
3216
3347
|
"sync",
|
|
3217
3348
|
"Sync Claude Code plugin package",
|
|
3218
3349
|
"Preview Claude Code managed plugin subagents, MCP, hooks, and skills.",
|
|
3219
|
-
buildClaudeCodeSetupPlan(claudeCodeConfig(
|
|
3350
|
+
buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true))
|
|
3220
3351
|
);
|
|
3221
3352
|
}
|
|
3222
3353
|
function isClaudeCodeRole(role) {
|
|
3223
3354
|
return CLAUDE_CODE_ROLE_NAMES.includes(role);
|
|
3224
3355
|
}
|
|
3225
|
-
function buildClaudeCodeModelPlan(input,
|
|
3226
|
-
const status = getClaudeCodeStatus(
|
|
3227
|
-
const
|
|
3356
|
+
function buildClaudeCodeModelPlan(input, context2 = { cwd: process.cwd() }) {
|
|
3357
|
+
const status = getClaudeCodeStatus(context2);
|
|
3358
|
+
const resolvedRoles = input.roles.map((role) => ({
|
|
3359
|
+
role,
|
|
3360
|
+
effort: resolveClaudeCodeEffort(role),
|
|
3361
|
+
validRole: isClaudeCodeRole(role.role),
|
|
3362
|
+
validModel: isClaudeCodeModelAlias(role.model) || role.catalogId === role.model
|
|
3363
|
+
}));
|
|
3364
|
+
const supportedRoles = resolvedRoles.filter((entry) => entry.validRole && entry.validModel && entry.effort.ok).map(({ role, effort }) => ({
|
|
3228
3365
|
role: role.role,
|
|
3229
|
-
model: role.model
|
|
3366
|
+
model: role.model,
|
|
3367
|
+
...role.catalogId ? { catalogId: role.catalogId } : {},
|
|
3368
|
+
...effort.ok && effort.effort !== void 0 ? { effort: effort.effort } : {},
|
|
3369
|
+
...role.effort?.kind === "inherit" ? { clearEffort: true } : {}
|
|
3230
3370
|
}));
|
|
3231
|
-
const rejectedRoles =
|
|
3232
|
-
(
|
|
3371
|
+
const rejectedRoles = resolvedRoles.filter(
|
|
3372
|
+
(entry) => !entry.validRole || !entry.validModel
|
|
3373
|
+
);
|
|
3374
|
+
const effortErrors = resolvedRoles.filter(
|
|
3375
|
+
(entry) => entry.validRole && entry.validModel && !entry.effort.ok
|
|
3233
3376
|
);
|
|
3234
3377
|
const warnings = [
|
|
3235
3378
|
...status.diagnostics,
|
|
3236
3379
|
...input.warnings ?? [],
|
|
3237
3380
|
...rejectedRoles.map(
|
|
3238
|
-
(role) => warning(
|
|
3381
|
+
({ role }) => warning(
|
|
3239
3382
|
`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
3383
|
"claude-code-unsupported-model-role"
|
|
3241
3384
|
)
|
|
3385
|
+
),
|
|
3386
|
+
...effortErrors.map(
|
|
3387
|
+
({ effort }) => warning(
|
|
3388
|
+
effort.ok ? "Unknown Claude Code effort error." : effort.message,
|
|
3389
|
+
effort.ok ? "claude-code-effort-unknown" : effort.code
|
|
3390
|
+
)
|
|
3242
3391
|
)
|
|
3243
3392
|
];
|
|
3244
3393
|
if (input.harness !== "claude") {
|
|
@@ -3269,7 +3418,7 @@ function buildClaudeCodeModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3269
3418
|
title: "Configure Claude Code subagent model lines",
|
|
3270
3419
|
summary: "Preview model changes for generated Claude Code subagent files and managed model state only.",
|
|
3271
3420
|
dryRun: true,
|
|
3272
|
-
canApply: input.harness === "claude" && supportedRoles.length > 0 && status.state !== "unknown",
|
|
3421
|
+
canApply: input.harness === "claude" && supportedRoles.length > 0 && effortErrors.length === 0 && status.state !== "unknown",
|
|
3273
3422
|
targets: [...targets, ...stateTarget ? [stateTarget] : []],
|
|
3274
3423
|
surfaces: targets.map((target) => ({
|
|
3275
3424
|
id: `claude-code-model:${target.label}`,
|
|
@@ -3282,7 +3431,7 @@ function buildClaudeCodeModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3282
3431
|
strategy: "managed-backup-file",
|
|
3283
3432
|
description: "Existing subagent files and managed model state are backed up by the managed write helper."
|
|
3284
3433
|
},
|
|
3285
|
-
items: supportedRoles.map(({ role, model }) => ({
|
|
3434
|
+
items: supportedRoles.map(({ role, model, effort }) => ({
|
|
3286
3435
|
title: `Set ${role} Claude Code subagent model line`,
|
|
3287
3436
|
target: targets.find(
|
|
3288
3437
|
(target) => target.path?.endsWith(`agents${pathSep()}${role}.md`)
|
|
@@ -3290,7 +3439,7 @@ function buildClaudeCodeModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3290
3439
|
kind: "generated-artifact",
|
|
3291
3440
|
label: `Claude Code ${role} subagent`
|
|
3292
3441
|
},
|
|
3293
|
-
preview: JSON.stringify({ role, model }),
|
|
3442
|
+
preview: JSON.stringify({ role, model, effort: effort ?? null }),
|
|
3294
3443
|
backup: { required: true, strategy: "managed-backup-file" }
|
|
3295
3444
|
})),
|
|
3296
3445
|
warnings,
|
|
@@ -3304,7 +3453,7 @@ function buildClaudeCodeModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3304
3453
|
]
|
|
3305
3454
|
};
|
|
3306
3455
|
claudeCodeModelSources.set(plan, {
|
|
3307
|
-
config: claudeCodeConfig(
|
|
3456
|
+
config: claudeCodeConfig(context2, false),
|
|
3308
3457
|
roles: supportedRoles
|
|
3309
3458
|
});
|
|
3310
3459
|
return plan;
|
|
@@ -3360,7 +3509,10 @@ function applyClaudeCodePlan(plan) {
|
|
|
3360
3509
|
source.config,
|
|
3361
3510
|
source.roles.map((role) => ({
|
|
3362
3511
|
role: role.role,
|
|
3363
|
-
model: role.model
|
|
3512
|
+
model: role.model,
|
|
3513
|
+
...role.catalogId ? { catalogId: role.catalogId } : {},
|
|
3514
|
+
...role.effort ? { effort: role.effort } : {},
|
|
3515
|
+
...role.clearEffort ? { clearEffort: true } : {}
|
|
3364
3516
|
}))
|
|
3365
3517
|
);
|
|
3366
3518
|
return {
|
|
@@ -3479,16 +3631,16 @@ var codexOperationAdapter = {
|
|
|
3479
3631
|
description: "Codex agent-pack and managed subagent surfaces.",
|
|
3480
3632
|
actions: codexActions
|
|
3481
3633
|
};
|
|
3482
|
-
function codexConfig(
|
|
3634
|
+
function codexConfig(context2 = { cwd: process.cwd() }, dryRun) {
|
|
3483
3635
|
return {
|
|
3484
3636
|
dryRun,
|
|
3485
3637
|
reset: false,
|
|
3486
|
-
scope:
|
|
3487
|
-
projectRoot:
|
|
3488
|
-
homeDir:
|
|
3489
|
-
codexHome:
|
|
3490
|
-
packageRoot:
|
|
3491
|
-
pluginId:
|
|
3638
|
+
scope: context2.scope ?? "user",
|
|
3639
|
+
projectRoot: context2.cwd,
|
|
3640
|
+
homeDir: context2.homeDir,
|
|
3641
|
+
codexHome: context2.codexHome,
|
|
3642
|
+
packageRoot: context2.packageRoot,
|
|
3643
|
+
pluginId: context2.pluginId
|
|
3492
3644
|
};
|
|
3493
3645
|
}
|
|
3494
3646
|
function codexDisclaimers() {
|
|
@@ -3654,10 +3806,10 @@ function statusSummary2(state) {
|
|
|
3654
3806
|
return "Codex managed setup could not be classified safely.";
|
|
3655
3807
|
}
|
|
3656
3808
|
}
|
|
3657
|
-
function getCodexStatus(
|
|
3809
|
+
function getCodexStatus(context2 = { cwd: process.cwd() }) {
|
|
3658
3810
|
let plan;
|
|
3659
3811
|
try {
|
|
3660
|
-
plan = buildCodexSetupPlan(codexConfig(
|
|
3812
|
+
plan = buildCodexSetupPlan(codexConfig(context2, true));
|
|
3661
3813
|
} catch (error) {
|
|
3662
3814
|
const message = error instanceof Error ? error.message : String(error);
|
|
3663
3815
|
return {
|
|
@@ -3708,8 +3860,8 @@ function planItemFromSetup2(item) {
|
|
|
3708
3860
|
backup: backupForItem2(item)
|
|
3709
3861
|
};
|
|
3710
3862
|
}
|
|
3711
|
-
function planFromSetup2(id, action, title, summary, setupPlan,
|
|
3712
|
-
const status = getCodexStatus(
|
|
3863
|
+
function planFromSetup2(id, action, title, summary, setupPlan, context2) {
|
|
3864
|
+
const status = getCodexStatus(context2);
|
|
3713
3865
|
const canApply = status.state === "installed" || status.state === "missing" || status.state === "outdated";
|
|
3714
3866
|
const plan = {
|
|
3715
3867
|
id,
|
|
@@ -3744,37 +3896,37 @@ function planFromSetup2(id, action, title, summary, setupPlan, context) {
|
|
|
3744
3896
|
codexPlanSources.set(plan, setupPlan);
|
|
3745
3897
|
return plan;
|
|
3746
3898
|
}
|
|
3747
|
-
function buildCodexUpdatePlan(
|
|
3748
|
-
const setupPlan = buildCodexSetupPlan(codexConfig(
|
|
3899
|
+
function buildCodexUpdatePlan(context2 = { cwd: process.cwd() }) {
|
|
3900
|
+
const setupPlan = buildCodexSetupPlan(codexConfig(context2, true));
|
|
3749
3901
|
return planFromSetup2(
|
|
3750
3902
|
"codex-update-preview",
|
|
3751
3903
|
"update",
|
|
3752
3904
|
"Update Codex managed setup",
|
|
3753
3905
|
"Preview Codex managed setup refresh using buildCodexSetupPlan().",
|
|
3754
3906
|
setupPlan,
|
|
3755
|
-
|
|
3907
|
+
context2
|
|
3756
3908
|
);
|
|
3757
3909
|
}
|
|
3758
|
-
function buildCodexSyncPlan(
|
|
3759
|
-
const setupPlan = buildCodexSetupPlan(codexConfig(
|
|
3910
|
+
function buildCodexSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
3911
|
+
const setupPlan = buildCodexSetupPlan(codexConfig(context2, true));
|
|
3760
3912
|
return planFromSetup2(
|
|
3761
3913
|
"codex-sync-preview",
|
|
3762
3914
|
"sync",
|
|
3763
3915
|
"Sync Codex managed configuration",
|
|
3764
3916
|
"Preview Codex managed root instructions, subagents, plugin source, marketplace entry, and feature gates.",
|
|
3765
3917
|
setupPlan,
|
|
3766
|
-
|
|
3918
|
+
context2
|
|
3767
3919
|
);
|
|
3768
3920
|
}
|
|
3769
|
-
function buildCodexInstallPlan(
|
|
3770
|
-
const setupPlan = buildCodexSetupPlan(codexConfig(
|
|
3921
|
+
function buildCodexInstallPlan(context2 = { cwd: process.cwd() }) {
|
|
3922
|
+
const setupPlan = buildCodexSetupPlan(codexConfig(context2, true));
|
|
3771
3923
|
return planFromSetup2(
|
|
3772
3924
|
"codex-install-preview",
|
|
3773
3925
|
"install",
|
|
3774
3926
|
"Install Codex managed setup",
|
|
3775
3927
|
"Preview Codex managed agent-pack setup using buildCodexSetupPlan().",
|
|
3776
3928
|
setupPlan,
|
|
3777
|
-
|
|
3929
|
+
context2
|
|
3778
3930
|
);
|
|
3779
3931
|
}
|
|
3780
3932
|
function normalizeCodexModel(input) {
|
|
@@ -3783,15 +3935,55 @@ function normalizeCodexModel(input) {
|
|
|
3783
3935
|
}
|
|
3784
3936
|
return input.model;
|
|
3785
3937
|
}
|
|
3938
|
+
var CODEX_DOCUMENTED_EFFORTS = /* @__PURE__ */ new Set([
|
|
3939
|
+
"none",
|
|
3940
|
+
"minimal",
|
|
3941
|
+
"low",
|
|
3942
|
+
"medium",
|
|
3943
|
+
"high",
|
|
3944
|
+
"xhigh",
|
|
3945
|
+
"max",
|
|
3946
|
+
"ultra"
|
|
3947
|
+
]);
|
|
3948
|
+
function resolveCodexEffort(input) {
|
|
3949
|
+
if (!input.effort || input.effort.kind === "inherit") {
|
|
3950
|
+
return { ok: true, value: void 0 };
|
|
3951
|
+
}
|
|
3952
|
+
const value = input.effort.value;
|
|
3953
|
+
if (!CODEX_DOCUMENTED_EFFORTS.has(value)) {
|
|
3954
|
+
return {
|
|
3955
|
+
ok: false,
|
|
3956
|
+
code: "codex-effort-undocumented",
|
|
3957
|
+
message: `Codex does not document reasoning effort "${value}".`
|
|
3958
|
+
};
|
|
3959
|
+
}
|
|
3960
|
+
if (!input.catalogId?.startsWith("openai/") || !input.availableEfforts?.includes(value)) {
|
|
3961
|
+
return {
|
|
3962
|
+
ok: false,
|
|
3963
|
+
code: "codex-effort-model-unsupported",
|
|
3964
|
+
message: `Model ${input.catalogId ?? input.model} does not publish Codex effort "${value}".`
|
|
3965
|
+
};
|
|
3966
|
+
}
|
|
3967
|
+
return { ok: true, value };
|
|
3968
|
+
}
|
|
3786
3969
|
function isCodexRole(role) {
|
|
3787
3970
|
return CODEX_ROLE_NAMES.includes(role);
|
|
3788
3971
|
}
|
|
3789
|
-
function buildCodexModelPlan(input,
|
|
3790
|
-
const status = getCodexStatus(
|
|
3791
|
-
const
|
|
3972
|
+
function buildCodexModelPlan(input, context2 = { cwd: process.cwd() }) {
|
|
3973
|
+
const status = getCodexStatus(context2);
|
|
3974
|
+
const candidateRoles = input.roles.filter((role) => isCodexRole(role.role)).map((role) => ({
|
|
3792
3975
|
role: role.role,
|
|
3793
|
-
model: normalizeCodexModel(role)
|
|
3976
|
+
model: normalizeCodexModel(role),
|
|
3977
|
+
effort: resolveCodexEffort(role),
|
|
3978
|
+
clearEffort: role.effort?.kind === "inherit"
|
|
3794
3979
|
}));
|
|
3980
|
+
const supportedRoles = candidateRoles.filter((role) => role.effort.ok).map((role) => ({
|
|
3981
|
+
role: role.role,
|
|
3982
|
+
model: role.model,
|
|
3983
|
+
...role.effort.value ? { effort: role.effort.value } : {},
|
|
3984
|
+
...role.clearEffort ? { clearEffort: true } : {}
|
|
3985
|
+
}));
|
|
3986
|
+
const effortErrors = candidateRoles.filter((role) => !role.effort.ok);
|
|
3795
3987
|
const unsupportedRoles = input.roles.filter(
|
|
3796
3988
|
(role) => !isCodexRole(role.role)
|
|
3797
3989
|
);
|
|
@@ -3803,6 +3995,12 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3803
3995
|
`Codex does not expose a supported generated model surface for ${role.role}; this plan will not write that role.`,
|
|
3804
3996
|
"codex-unsupported-model-role"
|
|
3805
3997
|
)
|
|
3998
|
+
),
|
|
3999
|
+
...effortErrors.map(
|
|
4000
|
+
(role) => warning2(
|
|
4001
|
+
role.effort.message,
|
|
4002
|
+
role.effort.code
|
|
4003
|
+
)
|
|
3806
4004
|
)
|
|
3807
4005
|
];
|
|
3808
4006
|
if (input.harness !== "codex") {
|
|
@@ -3823,7 +4021,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3823
4021
|
state: "missing"
|
|
3824
4022
|
};
|
|
3825
4023
|
});
|
|
3826
|
-
const config = codexConfig(
|
|
4024
|
+
const config = codexConfig(context2, false);
|
|
3827
4025
|
const stateTarget = status.targets.find(
|
|
3828
4026
|
(target) => target.path?.endsWith(".thoth-agents-managed-models.json")
|
|
3829
4027
|
);
|
|
@@ -3834,7 +4032,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3834
4032
|
title: "Configure Codex subagent model lines",
|
|
3835
4033
|
summary: "Preview model changes for generated Codex subagent TOML files and .thoth-agents-managed-models.json only.",
|
|
3836
4034
|
dryRun: true,
|
|
3837
|
-
canApply: input.harness === "codex" && supportedRoles.length > 0 && status.state !== "unknown",
|
|
4035
|
+
canApply: input.harness === "codex" && supportedRoles.length > 0 && effortErrors.length === 0 && status.state !== "unknown",
|
|
3838
4036
|
targets: [...targets, ...stateTarget ? [stateTarget] : []],
|
|
3839
4037
|
surfaces: targets.map((target) => ({
|
|
3840
4038
|
id: `codex-model:${target.label}`,
|
|
@@ -3847,7 +4045,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3847
4045
|
strategy: "managed-backup-file",
|
|
3848
4046
|
description: "Existing subagent TOML and managed model state files are backed up by the managed write helper."
|
|
3849
4047
|
},
|
|
3850
|
-
items: supportedRoles.map(({ role, model }) => ({
|
|
4048
|
+
items: supportedRoles.map(({ role, model, effort }) => ({
|
|
3851
4049
|
title: `Set ${role} Codex subagent model line`,
|
|
3852
4050
|
target: targets.find(
|
|
3853
4051
|
(target) => target.path?.endsWith(`thoth-agents-${role}.toml`)
|
|
@@ -3855,7 +4053,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
3855
4053
|
kind: "generated-artifact",
|
|
3856
4054
|
label: `Codex ${role} subagent`
|
|
3857
4055
|
},
|
|
3858
|
-
preview: JSON.stringify({ role, model }),
|
|
4056
|
+
preview: JSON.stringify({ role, model, effort }),
|
|
3859
4057
|
backup: {
|
|
3860
4058
|
required: true,
|
|
3861
4059
|
strategy: "managed-backup-file"
|
|
@@ -3971,6 +4169,36 @@ function applyCodexPlan(plan) {
|
|
|
3971
4169
|
import { existsSync as existsSync10 } from "fs";
|
|
3972
4170
|
import { join as join10 } from "path";
|
|
3973
4171
|
|
|
4172
|
+
// src/cli/opencode-effort.ts
|
|
4173
|
+
var OPENAI_GPT_RUNTIME_VARIANTS = /* @__PURE__ */ new Set([
|
|
4174
|
+
"none",
|
|
4175
|
+
"low",
|
|
4176
|
+
"medium",
|
|
4177
|
+
"high",
|
|
4178
|
+
"xhigh"
|
|
4179
|
+
]);
|
|
4180
|
+
function resolveOpenCodeEffort(input) {
|
|
4181
|
+
if (!input.effort || input.effort.kind === "inherit") {
|
|
4182
|
+
return { ok: true, variant: void 0 };
|
|
4183
|
+
}
|
|
4184
|
+
const variant = input.effort.value;
|
|
4185
|
+
if (!input.availableEfforts?.includes(variant)) {
|
|
4186
|
+
return {
|
|
4187
|
+
ok: false,
|
|
4188
|
+
code: "opencode-effort-catalog-unsupported",
|
|
4189
|
+
message: `Effort ${variant} is not supported by ${input.catalogId ?? input.model} in the models.dev catalog.`
|
|
4190
|
+
};
|
|
4191
|
+
}
|
|
4192
|
+
if (input.catalogId?.startsWith("openai/gpt-") && OPENAI_GPT_RUNTIME_VARIANTS.has(variant)) {
|
|
4193
|
+
return { ok: true, variant };
|
|
4194
|
+
}
|
|
4195
|
+
return {
|
|
4196
|
+
ok: false,
|
|
4197
|
+
code: "opencode-effort-runtime-unconfirmed",
|
|
4198
|
+
message: `Effort ${variant} is catalogued for ${input.catalogId ?? input.model}, but the OpenCode runtime mapping is not confirmed.`
|
|
4199
|
+
};
|
|
4200
|
+
}
|
|
4201
|
+
|
|
3974
4202
|
// src/cli/skills.ts
|
|
3975
4203
|
import { spawnSync } from "child_process";
|
|
3976
4204
|
import { existsSync as existsSync9 } from "fs";
|
|
@@ -4141,11 +4369,11 @@ function titleCaseSkillName(value) {
|
|
|
4141
4369
|
return `${part.charAt(0).toUpperCase()}${part.slice(1).toLowerCase()}`;
|
|
4142
4370
|
}).join("-");
|
|
4143
4371
|
}
|
|
4144
|
-
function homeDirFromContext(
|
|
4145
|
-
return
|
|
4372
|
+
function homeDirFromContext(context2) {
|
|
4373
|
+
return context2.env?.HOME ?? context2.env?.USERPROFILE;
|
|
4146
4374
|
}
|
|
4147
|
-
function openCodeSkillTargets(
|
|
4148
|
-
const homeDir = homeDirFromContext(
|
|
4375
|
+
function openCodeSkillTargets(context2) {
|
|
4376
|
+
const homeDir = homeDirFromContext(context2);
|
|
4149
4377
|
const recommendedTargets = RECOMMENDED_SKILLS.map(
|
|
4150
4378
|
(skill) => {
|
|
4151
4379
|
const path4 = getRecommendedSkillPath(skill, homeDir);
|
|
@@ -4226,13 +4454,13 @@ function hasSevenAgentPreset(config) {
|
|
|
4226
4454
|
function classifyApplySafety(state) {
|
|
4227
4455
|
return state === "missing" || state === "installed";
|
|
4228
4456
|
}
|
|
4229
|
-
function getOpenCodeStatus(
|
|
4457
|
+
function getOpenCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
4230
4458
|
const mainPath = getExistingConfigPath();
|
|
4231
4459
|
const litePath = getExistingLiteConfigPath();
|
|
4232
4460
|
const main = parseConfig(mainPath);
|
|
4233
4461
|
const lite = parseConfig(litePath);
|
|
4234
4462
|
const diagnostics = [];
|
|
4235
|
-
const skillStatus = openCodeSkillTargets(
|
|
4463
|
+
const skillStatus = openCodeSkillTargets(context2);
|
|
4236
4464
|
if (main.error) {
|
|
4237
4465
|
diagnostics.push({
|
|
4238
4466
|
severity: "critical",
|
|
@@ -4554,11 +4782,26 @@ function normalizeRoleModel(input) {
|
|
|
4554
4782
|
function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
4555
4783
|
const items = input.roles.map((role) => {
|
|
4556
4784
|
const model = normalizeRoleModel(role);
|
|
4785
|
+
const effort = resolveOpenCodeEffort(role);
|
|
4557
4786
|
return {
|
|
4558
4787
|
title: `Set ${role.role} OpenCode model override`,
|
|
4559
4788
|
target: targetForLiteConfig(),
|
|
4560
|
-
preview: JSON.stringify({
|
|
4561
|
-
|
|
4789
|
+
preview: JSON.stringify({
|
|
4790
|
+
[role.role]: {
|
|
4791
|
+
model,
|
|
4792
|
+
variant: effort.ok ? effort.variant ?? null : null
|
|
4793
|
+
}
|
|
4794
|
+
}),
|
|
4795
|
+
backup: defaultBackup(getExistingLiteConfigPath()),
|
|
4796
|
+
...!effort.ok ? {
|
|
4797
|
+
warnings: [
|
|
4798
|
+
{
|
|
4799
|
+
severity: "critical",
|
|
4800
|
+
code: effort.code,
|
|
4801
|
+
message: effort.message
|
|
4802
|
+
}
|
|
4803
|
+
]
|
|
4804
|
+
} : {}
|
|
4562
4805
|
};
|
|
4563
4806
|
});
|
|
4564
4807
|
const plan = planFromItems(
|
|
@@ -4568,6 +4811,11 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
|
4568
4811
|
"Preview thoth-agents role model overrides in the plugin config agents map.",
|
|
4569
4812
|
items
|
|
4570
4813
|
);
|
|
4814
|
+
const effortWarnings = items.flatMap((item) => item.warnings ?? []);
|
|
4815
|
+
if (effortWarnings.length > 0) {
|
|
4816
|
+
plan.canApply = false;
|
|
4817
|
+
plan.warnings.push(...effortWarnings);
|
|
4818
|
+
}
|
|
4571
4819
|
if (input.harness !== "opencode") {
|
|
4572
4820
|
return {
|
|
4573
4821
|
...plan,
|
|
@@ -4693,13 +4941,14 @@ function applyModelPlan(plan) {
|
|
|
4693
4941
|
}
|
|
4694
4942
|
const role = match[1] ?? "";
|
|
4695
4943
|
const model = parsed2[role]?.model;
|
|
4696
|
-
|
|
4944
|
+
const variant = parsed2[role]?.variant;
|
|
4945
|
+
if (!ROLE_NAMES.includes(role) || typeof model !== "string" || variant !== null && typeof variant !== "string") {
|
|
4697
4946
|
return rejectPlan3(
|
|
4698
4947
|
plan,
|
|
4699
4948
|
"OpenCode model plan contains an invalid role or model."
|
|
4700
4949
|
);
|
|
4701
4950
|
}
|
|
4702
|
-
roleModels.set(role, model);
|
|
4951
|
+
roleModels.set(role, { model, variant });
|
|
4703
4952
|
}
|
|
4704
4953
|
if (roleModels.size === 0) {
|
|
4705
4954
|
return rejectPlan3(
|
|
@@ -4719,14 +4968,44 @@ function applyModelPlan(plan) {
|
|
|
4719
4968
|
reset: false
|
|
4720
4969
|
});
|
|
4721
4970
|
const agents = base.agents && typeof base.agents === "object" ? { ...base.agents } : {};
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4971
|
+
const statePath = getOpenCodeManagedModelStatePath();
|
|
4972
|
+
const state = readManagedModelState(statePath, 1);
|
|
4973
|
+
const configuredEfforts = { ...state.configuredEfforts ?? {} };
|
|
4974
|
+
const warnings = [];
|
|
4975
|
+
for (const [role, override] of roleModels) {
|
|
4976
|
+
const current = {
|
|
4977
|
+
...agents[role] ?? {}
|
|
4726
4978
|
};
|
|
4979
|
+
const currentVariant = typeof current.variant === "string" ? current.variant : void 0;
|
|
4980
|
+
const trackedVariant = configuredEfforts[role];
|
|
4981
|
+
const userOwnsVariant = override.variant === null && currentVariant !== void 0 && (trackedVariant === void 0 || currentVariant !== trackedVariant);
|
|
4982
|
+
current.model = override.model;
|
|
4983
|
+
if (userOwnsVariant) {
|
|
4984
|
+
delete configuredEfforts[role];
|
|
4985
|
+
warnings.push({
|
|
4986
|
+
severity: "important",
|
|
4987
|
+
code: "opencode-effort-user-owned",
|
|
4988
|
+
message: `Preserved user-owned OpenCode variant ${currentVariant} for ${role}.`
|
|
4989
|
+
});
|
|
4990
|
+
} else if (override.variant === null) {
|
|
4991
|
+
delete current.variant;
|
|
4992
|
+
delete configuredEfforts[role];
|
|
4993
|
+
} else {
|
|
4994
|
+
current.variant = override.variant;
|
|
4995
|
+
configuredEfforts[role] = override.variant;
|
|
4996
|
+
}
|
|
4997
|
+
agents[role] = current;
|
|
4727
4998
|
}
|
|
4728
4999
|
base.agents = agents;
|
|
4729
5000
|
writeConfig(targetPath, base);
|
|
5001
|
+
writeTextWithBackup(
|
|
5002
|
+
statePath,
|
|
5003
|
+
stableJson3({
|
|
5004
|
+
version: 1,
|
|
5005
|
+
models: state.models,
|
|
5006
|
+
...Object.keys(configuredEfforts).length > 0 ? { configuredEfforts } : {}
|
|
5007
|
+
})
|
|
5008
|
+
);
|
|
4730
5009
|
return {
|
|
4731
5010
|
harness: "opencode",
|
|
4732
5011
|
action: "model-config",
|
|
@@ -4739,7 +5018,7 @@ function applyModelPlan(plan) {
|
|
|
4739
5018
|
}
|
|
4740
5019
|
],
|
|
4741
5020
|
backups: existsSync10(`${targetPath}.bak`) ? [{ path: `${targetPath}.bak`, label: "managed backup" }] : [],
|
|
4742
|
-
warnings
|
|
5021
|
+
warnings,
|
|
4743
5022
|
disclaimers: defaultDisclaimers()
|
|
4744
5023
|
};
|
|
4745
5024
|
}
|
|
@@ -4868,23 +5147,571 @@ function getOperationHarness(harness) {
|
|
|
4868
5147
|
return OPERATION_HARNESSES[harness];
|
|
4869
5148
|
}
|
|
4870
5149
|
|
|
5150
|
+
// src/cli/tui/model-catalog.ts
|
|
5151
|
+
import { execFileSync } from "child_process";
|
|
5152
|
+
|
|
5153
|
+
// src/cli/model-catalog/cache.ts
|
|
5154
|
+
import {
|
|
5155
|
+
existsSync as existsSync11,
|
|
5156
|
+
mkdirSync as mkdirSync3,
|
|
5157
|
+
readFileSync as readFileSync10,
|
|
5158
|
+
renameSync as renameSync2,
|
|
5159
|
+
unlinkSync,
|
|
5160
|
+
writeFileSync as writeFileSync3
|
|
5161
|
+
} from "fs";
|
|
5162
|
+
import { homedir as homedir4 } from "os";
|
|
5163
|
+
import { dirname as dirname5, posix, win32 } from "path";
|
|
5164
|
+
import { z as z2 } from "zod";
|
|
5165
|
+
var modelCatalogSourceSchema = z2.enum(["remote", "lkg", "manual"]);
|
|
5166
|
+
var cachedModelOptionSchema = z2.object({
|
|
5167
|
+
id: z2.string().min(1),
|
|
5168
|
+
catalogId: z2.string().min(1).optional(),
|
|
5169
|
+
label: z2.string().min(1),
|
|
5170
|
+
provider: z2.string().min(1),
|
|
5171
|
+
efforts: z2.array(z2.string().min(1)),
|
|
5172
|
+
source: modelCatalogSourceSchema
|
|
5173
|
+
}).passthrough();
|
|
5174
|
+
var modelsDevCacheSchema = z2.object({
|
|
5175
|
+
version: z2.literal(1),
|
|
5176
|
+
etag: z2.string().optional(),
|
|
5177
|
+
fetchedAt: z2.string().datetime(),
|
|
5178
|
+
catalog: z2.object({ models: z2.array(cachedModelOptionSchema) }).passthrough()
|
|
5179
|
+
}).passthrough();
|
|
5180
|
+
function resolveModelsDevCachePath(options = {}) {
|
|
5181
|
+
const platform = options.platform ?? process.platform;
|
|
5182
|
+
const env = options.env ?? process.env;
|
|
5183
|
+
const home = options.homeDir ?? homedir4();
|
|
5184
|
+
if (platform === "win32") {
|
|
5185
|
+
const path4 = win32;
|
|
5186
|
+
const base2 = env.LOCALAPPDATA ?? env.APPDATA ?? path4.join(home, "AppData", "Local");
|
|
5187
|
+
return path4.join(base2, "thoth-agents", "models-dev-v1.json");
|
|
5188
|
+
}
|
|
5189
|
+
const base = env.XDG_CACHE_HOME ?? posix.join(home, ".cache");
|
|
5190
|
+
return posix.join(base, "thoth-agents", "models-dev-v1.json");
|
|
5191
|
+
}
|
|
5192
|
+
function readModelsDevCache(cachePath) {
|
|
5193
|
+
if (!existsSync11(cachePath)) return void 0;
|
|
5194
|
+
try {
|
|
5195
|
+
const input = JSON.parse(readFileSync10(cachePath, "utf8"));
|
|
5196
|
+
const parsed = modelsDevCacheSchema.safeParse(input);
|
|
5197
|
+
return parsed.success ? parsed.data : void 0;
|
|
5198
|
+
} catch {
|
|
5199
|
+
return void 0;
|
|
5200
|
+
}
|
|
5201
|
+
}
|
|
5202
|
+
function writeModelsDevCacheAtomic(cachePath, cache) {
|
|
5203
|
+
const validated = modelsDevCacheSchema.safeParse(cache);
|
|
5204
|
+
if (!validated.success) {
|
|
5205
|
+
throw new Error(`Invalid models.dev cache: ${validated.error.message}`);
|
|
5206
|
+
}
|
|
5207
|
+
mkdirSync3(dirname5(cachePath), { recursive: true });
|
|
5208
|
+
const temporaryPath = `${cachePath}.${process.pid}.${Date.now()}.tmp`;
|
|
5209
|
+
try {
|
|
5210
|
+
writeFileSync3(
|
|
5211
|
+
temporaryPath,
|
|
5212
|
+
`${JSON.stringify(validated.data, null, 2)}
|
|
5213
|
+
`
|
|
5214
|
+
);
|
|
5215
|
+
renameSync2(temporaryPath, cachePath);
|
|
5216
|
+
} catch (error) {
|
|
5217
|
+
if (existsSync11(temporaryPath)) unlinkSync(temporaryPath);
|
|
5218
|
+
throw error;
|
|
5219
|
+
}
|
|
5220
|
+
}
|
|
5221
|
+
|
|
5222
|
+
// src/cli/model-catalog/models-dev.ts
|
|
5223
|
+
import { z as z3 } from "zod";
|
|
5224
|
+
var modelsDevModelSchema = z3.object({
|
|
5225
|
+
id: z3.string().min(1).optional(),
|
|
5226
|
+
name: z3.string().min(1).optional(),
|
|
5227
|
+
reasoning: z3.boolean().optional(),
|
|
5228
|
+
reasoning_options: z3.array(z3.unknown()).optional()
|
|
5229
|
+
}).passthrough();
|
|
5230
|
+
var modelsDevProviderSchema = z3.object({
|
|
5231
|
+
models: z3.record(z3.string(), modelsDevModelSchema)
|
|
5232
|
+
}).passthrough();
|
|
5233
|
+
var modelsDevCatalogSchema = z3.record(
|
|
5234
|
+
z3.string(),
|
|
5235
|
+
modelsDevProviderSchema
|
|
5236
|
+
);
|
|
5237
|
+
var effortOptionSchema = z3.object({
|
|
5238
|
+
type: z3.literal("effort"),
|
|
5239
|
+
values: z3.array(z3.string().min(1))
|
|
5240
|
+
}).passthrough();
|
|
5241
|
+
function normalizedCatalogId(provider, modelKey, declaredId) {
|
|
5242
|
+
const candidate = (declaredId ?? modelKey).trim().toLowerCase();
|
|
5243
|
+
return candidate.includes("/") ? candidate : `${provider}/${candidate}`;
|
|
5244
|
+
}
|
|
5245
|
+
function runtimeModelId(provider, catalogId) {
|
|
5246
|
+
const prefix = `${provider}/`;
|
|
5247
|
+
return catalogId.startsWith(prefix) ? catalogId.slice(prefix.length) : catalogId;
|
|
5248
|
+
}
|
|
5249
|
+
function effortValues(options) {
|
|
5250
|
+
const values = /* @__PURE__ */ new Set();
|
|
5251
|
+
for (const option of options ?? []) {
|
|
5252
|
+
const parsed = effortOptionSchema.safeParse(option);
|
|
5253
|
+
if (!parsed.success) continue;
|
|
5254
|
+
for (const value of parsed.data.values) {
|
|
5255
|
+
const normalized = value.trim();
|
|
5256
|
+
if (normalized) values.add(normalized);
|
|
5257
|
+
}
|
|
5258
|
+
}
|
|
5259
|
+
return [...values];
|
|
5260
|
+
}
|
|
5261
|
+
function normalizeModelsDevCatalog(input) {
|
|
5262
|
+
const parsed = modelsDevCatalogSchema.safeParse(input);
|
|
5263
|
+
if (!parsed.success) {
|
|
5264
|
+
return {
|
|
5265
|
+
ok: false,
|
|
5266
|
+
issues: parsed.error.issues.map(
|
|
5267
|
+
(issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`
|
|
5268
|
+
)
|
|
5269
|
+
};
|
|
5270
|
+
}
|
|
5271
|
+
const models = [];
|
|
5272
|
+
for (const [rawProvider, provider] of Object.entries(parsed.data)) {
|
|
5273
|
+
const providerId = rawProvider.trim().toLowerCase();
|
|
5274
|
+
for (const [modelKey, model] of Object.entries(provider.models)) {
|
|
5275
|
+
const catalogId = normalizedCatalogId(providerId, modelKey, model.id);
|
|
5276
|
+
const id = runtimeModelId(providerId, catalogId);
|
|
5277
|
+
models.push({
|
|
5278
|
+
id,
|
|
5279
|
+
catalogId,
|
|
5280
|
+
label: model.name ?? id,
|
|
5281
|
+
provider: providerId,
|
|
5282
|
+
efforts: effortValues(model.reasoning_options),
|
|
5283
|
+
source: "remote"
|
|
5284
|
+
});
|
|
5285
|
+
}
|
|
5286
|
+
}
|
|
5287
|
+
return { ok: true, catalog: { models } };
|
|
5288
|
+
}
|
|
5289
|
+
|
|
5290
|
+
// src/cli/model-catalog/index.ts
|
|
5291
|
+
var MODELS_DEV_URL = "https://models.dev/api.json";
|
|
5292
|
+
var MODELS_DEV_MAX_BYTES = 8 * 1024 * 1024;
|
|
5293
|
+
var MODELS_DEV_TIMEOUT_MS = 5e3;
|
|
5294
|
+
function ownsCatalogKey(model, key) {
|
|
5295
|
+
const separator = key.indexOf("/");
|
|
5296
|
+
return separator > 0 && model.provider === key.slice(0, separator);
|
|
5297
|
+
}
|
|
5298
|
+
function mergeModelCatalog(dynamic, manual) {
|
|
5299
|
+
const merged = /* @__PURE__ */ new Map();
|
|
5300
|
+
for (const model of dynamic) {
|
|
5301
|
+
const key = model.catalogId ?? model.id;
|
|
5302
|
+
const existing = merged.get(key);
|
|
5303
|
+
if (existing && ownsCatalogKey(existing, key) && !ownsCatalogKey(model, key)) {
|
|
5304
|
+
continue;
|
|
5305
|
+
}
|
|
5306
|
+
merged.set(key, model);
|
|
5307
|
+
}
|
|
5308
|
+
for (const model of manual) {
|
|
5309
|
+
const key = model.catalogId ?? model.id;
|
|
5310
|
+
if (!merged.has(key)) merged.set(key, model);
|
|
5311
|
+
}
|
|
5312
|
+
return [...merged.values()];
|
|
5313
|
+
}
|
|
5314
|
+
function lkgModels(models) {
|
|
5315
|
+
return models.map((model) => ({
|
|
5316
|
+
...model,
|
|
5317
|
+
efforts: [...model.efforts],
|
|
5318
|
+
source: "lkg"
|
|
5319
|
+
}));
|
|
5320
|
+
}
|
|
5321
|
+
async function loadModelsDevCatalog(options = {}) {
|
|
5322
|
+
const cachePath = options.cachePath ?? resolveModelsDevCachePath();
|
|
5323
|
+
const manual = options.manual ?? [];
|
|
5324
|
+
const fetcher = options.fetcher ?? fetch;
|
|
5325
|
+
const checkedAt = (options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
|
|
5326
|
+
const cached = readModelsDevCache(cachePath);
|
|
5327
|
+
const warnings = [];
|
|
5328
|
+
try {
|
|
5329
|
+
const headers = new Headers({ Accept: "application/json" });
|
|
5330
|
+
if (cached?.etag) headers.set("If-None-Match", cached.etag);
|
|
5331
|
+
const response = await fetcher(MODELS_DEV_URL, {
|
|
5332
|
+
headers,
|
|
5333
|
+
signal: AbortSignal.timeout(MODELS_DEV_TIMEOUT_MS)
|
|
5334
|
+
});
|
|
5335
|
+
if (response.status === 304) {
|
|
5336
|
+
if (!cached)
|
|
5337
|
+
throw new Error("models.dev returned 304 without an LKG cache");
|
|
5338
|
+
return {
|
|
5339
|
+
models: mergeModelCatalog(lkgModels(cached.catalog.models), manual),
|
|
5340
|
+
source: "lkg",
|
|
5341
|
+
stale: false,
|
|
5342
|
+
checkedAt,
|
|
5343
|
+
warnings
|
|
5344
|
+
};
|
|
5345
|
+
}
|
|
5346
|
+
if (!response.ok)
|
|
5347
|
+
throw new Error(`models.dev returned HTTP ${response.status}`);
|
|
5348
|
+
const text = await response.text();
|
|
5349
|
+
if (Buffer.byteLength(text, "utf8") > MODELS_DEV_MAX_BYTES) {
|
|
5350
|
+
throw new Error("models.dev response exceeded the maximum size");
|
|
5351
|
+
}
|
|
5352
|
+
const input = JSON.parse(text);
|
|
5353
|
+
const normalized = normalizeModelsDevCatalog(input);
|
|
5354
|
+
if (!normalized.ok) {
|
|
5355
|
+
throw new Error(
|
|
5356
|
+
`models.dev validation failed: ${normalized.issues.join("; ")}`
|
|
5357
|
+
);
|
|
5358
|
+
}
|
|
5359
|
+
const remoteModels = normalized.catalog.models.map((model) => ({
|
|
5360
|
+
...model,
|
|
5361
|
+
source: "remote"
|
|
5362
|
+
}));
|
|
5363
|
+
writeModelsDevCacheAtomic(cachePath, {
|
|
5364
|
+
version: 1,
|
|
5365
|
+
...response.headers.get("etag") ? { etag: response.headers.get("etag") ?? void 0 } : {},
|
|
5366
|
+
fetchedAt: checkedAt,
|
|
5367
|
+
catalog: { models: lkgModels(remoteModels) }
|
|
5368
|
+
});
|
|
5369
|
+
return {
|
|
5370
|
+
models: mergeModelCatalog(remoteModels, manual),
|
|
5371
|
+
source: "remote",
|
|
5372
|
+
stale: false,
|
|
5373
|
+
checkedAt,
|
|
5374
|
+
warnings
|
|
5375
|
+
};
|
|
5376
|
+
} catch (error) {
|
|
5377
|
+
warnings.push(error instanceof Error ? error.message : String(error));
|
|
5378
|
+
}
|
|
5379
|
+
if (cached) {
|
|
5380
|
+
return {
|
|
5381
|
+
models: mergeModelCatalog(lkgModels(cached.catalog.models), manual),
|
|
5382
|
+
source: "lkg",
|
|
5383
|
+
stale: true,
|
|
5384
|
+
checkedAt,
|
|
5385
|
+
warnings
|
|
5386
|
+
};
|
|
5387
|
+
}
|
|
5388
|
+
return {
|
|
5389
|
+
models: [...manual],
|
|
5390
|
+
source: "manual",
|
|
5391
|
+
stale: true,
|
|
5392
|
+
checkedAt,
|
|
5393
|
+
warnings
|
|
5394
|
+
};
|
|
5395
|
+
}
|
|
5396
|
+
|
|
5397
|
+
// src/cli/tui/model-catalog.ts
|
|
5398
|
+
var MODEL_CATALOG_TIMEOUT_MS = 5e3;
|
|
5399
|
+
var CODEX_DOCUMENTED_EFFORTS2 = /* @__PURE__ */ new Set([
|
|
5400
|
+
"none",
|
|
5401
|
+
"minimal",
|
|
5402
|
+
"low",
|
|
5403
|
+
"medium",
|
|
5404
|
+
"high",
|
|
5405
|
+
"xhigh",
|
|
5406
|
+
"max",
|
|
5407
|
+
"ultra"
|
|
5408
|
+
]);
|
|
5409
|
+
var CLAUDE_CODE_EFFORTS2 = ["low", "medium", "high", "xhigh", "max"];
|
|
5410
|
+
function parseOpenCodeModels(output) {
|
|
5411
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5412
|
+
const options = [];
|
|
5413
|
+
for (const line of output.split(/\r?\n/)) {
|
|
5414
|
+
const match = line.trim().match(/^([a-z0-9_-]+)\/([^\s{]+)/i);
|
|
5415
|
+
if (!match) continue;
|
|
5416
|
+
const id = `${match[1]}/${match[2]}`;
|
|
5417
|
+
if (seen.has(id)) continue;
|
|
5418
|
+
seen.add(id);
|
|
5419
|
+
options.push({
|
|
5420
|
+
id,
|
|
5421
|
+
catalogId: id,
|
|
5422
|
+
label: id,
|
|
5423
|
+
provider: match[1] ?? "unknown",
|
|
5424
|
+
efforts: [],
|
|
5425
|
+
source: "manual"
|
|
5426
|
+
});
|
|
5427
|
+
}
|
|
5428
|
+
return options;
|
|
5429
|
+
}
|
|
5430
|
+
function isCodexOpenAiModelId(id) {
|
|
5431
|
+
const match = id.match(/^gpt-(\d+)(?:[.-]|$)/);
|
|
5432
|
+
return match?.[1] !== void 0 && Number(match[1]) >= 5;
|
|
5433
|
+
}
|
|
5434
|
+
function writableEfforts(harness, option) {
|
|
5435
|
+
if (harness === "codex") {
|
|
5436
|
+
return option.efforts.filter(
|
|
5437
|
+
(effort) => CODEX_DOCUMENTED_EFFORTS2.has(effort)
|
|
5438
|
+
);
|
|
5439
|
+
}
|
|
5440
|
+
if (harness === "claude") {
|
|
5441
|
+
return option.efforts.filter(
|
|
5442
|
+
(effort) => CLAUDE_CODE_EFFORTS2.includes(effort)
|
|
5443
|
+
);
|
|
5444
|
+
}
|
|
5445
|
+
return option.efforts.filter(
|
|
5446
|
+
(effort) => resolveOpenCodeEffort({
|
|
5447
|
+
model: option.id,
|
|
5448
|
+
catalogId: option.catalogId,
|
|
5449
|
+
availableEfforts: option.efforts,
|
|
5450
|
+
effort: { kind: "effort", value: effort }
|
|
5451
|
+
}).ok
|
|
5452
|
+
);
|
|
5453
|
+
}
|
|
5454
|
+
function effortChoicesForModel(option) {
|
|
5455
|
+
return ["inherit", ...option?.efforts ?? []];
|
|
5456
|
+
}
|
|
5457
|
+
function getOpenCodeModelsInvocation(platform = process.platform) {
|
|
5458
|
+
const options = {
|
|
5459
|
+
encoding: "utf8",
|
|
5460
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
5461
|
+
timeout: MODEL_CATALOG_TIMEOUT_MS
|
|
5462
|
+
};
|
|
5463
|
+
if (platform === "win32") {
|
|
5464
|
+
return {
|
|
5465
|
+
command: "opencode models",
|
|
5466
|
+
args: [],
|
|
5467
|
+
options: { ...options, shell: true }
|
|
5468
|
+
};
|
|
5469
|
+
}
|
|
5470
|
+
return { command: "opencode", args: ["models"], options };
|
|
5471
|
+
}
|
|
5472
|
+
function getNativeOpenCodeOptions() {
|
|
5473
|
+
try {
|
|
5474
|
+
const invocation = getOpenCodeModelsInvocation();
|
|
5475
|
+
return parseOpenCodeModels(
|
|
5476
|
+
execFileSync(invocation.command, invocation.args, invocation.options)
|
|
5477
|
+
);
|
|
5478
|
+
} catch {
|
|
5479
|
+
return [];
|
|
5480
|
+
}
|
|
5481
|
+
}
|
|
5482
|
+
var CLAUDE_CODE_MODEL_OPTIONS = [
|
|
5483
|
+
{ id: "sonnet", label: "sonnet", provider: "anthropic" },
|
|
5484
|
+
{ id: "opus", label: "opus", provider: "anthropic" },
|
|
5485
|
+
{ id: "haiku", label: "haiku", provider: "anthropic" },
|
|
5486
|
+
{
|
|
5487
|
+
id: "inherit",
|
|
5488
|
+
label: "inherit (main session model)",
|
|
5489
|
+
provider: "anthropic"
|
|
5490
|
+
}
|
|
5491
|
+
].map((option) => ({
|
|
5492
|
+
...option,
|
|
5493
|
+
efforts: option.id === "inherit" ? [] : [...CLAUDE_CODE_EFFORTS2],
|
|
5494
|
+
source: "manual"
|
|
5495
|
+
}));
|
|
5496
|
+
async function getModelOptions(harness) {
|
|
5497
|
+
const native = harness === "opencode" ? getNativeOpenCodeOptions() : [];
|
|
5498
|
+
const loaded = await loadModelsDevCatalog({ manual: native });
|
|
5499
|
+
if (harness === "claude") {
|
|
5500
|
+
const aliases = CLAUDE_CODE_MODEL_OPTIONS.map((option) => ({
|
|
5501
|
+
...option,
|
|
5502
|
+
efforts: [...option.efforts]
|
|
5503
|
+
}));
|
|
5504
|
+
const concrete = loaded.models.filter((option) => option.provider === "anthropic").map((option) => ({
|
|
5505
|
+
...option,
|
|
5506
|
+
id: option.catalogId ?? option.id,
|
|
5507
|
+
efforts: writableEfforts(harness, option)
|
|
5508
|
+
}));
|
|
5509
|
+
return [...aliases, ...concrete];
|
|
5510
|
+
}
|
|
5511
|
+
if (harness === "codex") {
|
|
5512
|
+
return loaded.models.filter(
|
|
5513
|
+
(option) => option.provider === "openai" && isCodexOpenAiModelId(option.id)
|
|
5514
|
+
).map((option) => ({
|
|
5515
|
+
...option,
|
|
5516
|
+
efforts: writableEfforts(harness, option)
|
|
5517
|
+
}));
|
|
5518
|
+
}
|
|
5519
|
+
const catalogById = new Map(
|
|
5520
|
+
loaded.models.map((option) => [option.catalogId ?? option.id, option])
|
|
5521
|
+
);
|
|
5522
|
+
return native.map((option) => {
|
|
5523
|
+
const catalog = catalogById.get(option.catalogId ?? option.id);
|
|
5524
|
+
const enriched = catalog ? { ...catalog, id: option.id, catalogId: option.catalogId } : option;
|
|
5525
|
+
return { ...enriched, efforts: writableEfforts(harness, enriched) };
|
|
5526
|
+
});
|
|
5527
|
+
}
|
|
5528
|
+
|
|
5529
|
+
// src/cli/tui/operations.ts
|
|
5530
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
5531
|
+
var context = { cwd: process.cwd() };
|
|
5532
|
+
var codexContext = { cwd: process.cwd() };
|
|
5533
|
+
var claudeCodeContext = {
|
|
5534
|
+
cwd: process.cwd(),
|
|
5535
|
+
scope: "user"
|
|
5536
|
+
};
|
|
5537
|
+
var opencodeModelRoles = ALL_AGENT_NAMES.map(
|
|
5538
|
+
(role) => ({
|
|
5539
|
+
role,
|
|
5540
|
+
model: DEFAULT_MODELS[role] ?? "openai/gpt-5.4"
|
|
5541
|
+
})
|
|
5542
|
+
);
|
|
5543
|
+
var codexModelRoles = CODEX_ROLE_NAMES.map(
|
|
5544
|
+
(role) => ({
|
|
5545
|
+
role,
|
|
5546
|
+
model: "gpt-5.4-mini"
|
|
5547
|
+
})
|
|
5548
|
+
);
|
|
5549
|
+
var codexDefaultModels = new Map(
|
|
5550
|
+
codexModelRoles.map((role) => [role.role, role.model])
|
|
5551
|
+
);
|
|
5552
|
+
function codexInstallConfig(source, dryRun) {
|
|
5553
|
+
return {
|
|
5554
|
+
dryRun,
|
|
5555
|
+
reset: false,
|
|
5556
|
+
scope: source.scope ?? "user",
|
|
5557
|
+
projectRoot: source.cwd,
|
|
5558
|
+
homeDir: source.homeDir,
|
|
5559
|
+
codexHome: source.codexHome,
|
|
5560
|
+
packageRoot: source.packageRoot,
|
|
5561
|
+
pluginId: source.pluginId
|
|
5562
|
+
};
|
|
5563
|
+
}
|
|
5564
|
+
function getCodexModelRoles(source = codexContext) {
|
|
5565
|
+
try {
|
|
5566
|
+
const plan = buildCodexSetupPlan(codexInstallConfig(source, true));
|
|
5567
|
+
return CODEX_ROLE_NAMES.map((role) => {
|
|
5568
|
+
const item = plan.items.find(
|
|
5569
|
+
(candidate) => candidate.action === "write-role-toml" && candidate.role === role
|
|
5570
|
+
);
|
|
5571
|
+
const content = item && existsSync12(item.targetPath) ? readFileSync11(item.targetPath, "utf8") : item?.content;
|
|
5572
|
+
const effort = content ? parseRoleTomlEffort(content) : void 0;
|
|
5573
|
+
return {
|
|
5574
|
+
role,
|
|
5575
|
+
model: (content ? parseRoleTomlModel(content) : void 0) ?? codexDefaultModels.get(role) ?? "gpt-5.4-mini",
|
|
5576
|
+
effort: effort ? { kind: "effort", value: effort } : { kind: "inherit" }
|
|
5577
|
+
};
|
|
5578
|
+
});
|
|
5579
|
+
} catch {
|
|
5580
|
+
return codexModelRoles.map((role) => ({ ...role }));
|
|
5581
|
+
}
|
|
5582
|
+
}
|
|
5583
|
+
function getClaudeCodeModelRoles(source = claudeCodeContext) {
|
|
5584
|
+
try {
|
|
5585
|
+
const plan = buildClaudeCodeSetupPlan({
|
|
5586
|
+
dryRun: true,
|
|
5587
|
+
reset: false,
|
|
5588
|
+
scope: source.scope ?? "user",
|
|
5589
|
+
projectRoot: source.cwd,
|
|
5590
|
+
homeDir: source.homeDir,
|
|
5591
|
+
packageRoot: source.packageRoot
|
|
5592
|
+
});
|
|
5593
|
+
return CLAUDE_CODE_ROLE_NAMES.map((role) => {
|
|
5594
|
+
const item = plan.items.find(
|
|
5595
|
+
(candidate) => candidate.kind === "subagent" && candidate.role === role
|
|
5596
|
+
);
|
|
5597
|
+
const content = item && existsSync12(item.targetPath) ? readFileSync11(item.targetPath, "utf8") : item?.content;
|
|
5598
|
+
const model = content ? parseSubagentModel(content) : void 0;
|
|
5599
|
+
const effort = content ? parseSubagentEffort(content) : void 0;
|
|
5600
|
+
return {
|
|
5601
|
+
role,
|
|
5602
|
+
model: model ?? "inherit",
|
|
5603
|
+
effort: effort ? { kind: "effort", value: effort } : { kind: "inherit" }
|
|
5604
|
+
};
|
|
5605
|
+
});
|
|
5606
|
+
} catch {
|
|
5607
|
+
return defaultClaudeCodeModelRoles();
|
|
5608
|
+
}
|
|
5609
|
+
}
|
|
5610
|
+
function readRoleField(config, role, field) {
|
|
5611
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
5612
|
+
return void 0;
|
|
5613
|
+
}
|
|
5614
|
+
const record = config;
|
|
5615
|
+
const value = record[role];
|
|
5616
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
5617
|
+
return void 0;
|
|
5618
|
+
}
|
|
5619
|
+
const fieldValue = value[field];
|
|
5620
|
+
return typeof fieldValue === "string" && fieldValue.length > 0 ? fieldValue : void 0;
|
|
5621
|
+
}
|
|
5622
|
+
function getOpenCodeModelRoles() {
|
|
5623
|
+
const parsed = parseConfig(getExistingLiteConfigPath());
|
|
5624
|
+
const agents = parsed.config?.agents && typeof parsed.config.agents === "object" ? parsed.config.agents : void 0;
|
|
5625
|
+
const presets = parsed.config?.presets && typeof parsed.config.presets === "object" ? parsed.config.presets : {};
|
|
5626
|
+
const openaiPreset = presets.openai && typeof presets.openai === "object" ? presets.openai : void 0;
|
|
5627
|
+
return ALL_AGENT_NAMES.map((role) => {
|
|
5628
|
+
const variant = readRoleField(agents, role, "variant") ?? readRoleField(openaiPreset, role, "variant");
|
|
5629
|
+
return {
|
|
5630
|
+
role,
|
|
5631
|
+
model: readRoleField(agents, role, "model") ?? readRoleField(openaiPreset, role, "model") ?? DEFAULT_MODELS[role] ?? "openai/gpt-5.4",
|
|
5632
|
+
effort: variant ? { kind: "effort", value: variant } : { kind: "inherit" }
|
|
5633
|
+
};
|
|
5634
|
+
});
|
|
5635
|
+
}
|
|
5636
|
+
function buildTuiModelPlan(harness, roles) {
|
|
5637
|
+
if (harness === "opencode") {
|
|
5638
|
+
return buildOpenCodeModelPlan({ harness, dryRun: true, roles }, context);
|
|
5639
|
+
}
|
|
5640
|
+
if (harness === "claude") {
|
|
5641
|
+
return buildClaudeCodeModelPlan(
|
|
5642
|
+
{ harness, dryRun: true, roles },
|
|
5643
|
+
claudeCodeContext
|
|
5644
|
+
);
|
|
5645
|
+
}
|
|
5646
|
+
return buildCodexModelPlan({ harness, dryRun: true, roles }, codexContext);
|
|
5647
|
+
}
|
|
5648
|
+
var defaultTuiOperations = {
|
|
5649
|
+
status(harness) {
|
|
5650
|
+
if (harness === "opencode") return getOpenCodeStatus(context);
|
|
5651
|
+
if (harness === "claude") {
|
|
5652
|
+
return getClaudeCodeStatus(claudeCodeContext);
|
|
5653
|
+
}
|
|
5654
|
+
return getCodexStatus(codexContext);
|
|
5655
|
+
},
|
|
5656
|
+
modelRoles(harness) {
|
|
5657
|
+
if (harness === "opencode") return getOpenCodeModelRoles();
|
|
5658
|
+
if (harness === "claude") {
|
|
5659
|
+
return getClaudeCodeModelRoles(claudeCodeContext);
|
|
5660
|
+
}
|
|
5661
|
+
return getCodexModelRoles(codexContext);
|
|
5662
|
+
},
|
|
5663
|
+
modelOptions(harness) {
|
|
5664
|
+
return getModelOptions(harness);
|
|
5665
|
+
},
|
|
5666
|
+
plan(harness, action) {
|
|
5667
|
+
if (harness === "opencode") {
|
|
5668
|
+
if (action === "install") return buildOpenCodeInstallPlan(context);
|
|
5669
|
+
if (action === "update") return buildOpenCodeUpdatePlan(context);
|
|
5670
|
+
if (action === "sync") return buildOpenCodeSyncPlan(context);
|
|
5671
|
+
return buildTuiModelPlan(harness, getOpenCodeModelRoles());
|
|
5672
|
+
}
|
|
5673
|
+
if (harness === "claude") {
|
|
5674
|
+
if (action === "install") {
|
|
5675
|
+
return buildClaudeCodeInstallPlan(claudeCodeContext);
|
|
5676
|
+
}
|
|
5677
|
+
if (action === "update") {
|
|
5678
|
+
return buildClaudeCodeUpdatePlan(claudeCodeContext);
|
|
5679
|
+
}
|
|
5680
|
+
if (action === "sync") return buildClaudeCodeSyncPlan(claudeCodeContext);
|
|
5681
|
+
return buildTuiModelPlan(
|
|
5682
|
+
harness,
|
|
5683
|
+
getClaudeCodeModelRoles(claudeCodeContext)
|
|
5684
|
+
);
|
|
5685
|
+
}
|
|
5686
|
+
if (action === "install") return buildCodexInstallPlan(codexContext);
|
|
5687
|
+
if (action === "update") return buildCodexUpdatePlan(codexContext);
|
|
5688
|
+
if (action === "sync") return buildCodexSyncPlan(codexContext);
|
|
5689
|
+
return buildTuiModelPlan(harness, getCodexModelRoles(codexContext));
|
|
5690
|
+
},
|
|
5691
|
+
modelPlan(harness, roles) {
|
|
5692
|
+
return buildTuiModelPlan(harness, roles);
|
|
5693
|
+
},
|
|
5694
|
+
apply(plan) {
|
|
5695
|
+
if (plan.harness === "opencode") return applyOpenCodePlan(plan);
|
|
5696
|
+
if (plan.harness === "claude") return applyClaudeCodePlan(plan);
|
|
5697
|
+
return applyCodexPlan(plan);
|
|
5698
|
+
}
|
|
5699
|
+
};
|
|
5700
|
+
|
|
4871
5701
|
export {
|
|
4872
5702
|
claudeCodeAdapter,
|
|
4873
5703
|
codexAdapter,
|
|
4874
5704
|
CODEX_ROLE_NAMES,
|
|
4875
|
-
|
|
5705
|
+
normalizeEffortSelection,
|
|
4876
5706
|
buildCodexSetupPlan,
|
|
4877
5707
|
formatCodexSetupPlan,
|
|
4878
5708
|
applyCodexSetup,
|
|
4879
|
-
CLAUDE_CODE_ROLE_NAMES,
|
|
4880
|
-
parseSubagentModel,
|
|
4881
5709
|
buildClaudeCodeSetupPlan,
|
|
4882
5710
|
applyClaudeCodeSetup,
|
|
4883
5711
|
formatClaudeCodeSetupPlan,
|
|
4884
5712
|
RECOMMENDED_SKILLS,
|
|
4885
5713
|
installRecommendedSkill,
|
|
4886
5714
|
getClaudeCodeStatus,
|
|
4887
|
-
buildClaudeCodeInstallPlan,
|
|
4888
5715
|
buildClaudeCodeUpdatePlan,
|
|
4889
5716
|
buildClaudeCodeSyncPlan,
|
|
4890
5717
|
buildClaudeCodeModelPlan,
|
|
@@ -4893,16 +5720,20 @@ export {
|
|
|
4893
5720
|
getCodexStatus,
|
|
4894
5721
|
buildCodexUpdatePlan,
|
|
4895
5722
|
buildCodexSyncPlan,
|
|
4896
|
-
buildCodexInstallPlan,
|
|
4897
5723
|
buildCodexModelPlan,
|
|
4898
5724
|
applyCodexPlan,
|
|
4899
5725
|
getOpenCodeStatus,
|
|
4900
5726
|
buildOpenCodeUpdatePlan,
|
|
4901
5727
|
buildOpenCodeSyncPlan,
|
|
4902
|
-
buildOpenCodeInstallPlan,
|
|
4903
5728
|
buildOpenCodeModelPlan,
|
|
4904
5729
|
applyOpenCodePlan,
|
|
4905
5730
|
SUPPORTED_OPERATION_HARNESSES,
|
|
4906
5731
|
listOperationHarnesses,
|
|
4907
|
-
getOperationHarness
|
|
5732
|
+
getOperationHarness,
|
|
5733
|
+
effortChoicesForModel,
|
|
5734
|
+
getModelOptions,
|
|
5735
|
+
getCodexModelRoles,
|
|
5736
|
+
getClaudeCodeModelRoles,
|
|
5737
|
+
getOpenCodeModelRoles,
|
|
5738
|
+
defaultTuiOperations
|
|
4908
5739
|
};
|