@akagilnc/pi-workflow-roles 0.1.5614 → 0.1.5650
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/acp-host/production-host.js +960 -465
- package/dist/activation-ledger-topology.js +12 -0
- package/dist/external-host-turn-loop.js +281 -0
- package/dist/headless-host/production-host.js +900 -406
- package/dist/migrate-book-topology.js +8 -5
- package/dist/pi/role-turn-host.js +33 -3
- package/dist/public-cli/instruction-seat-run.js +177 -46
- package/dist/public-cli/invocation.js +5 -12
- package/dist/public-cli/main.js +541 -165
- package/dist/public-cli/post-admission.js +171 -18
- package/dist/public-cli/settlement.js +20 -10
- package/dist/report-ticket-tool.js +136 -0
- package/dist/role-run-placement.js +1 -2
- package/dist/role-run-relocation.js +24 -4
- package/package.json +1 -1
- package/resources/engine-dispatch.md +6 -3
- package/resources/engines/claude-code.md +4 -5
- package/resources/missing-ticket-reask.md +1 -0
- package/src/acp-host/role-turn-host.ts +41 -31
- package/src/activation-ledger-topology.ts +16 -0
- package/src/external-host-turn-loop.ts +103 -12
- package/src/headless-host/role-turn-host.ts +41 -25
- package/src/pi/role-turn-host.ts +37 -3
- package/src/public-cli/instruction-seat-run.ts +248 -50
- package/src/public-cli/invocation.ts +15 -27
- package/src/public-cli/post-admission.ts +250 -33
- package/src/public-cli/run-lifecycle.ts +5 -5
- package/src/public-cli/settlement.ts +38 -11
- package/src/public-cli/terminal.ts +6 -0
- package/src/report-ticket-tool.ts +176 -0
- package/src/role-envelope.ts +28 -3
- package/src/role-run-placement.ts +0 -2
- package/src/role-run-relocation.ts +45 -4
- package/src/role-runtime.ts +6 -0
- package/src/worker-submission-gates.ts +26 -3
|
@@ -6,6 +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 { existsSync } from "node:fs";
|
|
9
10
|
import { writeFile } from "node:fs/promises";
|
|
10
11
|
|
|
11
12
|
import {
|
|
@@ -24,14 +25,15 @@ import {
|
|
|
24
25
|
relocateAdmittedRunToTicket,
|
|
25
26
|
} from "./invocation.ts";
|
|
26
27
|
import { readRecordedSubmissionRows } from "../submission-ledger.ts";
|
|
27
|
-
import { parseTicketNumber, readDeclaredTicketNumber } from "../run-ticket-number.ts";
|
|
28
|
+
import { parseTicketNumber, readBoardTicketNumber, readDeclaredTicketNumber } from "../run-ticket-number.ts";
|
|
29
|
+
import { homeFromRunDirectory } from "../activation-ledger-topology.ts";
|
|
28
30
|
import { readStoredHostSessionId } from "../session-identity.ts";
|
|
29
31
|
import { reportRunRecord } from "../sitian-facade.ts";
|
|
30
32
|
import {
|
|
31
33
|
createReceiptDeliveryPolicy,
|
|
32
34
|
deliveryLimitFromConfig,
|
|
33
35
|
} from "../receipt-delivery-policy.ts";
|
|
34
|
-
import { rewriteRunDirectoryPathValue } from "../role-run-relocation.ts";
|
|
36
|
+
import { rewritePrincipalSessionPaths, rewriteRunDirectoryPathValue, rewriteAdmittedRoleRunPage } from "../role-run-relocation.ts";
|
|
35
37
|
|
|
36
38
|
import type {
|
|
37
39
|
ControlledFailureCause,
|
|
@@ -145,6 +147,148 @@ function projectRelocatedTurnIdentity(
|
|
|
145
147
|
}
|
|
146
148
|
}
|
|
147
149
|
|
|
150
|
+
/**
|
|
151
|
+
* #1171: after the host returns, re-locate the leg by run id (report-ticket may
|
|
152
|
+
* have moved it mid-turn in the tool process). Settlement and render use the
|
|
153
|
+
* live path; lease cleanup follows when the held path changed.
|
|
154
|
+
* Failure is not best-effort: a missing former path with no replacement must not
|
|
155
|
+
* settle or write back under the vanished unbound leaf (失败诚实宪法).
|
|
156
|
+
*/
|
|
157
|
+
export async function refreshAdmittedPlacementAfterHostTurn(
|
|
158
|
+
admitted: AdmittedRoleInvocation,
|
|
159
|
+
authority: DurablePrincipalAuthority,
|
|
160
|
+
heldLease?: { relocate(runDirectory: string): void },
|
|
161
|
+
): Promise<RunDirectoryRelocation | undefined> {
|
|
162
|
+
const home = homeFromRunDirectory(admitted.runDirectory);
|
|
163
|
+
const found = await findRunDirectoryById(home, admitted.runId, admitted.bookKey, admitted.role);
|
|
164
|
+
if (found === undefined && !existsSync(admitted.runDirectory)) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
`admitted run ${admitted.runId} missing after host turn; not found under ${home}`,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
// Live identity first: later board-ticket read must not leave settlement on the
|
|
170
|
+
// pre-relocate path when facts are damaged (#1171 F4-R1).
|
|
171
|
+
let relocation: RunDirectoryRelocation | undefined;
|
|
172
|
+
if (found !== undefined && found !== admitted.runDirectory) {
|
|
173
|
+
const oldRunDirectory = admitted.runDirectory;
|
|
174
|
+
const coords = { ...authority.decode(admitted.principal) };
|
|
175
|
+
rewritePrincipalSessionPaths(
|
|
176
|
+
coords as Record<string, unknown>,
|
|
177
|
+
oldRunDirectory,
|
|
178
|
+
found,
|
|
179
|
+
);
|
|
180
|
+
const principal = authority.seal(coords);
|
|
181
|
+
rewriteAdmittedRoleRunPage(
|
|
182
|
+
admitted as unknown as Record<string, unknown>,
|
|
183
|
+
[{ oldRunDirectory, newRunDirectory: found }],
|
|
184
|
+
);
|
|
185
|
+
(admitted as { principal: typeof principal }).principal = principal;
|
|
186
|
+
heldLease?.relocate(found);
|
|
187
|
+
relocation = { oldRunDirectory, newRunDirectory: found };
|
|
188
|
+
}
|
|
189
|
+
// Board re-read discovers a ticket written mid-turn while still unbound
|
|
190
|
+
// (report-ticket board page). Already-bound identity needs no discovery —
|
|
191
|
+
// path relocate already used findRunDirectoryById. This is not damage
|
|
192
|
+
// recovery: when unbound, readBoardTicketNumber damage/non-ENOENT IO still
|
|
193
|
+
// propagates (失败诚实; notary: no damaged-control-plane → no-ticket wash).
|
|
194
|
+
if (admitted.ticketNumber === undefined) {
|
|
195
|
+
const boardTicket = await readBoardTicketNumber(found ?? admitted.runDirectory);
|
|
196
|
+
if (boardTicket !== undefined) {
|
|
197
|
+
(admitted as { ticketNumber?: number }).ticketNumber = boardTicket;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return relocation;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Apply a successful post-host placement refresh onto the live turn request. */
|
|
204
|
+
function applyRefreshedPlacementToTurn(
|
|
205
|
+
request: RoleTurnRequest,
|
|
206
|
+
turnRequest: RoleTurnRequest,
|
|
207
|
+
admitted: AdmittedRoleInvocation,
|
|
208
|
+
refreshed: RunDirectoryRelocation,
|
|
209
|
+
): RoleTurnRequest {
|
|
210
|
+
projectRelocatedTurnIdentity(request, {}, admitted, refreshed);
|
|
211
|
+
return {
|
|
212
|
+
...turnRequest,
|
|
213
|
+
runDirectory: admitted.runDirectory,
|
|
214
|
+
principal: admitted.principal,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* #1171: host throw after a started turn — one live placement refresh, then the
|
|
220
|
+
* one settlement authority (ADR 0080). Shared by first turn and 催交; never
|
|
221
|
+
* settle under a vanished unbound leaf.
|
|
222
|
+
*/
|
|
223
|
+
async function settleHostThrowAfterLivePlacementRefresh<
|
|
224
|
+
A extends AdmittedRoleInvocation,
|
|
225
|
+
T extends TerminalResult = TerminalResult,
|
|
226
|
+
>(input: {
|
|
227
|
+
admitted: A;
|
|
228
|
+
hostError: unknown;
|
|
229
|
+
/** False only when executeTurn itself threw; post-success refresh share this catch. */
|
|
230
|
+
hostTurnCompleted: boolean;
|
|
231
|
+
request: RoleTurnRequest;
|
|
232
|
+
turnRequest: RoleTurnRequest;
|
|
233
|
+
lease: RunWriterLease | undefined;
|
|
234
|
+
adapters: PostAdmissionAdapters<A, T>;
|
|
235
|
+
env: PostAdmissionEnv;
|
|
236
|
+
io: CliIo;
|
|
237
|
+
persistRunState: boolean;
|
|
238
|
+
notePackageFault?: (diagnostic: string) => void | Promise<void>;
|
|
239
|
+
courtAttemptId?: string;
|
|
240
|
+
}): Promise<{
|
|
241
|
+
settled: { exitCode: number; admitted: A; terminal: T };
|
|
242
|
+
turnRequest: RoleTurnRequest;
|
|
243
|
+
}> {
|
|
244
|
+
let refreshError: unknown;
|
|
245
|
+
let turnRequest = input.turnRequest;
|
|
246
|
+
try {
|
|
247
|
+
const refreshed = await refreshAdmittedPlacementAfterHostTurn(
|
|
248
|
+
input.admitted,
|
|
249
|
+
input.env.principalAuthority,
|
|
250
|
+
input.lease,
|
|
251
|
+
);
|
|
252
|
+
if (refreshed !== undefined) {
|
|
253
|
+
turnRequest = applyRefreshedPlacementToTurn(
|
|
254
|
+
input.request,
|
|
255
|
+
turnRequest,
|
|
256
|
+
input.admitted,
|
|
257
|
+
refreshed,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
} catch (err) {
|
|
261
|
+
refreshError = err;
|
|
262
|
+
}
|
|
263
|
+
const processCancelName = processCancelSignalName(input.env.signal);
|
|
264
|
+
// Secondary hostThrow only when the host actually threw and refresh also failed.
|
|
265
|
+
// Package-side refresh failure must not borrow that label (#1171 F10).
|
|
266
|
+
const hostThrowSecondary =
|
|
267
|
+
refreshError !== undefined && input.hostTurnCompleted === false
|
|
268
|
+
? { knownDetails: { hostThrow: describeCaughtError(input.hostError) } }
|
|
269
|
+
: {};
|
|
270
|
+
const settled = await settleAfterTurnStarted(
|
|
271
|
+
input.admitted,
|
|
272
|
+
withEngineDetourInvocationScope({
|
|
273
|
+
timedOut: false,
|
|
274
|
+
code: null,
|
|
275
|
+
stderr: "",
|
|
276
|
+
// Prefer the refresh failure when the live path is gone; otherwise the
|
|
277
|
+
// host throw remains the cause (失败诚实:不在旧 unbound 上结算写回).
|
|
278
|
+
...(processCancelName === undefined ? {} : { cancelName: processCancelName }),
|
|
279
|
+
thrown: refreshError ?? input.hostError,
|
|
280
|
+
...hostThrowSecondary,
|
|
281
|
+
}, input.request.invocationScopeId),
|
|
282
|
+
input.adapters,
|
|
283
|
+
input.env.principalAuthority,
|
|
284
|
+
input.io,
|
|
285
|
+
input.persistRunState,
|
|
286
|
+
input.notePackageFault,
|
|
287
|
+
input.courtAttemptId,
|
|
288
|
+
);
|
|
289
|
+
return { settled, turnRequest };
|
|
290
|
+
}
|
|
291
|
+
|
|
148
292
|
/** #855: process-cancel settlement never re-enters auto-resume. */
|
|
149
293
|
function withProcessCancelSkipAutoResume<T extends object>(
|
|
150
294
|
result: T,
|
|
@@ -307,6 +451,23 @@ export type PostAdmissionEnv = {
|
|
|
307
451
|
* keeps this env's autoResumeLimit; the counter is the only thing that moves.
|
|
308
452
|
*/
|
|
309
453
|
unreadableReasksSpent?: number;
|
|
454
|
+
/**
|
|
455
|
+
* #1171: missing-ticket soft reasks already issued on this chain. Hard ceiling
|
|
456
|
+
* is one — still no ticket after that stays unbound with the sealed receipt.
|
|
457
|
+
* Budget only across rounds; not the current turn's settlement identity.
|
|
458
|
+
*/
|
|
459
|
+
ticketReasksSpent?: number;
|
|
460
|
+
/**
|
|
461
|
+
* #1171 F2-R5: this turn is the one soft reask. Keep the sealed terminal face
|
|
462
|
+
* when it ends without a substitute seal. Not carried into audit continue.
|
|
463
|
+
*/
|
|
464
|
+
softTicketReaskTurn?: true;
|
|
465
|
+
/**
|
|
466
|
+
* #1171 F2-R7: live ownership cell for this soft-reask nested chain.
|
|
467
|
+
* Created at soft-reask dispatch; settlement of that turn sets sealedSubstitute.
|
|
468
|
+
* Shared ref survives env spreads — not a second ledger.
|
|
469
|
+
*/
|
|
470
|
+
softTicketReaskOwnership?: { sealedSubstitute: boolean };
|
|
310
471
|
createRunId?: () => string;
|
|
311
472
|
/**
|
|
312
473
|
* Parent cancellation for a nested public summon (#675). Every dispatched turn
|
|
@@ -1023,6 +1184,8 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1023
1184
|
const finishAfterTurn = async (result: DispatchOutcomeFor<A, T>): Promise<DispatchOutcomeFor<A, T>> => {
|
|
1024
1185
|
if (afterDispatchApplied) return result;
|
|
1025
1186
|
afterDispatchApplied = true;
|
|
1187
|
+
// #1171: placement refresh runs once after each host return (below), before
|
|
1188
|
+
// settlement — not again here. finishAfterTurn only binds/relocates sealed tickets.
|
|
1026
1189
|
// #855: re-read cancel before committing the terminal and afterDispatch.
|
|
1027
1190
|
// A signal after the history-first commit cannot retroactively erase it.
|
|
1028
1191
|
// skipRunStateWrite: run-state
|
|
@@ -1069,15 +1232,35 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1069
1232
|
result.terminal === pendingTerminal
|
|
1070
1233
|
) {
|
|
1071
1234
|
try {
|
|
1235
|
+
// #1171 F2-R2 / F2-R3 (#419) / F2-R5: only the soft-reask turn itself
|
|
1236
|
+
// preserves the sealed terminal face. ticketReasksSpent is budget across
|
|
1237
|
+
// rounds — after audit continue, ordinary no-volume turns publish
|
|
1238
|
+
// no_receipt honestly. previewOnly skips the terminal pointer;
|
|
1239
|
+
// recordAttemptHistory still appends.
|
|
1240
|
+
const softTicketReaskNoSeal =
|
|
1241
|
+
pendingSettlement !== "sealed" && env.softTicketReaskTurn === true;
|
|
1242
|
+
const noSealScope = softTicketReaskNoSeal
|
|
1243
|
+
? {
|
|
1244
|
+
...courtScope,
|
|
1245
|
+
previewOnly: true as const,
|
|
1246
|
+
recordAttemptHistory: true as const,
|
|
1247
|
+
}
|
|
1248
|
+
: { ...courtScope, recordAttemptHistory: true as const };
|
|
1072
1249
|
const terminal = pendingSettlement === "sealed"
|
|
1073
1250
|
? await adapters.trySettle(admitted, env.principalAuthority,
|
|
1074
1251
|
{ ...courtScope, recordAttemptHistory: true })
|
|
1075
1252
|
: await attachRecordedSubmissions(admitted,
|
|
1076
1253
|
await settleHostEndedNoReceipt(admitted, env.principalAuthority,
|
|
1077
|
-
|
|
1078
|
-
|
|
1254
|
+
noSealScope, receiptDelivery.issuedDeliveryRequests()) as T,
|
|
1255
|
+
noSealScope);
|
|
1079
1256
|
if (terminal === undefined) throw new Error("settled host attempt vanished before publication");
|
|
1080
1257
|
result = { ...result, terminal, exitCode: exitCodeForTerminalOutcome(terminal.roleOutcome) };
|
|
1258
|
+
// #1171 F2-R7: ownership evidence at this soft-reask turn's seal —
|
|
1259
|
+
// mutate the live ownership cell carried on env (survives spreads).
|
|
1260
|
+
if (pendingSettlement === "sealed" && env.softTicketReaskTurn === true
|
|
1261
|
+
&& env.softTicketReaskOwnership !== undefined) {
|
|
1262
|
+
env.softTicketReaskOwnership.sealedSubstitute = true;
|
|
1263
|
+
}
|
|
1081
1264
|
if (pendingSettlement === "sealed" && terminal.roleOutcome.kind === "accepted"
|
|
1082
1265
|
&& request.courtAttemptId !== undefined && request.courtAttemptId.length > 0) {
|
|
1083
1266
|
try {
|
|
@@ -1223,6 +1406,7 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1223
1406
|
// auto-resume re-enters this dispatch and must reuse the same scope.
|
|
1224
1407
|
|
|
1225
1408
|
let result: RoleTurnResult;
|
|
1409
|
+
let hostTurnCompleted = false;
|
|
1226
1410
|
try {
|
|
1227
1411
|
// A bare `ak-role resume` only forwards the caller's instruction to the
|
|
1228
1412
|
// host CLI's native resume. Nothing here may gate, redirect or reshape
|
|
@@ -1230,29 +1414,41 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1230
1414
|
// the authoritative post-turn settlement reads whatever really happened.
|
|
1231
1415
|
turnDispatched = true;
|
|
1232
1416
|
result = await env.roleTurnHost.executeTurn(turnRequest);
|
|
1417
|
+
hostTurnCompleted = true;
|
|
1418
|
+
// #1171: one placement refresh before settlement, same try as executeTurn.
|
|
1419
|
+
// Failure shares the catch below (refresh-then-settle + #855 cancel wrap).
|
|
1420
|
+
const refreshed = await refreshAdmittedPlacementAfterHostTurn(
|
|
1421
|
+
admitted,
|
|
1422
|
+
env.principalAuthority,
|
|
1423
|
+
lease,
|
|
1424
|
+
);
|
|
1425
|
+
if (refreshed !== undefined) {
|
|
1426
|
+
turnRequest = applyRefreshedPlacementToTurn(request, turnRequest, admitted, refreshed);
|
|
1427
|
+
}
|
|
1233
1428
|
} catch (error) {
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
thrown: error,
|
|
1245
|
-
}, request.invocationScopeId),
|
|
1429
|
+
// #1171: one placement refresh before settlement — failure is a real cause,
|
|
1430
|
+
// never catch-and-continue onto a vanished unbound leaf.
|
|
1431
|
+
// Host throw and post-success refresh throw share this catch (#1171 F9).
|
|
1432
|
+
const thrown = await settleHostThrowAfterLivePlacementRefresh({
|
|
1433
|
+
admitted,
|
|
1434
|
+
hostError: error,
|
|
1435
|
+
hostTurnCompleted,
|
|
1436
|
+
request,
|
|
1437
|
+
turnRequest,
|
|
1438
|
+
lease,
|
|
1246
1439
|
adapters,
|
|
1247
|
-
env
|
|
1440
|
+
env,
|
|
1248
1441
|
io,
|
|
1249
1442
|
persistRunState,
|
|
1250
|
-
courtScope.notePackageFault,
|
|
1251
|
-
courtScope.courtAttemptId
|
|
1252
|
-
|
|
1443
|
+
notePackageFault: courtScope.notePackageFault,
|
|
1444
|
+
...(courtScope.courtAttemptId === undefined
|
|
1445
|
+
? {}
|
|
1446
|
+
: { courtAttemptId: courtScope.courtAttemptId }),
|
|
1447
|
+
});
|
|
1448
|
+
turnRequest = thrown.turnRequest;
|
|
1253
1449
|
return await finishAfterTurn(
|
|
1254
1450
|
withProcessCancelSkipAutoResume(
|
|
1255
|
-
{ ...settled, turnDispatched: true as const, ...deferredPersist },
|
|
1451
|
+
{ ...thrown.settled, turnDispatched: true as const, ...deferredPersist },
|
|
1256
1452
|
env.signal,
|
|
1257
1453
|
),
|
|
1258
1454
|
);
|
|
@@ -1289,6 +1485,7 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1289
1485
|
// that never reached the host.
|
|
1290
1486
|
let deliveryResult: RoleTurnResult;
|
|
1291
1487
|
let deliveryTurnRequest: RoleTurnRequest;
|
|
1488
|
+
let deliveryHostTurnCompleted = false;
|
|
1292
1489
|
try {
|
|
1293
1490
|
// Host and the other axes already selected on this turn ride the
|
|
1294
1491
|
// shared projection. The pre-projection request does not have them.
|
|
@@ -1306,28 +1503,48 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1306
1503
|
};
|
|
1307
1504
|
receiptDelivery.recordDeliveryRequest();
|
|
1308
1505
|
deliveryResult = await env.roleTurnHost.executeTurn(deliveryTurnRequest);
|
|
1506
|
+
deliveryHostTurnCompleted = true;
|
|
1507
|
+
// Same single refresh-before-settle rule as the first turn (#1171):
|
|
1508
|
+
// stay inside this try so cancel wrap is not dropped (#1171 F9 / #855).
|
|
1509
|
+
const refreshed = await refreshAdmittedPlacementAfterHostTurn(
|
|
1510
|
+
admitted,
|
|
1511
|
+
env.principalAuthority,
|
|
1512
|
+
lease,
|
|
1513
|
+
);
|
|
1514
|
+
if (refreshed !== undefined) {
|
|
1515
|
+
turnRequest = applyRefreshedPlacementToTurn(request, turnRequest, admitted, refreshed);
|
|
1516
|
+
deliveryTurnRequest = {
|
|
1517
|
+
...deliveryTurnRequest,
|
|
1518
|
+
runDirectory: admitted.runDirectory,
|
|
1519
|
+
principal: admitted.principal,
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1309
1522
|
} catch (error) {
|
|
1310
1523
|
// 催交 cannot substitute for host failure recovery: a real failure with
|
|
1311
1524
|
// its true cause, through the one settlement authority (ADR 0080).
|
|
1312
|
-
//
|
|
1313
|
-
//
|
|
1314
|
-
const
|
|
1525
|
+
// Same refresh-before-settle seam as the first turn (#1171 F4-R1).
|
|
1526
|
+
// Host throw and post-success refresh throw share this catch (#1171 F9).
|
|
1527
|
+
const thrown = await settleHostThrowAfterLivePlacementRefresh({
|
|
1315
1528
|
admitted,
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1529
|
+
hostError: error,
|
|
1530
|
+
hostTurnCompleted: deliveryHostTurnCompleted,
|
|
1531
|
+
request,
|
|
1532
|
+
turnRequest,
|
|
1533
|
+
lease,
|
|
1320
1534
|
adapters,
|
|
1321
|
-
env
|
|
1535
|
+
env,
|
|
1322
1536
|
io,
|
|
1323
1537
|
persistRunState,
|
|
1324
|
-
courtScope.notePackageFault,
|
|
1325
|
-
courtScope.courtAttemptId
|
|
1326
|
-
|
|
1538
|
+
notePackageFault: courtScope.notePackageFault,
|
|
1539
|
+
...(courtScope.courtAttemptId === undefined
|
|
1540
|
+
? {}
|
|
1541
|
+
: { courtAttemptId: courtScope.courtAttemptId }),
|
|
1542
|
+
});
|
|
1543
|
+
turnRequest = thrown.turnRequest;
|
|
1327
1544
|
return await finishAfterTurn(
|
|
1328
1545
|
withProcessCancelSkipAutoResume(
|
|
1329
1546
|
{
|
|
1330
|
-
...settled,
|
|
1547
|
+
...thrown.settled,
|
|
1331
1548
|
turnDispatched: true as const,
|
|
1332
1549
|
...deferredPersist,
|
|
1333
1550
|
},
|
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
type CoderPhase,
|
|
54
54
|
type DerivedMergerEnvelope,
|
|
55
55
|
normalizeAdmittedAttachment,
|
|
56
|
-
type
|
|
56
|
+
type AdmittedAttachment,
|
|
57
57
|
type InvocationEffectiveModel,
|
|
58
58
|
type ReviewerLens,
|
|
59
59
|
} from "./invocation.ts";
|
|
@@ -118,7 +118,7 @@ export type CurrentCourtState = {
|
|
|
118
118
|
|
|
119
119
|
/**
|
|
120
120
|
* Materials delivered on same-ticket re-summons while reusing the same-run resume seam.
|
|
121
|
-
* Instruction seats
|
|
121
|
+
* Instruction seats pass caller file-flag paths through the transport prompt (ADR 0087);
|
|
122
122
|
* notary overrides the source-run activation pointer for this turn only.
|
|
123
123
|
*/
|
|
124
124
|
export type SameTicketSummonsMaterials = {
|
|
@@ -974,7 +974,7 @@ export async function findLatestRunIdForSeatTicket(input: {
|
|
|
974
974
|
type LoadedAdmittedRequestFields = {
|
|
975
975
|
readonly instruction: string;
|
|
976
976
|
readonly instructionEmpty: boolean;
|
|
977
|
-
readonly attachments:
|
|
977
|
+
readonly attachments: AdmittedAttachment[];
|
|
978
978
|
readonly phase?: CoderPhase | FixerPhase;
|
|
979
979
|
readonly prerequisitesPath?: string;
|
|
980
980
|
readonly baseRevision?: string;
|
|
@@ -1060,7 +1060,7 @@ async function loadResumableRunRecord(
|
|
|
1060
1060
|
// Reconstruct admitted identity from durable run record + the admitted section.
|
|
1061
1061
|
let instruction = "";
|
|
1062
1062
|
let instructionEmpty = true;
|
|
1063
|
-
let attachments:
|
|
1063
|
+
let attachments: AdmittedAttachment[] = [];
|
|
1064
1064
|
let phase: CoderPhase | FixerPhase | undefined;
|
|
1065
1065
|
let prerequisitesPath: string | undefined;
|
|
1066
1066
|
let baseRevision: string | undefined;
|
|
@@ -1101,7 +1101,7 @@ async function loadResumableRunRecord(
|
|
|
1101
1101
|
if (Array.isArray(record.attachments)) {
|
|
1102
1102
|
attachments = record.attachments
|
|
1103
1103
|
.map((item) => normalizeAdmittedAttachment(item))
|
|
1104
|
-
.filter((item): item is
|
|
1104
|
+
.filter((item): item is AdmittedAttachment => item !== undefined);
|
|
1105
1105
|
}
|
|
1106
1106
|
if (record.phase === "plan" || record.phase === "apply") {
|
|
1107
1107
|
phase = record.phase;
|
|
@@ -189,6 +189,12 @@ export function ledgerReadScope(
|
|
|
189
189
|
};
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
type SealedLedgerSettle = {
|
|
193
|
+
readonly outcome: Extract<TerminalRoleOutcome, { kind: "accepted" | "audit_escalation" }>;
|
|
194
|
+
/** Tool-call identity of the latest this-settle terminal row (#1171 F2-R8). */
|
|
195
|
+
readonly submissionToolCallId?: string;
|
|
196
|
+
};
|
|
197
|
+
|
|
192
198
|
function roleOutcomeFromRows(
|
|
193
199
|
role: TerminalRoleName,
|
|
194
200
|
rows: readonly {
|
|
@@ -197,8 +203,9 @@ function roleOutcomeFromRows(
|
|
|
197
203
|
readonly accepted: unknown;
|
|
198
204
|
readonly auditReceipt?: unknown;
|
|
199
205
|
readonly auditOfficer?: unknown;
|
|
206
|
+
readonly toolCallId?: string;
|
|
200
207
|
}[],
|
|
201
|
-
):
|
|
208
|
+
): SealedLedgerSettle | undefined {
|
|
202
209
|
const mine = rows.filter((row) => row.role === role);
|
|
203
210
|
// Terminal acceptance kind still only follows sealed / audit-escalation (#881):
|
|
204
211
|
// correctable-rejection / infrastructure / candidate stay payloads, not acceptance.
|
|
@@ -211,23 +218,32 @@ function roleOutcomeFromRows(
|
|
|
211
218
|
// keep that earlier receipt (#1057).
|
|
212
219
|
const payloads = mine.map((row) => row.accepted);
|
|
213
220
|
const latest = terminal[terminal.length - 1]!;
|
|
221
|
+
const submissionToolCallId =
|
|
222
|
+
typeof latest.toolCallId === "string" && latest.toolCallId.length > 0
|
|
223
|
+
? latest.toolCallId
|
|
224
|
+
: undefined;
|
|
225
|
+
const belonging =
|
|
226
|
+
submissionToolCallId === undefined ? {} : { submissionToolCallId };
|
|
214
227
|
if (latest.kind === "audit-escalation") {
|
|
215
228
|
return {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
229
|
+
outcome: {
|
|
230
|
+
kind: "audit_escalation", role, status: "audit_escalation", payloads,
|
|
231
|
+
...(Object.hasOwn(latest, "auditReceipt") ? { decisiveFacts: {
|
|
232
|
+
auditEscalationReceipt: latest.auditReceipt,
|
|
233
|
+
...(Object.hasOwn(latest, "auditOfficer") ? { auditEscalationOfficer: latest.auditOfficer } : {}),
|
|
234
|
+
} } : {}),
|
|
235
|
+
},
|
|
236
|
+
...belonging,
|
|
221
237
|
};
|
|
222
238
|
}
|
|
223
|
-
return { kind: "accepted", role, payloads };
|
|
239
|
+
return { outcome: { kind: "accepted", role, payloads }, ...belonging };
|
|
224
240
|
}
|
|
225
241
|
|
|
226
242
|
async function sealedLedgerOutcome(
|
|
227
243
|
admitted: AdmittedRoleInvocation,
|
|
228
244
|
role: TerminalRoleName,
|
|
229
245
|
scope?: SettlementCourtScope,
|
|
230
|
-
): Promise<
|
|
246
|
+
): Promise<SealedLedgerSettle | undefined> {
|
|
231
247
|
const home = sealedLedgerHome(admitted);
|
|
232
248
|
// #879: when court scope is present, roleOutcome is this-court original only —
|
|
233
249
|
// never last-wins over an undivided historical array, and never falls back to
|
|
@@ -1450,8 +1466,8 @@ async function settleSealedSeat(
|
|
|
1450
1466
|
},
|
|
1451
1467
|
): Promise<TerminalResult | undefined> {
|
|
1452
1468
|
const coordinates = coordinatesFromAdmitted(authority, admitted);
|
|
1453
|
-
const
|
|
1454
|
-
if (
|
|
1469
|
+
const settled = await sealedLedgerOutcome(admitted, admitted.role, scope);
|
|
1470
|
+
if (settled === undefined || (options.acceptedOnly && settled.outcome.kind !== "accepted")) {
|
|
1455
1471
|
return undefined;
|
|
1456
1472
|
}
|
|
1457
1473
|
const read = await readLawfulSettlementEntries(coordinates.sessionFile);
|
|
@@ -1462,7 +1478,14 @@ async function settleSealedSeat(
|
|
|
1462
1478
|
`session read failed beside lawful terminal: ${describeErrorIdentity(read.error)}`,
|
|
1463
1479
|
);
|
|
1464
1480
|
}
|
|
1465
|
-
return finishLawfulSeat(
|
|
1481
|
+
return finishLawfulSeat(
|
|
1482
|
+
admitted,
|
|
1483
|
+
coordinates,
|
|
1484
|
+
entriesOf(read),
|
|
1485
|
+
settled.outcome,
|
|
1486
|
+
scope,
|
|
1487
|
+
settled.submissionToolCallId,
|
|
1488
|
+
);
|
|
1466
1489
|
}
|
|
1467
1490
|
|
|
1468
1491
|
async function finishLawfulSeat(
|
|
@@ -1471,6 +1494,7 @@ async function finishLawfulSeat(
|
|
|
1471
1494
|
entries: readonly SessionEntry[],
|
|
1472
1495
|
roleOutcome: TerminalRoleOutcome,
|
|
1473
1496
|
scope: SettlementCourtScope | undefined,
|
|
1497
|
+
submissionToolCallId?: string,
|
|
1474
1498
|
): Promise<TerminalResult> {
|
|
1475
1499
|
const artifacts = scope?.previewOnly === true
|
|
1476
1500
|
? []
|
|
@@ -1480,6 +1504,9 @@ async function finishLawfulSeat(
|
|
|
1480
1504
|
navigator: extractNavigatorFact(entries),
|
|
1481
1505
|
artifacts,
|
|
1482
1506
|
runId: admitted.runId,
|
|
1507
|
+
...(submissionToolCallId === undefined || submissionToolCallId.length === 0
|
|
1508
|
+
? {}
|
|
1509
|
+
: { submissionToolCallId }),
|
|
1483
1510
|
}, coordinates.sessionDirectory, detourUsageContext(admitted, scope));
|
|
1484
1511
|
return attachRecordedSubmissions(admitted, terminal, scope);
|
|
1485
1512
|
}
|
|
@@ -139,6 +139,12 @@ export type TerminalResult = {
|
|
|
139
139
|
* (#953 — not copied onto failure.payloads).
|
|
140
140
|
*/
|
|
141
141
|
submissions?: readonly unknown[];
|
|
142
|
+
/**
|
|
143
|
+
* Tool-call identity of the sealed submission this terminal settled from
|
|
144
|
+
* (#1171 F2-R8). Carried from court/settlement so audit consumers bind the
|
|
145
|
+
* object in hand — never re-infer from the whole-leg latest accepted row.
|
|
146
|
+
*/
|
|
147
|
+
submissionToolCallId?: string;
|
|
142
148
|
/** Call-local auto-resume observation (0..2) for this single LLM call; read-only, not persisted. */
|
|
143
149
|
autoResumeCount?: number;
|
|
144
150
|
} & (
|