@akagilnc/pi-workflow-roles 0.1.2048 → 0.1.2052
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 +1 -0
- package/README.zh-CN.md +1 -0
- package/dist/public-cli/main.js +489 -183
- package/package.json +1 -1
- package/resources/engines/codex.md +32 -0
- package/resources/engines/cursor.md +32 -0
- package/resources/engines/kimi.md +31 -0
- package/src/package-resources/engine-material.ts +111 -0
- package/src/public-cli/cli.ts +168 -22
- package/src/public-cli/config.ts +167 -22
- package/src/public-cli/invocation.ts +7 -1
- package/src/public-cli/judge-run.ts +11 -1
- package/src/public-cli/option-definitions.ts +14 -0
package/package.json
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# codex engine method material
|
|
2
|
+
|
|
3
|
+
This file is packaged method material for the optional `codex` labor engine.
|
|
4
|
+
When a role run selects this engine, read these bytes and follow the local CLI's
|
|
5
|
+
actual interface for the labor detour. Return the labor result to the same role
|
|
6
|
+
session so typed submission stays on the existing in-session path.
|
|
7
|
+
|
|
8
|
+
Material is data for the model, not a code contract. Do not invent package flags.
|
|
9
|
+
|
|
10
|
+
## Invocation examples (local Codex CLI)
|
|
11
|
+
|
|
12
|
+
Non-interactive labor prompt (working root = role project):
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
codex exec -C "$PROJECT_ROOT" "YOUR_LABOR_PROMPT"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Write the last agent message to a file, then bring that text back into the role
|
|
19
|
+
session:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
codex exec -C "$PROJECT_ROOT" -o /tmp/codex-labor-last.txt "YOUR_LABOR_PROMPT"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
JSON event stream when the host needs machine-readable progress:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
codex exec --json -C "$PROJECT_ROOT" "YOUR_LABOR_PROMPT"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Prefer the installed `codex` binary's own `--help` / `codex exec --help` over
|
|
32
|
+
any remembered flag set. Do not wrap this engine behind `ak-role` flags.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# cursor engine method material
|
|
2
|
+
|
|
3
|
+
This file is packaged method material for the optional `cursor` labor engine.
|
|
4
|
+
When a role run selects this engine, read these bytes and follow the local CLI's
|
|
5
|
+
actual interface for the labor detour. Return the labor result to the same role
|
|
6
|
+
session so typed submission stays on the existing in-session path.
|
|
7
|
+
|
|
8
|
+
Material is data for the model, not a code contract. Do not invent package flags.
|
|
9
|
+
|
|
10
|
+
## Invocation examples (local Cursor Agent CLI)
|
|
11
|
+
|
|
12
|
+
The machine entrypoint is `agent` (also exposed as `cursor-agent`). Run from the
|
|
13
|
+
role project root. Non-interactive print mode:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
agent -p --force --output-format text "YOUR_LABOR_PROMPT"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Choose a model explicitly when the seat needs a known Cursor model id:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
agent -p --force --model grok-4.5 --output-format text "YOUR_LABOR_PROMPT"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Stream JSON events for long labor:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
agent -p --force --output-format stream-json "YOUR_LABOR_PROMPT"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Prefer `agent --help` on the host over any remembered flag set. Do not wrap this
|
|
32
|
+
engine behind `ak-role` flags.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# kimi engine method material
|
|
2
|
+
|
|
3
|
+
This file is packaged method material for the optional `kimi` labor engine.
|
|
4
|
+
When a role run selects this engine, read these bytes and follow the local CLI's
|
|
5
|
+
actual interface for the labor detour. Return the labor result to the same role
|
|
6
|
+
session so typed submission stays on the existing in-session path.
|
|
7
|
+
|
|
8
|
+
Material is data for the model, not a code contract. Do not invent package flags.
|
|
9
|
+
|
|
10
|
+
## Invocation examples (local Kimi Code CLI)
|
|
11
|
+
|
|
12
|
+
One-shot non-interactive prompt from the role project root:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
kimi --yolo -p "YOUR_LABOR_PROMPT"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Pin a model alias when needed:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
kimi --yolo -m <model-alias> -p "YOUR_LABOR_PROMPT"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Machine-readable prompt mode:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
kimi --yolo --output-format text -p "YOUR_LABOR_PROMPT"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Prefer `kimi --help` on the host over any remembered flag set. Do not wrap this
|
|
31
|
+
engine behind `ak-role` flags.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Packaged engine method-material seam (#356 T1 / ADR 0069).
|
|
3
|
+
* Legal engine names = directory stems under resources/engines/ — no code whitelist.
|
|
4
|
+
* Material body is data for the LLM, not a code contract.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
|
|
9
|
+
const ENGINE_MATERIAL_RELATIVE_ROOT = "resources/engines" as const;
|
|
10
|
+
|
|
11
|
+
export type EngineSessionMaterial = Readonly<{
|
|
12
|
+
name: string;
|
|
13
|
+
materialPath: string;
|
|
14
|
+
}>;
|
|
15
|
+
|
|
16
|
+
/** Non-empty, trimmed, no path separators or traversal. */
|
|
17
|
+
export function isEngineNameSyntax(name: string): boolean {
|
|
18
|
+
if (typeof name !== "string") return false;
|
|
19
|
+
if (name.length === 0 || name.trim() !== name) return false;
|
|
20
|
+
if (name === "." || name === "..") return false;
|
|
21
|
+
if (name.includes("/") || name.includes("\\") || name.includes("\0")) return false;
|
|
22
|
+
if (name.includes("..")) return false;
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function engineMaterialRelativeDirectory(): string {
|
|
27
|
+
return ENGINE_MATERIAL_RELATIVE_ROOT;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function resolveEngineMaterialDirectory(packageRoot: string): string {
|
|
31
|
+
return join(packageRoot, ENGINE_MATERIAL_RELATIVE_ROOT);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Enumerate legal engine names from packaged material files.
|
|
36
|
+
* Only `*.md` stems that pass name syntax are legal.
|
|
37
|
+
*/
|
|
38
|
+
export function listEngineMaterialNames(packageRoot: string): readonly string[] {
|
|
39
|
+
const dir = resolveEngineMaterialDirectory(packageRoot);
|
|
40
|
+
if (!existsSync(dir)) return Object.freeze([]);
|
|
41
|
+
const names = readdirSync(dir)
|
|
42
|
+
.filter((entry) => entry.endsWith(".md"))
|
|
43
|
+
.map((entry) => entry.slice(0, -".md".length))
|
|
44
|
+
.filter((stem) => isEngineNameSyntax(stem))
|
|
45
|
+
.sort();
|
|
46
|
+
return Object.freeze([...names]);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function resolveEngineMaterialPath(
|
|
50
|
+
packageRoot: string,
|
|
51
|
+
name: string,
|
|
52
|
+
): string {
|
|
53
|
+
const legal = assertLegalEngineName(packageRoot, name);
|
|
54
|
+
return join(resolveEngineMaterialDirectory(packageRoot), `${legal}.md`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Assert `name` is a legal engine material stem under packageRoot.
|
|
59
|
+
* Returns the canonical name on success; throws Error on illegal.
|
|
60
|
+
*/
|
|
61
|
+
export function assertLegalEngineName(packageRoot: string, name: string): string {
|
|
62
|
+
if (!isEngineNameSyntax(name)) {
|
|
63
|
+
throw new Error(`illegal engine name: ${name}`);
|
|
64
|
+
}
|
|
65
|
+
const legal = listEngineMaterialNames(packageRoot);
|
|
66
|
+
if (!legal.includes(name)) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`unknown engine: ${name} (known: ${legal.length === 0 ? "(none)" : legal.join(", ")})`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return name;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Resolve optional engine options into session material coordinates.
|
|
76
|
+
* No engine → undefined (caller keeps default prompt bytes).
|
|
77
|
+
*/
|
|
78
|
+
export function engineSessionMaterialFromOptions(options: {
|
|
79
|
+
engine?: string;
|
|
80
|
+
packageRoot?: string;
|
|
81
|
+
}): EngineSessionMaterial | undefined {
|
|
82
|
+
if (options.engine === undefined) return undefined;
|
|
83
|
+
if (options.packageRoot === undefined || options.packageRoot.trim() === "") {
|
|
84
|
+
throw new Error("packageRoot is required when engine is configured");
|
|
85
|
+
}
|
|
86
|
+
const name = assertLegalEngineName(options.packageRoot, options.engine);
|
|
87
|
+
return Object.freeze({
|
|
88
|
+
name,
|
|
89
|
+
materialPath: resolveEngineMaterialPath(options.packageRoot, name),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Append engine method-material path delivery to session initial material lines.
|
|
95
|
+
* No material → identity copy (byte-stable when joined the same way).
|
|
96
|
+
* Delivers engine name + absolute material path only — never material body.
|
|
97
|
+
*/
|
|
98
|
+
export function appendEngineSessionMaterial(
|
|
99
|
+
lines: readonly string[],
|
|
100
|
+
engineMaterial?: EngineSessionMaterial,
|
|
101
|
+
): string[] {
|
|
102
|
+
if (engineMaterial === undefined) {
|
|
103
|
+
return [...lines];
|
|
104
|
+
}
|
|
105
|
+
const out = [...lines];
|
|
106
|
+
out.push("");
|
|
107
|
+
out.push("Engine method material (read these bytes and follow them):");
|
|
108
|
+
out.push(`- engine: ${engineMaterial.name}`);
|
|
109
|
+
out.push(`- ${engineMaterial.materialPath}`);
|
|
110
|
+
return out;
|
|
111
|
+
}
|
package/src/public-cli/cli.ts
CHANGED
|
@@ -5,6 +5,9 @@ import { realpath } from "node:fs/promises";
|
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
|
|
8
|
+
import {
|
|
9
|
+
assertLegalEngineName,
|
|
10
|
+
} from "../package-resources/engine-material.ts";
|
|
8
11
|
import {
|
|
9
12
|
effectiveSeatConfigurations,
|
|
10
13
|
formatModelSpec,
|
|
@@ -14,6 +17,8 @@ import {
|
|
|
14
17
|
resolveEffectiveSeat,
|
|
15
18
|
savePublicCliConfig,
|
|
16
19
|
setPersistentSeatConfig,
|
|
20
|
+
setPersistentSeatEngine,
|
|
21
|
+
validatePublicCliConfigEngines,
|
|
17
22
|
type CredentialProviders,
|
|
18
23
|
type EffectiveSeat,
|
|
19
24
|
type InvocationModelOverride,
|
|
@@ -94,7 +99,8 @@ export const PUBLIC_GLOBAL_OPTIONS: readonly PublicOptionDefinition[] =
|
|
|
94
99
|
type TakenPublicGlobalFlag =
|
|
95
100
|
| { flag: "help"; consume: 1 }
|
|
96
101
|
| { flag: "model"; consume: 1 | 2; value: string | undefined }
|
|
97
|
-
| { flag: "thinking"; consume: 1 | 2; raw: string | undefined }
|
|
102
|
+
| { flag: "thinking"; consume: 1 | 2; raw: string | undefined }
|
|
103
|
+
| { flag: "engine"; consume: 1 | 2; value: string | undefined };
|
|
98
104
|
|
|
99
105
|
/**
|
|
100
106
|
* If `argv[index]` is a public global flag, describe its span and payload.
|
|
@@ -127,6 +133,13 @@ function takePublicGlobalFlag(
|
|
|
127
133
|
raw: taken.value,
|
|
128
134
|
};
|
|
129
135
|
}
|
|
136
|
+
if (taken.def.id === "engine") {
|
|
137
|
+
return {
|
|
138
|
+
flag: "engine",
|
|
139
|
+
consume: consumed as 1 | 2,
|
|
140
|
+
value: taken.value,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
130
143
|
return undefined;
|
|
131
144
|
}
|
|
132
145
|
|
|
@@ -217,6 +230,7 @@ type ParsedGlobal = {
|
|
|
217
230
|
args: string[];
|
|
218
231
|
model?: string;
|
|
219
232
|
thinking?: PublicThinkingLevel;
|
|
233
|
+
engine?: string;
|
|
220
234
|
help: boolean;
|
|
221
235
|
};
|
|
222
236
|
|
|
@@ -231,6 +245,7 @@ function parseArgv(argv: readonly string[]): ParsedGlobal {
|
|
|
231
245
|
const args = [...argv];
|
|
232
246
|
let model: string | undefined;
|
|
233
247
|
let thinking: PublicThinkingLevel | undefined;
|
|
248
|
+
let engine: string | undefined;
|
|
234
249
|
let help = false;
|
|
235
250
|
const positional: string[] = [];
|
|
236
251
|
const globalOptions = createTypedOptionConsumer(PUBLIC_GLOBAL_OPTIONS);
|
|
@@ -259,12 +274,24 @@ function parseArgv(argv: readonly string[]): ParsedGlobal {
|
|
|
259
274
|
args.splice(0, taken.consume);
|
|
260
275
|
continue;
|
|
261
276
|
}
|
|
262
|
-
if (taken.
|
|
263
|
-
|
|
277
|
+
if (taken.flag === "thinking") {
|
|
278
|
+
if (taken.raw === undefined) {
|
|
279
|
+
throw new CliUsageError("--thinking requires a value");
|
|
280
|
+
}
|
|
281
|
+
thinking = parseThinking(taken.raw);
|
|
282
|
+
args.splice(0, taken.consume);
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (taken.flag === "engine") {
|
|
286
|
+
if (taken.value === undefined) {
|
|
287
|
+
throw new CliUsageError("--engine requires a value");
|
|
288
|
+
}
|
|
289
|
+
engine = taken.value;
|
|
290
|
+
args.splice(0, taken.consume);
|
|
291
|
+
continue;
|
|
264
292
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
continue;
|
|
293
|
+
// Exhaustive for known global flags; unknown id is a table bug.
|
|
294
|
+
throw new CliUsageError(`unhandled global option: ${String((taken as { flag: string }).flag)}`);
|
|
268
295
|
}
|
|
269
296
|
// Subcommands may own additional flags later; unknown dashed tokens stay
|
|
270
297
|
// positional here (same as pre-unification parseArgv).
|
|
@@ -277,18 +304,54 @@ function parseArgv(argv: readonly string[]): ParsedGlobal {
|
|
|
277
304
|
args: rest,
|
|
278
305
|
...(model === undefined ? {} : { model }),
|
|
279
306
|
...(thinking === undefined ? {} : { thinking }),
|
|
307
|
+
...(engine === undefined ? {} : { engine }),
|
|
280
308
|
help,
|
|
281
309
|
};
|
|
282
310
|
}
|
|
283
311
|
|
|
284
312
|
function invocationFromParsed(parsed: ParsedGlobal): InvocationModelOverride | undefined {
|
|
285
|
-
if (
|
|
313
|
+
if (
|
|
314
|
+
parsed.model === undefined &&
|
|
315
|
+
parsed.thinking === undefined &&
|
|
316
|
+
parsed.engine === undefined
|
|
317
|
+
) {
|
|
318
|
+
return undefined;
|
|
319
|
+
}
|
|
286
320
|
return {
|
|
287
321
|
...(parsed.model === undefined ? {} : { model: parsed.model }),
|
|
288
322
|
...(parsed.thinking === undefined ? {} : { thinking: parsed.thinking }),
|
|
323
|
+
...(parsed.engine === undefined ? {} : { engine: parsed.engine }),
|
|
289
324
|
};
|
|
290
325
|
}
|
|
291
326
|
|
|
327
|
+
function requireLegalEngineName(packageRoot: string, name: string): string {
|
|
328
|
+
try {
|
|
329
|
+
return assertLegalEngineName(packageRoot, name);
|
|
330
|
+
} catch (error) {
|
|
331
|
+
throw new CliUsageError(
|
|
332
|
+
error instanceof Error ? error.message : String(error),
|
|
333
|
+
{ cause: error },
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function loadAndValidateConfig(
|
|
339
|
+
home: string,
|
|
340
|
+
packageRoot: string,
|
|
341
|
+
): Promise<PublicCliConfig> {
|
|
342
|
+
return loadPublicCliConfig(home).then((config) => {
|
|
343
|
+
try {
|
|
344
|
+
validatePublicCliConfigEngines(config, packageRoot);
|
|
345
|
+
} catch (error) {
|
|
346
|
+
throw new CliUsageError(
|
|
347
|
+
error instanceof Error ? error.message : String(error),
|
|
348
|
+
{ cause: error },
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
return config;
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
292
355
|
/** Typed facts used by help presentation — not layout. */
|
|
293
356
|
export function helpDocument() {
|
|
294
357
|
return {
|
|
@@ -355,6 +418,7 @@ function renderHelp(): string {
|
|
|
355
418
|
"",
|
|
356
419
|
"Role options: ak-role help <command>",
|
|
357
420
|
"Persistent config: ak-role config set <seat> <provider/model:thinking>",
|
|
421
|
+
"Persistent engine (judge only): ak-role config set-engine judge <name> | unset-engine judge",
|
|
358
422
|
"Effective seats: ak-role roles",
|
|
359
423
|
);
|
|
360
424
|
return `${lines.join("\n")}\n`;
|
|
@@ -398,7 +462,7 @@ function renderRoles(seats: readonly EffectiveSeat[]): string {
|
|
|
398
462
|
}
|
|
399
463
|
|
|
400
464
|
function renderConfig(config: PublicCliConfig): string {
|
|
401
|
-
const lines: string[] = ["seat\tmodel"];
|
|
465
|
+
const lines: string[] = ["seat\tmodel\tengine"];
|
|
402
466
|
const keys = Object.keys(config.seats) as (keyof typeof config.seats)[];
|
|
403
467
|
if (keys.length === 0) {
|
|
404
468
|
lines.push("(empty)");
|
|
@@ -406,7 +470,8 @@ function renderConfig(config: PublicCliConfig): string {
|
|
|
406
470
|
for (const seat of keys.sort()) {
|
|
407
471
|
const selection = config.seats[seat];
|
|
408
472
|
if (selection === undefined) continue;
|
|
409
|
-
|
|
473
|
+
const engine = selection.engine === undefined ? "-" : selection.engine;
|
|
474
|
+
lines.push(`${seat}\t${formatModelSpec(selection)}\t${engine}`);
|
|
410
475
|
}
|
|
411
476
|
}
|
|
412
477
|
return `${lines.join("\n")}\n`;
|
|
@@ -415,10 +480,11 @@ function renderConfig(config: PublicCliConfig): string {
|
|
|
415
480
|
async function runConfigCommand(
|
|
416
481
|
args: readonly string[],
|
|
417
482
|
home: string,
|
|
483
|
+
packageRoot: string,
|
|
418
484
|
io: CliIo,
|
|
419
485
|
): Promise<number> {
|
|
420
486
|
if (args.length === 0 || args[0] === "get" || args[0] === "list" || args[0] === "show") {
|
|
421
|
-
const config = await
|
|
487
|
+
const config = await loadAndValidateConfig(home, packageRoot);
|
|
422
488
|
if (args[0] === "get" && args[1] !== undefined) {
|
|
423
489
|
if (!isPublicConfigurableSeat(args[1])) {
|
|
424
490
|
throw new CliUsageError(`unknown configurable seat: ${args[1]}`);
|
|
@@ -427,7 +493,9 @@ async function runConfigCommand(
|
|
|
427
493
|
if (selection === undefined) {
|
|
428
494
|
io.stdout(`${args[1]}\t(unconfigured)\n`);
|
|
429
495
|
} else {
|
|
430
|
-
|
|
496
|
+
const engine =
|
|
497
|
+
selection.engine === undefined ? "-" : selection.engine;
|
|
498
|
+
io.stdout(`${args[1]}\t${formatModelSpec(selection)}\t${engine}\n`);
|
|
431
499
|
}
|
|
432
500
|
return 0;
|
|
433
501
|
}
|
|
@@ -448,7 +516,7 @@ async function runConfigCommand(
|
|
|
448
516
|
"config set requires seat/spec pairs: ak-role config set <seat> <spec> [<seat> <spec> ...]",
|
|
449
517
|
);
|
|
450
518
|
}
|
|
451
|
-
let config = await
|
|
519
|
+
let config = await loadAndValidateConfig(home, packageRoot);
|
|
452
520
|
for (let i = 0; i < pairs.length; i += 2) {
|
|
453
521
|
const seat = pairs[i]!;
|
|
454
522
|
const spec = pairs[i + 1]!;
|
|
@@ -462,6 +530,66 @@ async function runConfigCommand(
|
|
|
462
530
|
return 0;
|
|
463
531
|
}
|
|
464
532
|
|
|
533
|
+
if (args[0] === "set-engine") {
|
|
534
|
+
if (args.length !== 3) {
|
|
535
|
+
throw new CliUsageError(
|
|
536
|
+
"usage: ak-role config set-engine judge <name>",
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
const seat = args[1]!;
|
|
540
|
+
const name = args[2]!;
|
|
541
|
+
if (!isPublicConfigurableSeat(seat)) {
|
|
542
|
+
throw new CliUsageError(`unknown configurable seat: ${seat}`);
|
|
543
|
+
}
|
|
544
|
+
if (seat !== "judge") {
|
|
545
|
+
throw new CliUsageError(
|
|
546
|
+
`engine axis is judge-only; refused seat ${seat}`,
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
requireLegalEngineName(packageRoot, name);
|
|
550
|
+
let config = await loadAndValidateConfig(home, packageRoot);
|
|
551
|
+
try {
|
|
552
|
+
config = setPersistentSeatEngine(config, seat, name);
|
|
553
|
+
} catch (error) {
|
|
554
|
+
throw new CliUsageError(
|
|
555
|
+
error instanceof Error ? error.message : String(error),
|
|
556
|
+
{ cause: error },
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
await savePublicCliConfig(config, home);
|
|
560
|
+
io.stdout(renderConfig(config));
|
|
561
|
+
return 0;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
if (args[0] === "unset-engine") {
|
|
565
|
+
if (args.length !== 2) {
|
|
566
|
+
throw new CliUsageError(
|
|
567
|
+
"usage: ak-role config unset-engine judge",
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
const seat = args[1]!;
|
|
571
|
+
if (!isPublicConfigurableSeat(seat)) {
|
|
572
|
+
throw new CliUsageError(`unknown configurable seat: ${seat}`);
|
|
573
|
+
}
|
|
574
|
+
if (seat !== "judge") {
|
|
575
|
+
throw new CliUsageError(
|
|
576
|
+
`engine axis is judge-only; refused seat ${seat}`,
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
let config = await loadAndValidateConfig(home, packageRoot);
|
|
580
|
+
try {
|
|
581
|
+
config = setPersistentSeatEngine(config, seat, undefined);
|
|
582
|
+
} catch (error) {
|
|
583
|
+
throw new CliUsageError(
|
|
584
|
+
error instanceof Error ? error.message : String(error),
|
|
585
|
+
{ cause: error },
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
await savePublicCliConfig(config, home);
|
|
589
|
+
io.stdout(renderConfig(config));
|
|
590
|
+
return 0;
|
|
591
|
+
}
|
|
592
|
+
|
|
465
593
|
throw new CliUsageError(`unknown config subcommand: ${args[0]}`);
|
|
466
594
|
}
|
|
467
595
|
|
|
@@ -477,6 +605,21 @@ export async function runAkRole(
|
|
|
477
605
|
// runtime entry, activation argv, or invocation provenance is derived.
|
|
478
606
|
env = { ...env, packageRoot: await realpath(env.packageRoot) };
|
|
479
607
|
const parsed = parseArgv(argv);
|
|
608
|
+
// Invocation --engine rejects at the call-request seam (not role submission).
|
|
609
|
+
// #356 MVP: engine axis is Judge-only (not resume / non-Judge seats).
|
|
610
|
+
if (parsed.engine !== undefined) {
|
|
611
|
+
requireLegalEngineName(env.packageRoot, parsed.engine);
|
|
612
|
+
if (
|
|
613
|
+
!parsed.help &&
|
|
614
|
+
parsed.command !== undefined &&
|
|
615
|
+
parsed.command !== "help" &&
|
|
616
|
+
parsed.command !== "judge"
|
|
617
|
+
) {
|
|
618
|
+
throw new CliUsageError(
|
|
619
|
+
`engine axis is judge-only; refused command ${parsed.command}`,
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
480
623
|
|
|
481
624
|
if (
|
|
482
625
|
parsed.help ||
|
|
@@ -501,7 +644,7 @@ export async function runAkRole(
|
|
|
501
644
|
if (parsed.args.length > 0) {
|
|
502
645
|
throw new CliUsageError("roles takes no arguments");
|
|
503
646
|
}
|
|
504
|
-
const config = await
|
|
647
|
+
const config = await loadAndValidateConfig(home, env.packageRoot);
|
|
505
648
|
const credentials =
|
|
506
649
|
env.credentials ??
|
|
507
650
|
(await loadCredentialProviders(resolveAgentDir(env, home)));
|
|
@@ -515,7 +658,9 @@ export async function runAkRole(
|
|
|
515
658
|
}
|
|
516
659
|
|
|
517
660
|
if (parsed.command === "config") {
|
|
518
|
-
return {
|
|
661
|
+
return {
|
|
662
|
+
exitCode: await runConfigCommand(parsed.args, home, env.packageRoot, io),
|
|
663
|
+
};
|
|
519
664
|
}
|
|
520
665
|
|
|
521
666
|
// Resume reopens an exact Role run after a typed HTTP 429 (#108/#109).
|
|
@@ -523,7 +668,7 @@ export async function runAkRole(
|
|
|
523
668
|
if (parsed.command === "resume") {
|
|
524
669
|
const agentDir = resolveAgentDir(env, home);
|
|
525
670
|
const cwd = env.cwd ?? process.cwd();
|
|
526
|
-
const config = await
|
|
671
|
+
const config = await loadAndValidateConfig(home, env.packageRoot);
|
|
527
672
|
const credentials =
|
|
528
673
|
env.credentials ?? (await loadCredentialProviders(agentDir));
|
|
529
674
|
const resumeRunId = parsed.args[0];
|
|
@@ -706,7 +851,7 @@ export async function runAkRole(
|
|
|
706
851
|
if (parsed.command === "judge") {
|
|
707
852
|
const agentDir = resolveAgentDir(env, home);
|
|
708
853
|
const cwd = env.cwd ?? process.cwd();
|
|
709
|
-
const config = await
|
|
854
|
+
const config = await loadAndValidateConfig(home, env.packageRoot);
|
|
710
855
|
const credentials =
|
|
711
856
|
env.credentials ?? (await loadCredentialProviders(agentDir));
|
|
712
857
|
const seat = resolveEffectiveSeat(
|
|
@@ -728,6 +873,7 @@ export async function runAkRole(
|
|
|
728
873
|
: { correlationId: env.correlationId }),
|
|
729
874
|
...(env.piRunner === undefined ? {} : { piRunner: env.piRunner }),
|
|
730
875
|
...(seat.selection === undefined ? {} : { model: seat.selection }),
|
|
876
|
+
...(seat.engine === undefined ? {} : { engine: seat.engine }),
|
|
731
877
|
...(env.judgeExtraPiArgs === undefined
|
|
732
878
|
? {}
|
|
733
879
|
: { extraPiArgs: env.judgeExtraPiArgs }),
|
|
@@ -749,7 +895,7 @@ export async function runAkRole(
|
|
|
749
895
|
if (parsed.command === "coder") {
|
|
750
896
|
const agentDir = resolveAgentDir(env, home);
|
|
751
897
|
const cwd = env.cwd ?? process.cwd();
|
|
752
|
-
const config = await
|
|
898
|
+
const config = await loadAndValidateConfig(home, env.packageRoot);
|
|
753
899
|
const credentials =
|
|
754
900
|
env.credentials ?? (await loadCredentialProviders(agentDir));
|
|
755
901
|
const seat = resolveEffectiveSeat(
|
|
@@ -792,7 +938,7 @@ export async function runAkRole(
|
|
|
792
938
|
if (parsed.command === "fixer") {
|
|
793
939
|
const agentDir = resolveAgentDir(env, home);
|
|
794
940
|
const cwd = env.cwd ?? process.cwd();
|
|
795
|
-
const config = await
|
|
941
|
+
const config = await loadAndValidateConfig(home, env.packageRoot);
|
|
796
942
|
const credentials =
|
|
797
943
|
env.credentials ?? (await loadCredentialProviders(agentDir));
|
|
798
944
|
const seat = resolveEffectiveSeat(
|
|
@@ -835,7 +981,7 @@ export async function runAkRole(
|
|
|
835
981
|
if (parsed.command === "collector") {
|
|
836
982
|
const agentDir = resolveAgentDir(env, home);
|
|
837
983
|
const cwd = env.cwd ?? process.cwd();
|
|
838
|
-
const config = await
|
|
984
|
+
const config = await loadAndValidateConfig(home, env.packageRoot);
|
|
839
985
|
const credentials =
|
|
840
986
|
env.credentials ?? (await loadCredentialProviders(agentDir));
|
|
841
987
|
const seat = resolveEffectiveSeat(
|
|
@@ -878,7 +1024,7 @@ export async function runAkRole(
|
|
|
878
1024
|
if (parsed.command === "reviewer") {
|
|
879
1025
|
const agentDir = resolveAgentDir(env, home);
|
|
880
1026
|
const cwd = env.cwd ?? process.cwd();
|
|
881
|
-
const config = await
|
|
1027
|
+
const config = await loadAndValidateConfig(home, env.packageRoot);
|
|
882
1028
|
const credentials =
|
|
883
1029
|
env.credentials ?? (await loadCredentialProviders(agentDir));
|
|
884
1030
|
const seat = resolveEffectiveSeat(
|
|
@@ -921,7 +1067,7 @@ export async function runAkRole(
|
|
|
921
1067
|
if (parsed.command === "doctor") {
|
|
922
1068
|
const agentDir = resolveAgentDir(env, home);
|
|
923
1069
|
const cwd = env.cwd ?? process.cwd();
|
|
924
|
-
const config = await
|
|
1070
|
+
const config = await loadAndValidateConfig(home, env.packageRoot);
|
|
925
1071
|
const credentials =
|
|
926
1072
|
env.credentials ?? (await loadCredentialProviders(agentDir));
|
|
927
1073
|
const seat = resolveEffectiveSeat(
|
|
@@ -964,7 +1110,7 @@ export async function runAkRole(
|
|
|
964
1110
|
if (parsed.command === "merger") {
|
|
965
1111
|
const agentDir = resolveAgentDir(env, home);
|
|
966
1112
|
const cwd = env.cwd ?? process.cwd();
|
|
967
|
-
const config = await
|
|
1113
|
+
const config = await loadAndValidateConfig(home, env.packageRoot);
|
|
968
1114
|
const credentials =
|
|
969
1115
|
env.credentials ?? (await loadCredentialProviders(agentDir));
|
|
970
1116
|
const seat = resolveEffectiveSeat(
|