@akagilnc/pi-workflow-roles 0.1.2152 → 0.1.2157
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 +455 -360
- package/package.json +1 -1
- package/src/public-cli/auto-resume.ts +100 -0
- package/src/public-cli/coder-run.ts +34 -29
- package/src/public-cli/fixer-run.ts +31 -30
- package/src/public-cli/judge-run.ts +32 -28
- package/src/public-cli/merger-run.ts +31 -29
- package/src/public-cli/reviewer-run.ts +31 -30
- package/src/public-cli/run-lifecycle.ts +43 -25
- package/src/public-cli/terminal.ts +8 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Durable Role run lifecycle for public CLI (ADR 0052 / #11 / #108).
|
|
2
|
+
* Durable Role run lifecycle for public CLI (ADR 0052 / #11 / #108 / #416).
|
|
3
3
|
* States: admitted → running → resumable | terminal.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* #416 (owner 2026-08-22): resume no longer gates on terminal/resumable or typed 429 —
|
|
5
|
+
* any existing run with an available Pi session principal may be resumed; caller decides.
|
|
6
|
+
* Prose is never regex-classified as quota evidence.
|
|
6
7
|
*/
|
|
7
|
-
import { lstat, open, readdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
8
|
+
import { chmod, lstat, open, readdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
8
9
|
import { join } from "node:path";
|
|
9
10
|
|
|
10
11
|
import {
|
|
@@ -38,7 +39,9 @@ import {
|
|
|
38
39
|
type InvocationEffectiveModel,
|
|
39
40
|
} from "./invocation.ts";
|
|
40
41
|
|
|
41
|
-
/** Providers eligible for v1 typed-429 resume (Codex / xAI only).
|
|
42
|
+
/** Providers eligible for v1 typed-429 resume (Codex / xAI only).
|
|
43
|
+
* @deprecated v1 429-only gate removed by #416 (owner 2026-08-22: "根本不要有限制"). Kept for compatibility; do not use for new branching.
|
|
44
|
+
*/
|
|
42
45
|
export const V1_RESUMABLE_PROVIDERS = ["openai-codex", "xai"] as const;
|
|
43
46
|
export type V1ResumableProvider = (typeof V1_RESUMABLE_PROVIDERS)[number];
|
|
44
47
|
|
|
@@ -49,6 +52,9 @@ export type TypedHttp429Observation = {
|
|
|
49
52
|
readonly provider: V1ResumableProvider;
|
|
50
53
|
};
|
|
51
54
|
|
|
55
|
+
/** Per single LLM call auto-resume retries (call-local, no persistence). See #416 scope correction 2026-08-22. */
|
|
56
|
+
export const AUTO_RESUME_LIMIT = 2 as const;
|
|
57
|
+
|
|
52
58
|
export type RoleRunRecord = {
|
|
53
59
|
readonly runId: string;
|
|
54
60
|
readonly role:
|
|
@@ -69,7 +75,9 @@ export type RoleRunRecord = {
|
|
|
69
75
|
readonly admittedRequestPath: string;
|
|
70
76
|
/** Coder/Fixer — preserved for resume continuation. */
|
|
71
77
|
readonly phase?: CoderPhase | FixerPhase;
|
|
72
|
-
/** Present only while state === "resumable".
|
|
78
|
+
/** Present only while state === "resumable".
|
|
79
|
+
* @deprecated retained only for historical 429 runs; #416 no longer gates resume on this field.
|
|
80
|
+
*/
|
|
73
81
|
readonly resumable?: TypedHttp429Observation;
|
|
74
82
|
};
|
|
75
83
|
|
|
@@ -79,12 +87,14 @@ export const RESUME_TRANSPORT_ENVELOPE = "[ak-role:resume-continue]" as const;
|
|
|
79
87
|
const RUN_STATE_FILE = "run-state.json";
|
|
80
88
|
const WRITER_LOCK_FILE = "writer.lock";
|
|
81
89
|
|
|
90
|
+
/** @deprecated #416: 429-only classification removed; kept for compatibility. */
|
|
82
91
|
export function isV1ResumableProvider(
|
|
83
92
|
provider: string,
|
|
84
93
|
): provider is V1ResumableProvider {
|
|
85
94
|
return (V1_RESUMABLE_PROVIDERS as readonly string[]).includes(provider);
|
|
86
95
|
}
|
|
87
96
|
|
|
97
|
+
/** @deprecated #416: 429-only observation no longer gates resume; kept for historical runs. */
|
|
88
98
|
export async function readTypedHttp429Observation(
|
|
89
99
|
runDirectory: string,
|
|
90
100
|
): Promise<TypedHttp429Observation | undefined> {
|
|
@@ -96,6 +106,7 @@ export async function readTypedHttp429Observation(
|
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
/**
|
|
109
|
+
* @deprecated #416: 429-only resumability predicate removed from gating. Kept for compatibility.
|
|
99
110
|
* True only when a typed HTTP 429 was observed and no lawful terminal exists.
|
|
100
111
|
* Callers must pass the lawful-terminal fact from settlement, not re-infer it.
|
|
101
112
|
*/
|
|
@@ -266,6 +277,7 @@ export async function markRunRunning(
|
|
|
266
277
|
});
|
|
267
278
|
}
|
|
268
279
|
|
|
280
|
+
/** @deprecated #416: 429-only resumable marker; kept for historical runs. */
|
|
269
281
|
export async function markRunResumable(
|
|
270
282
|
runDirectory: string,
|
|
271
283
|
observation: TypedHttp429Observation,
|
|
@@ -352,7 +364,16 @@ export async function acquireRunWriterLease(
|
|
|
352
364
|
if (released) return;
|
|
353
365
|
released = true;
|
|
354
366
|
await handle.close().catch(() => undefined);
|
|
355
|
-
|
|
367
|
+
try {
|
|
368
|
+
await unlink(lockPath);
|
|
369
|
+
} catch (error) {
|
|
370
|
+
if ((error as { code?: unknown }).code === "EACCES") {
|
|
371
|
+
try {
|
|
372
|
+
await chmod(runDirectory, 0o755);
|
|
373
|
+
await unlink(lockPath);
|
|
374
|
+
} catch {}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
356
377
|
},
|
|
357
378
|
};
|
|
358
379
|
} catch (error) {
|
|
@@ -453,7 +474,7 @@ async function loadResumableRunRecord(
|
|
|
453
474
|
runId: string,
|
|
454
475
|
): Promise<{
|
|
455
476
|
readonly run: RoleRunRecord;
|
|
456
|
-
readonly observation
|
|
477
|
+
readonly observation?: TypedHttp429Observation;
|
|
457
478
|
readonly admittedFields: LoadedAdmittedRequestFields;
|
|
458
479
|
}> {
|
|
459
480
|
const runDirectory = await findRunDirectoryById(home, runId);
|
|
@@ -464,12 +485,8 @@ async function loadResumableRunRecord(
|
|
|
464
485
|
if (run === undefined) {
|
|
465
486
|
throw new CliUsageError(`unknown role run id: ${runId}`);
|
|
466
487
|
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
}
|
|
470
|
-
if (run.state !== "resumable" || run.resumable === undefined) {
|
|
471
|
-
throw new CliUsageError(`role run is not resumable: ${runId}`);
|
|
472
|
-
}
|
|
488
|
+
// #416: removed terminal/resumable gates per owner decision "根本不要有限制" (2026-08-22).
|
|
489
|
+
// Only the exact Pi session principal check remains as honest failure.
|
|
473
490
|
// Exact Pi session principal must be present before resume dispatches.
|
|
474
491
|
if (!(await isSessionPrincipalAvailable(run.sessionFile))) {
|
|
475
492
|
throw new CliUsageError(
|
|
@@ -618,7 +635,7 @@ async function loadResumableRunRecord(
|
|
|
618
635
|
}
|
|
619
636
|
return {
|
|
620
637
|
run,
|
|
621
|
-
observation: run.resumable,
|
|
638
|
+
...(run.resumable === undefined ? {} : { observation: run.resumable }),
|
|
622
639
|
admittedFields: {
|
|
623
640
|
instruction,
|
|
624
641
|
instructionEmpty,
|
|
@@ -641,25 +658,26 @@ async function loadResumableRunRecord(
|
|
|
641
658
|
export type LoadedResumableJudgeRun = {
|
|
642
659
|
readonly admitted: AdmittedJudgeInvocation;
|
|
643
660
|
readonly run: RoleRunRecord;
|
|
644
|
-
|
|
661
|
+
/** @deprecated #416: resumable observation no longer gates resume; may be undefined. */
|
|
662
|
+
readonly observation?: TypedHttp429Observation;
|
|
645
663
|
};
|
|
646
664
|
|
|
647
665
|
export type LoadedResumableCoderRun = {
|
|
648
666
|
readonly admitted: AdmittedCoderInvocation;
|
|
649
667
|
readonly run: RoleRunRecord;
|
|
650
|
-
readonly observation
|
|
668
|
+
readonly observation?: TypedHttp429Observation;
|
|
651
669
|
};
|
|
652
670
|
|
|
653
671
|
export type LoadedResumableFixerRun = {
|
|
654
672
|
readonly admitted: AdmittedFixerInvocation;
|
|
655
673
|
readonly run: RoleRunRecord;
|
|
656
|
-
readonly observation
|
|
674
|
+
readonly observation?: TypedHttp429Observation;
|
|
657
675
|
};
|
|
658
676
|
|
|
659
677
|
export type LoadedResumableReviewerRun = {
|
|
660
678
|
readonly admitted: AdmittedReviewerInvocation;
|
|
661
679
|
readonly run: RoleRunRecord;
|
|
662
|
-
readonly observation
|
|
680
|
+
readonly observation?: TypedHttp429Observation;
|
|
663
681
|
};
|
|
664
682
|
|
|
665
683
|
/**
|
|
@@ -693,7 +711,7 @@ export async function loadResumableJudgeRun(
|
|
|
693
711
|
return {
|
|
694
712
|
admitted,
|
|
695
713
|
run: loaded.run,
|
|
696
|
-
observation: loaded.observation,
|
|
714
|
+
...(loaded.observation === undefined ? {} : { observation: loaded.observation }),
|
|
697
715
|
};
|
|
698
716
|
}
|
|
699
717
|
|
|
@@ -747,7 +765,7 @@ export async function loadResumableCoderRun(
|
|
|
747
765
|
return {
|
|
748
766
|
admitted,
|
|
749
767
|
run: loaded.run,
|
|
750
|
-
observation: loaded.observation,
|
|
768
|
+
...(loaded.observation === undefined ? {} : { observation: loaded.observation }),
|
|
751
769
|
};
|
|
752
770
|
}
|
|
753
771
|
|
|
@@ -806,7 +824,7 @@ export async function loadResumableFixerRun(
|
|
|
806
824
|
return {
|
|
807
825
|
admitted,
|
|
808
826
|
run: loaded.run,
|
|
809
|
-
observation: loaded.observation,
|
|
827
|
+
...(loaded.observation === undefined ? {} : { observation: loaded.observation }),
|
|
810
828
|
};
|
|
811
829
|
}
|
|
812
830
|
|
|
@@ -853,14 +871,14 @@ export async function loadResumableReviewerRun(
|
|
|
853
871
|
return {
|
|
854
872
|
admitted,
|
|
855
873
|
run: loaded.run,
|
|
856
|
-
observation: loaded.observation,
|
|
874
|
+
...(loaded.observation === undefined ? {} : { observation: loaded.observation }),
|
|
857
875
|
};
|
|
858
876
|
}
|
|
859
877
|
|
|
860
878
|
export type LoadedResumableMergerRun = {
|
|
861
879
|
readonly admitted: AdmittedMergerInvocation;
|
|
862
880
|
readonly run: RoleRunRecord;
|
|
863
|
-
readonly observation
|
|
881
|
+
readonly observation?: TypedHttp429Observation;
|
|
864
882
|
};
|
|
865
883
|
|
|
866
884
|
/**
|
|
@@ -913,7 +931,7 @@ export async function loadResumableMergerRun(
|
|
|
913
931
|
return {
|
|
914
932
|
admitted,
|
|
915
933
|
run: loaded.run,
|
|
916
|
-
observation: loaded.observation,
|
|
934
|
+
...(loaded.observation === undefined ? {} : { observation: loaded.observation }),
|
|
917
935
|
};
|
|
918
936
|
}
|
|
919
937
|
|
|
@@ -231,11 +231,16 @@ export function redactExactRunId(text: string, runId: string): string {
|
|
|
231
231
|
* One admitted Role run's typed Terminal aggregate.
|
|
232
232
|
* Resumable failures carry `resume` and must not re-disclose the run ID via
|
|
233
233
|
* top-level `runId` or public artifact path components — only `resume.command`.
|
|
234
|
+
* #416: autoResumeCount (0..2) is a call-local observation of how many in-place
|
|
235
|
+
* auto-resumes occurred during this single LLM call; it is not persisted to
|
|
236
|
+
* run-state.json and does not participate in limit decisions.
|
|
234
237
|
*/
|
|
235
238
|
export type TerminalResult = {
|
|
236
239
|
roleOutcome: TerminalRoleOutcome;
|
|
237
240
|
navigator: TerminalNavigatorFact;
|
|
238
241
|
artifacts: readonly TerminalArtifactRef[];
|
|
242
|
+
/** Call-local auto-resume observation (0..2) for this single LLM call; read-only, not persisted. */
|
|
243
|
+
autoResumeCount?: number;
|
|
239
244
|
} & (
|
|
240
245
|
| {
|
|
241
246
|
/** Resumable failure: run ID appears only inside resume.command. */
|
|
@@ -330,5 +335,8 @@ export function formatTerminalResult(result: TerminalResult): string {
|
|
|
330
335
|
} else if (result.runId !== undefined) {
|
|
331
336
|
lines.push(`run\t${encodeTerminalField(result.runId)}`);
|
|
332
337
|
}
|
|
338
|
+
if (result.autoResumeCount !== undefined) {
|
|
339
|
+
lines.push(`autoResumeCount\t${encodeTerminalField(String(result.autoResumeCount))}`);
|
|
340
|
+
}
|
|
333
341
|
return `${lines.join("\n")}\n`;
|
|
334
342
|
}
|