@akagilnc/pi-workflow-roles 0.1.2521 → 0.1.2653-next.71811ca
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 +129 -39
- package/README.zh-CN.md +128 -39
- package/dist/activation-ledger-topology.js +1 -1
- package/dist/archivist-record-entry.js +47 -26
- package/dist/auditor-dossier-tool.js +3 -1
- package/dist/compliance-transport.js +17 -26
- package/dist/doctor-contracts.js +1 -2
- package/dist/dossier-resolution.js +4 -8
- package/dist/engine-detour-tool.js +33 -8
- package/dist/engine-detour.js +1 -0
- package/dist/evidence-child-executor.js +277 -424
- package/dist/host-contracts.js +22 -0
- package/dist/institutional-resolution.js +88 -0
- package/dist/merger-contracts.js +1 -2
- package/dist/navigator-attendance.js +16 -0
- package/dist/notary-contracts.js +2 -3
- package/dist/package-contracts/fixer-output.js +1 -2
- package/dist/packaged-role-registry.js +102 -9
- package/dist/pi/in-process-session.js +671 -0
- package/dist/public-cli/config.js +7 -5
- package/dist/public-cli/main.js +3767 -4113
- package/dist/reviewer-child-executor.js +6 -1
- package/dist/session-opening-materials.js +10 -38
- package/dist/sitian-appender.js +146 -0
- package/dist/sitian-contracts.js +21 -0
- package/dist/sitian-facade.js +15 -0
- package/dist/sitian-reader.js +52 -0
- package/extensions/role-runtime.ts +9 -7
- package/package.json +5 -5
- package/resources/engines/cursor.md +12 -26
- package/resources/engines/hermes.md +2 -23
- package/resources/engines/opus.md +1 -16
- package/resources/navigator-route-playbook.md +2 -2
- package/scripts/build-package.mjs +0 -1
- package/souls/quality-law.md +1 -1
- package/src/activation-ledger-topology.ts +1 -1
- package/src/archivist-record-entry.ts +57 -45
- package/src/auditor-dossier-tool.ts +4 -2
- package/src/canonical-skill-binding.ts +37 -27
- package/src/collector-role.ts +11 -20
- package/src/collector-tool-schemas.ts +1 -4
- package/src/compliance-transport.ts +19 -44
- package/src/doctor-auditor.ts +1 -5
- package/src/doctor-contracts.ts +1 -4
- package/src/doctor-role.ts +31 -8
- package/src/dossier-resolution.ts +8 -8
- package/src/engine-detour-tool.ts +48 -19
- package/src/engine-detour.ts +3 -0
- package/src/evidence-child-executor.ts +277 -485
- package/src/factory-board.ts +3 -3
- package/src/gatekeeper-role.ts +21 -10
- package/src/host-contracts.ts +386 -0
- package/src/in-process-session.ts +1 -115
- package/src/institutional-resolution.ts +135 -0
- package/src/judge-auditor.ts +1 -5
- package/src/judge-role.ts +37 -51
- package/src/merger-contracts.ts +1 -4
- package/src/merger-role.ts +7 -11
- package/src/navigator-attendance.ts +20 -1
- package/src/notary-contracts.ts +8 -11
- package/src/notary-role.ts +7 -23
- package/src/notary-source-run.ts +4 -3
- package/src/package-contracts/fixer-output.ts +1 -4
- package/src/package-resources/method-skill-binding.ts +10 -34
- package/src/packaged-role-registry.ts +122 -12
- package/src/pi/adapter.ts +192 -0
- package/src/pi/durable-principal.ts +98 -0
- package/src/pi/in-process-session.ts +819 -0
- package/src/pi/known-failure.ts +77 -0
- package/src/pi/pi-normalization.ts +284 -0
- package/src/pi/role-turn-host.ts +459 -0
- package/src/public-cli/auto-resume.ts +42 -44
- package/src/public-cli/cli.ts +171 -257
- package/src/public-cli/coder-run.ts +115 -433
- package/src/public-cli/collector-run.ts +62 -314
- package/src/public-cli/config.ts +7 -5
- package/src/public-cli/doctor-run.ts +41 -52
- package/src/public-cli/fixer-run.ts +122 -427
- package/src/public-cli/invocation.ts +287 -96
- package/src/public-cli/judge-run.ts +92 -422
- package/src/public-cli/merger-run.ts +167 -429
- package/src/public-cli/notary-run.ts +41 -47
- package/src/public-cli/option-definitions.ts +136 -3
- package/src/public-cli/post-admission.ts +497 -0
- package/src/public-cli/public-run-credentials.ts +4 -4
- package/src/public-cli/reviewer-dispatch-rejection.ts +106 -0
- package/src/public-cli/reviewer-run.ts +132 -449
- package/src/public-cli/run-lifecycle.ts +339 -327
- package/src/public-cli/settlement.ts +251 -134
- package/src/public-cli/terminal.ts +3 -9
- package/src/public-cli/turn-request.ts +87 -0
- package/src/reviewer-child-executor.ts +9 -1
- package/src/reviewer-role.ts +11 -20
- package/src/role-runtime.ts +129 -78
- package/src/session-opening-materials.ts +25 -39
- package/src/sitian-appender.ts +171 -0
- package/src/sitian-contracts.ts +106 -0
- package/src/sitian-facade.ts +18 -0
- package/src/sitian-reader.ts +62 -0
- package/src/worker-role.ts +42 -40
- package/src/worker-submission-gates.ts +33 -0
- package/dist/archivist-role-run-coordinates.js +0 -28
- package/dist/package-contracts/terminating-infrastructure.js +0 -90
- package/resources/engines/sonnet.md +0 -27
- package/resources/engines/zcode.md +0 -63
- package/src/archivist-role-run-coordinates.ts +0 -45
- package/src/package-contracts/terminating-infrastructure.ts +0 -132
- package/src/public-cli/explicit-internal.ts +0 -427
- package/src/public-cli/one-shot-dispatch.ts +0 -300
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified post-admission Role lifecycle coordinator (stages ③–⑤; ADR 0018 / #505 / #517 / #526).
|
|
3
|
+
* Owns writer lease → running → ③ dispatch → ④ tool loop / gates → ⑤ settle / fail →
|
|
4
|
+
* terminal → release. The durable admitted mark (markRunAdmitted) is owned by the
|
|
5
|
+
* initial role facades before entering; manual resume never re-admits.
|
|
6
|
+
* Role runners supply only turn request projection and narrow settlement adapters.
|
|
7
|
+
*/
|
|
8
|
+
import { writeFile } from "node:fs/promises";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
ControlledFailureCause,
|
|
13
|
+
DurablePrincipal,
|
|
14
|
+
DurablePrincipalAuthority,
|
|
15
|
+
RoleTurnHost,
|
|
16
|
+
RoleTurnKnownFailure,
|
|
17
|
+
RoleTurnRequest,
|
|
18
|
+
RoleTurnResult,
|
|
19
|
+
SessionCustomEntryAppender,
|
|
20
|
+
} from "../host-contracts.ts";
|
|
21
|
+
import type { CredentialProviders, SeatModelConfig } from "./config.ts";
|
|
22
|
+
import { resolveResumeModel } from "./turn-request.ts";
|
|
23
|
+
import {
|
|
24
|
+
missingCredentialPreDispatchFailure,
|
|
25
|
+
postRunMissingCredentialFailure,
|
|
26
|
+
} from "./public-run-credentials.ts";
|
|
27
|
+
import {
|
|
28
|
+
acquireRunWriterLease,
|
|
29
|
+
clearTypedProviderHttpObservation,
|
|
30
|
+
isV1ResumableFailure,
|
|
31
|
+
markRunResumable,
|
|
32
|
+
markRunRunning,
|
|
33
|
+
markRunTerminal,
|
|
34
|
+
renderResumeCommand,
|
|
35
|
+
RunWriterLeaseHeldError,
|
|
36
|
+
type RunWriterLease,
|
|
37
|
+
type TypedProviderHttpObservation,
|
|
38
|
+
} from "./run-lifecycle.ts";
|
|
39
|
+
import {
|
|
40
|
+
classifyPostAdmissionFailure,
|
|
41
|
+
controlledFailureInputFromResolution,
|
|
42
|
+
exitCodeForTerminalOutcome,
|
|
43
|
+
explicitInternalKnownFailureClassificationInput,
|
|
44
|
+
formatCliDiagnostic,
|
|
45
|
+
formatTerminalResult,
|
|
46
|
+
inspectJudgeSession,
|
|
47
|
+
isLawfulTypedTerminalOutcome,
|
|
48
|
+
presentFailureTerminal,
|
|
49
|
+
presentStructuralRejection,
|
|
50
|
+
resolveAuditedRunnerFailureResolution,
|
|
51
|
+
resolveControlledFailureResumeObservation,
|
|
52
|
+
settleFailureTerminalResult,
|
|
53
|
+
} from "./settlement.ts";
|
|
54
|
+
import type { CliIo } from "./cli-io.ts";
|
|
55
|
+
import type { AdmittedRoleInvocation } from "./invocation.ts";
|
|
56
|
+
import {
|
|
57
|
+
type TerminalResult,
|
|
58
|
+
} from "./terminal.ts";
|
|
59
|
+
import { runWithAutoResumeLoop } from "./auto-resume.ts";
|
|
60
|
+
|
|
61
|
+
export type PostAdmissionEnv = {
|
|
62
|
+
home: string;
|
|
63
|
+
agentDir: string;
|
|
64
|
+
packageRoot: string;
|
|
65
|
+
cwd: string;
|
|
66
|
+
correlationId?: string;
|
|
67
|
+
roleTurnHost: RoleTurnHost;
|
|
68
|
+
model?: SeatModelConfig;
|
|
69
|
+
engine?: string;
|
|
70
|
+
credentials?: CredentialProviders;
|
|
71
|
+
timeoutMs?: number;
|
|
72
|
+
principalAuthority: DurablePrincipalAuthority;
|
|
73
|
+
sessionAppender: SessionCustomEntryAppender;
|
|
74
|
+
autoResumeLimit?: number;
|
|
75
|
+
createRunId?: () => string;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Role-specific settlement hooks and failure resolvers.
|
|
80
|
+
* Lifecycle coordination stays in this coordinator module.
|
|
81
|
+
*/
|
|
82
|
+
export type PostAdmissionAdapters<
|
|
83
|
+
A extends AdmittedRoleInvocation = AdmittedRoleInvocation,
|
|
84
|
+
T extends TerminalResult = TerminalResult,
|
|
85
|
+
> = {
|
|
86
|
+
trySettle: (admitted: A, authority: DurablePrincipalAuthority) => Promise<T | undefined>;
|
|
87
|
+
/** Default: isLawfulTypedTerminalOutcome(terminal.roleOutcome). */
|
|
88
|
+
shouldPresentSettled?: (terminal: T) => boolean;
|
|
89
|
+
trySettleSecondary?: (admitted: A, authority: DurablePrincipalAuthority) => Promise<TerminalResult | undefined>;
|
|
90
|
+
resolveRunnerKnownFailure?: (input: {
|
|
91
|
+
result: RoleTurnResult;
|
|
92
|
+
sessionFile: string;
|
|
93
|
+
}) => Promise<RoleTurnKnownFailure | undefined>;
|
|
94
|
+
hasLawfulTerminalResult?: (admitted: A, authority: DurablePrincipalAuthority) => Promise<boolean>;
|
|
95
|
+
isResumableRole?: boolean;
|
|
96
|
+
beforeDispatch?: (admitted: A) => Promise<void> | void;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export type ControlledFailureInput = {
|
|
100
|
+
timedOut: boolean;
|
|
101
|
+
code: number | null;
|
|
102
|
+
stderr: string;
|
|
103
|
+
thrown?: unknown;
|
|
104
|
+
knownFailure?: RoleTurnKnownFailure;
|
|
105
|
+
knownCause?: ControlledFailureCause;
|
|
106
|
+
knownIdentity?: {
|
|
107
|
+
readonly name?: string;
|
|
108
|
+
readonly code?: string | number;
|
|
109
|
+
};
|
|
110
|
+
knownDiagnostic?: string;
|
|
111
|
+
typedHttpObservationSettled?: true;
|
|
112
|
+
typedHttpObservation?: TypedProviderHttpObservation;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export async function presentControlledFailure<
|
|
116
|
+
A extends AdmittedRoleInvocation,
|
|
117
|
+
T extends TerminalResult = TerminalResult,
|
|
118
|
+
>(
|
|
119
|
+
admitted: A,
|
|
120
|
+
failureInput: ControlledFailureInput,
|
|
121
|
+
adapters: PostAdmissionAdapters<A, T>,
|
|
122
|
+
authority: DurablePrincipalAuthority,
|
|
123
|
+
io: CliIo,
|
|
124
|
+
): Promise<{
|
|
125
|
+
exitCode: number;
|
|
126
|
+
admitted: A;
|
|
127
|
+
terminal: TerminalResult;
|
|
128
|
+
}> {
|
|
129
|
+
const hasThrown = Object.hasOwn(failureInput, "thrown");
|
|
130
|
+
const resumeObservation = await resolveControlledFailureResumeObservation({
|
|
131
|
+
runDirectory: admitted.runDirectory,
|
|
132
|
+
...(failureInput.typedHttpObservationSettled === true
|
|
133
|
+
? {
|
|
134
|
+
typedHttpObservationSettled: true as const,
|
|
135
|
+
...(failureInput.typedHttpObservation === undefined
|
|
136
|
+
? {}
|
|
137
|
+
: { typedHttpObservation: failureInput.typedHttpObservation }),
|
|
138
|
+
}
|
|
139
|
+
: {}),
|
|
140
|
+
});
|
|
141
|
+
const knownFailure =
|
|
142
|
+
failureInput.knownFailure ?? resumeObservation.observationReadFailure;
|
|
143
|
+
const session =
|
|
144
|
+
!hasThrown &&
|
|
145
|
+
!failureInput.timedOut &&
|
|
146
|
+
knownFailure === undefined &&
|
|
147
|
+
failureInput.knownCause === undefined &&
|
|
148
|
+
admitted.principal !== undefined
|
|
149
|
+
? await inspectJudgeSession(authority.decode(admitted.principal).sessionFile)
|
|
150
|
+
: undefined;
|
|
151
|
+
const failure = classifyPostAdmissionFailure({
|
|
152
|
+
timedOut: failureInput.timedOut,
|
|
153
|
+
code: failureInput.code,
|
|
154
|
+
stderr: failureInput.stderr,
|
|
155
|
+
...(hasThrown ? { thrown: failureInput.thrown } : {}),
|
|
156
|
+
...explicitInternalKnownFailureClassificationInput(knownFailure),
|
|
157
|
+
...(failureInput.knownCause === undefined
|
|
158
|
+
? {}
|
|
159
|
+
: { knownCause: failureInput.knownCause }),
|
|
160
|
+
...(failureInput.knownIdentity === undefined
|
|
161
|
+
? {}
|
|
162
|
+
: { knownIdentity: failureInput.knownIdentity }),
|
|
163
|
+
...(failureInput.knownDiagnostic === undefined
|
|
164
|
+
? {}
|
|
165
|
+
: { knownDiagnostic: failureInput.knownDiagnostic }),
|
|
166
|
+
...(session === undefined ? {} : { session }),
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
let resumable = false;
|
|
170
|
+
const typedHttp429 = resumeObservation.typedHttp429;
|
|
171
|
+
if (adapters.isResumableRole === true && admitted.principal !== undefined) {
|
|
172
|
+
const sessionPrincipalAvailable = await authority.isAvailable(admitted.principal);
|
|
173
|
+
const hasLawful = adapters.hasLawfulTerminalResult !== undefined
|
|
174
|
+
? await adapters.hasLawfulTerminalResult(admitted, authority)
|
|
175
|
+
: false;
|
|
176
|
+
resumable =
|
|
177
|
+
sessionPrincipalAvailable &&
|
|
178
|
+
isV1ResumableFailure({
|
|
179
|
+
hasLawfulTerminalResult: hasLawful,
|
|
180
|
+
...(typedHttp429 === undefined ? {} : { typedHttp429 }),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (resumable && typedHttp429 !== undefined) {
|
|
185
|
+
await markRunResumable(admitted.runDirectory, typedHttp429);
|
|
186
|
+
} else {
|
|
187
|
+
await markRunTerminal(admitted.runDirectory);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const terminal = await settleFailureTerminalResult(
|
|
191
|
+
admitted,
|
|
192
|
+
failure,
|
|
193
|
+
authority,
|
|
194
|
+
resumable
|
|
195
|
+
? { resume: { command: renderResumeCommand(admitted.runId) } }
|
|
196
|
+
: {},
|
|
197
|
+
);
|
|
198
|
+
presentFailureTerminal(terminal, io);
|
|
199
|
+
return {
|
|
200
|
+
exitCode: exitCodeForTerminalOutcome(terminal.roleOutcome),
|
|
201
|
+
admitted,
|
|
202
|
+
terminal,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function presentSecondaryTerminal(terminal: TerminalResult, io: CliIo): void {
|
|
207
|
+
if (terminal.roleOutcome.kind === "failure" || terminal.roleOutcome.kind === "no_receipt") {
|
|
208
|
+
presentFailureTerminal(terminal, io);
|
|
209
|
+
} else {
|
|
210
|
+
io.stdout(formatTerminalResult(terminal));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export async function dispatchPostAdmissionTurn<
|
|
215
|
+
A extends AdmittedRoleInvocation,
|
|
216
|
+
T extends TerminalResult = TerminalResult,
|
|
217
|
+
>(input: {
|
|
218
|
+
admitted: A;
|
|
219
|
+
env: PostAdmissionEnv;
|
|
220
|
+
io: CliIo;
|
|
221
|
+
request: RoleTurnRequest;
|
|
222
|
+
lease: RunWriterLease;
|
|
223
|
+
adapters: PostAdmissionAdapters<A, T>;
|
|
224
|
+
effectiveEngine?: string;
|
|
225
|
+
}): Promise<{
|
|
226
|
+
exitCode: number;
|
|
227
|
+
admitted: A;
|
|
228
|
+
terminal?: T;
|
|
229
|
+
}> {
|
|
230
|
+
const { admitted, env, io, request, lease, adapters, effectiveEngine } = input;
|
|
231
|
+
const shouldPresent =
|
|
232
|
+
adapters.shouldPresentSettled ?? ((terminal: T) => isLawfulTypedTerminalOutcome(terminal.roleOutcome));
|
|
233
|
+
try {
|
|
234
|
+
const missingCredential = missingCredentialPreDispatchFailure(
|
|
235
|
+
env.model,
|
|
236
|
+
env.credentials,
|
|
237
|
+
);
|
|
238
|
+
if (missingCredential !== undefined) {
|
|
239
|
+
return (await presentControlledFailure(
|
|
240
|
+
admitted,
|
|
241
|
+
missingCredential,
|
|
242
|
+
adapters,
|
|
243
|
+
env.principalAuthority,
|
|
244
|
+
io,
|
|
245
|
+
)) as { exitCode: number; admitted: A; terminal: T };
|
|
246
|
+
}
|
|
247
|
+
await markRunRunning(admitted.runDirectory, env.model, effectiveEngine);
|
|
248
|
+
await clearTypedProviderHttpObservation(admitted.runDirectory);
|
|
249
|
+
if (adapters.beforeDispatch !== undefined) {
|
|
250
|
+
await adapters.beforeDispatch(admitted);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
let result: RoleTurnResult;
|
|
254
|
+
try {
|
|
255
|
+
result = await env.roleTurnHost.executeTurn(request);
|
|
256
|
+
} catch (error) {
|
|
257
|
+
return (await presentControlledFailure(
|
|
258
|
+
admitted,
|
|
259
|
+
{
|
|
260
|
+
timedOut: false,
|
|
261
|
+
code: null,
|
|
262
|
+
stderr: "",
|
|
263
|
+
thrown: error,
|
|
264
|
+
},
|
|
265
|
+
adapters,
|
|
266
|
+
env.principalAuthority,
|
|
267
|
+
io,
|
|
268
|
+
)) as { exitCode: number; admitted: A; terminal: T };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
await writeFile(
|
|
273
|
+
join(admitted.runDirectory, "stderr.log"),
|
|
274
|
+
result.stderr,
|
|
275
|
+
"utf8",
|
|
276
|
+
);
|
|
277
|
+
} catch {
|
|
278
|
+
// continue to lawful / controlled-failure settlement
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
let settled: T | undefined;
|
|
282
|
+
try {
|
|
283
|
+
settled = await adapters.trySettle(admitted, env.principalAuthority);
|
|
284
|
+
} catch (error) {
|
|
285
|
+
return (await presentControlledFailure(
|
|
286
|
+
admitted,
|
|
287
|
+
{
|
|
288
|
+
timedOut: false,
|
|
289
|
+
code: result.code,
|
|
290
|
+
stderr: result.stderr,
|
|
291
|
+
thrown: error,
|
|
292
|
+
},
|
|
293
|
+
adapters,
|
|
294
|
+
env.principalAuthority,
|
|
295
|
+
io,
|
|
296
|
+
)) as { exitCode: number; admitted: A; terminal: T };
|
|
297
|
+
}
|
|
298
|
+
if (settled !== undefined && shouldPresent(settled)) {
|
|
299
|
+
await markRunTerminal(admitted.runDirectory);
|
|
300
|
+
io.stdout(formatTerminalResult(settled));
|
|
301
|
+
return {
|
|
302
|
+
exitCode: exitCodeForTerminalOutcome(settled.roleOutcome),
|
|
303
|
+
admitted,
|
|
304
|
+
terminal: settled,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (adapters.trySettleSecondary !== undefined) {
|
|
309
|
+
const secondary = await adapters.trySettleSecondary(admitted, env.principalAuthority);
|
|
310
|
+
if (secondary !== undefined) {
|
|
311
|
+
await markRunTerminal(admitted.runDirectory);
|
|
312
|
+
presentSecondaryTerminal(secondary, io);
|
|
313
|
+
return {
|
|
314
|
+
exitCode: exitCodeForTerminalOutcome(secondary.roleOutcome),
|
|
315
|
+
admitted,
|
|
316
|
+
terminal: secondary as unknown as T,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const sessionFile =
|
|
322
|
+
admitted.principal !== undefined
|
|
323
|
+
? env.principalAuthority.decode(admitted.principal).sessionFile
|
|
324
|
+
: "";
|
|
325
|
+
const runnerKnownFailure =
|
|
326
|
+
adapters.resolveRunnerKnownFailure !== undefined && sessionFile !== ""
|
|
327
|
+
? await adapters.resolveRunnerKnownFailure({ result, sessionFile })
|
|
328
|
+
: result.knownFailure;
|
|
329
|
+
const credentialFailure = postRunMissingCredentialFailure(
|
|
330
|
+
result,
|
|
331
|
+
env.model,
|
|
332
|
+
env.credentials,
|
|
333
|
+
);
|
|
334
|
+
const resolution = await resolveAuditedRunnerFailureResolution({
|
|
335
|
+
runner: runnerKnownFailure,
|
|
336
|
+
sessionFile,
|
|
337
|
+
credential: credentialFailure,
|
|
338
|
+
runDirectory: admitted.runDirectory,
|
|
339
|
+
});
|
|
340
|
+
return (await presentControlledFailure(
|
|
341
|
+
admitted,
|
|
342
|
+
{
|
|
343
|
+
timedOut: result.timedOut,
|
|
344
|
+
code: result.code,
|
|
345
|
+
stderr: result.stderr,
|
|
346
|
+
...controlledFailureInputFromResolution(resolution),
|
|
347
|
+
},
|
|
348
|
+
adapters,
|
|
349
|
+
env.principalAuthority,
|
|
350
|
+
io,
|
|
351
|
+
)) as { exitCode: number; admitted: A; terminal: T };
|
|
352
|
+
} finally {
|
|
353
|
+
await lease.release();
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Shared post-admission one-shot path: writer lease, then turn dispatch.
|
|
359
|
+
* Initial facades own the durable admitted mark (markRunAdmitted) before
|
|
360
|
+
* entering; manual resume never re-admits.
|
|
361
|
+
*/
|
|
362
|
+
export async function runPostAdmissionOneShot<
|
|
363
|
+
A extends AdmittedRoleInvocation,
|
|
364
|
+
T extends TerminalResult = TerminalResult,
|
|
365
|
+
>(input: {
|
|
366
|
+
admitted: A;
|
|
367
|
+
env: PostAdmissionEnv;
|
|
368
|
+
io: CliIo;
|
|
369
|
+
request: RoleTurnRequest;
|
|
370
|
+
adapters: PostAdmissionAdapters<A, T>;
|
|
371
|
+
effectiveEngine?: string;
|
|
372
|
+
}): Promise<{
|
|
373
|
+
exitCode: number;
|
|
374
|
+
admitted?: A;
|
|
375
|
+
terminal?: T;
|
|
376
|
+
}> {
|
|
377
|
+
const { admitted, env, io, request, adapters, effectiveEngine } = input;
|
|
378
|
+
|
|
379
|
+
let lease: RunWriterLease;
|
|
380
|
+
try {
|
|
381
|
+
lease = await acquireRunWriterLease(admitted.runDirectory, (diagnostic) =>
|
|
382
|
+
io.stderr(diagnostic),
|
|
383
|
+
);
|
|
384
|
+
} catch (error) {
|
|
385
|
+
if (error instanceof RunWriterLeaseHeldError) {
|
|
386
|
+
presentStructuralRejection(error, io);
|
|
387
|
+
return { exitCode: 2, admitted };
|
|
388
|
+
}
|
|
389
|
+
throw error;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return await dispatchPostAdmissionTurn({
|
|
393
|
+
admitted,
|
|
394
|
+
env,
|
|
395
|
+
io,
|
|
396
|
+
request,
|
|
397
|
+
lease,
|
|
398
|
+
adapters,
|
|
399
|
+
...(effectiveEngine === undefined ? {} : { effectiveEngine }),
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Shared post-admission resumable path with auto-resume retry loop.
|
|
405
|
+
* Initial facades own the durable admitted mark before entering.
|
|
406
|
+
*/
|
|
407
|
+
export async function runPostAdmissionResumable<
|
|
408
|
+
A extends AdmittedRoleInvocation,
|
|
409
|
+
T extends TerminalResult = TerminalResult,
|
|
410
|
+
>(input: {
|
|
411
|
+
admitted: A;
|
|
412
|
+
env: PostAdmissionEnv;
|
|
413
|
+
io: CliIo;
|
|
414
|
+
buildInitialRequest: () => RoleTurnRequest;
|
|
415
|
+
buildResumeRequest: () => RoleTurnRequest;
|
|
416
|
+
adapters: PostAdmissionAdapters<A, T>;
|
|
417
|
+
effectiveEngine?: string;
|
|
418
|
+
}): Promise<{
|
|
419
|
+
exitCode: number;
|
|
420
|
+
admitted?: A;
|
|
421
|
+
terminal?: T;
|
|
422
|
+
}> {
|
|
423
|
+
const { admitted, env, io, buildInitialRequest, buildResumeRequest, adapters, effectiveEngine } = input;
|
|
424
|
+
|
|
425
|
+
return runWithAutoResumeLoop({
|
|
426
|
+
admitted,
|
|
427
|
+
principalAuthority: env.principalAuthority,
|
|
428
|
+
io,
|
|
429
|
+
sessionAppender: env.sessionAppender,
|
|
430
|
+
autoResumeLimit: env.autoResumeLimit,
|
|
431
|
+
buildInitialPayload: buildInitialRequest,
|
|
432
|
+
buildResumePayload: buildResumeRequest,
|
|
433
|
+
dispatch: (request, lease, isFirst, attemptIo) =>
|
|
434
|
+
dispatchPostAdmissionTurn({
|
|
435
|
+
admitted,
|
|
436
|
+
env: {
|
|
437
|
+
...env,
|
|
438
|
+
...(admitted.correlationId === undefined ? {} : { correlationId: admitted.correlationId }),
|
|
439
|
+
},
|
|
440
|
+
io: attemptIo,
|
|
441
|
+
request,
|
|
442
|
+
lease,
|
|
443
|
+
adapters,
|
|
444
|
+
...(isFirst && effectiveEngine !== undefined ? { effectiveEngine } : {}),
|
|
445
|
+
}),
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Shared post-admission manual resume path: acquire writer lease and dispatch turn.
|
|
451
|
+
*/
|
|
452
|
+
export async function runPostAdmissionManualResume<
|
|
453
|
+
A extends AdmittedRoleInvocation,
|
|
454
|
+
T extends TerminalResult = TerminalResult,
|
|
455
|
+
>(input: {
|
|
456
|
+
admitted: A;
|
|
457
|
+
env: PostAdmissionEnv;
|
|
458
|
+
io: CliIo;
|
|
459
|
+
request: RoleTurnRequest;
|
|
460
|
+
adapters: PostAdmissionAdapters<A, T>;
|
|
461
|
+
}): Promise<{
|
|
462
|
+
exitCode: number;
|
|
463
|
+
admitted?: A;
|
|
464
|
+
terminal?: T;
|
|
465
|
+
}> {
|
|
466
|
+
const { admitted, env, io, request, adapters } = input;
|
|
467
|
+
// Single seam: explicit env model wins; otherwise restore admitted.model
|
|
468
|
+
// (including thinking) so a model-less manual resume reuses the recorded model.
|
|
469
|
+
const effectiveModel = resolveResumeModel(env.model, admitted.model);
|
|
470
|
+
let lease: RunWriterLease;
|
|
471
|
+
try {
|
|
472
|
+
lease = await acquireRunWriterLease(admitted.runDirectory, (diagnostic) => io.stderr(diagnostic));
|
|
473
|
+
} catch (error) {
|
|
474
|
+
if (error instanceof RunWriterLeaseHeldError) {
|
|
475
|
+
io.stderr(formatCliDiagnostic(error.message));
|
|
476
|
+
return { exitCode: 1 };
|
|
477
|
+
}
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const result = await dispatchPostAdmissionTurn({
|
|
482
|
+
admitted,
|
|
483
|
+
env: {
|
|
484
|
+
...env,
|
|
485
|
+
...(effectiveModel === undefined ? {} : { model: effectiveModel }),
|
|
486
|
+
...(admitted.correlationId === undefined ? {} : { correlationId: admitted.correlationId }),
|
|
487
|
+
},
|
|
488
|
+
io,
|
|
489
|
+
request,
|
|
490
|
+
lease,
|
|
491
|
+
adapters,
|
|
492
|
+
});
|
|
493
|
+
if (result.terminal !== undefined) {
|
|
494
|
+
(result.terminal as { autoResumeCount?: number }).autoResumeCount = 0;
|
|
495
|
+
}
|
|
496
|
+
return result;
|
|
497
|
+
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
type CredentialProviders,
|
|
9
9
|
type SeatModelConfig,
|
|
10
10
|
} from "./config.ts";
|
|
11
|
-
import type {
|
|
11
|
+
import type { RoleTurnKnownFailure } from "../host-contracts.ts";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Production-owned provider failure when the selected public seat provider has
|
|
@@ -18,7 +18,7 @@ import type { ExplicitInternalKnownFailure } from "./explicit-internal.ts";
|
|
|
18
18
|
export function knownFailureForMissingProviderCredential(
|
|
19
19
|
model: SeatModelConfig | undefined,
|
|
20
20
|
credentials: CredentialProviders | undefined,
|
|
21
|
-
):
|
|
21
|
+
): RoleTurnKnownFailure | undefined {
|
|
22
22
|
if (model === undefined || credentials === undefined) return undefined;
|
|
23
23
|
// Only the public credential catalog is fail-closed here; offline/test providers
|
|
24
24
|
// are not represented in auth.json shape and must not be washed into MissingProviderCredential.
|
|
@@ -40,7 +40,7 @@ export type MissingCredentialPreDispatchFailure = Readonly<{
|
|
|
40
40
|
timedOut: false;
|
|
41
41
|
code: 1;
|
|
42
42
|
stderr: string;
|
|
43
|
-
knownFailure:
|
|
43
|
+
knownFailure: RoleTurnKnownFailure;
|
|
44
44
|
}>;
|
|
45
45
|
|
|
46
46
|
export function missingCredentialPreDispatchFailure(
|
|
@@ -65,7 +65,7 @@ export function postRunMissingCredentialFailure(
|
|
|
65
65
|
result: Readonly<{ timedOut: boolean; code: number | null }>,
|
|
66
66
|
model: SeatModelConfig | undefined,
|
|
67
67
|
credentials: CredentialProviders | undefined,
|
|
68
|
-
):
|
|
68
|
+
): RoleTurnKnownFailure | undefined {
|
|
69
69
|
if (!(result.timedOut || result.code !== 0)) return undefined;
|
|
70
70
|
return knownFailureForMissingProviderCredential(model, credentials);
|
|
71
71
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable Reviewer-rejection child→parent page under AK_ROLE_RUN_DIR.
|
|
3
|
+
* Stays in public-cli (AK artifact owner); role-runtime imports this neighbor.
|
|
4
|
+
*/
|
|
5
|
+
import { writeFileSync } from "node:fs";
|
|
6
|
+
import { readFile, unlink } from "node:fs/promises";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
|
|
9
|
+
import type { RoleTurnKnownFailure } from "../host-contracts.ts";
|
|
10
|
+
import {
|
|
11
|
+
REVIEWER_PREFLIGHT_VIOLATIONS,
|
|
12
|
+
type ReviewerPreflightViolation,
|
|
13
|
+
} from "../reviewer-dispatch.ts";
|
|
14
|
+
|
|
15
|
+
const REVIEWER_DISPATCH_REJECTION_FILE = "typed-known-failure.json";
|
|
16
|
+
|
|
17
|
+
function isReviewerPreflightViolation(value: unknown): value is ReviewerPreflightViolation {
|
|
18
|
+
return (
|
|
19
|
+
typeof value === "string" &&
|
|
20
|
+
(REVIEWER_PREFLIGHT_VIOLATIONS as readonly string[]).includes(value)
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function reviewerDispatchRejectionPath(runDirectory: string): string {
|
|
25
|
+
return join(runDirectory, REVIEWER_DISPATCH_REJECTION_FILE);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Clear any prior attempt's Reviewer rejection page so resume/retry cannot
|
|
30
|
+
* inherit a stale knownFailure.details.
|
|
31
|
+
*/
|
|
32
|
+
export async function clearReviewerDispatchRejection(runDirectory: string): Promise<void> {
|
|
33
|
+
try {
|
|
34
|
+
await unlink(reviewerDispatchRejectionPath(runDirectory));
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (
|
|
37
|
+
error instanceof Error &&
|
|
38
|
+
"code" in error &&
|
|
39
|
+
(error as { code?: unknown }).code === "ENOENT"
|
|
40
|
+
) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Synchronous durable write for Reviewer dispatch rejection (child process exit is sync).
|
|
49
|
+
* Parent public CLI recovers via readReviewerDispatchRejection into knownFailure.
|
|
50
|
+
*/
|
|
51
|
+
export function recordReviewerDispatchRejectionSync(
|
|
52
|
+
runDirectory: string,
|
|
53
|
+
rejection: Readonly<{
|
|
54
|
+
diagnostic: string;
|
|
55
|
+
violations: readonly ReviewerPreflightViolation[];
|
|
56
|
+
}>,
|
|
57
|
+
): void {
|
|
58
|
+
writeFileSync(
|
|
59
|
+
reviewerDispatchRejectionPath(runDirectory),
|
|
60
|
+
`${JSON.stringify(rejection)}\n`,
|
|
61
|
+
"utf8",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Recover a child-written Reviewer dispatch rejection through a fixed mapping. */
|
|
66
|
+
export async function readReviewerDispatchRejection(
|
|
67
|
+
runDirectory: string,
|
|
68
|
+
): Promise<RoleTurnKnownFailure | undefined> {
|
|
69
|
+
let raw: string;
|
|
70
|
+
try {
|
|
71
|
+
raw = await readFile(reviewerDispatchRejectionPath(runDirectory), "utf8");
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (
|
|
74
|
+
error instanceof Error &&
|
|
75
|
+
"code" in error &&
|
|
76
|
+
(error as { code?: unknown }).code === "ENOENT"
|
|
77
|
+
) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
const parsed: unknown = JSON.parse(raw);
|
|
83
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
84
|
+
const error = new Error("Reviewer dispatch rejection page must be a JSON object");
|
|
85
|
+
error.name = "ReviewerDispatchRejectionContractError";
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
const record = parsed as Record<string, unknown>;
|
|
89
|
+
if (
|
|
90
|
+
typeof record.diagnostic !== "string" ||
|
|
91
|
+
record.diagnostic.trim() === "" ||
|
|
92
|
+
!Array.isArray(record.violations) ||
|
|
93
|
+
record.violations.length === 0 ||
|
|
94
|
+
record.violations.some((value) => !isReviewerPreflightViolation(value))
|
|
95
|
+
) {
|
|
96
|
+
const error = new Error("Reviewer dispatch rejection page has unusable required fields");
|
|
97
|
+
error.name = "ReviewerDispatchRejectionContractError";
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
cause: "activation",
|
|
102
|
+
diagnostic: record.diagnostic,
|
|
103
|
+
identity: { name: "ReviewerDispatchRejectionError" },
|
|
104
|
+
details: { violations: Object.freeze([...record.violations]) },
|
|
105
|
+
};
|
|
106
|
+
}
|