opencode-akane 0.1.3 → 0.1.4
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 +16 -0
- package/dist/artifacts.js +16 -0
- package/dist/config.js +16 -1
- package/dist/constants.d.ts +10 -0
- package/dist/constants.js +10 -0
- package/dist/types.d.ts +4 -0
- package/dist/workflow.js +65 -20
- package/examples/akane.example.json +10 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,6 +73,22 @@ For package-based installation, publish this repository to npm and add it to the
|
|
|
73
73
|
Akane still reads its runtime config from `~/.config/opencode/akane.json`.
|
|
74
74
|
If that file does not exist yet, the plugin now bootstraps it automatically on first load with the default config.
|
|
75
75
|
You only need to edit it when you want to override the default role or artifact settings.
|
|
76
|
+
When `oh-my-opencode` is installed, Akane now prefers OMO agents such as `prometheus`, `atlas`, `momus`, `oracle`, and `sisyphus`, then falls back to direct model routing when those agents are unavailable.
|
|
77
|
+
|
|
78
|
+
`akane.json` supports both model routing and agent routing:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"roleAgents": {
|
|
83
|
+
"planner": "prometheus",
|
|
84
|
+
"plan_reviewer": "hephaestus",
|
|
85
|
+
"implementer": "atlas",
|
|
86
|
+
"reviewer_codex": "momus",
|
|
87
|
+
"reviewer_claude": "oracle",
|
|
88
|
+
"synthesizer": "sisyphus"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
76
92
|
|
|
77
93
|
## Publish flow
|
|
78
94
|
|
package/dist/artifacts.js
CHANGED
|
@@ -56,6 +56,7 @@ export function createInitialState(input) {
|
|
|
56
56
|
activeStage: null,
|
|
57
57
|
stageOrder: [...input.config.workflow.stageOrder],
|
|
58
58
|
stages,
|
|
59
|
+
roleAgents: { ...input.config.roleAgents },
|
|
59
60
|
roles: { ...input.config.roles },
|
|
60
61
|
};
|
|
61
62
|
}
|
|
@@ -109,6 +110,21 @@ export async function ensureArtifactLayout(input) {
|
|
|
109
110
|
await writeStateFile(statePath, state);
|
|
110
111
|
createdFiles.push(statePath);
|
|
111
112
|
}
|
|
113
|
+
else {
|
|
114
|
+
let updated = false;
|
|
115
|
+
if (!state.roleAgents) {
|
|
116
|
+
state.roleAgents = { ...input.config.roleAgents };
|
|
117
|
+
updated = true;
|
|
118
|
+
}
|
|
119
|
+
if (!state.roles) {
|
|
120
|
+
state.roles = { ...input.config.roles };
|
|
121
|
+
updated = true;
|
|
122
|
+
}
|
|
123
|
+
if (updated) {
|
|
124
|
+
state.updatedAt = nowIso();
|
|
125
|
+
await writeStateFile(statePath, state);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
112
128
|
return { artifactDir, statePath, state, createdFiles };
|
|
113
129
|
}
|
|
114
130
|
export async function writeStageArtifact(input) {
|
package/dist/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { AKANE_SERVICE_NAME, AKANE_STAGE_IDS, DEFAULT_ARTIFACT_DIR, DEFAULT_GLOBAL_CONFIG_PATH, DEFAULT_PLUGIN_OUTPUT_PATH, DEFAULT_ROLE_MODELS, DEFAULT_STAGE_FILES, DEFAULT_STAGE_ORDER, DEFAULT_STATE_FILE, } from "./constants.js";
|
|
4
|
+
import { AKANE_SERVICE_NAME, AKANE_ROLE_IDS, AKANE_STAGE_IDS, DEFAULT_ARTIFACT_DIR, DEFAULT_GLOBAL_CONFIG_PATH, DEFAULT_PLUGIN_OUTPUT_PATH, DEFAULT_ROLE_AGENTS, DEFAULT_ROLE_MODELS, DEFAULT_STAGE_FILES, DEFAULT_STAGE_ORDER, DEFAULT_STATE_FILE, } from "./constants.js";
|
|
5
5
|
function isRecord(value) {
|
|
6
6
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
7
|
}
|
|
@@ -51,6 +51,7 @@ export function defaultAkaneConfig(configPath = DEFAULT_GLOBAL_CONFIG_PATH) {
|
|
|
51
51
|
workflow: {
|
|
52
52
|
stageOrder: [...DEFAULT_STAGE_ORDER],
|
|
53
53
|
},
|
|
54
|
+
roleAgents: { ...DEFAULT_ROLE_AGENTS },
|
|
54
55
|
roles: { ...DEFAULT_ROLE_MODELS },
|
|
55
56
|
};
|
|
56
57
|
}
|
|
@@ -76,6 +77,19 @@ function normalizeRoles(input, fallback) {
|
|
|
76
77
|
}
|
|
77
78
|
return next;
|
|
78
79
|
}
|
|
80
|
+
function normalizeRoleAgents(input, fallback) {
|
|
81
|
+
if (!isRecord(input)) {
|
|
82
|
+
return { ...fallback };
|
|
83
|
+
}
|
|
84
|
+
const next = { ...fallback };
|
|
85
|
+
for (const role of AKANE_ROLE_IDS) {
|
|
86
|
+
const candidate = input[role];
|
|
87
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
88
|
+
next[role] = candidate.trim();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return next;
|
|
92
|
+
}
|
|
79
93
|
function normalizeFiles(input, fallback) {
|
|
80
94
|
if (!isRecord(input)) {
|
|
81
95
|
return { ...fallback };
|
|
@@ -127,6 +141,7 @@ export function mergeAkaneConfig(base, overrides) {
|
|
|
127
141
|
workflow: {
|
|
128
142
|
stageOrder: normalizeStageOrder(isRecord(overrides.workflow) ? overrides.workflow.stageOrder : undefined, base.workflow.stageOrder),
|
|
129
143
|
},
|
|
144
|
+
roleAgents: normalizeRoleAgents(overrides.roleAgents, base.roleAgents),
|
|
130
145
|
roles: normalizeRoles(overrides.roles, base.roles),
|
|
131
146
|
};
|
|
132
147
|
}
|
package/dist/constants.d.ts
CHANGED
|
@@ -16,6 +16,16 @@ export declare const DEFAULT_ROLE_MODELS: {
|
|
|
16
16
|
readonly reviewer_claude: "anthropic/claude-opus-4-6";
|
|
17
17
|
readonly synthesizer: "openai/gpt-5.4";
|
|
18
18
|
};
|
|
19
|
+
export declare const DEFAULT_ROLE_AGENTS: {
|
|
20
|
+
readonly planner: "prometheus";
|
|
21
|
+
readonly plan_reviewer: "metis";
|
|
22
|
+
readonly implementer: "atlas";
|
|
23
|
+
readonly consultant_primary: "oracle";
|
|
24
|
+
readonly consultant_secondary: "librarian";
|
|
25
|
+
readonly reviewer_codex: "momus";
|
|
26
|
+
readonly reviewer_claude: "oracle";
|
|
27
|
+
readonly synthesizer: "sisyphus";
|
|
28
|
+
};
|
|
19
29
|
export declare const DEFAULT_STAGE_FILES: {
|
|
20
30
|
readonly plan: "plan.md";
|
|
21
31
|
readonly "plan-review": "plan-review.md";
|
package/dist/constants.js
CHANGED
|
@@ -41,6 +41,16 @@ export const DEFAULT_ROLE_MODELS = {
|
|
|
41
41
|
reviewer_claude: "anthropic/claude-opus-4-6",
|
|
42
42
|
synthesizer: "openai/gpt-5.4",
|
|
43
43
|
};
|
|
44
|
+
export const DEFAULT_ROLE_AGENTS = {
|
|
45
|
+
planner: "prometheus",
|
|
46
|
+
plan_reviewer: "metis",
|
|
47
|
+
implementer: "atlas",
|
|
48
|
+
consultant_primary: "oracle",
|
|
49
|
+
consultant_secondary: "librarian",
|
|
50
|
+
reviewer_codex: "momus",
|
|
51
|
+
reviewer_claude: "oracle",
|
|
52
|
+
synthesizer: "sisyphus",
|
|
53
|
+
};
|
|
44
54
|
export const DEFAULT_STAGE_FILES = {
|
|
45
55
|
plan: "plan.md",
|
|
46
56
|
"plan-review": "plan-review.md",
|
package/dist/types.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { AKANE_ROLE_IDS, AKANE_STAGE_IDS, DEFAULT_ROLE_MODELS, DEFAULT_STAG
|
|
|
2
2
|
export type AkaneStageId = (typeof AKANE_STAGE_IDS)[number];
|
|
3
3
|
export type AkaneRoleId = (typeof AKANE_ROLE_IDS)[number];
|
|
4
4
|
export type AkaneRoles = Record<AkaneRoleId, string>;
|
|
5
|
+
export type AkaneRoleAgents = Record<AkaneRoleId, string>;
|
|
5
6
|
export type AkaneStageFiles = Record<AkaneStageId, string>;
|
|
6
7
|
export interface AkaneConfig {
|
|
7
8
|
version: number;
|
|
@@ -16,6 +17,7 @@ export interface AkaneConfig {
|
|
|
16
17
|
workflow: {
|
|
17
18
|
stageOrder: AkaneStageId[];
|
|
18
19
|
};
|
|
20
|
+
roleAgents: AkaneRoleAgents;
|
|
19
21
|
roles: AkaneRoles;
|
|
20
22
|
}
|
|
21
23
|
export interface LoadedAkaneConfig {
|
|
@@ -28,6 +30,7 @@ export interface AkaneStageState {
|
|
|
28
30
|
status: "pending" | "initialized" | "completed";
|
|
29
31
|
updatedAt: string | null;
|
|
30
32
|
role?: AkaneRoleId;
|
|
33
|
+
agent?: string;
|
|
31
34
|
model?: string;
|
|
32
35
|
sessionID?: string;
|
|
33
36
|
messageID?: string;
|
|
@@ -44,6 +47,7 @@ export interface AkaneState {
|
|
|
44
47
|
activeStage: AkaneStageId | null;
|
|
45
48
|
stageOrder: AkaneStageId[];
|
|
46
49
|
stages: Record<AkaneStageId, AkaneStageState>;
|
|
50
|
+
roleAgents: AkaneRoleAgents;
|
|
47
51
|
roles: AkaneRoles;
|
|
48
52
|
}
|
|
49
53
|
export type ArtifactWriteMode = "append" | "replace";
|
package/dist/workflow.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { execFile } from "node:child_process";
|
|
3
3
|
import { promisify } from "node:util";
|
|
4
|
-
import { AKANE_SERVICE_NAME, AKANE_TOOL_IDS, } from "./constants.js";
|
|
4
|
+
import { AKANE_SERVICE_NAME, AKANE_TOOL_IDS, DEFAULT_ROLE_AGENTS, } from "./constants.js";
|
|
5
5
|
import { ensureArtifactLayout, resolveProjectRoot, resolveStageArtifactPath, writeStageArtifact, } from "./artifacts.js";
|
|
6
6
|
const execFileAsync = promisify(execFile);
|
|
7
7
|
const STAGE_ROLE_MAP = {
|
|
@@ -12,13 +12,27 @@ const STAGE_ROLE_MAP = {
|
|
|
12
12
|
"review-claude": "reviewer_claude",
|
|
13
13
|
"final-synthesis": "synthesizer",
|
|
14
14
|
};
|
|
15
|
-
const
|
|
16
|
-
planner: "plan",
|
|
17
|
-
plan_reviewer:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
const DEFAULT_ROLE_AGENT_CANDIDATES = {
|
|
16
|
+
planner: ["prometheus", "Prometheus (Plan Builder)", "plan"],
|
|
17
|
+
plan_reviewer: [
|
|
18
|
+
"metis",
|
|
19
|
+
"Metis (Plan Consultant)",
|
|
20
|
+
"hephaestus",
|
|
21
|
+
"Hephaestus (Deep Agent)",
|
|
22
|
+
"general",
|
|
23
|
+
],
|
|
24
|
+
implementer: ["atlas", "Atlas (Plan Executor)", "build"],
|
|
25
|
+
consultant_primary: ["oracle", "general"],
|
|
26
|
+
consultant_secondary: ["librarian", "explore", "general"],
|
|
27
|
+
reviewer_codex: [
|
|
28
|
+
"momus",
|
|
29
|
+
"Momus (Plan Critic)",
|
|
30
|
+
"hephaestus",
|
|
31
|
+
"Hephaestus (Deep Agent)",
|
|
32
|
+
"general",
|
|
33
|
+
],
|
|
34
|
+
reviewer_claude: ["oracle", "general"],
|
|
35
|
+
synthesizer: ["sisyphus", "Sisyphus (Ultraworker)", "general"],
|
|
22
36
|
};
|
|
23
37
|
const IMPLEMENT_TIMEOUT_MS = 15 * 60 * 1000;
|
|
24
38
|
const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;
|
|
@@ -39,6 +53,14 @@ function stageTitle(stage) {
|
|
|
39
53
|
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
40
54
|
.join(" ");
|
|
41
55
|
}
|
|
56
|
+
function normalizeAgentName(input) {
|
|
57
|
+
return input.trim().toLowerCase();
|
|
58
|
+
}
|
|
59
|
+
function agentNameAliases(input) {
|
|
60
|
+
const normalized = normalizeAgentName(input);
|
|
61
|
+
const base = normalized.replace(/\s*\([^)]*\)\s*$/, "").trim();
|
|
62
|
+
return Array.from(new Set([normalized, base].filter(Boolean)));
|
|
63
|
+
}
|
|
42
64
|
function parseModelRef(modelRef) {
|
|
43
65
|
const [providerID, ...rest] = modelRef.split("/");
|
|
44
66
|
const modelID = rest.join("/").trim();
|
|
@@ -86,23 +108,33 @@ function findLatestAssistantMessage(messages) {
|
|
|
86
108
|
function makeToolRestrictions(allowWorkspaceMutation) {
|
|
87
109
|
const restrictions = Object.fromEntries(AKANE_TOOL_IDS.map((toolID) => [toolID, false]));
|
|
88
110
|
if (!allowWorkspaceMutation) {
|
|
89
|
-
for (const toolID of ["bash", "edit", "patch", "write"
|
|
111
|
+
for (const toolID of ["bash", "edit", "patch", "write"]) {
|
|
90
112
|
restrictions[toolID] = false;
|
|
91
113
|
}
|
|
92
114
|
}
|
|
93
115
|
return restrictions;
|
|
94
116
|
}
|
|
95
|
-
async function resolveAgentName(client, directory, role) {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
117
|
+
async function resolveAgentName(client, directory, role, config) {
|
|
118
|
+
const candidates = [
|
|
119
|
+
config.roleAgents[role],
|
|
120
|
+
...DEFAULT_ROLE_AGENT_CANDIDATES[role],
|
|
121
|
+
DEFAULT_ROLE_AGENTS[role],
|
|
122
|
+
]
|
|
123
|
+
.filter((candidate) => typeof candidate === "string" && candidate.trim().length > 0)
|
|
124
|
+
.filter((candidate, index, all) => all.findIndex((value) => normalizeAgentName(value) === normalizeAgentName(candidate)) === index);
|
|
100
125
|
try {
|
|
101
126
|
const result = await client.app.agents({
|
|
102
127
|
query: { directory },
|
|
103
128
|
});
|
|
104
129
|
const agents = requireResultData(result, "agent list");
|
|
105
|
-
|
|
130
|
+
for (const candidate of candidates) {
|
|
131
|
+
const candidateAliases = agentNameAliases(candidate);
|
|
132
|
+
const matched = agents.find((agent) => agentNameAliases(agent.name).some((alias) => candidateAliases.includes(alias)));
|
|
133
|
+
if (matched) {
|
|
134
|
+
return matched.name;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return undefined;
|
|
106
138
|
}
|
|
107
139
|
catch {
|
|
108
140
|
return undefined;
|
|
@@ -199,6 +231,7 @@ function renderStageDocument(input) {
|
|
|
199
231
|
"",
|
|
200
232
|
`- Service: ${AKANE_SERVICE_NAME}`,
|
|
201
233
|
`- Role: ${input.role}`,
|
|
234
|
+
...(input.agent ? [`- Agent: ${input.agent}`] : []),
|
|
202
235
|
`- Model: ${input.model}`,
|
|
203
236
|
`- Session ID: ${input.sessionID}`,
|
|
204
237
|
`- Message ID: ${input.messageID}`,
|
|
@@ -246,9 +279,9 @@ function buildArtifactBlock(title, content) {
|
|
|
246
279
|
}
|
|
247
280
|
async function runStageSession(input) {
|
|
248
281
|
const role = STAGE_ROLE_MAP[input.stage];
|
|
249
|
-
const
|
|
250
|
-
const modelRef = parseModelRef(
|
|
251
|
-
const agent = await resolveAgentName(input.pluginInput.client, input.projectRoot, role);
|
|
282
|
+
const configuredModel = input.configInfo.config.roles[role];
|
|
283
|
+
const modelRef = parseModelRef(configuredModel);
|
|
284
|
+
const agent = await resolveAgentName(input.pluginInput.client, input.projectRoot, role, input.configInfo.config);
|
|
252
285
|
const session = await createStageSession({
|
|
253
286
|
client: input.pluginInput.client,
|
|
254
287
|
parentSessionID: input.toolContext.sessionID,
|
|
@@ -261,7 +294,7 @@ async function runStageSession(input) {
|
|
|
261
294
|
signal: input.toolContext.abort,
|
|
262
295
|
body: {
|
|
263
296
|
...(agent ? { agent } : {}),
|
|
264
|
-
model: modelRef,
|
|
297
|
+
...(agent ? {} : { model: modelRef }),
|
|
265
298
|
system: input.system,
|
|
266
299
|
tools: makeToolRestrictions(input.allowWorkspaceMutation),
|
|
267
300
|
parts: [
|
|
@@ -293,7 +326,9 @@ async function runStageSession(input) {
|
|
|
293
326
|
return {
|
|
294
327
|
stage: input.stage,
|
|
295
328
|
role,
|
|
296
|
-
model
|
|
329
|
+
model: latestAssistant.info.providerID && latestAssistant.info.modelID
|
|
330
|
+
? `${latestAssistant.info.providerID}/${latestAssistant.info.modelID}`
|
|
331
|
+
: configuredModel,
|
|
297
332
|
agent,
|
|
298
333
|
sessionID: session.id,
|
|
299
334
|
messageID: latestAssistant.info.id,
|
|
@@ -381,6 +416,7 @@ export async function executePlanStage(input) {
|
|
|
381
416
|
const content = renderStageDocument({
|
|
382
417
|
stage: result.stage,
|
|
383
418
|
role: result.role,
|
|
419
|
+
agent: result.agent,
|
|
384
420
|
model: result.model,
|
|
385
421
|
sessionID: result.sessionID,
|
|
386
422
|
messageID: result.messageID,
|
|
@@ -396,6 +432,7 @@ export async function executePlanStage(input) {
|
|
|
396
432
|
mode: "replace",
|
|
397
433
|
details: {
|
|
398
434
|
role: result.role,
|
|
435
|
+
agent: result.agent,
|
|
399
436
|
model: result.model,
|
|
400
437
|
sessionID: result.sessionID,
|
|
401
438
|
messageID: result.messageID,
|
|
@@ -429,6 +466,7 @@ export async function executePlanReviewStage(input) {
|
|
|
429
466
|
const content = renderStageDocument({
|
|
430
467
|
stage: result.stage,
|
|
431
468
|
role: result.role,
|
|
469
|
+
agent: result.agent,
|
|
432
470
|
model: result.model,
|
|
433
471
|
sessionID: result.sessionID,
|
|
434
472
|
messageID: result.messageID,
|
|
@@ -444,6 +482,7 @@ export async function executePlanReviewStage(input) {
|
|
|
444
482
|
mode: "replace",
|
|
445
483
|
details: {
|
|
446
484
|
role: result.role,
|
|
485
|
+
agent: result.agent,
|
|
447
486
|
model: result.model,
|
|
448
487
|
sessionID: result.sessionID,
|
|
449
488
|
messageID: result.messageID,
|
|
@@ -500,6 +539,7 @@ export async function executeImplementStage(input) {
|
|
|
500
539
|
const content = renderStageDocument({
|
|
501
540
|
stage: result.stage,
|
|
502
541
|
role: result.role,
|
|
542
|
+
agent: result.agent,
|
|
503
543
|
model: result.model,
|
|
504
544
|
sessionID: result.sessionID,
|
|
505
545
|
messageID: result.messageID,
|
|
@@ -530,6 +570,7 @@ export async function executeImplementStage(input) {
|
|
|
530
570
|
mode: "replace",
|
|
531
571
|
details: {
|
|
532
572
|
role: result.role,
|
|
573
|
+
agent: result.agent,
|
|
533
574
|
model: result.model,
|
|
534
575
|
sessionID: result.sessionID,
|
|
535
576
|
messageID: result.messageID,
|
|
@@ -577,6 +618,7 @@ async function executeReviewerStage(input) {
|
|
|
577
618
|
const content = renderStageDocument({
|
|
578
619
|
stage: result.stage,
|
|
579
620
|
role: result.role,
|
|
621
|
+
agent: result.agent,
|
|
580
622
|
model: result.model,
|
|
581
623
|
sessionID: result.sessionID,
|
|
582
624
|
messageID: result.messageID,
|
|
@@ -592,6 +634,7 @@ async function executeReviewerStage(input) {
|
|
|
592
634
|
mode: "replace",
|
|
593
635
|
details: {
|
|
594
636
|
role: result.role,
|
|
637
|
+
agent: result.agent,
|
|
595
638
|
model: result.model,
|
|
596
639
|
sessionID: result.sessionID,
|
|
597
640
|
messageID: result.messageID,
|
|
@@ -655,6 +698,7 @@ export async function executeSynthesizeStage(input) {
|
|
|
655
698
|
const content = renderStageDocument({
|
|
656
699
|
stage: result.stage,
|
|
657
700
|
role: result.role,
|
|
701
|
+
agent: result.agent,
|
|
658
702
|
model: result.model,
|
|
659
703
|
sessionID: result.sessionID,
|
|
660
704
|
messageID: result.messageID,
|
|
@@ -670,6 +714,7 @@ export async function executeSynthesizeStage(input) {
|
|
|
670
714
|
mode: "replace",
|
|
671
715
|
details: {
|
|
672
716
|
role: result.role,
|
|
717
|
+
agent: result.agent,
|
|
673
718
|
model: result.model,
|
|
674
719
|
sessionID: result.sessionID,
|
|
675
720
|
messageID: result.messageID,
|
|
@@ -25,6 +25,16 @@
|
|
|
25
25
|
"final-synthesis"
|
|
26
26
|
]
|
|
27
27
|
},
|
|
28
|
+
"roleAgents": {
|
|
29
|
+
"planner": "prometheus",
|
|
30
|
+
"plan_reviewer": "metis",
|
|
31
|
+
"implementer": "atlas",
|
|
32
|
+
"consultant_primary": "oracle",
|
|
33
|
+
"consultant_secondary": "librarian",
|
|
34
|
+
"reviewer_codex": "momus",
|
|
35
|
+
"reviewer_claude": "oracle",
|
|
36
|
+
"synthesizer": "sisyphus"
|
|
37
|
+
},
|
|
28
38
|
"roles": {
|
|
29
39
|
"planner": "anthropic/claude-opus-4-6",
|
|
30
40
|
"plan_reviewer": "openai/gpt-5.4",
|