@akagilnc/pi-workflow-roles 0.1.1883 → 0.1.1893
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/evidence-child-executor.js +153 -16
- package/dist/navigator-attendance.js +64 -29
- package/dist/public-cli/main.js +418 -204
- package/dist/reviewer-child-executor.js +1 -1
- package/dist/typed-provider-http.js +60 -0
- package/dist/upstream-error-testimony.js +19 -0
- package/package.json +1 -1
- package/scripts/build-package.mjs +2 -0
- package/src/evidence-child-executor.ts +177 -24
- package/src/navigator-attendance.ts +86 -33
- package/src/public-cli/coder-run.ts +25 -7
- package/src/public-cli/collector-run.ts +1 -0
- package/src/public-cli/doctor-run.ts +1 -0
- package/src/public-cli/explicit-internal.ts +61 -19
- package/src/public-cli/fixer-run.ts +25 -7
- package/src/public-cli/judge-run.ts +28 -7
- package/src/public-cli/merger-run.ts +25 -7
- package/src/public-cli/reviewer-run.ts +24 -7
- package/src/public-cli/run-lifecycle.ts +14 -76
- package/src/public-cli/settlement.ts +256 -66
- package/src/reviewer-child-executor.ts +1 -1
- package/src/role-runtime.ts +18 -8
- package/src/typed-provider-http.ts +99 -0
- package/src/upstream-error-testimony.ts +45 -0
|
@@ -12,6 +12,15 @@ import {
|
|
|
12
12
|
resolveActivationLedgerHome,
|
|
13
13
|
} from "../activation-ledger-topology.ts";
|
|
14
14
|
import { CliUsageError } from "./cli-errors.ts";
|
|
15
|
+
import {
|
|
16
|
+
readLatestTypedProviderHttpObservation,
|
|
17
|
+
} from "../typed-provider-http.ts";
|
|
18
|
+
export {
|
|
19
|
+
clearTypedProviderHttpObservation,
|
|
20
|
+
recordTypedProviderHttpStatus,
|
|
21
|
+
readLatestTypedProviderHttpObservation,
|
|
22
|
+
type TypedProviderHttpObservation,
|
|
23
|
+
} from "../typed-provider-http.ts";
|
|
15
24
|
import type { FixerPhase } from "../package-contracts/fixer-output.ts";
|
|
16
25
|
import type { FixerPrerequisite } from "../package-contracts/fixer-packet.ts";
|
|
17
26
|
import {
|
|
@@ -65,7 +74,6 @@ export type RoleRunRecord = {
|
|
|
65
74
|
export const RESUME_TRANSPORT_ENVELOPE = "[ak-role:resume-continue]" as const;
|
|
66
75
|
|
|
67
76
|
const RUN_STATE_FILE = "run-state.json";
|
|
68
|
-
const TYPED_HTTP_FILE = "typed-provider-http.json";
|
|
69
77
|
const WRITER_LOCK_FILE = "writer.lock";
|
|
70
78
|
|
|
71
79
|
export function isV1ResumableProvider(
|
|
@@ -74,84 +82,14 @@ export function isV1ResumableProvider(
|
|
|
74
82
|
return (V1_RESUMABLE_PROVIDERS as readonly string[]).includes(provider);
|
|
75
83
|
}
|
|
76
84
|
|
|
77
|
-
function typedProviderHttpPath(runDirectory: string): string {
|
|
78
|
-
return join(runDirectory, TYPED_HTTP_FILE);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Clear any prior attempt's typed provider HTTP observation.
|
|
83
|
-
* Each initial/resume dispatch must start without inherited 429 evidence so
|
|
84
|
-
* only the current attempt can qualify v1 resume.
|
|
85
|
-
*/
|
|
86
|
-
export async function clearTypedProviderHttpObservation(
|
|
87
|
-
runDirectory: string,
|
|
88
|
-
): Promise<void> {
|
|
89
|
-
try {
|
|
90
|
-
await unlink(typedProviderHttpPath(runDirectory));
|
|
91
|
-
} catch (error) {
|
|
92
|
-
if (
|
|
93
|
-
error instanceof Error &&
|
|
94
|
-
"code" in error &&
|
|
95
|
-
(error as { code?: unknown }).code === "ENOENT"
|
|
96
|
-
) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
throw error;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Record a typed provider HTTP status observation for the admitted run.
|
|
105
|
-
* The latest observation is authoritative: only a current HTTP 429 from
|
|
106
|
-
* Codex/xAI is retained for v1 resume; any other status or provider clears
|
|
107
|
-
* prior within-attempt 429 evidence. Never inspects diagnostic prose.
|
|
108
|
-
*/
|
|
109
|
-
export async function recordTypedProviderHttpStatus(
|
|
110
|
-
runDirectory: string,
|
|
111
|
-
observation: { readonly httpStatus: number; readonly provider: string },
|
|
112
|
-
): Promise<void> {
|
|
113
|
-
if (
|
|
114
|
-
observation.httpStatus === 429 &&
|
|
115
|
-
isV1ResumableProvider(observation.provider)
|
|
116
|
-
) {
|
|
117
|
-
const body: TypedHttp429Observation = {
|
|
118
|
-
httpStatus: 429,
|
|
119
|
-
provider: observation.provider,
|
|
120
|
-
};
|
|
121
|
-
await writeFile(
|
|
122
|
-
typedProviderHttpPath(runDirectory),
|
|
123
|
-
`${JSON.stringify(body)}\n`,
|
|
124
|
-
"utf8",
|
|
125
|
-
);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
// Non-qualifying latest response supersedes any earlier 429 in this attempt.
|
|
129
|
-
await clearTypedProviderHttpObservation(runDirectory);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Read a durable typed HTTP 429 observation. Returns undefined unless both
|
|
134
|
-
* httpStatus === 429 and provider is a v1-resumable provider are present as
|
|
135
|
-
* typed fields (never inferred from prose).
|
|
136
|
-
*/
|
|
137
85
|
export async function readTypedHttp429Observation(
|
|
138
86
|
runDirectory: string,
|
|
139
87
|
): Promise<TypedHttp429Observation | undefined> {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return undefined;
|
|
146
|
-
}
|
|
147
|
-
const record = raw as Record<string, unknown>;
|
|
148
|
-
if (record.httpStatus !== 429) return undefined;
|
|
149
|
-
if (typeof record.provider !== "string") return undefined;
|
|
150
|
-
if (!isV1ResumableProvider(record.provider)) return undefined;
|
|
151
|
-
return { httpStatus: 429, provider: record.provider };
|
|
152
|
-
} catch {
|
|
153
|
-
return undefined;
|
|
154
|
-
}
|
|
88
|
+
const observation = await readLatestTypedProviderHttpObservation(runDirectory);
|
|
89
|
+
if (observation === undefined) return undefined;
|
|
90
|
+
if (observation.httpStatus !== 429) return undefined;
|
|
91
|
+
if (!isV1ResumableProvider(observation.provider)) return undefined;
|
|
92
|
+
return { httpStatus: 429, provider: observation.provider };
|
|
155
93
|
}
|
|
156
94
|
|
|
157
95
|
/**
|
|
@@ -13,6 +13,13 @@ import { DOCTOR_AUDIT_TOOL_NAME } from "../doctor-auditor.ts";
|
|
|
13
13
|
import { JUDGE_AUDIT_TOOL_NAME } from "../judge-auditor.ts";
|
|
14
14
|
import { REVIEWER_AUDIT_TOOL_NAME } from "../reviewer-auditor.ts";
|
|
15
15
|
import { knownFailureFromProviderStop, type ExplicitInternalKnownFailure, readReviewerDispatchRejection } from "./explicit-internal.ts";
|
|
16
|
+
import {
|
|
17
|
+
isV1ResumableProvider,
|
|
18
|
+
readLatestTypedProviderHttpObservation,
|
|
19
|
+
readTypedHttp429Observation,
|
|
20
|
+
type TypedHttp429Observation,
|
|
21
|
+
type TypedProviderHttpObservation,
|
|
22
|
+
} from "./run-lifecycle.ts";
|
|
16
23
|
import {
|
|
17
24
|
AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE,
|
|
18
25
|
AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE,
|
|
@@ -367,14 +374,16 @@ export function classifyPostAdmissionFailure(input: {
|
|
|
367
374
|
input.knownDiagnostic !== undefined && input.knownDiagnostic.trim() !== ""
|
|
368
375
|
? input.knownDiagnostic
|
|
369
376
|
: conciseChildDiagnostic(input.stderr, fallback);
|
|
370
|
-
const {
|
|
377
|
+
const { timedOut: _knownTimedOut, ...knownDetails } =
|
|
371
378
|
input.knownDetails ?? {};
|
|
379
|
+
const remoteCode = knownDetails.code;
|
|
372
380
|
return {
|
|
373
381
|
cause: input.knownCause,
|
|
374
382
|
diagnostic,
|
|
375
383
|
details: {
|
|
376
384
|
...knownDetails,
|
|
377
|
-
code:
|
|
385
|
+
...(remoteCode === undefined ? {} : { code: remoteCode }),
|
|
386
|
+
exitCode: input.code,
|
|
378
387
|
...(input.timedOut ? { timedOut: true as const } : {}),
|
|
379
388
|
},
|
|
380
389
|
...(input.knownIdentity === undefined
|
|
@@ -386,7 +395,7 @@ export function classifyPostAdmissionFailure(input: {
|
|
|
386
395
|
return {
|
|
387
396
|
cause: "timeout",
|
|
388
397
|
diagnostic: "role run timed out",
|
|
389
|
-
details: { timedOut: true,
|
|
398
|
+
details: { timedOut: true, exitCode: input.code },
|
|
390
399
|
};
|
|
391
400
|
}
|
|
392
401
|
if (input.code !== 0) {
|
|
@@ -394,27 +403,27 @@ export function classifyPostAdmissionFailure(input: {
|
|
|
394
403
|
return {
|
|
395
404
|
cause: "activation",
|
|
396
405
|
diagnostic: conciseChildDiagnostic(input.stderr, fallback),
|
|
397
|
-
details: {
|
|
406
|
+
details: { exitCode: input.code },
|
|
398
407
|
};
|
|
399
408
|
}
|
|
400
409
|
if (input.session?.state === "missing") {
|
|
401
410
|
return {
|
|
402
411
|
cause: "session",
|
|
403
412
|
diagnostic: "role run left no readable session transcript",
|
|
404
|
-
details: {
|
|
413
|
+
details: { exitCode: input.code, session: "missing" },
|
|
405
414
|
};
|
|
406
415
|
}
|
|
407
416
|
if (input.session?.state === "unreadable") {
|
|
408
417
|
return {
|
|
409
418
|
cause: "session",
|
|
410
419
|
diagnostic: input.session.diagnostic,
|
|
411
|
-
details: {
|
|
420
|
+
details: { exitCode: input.code, session: "unreadable" },
|
|
412
421
|
};
|
|
413
422
|
}
|
|
414
423
|
return {
|
|
415
424
|
cause: "output",
|
|
416
425
|
diagnostic: "role run completed without a lawful typed terminal result",
|
|
417
|
-
details: {
|
|
426
|
+
details: { exitCode: input.code },
|
|
418
427
|
};
|
|
419
428
|
}
|
|
420
429
|
|
|
@@ -448,6 +457,15 @@ type SessionMessage = {
|
|
|
448
457
|
provider?: string;
|
|
449
458
|
model?: string;
|
|
450
459
|
api?: string;
|
|
460
|
+
rawStopReason?: string;
|
|
461
|
+
diagnostics?: unknown;
|
|
462
|
+
/** Typed HTTP / SDK structured fields when held on the call surface. */
|
|
463
|
+
statusCode?: number;
|
|
464
|
+
status?: number;
|
|
465
|
+
httpStatus?: number;
|
|
466
|
+
body?: unknown;
|
|
467
|
+
code?: unknown;
|
|
468
|
+
errno?: unknown;
|
|
451
469
|
};
|
|
452
470
|
|
|
453
471
|
type SessionEntry = {
|
|
@@ -546,19 +564,65 @@ async function readBoundSessionEntries(
|
|
|
546
564
|
}
|
|
547
565
|
|
|
548
566
|
/**
|
|
549
|
-
* Latest native assistant provider-stop in a session (stopReason
|
|
567
|
+
* Latest native assistant provider-stop in a session (stopReason error|aborted).
|
|
550
568
|
* Only the final assistant turn decides terminality — an older error followed by a
|
|
551
569
|
* later non-error stop is not a provider failure (would wash a no-lawful-output path).
|
|
552
570
|
* Typed production source for provider cause — not child stderr prose.
|
|
553
571
|
*/
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
): {
|
|
557
|
-
stopReason: "error";
|
|
572
|
+
type SessionProviderStop = {
|
|
573
|
+
stopReason: "error" | "aborted";
|
|
558
574
|
errorMessage?: string;
|
|
559
575
|
provider?: string;
|
|
560
576
|
model?: string;
|
|
561
|
-
|
|
577
|
+
api?: string;
|
|
578
|
+
rawStopReason?: string;
|
|
579
|
+
diagnostics?: unknown;
|
|
580
|
+
httpStatus?: number;
|
|
581
|
+
body?: unknown;
|
|
582
|
+
code?: unknown;
|
|
583
|
+
errno?: unknown;
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
function typedHttpStatusFromMessage(message: SessionMessage): number | undefined {
|
|
587
|
+
for (const candidate of [message.httpStatus, message.statusCode, message.status]) {
|
|
588
|
+
if (typeof candidate === "number" && (candidate < 200 || candidate >= 300)) return candidate;
|
|
589
|
+
}
|
|
590
|
+
return undefined;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function sessionProviderStopFromAssistant(message: SessionMessage | undefined): SessionProviderStop | undefined {
|
|
594
|
+
if (message?.role !== "assistant") return undefined;
|
|
595
|
+
if (message.stopReason !== "error" && message.stopReason !== "aborted") return undefined;
|
|
596
|
+
const httpStatus = typedHttpStatusFromMessage(message);
|
|
597
|
+
return {
|
|
598
|
+
stopReason: message.stopReason,
|
|
599
|
+
// Preserve held errorMessage bytes — emptiness check must not rewrite.
|
|
600
|
+
...(typeof message.errorMessage === "string" && message.errorMessage.trim() !== ""
|
|
601
|
+
? { errorMessage: message.errorMessage }
|
|
602
|
+
: {}),
|
|
603
|
+
...(typeof message.provider === "string" && message.provider.trim() !== ""
|
|
604
|
+
? { provider: message.provider }
|
|
605
|
+
: {}),
|
|
606
|
+
...(typeof message.model === "string" && message.model.trim() !== ""
|
|
607
|
+
? { model: message.model }
|
|
608
|
+
: {}),
|
|
609
|
+
...(typeof message.api === "string" && message.api.trim() !== ""
|
|
610
|
+
? { api: message.api }
|
|
611
|
+
: {}),
|
|
612
|
+
...(typeof message.rawStopReason === "string" && message.rawStopReason.trim() !== ""
|
|
613
|
+
? { rawStopReason: message.rawStopReason }
|
|
614
|
+
: {}),
|
|
615
|
+
...(message.diagnostics === undefined ? {} : { diagnostics: message.diagnostics }),
|
|
616
|
+
...(httpStatus === undefined ? {} : { httpStatus }),
|
|
617
|
+
...(message.body === undefined ? {} : { body: message.body }),
|
|
618
|
+
...(message.code === undefined ? {} : { code: message.code }),
|
|
619
|
+
...(message.errno === undefined ? {} : { errno: message.errno }),
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
export function extractSessionProviderStop(
|
|
624
|
+
entries: readonly SessionEntry[],
|
|
625
|
+
): SessionProviderStop | undefined {
|
|
562
626
|
// A resumed dispatch appends a typed top-level user turn to the same session.
|
|
563
627
|
// Retained audit state from an older attempt must not replace the newer attempt's
|
|
564
628
|
// native provider stop. Sessions without a user turn are the initial attempt.
|
|
@@ -578,14 +642,8 @@ export function extractSessionProviderStop(
|
|
|
578
642
|
const entry = entries[i];
|
|
579
643
|
if (entry?.type !== "custom" || entry.customType !== COMPLIANCE_RESPONSE_ENTRY_TYPE) continue;
|
|
580
644
|
const response = isRecord(entry.data) && isRecord(entry.data.response) ? entry.data.response : undefined;
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
stopReason: "error",
|
|
584
|
-
...(typeof response.errorMessage === "string" && response.errorMessage.trim() !== "" ? { errorMessage: response.errorMessage } : {}),
|
|
585
|
-
...(typeof response.provider === "string" && response.provider.trim() !== "" ? { provider: response.provider } : {}),
|
|
586
|
-
...(typeof response.model === "string" && response.model.trim() !== "" ? { model: response.model } : {}),
|
|
587
|
-
};
|
|
588
|
-
}
|
|
645
|
+
const stop = sessionProviderStopFromAssistant(response);
|
|
646
|
+
if (stop !== undefined) return stop;
|
|
589
647
|
break;
|
|
590
648
|
}
|
|
591
649
|
for (let i = entries.length - 1; i >= attemptStart; i -= 1) {
|
|
@@ -594,19 +652,7 @@ export function extractSessionProviderStop(
|
|
|
594
652
|
const message = entry.message;
|
|
595
653
|
if (message?.role !== "assistant") continue;
|
|
596
654
|
// Latest assistant in the current attempt only (reviewer-child-executor lastAssistant pattern).
|
|
597
|
-
|
|
598
|
-
return {
|
|
599
|
-
stopReason: "error",
|
|
600
|
-
...(typeof message.errorMessage === "string" && message.errorMessage.trim() !== ""
|
|
601
|
-
? { errorMessage: message.errorMessage }
|
|
602
|
-
: {}),
|
|
603
|
-
...(typeof message.provider === "string" && message.provider.trim() !== ""
|
|
604
|
-
? { provider: message.provider }
|
|
605
|
-
: {}),
|
|
606
|
-
...(typeof message.model === "string" && message.model.trim() !== ""
|
|
607
|
-
? { model: message.model }
|
|
608
|
-
: {}),
|
|
609
|
-
};
|
|
655
|
+
return sessionProviderStopFromAssistant(message);
|
|
610
656
|
}
|
|
611
657
|
return undefined;
|
|
612
658
|
}
|
|
@@ -614,15 +660,7 @@ export function extractSessionProviderStop(
|
|
|
614
660
|
/** Read the bound session principal and extract a typed provider-stop, if any. */
|
|
615
661
|
export async function readSessionProviderStop(
|
|
616
662
|
sessionFile: string,
|
|
617
|
-
): Promise<
|
|
618
|
-
| {
|
|
619
|
-
stopReason: "error";
|
|
620
|
-
errorMessage?: string;
|
|
621
|
-
provider?: string;
|
|
622
|
-
model?: string;
|
|
623
|
-
}
|
|
624
|
-
| undefined
|
|
625
|
-
> {
|
|
663
|
+
): Promise<SessionProviderStop | undefined> {
|
|
626
664
|
try {
|
|
627
665
|
const entries = await readBoundSessionEntries(sessionFile);
|
|
628
666
|
return extractSessionProviderStop(entries);
|
|
@@ -662,8 +700,7 @@ export async function readBoundEvidenceChildKnownFailure(
|
|
|
662
700
|
return {
|
|
663
701
|
...primary,
|
|
664
702
|
details: {
|
|
665
|
-
...(
|
|
666
|
-
...(stop.model === undefined ? {} : { model: stop.model }),
|
|
703
|
+
...(primary.details ?? {}),
|
|
667
704
|
secondaryEvidence: "evidence-child",
|
|
668
705
|
},
|
|
669
706
|
};
|
|
@@ -721,10 +758,10 @@ export async function readBoundAuditorKnownFailure(
|
|
|
721
758
|
if (entry?.type !== "custom" || entry.customType !== AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE || !isRecord(entry.data)) continue;
|
|
722
759
|
const parent = isRecord(entry.data.parent) ? entry.data.parent : undefined;
|
|
723
760
|
const failure = isRecord(entry.data.failure) ? entry.data.failure : undefined;
|
|
724
|
-
if (parent?.sessionId !== parentId || parent.sessionFile !== sessionFile || parent.attemptEntryId !== attemptEntryId || failure?.cause !== "provider") continue;
|
|
761
|
+
if (parent?.sessionId !== parentId || parent.sessionFile !== sessionFile || parent.attemptEntryId !== attemptEntryId || (failure?.cause !== "provider" && failure?.cause !== "unrecognized")) continue;
|
|
725
762
|
const identity = isRecord(failure.identity) ? failure.identity : undefined;
|
|
726
763
|
return {
|
|
727
|
-
cause: "provider",
|
|
764
|
+
cause: failure.cause === "provider" ? "provider" : "unrecognized",
|
|
728
765
|
...(identity === undefined ? {} : { identity: {
|
|
729
766
|
...(typeof identity.name === "string" ? { name: identity.name } : {}),
|
|
730
767
|
...(typeof identity.code === "string" || typeof identity.code === "number" ? { code: identity.code } : {}),
|
|
@@ -737,8 +774,7 @@ export async function readBoundAuditorKnownFailure(
|
|
|
737
774
|
return {
|
|
738
775
|
...primary,
|
|
739
776
|
details: {
|
|
740
|
-
...(
|
|
741
|
-
...(stop.model === undefined ? {} : { model: stop.model }),
|
|
777
|
+
...(primary.details ?? {}),
|
|
742
778
|
secondaryEvidence: "unavailable",
|
|
743
779
|
},
|
|
744
780
|
};
|
|
@@ -778,26 +814,57 @@ function typedFailedTerminatingToolKnownFailure(
|
|
|
778
814
|
return undefined;
|
|
779
815
|
}
|
|
780
816
|
|
|
817
|
+
/**
|
|
818
|
+
* One audited-runner resolution: knownFailure plus the typed-HTTP sidecar outcome
|
|
819
|
+
* from the same read. Callers that also decide v1 resume must consume this once —
|
|
820
|
+
* never re-read the sidecar in presentControlledFailure.
|
|
821
|
+
*/
|
|
822
|
+
export type AuditedRunnerFailureResolution = {
|
|
823
|
+
readonly knownFailure?: ExplicitInternalKnownFailure;
|
|
824
|
+
/** Successful sidecar read (not absence). */
|
|
825
|
+
readonly typedHttpObservation?: TypedProviderHttpObservation;
|
|
826
|
+
/**
|
|
827
|
+
* True when this resolution already performed the typed-HTTP sidecar read
|
|
828
|
+
* (success, absence, or non-absence failure folded into knownFailure).
|
|
829
|
+
* False when an earlier evidence tier short-circuited before the sidecar.
|
|
830
|
+
*/
|
|
831
|
+
readonly typedHttpObservationSettled: boolean;
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
function resolutionOf(
|
|
835
|
+
knownFailure: ExplicitInternalKnownFailure | undefined,
|
|
836
|
+
typedHttp: {
|
|
837
|
+
readonly settled: boolean;
|
|
838
|
+
readonly observation?: TypedProviderHttpObservation;
|
|
839
|
+
} = { settled: false },
|
|
840
|
+
): AuditedRunnerFailureResolution {
|
|
841
|
+
return {
|
|
842
|
+
...(knownFailure === undefined ? {} : { knownFailure }),
|
|
843
|
+
...(typedHttp.observation === undefined ? {} : { typedHttpObservation: typedHttp.observation }),
|
|
844
|
+
typedHttpObservationSettled: typedHttp.settled,
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
|
|
781
848
|
/** Sole evidence-priority owner for public runners with Soul auditors. */
|
|
782
|
-
export async function
|
|
849
|
+
export async function resolveAuditedRunnerFailureResolution(input: {
|
|
783
850
|
runner: ExplicitInternalKnownFailure | undefined;
|
|
784
851
|
sessionFile: string;
|
|
785
852
|
credential: ExplicitInternalKnownFailure | undefined;
|
|
786
853
|
/** Reviewer only: recover child-written rejection page into knownFailure.details. */
|
|
787
854
|
runDirectory?: string;
|
|
788
|
-
}): Promise<
|
|
789
|
-
if (input.runner !== undefined) return input.runner;
|
|
855
|
+
}): Promise<AuditedRunnerFailureResolution> {
|
|
856
|
+
if (input.runner !== undefined) return resolutionOf(input.runner);
|
|
790
857
|
if (input.runDirectory !== undefined) {
|
|
791
858
|
try {
|
|
792
859
|
const rejection = await readReviewerDispatchRejection(input.runDirectory);
|
|
793
|
-
if (rejection !== undefined) return rejection;
|
|
860
|
+
if (rejection !== undefined) return resolutionOf(rejection);
|
|
794
861
|
} catch (error) {
|
|
795
862
|
const failure = error instanceof Error ? error : new Error(String(error));
|
|
796
|
-
return {
|
|
863
|
+
return resolutionOf({
|
|
797
864
|
cause: "activation",
|
|
798
865
|
identity: thrownIdentity(failure),
|
|
799
866
|
diagnostic: failure.message || failure.name,
|
|
800
|
-
};
|
|
867
|
+
});
|
|
801
868
|
}
|
|
802
869
|
}
|
|
803
870
|
// Bound auditor evidence outranks a parent failure that the auditor path itself
|
|
@@ -805,24 +872,28 @@ export async function resolveAuditedRunnerKnownFailure(input: {
|
|
|
805
872
|
// it outranks provider/credential and nonzero fallbacks, but not its recorded cause.
|
|
806
873
|
try {
|
|
807
874
|
const auditorFailure = await readBoundAuditorKnownFailure(input.sessionFile);
|
|
808
|
-
if (auditorFailure !== undefined) return auditorFailure;
|
|
875
|
+
if (auditorFailure !== undefined) return resolutionOf(auditorFailure);
|
|
809
876
|
} catch (error) {
|
|
810
877
|
const failure = sessionReadFailure(error, "failed to recover bound auditor failure");
|
|
811
|
-
return {
|
|
878
|
+
return resolutionOf({
|
|
812
879
|
cause: "session",
|
|
813
880
|
identity: thrownIdentity(failure),
|
|
814
881
|
diagnostic: failure.message || failure.name,
|
|
815
|
-
};
|
|
882
|
+
});
|
|
816
883
|
}
|
|
817
884
|
try {
|
|
818
885
|
const terminatingFailure = typedFailedTerminatingToolKnownFailure(
|
|
819
886
|
await readBoundSessionEntries(input.sessionFile),
|
|
820
887
|
);
|
|
821
|
-
if (terminatingFailure !== undefined) return terminatingFailure;
|
|
888
|
+
if (terminatingFailure !== undefined) return resolutionOf(terminatingFailure);
|
|
822
889
|
} catch (error) {
|
|
823
890
|
if (!isMissingPathError(error)) {
|
|
824
891
|
const failure = sessionReadFailure(error, "failed to recover typed terminating-tool failure");
|
|
825
|
-
return {
|
|
892
|
+
return resolutionOf({
|
|
893
|
+
cause: "session",
|
|
894
|
+
identity: thrownIdentity(failure),
|
|
895
|
+
diagnostic: failure.message || failure.name,
|
|
896
|
+
});
|
|
826
897
|
}
|
|
827
898
|
}
|
|
828
899
|
// Reviewer axis evidence-children are next: fixed two-axis dispatch fails
|
|
@@ -830,19 +901,138 @@ export async function resolveAuditedRunnerKnownFailure(input: {
|
|
|
830
901
|
// fallback; credential is last.
|
|
831
902
|
try {
|
|
832
903
|
const evidenceChildFailure = await readBoundEvidenceChildKnownFailure(input.sessionFile);
|
|
833
|
-
if (evidenceChildFailure !== undefined) return evidenceChildFailure;
|
|
904
|
+
if (evidenceChildFailure !== undefined) return resolutionOf(evidenceChildFailure);
|
|
834
905
|
} catch (error) {
|
|
835
906
|
const failure = sessionReadFailure(error, "failed to recover bound evidence-child failure");
|
|
836
|
-
return {
|
|
907
|
+
return resolutionOf({
|
|
837
908
|
cause: "session",
|
|
838
909
|
identity: thrownIdentity(failure),
|
|
839
910
|
diagnostic: failure.message || failure.name,
|
|
840
|
-
};
|
|
911
|
+
});
|
|
841
912
|
}
|
|
842
913
|
const parentStop = await readSessionProviderStop(input.sessionFile);
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
914
|
+
// Typed HTTP observation: ENOENT=absence; other read/parse/shape failures keep real cause.
|
|
915
|
+
// This is the single sidecar read for both knownFailure projection and v1 resume.
|
|
916
|
+
let httpObservation: TypedProviderHttpObservation | undefined;
|
|
917
|
+
if (input.runDirectory !== undefined) {
|
|
918
|
+
try {
|
|
919
|
+
httpObservation = await readLatestTypedProviderHttpObservation(input.runDirectory);
|
|
920
|
+
} catch (error) {
|
|
921
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
922
|
+
return resolutionOf(
|
|
923
|
+
{
|
|
924
|
+
cause: "session",
|
|
925
|
+
identity: thrownIdentity(failure),
|
|
926
|
+
diagnostic: failure.message || failure.name,
|
|
927
|
+
},
|
|
928
|
+
{ settled: true },
|
|
929
|
+
);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
const typedHttp = {
|
|
933
|
+
settled: input.runDirectory !== undefined,
|
|
934
|
+
...(httpObservation === undefined ? {} : { observation: httpObservation }),
|
|
935
|
+
};
|
|
936
|
+
if (parentStop === undefined) {
|
|
937
|
+
if (input.credential !== undefined) return resolutionOf(input.credential, typedHttp);
|
|
938
|
+
if (httpObservation === undefined) return resolutionOf(undefined, typedHttp);
|
|
939
|
+
// Project the HTTP observation's status + provider/source association.
|
|
940
|
+
return resolutionOf(
|
|
941
|
+
knownFailureFromProviderStop({
|
|
942
|
+
stopReason: "error",
|
|
943
|
+
httpStatus: httpObservation.httpStatus,
|
|
944
|
+
provider: httpObservation.provider,
|
|
945
|
+
}),
|
|
946
|
+
typedHttp,
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
return resolutionOf(
|
|
950
|
+
knownFailureFromProviderStop({
|
|
951
|
+
...parentStop,
|
|
952
|
+
...(httpObservation === undefined
|
|
953
|
+
? {}
|
|
954
|
+
: {
|
|
955
|
+
httpStatus: httpObservation.httpStatus,
|
|
956
|
+
// Observation association outranks session-configured provider name alone.
|
|
957
|
+
provider: httpObservation.provider,
|
|
958
|
+
}),
|
|
959
|
+
}),
|
|
960
|
+
typedHttp,
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/** Sole evidence-priority owner for public runners with Soul auditors. */
|
|
965
|
+
export async function resolveAuditedRunnerKnownFailure(input: {
|
|
966
|
+
runner: ExplicitInternalKnownFailure | undefined;
|
|
967
|
+
sessionFile: string;
|
|
968
|
+
credential: ExplicitInternalKnownFailure | undefined;
|
|
969
|
+
/** Reviewer only: recover child-written rejection page into knownFailure.details. */
|
|
970
|
+
runDirectory?: string;
|
|
971
|
+
}): Promise<ExplicitInternalKnownFailure | undefined> {
|
|
972
|
+
return (await resolveAuditedRunnerFailureResolution(input)).knownFailure;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* v1 resume observation for controlled-failure settlement — at most one sidecar read.
|
|
977
|
+
* Prefer the pre-resolved outcome from resolveAuditedRunnerFailureResolution.
|
|
978
|
+
* Non-absence failures never throw: they return observationReadFailure for the
|
|
979
|
+
* existing controlled-failure → error.json chain.
|
|
980
|
+
*/
|
|
981
|
+
export async function resolveControlledFailureResumeObservation(input: {
|
|
982
|
+
readonly runDirectory: string;
|
|
983
|
+
readonly typedHttpObservationSettled?: boolean;
|
|
984
|
+
readonly typedHttpObservation?: TypedProviderHttpObservation;
|
|
985
|
+
}): Promise<{
|
|
986
|
+
readonly typedHttp429?: TypedHttp429Observation;
|
|
987
|
+
readonly observationReadFailure?: ExplicitInternalKnownFailure;
|
|
988
|
+
}> {
|
|
989
|
+
if (input.typedHttpObservationSettled === true) {
|
|
990
|
+
const observation = input.typedHttpObservation;
|
|
991
|
+
if (
|
|
992
|
+
observation !== undefined &&
|
|
993
|
+
observation.httpStatus === 429 &&
|
|
994
|
+
isV1ResumableProvider(observation.provider)
|
|
995
|
+
) {
|
|
996
|
+
return {
|
|
997
|
+
typedHttp429: { httpStatus: 429, provider: observation.provider },
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
return {};
|
|
1001
|
+
}
|
|
1002
|
+
try {
|
|
1003
|
+
const typedHttp429 = await readTypedHttp429Observation(input.runDirectory);
|
|
1004
|
+
return typedHttp429 === undefined ? {} : { typedHttp429 };
|
|
1005
|
+
} catch (error) {
|
|
1006
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
1007
|
+
return {
|
|
1008
|
+
observationReadFailure: {
|
|
1009
|
+
cause: "session",
|
|
1010
|
+
identity: thrownIdentity(failure),
|
|
1011
|
+
diagnostic: failure.message || failure.name,
|
|
1012
|
+
},
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/** Spread into presentControlledFailure failureInput from one audited resolution. */
|
|
1018
|
+
export function controlledFailureInputFromResolution(
|
|
1019
|
+
resolution: AuditedRunnerFailureResolution,
|
|
1020
|
+
): {
|
|
1021
|
+
knownFailure?: ExplicitInternalKnownFailure;
|
|
1022
|
+
typedHttpObservationSettled?: true;
|
|
1023
|
+
typedHttpObservation?: TypedProviderHttpObservation;
|
|
1024
|
+
} {
|
|
1025
|
+
return {
|
|
1026
|
+
...(resolution.knownFailure === undefined ? {} : { knownFailure: resolution.knownFailure }),
|
|
1027
|
+
...(resolution.typedHttpObservationSettled
|
|
1028
|
+
? {
|
|
1029
|
+
typedHttpObservationSettled: true as const,
|
|
1030
|
+
...(resolution.typedHttpObservation === undefined
|
|
1031
|
+
? {}
|
|
1032
|
+
: { typedHttpObservation: resolution.typedHttpObservation }),
|
|
1033
|
+
}
|
|
1034
|
+
: {}),
|
|
1035
|
+
};
|
|
846
1036
|
}
|
|
847
1037
|
|
|
848
1038
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
@@ -14,7 +14,7 @@ export type ReviewerChildExecuteOptions = Readonly<{
|
|
|
14
14
|
export function projectSharedChildFailure(error: unknown): unknown {
|
|
15
15
|
if (typeof error === "object" && error !== null && "evidenceChildFailure" in error) {
|
|
16
16
|
const classification = (error as { evidenceChildFailure?: unknown }).evidenceChildFailure;
|
|
17
|
-
if (classification === "provider" || classification === "child") {
|
|
17
|
+
if (classification === "provider" || classification === "child" || classification === "unknown") {
|
|
18
18
|
Object.assign(error, { reviewerFailure: classification });
|
|
19
19
|
}
|
|
20
20
|
}
|
package/src/role-runtime.ts
CHANGED
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
NAVIGATOR_INVOCATION_ENTRY,
|
|
45
45
|
resolveLifecycleInvocationPrincipal,
|
|
46
46
|
} from "./navigator-invocation-identity.ts";
|
|
47
|
-
import { recordTypedProviderHttpStatus } from "./
|
|
47
|
+
import { recordTypedProviderHttpStatus } from "./typed-provider-http.ts";
|
|
48
48
|
import {
|
|
49
49
|
ExplicitInternalActivationError,
|
|
50
50
|
recordReviewerDispatchRejectionSync,
|
|
@@ -807,22 +807,32 @@ export function createRoleRuntimeExtension(
|
|
|
807
807
|
await observe(() => observationFace.onEnd(event), ctx);
|
|
808
808
|
});
|
|
809
809
|
|
|
810
|
-
// Public Role run: record typed
|
|
811
|
-
//
|
|
812
|
-
//
|
|
810
|
+
// Public Role run: record typed non-success HTTP for error evidence + v1 resume.
|
|
811
|
+
// 2xx clears prior observation (see recordTypedProviderHttpStatus). Non-success
|
|
812
|
+
// write failure must surface — never silently drop authorized error evidence.
|
|
813
813
|
pi.on("after_provider_response", async (event, ctx) => {
|
|
814
814
|
const runDir = process.env.AK_ROLE_RUN_DIR;
|
|
815
815
|
if (typeof runDir !== "string" || runDir.trim() === "") return;
|
|
816
816
|
const provider = ctx.model?.provider;
|
|
817
817
|
if (typeof provider !== "string" || provider.trim() === "") return;
|
|
818
|
+
const status = event.status;
|
|
819
|
+
if (typeof status !== "number") return;
|
|
818
820
|
try {
|
|
819
821
|
await recordTypedProviderHttpStatus(runDir, {
|
|
820
|
-
httpStatus:
|
|
822
|
+
httpStatus: status,
|
|
821
823
|
provider,
|
|
822
824
|
});
|
|
823
|
-
} catch {
|
|
824
|
-
|
|
825
|
-
|
|
825
|
+
} catch (error) {
|
|
826
|
+
if (
|
|
827
|
+
status >= 200 &&
|
|
828
|
+
status < 300 &&
|
|
829
|
+
error instanceof Error &&
|
|
830
|
+
"code" in error &&
|
|
831
|
+
(error as { code?: unknown }).code === "ENOENT"
|
|
832
|
+
) {
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
failInfrastructure(error, ctx);
|
|
826
836
|
}
|
|
827
837
|
});
|
|
828
838
|
|