thoth-agents 0.2.6 → 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/cli/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  getOpenCodeVersion,
5
5
  isOpenCodeInstalled,
6
6
  opencodeAdapter
7
- } from "../chunk-5DHG5II4.js";
7
+ } from "../chunk-75JOF37E.js";
8
8
  import {
9
9
  CODEX_ROLE_NAMES,
10
10
  RECOMMENDED_SKILLS,
@@ -30,13 +30,18 @@ import {
30
30
  defaultClaudeCodeModelRoles,
31
31
  formatClaudeCodeSetupPlan,
32
32
  formatCodexSetupPlan,
33
+ getClaudeCodeModelRoles,
33
34
  getClaudeCodeStatus,
35
+ getCodexModelRoles,
34
36
  getCodexStatus,
37
+ getModelOptions,
38
+ getOpenCodeModelRoles,
35
39
  getOpenCodeStatus,
36
40
  getOperationHarness,
37
41
  installRecommendedSkill,
38
- listOperationHarnesses
39
- } from "../chunk-DAZFSPN4.js";
42
+ listOperationHarnesses,
43
+ normalizeEffortSelection
44
+ } from "../chunk-FM7HCZSW.js";
40
45
  import {
41
46
  ALL_AGENT_NAMES,
42
47
  CUSTOM_SKILLS,
@@ -48,7 +53,7 @@ import {
48
53
  getExistingLiteConfigPath,
49
54
  installCustomSkills,
50
55
  writeLiteConfig
51
- } from "../chunk-76DARQOT.js";
56
+ } from "../chunk-YLRPDTU7.js";
52
57
 
53
58
  // src/cli/index.ts
54
59
  import { pathToFileURL } from "url";
@@ -505,6 +510,8 @@ Options:
505
510
  --agent=opencode|codex|claude
506
511
  Select OpenCode plugin install (default), Codex agent-pack, or Claude Code plugin setup
507
512
  --harness=... Select harness for status/update/sync/model (opencode|codex|claude)
513
+ --role-effort=role=effort
514
+ Set a repeatable role effort; use inherit or default for no override
508
515
  -h, --help Show this help message
509
516
 
510
517
  Generate options:
@@ -584,9 +591,37 @@ function defaultModelRoles(harness) {
584
591
  model: DEFAULT_MODELS[role] ?? "openai/gpt-5.4"
585
592
  }));
586
593
  }
