@akagilnc/pi-workflow-roles 0.1.4239 → 0.1.4259
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/acp-host/production-host.js +547 -221
- package/dist/engine-detour-tool.js +64 -5
- package/dist/engine-detour-usage.js +285 -0
- package/dist/headless-host/production-host.js +557 -231
- package/dist/pi/role-turn-host.js +10 -0
- package/dist/public-cli/main.js +433 -202
- package/dist/public-cli/post-admission.js +72 -43
- package/dist/public-cli/settlement.js +80 -24
- package/dist/public-cli/turn-request.js +3 -0
- package/package.json +1 -1
- package/src/engine-detour-tool.ts +106 -8
- package/src/engine-detour-usage.ts +373 -0
- package/src/host-contracts.ts +12 -1
- package/src/pi/adapter.ts +6 -0
- package/src/pi/role-turn-host.ts +6 -0
- package/src/public-cli/post-admission.ts +91 -49
- package/src/public-cli/settlement.ts +148 -23
- package/src/public-cli/turn-request.ts +5 -0
- package/src/role-envelope.ts +2 -0
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Role runners supply only turn request projection and narrow settlement adapters.
|
|
7
7
|
*/
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
|
-
import {
|
|
9
|
+
import { writeFile } from "node:fs/promises";
|
|
10
10
|
import { isAbsolute, join, resolve } from "node:path";
|
|
11
11
|
import { buildResumeContinuationPrompt, RESUME_TRANSPORT_ENVELOPE, } from "./run-lifecycle.js";
|
|
12
12
|
import { CliUsageError } from "./cli-errors.js";
|
|
@@ -39,6 +39,7 @@ function appendContinuationSection(continuation, section) {
|
|
|
39
39
|
function isStationChildOfficerDialogue(role, env) {
|
|
40
40
|
return env.stationChild === true && isOfficerReviewSeat(role);
|
|
41
41
|
}
|
|
42
|
+
import { mintEngineDetourInvocationScope, readInvocationSelectedHost, withEngineDetourInvocationScope, } from "../engine-detour-usage.js";
|
|
42
43
|
import { projectHostTransitionPriorNative } from "../host-transition-prior-native.js";
|
|
43
44
|
import { missingCredentialPreDispatchFailure, postRunMissingCredentialFailure, } from "./public-run-credentials.js";
|
|
44
45
|
import { acquireRunWriterLease, clearCurrentCourt, clearTypedProviderHttpObservation, describeErrorIdentity, markRunRunning, readCurrentCourt, recordCurrentCourt, renderResumeCommand, RunWriterLeaseHeldError, } from "./run-lifecycle.js";
|
|
@@ -106,18 +107,6 @@ async function recordBestEffortPostDispatchDiagnostic(admitted, env, diagnostic,
|
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
|
-
/** Previous main-session host recorded on invocation.json, if any. */
|
|
110
|
-
async function readInvocationHost(runDirectory) {
|
|
111
|
-
try {
|
|
112
|
-
const raw = JSON.parse(await readFile(join(runDirectory, "invocation.json"), "utf8"));
|
|
113
|
-
return typeof raw.host === "string" && raw.host.trim() !== "" ? raw.host : undefined;
|
|
114
|
-
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
if (error.code === "ENOENT")
|
|
117
|
-
return undefined;
|
|
118
|
-
throw error;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
110
|
/**
|
|
122
111
|
* Factory-seat resume method-material face (#833): one authority for
|
|
123
112
|
* load → adapters / controlled-failure short-circuit. Seats only declare
|
|
@@ -196,9 +185,15 @@ export async function presentControlledFailure(admitted, failureInput, adapters,
|
|
|
196
185
|
if (persistRunState && !failureInput.skipRunStateWrite) {
|
|
197
186
|
await persistReturnedRunState(admitted, authority);
|
|
198
187
|
}
|
|
199
|
-
const terminal = await attachRecordedSubmissions(admitted, await settleFailureTerminalResult(admitted, failure, authority,
|
|
200
|
-
|
|
201
|
-
|
|
188
|
+
const terminal = await attachRecordedSubmissions(admitted, await settleFailureTerminalResult(admitted, failure, authority, {
|
|
189
|
+
...(resumable
|
|
190
|
+
? { resume: { command: renderResumeCommand(admitted.runId) } }
|
|
191
|
+
: {}),
|
|
192
|
+
...(failureInput.invocationScopeId === undefined ||
|
|
193
|
+
failureInput.invocationScopeId.length === 0
|
|
194
|
+
? {}
|
|
195
|
+
: { invocationScopeId: failureInput.invocationScopeId }),
|
|
196
|
+
}));
|
|
202
197
|
presentFailureTerminal(terminal, io);
|
|
203
198
|
return {
|
|
204
199
|
exitCode: exitCodeForTerminalOutcome(terminal.roleOutcome),
|
|
@@ -282,12 +277,12 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
282
277
|
return result;
|
|
283
278
|
}
|
|
284
279
|
return {
|
|
285
|
-
...(await presentControlledFailure(admitted, {
|
|
280
|
+
...(await presentControlledFailure(admitted, withEngineDetourInvocationScope({
|
|
286
281
|
timedOut: false,
|
|
287
282
|
code: null,
|
|
288
283
|
stderr: "",
|
|
289
284
|
thrown: error,
|
|
290
|
-
}, adapters, env.principalAuthority, io, persistRunState)),
|
|
285
|
+
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState)),
|
|
291
286
|
...(result.turnDispatched === true ? { turnDispatched: true } : {}),
|
|
292
287
|
...(result.skipAutoResume === true ? { skipAutoResume: true } : {}),
|
|
293
288
|
...deferredPersist,
|
|
@@ -298,7 +293,7 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
298
293
|
const missingCredential = missingCredentialPreDispatchFailure(env.model, env.credentials);
|
|
299
294
|
if (missingCredential !== undefined) {
|
|
300
295
|
return {
|
|
301
|
-
...(await presentControlledFailure(admitted, missingCredential, adapters, env.principalAuthority, io, persistRunState)),
|
|
296
|
+
...(await presentControlledFailure(admitted, withEngineDetourInvocationScope(missingCredential, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState)),
|
|
302
297
|
...deferredPersist,
|
|
303
298
|
};
|
|
304
299
|
}
|
|
@@ -312,7 +307,7 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
312
307
|
: env.principalAuthority.decode(admitted.principal);
|
|
313
308
|
let hostTransition;
|
|
314
309
|
try {
|
|
315
|
-
previousHost =
|
|
310
|
+
previousHost = readInvocationSelectedHost(admitted.runDirectory);
|
|
316
311
|
hostTransition =
|
|
317
312
|
previousHost !== undefined && liveHost !== undefined && principalCoordinates !== undefined
|
|
318
313
|
? await projectHostTransitionPriorNative({
|
|
@@ -325,12 +320,12 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
325
320
|
catch (error) {
|
|
326
321
|
// prior-native IO is on the public one-shot path — controlled failure, not bare throw.
|
|
327
322
|
return {
|
|
328
|
-
...(await presentControlledFailure(admitted, {
|
|
323
|
+
...(await presentControlledFailure(admitted, withEngineDetourInvocationScope({
|
|
329
324
|
timedOut: false,
|
|
330
325
|
code: null,
|
|
331
326
|
stderr: "",
|
|
332
327
|
thrown: error,
|
|
333
|
-
}, adapters, env.principalAuthority, io, persistRunState)),
|
|
328
|
+
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState)),
|
|
334
329
|
...deferredPersist,
|
|
335
330
|
};
|
|
336
331
|
}
|
|
@@ -342,7 +337,7 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
342
337
|
// executeTurn — not merely past beforeDispatch (#840 r9 判词 class 2). Any
|
|
343
338
|
// pre-turn retry (beforeDispatch, dossier projection, continuation
|
|
344
339
|
// assembly) must still see the prior invocation host on its next attempt;
|
|
345
|
-
// committing env.host earlier would make
|
|
340
|
+
// committing env.host earlier would make readInvocationSelectedHost read back the
|
|
346
341
|
// new host on the retry and silently drop hostTransition.
|
|
347
342
|
// Failures settle the run (presentControlledFailure); they must not leave it
|
|
348
343
|
// permanently running. Call-local auto-resume retries this hook until it
|
|
@@ -353,12 +348,12 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
353
348
|
await adapters.beforeDispatch(admitted, lease);
|
|
354
349
|
}
|
|
355
350
|
catch (error) {
|
|
356
|
-
const settled = (await presentControlledFailure(admitted, {
|
|
351
|
+
const settled = (await presentControlledFailure(admitted, withEngineDetourInvocationScope({
|
|
357
352
|
timedOut: false,
|
|
358
353
|
code: null,
|
|
359
354
|
stderr: "",
|
|
360
355
|
thrown: error,
|
|
361
|
-
}, adapters, env.principalAuthority, io, persistRunState));
|
|
356
|
+
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState));
|
|
362
357
|
if (error instanceof StationChildExhaustedError) {
|
|
363
358
|
return { ...settled, skipAutoResume: true, ...deferredPersist };
|
|
364
359
|
}
|
|
@@ -378,6 +373,11 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
378
373
|
if (hostTransition !== undefined) {
|
|
379
374
|
turnRequest = { ...turnRequest, hostTransition };
|
|
380
375
|
}
|
|
376
|
+
// Selected host axis rides the shared Host envelope for in-turn tools
|
|
377
|
+
// (detour usage ledger) — never a pre-spawn invocation.json reread.
|
|
378
|
+
if (typeof liveHost === "string" && liveHost.trim() !== "") {
|
|
379
|
+
turnRequest = { ...turnRequest, host: liveHost.trim() };
|
|
380
|
+
}
|
|
381
381
|
if (isStationChildOfficerDialogue(admitted.role, env)) {
|
|
382
382
|
// 0081 non-body face: freeze pointer section under run/attachments/.
|
|
383
383
|
// Peer dialogue continuation.prompt stays parent payload only — the seat
|
|
@@ -408,17 +408,20 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
408
408
|
// immediately before the turn actually starts, after every retryable
|
|
409
409
|
// pre-turn step above has succeeded on this attempt (#840 r9 判词 class 2).
|
|
410
410
|
await markRunRunning(admitted.runDirectory, env.model, effectiveEngine, env.host, env.engineModel);
|
|
411
|
+
// #537 invocation scope is bound once at the public-entry boundary
|
|
412
|
+
// (runPostAdmissionResumable / ManualResume / station-child), not here:
|
|
413
|
+
// auto-resume re-enters this dispatch and must reuse the same scope.
|
|
411
414
|
let result;
|
|
412
415
|
try {
|
|
413
416
|
result = await env.roleTurnHost.executeTurn(turnRequest);
|
|
414
417
|
}
|
|
415
418
|
catch (error) {
|
|
416
|
-
const settled = await settleAfterTurnStarted(admitted, {
|
|
419
|
+
const settled = await settleAfterTurnStarted(admitted, withEngineDetourInvocationScope({
|
|
417
420
|
timedOut: false,
|
|
418
421
|
code: null,
|
|
419
422
|
stderr: "",
|
|
420
423
|
thrown: error,
|
|
421
|
-
}, adapters, env.principalAuthority, io, persistRunState);
|
|
424
|
+
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState);
|
|
422
425
|
return await finishAfterTurn({ ...settled, turnDispatched: true, ...deferredPersist });
|
|
423
426
|
}
|
|
424
427
|
// `result.stderr` stays live in memory for the real classification below
|
|
@@ -462,9 +465,18 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
462
465
|
// wrong, becomes the reported failure itself (#836).
|
|
463
466
|
await recordBestEffortPostDispatchDiagnostic(admitted, env, `stderr.log write failed (best-effort continue): ${describeErrorIdentity(error)}`, io);
|
|
464
467
|
}
|
|
465
|
-
const courtScope = request.courtAttemptId === undefined || request.courtAttemptId.length === 0
|
|
468
|
+
const courtScope = (request.courtAttemptId === undefined || request.courtAttemptId.length === 0) &&
|
|
469
|
+
(request.invocationScopeId === undefined || request.invocationScopeId.length === 0)
|
|
466
470
|
? undefined
|
|
467
|
-
: {
|
|
471
|
+
: {
|
|
472
|
+
...(request.courtAttemptId === undefined || request.courtAttemptId.length === 0
|
|
473
|
+
? {}
|
|
474
|
+
: { courtAttemptId: request.courtAttemptId }),
|
|
475
|
+
...(request.invocationScopeId === undefined ||
|
|
476
|
+
request.invocationScopeId.length === 0
|
|
477
|
+
? {}
|
|
478
|
+
: { invocationScopeId: request.invocationScopeId }),
|
|
479
|
+
};
|
|
468
480
|
// Single complete boundary (#840 r9 判词 class 1): resolving the
|
|
469
481
|
// host/runner failure facts, trySettle, its shouldPresent gate, and the
|
|
470
482
|
// accepted-settlement cleanup all settle through this one catch — a
|
|
@@ -580,12 +592,12 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
580
592
|
catch (error) {
|
|
581
593
|
// Settle (or its shouldPresent gate, or the failure-fact resolution
|
|
582
594
|
// above) throw is a real failure fact — never swallow into undefined.
|
|
583
|
-
const settledFailure = await settleAfterTurnStarted(admitted, {
|
|
595
|
+
const settledFailure = await settleAfterTurnStarted(admitted, withEngineDetourInvocationScope({
|
|
584
596
|
timedOut: false,
|
|
585
597
|
code: result.code,
|
|
586
598
|
stderr: result.stderr,
|
|
587
599
|
thrown: error,
|
|
588
|
-
}, adapters, env.principalAuthority, io, persistRunState);
|
|
600
|
+
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState);
|
|
589
601
|
return await finishAfterTurn({ ...settledFailure, turnDispatched: true, ...deferredPersist });
|
|
590
602
|
}
|
|
591
603
|
if (settledOutcome !== undefined) {
|
|
@@ -604,13 +616,13 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
604
616
|
// at all. skipRunStateWrite: the write that just threw is the same
|
|
605
617
|
// write presentControlledFailure would otherwise retry — don't
|
|
606
618
|
// call a known-failing operation twice.
|
|
607
|
-
const failed = await settleAfterTurnStarted(admitted, {
|
|
619
|
+
const failed = await settleAfterTurnStarted(admitted, withEngineDetourInvocationScope({
|
|
608
620
|
timedOut: false,
|
|
609
621
|
code: null,
|
|
610
622
|
stderr: "",
|
|
611
623
|
thrown: error,
|
|
612
624
|
skipRunStateWrite: true,
|
|
613
|
-
}, adapters, env.principalAuthority, io, persistRunState);
|
|
625
|
+
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState);
|
|
614
626
|
return await finishAfterTurn({ ...failed, turnDispatched: true, ...deferredPersist });
|
|
615
627
|
}
|
|
616
628
|
}
|
|
@@ -624,7 +636,7 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
624
636
|
const stderrLogWriteDetails = stderrLogWriteFailure === undefined
|
|
625
637
|
? undefined
|
|
626
638
|
: { stderrLogWriteFailure: describeCaughtError(stderrLogWriteFailure) };
|
|
627
|
-
const failed = await settleAfterTurnStarted(admitted, {
|
|
639
|
+
const failed = await settleAfterTurnStarted(admitted, withEngineDetourInvocationScope({
|
|
628
640
|
timedOut: result.timedOut,
|
|
629
641
|
code: result.code,
|
|
630
642
|
stderr: result.stderr,
|
|
@@ -642,22 +654,22 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
642
654
|
},
|
|
643
655
|
}
|
|
644
656
|
: { knownDetails: stderrLogWriteDetails }),
|
|
645
|
-
}, adapters, env.principalAuthority, io, persistRunState);
|
|
657
|
+
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState);
|
|
646
658
|
return await finishAfterTurn({ ...failed, turnDispatched: true, ...deferredPersist });
|
|
647
659
|
}
|
|
648
660
|
// Nothing else was wrong, but the durable stderr mirror itself failed to
|
|
649
661
|
// write — that is the real (infrastructure) problem in this case, not a
|
|
650
662
|
// lawful absence of a receipt. Honest and loud, not silently no_receipt.
|
|
651
663
|
if (stderrLogWriteFailure !== undefined) {
|
|
652
|
-
const failed = await settleAfterTurnStarted(admitted, {
|
|
664
|
+
const failed = await settleAfterTurnStarted(admitted, withEngineDetourInvocationScope({
|
|
653
665
|
timedOut: false,
|
|
654
666
|
code: result.code,
|
|
655
667
|
stderr: result.stderr,
|
|
656
668
|
thrown: stderrLogWriteFailure,
|
|
657
|
-
}, adapters, env.principalAuthority, io, persistRunState);
|
|
669
|
+
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState);
|
|
658
670
|
return await finishAfterTurn({ ...failed, turnDispatched: true, ...deferredPersist });
|
|
659
671
|
}
|
|
660
|
-
const noReceipt = await attachRecordedSubmissions(admitted, await settleHostEndedNoReceipt(admitted, env.principalAuthority), courtScope);
|
|
672
|
+
const noReceipt = await attachRecordedSubmissions(admitted, await settleHostEndedNoReceipt(admitted, env.principalAuthority, courtScope), courtScope);
|
|
661
673
|
if (persistRunState) {
|
|
662
674
|
try {
|
|
663
675
|
await persistReturnedRunState(admitted, env.principalAuthority, { lawful: true });
|
|
@@ -668,13 +680,13 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
668
680
|
// the real failure surfaces loudly instead of escaping uncaught to
|
|
669
681
|
// auto-resume's dispatch-retry path with no recorded submissions.
|
|
670
682
|
// skipRunStateWrite: don't retry the write that just threw.
|
|
671
|
-
const failed = await settleAfterTurnStarted(admitted, {
|
|
683
|
+
const failed = await settleAfterTurnStarted(admitted, withEngineDetourInvocationScope({
|
|
672
684
|
timedOut: false,
|
|
673
685
|
code: null,
|
|
674
686
|
stderr: "",
|
|
675
687
|
thrown: error,
|
|
676
688
|
skipRunStateWrite: true,
|
|
677
|
-
}, adapters, env.principalAuthority, io, persistRunState);
|
|
689
|
+
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState);
|
|
678
690
|
return await finishAfterTurn({ ...failed, turnDispatched: true, ...deferredPersist });
|
|
679
691
|
}
|
|
680
692
|
}
|
|
@@ -941,6 +953,12 @@ export async function runPostAdmissionSeatResume(input) {
|
|
|
941
953
|
if (input.env.stationChild === true) {
|
|
942
954
|
let firstTurn;
|
|
943
955
|
const stationAdapters = withOnceSuccessfulBeforeDispatch(adapters);
|
|
956
|
+
// One public call → one detour scope across in-place auto-resume dispatches.
|
|
957
|
+
const invocationScopeId = mintEngineDetourInvocationScope({
|
|
958
|
+
...(input.effectiveEngine === undefined
|
|
959
|
+
? {}
|
|
960
|
+
: { effectiveEngine: input.effectiveEngine }),
|
|
961
|
+
});
|
|
944
962
|
return await runWithAutoResumeLoop({
|
|
945
963
|
admitted: loaded.admitted,
|
|
946
964
|
principalAuthority: input.env.principalAuthority,
|
|
@@ -974,7 +992,7 @@ export async function runPostAdmissionSeatResume(input) {
|
|
|
974
992
|
},
|
|
975
993
|
};
|
|
976
994
|
}
|
|
977
|
-
const turnRequest = await buildRequestAfterLease();
|
|
995
|
+
const turnRequest = withEngineDetourInvocationScope(await buildRequestAfterLease(), invocationScopeId);
|
|
978
996
|
firstTurn = turnRequest;
|
|
979
997
|
return turnRequest;
|
|
980
998
|
},
|
|
@@ -1058,6 +1076,12 @@ export async function runPostAdmissionOneShot(input) {
|
|
|
1058
1076
|
export async function runPostAdmissionResumable(input) {
|
|
1059
1077
|
const { admitted, env, io, buildInitialRequest, buildResumeRequest, effectiveEngine } = input;
|
|
1060
1078
|
const adapters = withOnceSuccessfulBeforeDispatch(input.adapters);
|
|
1079
|
+
// One public call → one detour scope across in-place auto-resume dispatches.
|
|
1080
|
+
const invocationScopeId = mintEngineDetourInvocationScope({
|
|
1081
|
+
...(effectiveEngine === undefined ? {} : { effectiveEngine }),
|
|
1082
|
+
});
|
|
1083
|
+
const buildScopedInitial = () => withEngineDetourInvocationScope(buildInitialRequest(), invocationScopeId);
|
|
1084
|
+
const buildScopedResume = () => withEngineDetourInvocationScope(buildResumeRequest(), invocationScopeId);
|
|
1061
1085
|
return runWithAutoResumeLoop({
|
|
1062
1086
|
admitted,
|
|
1063
1087
|
principalAuthority: env.principalAuthority,
|
|
@@ -1065,8 +1089,8 @@ export async function runPostAdmissionResumable(input) {
|
|
|
1065
1089
|
io,
|
|
1066
1090
|
sessionAppender: env.sessionAppender,
|
|
1067
1091
|
autoResumeLimit: env.autoResumeLimit,
|
|
1068
|
-
buildInitialPayload:
|
|
1069
|
-
buildResumePayload:
|
|
1092
|
+
buildInitialPayload: buildScopedInitial,
|
|
1093
|
+
buildResumePayload: buildScopedResume,
|
|
1070
1094
|
dispatch: async (request, lease, _isFirst, attemptIo) => {
|
|
1071
1095
|
const result = await dispatchPostAdmissionTurn({
|
|
1072
1096
|
admitted,
|
|
@@ -1124,6 +1148,10 @@ export async function runPostAdmissionManualResume(input) {
|
|
|
1124
1148
|
}
|
|
1125
1149
|
throw error;
|
|
1126
1150
|
}
|
|
1151
|
+
// Explicit public resume is a new counting unit — mint once for this call.
|
|
1152
|
+
const invocationScopeId = mintEngineDetourInvocationScope({
|
|
1153
|
+
...(effectiveEngine === undefined ? {} : { effectiveEngine }),
|
|
1154
|
+
});
|
|
1127
1155
|
const result = await dispatchAfterWriterLease({
|
|
1128
1156
|
lease,
|
|
1129
1157
|
build: async () => {
|
|
@@ -1133,6 +1161,7 @@ export async function runPostAdmissionManualResume(input) {
|
|
|
1133
1161
|
}
|
|
1134
1162
|
request = await buildRequestAfterLease();
|
|
1135
1163
|
}
|
|
1164
|
+
request = withEngineDetourInvocationScope(request, invocationScopeId);
|
|
1136
1165
|
return request;
|
|
1137
1166
|
},
|
|
1138
1167
|
dispatch: (turnRequest) => dispatchPostAdmissionTurn({
|
|
@@ -21,6 +21,7 @@ import { AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE, AUDITOR_PARENT_ATTEMPT_BINDING_E
|
|
|
21
21
|
// interval binding on historical session bytes). Provider-stop retain authority is Sitian.
|
|
22
22
|
import { COLLECTOR_BIND_TARGET_TOOL, COLLECTOR_OBSERVE_TOOL, COLLECTOR_READ_TOOL, COLLECTOR_REQUEST_TOOL, COLLECTOR_WAIT_TOOL, } from "../collector-ledger.js";
|
|
23
23
|
import { ENGINE_DETOUR_TOOL_NAME } from "../engine-detour.js";
|
|
24
|
+
import { projectEngineDetourToolUsageForPublicTerminal, readEngineDetourToolUsage, readInvocationEngineMounted, runDirectoryFromSessionDirectory, sessionFileFromSessionDirectory, withEngineDetourToolUsageFact, } from "../engine-detour-usage.js";
|
|
24
25
|
import { JUDGE_OUTPUT_TOOL_NAME, } from "../package-contracts/judge-output.js";
|
|
25
26
|
import { COLLECTOR_OUTPUT_TOOL, } from "../package-contracts/collector-output.js";
|
|
26
27
|
import { CODER_OUTPUT_TOOL_NAME, FIXER_OUTPUT_TOOL_NAME, } from "../package-contracts/worker-output.js";
|
|
@@ -127,7 +128,7 @@ export async function attachRecordedSubmissions(admitted, terminal, scope) {
|
|
|
127
128
|
* Host ended with no recorded submission — lawful no_receipt (exit 0).
|
|
128
129
|
* Does not invent an output failure for an empty ledger.
|
|
129
130
|
*/
|
|
130
|
-
export async function settleHostEndedNoReceipt(admitted, authority) {
|
|
131
|
+
export async function settleHostEndedNoReceipt(admitted, authority, scope) {
|
|
131
132
|
const facts = noReceiptLifecycleFacts({
|
|
132
133
|
terminalToolCalled: false,
|
|
133
134
|
rejectedReceipts: [],
|
|
@@ -147,7 +148,7 @@ export async function settleHostEndedNoReceipt(admitted, authority) {
|
|
|
147
148
|
navigator: await extractNavigatorFactFromAdmittedSession(coordinates.sessionFile),
|
|
148
149
|
artifacts: [],
|
|
149
150
|
runId: admitted.runId,
|
|
150
|
-
}, coordinates.sessionDirectory);
|
|
151
|
+
}, coordinates.sessionDirectory, detourGateContext(admitted, scope));
|
|
151
152
|
}
|
|
152
153
|
async function closedLedgerOutcome(admitted, role, scope) {
|
|
153
154
|
return sealedLedgerOutcome(admitted, role, scope);
|
|
@@ -1589,6 +1590,57 @@ export async function extractGateFactFromSessionDirectory(sessionDirectory, opti
|
|
|
1589
1590
|
* or swallowed); callers that already hold a controlled failure still surface the
|
|
1590
1591
|
* JSONL/session cause rather than pretend the gate was absent.
|
|
1591
1592
|
*/
|
|
1593
|
+
/**
|
|
1594
|
+
* #537: project this-invocation ak_engine_detour usage onto decisiveFacts.
|
|
1595
|
+
* Absent when engine is not mounted; callCount 0 when mounted with zero calls.
|
|
1596
|
+
* Never mutates role payloads (ADR 0003 / 0042 / 0052).
|
|
1597
|
+
* Scope is the public-invocation id bound once per ak-role call — never
|
|
1598
|
+
* courtAttemptId and never Pi session.jsonl toolResult join. Session damage
|
|
1599
|
+
* therefore cannot replace an already-formed roleOutcome (no_receipt / failure).
|
|
1600
|
+
*/
|
|
1601
|
+
async function attachEngineDetourToolUsage(base, sessionDirectory, gateContext = {}) {
|
|
1602
|
+
const runDirectory = typeof gateContext.runDirectory === "string" && gateContext.runDirectory.length > 0
|
|
1603
|
+
? gateContext.runDirectory
|
|
1604
|
+
: runDirectoryFromSessionDirectory(sessionDirectory);
|
|
1605
|
+
const engineMounted = await readInvocationEngineMounted(runDirectory);
|
|
1606
|
+
if (!engineMounted)
|
|
1607
|
+
return base;
|
|
1608
|
+
// Public-invocation scope from the shared Host envelope (settlement scope), never
|
|
1609
|
+
// courtAttemptId and never a detour sidecar file.
|
|
1610
|
+
const invocationScopeId = typeof gateContext.invocationScopeId === "string" &&
|
|
1611
|
+
gateContext.invocationScopeId.length > 0
|
|
1612
|
+
? gateContext.invocationScopeId
|
|
1613
|
+
: undefined;
|
|
1614
|
+
const sessionFile = sessionFileFromSessionDirectory(sessionDirectory);
|
|
1615
|
+
const usage = await readEngineDetourToolUsage({
|
|
1616
|
+
sessionParent: sessionFile,
|
|
1617
|
+
engineMounted: true,
|
|
1618
|
+
...(invocationScopeId === undefined ? {} : { invocationScopeId }),
|
|
1619
|
+
cwd: runDirectory,
|
|
1620
|
+
});
|
|
1621
|
+
const projected = usage === undefined
|
|
1622
|
+
? undefined
|
|
1623
|
+
: projectEngineDetourToolUsageForPublicTerminal(usage, {
|
|
1624
|
+
// Resumable Terminal: run ID only in resume.command — relative openable path.
|
|
1625
|
+
discloseRecordFile: base.resume === undefined,
|
|
1626
|
+
});
|
|
1627
|
+
return {
|
|
1628
|
+
...base,
|
|
1629
|
+
roleOutcome: withEngineDetourToolUsageFact(base.roleOutcome, projected),
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
/** Gate + detour projection context from admitted run + settlement scope. */
|
|
1633
|
+
function detourGateContext(admitted, scope) {
|
|
1634
|
+
return {
|
|
1635
|
+
runDirectory: admitted.runDirectory,
|
|
1636
|
+
...(scope?.courtAttemptId === undefined || scope.courtAttemptId.length === 0
|
|
1637
|
+
? {}
|
|
1638
|
+
: { courtAttemptId: scope.courtAttemptId }),
|
|
1639
|
+
...(scope?.invocationScopeId === undefined || scope.invocationScopeId.length === 0
|
|
1640
|
+
? {}
|
|
1641
|
+
: { invocationScopeId: scope.invocationScopeId }),
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1592
1644
|
async function withOptionalGateProjection(base, sessionDirectory, gateContext = {}) {
|
|
1593
1645
|
// A gate transport failure is already represented by typed evidence and has no
|
|
1594
1646
|
// accepted gate cycle to project. Re-reading that rejected receipt as an
|
|
@@ -1596,15 +1648,19 @@ async function withOptionalGateProjection(base, sessionDirectory, gateContext =
|
|
|
1596
1648
|
const secondaryEvidence = base.roleOutcome.kind === "failure"
|
|
1597
1649
|
? base.roleOutcome.decisiveFacts.secondaryEvidence
|
|
1598
1650
|
: undefined;
|
|
1599
|
-
|
|
1651
|
+
const skipGate = isRecord(secondaryEvidence)
|
|
1600
1652
|
&& secondaryEvidence.kind === "role_infrastructure_failure"
|
|
1601
1653
|
&& (secondaryEvidence.stage === "gatekeeper"
|
|
1602
1654
|
|| secondaryEvidence.stage === "inspector"
|
|
1603
|
-
|| secondaryEvidence.stage === "notary")
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1655
|
+
|| secondaryEvidence.stage === "notary");
|
|
1656
|
+
let next = base;
|
|
1657
|
+
if (!skipGate) {
|
|
1658
|
+
// Defaults live solely in extractGateFactFromSessionDirectory — do not re-derive.
|
|
1659
|
+
const gate = await extractGateFactFromSessionDirectory(sessionDirectory, gateContext);
|
|
1660
|
+
if (gate !== undefined)
|
|
1661
|
+
next = { ...base, gate };
|
|
1662
|
+
}
|
|
1663
|
+
return attachEngineDetourToolUsage(next, sessionDirectory, gateContext);
|
|
1608
1664
|
}
|
|
1609
1665
|
export function extractNavigatorFact(entries) {
|
|
1610
1666
|
// Affirmative attendance only. Missing / uncorrelated / unparseable is never no-advice.
|
|
@@ -1814,7 +1870,7 @@ async function settleLawfulJudgeTerminalResult(admitted, authority, scope) {
|
|
|
1814
1870
|
navigator,
|
|
1815
1871
|
artifacts,
|
|
1816
1872
|
runId: admitted.runId,
|
|
1817
|
-
}, coordinates.sessionDirectory);
|
|
1873
|
+
}, coordinates.sessionDirectory, detourGateContext(admitted, scope));
|
|
1818
1874
|
}
|
|
1819
1875
|
/**
|
|
1820
1876
|
* Settle a lawful typed terminal result from the admitted session.
|
|
@@ -1854,7 +1910,7 @@ async function settleLawfulCoderTerminalResult(admitted, authority, options = {}
|
|
|
1854
1910
|
navigator,
|
|
1855
1911
|
artifacts,
|
|
1856
1912
|
runId: admitted.runId,
|
|
1857
|
-
}, coordinates.sessionDirectory);
|
|
1913
|
+
}, coordinates.sessionDirectory, detourGateContext(admitted, scope));
|
|
1858
1914
|
}
|
|
1859
1915
|
/** Settle a lawful Coder Terminal from the admitted session (shared #106 success interface). */
|
|
1860
1916
|
export async function settleCoderTerminalResult(admitted, authority, options = {}, scope) {
|
|
@@ -1974,7 +2030,7 @@ async function settleLawfulFixerTerminalResult(admitted, authority, options, sco
|
|
|
1974
2030
|
navigator,
|
|
1975
2031
|
artifacts,
|
|
1976
2032
|
runId: admitted.runId,
|
|
1977
|
-
}, sessionDirectory);
|
|
2033
|
+
}, sessionDirectory, detourGateContext(admitted, scope));
|
|
1978
2034
|
}
|
|
1979
2035
|
/** Settle a lawful Fixer Terminal from the admitted session (shared #106 success interface). */
|
|
1980
2036
|
export async function settleFixerTerminalResult(admitted, authority, options, scope) {
|
|
@@ -2037,7 +2093,7 @@ async function settleLawfulCollectorTerminalResult(admitted, authority, scope) {
|
|
|
2037
2093
|
cause: "output",
|
|
2038
2094
|
diagnostic: residual.diagnostic,
|
|
2039
2095
|
details,
|
|
2040
|
-
}, authority);
|
|
2096
|
+
}, authority, scope ?? {});
|
|
2041
2097
|
return attachRecordedSubmissions(admitted, failed, scope);
|
|
2042
2098
|
}
|
|
2043
2099
|
return undefined;
|
|
@@ -2050,7 +2106,7 @@ async function settleLawfulCollectorTerminalResult(admitted, authority, scope) {
|
|
|
2050
2106
|
navigator,
|
|
2051
2107
|
artifacts,
|
|
2052
2108
|
runId: admitted.runId,
|
|
2053
|
-
}, sessionDirectory), scope);
|
|
2109
|
+
}, sessionDirectory, detourGateContext(admitted, scope)), scope);
|
|
2054
2110
|
}
|
|
2055
2111
|
/** Settle a lawful Collector Terminal from the admitted session. */
|
|
2056
2112
|
export async function settleCollectorTerminalResult(admitted, authority, scope) {
|
|
@@ -2144,7 +2200,7 @@ async function settleLawfulDoctorTerminalResult(admitted, authority, scope) {
|
|
|
2144
2200
|
navigator: extractNavigatorFact(entries),
|
|
2145
2201
|
artifacts,
|
|
2146
2202
|
runId: admitted.runId,
|
|
2147
|
-
}, sessionDirectory);
|
|
2203
|
+
}, sessionDirectory, detourGateContext(admitted, scope));
|
|
2148
2204
|
}
|
|
2149
2205
|
const roleOutcome = sealed;
|
|
2150
2206
|
const navigator = extractNavigatorFact(entries);
|
|
@@ -2159,7 +2215,7 @@ async function settleLawfulDoctorTerminalResult(admitted, authority, scope) {
|
|
|
2159
2215
|
navigator,
|
|
2160
2216
|
artifacts,
|
|
2161
2217
|
runId: admitted.runId,
|
|
2162
|
-
}, sessionDirectory);
|
|
2218
|
+
}, sessionDirectory, detourGateContext(admitted, scope));
|
|
2163
2219
|
}
|
|
2164
2220
|
/** Settle a lawful Doctor Terminal from the admitted session. */
|
|
2165
2221
|
export async function settleDoctorTerminalResult(admitted, authority, scope) {
|
|
@@ -2203,7 +2259,7 @@ async function settleLawfulSeatAcceptedTerminalResult(admitted, authority, spec,
|
|
|
2203
2259
|
cause: "output",
|
|
2204
2260
|
diagnostic: residual.diagnostic,
|
|
2205
2261
|
details,
|
|
2206
|
-
}, authority);
|
|
2262
|
+
}, authority, scope ?? {});
|
|
2207
2263
|
return withSubmissions(failed, submissions);
|
|
2208
2264
|
}
|
|
2209
2265
|
}
|
|
@@ -2215,7 +2271,7 @@ async function settleLawfulSeatAcceptedTerminalResult(admitted, authority, spec,
|
|
|
2215
2271
|
navigator,
|
|
2216
2272
|
artifacts: [],
|
|
2217
2273
|
runId: admitted.runId,
|
|
2218
|
-
}, sessionDirectory), submissions);
|
|
2274
|
+
}, sessionDirectory, detourGateContext(admitted, scope)), submissions);
|
|
2219
2275
|
}
|
|
2220
2276
|
if (roleOutcome?.role === spec.role) {
|
|
2221
2277
|
const navigator = extractNavigatorFact(entries);
|
|
@@ -2224,7 +2280,7 @@ async function settleLawfulSeatAcceptedTerminalResult(admitted, authority, spec,
|
|
|
2224
2280
|
navigator,
|
|
2225
2281
|
artifacts: [],
|
|
2226
2282
|
runId: admitted.runId,
|
|
2227
|
-
}, sessionDirectory), submissions);
|
|
2283
|
+
}, sessionDirectory, detourGateContext(admitted, scope)), submissions);
|
|
2228
2284
|
}
|
|
2229
2285
|
return undefined;
|
|
2230
2286
|
}
|
|
@@ -2429,7 +2485,7 @@ async function settleLawfulReviewerTerminalResult(admitted, authority, options,
|
|
|
2429
2485
|
navigator,
|
|
2430
2486
|
artifacts,
|
|
2431
2487
|
runId: admitted.runId,
|
|
2432
|
-
}, sessionDirectory);
|
|
2488
|
+
}, sessionDirectory, detourGateContext(admitted, scope));
|
|
2433
2489
|
}
|
|
2434
2490
|
/** Settle a lawful Reviewer Terminal from the admitted session (shared #106 success interface). */
|
|
2435
2491
|
export async function settleReviewerTerminalResult(admitted, authority, options, scope) {
|
|
@@ -2525,7 +2581,7 @@ async function settleLawfulMergerTerminalResult(admitted, authority, options, sc
|
|
|
2525
2581
|
cause: "output",
|
|
2526
2582
|
diagnostic: residual.diagnostic,
|
|
2527
2583
|
details,
|
|
2528
|
-
}, authority);
|
|
2584
|
+
}, authority, scope ?? {});
|
|
2529
2585
|
return attachRecordedSubmissions(admitted, failed, scope);
|
|
2530
2586
|
}
|
|
2531
2587
|
return undefined;
|
|
@@ -2544,7 +2600,7 @@ async function settleLawfulMergerTerminalResult(admitted, authority, options, sc
|
|
|
2544
2600
|
navigator,
|
|
2545
2601
|
artifacts,
|
|
2546
2602
|
runId: admitted.runId,
|
|
2547
|
-
}, sessionDirectory), scope);
|
|
2603
|
+
}, sessionDirectory, detourGateContext(admitted, scope)), scope);
|
|
2548
2604
|
}
|
|
2549
2605
|
/** Settle a lawful Merger Terminal from the admitted session (shared #106 success interface). */
|
|
2550
2606
|
export async function settleMergerTerminalResult(admitted, authority, options, scope) {
|
|
@@ -2760,7 +2816,7 @@ export async function settleFailureTerminalResult(admitted, failure, authority,
|
|
|
2760
2816
|
navigator: await extractNavigatorFactFromAdmittedSession(sessionFile),
|
|
2761
2817
|
artifacts: [],
|
|
2762
2818
|
runId: admitted.runId,
|
|
2763
|
-
}, sessionDirectory);
|
|
2819
|
+
}, sessionDirectory, detourGateContext(admitted, options));
|
|
2764
2820
|
}
|
|
2765
2821
|
}
|
|
2766
2822
|
catch { /* malformed lifecycle bytes remain the existing nonzero output failure */ }
|
|
@@ -2801,7 +2857,7 @@ export async function settleFailureTerminalResult(admitted, failure, authority,
|
|
|
2801
2857
|
navigator,
|
|
2802
2858
|
artifacts: [],
|
|
2803
2859
|
resume: options.resume,
|
|
2804
|
-
}, sessionDirectory);
|
|
2860
|
+
}, sessionDirectory, detourGateContext(admitted, options));
|
|
2805
2861
|
}
|
|
2806
2862
|
const roleOutcome = {
|
|
2807
2863
|
kind: "failure",
|
|
@@ -2816,7 +2872,7 @@ export async function settleFailureTerminalResult(admitted, failure, authority,
|
|
|
2816
2872
|
navigator,
|
|
2817
2873
|
artifacts,
|
|
2818
2874
|
runId: admitted.runId,
|
|
2819
|
-
}, sessionDirectory);
|
|
2875
|
+
}, sessionDirectory, detourGateContext(admitted, options));
|
|
2820
2876
|
}
|
|
2821
2877
|
/** Judge-named alias retained for #107 call sites. */
|
|
2822
2878
|
export async function settleJudgeFailureTerminalResult(admitted, failure, authority, options = {}) {
|
|
@@ -25,6 +25,9 @@ export function projectRoleTurnRequest(admitted, roleDetails, options) {
|
|
|
25
25
|
...(options.courtAttemptId === undefined || options.courtAttemptId.length === 0
|
|
26
26
|
? {}
|
|
27
27
|
: { courtAttemptId: options.courtAttemptId }),
|
|
28
|
+
...(options.invocationScopeId === undefined || options.invocationScopeId.length === 0
|
|
29
|
+
? {}
|
|
30
|
+
: { invocationScopeId: options.invocationScopeId }),
|
|
28
31
|
...(options.stationChild === undefined ? {} : { stationChild: options.stationChild }),
|
|
29
32
|
};
|
|
30
33
|
}
|