@akagilnc/pi-workflow-roles 0.1.5478 → 0.1.5504
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 +4945 -4980
- package/dist/analyst-gate-cycles-read.js +11 -29
- package/dist/analyst-ledger.js +10 -12
- package/dist/archivist-record-entry.js +4 -3
- package/dist/archivist-record-pointer.js +59 -32
- package/dist/atomic-write.js +2 -2
- package/dist/auditor-dossier-tool.js +8 -4
- package/dist/auditor-soul.js +2 -1
- package/dist/collector-config.js +6 -5
- package/dist/doctor-evidence.js +5 -1
- package/dist/engine-detour-usage.js +4 -10
- package/dist/headless-host/description.js +251 -0
- package/dist/headless-host/production-host.js +2787 -2658
- package/dist/ledger-session-read.js +5 -0
- package/dist/migrate-book-topology.js +129 -100
- package/dist/navigator-public-session.js +13 -17
- package/dist/notary-source-run.js +7 -15
- package/dist/packaged-role-registry.js +0 -12
- package/dist/pi/durable-principal.js +2 -3
- package/dist/pi/role-turn-host.js +27 -13
- package/dist/public-cli/instruction-seat-run.js +4 -4
- package/dist/public-cli/invocation.js +17 -45
- package/dist/public-cli/main.js +1074 -1085
- package/dist/public-cli/run-lifecycle.js +17 -47
- package/dist/public-cli/settlement.js +28 -128
- package/dist/public-cli/terminal.js +0 -12
- package/dist/role-run-placement.js +52 -5
- package/dist/role-run-relocation.js +39 -2
- package/dist/run-terminal-artifacts.js +2 -6
- package/dist/run-ticket-number.js +33 -4
- package/dist/session-assistant-usage.js +9 -18
- package/dist/session-identity.js +3 -4
- package/dist/sitian-appender.js +20 -6
- package/dist/submission-ledger.js +4 -4
- package/dist/ticket-provenance-contracts.js +2 -8
- package/dist/ticket-provenance.js +5 -4
- package/package.json +1 -1
- package/src/acp-host/role-turn-host.ts +2 -2
- package/src/analyst-gate-cycles-read.ts +11 -34
- package/src/analyst-ledger.ts +14 -14
- package/src/archivist-record-entry.ts +4 -3
- package/src/archivist-record-pointer.ts +70 -34
- package/src/atomic-write.ts +3 -1
- package/src/auditor-dossier-tool.ts +8 -4
- package/src/auditor-soul.ts +2 -1
- package/src/collector-config.ts +5 -3
- package/src/doctor-evidence.ts +5 -1
- package/src/engine-detour-usage.ts +10 -13
- package/src/factory-board.ts +16 -81
- package/src/headless-host/description.ts +248 -1
- package/src/headless-host/production-host.ts +1 -2
- package/src/headless-host/role-turn-host.ts +10 -9
- package/src/ledger-session-read.ts +6 -0
- package/src/navigator-public-session.ts +13 -18
- package/src/navigator-work-context.ts +3 -1
- package/src/notary-role.ts +2 -1
- package/src/notary-source-run.ts +7 -17
- package/src/packaged-role-registry.ts +0 -15
- package/src/pi/durable-principal.ts +2 -4
- package/src/pi/role-turn-host.ts +35 -12
- package/src/public-cli/instruction-seat-run.ts +4 -4
- package/src/public-cli/invocation.ts +29 -57
- package/src/public-cli/run-lifecycle.ts +20 -43
- package/src/public-cli/settlement.ts +30 -173
- package/src/public-cli/terminal.ts +0 -60
- package/src/role-envelope.ts +4 -2
- package/src/role-run-placement.ts +63 -4
- package/src/role-run-relocation.ts +39 -2
- package/src/role-runtime.ts +40 -59
- package/src/run-terminal-artifacts.ts +3 -5
- package/src/run-ticket-number.ts +41 -3
- package/src/session-assistant-usage.ts +8 -20
- package/src/session-identity.ts +3 -4
- package/src/sitian-appender.ts +20 -12
- package/src/submission-ledger.ts +4 -4
- package/src/ticket-provenance-contracts.ts +3 -6
- package/src/ticket-provenance.ts +5 -6
- package/src/ticket-snapshot.ts +3 -2
- package/src/ticket-trajectory.ts +85 -99
|
@@ -7,11 +7,9 @@ import { randomUUID } from "node:crypto";
|
|
|
7
7
|
import { dirname, join } from "node:path";
|
|
8
8
|
import { lstat, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
9
9
|
|
|
10
|
+
import { sessionFileOf } from "../role-run-placement.ts";
|
|
11
|
+
|
|
10
12
|
import { writeHardenedArtifactFile } from "./auto-resume.ts";
|
|
11
|
-
import {
|
|
12
|
-
readAnalystGateCyclesFromAuditorRoles,
|
|
13
|
-
type AnalystGateCycleRound,
|
|
14
|
-
} from "../analyst-gate-cycles-read.ts";
|
|
15
13
|
import { sitianReport } from "../sitian-facade.ts";
|
|
16
14
|
|
|
17
15
|
import {
|
|
@@ -26,6 +24,7 @@ import {
|
|
|
26
24
|
processCancelDiagnostic,
|
|
27
25
|
type CatchableProcessSignal,
|
|
28
26
|
} from "./process-cancel.ts";
|
|
27
|
+
import { readStrictPiSessionJsonl } from "../ledger-session-read.ts";
|
|
29
28
|
import { serializeThrownValue } from "../serialize-thrown-value.ts";
|
|
30
29
|
import { describeErrorIdentity } from "./run-lifecycle.ts";
|
|
31
30
|
import {
|
|
@@ -55,7 +54,6 @@ import {
|
|
|
55
54
|
packagedDurableOfficerEntry,
|
|
56
55
|
packagedRoleAcceptedOutputTool,
|
|
57
56
|
packagedRoleMetadata,
|
|
58
|
-
packagedSkipsGateOnInfrastructureStage,
|
|
59
57
|
type PackagedArtifactFace,
|
|
60
58
|
type PackagedArtifactLeaf,
|
|
61
59
|
} from "../packaged-role-registry.ts";
|
|
@@ -217,7 +215,7 @@ export function ledgerReadScope(
|
|
|
217
215
|
): { home: string; sessionParent: string; attemptId?: string } {
|
|
218
216
|
return {
|
|
219
217
|
home: sealedLedgerHome(admitted),
|
|
220
|
-
sessionParent:
|
|
218
|
+
sessionParent: sessionFileOf(admitted.runDirectory),
|
|
221
219
|
...(scope?.courtAttemptId === undefined || scope.courtAttemptId.length === 0
|
|
222
220
|
? {}
|
|
223
221
|
: { attemptId: scope.courtAttemptId }),
|
|
@@ -469,7 +467,7 @@ async function settleNoReceiptTerminal(
|
|
|
469
467
|
);
|
|
470
468
|
}
|
|
471
469
|
if (scope?.previewOnly !== true) await clearOppositeTerminalArtifactFace(admitted.runDirectory);
|
|
472
|
-
return
|
|
470
|
+
return attachEngineDetourToolUsage({
|
|
473
471
|
roleOutcome,
|
|
474
472
|
navigator: await extractNavigatorFactFromAdmittedSession(
|
|
475
473
|
coordinates.sessionFile,
|
|
@@ -478,7 +476,7 @@ async function settleNoReceiptTerminal(
|
|
|
478
476
|
),
|
|
479
477
|
artifacts: [],
|
|
480
478
|
runId: admitted.runId,
|
|
481
|
-
}, coordinates.sessionDirectory,
|
|
479
|
+
}, coordinates.sessionDirectory, detourUsageContext(admitted, scope));
|
|
482
480
|
}
|
|
483
481
|
|
|
484
482
|
/** Transitional host-session reads remain only for non-sealed failure and audit evidence. */
|
|
@@ -495,8 +493,6 @@ import {
|
|
|
495
493
|
adviceNavigatorFact,
|
|
496
494
|
type ControlledFailureCause,
|
|
497
495
|
type TerminalArtifactRef,
|
|
498
|
-
type TerminalGateFact,
|
|
499
|
-
type TerminalGateSeat,
|
|
500
496
|
type TerminalNavigatorFact,
|
|
501
497
|
type TerminalResult,
|
|
502
498
|
type TerminalRoleName,
|
|
@@ -1034,12 +1030,7 @@ type SessionEntry = {
|
|
|
1034
1030
|
export async function readBoundSessionEntries(
|
|
1035
1031
|
sessionFile: string,
|
|
1036
1032
|
): Promise<SessionEntry[]> {
|
|
1037
|
-
|
|
1038
|
-
const entries: SessionEntry[] = [];
|
|
1039
|
-
for (const line of text.trim().split("\n").filter(Boolean)) {
|
|
1040
|
-
entries.push(JSON.parse(line) as SessionEntry);
|
|
1041
|
-
}
|
|
1042
|
-
return entries;
|
|
1033
|
+
return await readStrictPiSessionJsonl(sessionFile) as SessionEntry[];
|
|
1043
1034
|
}
|
|
1044
1035
|
|
|
1045
1036
|
/**
|
|
@@ -1151,73 +1142,6 @@ function parseNavigatorAttendanceDetails(
|
|
|
1151
1142
|
};
|
|
1152
1143
|
}
|
|
1153
1144
|
|
|
1154
|
-
/**
|
|
1155
|
-
* Project direct and historical paired gate rounds onto the public Terminal.
|
|
1156
|
-
* actualSeats derive only from accepted receipts, never expected/missing seats.
|
|
1157
|
-
*/
|
|
1158
|
-
export function projectTerminalGateFact(
|
|
1159
|
-
rounds: readonly AnalystGateCycleRound[],
|
|
1160
|
-
): TerminalGateFact | undefined {
|
|
1161
|
-
if (rounds.length === 0) return undefined;
|
|
1162
|
-
const seen = new Set<TerminalGateSeat>();
|
|
1163
|
-
for (const round of rounds) {
|
|
1164
|
-
if (round.origin.kind === "historical_dispatch") seen.add("gatekeeper");
|
|
1165
|
-
seen.add(round.officer);
|
|
1166
|
-
}
|
|
1167
|
-
const actualSeats = (["gatekeeper", "inspector", "notary"] as const).filter(
|
|
1168
|
-
(seat) => seen.has(seat),
|
|
1169
|
-
);
|
|
1170
|
-
return {
|
|
1171
|
-
actualSeats,
|
|
1172
|
-
rounds: rounds.map((round) => ({
|
|
1173
|
-
roundIndex: round.roundIndex,
|
|
1174
|
-
dispatch:
|
|
1175
|
-
round.origin.kind === "direct"
|
|
1176
|
-
? { kind: "direct" as const, officer: round.officer }
|
|
1177
|
-
: {
|
|
1178
|
-
kind: "historical_dispatch" as const,
|
|
1179
|
-
officer: round.officer,
|
|
1180
|
-
...(round.origin.reason === undefined
|
|
1181
|
-
? {}
|
|
1182
|
-
: { reason: round.origin.reason }),
|
|
1183
|
-
},
|
|
1184
|
-
officer: {
|
|
1185
|
-
seat: round.officer,
|
|
1186
|
-
status: round.status,
|
|
1187
|
-
findings: round.findings,
|
|
1188
|
-
},
|
|
1189
|
-
})),
|
|
1190
|
-
};
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
/**
|
|
1194
|
-
* Read gate facts from the run's session/auditor-roles nest (#446/#478).
|
|
1195
|
-
* Missing directories → undefined (no-gate zero change).
|
|
1196
|
-
* Damaged discovered volumes propagate — never wash to "no gate".
|
|
1197
|
-
*/
|
|
1198
|
-
export async function extractGateFactFromSessionDirectory(
|
|
1199
|
-
sessionDirectory: string,
|
|
1200
|
-
options: {
|
|
1201
|
-
readonly runDirectory?: string;
|
|
1202
|
-
readonly parentSessionFile?: string;
|
|
1203
|
-
} = {},
|
|
1204
|
-
): Promise<TerminalGateFact | undefined> {
|
|
1205
|
-
const directories = [join(sessionDirectory, "auditor-roles")];
|
|
1206
|
-
const parentSessionFile =
|
|
1207
|
-
options.parentSessionFile ?? join(sessionDirectory, "session.jsonl");
|
|
1208
|
-
const rounds = await readAnalystGateCyclesFromAuditorRoles(directories, {
|
|
1209
|
-
parentSessionFile,
|
|
1210
|
-
});
|
|
1211
|
-
return projectTerminalGateFact(rounds);
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
/**
|
|
1215
|
-
* Attach optional gate projection onto a settled Terminal base.
|
|
1216
|
-
* Shared by every settle path so auditor-roles is scanned once here only.
|
|
1217
|
-
* `runId` is optional only for a batch projection that has no parent run.
|
|
1218
|
-
* Gate read damage is a package note beside the host terminal. It does not
|
|
1219
|
-
* replace that terminal and it does not omit the gate without a trace.
|
|
1220
|
-
*/
|
|
1221
1145
|
/**
|
|
1222
1146
|
* #537: project this-invocation ak_engine_detour usage onto decisiveFacts.
|
|
1223
1147
|
* Absent when engine is not mounted; callCount 0 when mounted with zero calls.
|
|
@@ -1231,16 +1155,15 @@ async function attachEngineDetourToolUsage<
|
|
|
1231
1155
|
>(
|
|
1232
1156
|
base: T,
|
|
1233
1157
|
sessionDirectory: string,
|
|
1234
|
-
|
|
1158
|
+
detourContext: {
|
|
1235
1159
|
readonly runDirectory?: string;
|
|
1236
|
-
readonly courtAttemptId?: string;
|
|
1237
1160
|
readonly invocationScopeId?: string;
|
|
1238
1161
|
readonly notePackageFault?: SettlementCourtScope["notePackageFault"];
|
|
1239
1162
|
} = {},
|
|
1240
1163
|
): Promise<T> {
|
|
1241
1164
|
const runDirectory =
|
|
1242
|
-
typeof
|
|
1243
|
-
?
|
|
1165
|
+
typeof detourContext.runDirectory === "string" && detourContext.runDirectory.length > 0
|
|
1166
|
+
? detourContext.runDirectory
|
|
1244
1167
|
: runDirectoryFromSessionDirectory(sessionDirectory);
|
|
1245
1168
|
let engineMounted = false;
|
|
1246
1169
|
try {
|
|
@@ -1248,9 +1171,9 @@ async function attachEngineDetourToolUsage<
|
|
|
1248
1171
|
} catch (error) {
|
|
1249
1172
|
await noteSettlementFault(
|
|
1250
1173
|
runDirectory,
|
|
1251
|
-
|
|
1174
|
+
detourContext.notePackageFault === undefined
|
|
1252
1175
|
? undefined
|
|
1253
|
-
: { notePackageFault:
|
|
1176
|
+
: { notePackageFault: detourContext.notePackageFault },
|
|
1254
1177
|
`engine mount read failed beside host terminal: ${describeErrorIdentity(error)}`,
|
|
1255
1178
|
);
|
|
1256
1179
|
return base;
|
|
@@ -1260,9 +1183,9 @@ async function attachEngineDetourToolUsage<
|
|
|
1260
1183
|
// Public-invocation scope from the shared Host envelope (settlement scope), never
|
|
1261
1184
|
// courtAttemptId and never a detour sidecar file.
|
|
1262
1185
|
const invocationScopeId =
|
|
1263
|
-
typeof
|
|
1264
|
-
|
|
1265
|
-
?
|
|
1186
|
+
typeof detourContext.invocationScopeId === "string" &&
|
|
1187
|
+
detourContext.invocationScopeId.length > 0
|
|
1188
|
+
? detourContext.invocationScopeId
|
|
1266
1189
|
: undefined;
|
|
1267
1190
|
|
|
1268
1191
|
const sessionFile = sessionFileFromSessionDirectory(sessionDirectory);
|
|
@@ -1277,9 +1200,9 @@ async function attachEngineDetourToolUsage<
|
|
|
1277
1200
|
} catch (error) {
|
|
1278
1201
|
await noteSettlementFault(
|
|
1279
1202
|
runDirectory,
|
|
1280
|
-
|
|
1203
|
+
detourContext.notePackageFault === undefined
|
|
1281
1204
|
? undefined
|
|
1282
|
-
: { notePackageFault:
|
|
1205
|
+
: { notePackageFault: detourContext.notePackageFault },
|
|
1283
1206
|
`engine detour read failed beside host terminal: ${describeErrorIdentity(error)}`,
|
|
1284
1207
|
);
|
|
1285
1208
|
return base;
|
|
@@ -1290,20 +1213,17 @@ async function attachEngineDetourToolUsage<
|
|
|
1290
1213
|
};
|
|
1291
1214
|
}
|
|
1292
1215
|
|
|
1293
|
-
/**
|
|
1294
|
-
function
|
|
1216
|
+
/** Engine-detour usage context from admitted run + settlement scope. */
|
|
1217
|
+
function detourUsageContext(
|
|
1295
1218
|
admitted: { readonly runDirectory: string },
|
|
1296
1219
|
scope?: SettlementCourtScope,
|
|
1297
1220
|
): {
|
|
1298
1221
|
readonly runDirectory: string;
|
|
1299
|
-
readonly courtAttemptId?: string;
|
|
1300
1222
|
readonly invocationScopeId?: string;
|
|
1223
|
+
readonly notePackageFault?: SettlementCourtScope["notePackageFault"];
|
|
1301
1224
|
} {
|
|
1302
1225
|
return {
|
|
1303
1226
|
runDirectory: admitted.runDirectory,
|
|
1304
|
-
...(scope?.courtAttemptId === undefined || scope.courtAttemptId.length === 0
|
|
1305
|
-
? {}
|
|
1306
|
-
: { courtAttemptId: scope.courtAttemptId }),
|
|
1307
1227
|
...(scope?.invocationScopeId === undefined || scope.invocationScopeId.length === 0
|
|
1308
1228
|
? {}
|
|
1309
1229
|
: { invocationScopeId: scope.invocationScopeId }),
|
|
@@ -1311,54 +1231,6 @@ function detourGateContext(
|
|
|
1311
1231
|
};
|
|
1312
1232
|
}
|
|
1313
1233
|
|
|
1314
|
-
async function withOptionalGateProjection<
|
|
1315
|
-
T extends {
|
|
1316
|
-
roleOutcome: TerminalRoleOutcome;
|
|
1317
|
-
navigator: TerminalNavigatorFact;
|
|
1318
|
-
artifacts: readonly TerminalArtifactRef[];
|
|
1319
|
-
},
|
|
1320
|
-
>(
|
|
1321
|
-
base: T,
|
|
1322
|
-
sessionDirectory: string,
|
|
1323
|
-
gateContext: {
|
|
1324
|
-
readonly runDirectory?: string;
|
|
1325
|
-
readonly parentSessionFile?: string;
|
|
1326
|
-
readonly courtAttemptId?: string;
|
|
1327
|
-
readonly invocationScopeId?: string;
|
|
1328
|
-
readonly notePackageFault?: SettlementCourtScope["notePackageFault"];
|
|
1329
|
-
} = {},
|
|
1330
|
-
): Promise<T & { gate?: TerminalGateFact }> {
|
|
1331
|
-
// A gate transport failure is already represented by typed evidence and has no
|
|
1332
|
-
// accepted gate cycle to project. Re-reading that rejected receipt as an
|
|
1333
|
-
// accepted cycle would replace the original failure with a projection error.
|
|
1334
|
-
const secondaryEvidence = base.roleOutcome.kind === "failure"
|
|
1335
|
-
? base.roleOutcome.decisiveFacts.secondaryEvidence
|
|
1336
|
-
: undefined;
|
|
1337
|
-
const skipGate =
|
|
1338
|
-
isRecord(secondaryEvidence)
|
|
1339
|
-
&& secondaryEvidence.kind === "role_infrastructure_failure"
|
|
1340
|
-
&& packagedSkipsGateOnInfrastructureStage(secondaryEvidence.stage);
|
|
1341
|
-
|
|
1342
|
-
let next: T & { gate?: TerminalGateFact } = base;
|
|
1343
|
-
if (!skipGate) {
|
|
1344
|
-
try {
|
|
1345
|
-
const gate = await extractGateFactFromSessionDirectory(sessionDirectory, gateContext);
|
|
1346
|
-
if (gate !== undefined) next = { ...base, gate };
|
|
1347
|
-
} catch (error) {
|
|
1348
|
-
const runDirectory = gateContext.runDirectory ?? runDirectoryFromSessionDirectory(sessionDirectory);
|
|
1349
|
-
await noteSettlementFault(
|
|
1350
|
-
runDirectory,
|
|
1351
|
-
gateContext.notePackageFault === undefined
|
|
1352
|
-
? undefined
|
|
1353
|
-
: { notePackageFault: gateContext.notePackageFault },
|
|
1354
|
-
`gate read failed beside host terminal: ${describeErrorIdentity(error)}`,
|
|
1355
|
-
);
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
return attachEngineDetourToolUsage(next, sessionDirectory, gateContext);
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
1234
|
function routePlaybookFailureMessage(entries: readonly SessionEntry[]): string | undefined {
|
|
1363
1235
|
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
|
1364
1236
|
const entry = entries[i];
|
|
@@ -1620,12 +1492,12 @@ async function finishLawfulSeat(
|
|
|
1620
1492
|
const artifacts = scope?.previewOnly === true
|
|
1621
1493
|
? []
|
|
1622
1494
|
: await publishDeclaredSeatArtifacts(admitted, roleOutcome, coordinates, entries, scope?.recordAttemptHistory === true);
|
|
1623
|
-
const terminal = await
|
|
1495
|
+
const terminal = await attachEngineDetourToolUsage({
|
|
1624
1496
|
roleOutcome,
|
|
1625
1497
|
navigator: extractNavigatorFact(entries),
|
|
1626
1498
|
artifacts,
|
|
1627
1499
|
runId: admitted.runId,
|
|
1628
|
-
}, coordinates.sessionDirectory,
|
|
1500
|
+
}, coordinates.sessionDirectory, detourUsageContext(admitted, scope));
|
|
1629
1501
|
return attachRecordedSubmissions(admitted, terminal, scope);
|
|
1630
1502
|
}
|
|
1631
1503
|
|
|
@@ -1933,33 +1805,18 @@ async function applySecretariatCountersignTerminal(
|
|
|
1933
1805
|
}
|
|
1934
1806
|
|
|
1935
1807
|
/**
|
|
1936
|
-
* After the audit gate returns, attach
|
|
1937
|
-
*
|
|
1938
|
-
*
|
|
1808
|
+
* After the audit gate returns, attach the secretariat officer fact onto the
|
|
1809
|
+
* terminal this turn already settled. Does not publish again — a second publish
|
|
1810
|
+
* would append another attempt-history row for the same attempt.
|
|
1939
1811
|
*/
|
|
1940
|
-
export async function
|
|
1812
|
+
export async function attachPostAuditCountersignFact(
|
|
1941
1813
|
admitted: AdmittedRoleInvocation,
|
|
1942
1814
|
authority: DurablePrincipalAuthority,
|
|
1943
1815
|
terminal: TerminalResult,
|
|
1944
1816
|
scope?: SettlementCourtScope,
|
|
1945
1817
|
): Promise<TerminalResult> {
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
try {
|
|
1949
|
-
gate = await extractGateFactFromSessionDirectory(sessionDirectory, {
|
|
1950
|
-
runDirectory: admitted.runDirectory,
|
|
1951
|
-
parentSessionFile: sessionFile,
|
|
1952
|
-
});
|
|
1953
|
-
} catch (error) {
|
|
1954
|
-
await noteSettlementFault(
|
|
1955
|
-
admitted.runDirectory,
|
|
1956
|
-
scope,
|
|
1957
|
-
`gate read failed beside host terminal: ${describeErrorIdentity(error)}`,
|
|
1958
|
-
);
|
|
1959
|
-
}
|
|
1960
|
-
const withGate = gate === undefined ? terminal : { ...terminal, gate };
|
|
1961
|
-
if (admitted.role !== "secretariat") return withGate;
|
|
1962
|
-
return applySecretariatCountersignTerminal(admitted, authority, withGate, scope);
|
|
1818
|
+
if (admitted.role !== "secretariat") return terminal;
|
|
1819
|
+
return applySecretariatCountersignTerminal(admitted, authority, terminal, scope);
|
|
1963
1820
|
}
|
|
1964
1821
|
|
|
1965
1822
|
/** One failed attempt to place a durable failure artifact (path is private layout). */
|
|
@@ -2246,10 +2103,10 @@ export async function settleFailureTerminalResult(
|
|
|
2246
2103
|
diagnostic: failure.diagnostic,
|
|
2247
2104
|
decisiveFacts,
|
|
2248
2105
|
};
|
|
2249
|
-
return
|
|
2106
|
+
return attachEngineDetourToolUsage(
|
|
2250
2107
|
{ roleOutcome, navigator, artifacts, runId: admitted.runId },
|
|
2251
2108
|
sessionDirectory,
|
|
2252
|
-
|
|
2109
|
+
detourUsageContext(admitted, options),
|
|
2253
2110
|
);
|
|
2254
2111
|
}
|
|
2255
2112
|
|
|
@@ -103,44 +103,6 @@ export type TerminalNavigatorFact =
|
|
|
103
103
|
advisoryDiagnostic?: string;
|
|
104
104
|
};
|
|
105
105
|
|
|
106
|
-
/** Current English gate seat faces projected on Terminal (#478). */
|
|
107
|
-
export type TerminalGateSeat = "gatekeeper" | "inspector" | "notary";
|
|
108
|
-
|
|
109
|
-
/** Honest discriminant: direct summons vs historical province dispatch. */
|
|
110
|
-
export type TerminalGateDispatch =
|
|
111
|
-
| { readonly kind: "direct"; readonly officer: "inspector" | "notary" }
|
|
112
|
-
| {
|
|
113
|
-
readonly kind: "historical_dispatch";
|
|
114
|
-
readonly officer: "inspector" | "notary";
|
|
115
|
-
/** Present only when the accepted dispatch wrote a non-empty reason. */
|
|
116
|
-
readonly reason?: string;
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
/** One accepted officer report: seat, status, full typed findings. */
|
|
120
|
-
export type TerminalGateOfficerReport = {
|
|
121
|
-
readonly seat: "inspector" | "notary";
|
|
122
|
-
readonly status: string;
|
|
123
|
-
readonly findings: readonly unknown[];
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
/** One direct or historical paired gate round on the public Terminal. */
|
|
127
|
-
export type TerminalGateRound = {
|
|
128
|
-
readonly roundIndex: number;
|
|
129
|
-
readonly dispatch: TerminalGateDispatch;
|
|
130
|
-
readonly officer: TerminalGateOfficerReport;
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Optional gate projection (#478).
|
|
135
|
-
* Absent when no accepted officer rounds exist. Only durable accepted child
|
|
136
|
-
* receipts — never soul-derived expected/missing seats.
|
|
137
|
-
*/
|
|
138
|
-
export type TerminalGateFact = {
|
|
139
|
-
/** Seats that actually ran, derived from accepted receipts. */
|
|
140
|
-
readonly actualSeats: readonly TerminalGateSeat[];
|
|
141
|
-
readonly rounds: readonly TerminalGateRound[];
|
|
142
|
-
};
|
|
143
|
-
|
|
144
106
|
/**
|
|
145
107
|
* Parent-facing current reply: a court's own accepted/audit payloads only.
|
|
146
108
|
* Run-scoped history stays on TerminalResult.submissions (#879 / #836), and a
|
|
@@ -177,11 +139,6 @@ export type TerminalResult = {
|
|
|
177
139
|
* (#953 — not copied onto failure.payloads).
|
|
178
140
|
*/
|
|
179
141
|
submissions?: readonly unknown[];
|
|
180
|
-
/**
|
|
181
|
-
* Optional gate facts (#478). Present when accepted direct or historical
|
|
182
|
-
* paired rounds exist under session/auditor-roles; omitted on no-gate runs.
|
|
183
|
-
*/
|
|
184
|
-
gate?: TerminalGateFact;
|
|
185
142
|
/** Call-local auto-resume observation (0..2) for this single LLM call; read-only, not persisted. */
|
|
186
143
|
autoResumeCount?: number;
|
|
187
144
|
} & (
|
|
@@ -272,23 +229,6 @@ export function formatTerminalResult(result: TerminalResult): string {
|
|
|
272
229
|
for (const artifact of result.artifacts) {
|
|
273
230
|
lines.push(`artifact\t${artifact.kind}\t${encodeTerminalField(artifact.path)}`);
|
|
274
231
|
}
|
|
275
|
-
// Gate region is unfrozen presentation of typed facts (ADR 0052) — machines
|
|
276
|
-
// read result.gate, never these row labels.
|
|
277
|
-
if (result.gate !== undefined) {
|
|
278
|
-
lines.push(
|
|
279
|
-
`gate\t${encodeTerminalField(result.gate.actualSeats.join(","))}\t${result.gate.rounds.length}`,
|
|
280
|
-
);
|
|
281
|
-
for (const round of result.gate.rounds) {
|
|
282
|
-
const reason =
|
|
283
|
-
round.dispatch.kind === "historical_dispatch"
|
|
284
|
-
&& round.dispatch.reason !== undefined
|
|
285
|
-
? encodeTerminalField(round.dispatch.reason)
|
|
286
|
-
: "";
|
|
287
|
-
lines.push(
|
|
288
|
-
`gate-round\t${round.roundIndex}\t${round.dispatch.kind}\t${round.dispatch.officer}\t${reason}\t${round.officer.seat}\t${encodeTerminalField(round.officer.status)}\t${encodeTerminalField(JSON.stringify(round.officer.findings))}`,
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
232
|
if (result.reviewerChildOutcomes !== undefined) {
|
|
293
233
|
for (const axis of ["completeness", "correctness"] as const) {
|
|
294
234
|
const child = result.reviewerChildOutcomes[axis];
|
package/src/role-envelope.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { appendFile, mkdir, writeFile } from "node:fs/promises";
|
|
3
3
|
import { createServer, type Server, type Socket } from "node:net";
|
|
4
|
-
import { dirname
|
|
4
|
+
import { dirname } from "node:path";
|
|
5
|
+
|
|
6
|
+
import { sessionFileOf } from "./role-run-placement.ts";
|
|
5
7
|
import { fileURLToPath } from "node:url";
|
|
6
8
|
|
|
7
9
|
import {
|
|
@@ -124,7 +126,7 @@ export async function prepareRoleEnvelope(options: {
|
|
|
124
126
|
// navigator attendance, no-receipt) MUST flush so parent settlement can read
|
|
125
127
|
// them after the headless process ends — memory-only books left extractNavigatorFact
|
|
126
128
|
// with no durable terminal / attendance on codex/claude/ACP parents.
|
|
127
|
-
let sessionFile = options.sessionFile ??
|
|
129
|
+
let sessionFile = options.sessionFile ?? sessionFileOf(request.runDirectory);
|
|
128
130
|
await mkdir(dirname(sessionFile), { recursive: true });
|
|
129
131
|
if (request.continuation.kind !== "resume") {
|
|
130
132
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readdir } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
activationBookDirectory,
|
|
@@ -21,6 +21,45 @@ export type RoleRunPlacement = {
|
|
|
21
21
|
readonly attachmentsDirectory: string;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
/** Writer leaf `<runId>@<role>`. Empty sides stay empty; this is not a validator. */
|
|
25
|
+
export function formatRunLeaf(runId: string, role: string): string {
|
|
26
|
+
return `${runId}@${role}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Inverse of formatRunLeaf: last `@`, both sides non-empty.
|
|
31
|
+
* `a@b@c` is runId `a@b`, role `c`. Charset is not a second grammar.
|
|
32
|
+
*/
|
|
33
|
+
export function parseRunLeaf(name: string): { readonly runId: string; readonly role: string } | undefined {
|
|
34
|
+
const at = name.lastIndexOf("@");
|
|
35
|
+
if (at <= 0 || at === name.length - 1) return undefined;
|
|
36
|
+
return { runId: name.slice(0, at), role: name.slice(at + 1) };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function sessionDirectoryOf(runDirectory: string): string {
|
|
40
|
+
return join(runDirectory, "session");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const ROLE_RUN_SESSION_FILENAME = "session.jsonl";
|
|
44
|
+
|
|
45
|
+
export function sessionFileIn(sessionDirectory: string): string {
|
|
46
|
+
return join(sessionDirectory, ROLE_RUN_SESSION_FILENAME);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function sessionFileOf(runDirectory: string): string {
|
|
50
|
+
return sessionFileIn(sessionDirectoryOf(runDirectory));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** `<run>/session/session.jsonl` → run directory. */
|
|
54
|
+
export function runDirectoryOfSessionFile(sessionFile: string): string {
|
|
55
|
+
return dirname(dirname(sessionFile));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** `<run>/session` → run directory. */
|
|
59
|
+
export function runDirectoryFromSessionDirectory(sessionDirectory: string): string {
|
|
60
|
+
return dirname(sessionDirectory);
|
|
61
|
+
}
|
|
62
|
+
|
|
24
63
|
/**
|
|
25
64
|
* Sole book-level run directory walk: flat legacy `runs/` plus each
|
|
26
65
|
* `subject/runs/` child (ticket / unbound). One authority for read-side
|
|
@@ -64,6 +103,26 @@ export async function listBookRunDirectories(bookDir: string): Promise<string[]>
|
|
|
64
103
|
return out.sort();
|
|
65
104
|
}
|
|
66
105
|
|
|
106
|
+
/** Resolve a complete run identity across the caller's books; ambiguity is never readdir-first. */
|
|
107
|
+
export async function findRoleRunDirectory(
|
|
108
|
+
bookDirectories: readonly string[],
|
|
109
|
+
runId: string,
|
|
110
|
+
onlyRole?: string,
|
|
111
|
+
): Promise<string | undefined> {
|
|
112
|
+
const matches: string[] = [];
|
|
113
|
+
for (const bookDirectory of bookDirectories) {
|
|
114
|
+
for (const runDirectory of await listBookRunDirectories(bookDirectory)) {
|
|
115
|
+
const parsed = parseRunLeaf(basename(runDirectory));
|
|
116
|
+
if (parsed === undefined || parsed.runId !== runId) continue;
|
|
117
|
+
if (onlyRole !== undefined && parsed.role !== onlyRole) continue;
|
|
118
|
+
matches.push(runDirectory);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (matches.length === 0) return undefined;
|
|
122
|
+
if (matches.length === 1) return matches[0];
|
|
123
|
+
throw new Error(`ambiguous role run id ${runId}: ${matches.join(", ")}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
67
126
|
/** The single authority for every path belonging to an admitted role run. */
|
|
68
127
|
export function roleRunPlacement(
|
|
69
128
|
ledgerHome: string,
|
|
@@ -84,13 +143,13 @@ export function roleRunPlacement(
|
|
|
84
143
|
activationBookDirectory(ledgerHome, input.bookKey),
|
|
85
144
|
subjectDirectory,
|
|
86
145
|
"runs",
|
|
87
|
-
|
|
146
|
+
formatRunLeaf(input.runId, input.role),
|
|
88
147
|
);
|
|
89
|
-
const sessionDirectory =
|
|
148
|
+
const sessionDirectory = sessionDirectoryOf(runDirectory);
|
|
90
149
|
return {
|
|
91
150
|
runDirectory,
|
|
92
151
|
sessionDirectory,
|
|
93
|
-
sessionFile:
|
|
152
|
+
sessionFile: sessionFileIn(sessionDirectory),
|
|
94
153
|
artifactsDirectory: roleRunArtifactsDirectory(runDirectory),
|
|
95
154
|
attachmentsDirectory: join(runDirectory, "attachments"),
|
|
96
155
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Single authority for rewriting durable run pages after a run directory moves.
|
|
3
|
-
* Live
|
|
3
|
+
* Live callers project typed fields; only historical migrators walk durable pages.
|
|
4
4
|
*
|
|
5
5
|
* Scope is typed machine-consumed path fields only — never free text, never
|
|
6
6
|
* frozen attachment/artifact bytes. Nested walk is confined to package-owned
|
|
@@ -8,7 +8,11 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { existsSync } from "node:fs";
|
|
10
10
|
import { readdir, readFile, writeFile } from "node:fs/promises";
|
|
11
|
-
import { join, sep } from "node:path";
|
|
11
|
+
import { dirname, join, sep } from "node:path";
|
|
12
|
+
|
|
13
|
+
import { activationBookDirectory, physicalPathIdentity } from "./activation-ledger-topology.ts";
|
|
14
|
+
import { readRoleRunIdentity } from "./public-cli/run-lifecycle.ts";
|
|
15
|
+
import { findRoleRunDirectory, roleRunPlacement } from "./role-run-placement.ts";
|
|
12
16
|
|
|
13
17
|
import { AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE } from "./compliance-transport.ts";
|
|
14
18
|
|
|
@@ -67,6 +71,39 @@ export function rewriteRunDirectoryPathValue(
|
|
|
67
71
|
return value;
|
|
68
72
|
}
|
|
69
73
|
|
|
74
|
+
/** Match physical aliases or a normal unbound→ticket bind, never historical migration layouts. */
|
|
75
|
+
export async function retainedRunPathsMatch(
|
|
76
|
+
recordedPath: unknown,
|
|
77
|
+
currentPath: string,
|
|
78
|
+
currentRunDirectory?: string,
|
|
79
|
+
): Promise<boolean> {
|
|
80
|
+
if (recordedPath === currentPath) return true;
|
|
81
|
+
if (typeof recordedPath !== "string") return false;
|
|
82
|
+
const recordedPathIdentity = physicalPathIdentity(recordedPath);
|
|
83
|
+
const currentPathIdentity = physicalPathIdentity(currentPath);
|
|
84
|
+
if (recordedPathIdentity === currentPathIdentity) return true;
|
|
85
|
+
if (currentRunDirectory === undefined) return false;
|
|
86
|
+
const runDirectory = physicalPathIdentity(currentRunDirectory);
|
|
87
|
+
const identity = await readRoleRunIdentity(runDirectory);
|
|
88
|
+
if (identity === undefined) return false;
|
|
89
|
+
for (let directory = runDirectory; dirname(directory) !== directory; directory = dirname(directory)) {
|
|
90
|
+
const ledgerHome = dirname(dirname(directory));
|
|
91
|
+
if (directory !== activationBookDirectory(ledgerHome, identity.bookKey)) continue;
|
|
92
|
+
const unbound = roleRunPlacement(ledgerHome, {
|
|
93
|
+
bookKey: identity.bookKey,
|
|
94
|
+
subject: { unbound: true },
|
|
95
|
+
runId: identity.runId,
|
|
96
|
+
role: identity.role,
|
|
97
|
+
});
|
|
98
|
+
if (rewriteRunDirectoryPathValue(recordedPathIdentity, unbound.runDirectory, runDirectory) !== currentPathIdentity) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
const placed = await findRoleRunDirectory([directory], identity.runId, identity.role);
|
|
102
|
+
return placed !== undefined && physicalPathIdentity(placed) === runDirectory;
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
|
|
70
107
|
/**
|
|
71
108
|
* Apply the longest matching oldRunDirectory rewrite. Longer keys win so a
|
|
72
109
|
* nested run path is not partially claimed by a shorter sibling prefix.
|