@akagilnc/pi-workflow-roles 0.1.2169 → 0.1.2173
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/public-cli/main.js +86 -10
- package/package.json +1 -1
- package/src/public-cli/auto-resume.ts +16 -2
- package/src/public-cli/cli.ts +57 -1
- package/src/public-cli/coder-run.ts +4 -0
- package/src/public-cli/config.ts +53 -3
- package/src/public-cli/fixer-run.ts +4 -0
- package/src/public-cli/judge-run.ts +4 -0
- package/src/public-cli/merger-run.ts +4 -0
- package/src/public-cli/option-definitions.ts +2 -0
- package/src/public-cli/reviewer-run.ts +4 -0
- package/src/public-cli/run-lifecycle.ts +4 -1
package/dist/public-cli/main.js
CHANGED
|
@@ -14505,6 +14505,9 @@ async function savePublicCliConfig(config, home = homedir()) {
|
|
|
14505
14505
|
function setPersistentSeatConfig(config, seat, selection) {
|
|
14506
14506
|
const previous = config.seats[seat];
|
|
14507
14507
|
return {
|
|
14508
|
+
// Spread preserves sibling top-level keys such as autoResumeLimit (#422):
|
|
14509
|
+
// a seat write must never silently drop them.
|
|
14510
|
+
...config,
|
|
14508
14511
|
seats: {
|
|
14509
14512
|
...config.seats,
|
|
14510
14513
|
[seat]: {
|
|
@@ -14528,6 +14531,7 @@ function setPersistentSeatEngine(config, seat, engine) {
|
|
|
14528
14531
|
if (engine === void 0) {
|
|
14529
14532
|
const { engine: _dropped, ...modelOnly } = previous;
|
|
14530
14533
|
return {
|
|
14534
|
+
...config,
|
|
14531
14535
|
seats: {
|
|
14532
14536
|
...config.seats,
|
|
14533
14537
|
[seat]: modelOnly
|
|
@@ -14535,12 +14539,24 @@ function setPersistentSeatEngine(config, seat, engine) {
|
|
|
14535
14539
|
};
|
|
14536
14540
|
}
|
|
14537
14541
|
return {
|
|
14542
|
+
...config,
|
|
14538
14543
|
seats: {
|
|
14539
14544
|
...config.seats,
|
|
14540
14545
|
[seat]: { ...previous, engine }
|
|
14541
14546
|
}
|
|
14542
14547
|
};
|
|
14543
14548
|
}
|
|
14549
|
+
function parseAutoResumeLimit(value) {
|
|
14550
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
14551
|
+
throw new Error(
|
|
14552
|
+
`auto-resume limit must be a non-negative integer, got ${JSON.stringify(value)}`
|
|
14553
|
+
);
|
|
14554
|
+
}
|
|
14555
|
+
return value;
|
|
14556
|
+
}
|
|
14557
|
+
function setAutoResumeLimit(config, limit) {
|
|
14558
|
+
return { ...config, autoResumeLimit: parseAutoResumeLimit(limit) };
|
|
14559
|
+
}
|
|
14544
14560
|
function seatModelOnly(seat) {
|
|
14545
14561
|
return seat.thinking === void 0 ? { provider: seat.provider, model: seat.model } : { provider: seat.provider, model: seat.model, thinking: seat.thinking };
|
|
14546
14562
|
}
|
|
@@ -14610,8 +14626,15 @@ function parsePublicCliConfig(value) {
|
|
|
14610
14626
|
throw new Error("public CLI config must be an object");
|
|
14611
14627
|
}
|
|
14612
14628
|
const record4 = value;
|
|
14629
|
+
let autoResumeLimit;
|
|
14630
|
+
if (record4.autoResumeLimit !== void 0) {
|
|
14631
|
+
autoResumeLimit = parseAutoResumeLimit(record4.autoResumeLimit);
|
|
14632
|
+
}
|
|
14613
14633
|
if (record4.seats === void 0) {
|
|
14614
|
-
return {
|
|
14634
|
+
return {
|
|
14635
|
+
seats: {},
|
|
14636
|
+
...autoResumeLimit === void 0 ? {} : { autoResumeLimit }
|
|
14637
|
+
};
|
|
14615
14638
|
}
|
|
14616
14639
|
if (record4.seats === null || typeof record4.seats !== "object" || Array.isArray(record4.seats)) {
|
|
14617
14640
|
throw new Error("public CLI config.seats must be an object");
|
|
@@ -14625,7 +14648,10 @@ function parsePublicCliConfig(value) {
|
|
|
14625
14648
|
}
|
|
14626
14649
|
seats[key] = parseSeatModelConfig(raw, key);
|
|
14627
14650
|
}
|
|
14628
|
-
return {
|
|
14651
|
+
return {
|
|
14652
|
+
seats,
|
|
14653
|
+
...autoResumeLimit === void 0 ? {} : { autoResumeLimit }
|
|
14654
|
+
};
|
|
14629
14655
|
}
|
|
14630
14656
|
function parseSeatModelConfig(value, seat) {
|
|
14631
14657
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -16470,11 +16496,13 @@ var init_option_definitions = __esm({
|
|
|
16470
16496
|
usage: [
|
|
16471
16497
|
"ak-role config set <seat> <provider/model[:thinking]> [<seat> <spec> ...]",
|
|
16472
16498
|
"ak-role config set-engine <seat> <name>",
|
|
16473
|
-
"ak-role config unset-engine <seat>"
|
|
16499
|
+
"ak-role config unset-engine <seat>",
|
|
16500
|
+
"ak-role config set-auto-resume-limit <N>"
|
|
16474
16501
|
],
|
|
16475
16502
|
examples: [
|
|
16476
16503
|
"ak-role config set judge openai-codex/gpt-5.6-sol:high",
|
|
16477
|
-
"ak-role config set-engine judge opus"
|
|
16504
|
+
"ak-role config set-engine judge opus",
|
|
16505
|
+
"ak-role config set-auto-resume-limit 3"
|
|
16478
16506
|
]
|
|
16479
16507
|
},
|
|
16480
16508
|
help: {
|
|
@@ -22607,6 +22635,8 @@ function presentTerminal(terminal, io) {
|
|
|
22607
22635
|
}
|
|
22608
22636
|
}
|
|
22609
22637
|
async function runWithAutoResumeLoop(options) {
|
|
22638
|
+
const limit = options.autoResumeLimit ?? AUTO_RESUME_LIMIT;
|
|
22639
|
+
parseAutoResumeLimit(limit);
|
|
22610
22640
|
let autoResumeAttempts = 0;
|
|
22611
22641
|
let isFirst = true;
|
|
22612
22642
|
let currentExtraArgs = options.buildInitialArgs();
|
|
@@ -22636,7 +22666,7 @@ async function runWithAutoResumeLoop(options) {
|
|
|
22636
22666
|
}
|
|
22637
22667
|
return result2;
|
|
22638
22668
|
}
|
|
22639
|
-
if (autoResumeAttempts >=
|
|
22669
|
+
if (autoResumeAttempts >= limit) {
|
|
22640
22670
|
if (terminal !== void 0) presentTerminal(terminal, options.io);
|
|
22641
22671
|
return result2;
|
|
22642
22672
|
}
|
|
@@ -22654,6 +22684,7 @@ var init_auto_resume = __esm({
|
|
|
22654
22684
|
"src/public-cli/auto-resume.ts"() {
|
|
22655
22685
|
"use strict";
|
|
22656
22686
|
init_run_lifecycle();
|
|
22687
|
+
init_config2();
|
|
22657
22688
|
init_terminal();
|
|
22658
22689
|
init_settlement();
|
|
22659
22690
|
dummyIo = { stdout: () => {
|
|
@@ -22930,6 +22961,8 @@ async function runPublicCoder(argv, env, io, parseCoderArgv2) {
|
|
|
22930
22961
|
return runWithAutoResumeLoop({
|
|
22931
22962
|
admitted,
|
|
22932
22963
|
io,
|
|
22964
|
+
// #422: pass-through only; the loop entry resolves the default and validates the domain once.
|
|
22965
|
+
autoResumeLimit: env.autoResumeLimit,
|
|
22933
22966
|
buildInitialArgs: () => buildCoderActivationExtraArgs(admitted, {
|
|
22934
22967
|
packageRoot: env.packageRoot,
|
|
22935
22968
|
...env.model === void 0 ? {} : { model: env.model },
|
|
@@ -23814,6 +23847,8 @@ async function runPublicFixer(argv, env, io, parseFixerArgv2) {
|
|
|
23814
23847
|
return runWithAutoResumeLoop({
|
|
23815
23848
|
admitted,
|
|
23816
23849
|
io,
|
|
23850
|
+
// #422: pass-through only; the loop entry resolves the default and validates the domain once.
|
|
23851
|
+
autoResumeLimit: env.autoResumeLimit,
|
|
23817
23852
|
buildInitialArgs: () => buildFixerActivationExtraArgs(admitted, {
|
|
23818
23853
|
packageRoot: env.packageRoot,
|
|
23819
23854
|
...env.model === void 0 ? {} : { model: env.model },
|
|
@@ -24178,6 +24213,8 @@ async function runPublicJudge(argv, env, io, parseJudgeArgv2) {
|
|
|
24178
24213
|
return runWithAutoResumeLoop({
|
|
24179
24214
|
admitted,
|
|
24180
24215
|
io,
|
|
24216
|
+
// #422: pass-through only; the loop entry resolves the default and validates the domain once.
|
|
24217
|
+
autoResumeLimit: env.autoResumeLimit,
|
|
24181
24218
|
buildInitialArgs: () => buildJudgeActivationExtraArgs(admitted, {
|
|
24182
24219
|
packageRoot: env.packageRoot,
|
|
24183
24220
|
...env.model === void 0 ? {} : { model: env.model },
|
|
@@ -24625,6 +24662,8 @@ async function runPublicMerger(argv, env, io, parseMergerArgv2) {
|
|
|
24625
24662
|
return runWithAutoResumeLoop({
|
|
24626
24663
|
admitted,
|
|
24627
24664
|
io,
|
|
24665
|
+
// #422: pass-through only; the loop entry resolves the default and validates the domain once.
|
|
24666
|
+
autoResumeLimit: env.autoResumeLimit,
|
|
24628
24667
|
buildInitialArgs: () => buildMergerActivationExtraArgs(admitted, {
|
|
24629
24668
|
packageRoot: env.packageRoot,
|
|
24630
24669
|
...env.model === void 0 ? {} : { model: env.model },
|
|
@@ -25041,6 +25080,8 @@ async function runPublicReviewer(argv, env, io, parseReviewerArgv2) {
|
|
|
25041
25080
|
return runWithAutoResumeLoop({
|
|
25042
25081
|
admitted,
|
|
25043
25082
|
io,
|
|
25083
|
+
// #422: pass-through only; the loop entry resolves the default and validates the domain once.
|
|
25084
|
+
autoResumeLimit: env.autoResumeLimit,
|
|
25044
25085
|
buildInitialArgs: () => buildReviewerActivationExtraArgs(admitted, {
|
|
25045
25086
|
packageRoot: env.packageRoot,
|
|
25046
25087
|
...env.model === void 0 ? {} : { model: env.model },
|
|
@@ -27594,6 +27635,7 @@ function renderConfig(config) {
|
|
|
27594
27635
|
lines.push(`${seat} ${formatModelSpec(selection)} ${engine}`);
|
|
27595
27636
|
}
|
|
27596
27637
|
}
|
|
27638
|
+
lines.push(`autoResumeLimit ${config.autoResumeLimit ?? AUTO_RESUME_LIMIT}`);
|
|
27597
27639
|
return `${lines.join("\n")}
|
|
27598
27640
|
`;
|
|
27599
27641
|
}
|
|
@@ -27687,6 +27729,30 @@ async function runConfigCommand(args, home, packageRoot2, io) {
|
|
|
27687
27729
|
io.stdout(renderConfig(config));
|
|
27688
27730
|
return 0;
|
|
27689
27731
|
}
|
|
27732
|
+
if (args[0] === "set-auto-resume-limit") {
|
|
27733
|
+
if (args.length !== 2) {
|
|
27734
|
+
throw new CliUsageError(
|
|
27735
|
+
"usage: ak-role config set-auto-resume-limit <N>"
|
|
27736
|
+
);
|
|
27737
|
+
}
|
|
27738
|
+
const raw = args[1];
|
|
27739
|
+
if (!/^[0-9]+$/.test(raw)) {
|
|
27740
|
+
throw new CliUsageError(
|
|
27741
|
+
`auto-resume limit must be a non-negative integer, got ${raw}`
|
|
27742
|
+
);
|
|
27743
|
+
}
|
|
27744
|
+
const converted = Number(raw);
|
|
27745
|
+
if (!Number.isFinite(converted) || BigInt(converted) !== BigInt(raw)) {
|
|
27746
|
+
throw new CliUsageError(
|
|
27747
|
+
`auto-resume limit ${raw} is not exactly representable as a number; refusing to silently round the value`
|
|
27748
|
+
);
|
|
27749
|
+
}
|
|
27750
|
+
let config = await loadAndValidateConfig(home, packageRoot2);
|
|
27751
|
+
config = setAutoResumeLimit(config, converted);
|
|
27752
|
+
await savePublicCliConfig(config, home);
|
|
27753
|
+
io.stdout(renderConfig(config));
|
|
27754
|
+
return 0;
|
|
27755
|
+
}
|
|
27690
27756
|
throw new CliUsageError(`unknown config subcommand: ${args[0]}`);
|
|
27691
27757
|
}
|
|
27692
27758
|
async function runAkRole(argv, env) {
|
|
@@ -27896,7 +27962,9 @@ async function runAkRole(argv, env) {
|
|
|
27896
27962
|
...projectSeatEngine(seat),
|
|
27897
27963
|
...env.judgeExtraPiArgs === void 0 ? {} : { extraPiArgs: env.judgeExtraPiArgs },
|
|
27898
27964
|
...env.judgeTimeoutMs === void 0 ? {} : { timeoutMs: env.judgeTimeoutMs },
|
|
27899
|
-
...env.createRunId === void 0 ? {} : { createRunId: env.createRunId }
|
|
27965
|
+
...env.createRunId === void 0 ? {} : { createRunId: env.createRunId },
|
|
27966
|
+
// #422: effective auto-resume ceiling resolved once here; the loop never re-reads disk.
|
|
27967
|
+
...config.autoResumeLimit === void 0 ? {} : { autoResumeLimit: config.autoResumeLimit }
|
|
27900
27968
|
},
|
|
27901
27969
|
io,
|
|
27902
27970
|
PUBLIC_ROLE_ARGV.judge.parse
|
|
@@ -27931,7 +27999,9 @@ async function runAkRole(argv, env) {
|
|
|
27931
27999
|
...projectSeatEngine(seat),
|
|
27932
28000
|
...env.coderExtraPiArgs === void 0 ? {} : { extraPiArgs: env.coderExtraPiArgs },
|
|
27933
28001
|
...env.coderTimeoutMs === void 0 ? {} : { timeoutMs: env.coderTimeoutMs },
|
|
27934
|
-
...env.createRunId === void 0 ? {} : { createRunId: env.createRunId }
|
|
28002
|
+
...env.createRunId === void 0 ? {} : { createRunId: env.createRunId },
|
|
28003
|
+
// #422: effective auto-resume ceiling resolved once here; the loop never re-reads disk.
|
|
28004
|
+
...config.autoResumeLimit === void 0 ? {} : { autoResumeLimit: config.autoResumeLimit }
|
|
27935
28005
|
},
|
|
27936
28006
|
io,
|
|
27937
28007
|
PUBLIC_ROLE_ARGV.coder.parse
|
|
@@ -27966,7 +28036,9 @@ async function runAkRole(argv, env) {
|
|
|
27966
28036
|
...projectSeatEngine(seat),
|
|
27967
28037
|
...env.fixerExtraPiArgs === void 0 ? {} : { extraPiArgs: env.fixerExtraPiArgs },
|
|
27968
28038
|
...env.fixerTimeoutMs === void 0 ? {} : { timeoutMs: env.fixerTimeoutMs },
|
|
27969
|
-
...env.createRunId === void 0 ? {} : { createRunId: env.createRunId }
|
|
28039
|
+
...env.createRunId === void 0 ? {} : { createRunId: env.createRunId },
|
|
28040
|
+
// #422: effective auto-resume ceiling resolved once here; the loop never re-reads disk.
|
|
28041
|
+
...config.autoResumeLimit === void 0 ? {} : { autoResumeLimit: config.autoResumeLimit }
|
|
27970
28042
|
},
|
|
27971
28043
|
io,
|
|
27972
28044
|
PUBLIC_ROLE_ARGV.fixer.parse
|
|
@@ -28036,7 +28108,9 @@ async function runAkRole(argv, env) {
|
|
|
28036
28108
|
...projectSeatEngine(seat),
|
|
28037
28109
|
...env.reviewerExtraPiArgs === void 0 ? {} : { extraPiArgs: env.reviewerExtraPiArgs },
|
|
28038
28110
|
...env.reviewerTimeoutMs === void 0 ? {} : { timeoutMs: env.reviewerTimeoutMs },
|
|
28039
|
-
...env.createRunId === void 0 ? {} : { createRunId: env.createRunId }
|
|
28111
|
+
...env.createRunId === void 0 ? {} : { createRunId: env.createRunId },
|
|
28112
|
+
// #422: effective auto-resume ceiling resolved once here; the loop never re-reads disk.
|
|
28113
|
+
...config.autoResumeLimit === void 0 ? {} : { autoResumeLimit: config.autoResumeLimit }
|
|
28040
28114
|
},
|
|
28041
28115
|
io,
|
|
28042
28116
|
PUBLIC_ROLE_ARGV.reviewer.parse
|
|
@@ -28106,7 +28180,9 @@ async function runAkRole(argv, env) {
|
|
|
28106
28180
|
...projectSeatEngine(seat),
|
|
28107
28181
|
...env.mergerExtraPiArgs === void 0 ? {} : { extraPiArgs: env.mergerExtraPiArgs },
|
|
28108
28182
|
...env.mergerTimeoutMs === void 0 ? {} : { timeoutMs: env.mergerTimeoutMs },
|
|
28109
|
-
...env.createRunId === void 0 ? {} : { createRunId: env.createRunId }
|
|
28183
|
+
...env.createRunId === void 0 ? {} : { createRunId: env.createRunId },
|
|
28184
|
+
// #422: effective auto-resume ceiling resolved once here; the loop never re-reads disk.
|
|
28185
|
+
...config.autoResumeLimit === void 0 ? {} : { autoResumeLimit: config.autoResumeLimit }
|
|
28110
28186
|
},
|
|
28111
28187
|
io,
|
|
28112
28188
|
PUBLIC_ROLE_ARGV.merger.parse
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Single generic auto-resume loop for #416 (owner scope = single LLM call).
|
|
3
|
-
* Call-local retries, at most
|
|
3
|
+
* Call-local retries, at most the effective autoResumeLimit times (injected once
|
|
4
|
+
* per call by the caller, #422 — never re-read from disk inside the loop),
|
|
5
|
+
* in-place (same runId/session).
|
|
4
6
|
* Unifies presentation: intermediate attempts use dummyIo, only final Terminal is presented.
|
|
5
7
|
*/
|
|
6
8
|
import { AUTO_RESUME_LIMIT, isSessionPrincipalAvailable, acquireRunWriterLease, RunWriterLeaseHeldError, type RunWriterLease } from "./run-lifecycle.ts";
|
|
9
|
+
import { parseAutoResumeLimit } from "./config.ts";
|
|
7
10
|
import { isLawfulTypedTerminalOutcome, formatTerminalResult, type TerminalResult } from "./terminal.ts";
|
|
8
11
|
import { presentFailureTerminal, presentStructuralRejection } from "./settlement.ts";
|
|
9
12
|
import type { CliIo } from "./cli-io.ts";
|
|
@@ -26,10 +29,21 @@ export type AutoResumeDispatchResult = {
|
|
|
26
29
|
export async function runWithAutoResumeLoop<T extends AutoResumeDispatchResult>(options: {
|
|
27
30
|
admitted: { sessionFile: string; runDirectory: string };
|
|
28
31
|
io: CliIo;
|
|
32
|
+
/**
|
|
33
|
+
* Effective ceiling (#422), resolved by the caller before the loop; never re-read
|
|
34
|
+
* per round. undefined = package default (AUTO_RESUME_LIMIT). Domain-validated at
|
|
35
|
+
* this single entry point (#422): NaN/negative/fractional/Infinity reject loudly
|
|
36
|
+
* before the first dispatch instead of silently bypassing the ceiling comparison.
|
|
37
|
+
*/
|
|
38
|
+
autoResumeLimit?: number | undefined;
|
|
29
39
|
buildInitialArgs: () => string[];
|
|
30
40
|
buildResumeArgs: () => string[];
|
|
31
41
|
dispatch: (extraArgs: string[], lease: RunWriterLease, isFirst: boolean, attemptIo: CliIo) => Promise<T>;
|
|
32
42
|
}): Promise<T> {
|
|
43
|
+
// #422 single-point resolution + domain validation. NaN would bypass every
|
|
44
|
+
// `attempts >= limit` comparison (always false) — reject here, before any dispatch.
|
|
45
|
+
const limit = options.autoResumeLimit ?? AUTO_RESUME_LIMIT;
|
|
46
|
+
parseAutoResumeLimit(limit);
|
|
33
47
|
let autoResumeAttempts = 0;
|
|
34
48
|
let isFirst = true;
|
|
35
49
|
let currentExtraArgs = options.buildInitialArgs();
|
|
@@ -64,7 +78,7 @@ export async function runWithAutoResumeLoop<T extends AutoResumeDispatchResult>(
|
|
|
64
78
|
return result;
|
|
65
79
|
}
|
|
66
80
|
|
|
67
|
-
if (autoResumeAttempts >=
|
|
81
|
+
if (autoResumeAttempts >= limit) {
|
|
68
82
|
if (terminal !== undefined) presentTerminal(terminal, options.io);
|
|
69
83
|
return result;
|
|
70
84
|
}
|
package/src/public-cli/cli.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
parseModelSpec,
|
|
18
18
|
resolveEffectiveSeat,
|
|
19
19
|
savePublicCliConfig,
|
|
20
|
+
setAutoResumeLimit,
|
|
20
21
|
setPersistentSeatConfig,
|
|
21
22
|
setPersistentSeatEngine,
|
|
22
23
|
validatePublicCliConfigEngines,
|
|
@@ -58,7 +59,7 @@ import { runPublicJudge, runPublicResume } from "./judge-run.ts";
|
|
|
58
59
|
import { runPublicMerger, runPublicMergerResume } from "./merger-run.ts";
|
|
59
60
|
import { runPublicReviewer, runPublicReviewerResume } from "./reviewer-run.ts";
|
|
60
61
|
import { runPublicTaishi } from "./taishi-run.ts";
|
|
61
|
-
import { peekRoleRunRole } from "./run-lifecycle.ts";
|
|
62
|
+
import { AUTO_RESUME_LIMIT, peekRoleRunRole } from "./run-lifecycle.ts";
|
|
62
63
|
import {
|
|
63
64
|
AUTOMATIC_NAVIGATOR_SEAT,
|
|
64
65
|
INTERNAL_ROLE_ENTRYPOINT_RELATIVE,
|
|
@@ -527,6 +528,8 @@ function renderConfig(config: PublicCliConfig): string {
|
|
|
527
528
|
lines.push(`${seat}\t${formatModelSpec(selection)}\t${engine}`);
|
|
528
529
|
}
|
|
529
530
|
}
|
|
531
|
+
// #422: show the effective auto-resume ceiling (configured value or default).
|
|
532
|
+
lines.push(`autoResumeLimit\t${config.autoResumeLimit ?? AUTO_RESUME_LIMIT}`);
|
|
530
533
|
return `${lines.join("\n")}\n`;
|
|
531
534
|
}
|
|
532
535
|
|
|
@@ -631,6 +634,39 @@ async function runConfigCommand(
|
|
|
631
634
|
return 0;
|
|
632
635
|
}
|
|
633
636
|
|
|
637
|
+
// #422: standalone verb per the set-engine/unset-engine precedent — the
|
|
638
|
+
// existing `config set` grammar stays strictly even-position seat/spec pairs.
|
|
639
|
+
if (args[0] === "set-auto-resume-limit") {
|
|
640
|
+
if (args.length !== 2) {
|
|
641
|
+
throw new CliUsageError(
|
|
642
|
+
"usage: ak-role config set-auto-resume-limit <N>",
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
const raw = args[1]!;
|
|
646
|
+
if (!/^[0-9]+$/.test(raw)) {
|
|
647
|
+
throw new CliUsageError(
|
|
648
|
+
`auto-resume limit must be a non-negative integer, got ${raw}`,
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
// #422 fidelity boundary (not an upper bound — ADR 0035 stays intact): the
|
|
652
|
+
// persisted representation is a JS number, and Number() silently rounds
|
|
653
|
+
// integers beyond 2^53-1 (e.g. 9007199254740993 → 9007199254740992). Refuse
|
|
654
|
+
// loudly instead of persisting a different N; every exactly-representable
|
|
655
|
+
// non-negative integer remains legal. The regex above guarantees pure
|
|
656
|
+
// digits, so BigInt(raw) has no leading-zero ambiguity.
|
|
657
|
+
const converted = Number(raw);
|
|
658
|
+
if (!Number.isFinite(converted) || BigInt(converted) !== BigInt(raw)) {
|
|
659
|
+
throw new CliUsageError(
|
|
660
|
+
`auto-resume limit ${raw} is not exactly representable as a number; refusing to silently round the value`,
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
let config = await loadAndValidateConfig(home, packageRoot);
|
|
664
|
+
config = setAutoResumeLimit(config, converted);
|
|
665
|
+
await savePublicCliConfig(config, home);
|
|
666
|
+
io.stdout(renderConfig(config));
|
|
667
|
+
return 0;
|
|
668
|
+
}
|
|
669
|
+
|
|
634
670
|
throw new CliUsageError(`unknown config subcommand: ${args[0]}`);
|
|
635
671
|
}
|
|
636
672
|
|
|
@@ -922,6 +958,10 @@ export async function runAkRole(
|
|
|
922
958
|
? {}
|
|
923
959
|
: { timeoutMs: env.judgeTimeoutMs }),
|
|
924
960
|
...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
|
|
961
|
+
// #422: effective auto-resume ceiling resolved once here; the loop never re-reads disk.
|
|
962
|
+
...(config.autoResumeLimit === undefined
|
|
963
|
+
? {}
|
|
964
|
+
: { autoResumeLimit: config.autoResumeLimit }),
|
|
925
965
|
},
|
|
926
966
|
io,
|
|
927
967
|
PUBLIC_ROLE_ARGV.judge.parse,
|
|
@@ -966,6 +1006,10 @@ export async function runAkRole(
|
|
|
966
1006
|
? {}
|
|
967
1007
|
: { timeoutMs: env.coderTimeoutMs }),
|
|
968
1008
|
...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
|
|
1009
|
+
// #422: effective auto-resume ceiling resolved once here; the loop never re-reads disk.
|
|
1010
|
+
...(config.autoResumeLimit === undefined
|
|
1011
|
+
? {}
|
|
1012
|
+
: { autoResumeLimit: config.autoResumeLimit }),
|
|
969
1013
|
},
|
|
970
1014
|
io,
|
|
971
1015
|
PUBLIC_ROLE_ARGV.coder.parse,
|
|
@@ -1010,6 +1054,10 @@ export async function runAkRole(
|
|
|
1010
1054
|
? {}
|
|
1011
1055
|
: { timeoutMs: env.fixerTimeoutMs }),
|
|
1012
1056
|
...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
|
|
1057
|
+
// #422: effective auto-resume ceiling resolved once here; the loop never re-reads disk.
|
|
1058
|
+
...(config.autoResumeLimit === undefined
|
|
1059
|
+
? {}
|
|
1060
|
+
: { autoResumeLimit: config.autoResumeLimit }),
|
|
1013
1061
|
},
|
|
1014
1062
|
io,
|
|
1015
1063
|
PUBLIC_ROLE_ARGV.fixer.parse,
|
|
@@ -1098,6 +1146,10 @@ export async function runAkRole(
|
|
|
1098
1146
|
? {}
|
|
1099
1147
|
: { timeoutMs: env.reviewerTimeoutMs }),
|
|
1100
1148
|
...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
|
|
1149
|
+
// #422: effective auto-resume ceiling resolved once here; the loop never re-reads disk.
|
|
1150
|
+
...(config.autoResumeLimit === undefined
|
|
1151
|
+
? {}
|
|
1152
|
+
: { autoResumeLimit: config.autoResumeLimit }),
|
|
1101
1153
|
},
|
|
1102
1154
|
io,
|
|
1103
1155
|
PUBLIC_ROLE_ARGV.reviewer.parse,
|
|
@@ -1186,6 +1238,10 @@ export async function runAkRole(
|
|
|
1186
1238
|
? {}
|
|
1187
1239
|
: { timeoutMs: env.mergerTimeoutMs }),
|
|
1188
1240
|
...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
|
|
1241
|
+
// #422: effective auto-resume ceiling resolved once here; the loop never re-reads disk.
|
|
1242
|
+
...(config.autoResumeLimit === undefined
|
|
1243
|
+
? {}
|
|
1244
|
+
: { autoResumeLimit: config.autoResumeLimit }),
|
|
1189
1245
|
},
|
|
1190
1246
|
io,
|
|
1191
1247
|
PUBLIC_ROLE_ARGV.merger.parse,
|
|
@@ -86,6 +86,8 @@ export type CoderRunEnv = {
|
|
|
86
86
|
engine?: string;
|
|
87
87
|
credentials?: CredentialProviders;
|
|
88
88
|
createRunId?: () => string;
|
|
89
|
+
/** #422: effective single-call auto-resume ceiling; undefined = package default (AUTO_RESUME_LIMIT). */
|
|
90
|
+
autoResumeLimit?: number;
|
|
89
91
|
extraPiArgs?: readonly string[];
|
|
90
92
|
timeoutMs?: number;
|
|
91
93
|
};
|
|
@@ -475,6 +477,8 @@ export async function runPublicCoder(
|
|
|
475
477
|
return runWithAutoResumeLoop({
|
|
476
478
|
admitted,
|
|
477
479
|
io,
|
|
480
|
+
// #422: pass-through only; the loop entry resolves the default and validates the domain once.
|
|
481
|
+
autoResumeLimit: env.autoResumeLimit,
|
|
478
482
|
buildInitialArgs: () =>
|
|
479
483
|
buildCoderActivationExtraArgs(admitted, {
|
|
480
484
|
packageRoot: env.packageRoot,
|
package/src/public-cli/config.ts
CHANGED
|
@@ -33,6 +33,13 @@ export type PersistentSeatConfig = SeatModelConfig & {
|
|
|
33
33
|
|
|
34
34
|
export type PublicCliConfig = {
|
|
35
35
|
seats: Partial<Record<PublicConfigurableSeat, PersistentSeatConfig>>;
|
|
36
|
+
/**
|
|
37
|
+
* #422: single-call auto-resume retry ceiling, sibling of `seats`.
|
|
38
|
+
* Non-negative integer; 0 disables auto-resume (one dispatch per call).
|
|
39
|
+
* undefined = package default (AUTO_RESUME_LIMIT). No package-local upper
|
|
40
|
+
* bound (ADR 0035).
|
|
41
|
+
*/
|
|
42
|
+
autoResumeLimit?: number;
|
|
36
43
|
};
|
|
37
44
|
|
|
38
45
|
export type EffectiveSource = "persistent" | "startup" | "invocation" | "unconfigured";
|
|
@@ -108,6 +115,9 @@ export function setPersistentSeatConfig(
|
|
|
108
115
|
): PublicCliConfig {
|
|
109
116
|
const previous = config.seats[seat];
|
|
110
117
|
return {
|
|
118
|
+
// Spread preserves sibling top-level keys such as autoResumeLimit (#422):
|
|
119
|
+
// a seat write must never silently drop them.
|
|
120
|
+
...config,
|
|
111
121
|
seats: {
|
|
112
122
|
...config.seats,
|
|
113
123
|
[seat]: {
|
|
@@ -146,6 +156,7 @@ export function setPersistentSeatEngine(
|
|
|
146
156
|
if (engine === undefined) {
|
|
147
157
|
const { engine: _dropped, ...modelOnly } = previous;
|
|
148
158
|
return {
|
|
159
|
+
...config,
|
|
149
160
|
seats: {
|
|
150
161
|
...config.seats,
|
|
151
162
|
[seat]: modelOnly,
|
|
@@ -155,6 +166,7 @@ export function setPersistentSeatEngine(
|
|
|
155
166
|
// Engine-name path-safety syntax is owned solely by assertLegalEngineName
|
|
156
167
|
// (call-request + config-parse seams). Setter is pure seat mutation.
|
|
157
168
|
return {
|
|
169
|
+
...config,
|
|
158
170
|
seats: {
|
|
159
171
|
...config.seats,
|
|
160
172
|
[seat]: { ...previous, engine },
|
|
@@ -162,6 +174,32 @@ export function setPersistentSeatEngine(
|
|
|
162
174
|
};
|
|
163
175
|
}
|
|
164
176
|
|
|
177
|
+
/**
|
|
178
|
+
* #422 value domain authority for the auto-resume ceiling: non-negative integer,
|
|
179
|
+
* no package-local upper bound (ADR 0035). `0` is legal and means auto-resume is
|
|
180
|
+
* disabled (a single dispatch, no in-place retry). Negative numbers, fractions,
|
|
181
|
+
* NaN, Infinity and non-number types are rejected loudly — never silently coerced.
|
|
182
|
+
*/
|
|
183
|
+
export function parseAutoResumeLimit(value: unknown): number {
|
|
184
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
`auto-resume limit must be a non-negative integer, got ${JSON.stringify(value)}`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
return value;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* #422: set the persistent autoResumeLimit top-level key. Pure mutation that
|
|
194
|
+
* preserves all sibling keys (seats included).
|
|
195
|
+
*/
|
|
196
|
+
export function setAutoResumeLimit(
|
|
197
|
+
config: PublicCliConfig,
|
|
198
|
+
limit: number,
|
|
199
|
+
): PublicCliConfig {
|
|
200
|
+
return { ...config, autoResumeLimit: parseAutoResumeLimit(limit) };
|
|
201
|
+
}
|
|
202
|
+
|
|
165
203
|
/** Strip optional engine so activation model argv never sees the engine axis. */
|
|
166
204
|
export function seatModelOnly(seat: PersistentSeatConfig): SeatModelConfig {
|
|
167
205
|
return seat.thinking === undefined
|
|
@@ -261,9 +299,18 @@ function parsePublicCliConfig(value: unknown): PublicCliConfig {
|
|
|
261
299
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
262
300
|
throw new Error("public CLI config must be an object");
|
|
263
301
|
}
|
|
264
|
-
const record = value as { seats?: unknown };
|
|
302
|
+
const record = value as { seats?: unknown; autoResumeLimit?: unknown };
|
|
303
|
+
// #422 round-trip preservation: the sibling top-level key must survive every
|
|
304
|
+
// parse→save cycle; an unknown-key drop would silently erase it on any write.
|
|
305
|
+
let autoResumeLimit: number | undefined;
|
|
306
|
+
if (record.autoResumeLimit !== undefined) {
|
|
307
|
+
autoResumeLimit = parseAutoResumeLimit(record.autoResumeLimit);
|
|
308
|
+
}
|
|
265
309
|
if (record.seats === undefined) {
|
|
266
|
-
return {
|
|
310
|
+
return {
|
|
311
|
+
seats: {},
|
|
312
|
+
...(autoResumeLimit === undefined ? {} : { autoResumeLimit }),
|
|
313
|
+
};
|
|
267
314
|
}
|
|
268
315
|
if (
|
|
269
316
|
record.seats === null ||
|
|
@@ -281,7 +328,10 @@ function parsePublicCliConfig(value: unknown): PublicCliConfig {
|
|
|
281
328
|
}
|
|
282
329
|
seats[key as PublicConfigurableSeat] = parseSeatModelConfig(raw, key);
|
|
283
330
|
}
|
|
284
|
-
return {
|
|
331
|
+
return {
|
|
332
|
+
seats,
|
|
333
|
+
...(autoResumeLimit === undefined ? {} : { autoResumeLimit }),
|
|
334
|
+
};
|
|
285
335
|
}
|
|
286
336
|
|
|
287
337
|
function parseSeatModelConfig(value: unknown, seat: string): PersistentSeatConfig {
|
|
@@ -89,6 +89,8 @@ export type FixerRunEnv = {
|
|
|
89
89
|
engine?: string;
|
|
90
90
|
credentials?: CredentialProviders;
|
|
91
91
|
createRunId?: () => string;
|
|
92
|
+
/** #422: effective single-call auto-resume ceiling; undefined = package default (AUTO_RESUME_LIMIT). */
|
|
93
|
+
autoResumeLimit?: number;
|
|
92
94
|
extraPiArgs?: readonly string[];
|
|
93
95
|
timeoutMs?: number;
|
|
94
96
|
};
|
|
@@ -494,6 +496,8 @@ export async function runPublicFixer(
|
|
|
494
496
|
return runWithAutoResumeLoop({
|
|
495
497
|
admitted,
|
|
496
498
|
io,
|
|
499
|
+
// #422: pass-through only; the loop entry resolves the default and validates the domain once.
|
|
500
|
+
autoResumeLimit: env.autoResumeLimit,
|
|
497
501
|
buildInitialArgs: () =>
|
|
498
502
|
buildFixerActivationExtraArgs(admitted, {
|
|
499
503
|
packageRoot: env.packageRoot,
|
|
@@ -89,6 +89,8 @@ export type JudgeRunEnv = {
|
|
|
89
89
|
*/
|
|
90
90
|
credentials?: CredentialProviders;
|
|
91
91
|
createRunId?: () => string;
|
|
92
|
+
/** #422: effective single-call auto-resume ceiling; undefined = package default (AUTO_RESUME_LIMIT). */
|
|
93
|
+
autoResumeLimit?: number;
|
|
92
94
|
/** Extra Pi args inserted before the prompt (tests: faux provider extension). */
|
|
93
95
|
extraPiArgs?: readonly string[];
|
|
94
96
|
/** Override default role-run timeout. */
|
|
@@ -463,6 +465,8 @@ export async function runPublicJudge(
|
|
|
463
465
|
return runWithAutoResumeLoop({
|
|
464
466
|
admitted,
|
|
465
467
|
io,
|
|
468
|
+
// #422: pass-through only; the loop entry resolves the default and validates the domain once.
|
|
469
|
+
autoResumeLimit: env.autoResumeLimit,
|
|
466
470
|
buildInitialArgs: () =>
|
|
467
471
|
buildJudgeActivationExtraArgs(admitted, {
|
|
468
472
|
packageRoot: env.packageRoot,
|
|
@@ -90,6 +90,8 @@ export type MergerRunEnv = {
|
|
|
90
90
|
engine?: string;
|
|
91
91
|
credentials?: CredentialProviders;
|
|
92
92
|
createRunId?: () => string;
|
|
93
|
+
/** #422: effective single-call auto-resume ceiling; undefined = package default (AUTO_RESUME_LIMIT). */
|
|
94
|
+
autoResumeLimit?: number;
|
|
93
95
|
extraPiArgs?: readonly string[];
|
|
94
96
|
timeoutMs?: number;
|
|
95
97
|
};
|
|
@@ -575,6 +577,8 @@ export async function runPublicMerger(
|
|
|
575
577
|
return runWithAutoResumeLoop({
|
|
576
578
|
admitted,
|
|
577
579
|
io,
|
|
580
|
+
// #422: pass-through only; the loop entry resolves the default and validates the domain once.
|
|
581
|
+
autoResumeLimit: env.autoResumeLimit,
|
|
578
582
|
buildInitialArgs: () =>
|
|
579
583
|
buildMergerActivationExtraArgs(admitted, {
|
|
580
584
|
packageRoot: env.packageRoot,
|
|
@@ -1098,10 +1098,12 @@ const SUPPORT_COMMAND_HELP = {
|
|
|
1098
1098
|
"ak-role config set <seat> <provider/model[:thinking]> [<seat> <spec> ...]",
|
|
1099
1099
|
"ak-role config set-engine <seat> <name>",
|
|
1100
1100
|
"ak-role config unset-engine <seat>",
|
|
1101
|
+
"ak-role config set-auto-resume-limit <N>",
|
|
1101
1102
|
],
|
|
1102
1103
|
examples: [
|
|
1103
1104
|
"ak-role config set judge openai-codex/gpt-5.6-sol:high",
|
|
1104
1105
|
"ak-role config set-engine judge opus",
|
|
1106
|
+
"ak-role config set-auto-resume-limit 3",
|
|
1105
1107
|
],
|
|
1106
1108
|
},
|
|
1107
1109
|
help: {
|
|
@@ -91,6 +91,8 @@ export type ReviewerRunEnv = {
|
|
|
91
91
|
engine?: string;
|
|
92
92
|
credentials?: CredentialProviders;
|
|
93
93
|
createRunId?: () => string;
|
|
94
|
+
/** #422: effective single-call auto-resume ceiling; undefined = package default (AUTO_RESUME_LIMIT). */
|
|
95
|
+
autoResumeLimit?: number;
|
|
94
96
|
extraPiArgs?: readonly string[];
|
|
95
97
|
timeoutMs?: number;
|
|
96
98
|
};
|
|
@@ -514,6 +516,8 @@ export async function runPublicReviewer(
|
|
|
514
516
|
return runWithAutoResumeLoop({
|
|
515
517
|
admitted,
|
|
516
518
|
io,
|
|
519
|
+
// #422: pass-through only; the loop entry resolves the default and validates the domain once.
|
|
520
|
+
autoResumeLimit: env.autoResumeLimit,
|
|
517
521
|
buildInitialArgs: () =>
|
|
518
522
|
buildReviewerActivationExtraArgs(admitted, {
|
|
519
523
|
packageRoot: env.packageRoot,
|
|
@@ -52,7 +52,10 @@ export type TypedHttp429Observation = {
|
|
|
52
52
|
readonly provider: V1ResumableProvider;
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
-
/**
|
|
55
|
+
/** Default for the #422 configurable single-call auto-resume ceiling
|
|
56
|
+
* (public-cli.json top-level `autoResumeLimit`). No longer the runtime truth
|
|
57
|
+
* source: runWithAutoResumeLoop receives the effective value once per call.
|
|
58
|
+
*/
|
|
56
59
|
export const AUTO_RESUME_LIMIT = 2 as const;
|
|
57
60
|
|
|
58
61
|
export type RoleRunRecord = {
|