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
package/dist/cli/tui/index.js
CHANGED
|
@@ -1,44 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
buildClaudeCodeInstallPlan,
|
|
8
|
-
buildClaudeCodeModelPlan,
|
|
9
|
-
buildClaudeCodeSetupPlan,
|
|
10
|
-
buildClaudeCodeSyncPlan,
|
|
11
|
-
buildClaudeCodeUpdatePlan,
|
|
12
|
-
buildCodexInstallPlan,
|
|
13
|
-
buildCodexModelPlan,
|
|
14
|
-
buildCodexSetupPlan,
|
|
15
|
-
buildCodexSyncPlan,
|
|
16
|
-
buildCodexUpdatePlan,
|
|
17
|
-
buildOpenCodeInstallPlan,
|
|
18
|
-
buildOpenCodeModelPlan,
|
|
19
|
-
buildOpenCodeSyncPlan,
|
|
20
|
-
buildOpenCodeUpdatePlan,
|
|
21
|
-
defaultClaudeCodeModelRoles,
|
|
22
|
-
getClaudeCodeStatus,
|
|
23
|
-
getCodexStatus,
|
|
24
|
-
getOpenCodeStatus,
|
|
25
|
-
listOperationHarnesses,
|
|
26
|
-
parseRoleTomlModel,
|
|
27
|
-
parseSubagentModel
|
|
28
|
-
} from "../../chunk-32672NLJ.js";
|
|
29
|
-
import {
|
|
30
|
-
ALL_AGENT_NAMES,
|
|
31
|
-
DEFAULT_MODELS,
|
|
32
|
-
getExistingLiteConfigPath,
|
|
33
|
-
parseConfig
|
|
34
|
-
} from "../../chunk-N7GFXGFJ.js";
|
|
2
|
+
defaultTuiOperations,
|
|
3
|
+
effortChoicesForModel,
|
|
4
|
+
listOperationHarnesses
|
|
5
|
+
} from "../../chunk-FM7HCZSW.js";
|
|
6
|
+
import "../../chunk-YLRPDTU7.js";
|
|
35
7
|
|
|
36
8
|
// src/cli/tui/index.tsx
|
|
37
9
|
import { render } from "ink";
|
|
38
10
|
|
|
39
11
|
// src/cli/tui/App.tsx
|
|
40
12
|
import { Box as Box8, Text as Text8, useApp, useInput } from "ink";
|
|
41
|
-
import { useMemo, useState } from "react";
|
|
13
|
+
import { useMemo, useRef, useState } from "react";
|
|
42
14
|
|
|
43
15
|
// src/cli/tui/components/Header.tsx
|
|
44
16
|
import { Box, Text } from "ink";
|
|
@@ -168,6 +140,41 @@ function ModelChoiceScreen({
|
|
|
168
140
|
] }) : null
|
|
169
141
|
] });
|
|
170
142
|
}
|
|
143
|
+
function EffortChoiceScreen({
|
|
144
|
+
model,
|
|
145
|
+
currentEffort,
|
|
146
|
+
draftEffort,
|
|
147
|
+
choices,
|
|
148
|
+
selected
|
|
149
|
+
}) {
|
|
150
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
151
|
+
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
152
|
+
"Model: ",
|
|
153
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.accent, children: model })
|
|
154
|
+
] }),
|
|
155
|
+
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
156
|
+
"Current effort: ",
|
|
157
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.accent, children: currentEffort })
|
|
158
|
+
] }),
|
|
159
|
+
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
160
|
+
"New effort: ",
|
|
161
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.warning, children: draftEffort })
|
|
162
|
+
] }),
|
|
163
|
+
choices.length === 1 ? /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "No explicit effort options are available for this model." }) : null,
|
|
164
|
+
choices.map((effort, index) => /* @__PURE__ */ jsxs3(
|
|
165
|
+
Text3,
|
|
166
|
+
{
|
|
167
|
+
color: index === selected ? theme.accent : void 0,
|
|
168
|
+
children: [
|
|
169
|
+
index === selected ? ">" : " ",
|
|
170
|
+
" ",
|
|
171
|
+
effort
|
|
172
|
+
]
|
|
173
|
+
},
|
|
174
|
+
effort
|
|
175
|
+
))
|
|
176
|
+
] });
|
|
177
|
+
}
|
|
171
178
|
|
|
172
179
|
// src/cli/tui/components/ModelScreen.tsx
|
|
173
180
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
@@ -196,7 +203,13 @@ function ModelScreen({
|
|
|
196
203
|
role.role,
|
|
197
204
|
": ",
|
|
198
205
|
role.model,
|
|
199
|
-
/* @__PURE__ */
|
|
206
|
+
/* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
|
|
207
|
+
" ",
|
|
208
|
+
"\xB7 effort",
|
|
209
|
+
" ",
|
|
210
|
+
role.effort?.kind === "effort" ? role.effort.value : "inherit"
|
|
211
|
+
] }),
|
|
212
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: role.dirty ? ` (was ${role.currentModel} \xB7 effort ${role.currentEffort.kind === "effort" ? role.currentEffort.value : "inherit"})` : "" })
|
|
200
213
|
]
|
|
201
214
|
},
|
|
202
215
|
role.role
|
|
@@ -498,277 +511,6 @@ function StatusView({ report }) {
|
|
|
498
511
|
] });
|
|
499
512
|
}
|
|
500
513
|
|
|
501
|
-
// src/cli/tui/model-catalog.ts
|
|
502
|
-
import { execFileSync } from "child_process";
|
|
503
|
-
var MODEL_CATALOG_TIMEOUT_MS = 5e3;
|
|
504
|
-
var MODELS_DEV_MAX_BUFFER = 8 * 1024 * 1024;
|
|
505
|
-
function parseOpenCodeModels(output) {
|
|
506
|
-
const seen = /* @__PURE__ */ new Set();
|
|
507
|
-
const options = [];
|
|
508
|
-
for (const line of output.split(/\r?\n/)) {
|
|
509
|
-
const match = line.trim().match(/^([a-z0-9_-]+)\/([^\s{]+)/i);
|
|
510
|
-
if (!match) continue;
|
|
511
|
-
const id = `${match[1]}/${match[2]}`;
|
|
512
|
-
if (seen.has(id)) continue;
|
|
513
|
-
seen.add(id);
|
|
514
|
-
options.push({ id, label: id, provider: match[1] ?? "unknown" });
|
|
515
|
-
}
|
|
516
|
-
return options;
|
|
517
|
-
}
|
|
518
|
-
function parseModelsDevOpenAi(output) {
|
|
519
|
-
const catalog = JSON.parse(output);
|
|
520
|
-
return Object.entries(catalog.openai?.models ?? {}).filter(([id]) => isCodexOpenAiModelId(id)).map(([id, model]) => ({
|
|
521
|
-
id,
|
|
522
|
-
label: model.name ?? id,
|
|
523
|
-
provider: "openai"
|
|
524
|
-
}));
|
|
525
|
-
}
|
|
526
|
-
function isCodexOpenAiModelId(id) {
|
|
527
|
-
const match = id.match(/^gpt-(\d+)(?:[.-]|$)/);
|
|
528
|
-
return match?.[1] !== void 0 && Number(match[1]) >= 5;
|
|
529
|
-
}
|
|
530
|
-
function getOpenCodeModelsInvocation(platform = process.platform) {
|
|
531
|
-
const options = {
|
|
532
|
-
encoding: "utf8",
|
|
533
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
534
|
-
timeout: MODEL_CATALOG_TIMEOUT_MS
|
|
535
|
-
};
|
|
536
|
-
if (platform === "win32") {
|
|
537
|
-
return {
|
|
538
|
-
command: "opencode models",
|
|
539
|
-
args: [],
|
|
540
|
-
options: { ...options, shell: true }
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
return {
|
|
544
|
-
command: "opencode",
|
|
545
|
-
args: ["models"],
|
|
546
|
-
options
|
|
547
|
-
};
|
|
548
|
-
}
|
|
549
|
-
function getModelsDevCatalog() {
|
|
550
|
-
try {
|
|
551
|
-
const output = execFileSync(
|
|
552
|
-
process.execPath,
|
|
553
|
-
[
|
|
554
|
-
"-e",
|
|
555
|
-
[
|
|
556
|
-
"const controller = new AbortController();",
|
|
557
|
-
`const timeout = setTimeout(() => controller.abort(), ${MODEL_CATALOG_TIMEOUT_MS});`,
|
|
558
|
-
"if (typeof fetch !== 'function') {",
|
|
559
|
-
" console.error('fetch unavailable');",
|
|
560
|
-
" process.exit(1);",
|
|
561
|
-
"}",
|
|
562
|
-
"fetch('https://models.dev/api.json', { signal: controller.signal })",
|
|
563
|
-
" .then(async (response) => {",
|
|
564
|
-
" if (!response.ok) throw new Error(String(response.status));",
|
|
565
|
-
" const catalog = await response.json();",
|
|
566
|
-
" const models = catalog?.openai?.models ?? {};",
|
|
567
|
-
" return JSON.stringify({ openai: { models } });",
|
|
568
|
-
" })",
|
|
569
|
-
" .then((body) => {",
|
|
570
|
-
" clearTimeout(timeout);",
|
|
571
|
-
" process.stdout.write(body);",
|
|
572
|
-
" })",
|
|
573
|
-
" .catch((error) => { clearTimeout(timeout); console.error(error.message); process.exit(1); });"
|
|
574
|
-
].join("\n")
|
|
575
|
-
],
|
|
576
|
-
{
|
|
577
|
-
encoding: "utf8",
|
|
578
|
-
maxBuffer: MODELS_DEV_MAX_BUFFER,
|
|
579
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
580
|
-
timeout: MODEL_CATALOG_TIMEOUT_MS
|
|
581
|
-
}
|
|
582
|
-
);
|
|
583
|
-
return parseModelsDevOpenAi(output);
|
|
584
|
-
} catch {
|
|
585
|
-
return [];
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
var CLAUDE_CODE_MODEL_OPTIONS = [
|
|
589
|
-
{ id: "sonnet", label: "sonnet", provider: "anthropic" },
|
|
590
|
-
{ id: "opus", label: "opus", provider: "anthropic" },
|
|
591
|
-
{ id: "haiku", label: "haiku", provider: "anthropic" },
|
|
592
|
-
{
|
|
593
|
-
id: "inherit",
|
|
594
|
-
label: "inherit (main session model)",
|
|
595
|
-
provider: "anthropic"
|
|
596
|
-
}
|
|
597
|
-
];
|
|
598
|
-
function getModelOptions(harness) {
|
|
599
|
-
if (harness === "codex") return getModelsDevCatalog();
|
|
600
|
-
if (harness === "claude") return CLAUDE_CODE_MODEL_OPTIONS;
|
|
601
|
-
try {
|
|
602
|
-
const invocation = getOpenCodeModelsInvocation();
|
|
603
|
-
const output = execFileSync(
|
|
604
|
-
invocation.command,
|
|
605
|
-
invocation.args,
|
|
606
|
-
invocation.options
|
|
607
|
-
);
|
|
608
|
-
return parseOpenCodeModels(output);
|
|
609
|
-
} catch {
|
|
610
|
-
return [];
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
// src/cli/tui/operations.ts
|
|
615
|
-
var context = { cwd: process.cwd() };
|
|
616
|
-
var codexContext = { cwd: process.cwd() };
|
|
617
|
-
var claudeCodeContext = {
|
|
618
|
-
cwd: process.cwd(),
|
|
619
|
-
scope: "user"
|
|
620
|
-
};
|
|
621
|
-
var opencodeModelRoles = ALL_AGENT_NAMES.map(
|
|
622
|
-
(role) => ({
|
|
623
|
-
role,
|
|
624
|
-
model: DEFAULT_MODELS[role] ?? "openai/gpt-5.4"
|
|
625
|
-
})
|
|
626
|
-
);
|
|
627
|
-
var codexModelRoles = CODEX_ROLE_NAMES.map(
|
|
628
|
-
(role) => ({
|
|
629
|
-
role,
|
|
630
|
-
model: "gpt-5.4-mini"
|
|
631
|
-
})
|
|
632
|
-
);
|
|
633
|
-
var codexDefaultModels = new Map(
|
|
634
|
-
codexModelRoles.map((role) => [role.role, role.model])
|
|
635
|
-
);
|
|
636
|
-
function codexInstallConfig(source, dryRun) {
|
|
637
|
-
return {
|
|
638
|
-
dryRun,
|
|
639
|
-
reset: false,
|
|
640
|
-
scope: source.scope ?? "user",
|
|
641
|
-
projectRoot: source.cwd,
|
|
642
|
-
homeDir: source.homeDir,
|
|
643
|
-
codexHome: source.codexHome,
|
|
644
|
-
packageRoot: source.packageRoot,
|
|
645
|
-
pluginId: source.pluginId
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
function getCodexModelRoles(source = codexContext) {
|
|
649
|
-
try {
|
|
650
|
-
const plan = buildCodexSetupPlan(codexInstallConfig(source, true));
|
|
651
|
-
return CODEX_ROLE_NAMES.map((role) => {
|
|
652
|
-
const item = plan.items.find(
|
|
653
|
-
(candidate) => candidate.action === "write-role-toml" && candidate.role === role
|
|
654
|
-
);
|
|
655
|
-
return {
|
|
656
|
-
role,
|
|
657
|
-
model: (item?.content ? parseRoleTomlModel(item.content) : void 0) ?? codexDefaultModels.get(role) ?? "gpt-5.4-mini"
|
|
658
|
-
};
|
|
659
|
-
});
|
|
660
|
-
} catch {
|
|
661
|
-
return codexModelRoles.map((role) => ({ ...role }));
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
function getClaudeCodeModelRoles(source = claudeCodeContext) {
|
|
665
|
-
try {
|
|
666
|
-
const plan = buildClaudeCodeSetupPlan({
|
|
667
|
-
dryRun: true,
|
|
668
|
-
reset: false,
|
|
669
|
-
scope: source.scope ?? "user",
|
|
670
|
-
projectRoot: source.cwd,
|
|
671
|
-
homeDir: source.homeDir,
|
|
672
|
-
packageRoot: source.packageRoot
|
|
673
|
-
});
|
|
674
|
-
return CLAUDE_CODE_ROLE_NAMES.map((role) => {
|
|
675
|
-
const item = plan.items.find(
|
|
676
|
-
(candidate) => candidate.kind === "subagent" && candidate.role === role
|
|
677
|
-
);
|
|
678
|
-
const model = item?.content ? parseSubagentModel(item.content) : void 0;
|
|
679
|
-
return { role, model: model ?? "inherit" };
|
|
680
|
-
});
|
|
681
|
-
} catch {
|
|
682
|
-
return defaultClaudeCodeModelRoles();
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
function readRoleModel(config, role) {
|
|
686
|
-
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
687
|
-
return void 0;
|
|
688
|
-
}
|
|
689
|
-
const record = config;
|
|
690
|
-
const value = record[role];
|
|
691
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
692
|
-
return void 0;
|
|
693
|
-
}
|
|
694
|
-
const model = value.model;
|
|
695
|
-
return typeof model === "string" && model.length > 0 ? model : void 0;
|
|
696
|
-
}
|
|
697
|
-
function getOpenCodeModelRoles() {
|
|
698
|
-
const parsed = parseConfig(getExistingLiteConfigPath());
|
|
699
|
-
const agents = parsed.config?.agents && typeof parsed.config.agents === "object" ? parsed.config.agents : void 0;
|
|
700
|
-
const presets = parsed.config?.presets && typeof parsed.config.presets === "object" ? parsed.config.presets : {};
|
|
701
|
-
const openaiPreset = presets.openai && typeof presets.openai === "object" ? presets.openai : void 0;
|
|
702
|
-
return ALL_AGENT_NAMES.map((role) => ({
|
|
703
|
-
role,
|
|
704
|
-
model: readRoleModel(agents, role) ?? readRoleModel(openaiPreset, role) ?? DEFAULT_MODELS[role] ?? "openai/gpt-5.4"
|
|
705
|
-
}));
|
|
706
|
-
}
|
|
707
|
-
function buildTuiModelPlan(harness, roles) {
|
|
708
|
-
if (harness === "opencode") {
|
|
709
|
-
return buildOpenCodeModelPlan({ harness, dryRun: true, roles }, context);
|
|
710
|
-
}
|
|
711
|
-
if (harness === "claude") {
|
|
712
|
-
return buildClaudeCodeModelPlan(
|
|
713
|
-
{ harness, dryRun: true, roles },
|
|
714
|
-
claudeCodeContext
|
|
715
|
-
);
|
|
716
|
-
}
|
|
717
|
-
return buildCodexModelPlan({ harness, dryRun: true, roles }, codexContext);
|
|
718
|
-
}
|
|
719
|
-
var defaultTuiOperations = {
|
|
720
|
-
status(harness) {
|
|
721
|
-
if (harness === "opencode") return getOpenCodeStatus(context);
|
|
722
|
-
if (harness === "claude") {
|
|
723
|
-
return getClaudeCodeStatus(claudeCodeContext);
|
|
724
|
-
}
|
|
725
|
-
return getCodexStatus(codexContext);
|
|
726
|
-
},
|
|
727
|
-
modelRoles(harness) {
|
|
728
|
-
if (harness === "opencode") return getOpenCodeModelRoles();
|
|
729
|
-
if (harness === "claude") {
|
|
730
|
-
return getClaudeCodeModelRoles(claudeCodeContext);
|
|
731
|
-
}
|
|
732
|
-
return getCodexModelRoles(codexContext);
|
|
733
|
-
},
|
|
734
|
-
modelOptions(harness) {
|
|
735
|
-
return getModelOptions(harness);
|
|
736
|
-
},
|
|
737
|
-
plan(harness, action) {
|
|
738
|
-
if (harness === "opencode") {
|
|
739
|
-
if (action === "install") return buildOpenCodeInstallPlan(context);
|
|
740
|
-
if (action === "update") return buildOpenCodeUpdatePlan(context);
|
|
741
|
-
if (action === "sync") return buildOpenCodeSyncPlan(context);
|
|
742
|
-
return buildTuiModelPlan(harness, getOpenCodeModelRoles());
|
|
743
|
-
}
|
|
744
|
-
if (harness === "claude") {
|
|
745
|
-
if (action === "install") {
|
|
746
|
-
return buildClaudeCodeInstallPlan(claudeCodeContext);
|
|
747
|
-
}
|
|
748
|
-
if (action === "update") {
|
|
749
|
-
return buildClaudeCodeUpdatePlan(claudeCodeContext);
|
|
750
|
-
}
|
|
751
|
-
if (action === "sync") return buildClaudeCodeSyncPlan(claudeCodeContext);
|
|
752
|
-
return buildTuiModelPlan(
|
|
753
|
-
harness,
|
|
754
|
-
getClaudeCodeModelRoles(claudeCodeContext)
|
|
755
|
-
);
|
|
756
|
-
}
|
|
757
|
-
if (action === "install") return buildCodexInstallPlan(codexContext);
|
|
758
|
-
if (action === "update") return buildCodexUpdatePlan(codexContext);
|
|
759
|
-
if (action === "sync") return buildCodexSyncPlan(codexContext);
|
|
760
|
-
return buildTuiModelPlan(harness, getCodexModelRoles(codexContext));
|
|
761
|
-
},
|
|
762
|
-
modelPlan(harness, roles) {
|
|
763
|
-
return buildTuiModelPlan(harness, roles);
|
|
764
|
-
},
|
|
765
|
-
apply(plan) {
|
|
766
|
-
if (plan.harness === "opencode") return applyOpenCodePlan(plan);
|
|
767
|
-
if (plan.harness === "claude") return applyClaudeCodePlan(plan);
|
|
768
|
-
return applyCodexPlan(plan);
|
|
769
|
-
}
|
|
770
|
-
};
|
|
771
|
-
|
|
772
514
|
// src/cli/tui/App.tsx
|
|
773
515
|
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
774
516
|
var rootItems = [
|
|
@@ -873,7 +615,46 @@ function normalizeSelection(current, items) {
|
|
|
873
615
|
return Math.min(current, items.length - 1);
|
|
874
616
|
}
|
|
875
617
|
function changedRoles(rows) {
|
|
876
|
-
return rows.filter((role) => role.dirty).map((role) =>
|
|
618
|
+
return rows.filter((role) => role.dirty).map((role) => {
|
|
619
|
+
const effortChanged = role.effort?.kind !== role.currentEffort.kind || role.effort?.kind === "effort" && role.currentEffort.kind === "effort" && role.effort.value !== role.currentEffort.value;
|
|
620
|
+
const includeEffort = effortChanged || role.effort?.kind === "effort";
|
|
621
|
+
return {
|
|
622
|
+
role: role.role,
|
|
623
|
+
model: role.model,
|
|
624
|
+
...includeEffort ? { effort: role.effort } : {},
|
|
625
|
+
...includeEffort && role.provider ? { provider: role.provider } : {},
|
|
626
|
+
...includeEffort && role.catalogId ? { catalogId: role.catalogId } : {},
|
|
627
|
+
...includeEffort && role.availableEfforts ? { availableEfforts: role.availableEfforts } : {}
|
|
628
|
+
};
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
function metadataForModel(role, model, option) {
|
|
632
|
+
if (option) {
|
|
633
|
+
return {
|
|
634
|
+
provider: option.provider,
|
|
635
|
+
...option.catalogId ? { catalogId: option.catalogId } : {},
|
|
636
|
+
availableEfforts: option.efforts
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
if (model !== role.model) return {};
|
|
640
|
+
return {
|
|
641
|
+
...role.provider ? { provider: role.provider } : {},
|
|
642
|
+
...role.catalogId ? { catalogId: role.catalogId } : {},
|
|
643
|
+
...role.availableEfforts ? { availableEfforts: role.availableEfforts } : {}
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
function choicesForRole(role, options, draftModel) {
|
|
647
|
+
const exact = options.find((option) => option.id === draftModel);
|
|
648
|
+
const sameModel = draftModel === role.model;
|
|
649
|
+
const current = exact ?? {
|
|
650
|
+
id: draftModel,
|
|
651
|
+
...sameModel && role.catalogId ? { catalogId: role.catalogId } : {},
|
|
652
|
+
label: draftModel,
|
|
653
|
+
provider: sameModel ? role.provider ?? "current" : "manual",
|
|
654
|
+
efforts: sameModel ? role.availableEfforts ?? [] : [],
|
|
655
|
+
source: "manual"
|
|
656
|
+
};
|
|
657
|
+
return [current, ...options.filter((option) => option.id !== draftModel)];
|
|
877
658
|
}
|
|
878
659
|
function App({
|
|
879
660
|
operations = defaultTuiOperations,
|
|
@@ -899,8 +680,14 @@ function App({
|
|
|
899
680
|
const [modelHarness, setModelHarness] = useState("codex");
|
|
900
681
|
const [modelRoles, setModelRoles] = useState([]);
|
|
901
682
|
const [modelOptions, setModelOptions] = useState([]);
|
|
902
|
-
const [
|
|
683
|
+
const [modelCatalogLoading, setModelCatalogLoading] = useState(false);
|
|
684
|
+
const [modelCatalogError, setModelCatalogError] = useState();
|
|
685
|
+
const modelCatalogRequest = useRef(0);
|
|
686
|
+
const [modelChoiceSelection, setModelChoiceSelection] = useState({ kind: "manual" });
|
|
903
687
|
const [editedModels, setEditedModels] = useState({});
|
|
688
|
+
const [editedEfforts, setEditedEfforts] = useState({});
|
|
689
|
+
const [draftModelOption, setDraftModelOption] = useState();
|
|
690
|
+
const [effortSelected, setEffortSelected] = useState(0);
|
|
904
691
|
const [modelSelected, setModelSelected] = useState(0);
|
|
905
692
|
const [editingRole, setEditingRole] = useState();
|
|
906
693
|
const [editDraft, setEditDraft] = useState("");
|
|
@@ -909,20 +696,37 @@ function App({
|
|
|
909
696
|
const currentManageItems = report?.state === "missing" ? installManageItems : manageItems;
|
|
910
697
|
const modelRows = modelRoles.map((role) => {
|
|
911
698
|
const model = editedModels[role.role] ?? role.model;
|
|
699
|
+
const option = modelOptions.find((candidate) => candidate.id === model);
|
|
700
|
+
const metadata = metadataForModel(role, model, option);
|
|
701
|
+
const effort = editedEfforts[role.role] ?? role.effort ?? { kind: "inherit" };
|
|
702
|
+
const currentEffort = role.effort ?? { kind: "inherit" };
|
|
703
|
+
const effortDirty = effort.kind !== currentEffort.kind || effort.kind === "effort" && currentEffort.kind === "effort" && effort.value !== currentEffort.value;
|
|
912
704
|
return {
|
|
913
|
-
|
|
705
|
+
role: role.role,
|
|
914
706
|
model,
|
|
707
|
+
effort,
|
|
708
|
+
...metadata,
|
|
915
709
|
currentModel: role.model,
|
|
916
|
-
|
|
710
|
+
currentEffort,
|
|
711
|
+
dirty: model !== role.model || effortDirty
|
|
917
712
|
};
|
|
918
713
|
});
|
|
714
|
+
const modelChoiceOptions = editingRole ? choicesForRole(
|
|
715
|
+
editingRole,
|
|
716
|
+
modelOptions,
|
|
717
|
+
editedModels[editingRole.role] ?? editingRole.model
|
|
718
|
+
) : modelOptions;
|
|
719
|
+
const selectedModelIndex = modelChoiceSelection.kind === "manual" ? modelChoiceOptions.length : modelChoiceOptions.findIndex(
|
|
720
|
+
(option) => option.id === modelChoiceSelection.id
|
|
721
|
+
);
|
|
722
|
+
const choiceSelected = selectedModelIndex < 0 ? 0 : selectedModelIndex;
|
|
919
723
|
const dirtyRoles = changedRoles(modelRows);
|
|
920
724
|
const modelActions = dirtyRoles.length > 0 ? ["Preview changes", "Apply changes", "Back"] : ["Back"];
|
|
921
725
|
const modelMenuItems = [
|
|
922
726
|
...modelRows.map((role) => ({
|
|
923
727
|
id: role.role,
|
|
924
728
|
label: `${role.dirty ? "* " : ""}${role.role}`,
|
|
925
|
-
detail: role.model
|
|
729
|
+
detail: `${role.model} \xB7 ${role.effort?.kind === "effort" ? role.effort.value : "inherit"}`
|
|
926
730
|
})),
|
|
927
731
|
...modelActions.map((action) => ({
|
|
928
732
|
id: action,
|
|
@@ -946,6 +750,8 @@ function App({
|
|
|
946
750
|
setView("manageHarness");
|
|
947
751
|
} else if (view === "modelChoice") {
|
|
948
752
|
setView("modelRoles");
|
|
753
|
+
} else if (view === "effortChoice") {
|
|
754
|
+
setView("modelChoice");
|
|
949
755
|
} else if (view === "modelEdit") {
|
|
950
756
|
setView("modelChoice");
|
|
951
757
|
}
|
|
@@ -964,13 +770,44 @@ function App({
|
|
|
964
770
|
setView("preview");
|
|
965
771
|
}
|
|
966
772
|
function openModelRoles(harness) {
|
|
773
|
+
const request = modelCatalogRequest.current + 1;
|
|
774
|
+
modelCatalogRequest.current = request;
|
|
967
775
|
setModelHarness(harness);
|
|
968
776
|
setModelRoles(operations.modelRoles(harness));
|
|
969
|
-
setModelOptions(
|
|
777
|
+
setModelOptions([]);
|
|
778
|
+
setModelCatalogLoading(true);
|
|
779
|
+
setModelCatalogError(void 0);
|
|
970
780
|
setEditedModels({});
|
|
781
|
+
setEditedEfforts({});
|
|
971
782
|
setModelResult(void 0);
|
|
972
783
|
setModelSelected(0);
|
|
973
784
|
setView("modelRoles");
|
|
785
|
+
void operations.modelOptions(harness).then((options) => {
|
|
786
|
+
if (modelCatalogRequest.current !== request) return;
|
|
787
|
+
setModelOptions(options);
|
|
788
|
+
setModelCatalogLoading(false);
|
|
789
|
+
}).catch((error) => {
|
|
790
|
+
if (modelCatalogRequest.current !== request) return;
|
|
791
|
+
setModelCatalogError(
|
|
792
|
+
error instanceof Error ? error.message : String(error)
|
|
793
|
+
);
|
|
794
|
+
setModelCatalogLoading(false);
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
function beginEffortSelection(role, model, option) {
|
|
798
|
+
const choices = effortChoicesForModel(option);
|
|
799
|
+
const pending = editedEfforts[role.role] ?? role.effort ?? { kind: "inherit" };
|
|
800
|
+
const nextEffort = pending.kind === "effort" && !option?.efforts.includes(pending.value) ? { kind: "inherit" } : pending;
|
|
801
|
+
setEditedModels((models) => ({ ...models, [role.role]: model }));
|
|
802
|
+
setModelChoiceSelection({ kind: "model", id: model });
|
|
803
|
+
setEditedEfforts((efforts) => ({
|
|
804
|
+
...efforts,
|
|
805
|
+
[role.role]: nextEffort
|
|
806
|
+
}));
|
|
807
|
+
setDraftModelOption(option);
|
|
808
|
+
const nextIndex = nextEffort.kind === "effort" ? choices.indexOf(nextEffort.value) : 0;
|
|
809
|
+
setEffortSelected(nextIndex < 0 ? 0 : nextIndex);
|
|
810
|
+
setView("effortChoice");
|
|
974
811
|
}
|
|
975
812
|
function previewModelChanges(applyImmediately) {
|
|
976
813
|
const nextPlan = operations.modelPlan(modelHarness, dirtyRoles);
|
|
@@ -982,12 +819,21 @@ function App({
|
|
|
982
819
|
setModelResult(applyResult);
|
|
983
820
|
if (applyResult.applied) {
|
|
984
821
|
setModelRoles(
|
|
985
|
-
(roles) => roles.map((role) =>
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
822
|
+
(roles) => roles.map((role) => {
|
|
823
|
+
const model = editedModels[role.role] ?? role.model;
|
|
824
|
+
const option = modelOptions.find(
|
|
825
|
+
(candidate) => candidate.id === model
|
|
826
|
+
);
|
|
827
|
+
return {
|
|
828
|
+
role: role.role,
|
|
829
|
+
model,
|
|
830
|
+
effort: editedEfforts[role.role] ?? role.effort ?? { kind: "inherit" },
|
|
831
|
+
...metadataForModel(role, model, option)
|
|
832
|
+
};
|
|
833
|
+
})
|
|
989
834
|
);
|
|
990
835
|
setEditedModels({});
|
|
836
|
+
setEditedEfforts({});
|
|
991
837
|
}
|
|
992
838
|
return;
|
|
993
839
|
}
|
|
@@ -1001,11 +847,7 @@ function App({
|
|
|
1001
847
|
return;
|
|
1002
848
|
}
|
|
1003
849
|
if (key.return && editingRole) {
|
|
1004
|
-
|
|
1005
|
-
...models,
|
|
1006
|
-
[editingRole.role]: editDraft
|
|
1007
|
-
}));
|
|
1008
|
-
setView("modelRoles");
|
|
850
|
+
beginEffortSelection(editingRole, editDraft, void 0);
|
|
1009
851
|
return;
|
|
1010
852
|
}
|
|
1011
853
|
if (key.tab) {
|
|
@@ -1022,34 +864,67 @@ function App({
|
|
|
1022
864
|
}
|
|
1023
865
|
return;
|
|
1024
866
|
}
|
|
867
|
+
if (view === "effortChoice") {
|
|
868
|
+
if (key.escape) {
|
|
869
|
+
goBack();
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
const choices = effortChoicesForModel(draftModelOption);
|
|
873
|
+
const choiceItems = choices.map((effort) => ({
|
|
874
|
+
id: effort,
|
|
875
|
+
label: effort,
|
|
876
|
+
detail: effort === "inherit" ? "Use the harness default" : "Set an explicit reasoning effort"
|
|
877
|
+
}));
|
|
878
|
+
if (key.upArrow || input === "k") {
|
|
879
|
+
setEffortSelected((current) => moveSelection(current, -1, choiceItems));
|
|
880
|
+
}
|
|
881
|
+
if (key.downArrow || input === "j") {
|
|
882
|
+
setEffortSelected((current) => moveSelection(current, 1, choiceItems));
|
|
883
|
+
}
|
|
884
|
+
if (key.return && editingRole) {
|
|
885
|
+
const selectedEffort = choices[effortSelected] ?? "inherit";
|
|
886
|
+
setEditedEfforts((efforts) => ({
|
|
887
|
+
...efforts,
|
|
888
|
+
[editingRole.role]: selectedEffort === "inherit" ? { kind: "inherit" } : { kind: "effort", value: selectedEffort }
|
|
889
|
+
}));
|
|
890
|
+
setView("modelRoles");
|
|
891
|
+
}
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
1025
894
|
if (view === "modelChoice") {
|
|
1026
895
|
if (key.escape) {
|
|
1027
896
|
goBack();
|
|
1028
897
|
return;
|
|
1029
898
|
}
|
|
1030
899
|
const choiceItems = [
|
|
1031
|
-
...
|
|
900
|
+
...modelChoiceOptions.map((option) => ({
|
|
1032
901
|
id: option.id,
|
|
1033
902
|
label: option.id,
|
|
1034
903
|
detail: option.provider
|
|
1035
904
|
})),
|
|
1036
905
|
{ id: "manual", label: "Manual entry", detail: "Type a model ID" }
|
|
1037
906
|
];
|
|
907
|
+
const selectChoiceAt = (index) => {
|
|
908
|
+
const option = modelChoiceOptions[index];
|
|
909
|
+
setModelChoiceSelection(
|
|
910
|
+
option ? { kind: "model", id: option.id } : { kind: "manual" }
|
|
911
|
+
);
|
|
912
|
+
};
|
|
1038
913
|
if (key.upArrow || input === "k") {
|
|
1039
|
-
|
|
914
|
+
selectChoiceAt(moveSelection(choiceSelected, -1, choiceItems));
|
|
1040
915
|
}
|
|
1041
916
|
if (key.downArrow || input === "j") {
|
|
1042
|
-
|
|
917
|
+
selectChoiceAt(moveSelection(choiceSelected, 1, choiceItems));
|
|
1043
918
|
}
|
|
1044
919
|
if (key.return && editingRole) {
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
920
|
+
if (modelChoiceSelection.kind === "model") {
|
|
921
|
+
const option = modelChoiceOptions.find(
|
|
922
|
+
(candidate) => candidate.id === modelChoiceSelection.id
|
|
923
|
+
);
|
|
924
|
+
if (option) {
|
|
925
|
+
beginEffortSelection(editingRole, option.id, option);
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
1053
928
|
}
|
|
1054
929
|
setEditDraft(editingRole.model);
|
|
1055
930
|
setView("modelEdit");
|
|
@@ -1172,7 +1047,11 @@ function App({
|
|
|
1172
1047
|
const role = modelRows[modelSelected];
|
|
1173
1048
|
setEditingRole(role);
|
|
1174
1049
|
setEditDraft(role?.model ?? "");
|
|
1175
|
-
|
|
1050
|
+
setDraftModelOption(void 0);
|
|
1051
|
+
setModelChoiceSelection({
|
|
1052
|
+
kind: "model",
|
|
1053
|
+
id: role?.model ?? ""
|
|
1054
|
+
});
|
|
1176
1055
|
setView("modelChoice");
|
|
1177
1056
|
return;
|
|
1178
1057
|
}
|
|
@@ -1276,6 +1155,12 @@ function App({
|
|
|
1276
1155
|
actions: modelActions
|
|
1277
1156
|
}
|
|
1278
1157
|
),
|
|
1158
|
+
modelCatalogLoading ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Loading model catalog\u2026" }) : null,
|
|
1159
|
+
modelCatalogError ? /* @__PURE__ */ jsxs7(Text8, { color: theme.warning, children: [
|
|
1160
|
+
"Could not load model catalog: ",
|
|
1161
|
+
modelCatalogError,
|
|
1162
|
+
". Manual entry is still available."
|
|
1163
|
+
] }) : null,
|
|
1279
1164
|
modelResult ? /* @__PURE__ */ jsx8(Text8, { color: modelResult.applied ? theme.ok : theme.warning, children: modelResult.summary }) : null
|
|
1280
1165
|
] });
|
|
1281
1166
|
}
|
|
@@ -1304,7 +1189,7 @@ function App({
|
|
|
1304
1189
|
Header,
|
|
1305
1190
|
{
|
|
1306
1191
|
title: `Choose ${editingRole.role} model`,
|
|
1307
|
-
subtitle: "
|
|
1192
|
+
subtitle: "Current model is selected first. Press Enter to continue to effort, or choose Manual entry."
|
|
1308
1193
|
}
|
|
1309
1194
|
),
|
|
1310
1195
|
/* @__PURE__ */ jsx8(
|
|
@@ -1312,9 +1197,39 @@ function App({
|
|
|
1312
1197
|
{
|
|
1313
1198
|
currentModel: editingRole.currentModel,
|
|
1314
1199
|
draftModel: editedModels[editingRole.role] ?? editingRole.model,
|
|
1315
|
-
options:
|
|
1200
|
+
options: modelChoiceOptions,
|
|
1316
1201
|
selected: choiceSelected
|
|
1317
1202
|
}
|
|
1203
|
+
),
|
|
1204
|
+
modelCatalogLoading ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Loading model catalog\u2026" }) : null,
|
|
1205
|
+
modelCatalogError ? /* @__PURE__ */ jsxs7(Text8, { color: theme.warning, children: [
|
|
1206
|
+
"Could not load model catalog: ",
|
|
1207
|
+
modelCatalogError
|
|
1208
|
+
] }) : null
|
|
1209
|
+
] });
|
|
1210
|
+
}
|
|
1211
|
+
if (view === "effortChoice" && editingRole) {
|
|
1212
|
+
const draftModel = editedModels[editingRole.role] ?? editingRole.model;
|
|
1213
|
+
const effort = editedEfforts[editingRole.role] ?? {
|
|
1214
|
+
kind: "inherit"
|
|
1215
|
+
};
|
|
1216
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
1217
|
+
/* @__PURE__ */ jsx8(
|
|
1218
|
+
Header,
|
|
1219
|
+
{
|
|
1220
|
+
title: `Choose ${editingRole.role} effort`,
|
|
1221
|
+
subtitle: "Select inherit or an effort supported by this model."
|
|
1222
|
+
}
|
|
1223
|
+
),
|
|
1224
|
+
/* @__PURE__ */ jsx8(
|
|
1225
|
+
EffortChoiceScreen,
|
|
1226
|
+
{
|
|
1227
|
+
model: draftModel,
|
|
1228
|
+
currentEffort: editingRole.currentEffort.kind === "effort" ? editingRole.currentEffort.value : "inherit",
|
|
1229
|
+
draftEffort: effort.kind === "effort" ? effort.value : "inherit",
|
|
1230
|
+
choices: effortChoicesForModel(draftModelOption),
|
|
1231
|
+
selected: effortSelected
|
|
1232
|
+
}
|
|
1318
1233
|
)
|
|
1319
1234
|
] });
|
|
1320
1235
|
}
|