thoth-agents 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/dist/{chunk-SEF6PFM6.js → chunk-5MYXVRZZ.js} +132 -157
- package/dist/chunk-6O4GKDS7.js +207 -0
- package/dist/{chunk-QOA2AETX.js → chunk-NOLDFT7O.js} +3 -3
- package/dist/{chunk-7F33WUSD.js → chunk-VVYAQTYE.js} +2 -2
- package/dist/{chunk-NY2IPJ7V.js → chunk-YCWGTDFE.js} +531 -233
- package/dist/cli/index.js +6 -6
- package/dist/cli/opencode-profiles.d.ts +10 -0
- package/dist/cli/tui/index.js +3 -3
- package/dist/config/loader.d.ts +1 -4
- package/dist/config/schema.d.ts +0 -22
- package/dist/index.js +824 -30
- package/dist/opencode/context.d.ts +16 -0
- package/dist/opencode/profile-rpc-handlers.d.ts +3 -0
- package/dist/opencode/profile-rpc.d.ts +688 -0
- package/dist/opencode/profile-runtime.d.ts +3 -0
- package/dist/opencode/profile-sessions.d.ts +18 -0
- package/dist/opencode/profiles/editor.d.ts +44 -0
- package/dist/opencode/profiles/index.d.ts +18 -0
- package/dist/opencode/profiles/resolver.d.ts +12 -0
- package/dist/opencode/profiles/store.d.ts +63 -0
- package/dist/opencode/profiles/types.d.ts +26 -0
- package/dist/opencode/tool-overrides/evaluator.d.ts +13 -0
- package/dist/opencode/tool-overrides/index.d.ts +5 -0
- package/dist/opencode/tool-overrides/store.d.ts +38 -0
- package/dist/opencode/tool-overrides/types.d.ts +32 -0
- package/dist/opencode/tool-overrides/validation.d.ts +5 -0
- package/dist/opencode/tool-overrides/wildcard.d.ts +3 -0
- package/dist/opencode/tool-rpc-handlers.d.ts +3 -0
- package/dist/opencode/tool-rpc.d.ts +428 -0
- package/dist/pi.js +2 -2
- package/dist/tui/fixtures.d.ts +2 -0
- package/dist/tui/flow.d.ts +16 -0
- package/dist/tui/index.d.ts +7 -0
- package/dist/tui/index.js +1384 -0
- package/dist/tui/model.d.ts +84 -0
- package/dist/tui/tools/fixtures.d.ts +3 -0
- package/dist/tui/tools/flow.d.ts +11 -0
- package/dist/tui/tools/model.d.ts +97 -0
- package/package.json +5 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/thoth-agents.schema.json +0 -70
|
@@ -19,6 +19,108 @@ function findPackageRoot(startDir) {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// src/config/schema.ts
|
|
23
|
+
import { z } from "zod";
|
|
24
|
+
var AGENT_NAMES = [
|
|
25
|
+
"orchestrator",
|
|
26
|
+
"oracle",
|
|
27
|
+
"designer",
|
|
28
|
+
"explorer",
|
|
29
|
+
"librarian",
|
|
30
|
+
"worker"
|
|
31
|
+
];
|
|
32
|
+
var FALLBACK_AGENT_NAMES = [...AGENT_NAMES];
|
|
33
|
+
var MANUAL_AGENT_NAMES = [...AGENT_NAMES];
|
|
34
|
+
var ProviderModelIdSchema = z.string().regex(
|
|
35
|
+
/^[^/\s]+\/[^\s]+$/,
|
|
36
|
+
"Expected provider/model format (provider/.../model)"
|
|
37
|
+
);
|
|
38
|
+
var ManualAgentPlanSchema = z.object({
|
|
39
|
+
primary: ProviderModelIdSchema,
|
|
40
|
+
fallback1: ProviderModelIdSchema,
|
|
41
|
+
fallback2: ProviderModelIdSchema,
|
|
42
|
+
fallback3: ProviderModelIdSchema
|
|
43
|
+
}).superRefine((value, ctx) => {
|
|
44
|
+
const unique = /* @__PURE__ */ new Set([
|
|
45
|
+
value.primary,
|
|
46
|
+
value.fallback1,
|
|
47
|
+
value.fallback2,
|
|
48
|
+
value.fallback3
|
|
49
|
+
]);
|
|
50
|
+
if (unique.size !== 4) {
|
|
51
|
+
ctx.addIssue({
|
|
52
|
+
code: z.ZodIssueCode.custom,
|
|
53
|
+
message: "primary and fallbacks must be unique per agent"
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
var ManualPlanSchema = z.object({
|
|
58
|
+
orchestrator: ManualAgentPlanSchema,
|
|
59
|
+
oracle: ManualAgentPlanSchema,
|
|
60
|
+
designer: ManualAgentPlanSchema,
|
|
61
|
+
explorer: ManualAgentPlanSchema,
|
|
62
|
+
librarian: ManualAgentPlanSchema,
|
|
63
|
+
worker: ManualAgentPlanSchema
|
|
64
|
+
}).strict();
|
|
65
|
+
var AgentModelChainSchema = z.array(z.string()).min(1);
|
|
66
|
+
var SupportedAgentRecordKeySchema = z.string().regex(/^(?!quick$|deep$).+$/, "Unsupported removed agent role");
|
|
67
|
+
var FallbackChainsSchema = z.record(
|
|
68
|
+
SupportedAgentRecordKeySchema,
|
|
69
|
+
AgentModelChainSchema
|
|
70
|
+
);
|
|
71
|
+
var AgentOverrideConfigSchema = z.object({
|
|
72
|
+
model: z.union([
|
|
73
|
+
z.string(),
|
|
74
|
+
z.array(
|
|
75
|
+
z.union([
|
|
76
|
+
z.string(),
|
|
77
|
+
z.object({
|
|
78
|
+
id: z.string(),
|
|
79
|
+
variant: z.string().optional()
|
|
80
|
+
})
|
|
81
|
+
])
|
|
82
|
+
)
|
|
83
|
+
]).optional(),
|
|
84
|
+
/** Pi specialist catalog filter; omitted discovers all, [] discovers none. */
|
|
85
|
+
skills: z.array(z.string().trim().min(1)).optional(),
|
|
86
|
+
temperature: z.number().min(0).max(2).optional(),
|
|
87
|
+
steps: z.number().int().min(1).optional(),
|
|
88
|
+
variant: z.string().optional().catch(void 0)
|
|
89
|
+
});
|
|
90
|
+
var AgentNameSchema = z.enum(AGENT_NAMES);
|
|
91
|
+
var McpNameSchema = z.enum(["exa", "context7", "grep_app"]);
|
|
92
|
+
var CodexGenerationConfigSchema = z.object({
|
|
93
|
+
enabled: z.boolean().default(false),
|
|
94
|
+
outputRoot: z.string().optional(),
|
|
95
|
+
dryRun: z.boolean().default(true)
|
|
96
|
+
});
|
|
97
|
+
var ClaudeCodeGenerationConfigSchema = z.object({
|
|
98
|
+
enabled: z.boolean().default(false),
|
|
99
|
+
outputRoot: z.string().optional(),
|
|
100
|
+
dryRun: z.boolean().default(true)
|
|
101
|
+
});
|
|
102
|
+
var FailoverConfigSchema = z.object({
|
|
103
|
+
enabled: z.boolean().default(true),
|
|
104
|
+
timeoutMs: z.number().min(0).default(15e3),
|
|
105
|
+
retryDelayMs: z.number().min(0).default(500),
|
|
106
|
+
chains: FallbackChainsSchema.default({})
|
|
107
|
+
});
|
|
108
|
+
var PluginConfigSchema = z.object({
|
|
109
|
+
$schema: z.string().optional(),
|
|
110
|
+
setDefaultAgent: z.boolean().optional(),
|
|
111
|
+
scoringEngineVersion: z.enum(["v1", "v2-shadow", "v2"]).optional(),
|
|
112
|
+
balanceProviderUsage: z.boolean().optional(),
|
|
113
|
+
manualPlan: ManualPlanSchema.optional(),
|
|
114
|
+
agents: z.record(SupportedAgentRecordKeySchema, AgentOverrideConfigSchema).optional(),
|
|
115
|
+
disabled_mcps: z.array(z.string()).optional(),
|
|
116
|
+
fallback: FailoverConfigSchema.optional(),
|
|
117
|
+
codex: CodexGenerationConfigSchema.optional(),
|
|
118
|
+
claudeCode: ClaudeCodeGenerationConfigSchema.optional()
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// src/opencode/profiles/types.ts
|
|
122
|
+
var PROFILE_ROLES = AgentNameSchema.options;
|
|
123
|
+
|
|
22
124
|
// src/config/constants.ts
|
|
23
125
|
var SUBAGENT_NAMES = [
|
|
24
126
|
"explorer",
|
|
@@ -88,6 +190,302 @@ function getDefaultOpenCodeVariant(name) {
|
|
|
88
190
|
var DEFAULT_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
89
191
|
var MAX_POLL_TIME_MS = 5 * 60 * 1e3;
|
|
90
192
|
|
|
193
|
+
// src/opencode/profiles/resolver.ts
|
|
194
|
+
function configuredModelChain(config) {
|
|
195
|
+
const models = config?.model;
|
|
196
|
+
return (typeof models === "string" ? [models] : models ?? []).map(
|
|
197
|
+
(entry) => {
|
|
198
|
+
const value = typeof entry === "string" ? { id: entry } : { ...entry };
|
|
199
|
+
const variant = Array.isArray(models) ? value.variant : config?.variant;
|
|
200
|
+
return { ...value, ...variant ? { variant } : {} };
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
function resolveModelPair(config, inherited = {}) {
|
|
205
|
+
const first = configuredModelChain(config)[0];
|
|
206
|
+
const model = first?.id ?? inherited.model;
|
|
207
|
+
const variant = config?.model !== void 0 ? first?.variant : config?.variant ?? inherited.variant;
|
|
208
|
+
return { ...model ? { model } : {}, ...variant ? { variant } : {} };
|
|
209
|
+
}
|
|
210
|
+
function resolveRoleProfile(role, profile, roster) {
|
|
211
|
+
return resolveModelPair(profile.agents[role], resolveModelPair(roster[role]));
|
|
212
|
+
}
|
|
213
|
+
function defaultModelProfile(roster) {
|
|
214
|
+
return {
|
|
215
|
+
agents: Object.fromEntries(
|
|
216
|
+
PROFILE_ROLES.map((role) => [
|
|
217
|
+
role,
|
|
218
|
+
resolveRoleProfile(role, { agents: {} }, roster)
|
|
219
|
+
])
|
|
220
|
+
)
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function mergeProfileModelChain(profile, configured) {
|
|
224
|
+
const entries = profile.model ? [
|
|
225
|
+
{
|
|
226
|
+
id: profile.model,
|
|
227
|
+
...profile.variant ? { variant: profile.variant } : {}
|
|
228
|
+
},
|
|
229
|
+
...configured
|
|
230
|
+
] : configured;
|
|
231
|
+
const seen = /* @__PURE__ */ new Set();
|
|
232
|
+
return entries.filter(({ id }) => {
|
|
233
|
+
if (seen.has(id)) return false;
|
|
234
|
+
seen.add(id);
|
|
235
|
+
return true;
|
|
236
|
+
}).map((entry) => ({ ...entry }));
|
|
237
|
+
}
|
|
238
|
+
function formatModelString(profile) {
|
|
239
|
+
return profile.model ? `${profile.model}${profile.variant ? `#${profile.variant}` : ""}` : void 0;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// src/opencode/profiles/store.ts
|
|
243
|
+
import { randomUUID } from "crypto";
|
|
244
|
+
import fs from "fs";
|
|
245
|
+
import os from "os";
|
|
246
|
+
import path from "path";
|
|
247
|
+
import { isDeepStrictEqual } from "util";
|
|
248
|
+
var DEFAULT_PROFILE = "default";
|
|
249
|
+
function modelProfilePaths(options) {
|
|
250
|
+
if (options.paths) return { ...options.paths };
|
|
251
|
+
const env = options.env ?? process.env;
|
|
252
|
+
const configDir = options.configDir ?? (env.OPENCODE_CONFIG_DIR?.trim() || path.join(
|
|
253
|
+
env.XDG_CONFIG_HOME || path.join(options.homeDir ?? os.homedir(), ".config"),
|
|
254
|
+
"opencode"
|
|
255
|
+
));
|
|
256
|
+
return {
|
|
257
|
+
global: path.join(configDir, "thoth", "profiles.json"),
|
|
258
|
+
project: path.join(options.cwd, ".opencode", "thoth", "profiles.json")
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function object(value) {
|
|
262
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
263
|
+
}
|
|
264
|
+
function validateProfileName(value, writable = false) {
|
|
265
|
+
if (typeof value !== "string" || !value.trim())
|
|
266
|
+
throw new Error("Profile name must be non-empty");
|
|
267
|
+
const name = value.trim();
|
|
268
|
+
if (writable && name === DEFAULT_PROFILE)
|
|
269
|
+
throw new Error("default is a reserved read-only virtual profile");
|
|
270
|
+
return name;
|
|
271
|
+
}
|
|
272
|
+
function parseRoleProfile(value) {
|
|
273
|
+
if (!object(value)) throw new Error("Role override must be an object");
|
|
274
|
+
if (value.model !== void 0 && (typeof value.model !== "string" || !/^[^/\s#]+\/[^\s#]+$/.test(value.model)))
|
|
275
|
+
throw new Error("Invalid model; expected provider/id without #variant");
|
|
276
|
+
if (value.variant !== void 0 && (typeof value.variant !== "string" || !/^[^\s#]+$/.test(value.variant)))
|
|
277
|
+
throw new Error(
|
|
278
|
+
"Invalid variant; expected a non-empty string without whitespace or #"
|
|
279
|
+
);
|
|
280
|
+
return {
|
|
281
|
+
...value.model !== void 0 ? { model: value.model } : {},
|
|
282
|
+
...value.variant !== void 0 ? { variant: value.variant } : {}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function parseNamedModelProfile(value) {
|
|
286
|
+
if (!object(value) || !object(value.agents))
|
|
287
|
+
throw new Error("Profile must contain an agents object");
|
|
288
|
+
const agents = {};
|
|
289
|
+
for (const [role, override] of Object.entries(value.agents)) {
|
|
290
|
+
const canonical = PROFILE_ROLES.find((name) => name === role);
|
|
291
|
+
if (!canonical) throw new Error(`Unknown profile role: ${role}`);
|
|
292
|
+
agents[canonical] = parseRoleProfile(override);
|
|
293
|
+
}
|
|
294
|
+
return { agents };
|
|
295
|
+
}
|
|
296
|
+
function parseModelProfilesDocument(raw) {
|
|
297
|
+
if (!object(raw) || raw.version !== 1 || !object(raw.profiles))
|
|
298
|
+
throw new Error("Expected version 1 and a profiles object");
|
|
299
|
+
const profiles = /* @__PURE__ */ Object.create(null);
|
|
300
|
+
for (const [key, value] of Object.entries(raw.profiles)) {
|
|
301
|
+
const name = validateProfileName(key, true);
|
|
302
|
+
if (Object.hasOwn(profiles, name))
|
|
303
|
+
throw new Error(`Duplicate profile: ${name}`);
|
|
304
|
+
profiles[name] = parseNamedModelProfile(value);
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
version: 1,
|
|
308
|
+
...raw.active !== void 0 ? { active: validateProfileName(raw.active) } : {},
|
|
309
|
+
profiles
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
var emptyDocument = () => ({
|
|
313
|
+
version: 1,
|
|
314
|
+
profiles: /* @__PURE__ */ Object.create(null)
|
|
315
|
+
});
|
|
316
|
+
var ProfileConflictError = class extends Error {
|
|
317
|
+
constructor(scope, profileName) {
|
|
318
|
+
super(
|
|
319
|
+
`Profile changed on disk: ${scope}:${profileName}; reload before saving.`
|
|
320
|
+
);
|
|
321
|
+
this.scope = scope;
|
|
322
|
+
this.profileName = profileName;
|
|
323
|
+
this.name = "ProfileConflictError";
|
|
324
|
+
}
|
|
325
|
+
scope;
|
|
326
|
+
profileName;
|
|
327
|
+
code = "PROFILE_CONFLICT";
|
|
328
|
+
};
|
|
329
|
+
var ModelProfileStore = class {
|
|
330
|
+
constructor(options) {
|
|
331
|
+
this.options = options;
|
|
332
|
+
this.paths = modelProfilePaths(options);
|
|
333
|
+
}
|
|
334
|
+
options;
|
|
335
|
+
paths;
|
|
336
|
+
defaultProfile() {
|
|
337
|
+
return defaultModelProfile(this.options.roster ?? {});
|
|
338
|
+
}
|
|
339
|
+
load(scope) {
|
|
340
|
+
try {
|
|
341
|
+
return parseModelProfilesDocument(
|
|
342
|
+
JSON.parse(fs.readFileSync(this.paths[scope], "utf8"))
|
|
343
|
+
);
|
|
344
|
+
} catch (error) {
|
|
345
|
+
if (error.code === "ENOENT")
|
|
346
|
+
return emptyDocument();
|
|
347
|
+
throw new Error(
|
|
348
|
+
`Cannot read model profiles ${this.paths[scope]}: ${String(error)}`,
|
|
349
|
+
{ cause: error }
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
read(scope, warnings = []) {
|
|
354
|
+
try {
|
|
355
|
+
return this.load(scope);
|
|
356
|
+
} catch (error) {
|
|
357
|
+
const warning = String(error);
|
|
358
|
+
warnings.push(warning);
|
|
359
|
+
this.options.warn?.(warning);
|
|
360
|
+
return emptyDocument();
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
lookup(name, scope) {
|
|
364
|
+
const key = validateProfileName(name);
|
|
365
|
+
if (key === DEFAULT_PROFILE) return { profile: this.defaultProfile() };
|
|
366
|
+
for (const source of scope ? [scope] : ["project", "global"]) {
|
|
367
|
+
const profiles = this.read(source).profiles;
|
|
368
|
+
if (Object.hasOwn(profiles, key))
|
|
369
|
+
return { profile: profiles[key], scope: source };
|
|
370
|
+
}
|
|
371
|
+
return void 0;
|
|
372
|
+
}
|
|
373
|
+
write(scope, document) {
|
|
374
|
+
const validated = parseModelProfilesDocument(document);
|
|
375
|
+
const file = this.paths[scope];
|
|
376
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
377
|
+
const temp = `${file}.${randomUUID()}.tmp`;
|
|
378
|
+
try {
|
|
379
|
+
fs.writeFileSync(temp, `${JSON.stringify(validated, null, 2)}
|
|
380
|
+
`, {
|
|
381
|
+
encoding: "utf8",
|
|
382
|
+
flag: "wx"
|
|
383
|
+
});
|
|
384
|
+
fs.renameSync(temp, file);
|
|
385
|
+
} finally {
|
|
386
|
+
if (fs.existsSync(temp)) fs.unlinkSync(temp);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/** Re-read before writing. An expected baseline enables same-profile conflict detection. */
|
|
390
|
+
save(scope, name, value, options = {}) {
|
|
391
|
+
const key = validateProfileName(name, true);
|
|
392
|
+
const next = parseNamedModelProfile(value);
|
|
393
|
+
const document = this.load(scope);
|
|
394
|
+
const existing = Object.hasOwn(document.profiles, key) ? document.profiles[key] : null;
|
|
395
|
+
if (Object.hasOwn(options, "expected") && !isDeepStrictEqual(existing, options.expected))
|
|
396
|
+
throw new ProfileConflictError(scope, key);
|
|
397
|
+
document.profiles[key] = next;
|
|
398
|
+
this.write(scope, document);
|
|
399
|
+
}
|
|
400
|
+
create(scope, name, value) {
|
|
401
|
+
const key = validateProfileName(name, true);
|
|
402
|
+
if (Object.hasOwn(this.load(scope).profiles, key))
|
|
403
|
+
throw new Error(`Profile already exists: ${key}`);
|
|
404
|
+
this.save(scope, key, value, { expected: null });
|
|
405
|
+
}
|
|
406
|
+
/** Pins that actually resolved to a global profile follow rename/delete. */
|
|
407
|
+
projectPointsToGlobal(name) {
|
|
408
|
+
const project = this.load("project");
|
|
409
|
+
return project.active === name && !Object.hasOwn(project.profiles, name);
|
|
410
|
+
}
|
|
411
|
+
rename(scope, source, target) {
|
|
412
|
+
const from = validateProfileName(source, true);
|
|
413
|
+
const to = validateProfileName(target, true);
|
|
414
|
+
const document = this.load(scope);
|
|
415
|
+
if (!Object.hasOwn(document.profiles, from))
|
|
416
|
+
throw new Error(`Profile not found: ${from}`);
|
|
417
|
+
if (Object.hasOwn(document.profiles, to))
|
|
418
|
+
throw new Error(`Profile already exists: ${to}`);
|
|
419
|
+
const followPin = scope === "global" && this.projectPointsToGlobal(from);
|
|
420
|
+
document.profiles[to] = document.profiles[from];
|
|
421
|
+
delete document.profiles[from];
|
|
422
|
+
if (document.active === from) document.active = to;
|
|
423
|
+
this.write(scope, document);
|
|
424
|
+
if (followPin) this.setProjectPin(to);
|
|
425
|
+
}
|
|
426
|
+
delete(scope, name) {
|
|
427
|
+
const key = validateProfileName(name, true);
|
|
428
|
+
const document = this.load(scope);
|
|
429
|
+
if (!Object.hasOwn(document.profiles, key))
|
|
430
|
+
throw new Error(`Profile not found: ${key}`);
|
|
431
|
+
const clearPin = scope === "global" && this.projectPointsToGlobal(key);
|
|
432
|
+
delete document.profiles[key];
|
|
433
|
+
const clearedActive = document.active === key;
|
|
434
|
+
if (clearedActive) delete document.active;
|
|
435
|
+
this.write(scope, document);
|
|
436
|
+
if (clearPin) this.setProjectPin();
|
|
437
|
+
return { clearedActive };
|
|
438
|
+
}
|
|
439
|
+
setActive(scope, name) {
|
|
440
|
+
const document = this.load(scope);
|
|
441
|
+
if (name === void 0) delete document.active;
|
|
442
|
+
else {
|
|
443
|
+
const key = validateProfileName(name);
|
|
444
|
+
if (key !== DEFAULT_PROFILE && !(scope === "global" ? Object.hasOwn(document.profiles, key) : this.lookup(key)))
|
|
445
|
+
throw new Error(`Profile not found: ${key}`);
|
|
446
|
+
document.active = key;
|
|
447
|
+
}
|
|
448
|
+
this.write(scope, document);
|
|
449
|
+
}
|
|
450
|
+
setGlobalActive(name) {
|
|
451
|
+
this.setActive("global", name);
|
|
452
|
+
}
|
|
453
|
+
setProjectPin(name) {
|
|
454
|
+
this.setActive("project", name);
|
|
455
|
+
}
|
|
456
|
+
resolve(sessionSelection) {
|
|
457
|
+
const warnings = [];
|
|
458
|
+
const documents = {
|
|
459
|
+
global: this.read("global", warnings),
|
|
460
|
+
project: this.read("project", warnings)
|
|
461
|
+
};
|
|
462
|
+
for (const [level, name] of [
|
|
463
|
+
["session", sessionSelection],
|
|
464
|
+
["project", documents.project.active],
|
|
465
|
+
["global", documents.global.active]
|
|
466
|
+
]) {
|
|
467
|
+
if (!name) continue;
|
|
468
|
+
if (name === DEFAULT_PROFILE)
|
|
469
|
+
return { name, profile: this.defaultProfile(), warnings };
|
|
470
|
+
const scope = Object.hasOwn(documents.project.profiles, name) ? "project" : "global";
|
|
471
|
+
if (Object.hasOwn(documents[scope].profiles, name) && (level !== "global" || Object.hasOwn(documents.global.profiles, name)))
|
|
472
|
+
return {
|
|
473
|
+
name,
|
|
474
|
+
scope,
|
|
475
|
+
profile: documents[scope].profiles[name],
|
|
476
|
+
warnings
|
|
477
|
+
};
|
|
478
|
+
const warning = `Missing ${level} model profile "${name}"; falling back.`;
|
|
479
|
+
warnings.push(warning);
|
|
480
|
+
this.options.warn?.(warning);
|
|
481
|
+
}
|
|
482
|
+
return { name: DEFAULT_PROFILE, profile: this.defaultProfile(), warnings };
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
// src/opencode/profiles/editor.ts
|
|
487
|
+
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
488
|
+
|
|
91
489
|
// src/cli/package-version.ts
|
|
92
490
|
import { readFileSync } from "fs";
|
|
93
491
|
import { dirname as dirname2, join as join2 } from "path";
|
|
@@ -181,8 +579,8 @@ function resolveExecutingPackageVersion(options = {}) {
|
|
|
181
579
|
}
|
|
182
580
|
|
|
183
581
|
// src/config/loader.ts
|
|
184
|
-
import * as
|
|
185
|
-
import * as
|
|
582
|
+
import * as fs2 from "fs";
|
|
583
|
+
import * as path2 from "path";
|
|
186
584
|
|
|
187
585
|
// src/cli/config-io.ts
|
|
188
586
|
import {
|
|
@@ -259,111 +657,6 @@ function ensureOpenCodeConfigDir() {
|
|
|
259
657
|
}
|
|
260
658
|
}
|
|
261
659
|
|
|
262
|
-
// src/config/schema.ts
|
|
263
|
-
import { z } from "zod";
|
|
264
|
-
var AGENT_NAMES = [
|
|
265
|
-
"orchestrator",
|
|
266
|
-
"oracle",
|
|
267
|
-
"designer",
|
|
268
|
-
"explorer",
|
|
269
|
-
"librarian",
|
|
270
|
-
"worker"
|
|
271
|
-
];
|
|
272
|
-
var FALLBACK_AGENT_NAMES = [...AGENT_NAMES];
|
|
273
|
-
var MANUAL_AGENT_NAMES = [...AGENT_NAMES];
|
|
274
|
-
var ProviderModelIdSchema = z.string().regex(
|
|
275
|
-
/^[^/\s]+\/[^\s]+$/,
|
|
276
|
-
"Expected provider/model format (provider/.../model)"
|
|
277
|
-
);
|
|
278
|
-
var ManualAgentPlanSchema = z.object({
|
|
279
|
-
primary: ProviderModelIdSchema,
|
|
280
|
-
fallback1: ProviderModelIdSchema,
|
|
281
|
-
fallback2: ProviderModelIdSchema,
|
|
282
|
-
fallback3: ProviderModelIdSchema
|
|
283
|
-
}).superRefine((value, ctx) => {
|
|
284
|
-
const unique = /* @__PURE__ */ new Set([
|
|
285
|
-
value.primary,
|
|
286
|
-
value.fallback1,
|
|
287
|
-
value.fallback2,
|
|
288
|
-
value.fallback3
|
|
289
|
-
]);
|
|
290
|
-
if (unique.size !== 4) {
|
|
291
|
-
ctx.addIssue({
|
|
292
|
-
code: z.ZodIssueCode.custom,
|
|
293
|
-
message: "primary and fallbacks must be unique per agent"
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
var ManualPlanSchema = z.object({
|
|
298
|
-
orchestrator: ManualAgentPlanSchema,
|
|
299
|
-
oracle: ManualAgentPlanSchema,
|
|
300
|
-
designer: ManualAgentPlanSchema,
|
|
301
|
-
explorer: ManualAgentPlanSchema,
|
|
302
|
-
librarian: ManualAgentPlanSchema,
|
|
303
|
-
worker: ManualAgentPlanSchema
|
|
304
|
-
}).strict();
|
|
305
|
-
var AgentModelChainSchema = z.array(z.string()).min(1);
|
|
306
|
-
var SupportedAgentRecordKeySchema = z.string().regex(/^(?!quick$|deep$).+$/, "Unsupported removed agent role");
|
|
307
|
-
var FallbackChainsSchema = z.record(
|
|
308
|
-
SupportedAgentRecordKeySchema,
|
|
309
|
-
AgentModelChainSchema
|
|
310
|
-
);
|
|
311
|
-
var AgentOverrideConfigSchema = z.object({
|
|
312
|
-
model: z.union([
|
|
313
|
-
z.string(),
|
|
314
|
-
z.array(
|
|
315
|
-
z.union([
|
|
316
|
-
z.string(),
|
|
317
|
-
z.object({
|
|
318
|
-
id: z.string(),
|
|
319
|
-
variant: z.string().optional()
|
|
320
|
-
})
|
|
321
|
-
])
|
|
322
|
-
)
|
|
323
|
-
]).optional(),
|
|
324
|
-
/** Pi specialist catalog filter; omitted discovers all, [] discovers none. */
|
|
325
|
-
skills: z.array(z.string().trim().min(1)).optional(),
|
|
326
|
-
temperature: z.number().min(0).max(2).optional(),
|
|
327
|
-
steps: z.number().int().min(1).optional(),
|
|
328
|
-
variant: z.string().optional().catch(void 0)
|
|
329
|
-
});
|
|
330
|
-
var PresetSchema = z.record(
|
|
331
|
-
SupportedAgentRecordKeySchema,
|
|
332
|
-
AgentOverrideConfigSchema
|
|
333
|
-
);
|
|
334
|
-
var AgentNameSchema = z.enum(AGENT_NAMES);
|
|
335
|
-
var McpNameSchema = z.enum(["exa", "context7", "grep_app"]);
|
|
336
|
-
var CodexGenerationConfigSchema = z.object({
|
|
337
|
-
enabled: z.boolean().default(false),
|
|
338
|
-
outputRoot: z.string().optional(),
|
|
339
|
-
dryRun: z.boolean().default(true)
|
|
340
|
-
});
|
|
341
|
-
var ClaudeCodeGenerationConfigSchema = z.object({
|
|
342
|
-
enabled: z.boolean().default(false),
|
|
343
|
-
outputRoot: z.string().optional(),
|
|
344
|
-
dryRun: z.boolean().default(true)
|
|
345
|
-
});
|
|
346
|
-
var FailoverConfigSchema = z.object({
|
|
347
|
-
enabled: z.boolean().default(true),
|
|
348
|
-
timeoutMs: z.number().min(0).default(15e3),
|
|
349
|
-
retryDelayMs: z.number().min(0).default(500),
|
|
350
|
-
chains: FallbackChainsSchema.default({})
|
|
351
|
-
});
|
|
352
|
-
var PluginConfigSchema = z.object({
|
|
353
|
-
$schema: z.string().optional(),
|
|
354
|
-
preset: z.string().optional(),
|
|
355
|
-
setDefaultAgent: z.boolean().optional(),
|
|
356
|
-
scoringEngineVersion: z.enum(["v1", "v2-shadow", "v2"]).optional(),
|
|
357
|
-
balanceProviderUsage: z.boolean().optional(),
|
|
358
|
-
manualPlan: ManualPlanSchema.optional(),
|
|
359
|
-
presets: z.record(z.string(), PresetSchema).optional(),
|
|
360
|
-
agents: z.record(SupportedAgentRecordKeySchema, AgentOverrideConfigSchema).optional(),
|
|
361
|
-
disabled_mcps: z.array(z.string()).optional(),
|
|
362
|
-
fallback: FailoverConfigSchema.optional(),
|
|
363
|
-
codex: CodexGenerationConfigSchema.optional(),
|
|
364
|
-
claudeCode: ClaudeCodeGenerationConfigSchema.optional()
|
|
365
|
-
});
|
|
366
|
-
|
|
367
660
|
// src/config/utils.ts
|
|
368
661
|
function getAgentOverride(config, name) {
|
|
369
662
|
return config?.agents?.[name];
|
|
@@ -399,28 +692,74 @@ var MODEL_MAPPINGS = {
|
|
|
399
692
|
openai: buildOpenAIModelMappings()
|
|
400
693
|
};
|
|
401
694
|
function generateLiteConfig(_installConfig) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
695
|
+
return { $schema: THOTH_AGENTS_CONFIG_SCHEMA_URL };
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// src/cli/opencode-profiles.ts
|
|
699
|
+
function openCodeProfileStore(roster) {
|
|
700
|
+
return new ModelProfileStore({
|
|
701
|
+
cwd: process.cwd(),
|
|
702
|
+
configDir: getConfigDir(),
|
|
703
|
+
roster
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
function activeOpenCodeProfile() {
|
|
707
|
+
const store = openCodeProfileStore();
|
|
708
|
+
const document = store.read("global");
|
|
709
|
+
return {
|
|
710
|
+
name: document.active,
|
|
711
|
+
profile: document.active ? store.lookup(document.active, "global")?.profile : void 0
|
|
412
712
|
};
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
713
|
+
}
|
|
714
|
+
function effectiveOpenCodeProfile(config) {
|
|
715
|
+
const root = config?.agents ?? {};
|
|
716
|
+
const roster = Object.fromEntries(
|
|
717
|
+
PROFILE_ROLES.map((role) => [
|
|
718
|
+
role,
|
|
719
|
+
resolveModelPair(root[role], MODEL_MAPPINGS.openai[role])
|
|
720
|
+
])
|
|
721
|
+
);
|
|
722
|
+
const selected = activeOpenCodeProfile().profile ?? { agents: {} };
|
|
723
|
+
return {
|
|
724
|
+
agents: Object.fromEntries(
|
|
725
|
+
PROFILE_ROLES.map((role) => [
|
|
726
|
+
role,
|
|
727
|
+
resolveRoleProfile(role, selected, roster)
|
|
419
728
|
])
|
|
420
|
-
)
|
|
729
|
+
)
|
|
421
730
|
};
|
|
422
|
-
|
|
423
|
-
|
|
731
|
+
}
|
|
732
|
+
function syncOpenCodeProfile(reset = false) {
|
|
733
|
+
const store = openCodeProfileStore();
|
|
734
|
+
const document = store.read("global");
|
|
735
|
+
const names = ["openai"];
|
|
736
|
+
if (document.active === "agents" && document.profiles.agents)
|
|
737
|
+
names.push("agents");
|
|
738
|
+
for (const name of names) {
|
|
739
|
+
const previous = document.profiles[name]?.agents ?? {};
|
|
740
|
+
const agents = Object.fromEntries(
|
|
741
|
+
PROFILE_ROLES.map((role) => {
|
|
742
|
+
const current = previous[role];
|
|
743
|
+
const legacy = LEGACY_OPENAI_PRESET[role];
|
|
744
|
+
const advance = current?.model === `openai/${legacy.model}` && current?.variant === legacy.effort;
|
|
745
|
+
return [
|
|
746
|
+
role,
|
|
747
|
+
reset && name === "openai" || advance ? MODEL_MAPPINGS.openai[role] : resolveRoleProfile(
|
|
748
|
+
role,
|
|
749
|
+
{ agents: previous },
|
|
750
|
+
MODEL_MAPPINGS.openai
|
|
751
|
+
)
|
|
752
|
+
];
|
|
753
|
+
})
|
|
754
|
+
);
|
|
755
|
+
store.save("global", name, { agents });
|
|
756
|
+
}
|
|
757
|
+
if (!document.active || !store.lookup(document.active, "global"))
|
|
758
|
+
store.setGlobalActive("openai");
|
|
759
|
+
}
|
|
760
|
+
function withoutLegacyPresets(config) {
|
|
761
|
+
const { preset: _preset, presets: _presets, ...rest } = config;
|
|
762
|
+
return rest;
|
|
424
763
|
}
|
|
425
764
|
|
|
426
765
|
// src/cli/config-io.ts
|
|
@@ -436,23 +775,23 @@ function stripJsonComments(json) {
|
|
|
436
775
|
(match, comma, closing) => comma ? closing : match
|
|
437
776
|
);
|
|
438
777
|
}
|
|
439
|
-
function parseConfigFile(
|
|
778
|
+
function parseConfigFile(path3) {
|
|
440
779
|
try {
|
|
441
|
-
if (!existsSync3(
|
|
442
|
-
const stat = statSync(
|
|
780
|
+
if (!existsSync3(path3)) return { config: null };
|
|
781
|
+
const stat = statSync(path3);
|
|
443
782
|
if (stat.size === 0) return { config: null };
|
|
444
|
-
const content = readFileSync2(
|
|
783
|
+
const content = readFileSync2(path3, "utf-8");
|
|
445
784
|
if (content.trim().length === 0) return { config: null };
|
|
446
785
|
return { config: JSON.parse(stripJsonComments(content)) };
|
|
447
786
|
} catch (err) {
|
|
448
787
|
return { config: null, error: String(err) };
|
|
449
788
|
}
|
|
450
789
|
}
|
|
451
|
-
function parseConfig(
|
|
452
|
-
const result = parseConfigFile(
|
|
790
|
+
function parseConfig(path3) {
|
|
791
|
+
const result = parseConfigFile(path3);
|
|
453
792
|
if (result.config || result.error) return result;
|
|
454
|
-
if (
|
|
455
|
-
const jsoncPath =
|
|
793
|
+
if (path3.endsWith(".json")) {
|
|
794
|
+
const jsoncPath = path3.replace(/\.json$/, ".jsonc");
|
|
456
795
|
return parseConfigFile(jsoncPath);
|
|
457
796
|
}
|
|
458
797
|
return { config: null };
|
|
@@ -556,43 +895,6 @@ function configObject(value) {
|
|
|
556
895
|
throw new Error("Expected an object in managed lite configuration.");
|
|
557
896
|
return value;
|
|
558
897
|
}
|
|
559
|
-
function mergeLiteConfig(defaults, installed) {
|
|
560
|
-
const presets = { ...configObject(installed.presets) };
|
|
561
|
-
const shipped = configObject(configObject(defaults.presets).openai);
|
|
562
|
-
const activeName = typeof installed.preset === "string" ? installed.preset : "openai";
|
|
563
|
-
const rootAgents = { ...configObject(installed.agents) };
|
|
564
|
-
for (const name of /* @__PURE__ */ new Set(["openai", activeName])) {
|
|
565
|
-
const roles = { ...configObject(presets[name]) };
|
|
566
|
-
for (const [role, legacy] of Object.entries(LEGACY_OPENAI_PRESET)) {
|
|
567
|
-
const rootRole = name === activeName ? rootAgents[role] : void 0;
|
|
568
|
-
const current = {
|
|
569
|
-
...configObject(roles[role]),
|
|
570
|
-
...configObject(rootRole)
|
|
571
|
-
};
|
|
572
|
-
const advancesLegacy = current.model === `openai/${legacy.model}` && current.variant === legacy.effort;
|
|
573
|
-
if (roles[role] === void 0 && rootRole === void 0) {
|
|
574
|
-
roles[role] = shipped[role];
|
|
575
|
-
} else if (advancesLegacy) {
|
|
576
|
-
roles[role] = { ...current, ...configObject(shipped[role]) };
|
|
577
|
-
if (rootRole !== void 0)
|
|
578
|
-
rootAgents[role] = {
|
|
579
|
-
...configObject(rootRole),
|
|
580
|
-
...configObject(shipped[role])
|
|
581
|
-
};
|
|
582
|
-
} else {
|
|
583
|
-
roles[role] = current;
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
presets[name] = roles;
|
|
587
|
-
}
|
|
588
|
-
return {
|
|
589
|
-
...defaults,
|
|
590
|
-
...installed,
|
|
591
|
-
preset: activeName,
|
|
592
|
-
presets,
|
|
593
|
-
...installed.agents !== void 0 ? { agents: rootAgents } : {}
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
898
|
function writeLiteConfig(installConfig, targetPath) {
|
|
597
899
|
const configPath = targetPath ?? getLiteConfig();
|
|
598
900
|
try {
|
|
@@ -601,8 +903,10 @@ function writeLiteConfig(installConfig, targetPath) {
|
|
|
601
903
|
if (!installConfig.reset && existsSync3(configPath)) {
|
|
602
904
|
const { config: installed, error } = parseConfigFile(configPath);
|
|
603
905
|
if (error) throw new Error(`Failed to parse lite config: ${error}`);
|
|
604
|
-
if (installed) config =
|
|
906
|
+
if (installed) config = { ...config, ...configObject(installed) };
|
|
605
907
|
}
|
|
908
|
+
config = withoutLegacyPresets(config);
|
|
909
|
+
syncOpenCodeProfile(installConfig.reset);
|
|
606
910
|
const tmpPath = `${configPath}.tmp`;
|
|
607
911
|
const bakPath = `${configPath}.bak`;
|
|
608
912
|
const content = `${JSON.stringify(config, null, 2)}
|
|
@@ -635,9 +939,7 @@ function detectCurrentConfig() {
|
|
|
635
939
|
const { config: liteConfig } = parseConfig(getLiteConfig());
|
|
636
940
|
if (liteConfig && typeof liteConfig === "object") {
|
|
637
941
|
const configObj = liteConfig;
|
|
638
|
-
const
|
|
639
|
-
const presets = configObj.presets;
|
|
640
|
-
const agents = presets?.[presetName];
|
|
942
|
+
const agents = effectiveOpenCodeProfile(configObj).agents;
|
|
641
943
|
if (agents) {
|
|
642
944
|
const models = Object.values(agents).map((a) => a?.model).filter(Boolean);
|
|
643
945
|
result.hasOpenAI = models.some((m) => m?.startsWith("openai/"));
|
|
@@ -648,10 +950,13 @@ function detectCurrentConfig() {
|
|
|
648
950
|
|
|
649
951
|
// src/config/loader.ts
|
|
650
952
|
var PROMPTS_DIR_NAME = "thoth-agents";
|
|
651
|
-
function loadConfigFromPath(configPath) {
|
|
953
|
+
function loadConfigFromPath(configPath, unsupportedKeyPaths) {
|
|
652
954
|
try {
|
|
653
|
-
const content =
|
|
955
|
+
const content = fs2.readFileSync(configPath, "utf-8");
|
|
654
956
|
const rawConfig = JSON.parse(stripJsonComments(content));
|
|
957
|
+
if (rawConfig !== null && typeof rawConfig === "object" && ("preset" in rawConfig || "presets" in rawConfig)) {
|
|
958
|
+
unsupportedKeyPaths.add(configPath);
|
|
959
|
+
}
|
|
655
960
|
const result = PluginConfigSchema.safeParse(rawConfig);
|
|
656
961
|
if (!result.success) {
|
|
657
962
|
console.warn(`[thoth-agents] Invalid config at ${configPath}:`);
|
|
@@ -672,10 +977,10 @@ function loadConfigFromPath(configPath) {
|
|
|
672
977
|
function findConfigPath(basePath) {
|
|
673
978
|
const jsoncPath = `${basePath}.jsonc`;
|
|
674
979
|
const jsonPath = `${basePath}.json`;
|
|
675
|
-
if (
|
|
980
|
+
if (fs2.existsSync(jsoncPath)) {
|
|
676
981
|
return jsoncPath;
|
|
677
982
|
}
|
|
678
|
-
if (
|
|
983
|
+
if (fs2.existsSync(jsonPath)) {
|
|
679
984
|
return jsonPath;
|
|
680
985
|
}
|
|
681
986
|
return null;
|
|
@@ -699,16 +1004,17 @@ function deepMerge(base, override) {
|
|
|
699
1004
|
return result;
|
|
700
1005
|
}
|
|
701
1006
|
function loadPluginConfig(directory) {
|
|
702
|
-
const userConfigBasePath =
|
|
703
|
-
const projectConfigBasePath =
|
|
1007
|
+
const userConfigBasePath = path2.join(getConfigDir(), "thoth-agents");
|
|
1008
|
+
const projectConfigBasePath = path2.join(
|
|
704
1009
|
directory,
|
|
705
1010
|
".opencode",
|
|
706
1011
|
"thoth-agents"
|
|
707
1012
|
);
|
|
708
1013
|
const userConfigPath = findConfigPath(userConfigBasePath);
|
|
709
1014
|
const projectConfigPath = findConfigPath(projectConfigBasePath);
|
|
710
|
-
|
|
711
|
-
|
|
1015
|
+
const unsupportedKeyPaths = /* @__PURE__ */ new Set();
|
|
1016
|
+
let config = userConfigPath ? loadConfigFromPath(userConfigPath, unsupportedKeyPaths) ?? {} : {};
|
|
1017
|
+
const projectConfig = projectConfigPath ? loadConfigFromPath(projectConfigPath, unsupportedKeyPaths) : null;
|
|
712
1018
|
if (projectConfig) {
|
|
713
1019
|
config = {
|
|
714
1020
|
...config,
|
|
@@ -719,51 +1025,31 @@ function loadPluginConfig(directory) {
|
|
|
719
1025
|
claudeCode: deepMerge(config.claudeCode, projectConfig.claudeCode)
|
|
720
1026
|
};
|
|
721
1027
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
if (config.preset) {
|
|
727
|
-
const preset = config.presets?.[config.preset];
|
|
728
|
-
if (preset) {
|
|
729
|
-
config.agents = deepMerge(preset, config.agents);
|
|
730
|
-
} else {
|
|
731
|
-
const presetSource = envPreset === config.preset ? "environment variable" : "config file";
|
|
732
|
-
const availablePresets = config.presets ? Object.keys(config.presets).join(", ") : "none";
|
|
733
|
-
console.warn(
|
|
734
|
-
`[thoth-agents] Preset "${config.preset}" not found (from ${presetSource}). Available presets: ${availablePresets}`
|
|
735
|
-
);
|
|
736
|
-
}
|
|
1028
|
+
if (unsupportedKeyPaths.size > 0) {
|
|
1029
|
+
console.warn(
|
|
1030
|
+
`[thoth-agents] Config keys preset/presets are unsupported and ignored (${[...unsupportedKeyPaths].join(", ")}).`
|
|
1031
|
+
);
|
|
737
1032
|
}
|
|
738
1033
|
return config;
|
|
739
1034
|
}
|
|
740
|
-
function loadAgentPrompt(agentName
|
|
741
|
-
const
|
|
742
|
-
const promptsDir = path.join(getConfigDir(), PROMPTS_DIR_NAME);
|
|
743
|
-
const promptSearchDirs = presetDirName ? [path.join(promptsDir, presetDirName), promptsDir] : [promptsDir];
|
|
1035
|
+
function loadAgentPrompt(agentName) {
|
|
1036
|
+
const promptsDir = path2.join(getConfigDir(), PROMPTS_DIR_NAME);
|
|
744
1037
|
const result = {};
|
|
745
|
-
const
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
error instanceof Error ? error.message : String(error)
|
|
757
|
-
);
|
|
758
|
-
}
|
|
1038
|
+
const readPrompt = (fileName, errorPrefix) => {
|
|
1039
|
+
const promptPath = path2.join(promptsDir, fileName);
|
|
1040
|
+
if (!fs2.existsSync(promptPath)) return void 0;
|
|
1041
|
+
try {
|
|
1042
|
+
return fs2.readFileSync(promptPath, "utf-8");
|
|
1043
|
+
} catch (error) {
|
|
1044
|
+
console.warn(
|
|
1045
|
+
`[thoth-agents] ${errorPrefix} ${promptPath}:`,
|
|
1046
|
+
error instanceof Error ? error.message : String(error)
|
|
1047
|
+
);
|
|
1048
|
+
return void 0;
|
|
759
1049
|
}
|
|
760
|
-
return void 0;
|
|
761
1050
|
};
|
|
762
|
-
result.prompt =
|
|
763
|
-
|
|
764
|
-
"Error reading prompt file"
|
|
765
|
-
);
|
|
766
|
-
result.appendPrompt = readFirstPrompt(
|
|
1051
|
+
result.prompt = readPrompt(`${agentName}.md`, "Error reading prompt file");
|
|
1052
|
+
result.appendPrompt = readPrompt(
|
|
767
1053
|
`${agentName}_append.md`,
|
|
768
1054
|
"Error reading append prompt file"
|
|
769
1055
|
);
|
|
@@ -774,7 +1060,7 @@ function loadAgentPrompt(agentName, preset) {
|
|
|
774
1060
|
var text = { type: "string" };
|
|
775
1061
|
var texts = { type: "array", items: text };
|
|
776
1062
|
var nullableText = { type: ["string", "null"] };
|
|
777
|
-
function
|
|
1063
|
+
function object2(properties) {
|
|
778
1064
|
return {
|
|
779
1065
|
type: "object",
|
|
780
1066
|
properties,
|
|
@@ -785,7 +1071,7 @@ function object(properties) {
|
|
|
785
1071
|
function array(items) {
|
|
786
1072
|
return { type: "array", items };
|
|
787
1073
|
}
|
|
788
|
-
var common =
|
|
1074
|
+
var common = object2({
|
|
789
1075
|
schemaVersion: { type: "number", const: 1 },
|
|
790
1076
|
role: {
|
|
791
1077
|
type: "string",
|
|
@@ -797,7 +1083,7 @@ var common = object({
|
|
|
797
1083
|
},
|
|
798
1084
|
conclusion: text,
|
|
799
1085
|
evidence: array(
|
|
800
|
-
|
|
1086
|
+
object2({
|
|
801
1087
|
id: { type: "string", minLength: 1 },
|
|
802
1088
|
kind: text,
|
|
803
1089
|
locator: text,
|
|
@@ -807,7 +1093,7 @@ var common = object({
|
|
|
807
1093
|
})
|
|
808
1094
|
),
|
|
809
1095
|
verification: array(
|
|
810
|
-
|
|
1096
|
+
object2({
|
|
811
1097
|
check: text,
|
|
812
1098
|
method: text,
|
|
813
1099
|
outcome: { type: "string", enum: ["pass", "fail", "not-run"] },
|
|
@@ -816,7 +1102,7 @@ var common = object({
|
|
|
816
1102
|
})
|
|
817
1103
|
),
|
|
818
1104
|
risks: array(
|
|
819
|
-
|
|
1105
|
+
object2({
|
|
820
1106
|
description: text,
|
|
821
1107
|
severity: { type: "string", enum: ["low", "medium", "high", "critical"] },
|
|
822
1108
|
blocking: { type: "boolean" },
|
|
@@ -824,11 +1110,11 @@ var common = object({
|
|
|
824
1110
|
})
|
|
825
1111
|
),
|
|
826
1112
|
openQuestions: array(
|
|
827
|
-
|
|
1113
|
+
object2({ question: text, owner: text, options: texts, facts: texts })
|
|
828
1114
|
)
|
|
829
1115
|
});
|
|
830
1116
|
var review = {
|
|
831
|
-
...
|
|
1117
|
+
...object2({
|
|
832
1118
|
kind: {
|
|
833
1119
|
type: "string",
|
|
834
1120
|
enum: ["plan-review", "final-verification"]
|
|
@@ -844,7 +1130,7 @@ var SPECIALIST_RESULT_CONTRACT = {
|
|
|
844
1130
|
fence: "thoth-result",
|
|
845
1131
|
common,
|
|
846
1132
|
roles: {
|
|
847
|
-
oracle:
|
|
1133
|
+
oracle: object2({ nextAction: nullableText, review })
|
|
848
1134
|
},
|
|
849
1135
|
reviewKinds: {
|
|
850
1136
|
"plan-review": { verdicts: ["OKAY", "REJECT"] },
|
|
@@ -2411,6 +2697,7 @@ function composeAgentPrompt({
|
|
|
2411
2697
|
}
|
|
2412
2698
|
|
|
2413
2699
|
export {
|
|
2700
|
+
PROFILE_ROLES,
|
|
2414
2701
|
SUBAGENT_NAMES,
|
|
2415
2702
|
ALL_AGENT_NAMES,
|
|
2416
2703
|
LEGACY_OPENAI_PRESET,
|
|
@@ -2419,15 +2706,26 @@ export {
|
|
|
2419
2706
|
DEFAULT_MODELS,
|
|
2420
2707
|
getDefaultOpenCodeModel,
|
|
2421
2708
|
getDefaultOpenCodeVariant,
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2709
|
+
resolveRoleProfile,
|
|
2710
|
+
mergeProfileModelChain,
|
|
2711
|
+
formatModelString,
|
|
2712
|
+
validateProfileName,
|
|
2713
|
+
ProfileConflictError,
|
|
2714
|
+
ModelProfileStore,
|
|
2425
2715
|
getConfigDir,
|
|
2426
2716
|
getOpenCodeManagedModelStatePath,
|
|
2427
2717
|
getExistingLiteConfigPath,
|
|
2428
2718
|
getExistingConfigPath,
|
|
2429
2719
|
ensureConfigDir,
|
|
2720
|
+
MODEL_MAPPINGS,
|
|
2430
2721
|
generateLiteConfig,
|
|
2722
|
+
openCodeProfileStore,
|
|
2723
|
+
activeOpenCodeProfile,
|
|
2724
|
+
effectiveOpenCodeProfile,
|
|
2725
|
+
withoutLegacyPresets,
|
|
2726
|
+
findPackageRoot,
|
|
2727
|
+
isValidPackageVersion,
|
|
2728
|
+
resolveExecutingPackageVersion,
|
|
2431
2729
|
parseConfig,
|
|
2432
2730
|
writeConfig,
|
|
2433
2731
|
pluginEntries,
|