587
- function modelRoles(harness, roles) {
588
- return roles.length > 0 ? [...roles] : defaultModelRoles(harness);
594
+ function resolveCliModelRoles(harness, roles, resolution) {
595
+ const defaults = resolution?.currentRoles ?? defaultModelRoles(harness);
596
+ if (roles.length === 0) return defaults.map((role) => ({ ...role }));
597
+ const defaultsByRole = new Map(defaults.map((role) => [role.role, role]));
598
+ return roles.map((role) => {
599
+ const fallback = defaultsByRole.get(role.role);
600
+ const model = role.model ?? fallback?.model ?? "";
601
+ const catalogId = harness === "codex" && !model.includes("/") ? `openai/${model}` : model;
602
+ const option = resolution?.modelOptions.find(
603
+ (candidate) => candidate.id === model || candidate.catalogId === model || candidate.catalogId === catalogId
604
+ );
605
+ const provider = role.provider ?? option?.provider ?? fallback?.provider;
606
+ return {
607
+ role: role.role,
608
+ model,
609
+ ...provider !== void 0 ? { provider } : {},
610
+ ...option?.catalogId !== void 0 ? { catalogId: option.catalogId } : fallback?.catalogId ? { catalogId: fallback.catalogId } : {},
611
+ ...option?.efforts !== void 0 ? { availableEfforts: option.efforts } : fallback?.availableEfforts ? { availableEfforts: fallback.availableEfforts } : {},
612
+ ...role.effort !== void 0 ? { effort: role.effort } : {}
613
+ };
614
+ });
589
615
  }
616
+ var defaultModelCommandServices = {
617
+ operationContext,
618
+ modelRoles(harness) {
619
+ if (harness === "opencode") return getOpenCodeModelRoles();
620
+ if (harness === "claude") return getClaudeCodeModelRoles();
621
+ return getCodexModelRoles();
622
+ },
623
+ modelOptions: getModelOptions
624
+ };
590
625
  function printModelGuidance() {
591
626
  console.log(
592
627
  [
@@ -599,14 +634,19 @@ function printModelGuidance() {
599
634
  );
600
635
  return 1;
601
636
  }
602
- function buildModelPlan(args) {
637
+ async function buildModelPlan(args, services) {
603
638
  if (args.roles.length === 0) return void 0;
604
639
  const harness = selectedHarness(args);
605
- const context = operationContext();
640
+ const context = services.operationContext();
641
+ const currentRoles = services.modelRoles(harness);
642
+ const modelOptions = await services.modelOptions(harness);
606
643
  const input = {
607
644
  harness,
608
645
  dryRun: true,
609
- roles: modelRoles(harness, args.roles)
646
+ roles: resolveCliModelRoles(harness, args.roles, {
647
+ currentRoles,
648
+ modelOptions
649
+ })
610
650
  };
611
651
  if (harness === "opencode") return buildOpenCodeModelPlan(input, context);
612
652
  if (harness === "claude") return buildClaudeCodeModelPlan(input, context);
@@ -629,7 +669,7 @@ function printPlanOrApply(plan, args) {
629
669
  console.log(formatOperationApplyResult(applyOperationPlan(plan)));
630
670
  return 0;
631
671
  }
632
- function runOperationCommand(command, args) {
672
+ async function runOperationCommand(command, args, services) {
633
673
  if (command === "status") {
634
674
  console.log(formatHarnessStatusReport(statusReports(args)));
635
675
  return 0;
@@ -641,7 +681,7 @@ function runOperationCommand(command, args) {
641
681
  if (command === "update" || command === "sync") {
642
682
  return printPlanOrApply(buildOperationPlan(command, args), args);
643
683
  }
644
- const plan = buildModelPlan(args);
684
+ const plan = await buildModelPlan(args, services);
645
685
  if (!plan) return printModelGuidance();
646
686
  return printPlanOrApply(plan, args);
647
687
  }
@@ -662,7 +702,7 @@ function printHarnessGeneration(args) {
662
702
  console.log(JSON.stringify(result, null, 2));
663
703
  return 0;
664
704
  }
665
- async function runCliCommand(parsed) {
705
+ async function runCliCommand(parsed, modelServices = defaultModelCommandServices) {
666
706
  if (parsed.command === "install") {
667
707
  return install(parsed.installArgs);
668
708
  }
@@ -682,7 +722,11 @@ async function runCliCommand(parsed) {
682
722
  console.error("Run with --help for usage information");
683
723
  return 1;
684
724
  }
685
- return runOperationCommand(parsed.command, parsed.operationArgs);
725
+ return runOperationCommand(
726
+ parsed.command,
727
+ parsed.operationArgs,
728
+ modelServices
729
+ );
686
730
  }
687
731
 
688
732
  // src/cli/runtime.ts
@@ -754,17 +798,39 @@ function parseRoleModel(value) {
754
798
  }
755
799
  return { role, model };
756
800
  }
801
+ function parseRoleEffort(value) {
802
+ const separator = value.includes("=") ? "=" : ":";
803
+ const [role, ...effortParts] = value.split(separator);
804
+ const effort = effortParts.join(separator);
805
+ if (!role || !effort) {
806
+ throw new Error(
807
+ "--role-effort must use role=effort or role:effort, for example --role-effort=deep=high."
808
+ );
809
+ }
810
+ return { role, effort: normalizeEffortSelection(effort) };
811
+ }
757
812
  function parseOperationArgs(args) {
758
813
  const result = { roles: [] };
759
814
  let pendingRole;
760
815
  let pendingProvider;
816
+ function upsertRole(input) {
817
+ const existing = result.roles.find((role) => role.role === input.role);
818
+ if (!existing) {
819
+ result.roles.push(input);
820
+ return;
821
+ }
822
+ if (existing.effort && input.effort && JSON.stringify(existing.effort) !== JSON.stringify(input.effort)) {
823
+ throw new Error(`Conflicting --role-effort values for ${input.role}.`);
824
+ }
825
+ Object.assign(existing, input);
826
+ }
761
827
  function pushPendingRole(model) {
762
828
  if (!pendingRole) {
763
829
  throw new Error(
764
830
  "--model requires --role, or pass --role-model=role=model."
765
831
  );
766
832
  }
767
- result.roles.push({
833
+ upsertRole({
768
834
  role: pendingRole,
769
835
  model,
770
836
  provider: pendingProvider
@@ -793,11 +859,13 @@ function parseOperationArgs(args) {
793
859
  pushPendingRole(arg.split("=")[1] ?? "");
794
860
  } else if (arg.startsWith("--role-model=")) {
795
861
  const parsed = parseRoleModel(arg.slice("--role-model=".length));
796
- result.roles.push({
862
+ upsertRole({
797
863
  ...parsed,
798
864
  provider: pendingProvider
799
865
  });
800
866
  pendingProvider = void 0;
867
+ } else if (arg.startsWith("--role-effort=")) {
868
+ upsertRole(parseRoleEffort(arg.slice("--role-effort=".length)));
801
869
  } else if (arg === "-h" || arg === "--help") {
802
870
  throw new Error("help");
803
871
  } else {
@@ -10,6 +10,7 @@ export interface ManagedModelState {
10
10
  version: number;
11
11
  models: Record<string, string>;
12
12
  configuredModels?: Record<string, string>;
13
+ configuredEfforts?: Record<string, string>;
13
14
  }
14
15
  export declare function emptyManagedModelState(version: number): ManagedModelState;
15
16
  export declare function parseManagedModelStateJson(text: string | undefined, version: number): ManagedModelState;
@@ -0,0 +1,29 @@
1
+ import { z } from 'zod';
2
+ export declare const modelsDevCacheSchema: z.ZodObject<{
3
+ version: z.ZodLiteral<1>;
4
+ etag: z.ZodOptional<z.ZodString>;
5
+ fetchedAt: z.ZodString;
6
+ catalog: z.ZodObject<{
7
+ models: z.ZodArray<z.ZodObject<{
8
+ id: z.ZodString;
9
+ catalogId: z.ZodOptional<z.ZodString>;
10
+ label: z.ZodString;
11
+ provider: z.ZodString;
12
+ efforts: z.ZodArray<z.ZodString>;
13
+ source: z.ZodEnum<{
14
+ manual: "manual";
15
+ remote: "remote";
16
+ lkg: "lkg";
17
+ }>;
18
+ }, z.core.$loose>>;
19
+ }, z.core.$loose>;
20
+ }, z.core.$loose>;
21
+ export type ModelsDevCacheV1 = z.infer<typeof modelsDevCacheSchema>;
22
+ export interface ModelsDevCachePathOptions {
23
+ platform?: NodeJS.Platform;
24
+ env?: Readonly<Record<string, string | undefined>>;
25
+ homeDir?: string;
26
+ }
27
+ export declare function resolveModelsDevCachePath(options?: ModelsDevCachePathOptions): string;
28
+ export declare function readModelsDevCache(cachePath: string): ModelsDevCacheV1 | undefined;
29
+ export declare function writeModelsDevCacheAtomic(cachePath: string, cache: ModelsDevCacheV1): void;
@@ -0,0 +1,18 @@
1
+ import type { ModelOption } from './types';
2
+ export interface LoadModelsDevCatalogOptions {
3
+ cachePath?: string;
4
+ manual?: readonly ModelOption[];
5
+ fetcher?: typeof fetch;
6
+ now?: () => Date;
7
+ }
8
+ export interface LoadedModelCatalog {
9
+ models: ModelOption[];
10
+ source: 'remote' | 'lkg' | 'manual';
11
+ stale: boolean;
12
+ checkedAt: string;
13
+ warnings: string[];
14
+ }
15
+ export declare function mergeModelCatalog(dynamic: readonly ModelOption[], manual: readonly ModelOption[]): ModelOption[];
16
+ export declare function loadModelsDevCatalog(options?: LoadModelsDevCatalogOptions): Promise<LoadedModelCatalog>;
17
+ export { readModelsDevCache, resolveModelsDevCachePath, writeModelsDevCacheAtomic, } from './cache';
18
+ export type { ModelCatalogSource, ModelOption, ModelsDevCatalogResult, NormalizedModelsDevCatalog, } from './types';
@@ -0,0 +1,12 @@
1
+ import { z } from 'zod';
2
+ import type { ModelsDevCatalogResult } from './types';
3
+ export declare const modelsDevCatalogSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
4
+ models: z.ZodRecord<z.ZodString, z.ZodObject<{
5
+ id: z.ZodOptional<z.ZodString>;
6
+ name: z.ZodOptional<z.ZodString>;
7
+ reasoning: z.ZodOptional<z.ZodBoolean>;
8
+ reasoning_options: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
9
+ }, z.core.$loose>>;
10
+ }, z.core.$loose>>;
11
+ export type ModelsDevCatalog = z.infer<typeof modelsDevCatalogSchema>;
12
+ export declare function normalizeModelsDevCatalog(input: unknown): ModelsDevCatalogResult;
@@ -0,0 +1,19 @@
1
+ export type ModelCatalogSource = 'remote' | 'lkg' | 'manual';
2
+ export interface ModelOption {
3
+ id: string;
4
+ catalogId?: string;
5
+ label: string;
6
+ provider: string;
7
+ efforts: readonly string[];
8
+ source: ModelCatalogSource;
9
+ }
10
+ export interface NormalizedModelsDevCatalog {
11
+ models: readonly ModelOption[];
12
+ }
13
+ export type ModelsDevCatalogResult = {
14
+ ok: true;
15
+ catalog: NormalizedModelsDevCatalog;
16
+ } | {
17
+ ok: false;
18
+ issues: readonly string[];
19
+ };
@@ -0,0 +1,11 @@
1
+ export type EffortSelection = {
2
+ kind: 'inherit';
3
+ } | {
4
+ kind: 'effort';
5
+ value: string;
6
+ };
7
+ export declare function normalizeEffortSelection(value: string | null | undefined): EffortSelection;
8
+ export declare function isExplicitEffort(selection: EffortSelection): selection is {
9
+ kind: 'effort';
10
+ value: string;
11
+ };
@@ -0,0 +1,10 @@
1
+ import type { ModelRoleInput } from './operations/types';
2
+ export type OpenCodeEffortResolution = {
3
+ ok: true;
4
+ variant: string | undefined;
5
+ } | {
6
+ ok: false;
7
+ code: string;
8
+ message: string;
9
+ };
10
+ export declare function resolveOpenCodeEffort(input: Pick<ModelRoleInput, 'catalogId' | 'availableEfforts' | 'effort' | 'model'>): OpenCodeEffortResolution;
@@ -5,6 +5,15 @@ export interface ClaudeCodeOperationContext extends OperationContext {
5
5
  homeDir?: string;
6
6
  packageRoot?: string;
7
7
  }
8
+ export type ClaudeCodeEffortResolution = {
9
+ ok: true;
10
+ effort: string | undefined;
11
+ } | {
12
+ ok: false;
13
+ code: string;
14
+ message: string;
15
+ };
16
+ export declare function resolveClaudeCodeEffort(input: Pick<ModelRoleInput, 'availableEfforts' | 'catalogId' | 'effort' | 'model'>): ClaudeCodeEffortResolution;
8
17
  export declare const claudeCodeOperationAdapter: {
9
18
  readonly id: "claude";
10
19
  readonly displayName: "Claude Code";
@@ -1,5 +1,5 @@
1
1
  import type { CodexInstallScope } from '../codex-paths';
2
- import type { HarnessAction, HarnessStatusReport, ModelConfigInput, OperationApplyResult, OperationContext, OperationPlan } from './types';
2
+ import type { HarnessAction, HarnessStatusReport, ModelConfigInput, ModelRoleInput, OperationApplyResult, OperationContext, OperationPlan } from './types';
3
3
  export interface CodexOperationContext extends OperationContext {
4
4
  scope?: CodexInstallScope;
5
5
  homeDir?: string;
@@ -18,5 +18,13 @@ export declare function getCodexStatus(context?: CodexOperationContext): Harness
18
18
  export declare function buildCodexUpdatePlan(context?: CodexOperationContext): OperationPlan;
19
19
  export declare function buildCodexSyncPlan(context?: CodexOperationContext): OperationPlan;
20
20
  export declare function buildCodexInstallPlan(context?: CodexOperationContext): OperationPlan;
21
+ export declare function resolveCodexEffort(input: ModelRoleInput): {
22
+ ok: true;
23
+ value: string | undefined;
24
+ } | {
25
+ ok: false;
26
+ code: string;
27
+ message: string;
28
+ };
21
29
  export declare function buildCodexModelPlan(input: ModelConfigInput, context?: CodexOperationContext): OperationPlan;
22
30
  export declare function applyCodexPlan(plan: OperationPlan): OperationApplyResult;
@@ -1,4 +1,5 @@
1
1
  import type { HarnessId } from '../../harness/types';
2
+ import type { EffortSelection } from '../model-effort';
2
3
  export type ManagedState = 'installed' | 'missing' | 'drift' | 'outdated' | 'unknown';
3
4
  export type OperationActionKind = 'status' | 'list' | 'install' | 'update' | 'sync' | 'repair' | 'model-config';
4
5
  export type ManagedTargetKind = 'file' | 'directory' | 'config' | 'skill' | 'hook' | 'memory-state' | 'generated-artifact' | 'package' | 'surface' | 'unknown';
@@ -89,6 +90,9 @@ export interface ModelRoleInput {
89
90
  role: string;
90
91
  model: string;
91
92
  provider?: string;
93
+ effort?: EffortSelection;
94
+ catalogId?: string;
95
+ availableEfforts?: readonly string[];
92
96
  }
93
97
  export interface ModelConfigInput {
94
98
  harness: HarnessId;
@@ -12,6 +12,7 @@ export declare function getConfigJson(): string;
12
12
  export declare function getConfigJsonc(): string;
13
13
  export declare function getLiteConfig(): string;
14
14
  export declare function getLiteConfigJsonc(): string;
15
+ export declare function getOpenCodeManagedModelStatePath(): string;
15
16
  export declare function getExistingLiteConfigPath(): string;
16
17
  export declare function getExistingConfigPath(): string;
17
18
  export declare function ensureConfigDir(): void;
@@ -6,28 +6,28 @@ export declare const MODEL_MAPPINGS: {
6
6
  readonly model: "openai/gpt-5.4";
7
7
  };
8
8
  readonly oracle: {
9
- readonly model: "openai/gpt-5.4";
9
+ readonly model: "openai/gpt-5.6-sol";
10
10
  readonly variant: "high";
11
11
  };
12
12
  readonly librarian: {
13
- readonly model: "openai/gpt-5.4-mini";
13
+ readonly model: "openai/gpt-5.6-luna";
14
14
  readonly variant: "low";
15
15
  };
16
16
  readonly explorer: {
17
- readonly model: "openai/gpt-5.4-mini";
17
+ readonly model: "openai/gpt-5.6-luna";
18
18
  readonly variant: "low";
19
19
  };
20
20
  readonly designer: {
21
- readonly model: "openai/gpt-5.4-mini";
22
- readonly variant: "medium";
21
+ readonly model: "openai/gpt-5.6-terra";
22
+ readonly variant: "high";
23
23
  };
24
24
  readonly quick: {
25
- readonly model: "openai/gpt-5.4-mini";
26
- readonly variant: "low";
25
+ readonly model: "openai/gpt-5.6-luna";
26
+ readonly variant: "medium";
27
27
  };
28
28
  readonly deep: {
29
- readonly model: "openai/gpt-5.4";
30
- readonly variant: "high";
29
+ readonly model: "openai/gpt-5.6-terra";
30
+ readonly variant: "xhigh";
31
31
  };
32
32
  };
33
33
  readonly kimi: {
@@ -6,4 +6,12 @@ interface ModelChoiceScreenProps {
6
6
  selected: number;
7
7
  }
8
8
  export declare function ModelChoiceScreen({ currentModel, draftModel, options, selected, }: ModelChoiceScreenProps): import("react/jsx-runtime").JSX.Element;
9
+ interface EffortChoiceScreenProps {
10
+ model: string;
11
+ currentEffort: string;
12
+ draftEffort: string;
13
+ choices: readonly string[];
14
+ selected: number;
15
+ }
16
+ export declare function EffortChoiceScreen({ model, currentEffort, draftEffort, choices, selected, }: EffortChoiceScreenProps): import("react/jsx-runtime").JSX.Element;
9
17
  export {};
@@ -2,6 +2,7 @@ import type { HarnessId } from '../../../harness/types';
2
2
  import type { ModelRoleInput } from '../../operations';
3
3
  export interface ModelRoleView extends ModelRoleInput {
4
4
  currentModel: string;
5
+ currentEffort: NonNullable<ModelRoleInput['effort']>;
5
6
  dirty: boolean;
6
7
  }
7
8
  interface ModelScreenProps {