@akagilnc/pi-workflow-roles 0.1.5148 → 0.1.5194
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 +13086 -13034
- package/dist/archivist-record-entry.js +4 -20
- package/dist/archivist-record-pointer.js +28 -0
- package/dist/audit-escalation.js +3 -119
- package/dist/auditor-soul.js +32 -0
- package/dist/doctor-auditor.js +26 -0
- package/dist/headless-host/production-host.js +11218 -11166
- package/dist/judge-role.js +73 -0
- package/dist/migrate-book-topology.js +8 -0
- package/dist/package-contracts/judge-output.js +1 -18
- package/dist/package-contracts/terminating-tools.js +2 -2
- package/dist/public-cli/cli.js +2 -2
- package/dist/public-cli/countersign-run.js +11 -0
- package/dist/public-cli/instruction-seat-run.js +428 -48
- package/dist/public-cli/invocation.js +10 -5
- package/dist/public-cli/main.js +10049 -8911
- package/dist/public-cli/settlement.js +28 -8
- package/dist/public-role-summons.js +6 -4
- package/dist/submission-errors.js +1 -1
- package/dist/submission-gate.js +165 -0
- package/dist/submission-ledger.js +0 -27
- package/dist/worker-submission-contracts.js +8 -0
- package/package.json +1 -1
- package/src/archivist-record-entry.ts +5 -49
- package/src/archivist-record-pointer.ts +49 -0
- package/src/audit-escalation.ts +4 -217
- package/src/auditor-soul.ts +33 -0
- package/src/doctor-role.ts +11 -5
- package/src/gatekeeper-role.ts +2 -2
- package/src/host-contracts.ts +0 -25
- package/src/judge-role.ts +63 -90
- package/src/package-contracts/judge-output.ts +0 -17
- package/src/package-contracts/terminating-tools.ts +0 -2
- package/src/pi/adapter.ts +1 -33
- package/src/public-cli/cli.ts +2 -2
- package/src/public-cli/countersign-run.ts +11 -0
- package/src/public-cli/instruction-seat-run.ts +433 -16
- package/src/public-cli/invocation.ts +9 -3
- package/src/public-cli/post-admission.ts +0 -1
- package/src/public-cli/settlement.ts +32 -8
- package/src/public-role-summons.ts +9 -6
- package/src/role-envelope.ts +0 -32
- package/src/role-runtime-dependencies.ts +0 -4
- package/src/role-runtime.ts +15 -218
- package/src/submission-errors.ts +1 -1
- package/src/submission-gate.ts +46 -10
- package/src/submission-ledger.ts +0 -27
- package/src/worker-role.ts +23 -102
- package/src/worker-submission-contracts.ts +8 -0
- package/src/worker-submission-gates.ts +2 -2
|
@@ -54,16 +54,18 @@ function roleOutcomeFromRows(role, rows) {
|
|
|
54
54
|
const terminal = mine.filter((row) => row.kind === "accepted" || row.kind === "audit-escalation");
|
|
55
55
|
if (terminal.length === 0)
|
|
56
56
|
return undefined;
|
|
57
|
-
//
|
|
57
|
+
// Full sequence stays on payloads. The queue kind follows the latest terminal
|
|
58
|
+
// row only — an earlier escalation must not outrank a later acceptance or
|
|
59
|
+
// keep that earlier receipt (#1057).
|
|
58
60
|
const payloads = mine.map((row) => row.accepted);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
const latest = terminal[terminal.length - 1];
|
|
62
|
+
if (latest.kind === "audit-escalation") {
|
|
61
63
|
return {
|
|
62
64
|
kind: "audit_escalation", role, status: "audit_escalation", payloads,
|
|
63
|
-
...(
|
|
64
|
-
auditEscalationReceipt:
|
|
65
|
-
...(Object.hasOwn(
|
|
66
|
-
} }),
|
|
65
|
+
...(Object.hasOwn(latest, "auditReceipt") ? { decisiveFacts: {
|
|
66
|
+
auditEscalationReceipt: latest.auditReceipt,
|
|
67
|
+
...(Object.hasOwn(latest, "auditOfficer") ? { auditEscalationOfficer: latest.auditOfficer } : {}),
|
|
68
|
+
} } : {}),
|
|
67
69
|
};
|
|
68
70
|
}
|
|
69
71
|
return { kind: "accepted", role, payloads };
|
|
@@ -535,7 +537,7 @@ function sessionReadFailure(error, fallbackMessage) {
|
|
|
535
537
|
* Read the exact bound Pi session file principal.
|
|
536
538
|
* Does not scan the session directory for "latest" — resume identity is the file.
|
|
537
539
|
*/
|
|
538
|
-
async function readBoundSessionEntries(sessionFile) {
|
|
540
|
+
export async function readBoundSessionEntries(sessionFile) {
|
|
539
541
|
const text = await readFile(sessionFile, "utf8");
|
|
540
542
|
const entries = [];
|
|
541
543
|
for (const line of text.trim().split("\n").filter(Boolean)) {
|
|
@@ -1286,6 +1288,8 @@ function snapshotAuditDetails(details) {
|
|
|
1286
1288
|
* terminal identity; the retained response must be this seat's real escalate
|
|
1287
1289
|
* decision and its projected audit-owned fields must agree with it.
|
|
1288
1290
|
*/
|
|
1291
|
+
// Historical pre-#1057 session artifacts may still contain this old projection;
|
|
1292
|
+
// no current submission path writes it, but resume/settlement must read it.
|
|
1289
1293
|
function boundAuditEscalationForResult(entries, resultIndex, message, role, outputToolName) {
|
|
1290
1294
|
const roleCall = boundRoleToolCallForResult(entries, resultIndex, message, outputToolName);
|
|
1291
1295
|
if (roleCall === undefined)
|
|
@@ -2216,6 +2220,22 @@ async function applySecretariatCountersignTerminal(admitted, authority, settled)
|
|
|
2216
2220
|
}
|
|
2217
2221
|
return settled;
|
|
2218
2222
|
}
|
|
2223
|
+
/**
|
|
2224
|
+
* After the audit gate returns, attach gate rounds and the secretariat officer
|
|
2225
|
+
* fact onto the terminal this turn already settled. Does not publish again —
|
|
2226
|
+
* a second publish would append another attempt-history row for the same attempt.
|
|
2227
|
+
*/
|
|
2228
|
+
export async function attachPostAuditProjection(admitted, authority, terminal) {
|
|
2229
|
+
const { sessionDirectory, sessionFile } = coordinatesFromAdmitted(authority, admitted);
|
|
2230
|
+
const gate = await extractGateFactFromSessionDirectory(sessionDirectory, {
|
|
2231
|
+
runDirectory: admitted.runDirectory,
|
|
2232
|
+
parentSessionFile: sessionFile,
|
|
2233
|
+
});
|
|
2234
|
+
const withGate = gate === undefined ? terminal : { ...terminal, gate };
|
|
2235
|
+
if (admitted.role !== "secretariat")
|
|
2236
|
+
return withGate;
|
|
2237
|
+
return applySecretariatCountersignTerminal(admitted, authority, withGate);
|
|
2238
|
+
}
|
|
2219
2239
|
function publicationAttemptFromError(path, error) {
|
|
2220
2240
|
if (error instanceof Error) {
|
|
2221
2241
|
const identity = {
|
|
@@ -439,6 +439,8 @@ async function summonGateOfficer(options) {
|
|
|
439
439
|
const { readableGateItem } = await import("./readable-gate-item.js");
|
|
440
440
|
gateReviewInstruction = readableGateItem(options.submission);
|
|
441
441
|
}
|
|
442
|
+
const { runIdFromRunDirectory } = await import("./run-terminal-artifacts.js");
|
|
443
|
+
const parentRunId = runIdFromRunDirectory(options.sourceRunDirectory);
|
|
442
444
|
const common = {
|
|
443
445
|
cwd: options.cwd,
|
|
444
446
|
...home === void 0 ? {} : { home },
|
|
@@ -449,7 +451,8 @@ async function summonGateOfficer(options) {
|
|
|
449
451
|
...gateReviewInstruction === void 0 ? {} : { gateReviewInstruction },
|
|
450
452
|
...options.roleTurnHost === void 0 ? {} : { roleTurnHost: options.roleTurnHost },
|
|
451
453
|
...options.hostAdapters === void 0 ? {} : { hostAdapters: options.hostAdapters },
|
|
452
|
-
...options.createRunId === void 0 ? {} : { createRunId: options.createRunId }
|
|
454
|
+
...options.createRunId === void 0 ? {} : { createRunId: options.createRunId },
|
|
455
|
+
...parentRunId === void 0 || parentRunId.trim() === "" ? {} : { correlationId: parentRunId }
|
|
453
456
|
};
|
|
454
457
|
const { packagedGateSummon } = await import("./packaged-role-registry.js");
|
|
455
458
|
const gateSummon = packagedGateSummon(options.officer);
|
|
@@ -474,13 +477,12 @@ async function summonGateOfficer(options) {
|
|
|
474
477
|
});
|
|
475
478
|
}
|
|
476
479
|
if (gateSummon === "parent-instruction") {
|
|
477
|
-
|
|
478
|
-
const correlationId = runIdFromRunDirectory(options.sourceRunDirectory);
|
|
479
|
-
if (correlationId === void 0 || correlationId.trim() === "") {
|
|
480
|
+
if (parentRunId === void 0 || parentRunId.trim() === "") {
|
|
480
481
|
throw new Error(
|
|
481
482
|
`countersign gate summon requires parent runId from sourceRunDirectory: ${options.sourceRunDirectory}`
|
|
482
483
|
);
|
|
483
484
|
}
|
|
485
|
+
const correlationId = parentRunId;
|
|
484
486
|
const { isSafePositiveTicketNumber, readBoardTicketNumber } = await import("./run-ticket-number.js");
|
|
485
487
|
const parentTicket = await readBoardTicketNumber(options.sourceRunDirectory);
|
|
486
488
|
const submittedTicket = options.submission !== null && typeof options.submission === "object" && !Array.isArray(options.submission) ? options.submission.ticketNumber : void 0;
|
|
@@ -9,7 +9,7 @@ function serializeReceipt(receipt) {
|
|
|
9
9
|
return readableGateItem(receipt);
|
|
10
10
|
}
|
|
11
11
|
function gatekeeperNonPassMessage(result) {
|
|
12
|
-
if (result.status === "continue"
|
|
12
|
+
if (result.status === "continue") {
|
|
13
13
|
return serializeReceipt(result.receipt);
|
|
14
14
|
}
|
|
15
15
|
if (result.status === "transport_failure") {
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { bookDirectOfficerRunPointer } from "./archivist-record-pointer.js";
|
|
2
|
+
import { GatekeeperDecisionError, officerConclusionReask, projectGatekeeperRun, } from "./gatekeeper-role.js";
|
|
3
|
+
/** Queue word of the latest this-terminal payload. History does not outrank it. */
|
|
4
|
+
export function latestQueueStatus(terminal) {
|
|
5
|
+
const outcome = terminal?.roleOutcome;
|
|
6
|
+
if (outcome === undefined)
|
|
7
|
+
return undefined;
|
|
8
|
+
if (outcome.kind === "audit_escalation")
|
|
9
|
+
return "escalate";
|
|
10
|
+
if (outcome.kind !== "accepted")
|
|
11
|
+
return undefined;
|
|
12
|
+
const latest = outcome.payloads?.[outcome.payloads.length - 1];
|
|
13
|
+
if (latest !== null && typeof latest === "object" && !Array.isArray(latest)) {
|
|
14
|
+
const status = latest.status;
|
|
15
|
+
if (typeof status === "string" && status.trim() !== "")
|
|
16
|
+
return status;
|
|
17
|
+
}
|
|
18
|
+
return typeof outcome.status === "string" && outcome.status.trim() !== ""
|
|
19
|
+
? outcome.status
|
|
20
|
+
: undefined;
|
|
21
|
+
}
|
|
22
|
+
export function latestQueuePayload(terminal) {
|
|
23
|
+
const outcome = terminal?.roleOutcome;
|
|
24
|
+
if (outcome === undefined)
|
|
25
|
+
return undefined;
|
|
26
|
+
if (outcome.kind !== "accepted" && outcome.kind !== "audit_escalation")
|
|
27
|
+
return undefined;
|
|
28
|
+
const payloads = outcome.payloads ?? [];
|
|
29
|
+
return payloads.length === 0 ? undefined : payloads[payloads.length - 1];
|
|
30
|
+
}
|
|
31
|
+
import { sessionFileFromPublicSummon } from "./session-assistant-usage.js";
|
|
32
|
+
/**
|
|
33
|
+
* Shared-envelope default officer summon (ADR 0018).
|
|
34
|
+
* Role modules must not own this drive seam — only project results.
|
|
35
|
+
*/
|
|
36
|
+
export function createDefaultGateOfficerSummon(options) {
|
|
37
|
+
return async (officer, sourceRunDirectory, signal, reask, submission) => {
|
|
38
|
+
const { summonGateOfficer } = await import("./public-role-summons.js");
|
|
39
|
+
return summonGateOfficer({
|
|
40
|
+
officer,
|
|
41
|
+
sourceRunDirectory,
|
|
42
|
+
cwd: options.cwd,
|
|
43
|
+
...(signal === undefined ? {} : { signal }),
|
|
44
|
+
...(reask === undefined ? {} : { reask }),
|
|
45
|
+
...(submission === undefined ? {} : { submission }),
|
|
46
|
+
...(options.home === undefined ? {} : { home: options.home }),
|
|
47
|
+
...(options.packageRoot === undefined ? {} : { packageRoot: options.packageRoot }),
|
|
48
|
+
...(options.io === undefined ? {} : { io: options.io }),
|
|
49
|
+
...(options.roleTurnHost === undefined ? {} : { roleTurnHost: options.roleTurnHost }),
|
|
50
|
+
...(options.hostAdapters === undefined ? {} : { hostAdapters: options.hostAdapters }),
|
|
51
|
+
...(options.createRunId === undefined ? {} : { createRunId: options.createRunId }),
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Book a typed pointer under parent session/auditor-roles (archivist-owned write).
|
|
57
|
+
* Offline mocks without a real session leave no nested volume (lawful zero).
|
|
58
|
+
*/
|
|
59
|
+
function bookDirectOfficerPointer(context, officer, result, summoned) {
|
|
60
|
+
if (result.status !== "converged"
|
|
61
|
+
&& result.status !== "continue"
|
|
62
|
+
&& result.status !== "escalate"
|
|
63
|
+
&& result.status !== "needs_reask"
|
|
64
|
+
&& result.status !== "transport_failure") {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const parentFile = context.sessionManager?.getSessionFile?.();
|
|
68
|
+
if (typeof parentFile !== "string" || parentFile.trim() === "")
|
|
69
|
+
return;
|
|
70
|
+
const sessionFile = sessionFileFromPublicSummon(summoned);
|
|
71
|
+
if (sessionFile === undefined) {
|
|
72
|
+
// No independent 正本 to point at — do not synthesize a parallel session.
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
bookDirectOfficerRunPointer({
|
|
76
|
+
parentSessionFile: parentFile,
|
|
77
|
+
officer,
|
|
78
|
+
sessionFile,
|
|
79
|
+
...(typeof summoned.runDirectory === "string" && summoned.runDirectory.trim() !== ""
|
|
80
|
+
? { runDirectory: summoned.runDirectory }
|
|
81
|
+
: {}),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Shared envelope: project gate, book officer pointer, map onto host actions.
|
|
86
|
+
* Review loop has no round cap (#753 no-round-cap).
|
|
87
|
+
* On converged, returns the officer snapshot (receipt + nested runId) for seat projection.
|
|
88
|
+
*/
|
|
89
|
+
export async function requireSubmissionGate(options) {
|
|
90
|
+
let reask;
|
|
91
|
+
// No round cap — end only on converged, continue/escalate, or real failure.
|
|
92
|
+
const summonOfficer = options.summonOfficer ??
|
|
93
|
+
createDefaultGateOfficerSummon({
|
|
94
|
+
cwd: options.context.cwd ?? process.cwd(),
|
|
95
|
+
});
|
|
96
|
+
for (;;) {
|
|
97
|
+
const projected = await projectGatekeeperRun({
|
|
98
|
+
context: options.context,
|
|
99
|
+
subject: options.subject,
|
|
100
|
+
summonOfficer,
|
|
101
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
102
|
+
...(options.submission === undefined ? {} : { submission: options.submission }),
|
|
103
|
+
...(reask === undefined ? {} : { reask }),
|
|
104
|
+
});
|
|
105
|
+
const gatekeeper = projected.result;
|
|
106
|
+
// Envelope-owned pointer book. Failure is host infrastructure — single face.
|
|
107
|
+
if (projected.summoned !== undefined) {
|
|
108
|
+
try {
|
|
109
|
+
bookDirectOfficerPointer(options.context, projected.officer, gatekeeper, projected.summoned);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
options.hostActions.failInfrastructure(error, options.context, options.toolCallId);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (gatekeeper.status === "converged") {
|
|
116
|
+
return {
|
|
117
|
+
status: "converged",
|
|
118
|
+
officer: projected.officer,
|
|
119
|
+
receipt: gatekeeper.receipt,
|
|
120
|
+
...(typeof gatekeeper.runId === "string" && gatekeeper.runId.trim() !== ""
|
|
121
|
+
? { runId: gatekeeper.runId }
|
|
122
|
+
: {}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (gatekeeper.status === "continue") {
|
|
126
|
+
return {
|
|
127
|
+
status: "continue",
|
|
128
|
+
officer: projected.officer,
|
|
129
|
+
receipt: gatekeeper.receipt,
|
|
130
|
+
...(typeof gatekeeper.runId === "string" && gatekeeper.runId.trim() !== ""
|
|
131
|
+
? { runId: gatekeeper.runId }
|
|
132
|
+
: {}),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (gatekeeper.status === "needs_reask") {
|
|
136
|
+
reask = officerConclusionReask(gatekeeper.receivedStatus);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (gatekeeper.status === "transport_failure") {
|
|
140
|
+
const failure = Object.assign(new Error(gatekeeper.reason), {
|
|
141
|
+
name: "GatekeeperTransportFailure",
|
|
142
|
+
...(gatekeeper.submission === undefined ? {} : { submission: gatekeeper.submission }),
|
|
143
|
+
});
|
|
144
|
+
options.hostActions.failInfrastructure(failure, options.context, options.toolCallId);
|
|
145
|
+
}
|
|
146
|
+
if (gatekeeper.status === "escalate") {
|
|
147
|
+
return {
|
|
148
|
+
status: "escalate",
|
|
149
|
+
officer: projected.officer,
|
|
150
|
+
receipt: gatekeeper.receipt,
|
|
151
|
+
...(typeof gatekeeper.runId === "string" && gatekeeper.runId.trim() !== ""
|
|
152
|
+
? { runId: gatekeeper.runId }
|
|
153
|
+
: {}),
|
|
154
|
+
...(typeof projected.summoned?.runDirectory === "string"
|
|
155
|
+
&& projected.summoned.runDirectory.trim() !== ""
|
|
156
|
+
? { runDirectory: projected.summoned.runDirectory }
|
|
157
|
+
: {}),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
// no_receipt: keep the lifecycle failure channel; continue is an ordinary
|
|
161
|
+
// nonterminal tool result above, not an exception or correctable rejection.
|
|
162
|
+
options.hostActions.bindSubmissionNonPass(options.toolCallId, gatekeeper);
|
|
163
|
+
throw new GatekeeperDecisionError(gatekeeper);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { resolveActivationLedgerHome, tryHomeFromAkRolesPath, } from "./activation-ledger-topology.js";
|
|
3
3
|
import { courtAttemptIdFromHostContext, runDirectoryFromHostContext, } from "./host-contracts.js";
|
|
4
|
-
import { isAuditEscalationProjection } from "./audit-escalation.js";
|
|
5
4
|
import { runIdFromRunDirectory } from "./run-terminal-artifacts.js";
|
|
6
5
|
import { readSitianRecords, resolveSitianRecordPathInLedger, sitianReport } from "./sitian-facade.js";
|
|
7
6
|
import { findRunDirectoryById } from "./public-cli/run-lifecycle.js";
|
|
@@ -528,32 +527,6 @@ export function createSubmissionLedgerHost(host, outputTools, failInfrastructure
|
|
|
528
527
|
}
|
|
529
528
|
// #836: ledger authority is the LLM tool-call params as-is (角色原话), never result.details.
|
|
530
529
|
// Machine facts on result.details stay on the tool-result face returned to the model.
|
|
531
|
-
if (isAuditEscalationProjection(result.details) || isAuditEscalationProjection(params)) {
|
|
532
|
-
const audit = isAuditEscalationProjection(result.details)
|
|
533
|
-
&& typeof result.details.audit === "object"
|
|
534
|
-
&& result.details.audit !== null
|
|
535
|
-
? result.details.audit
|
|
536
|
-
: undefined;
|
|
537
|
-
const closed = {
|
|
538
|
-
role,
|
|
539
|
-
kind: "audit_escalation",
|
|
540
|
-
accepted: params,
|
|
541
|
-
};
|
|
542
|
-
append({
|
|
543
|
-
type: "outcome",
|
|
544
|
-
attemptId,
|
|
545
|
-
toolCallId,
|
|
546
|
-
outcome: "audit-escalation",
|
|
547
|
-
role,
|
|
548
|
-
accepted: params,
|
|
549
|
-
...(audit !== undefined && (Object.hasOwn(audit, "receipt") || Object.hasOwn(audit, "conflicts"))
|
|
550
|
-
? { auditReceipt: Object.hasOwn(audit, "receipt") ? audit.receipt : audit.conflicts }
|
|
551
|
-
: {}),
|
|
552
|
-
...(audit !== undefined && Object.hasOwn(audit, "officer") ? { auditOfficer: audit.officer } : {}),
|
|
553
|
-
});
|
|
554
|
-
await projectClosure(closed, context);
|
|
555
|
-
return result;
|
|
556
|
-
}
|
|
557
530
|
// A continuing gate leaves the candidate recorded but has not accepted it.
|
|
558
531
|
if (result.terminate === false)
|
|
559
532
|
return result;
|
package/package.json
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import {
|
|
7
7
|
closeSync,
|
|
8
8
|
existsSync,
|
|
9
|
-
mkdirSync,
|
|
10
9
|
openSync,
|
|
11
10
|
readdirSync,
|
|
12
11
|
readFileSync,
|
|
@@ -32,6 +31,11 @@ import {
|
|
|
32
31
|
} from "./archivist-record-topology.ts";
|
|
33
32
|
import type { HostRecordSession, RecordSessionHost } from "./host-contracts.ts";
|
|
34
33
|
import { piRecordSessionHost } from "./pi/record-session-host.ts";
|
|
34
|
+
export {
|
|
35
|
+
DIRECT_OFFICER_RUN_POINTER_KIND,
|
|
36
|
+
bookDirectOfficerRunPointer,
|
|
37
|
+
} from "./archivist-record-pointer.ts";
|
|
38
|
+
export type { DirectOfficerRunPointer } from "./archivist-record-pointer.ts";
|
|
35
39
|
|
|
36
40
|
export {
|
|
37
41
|
NAVIGATOR_RECORD_KIND,
|
|
@@ -473,51 +477,3 @@ export function createRecordSessionOpen(
|
|
|
473
477
|
export function createRecordSession(options: CreateRecordSessionOptions): HostRecordSession {
|
|
474
478
|
return createRecordSessionOpen(options).session;
|
|
475
479
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
/** Typed parent-side pointer to an independent officer run 正本 (ADR 0079 / #675). */
|
|
479
|
-
export const DIRECT_OFFICER_RUN_POINTER_KIND = "direct-officer-run-pointer" as const;
|
|
480
|
-
|
|
481
|
-
export type DirectOfficerRunPointer = {
|
|
482
|
-
readonly version: 1;
|
|
483
|
-
readonly kind: typeof DIRECT_OFFICER_RUN_POINTER_KIND;
|
|
484
|
-
readonly officer: "inspector" | "notary" | "auditor" | "countersign";
|
|
485
|
-
/** Absolute path to the officer session.jsonl 正本. */
|
|
486
|
-
readonly sessionFile: string;
|
|
487
|
-
/** Officer run directory when known. */
|
|
488
|
-
readonly runDirectory?: string;
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* Book a typed pointer under parent session/auditor-roles (same nest owner as
|
|
493
|
-
* createRecordSession). Never fabricates user/assistant/toolResult rows (#675).
|
|
494
|
-
* Directory placement stays with the archivist record entry (ADR 0018 / 0065).
|
|
495
|
-
*
|
|
496
|
-
* Stable leaf per officer under one parent (#753 gate-round accounting):
|
|
497
|
-
* same-parent re-summons upsert the same pointer instead of minting N files that
|
|
498
|
-
* each re-scan the full officer session and multiply terminal gate-round counts.
|
|
499
|
-
*/
|
|
500
|
-
export function bookDirectOfficerRunPointer(options: {
|
|
501
|
-
readonly parentSessionFile: string;
|
|
502
|
-
readonly officer: "inspector" | "notary" | "auditor" | "countersign";
|
|
503
|
-
readonly sessionFile: string;
|
|
504
|
-
readonly runDirectory?: string;
|
|
505
|
-
}): DirectOfficerRunPointer {
|
|
506
|
-
const nest = join(dirname(options.parentSessionFile), "auditor-roles");
|
|
507
|
-
mkdirSync(nest, { recursive: true });
|
|
508
|
-
const pointer: DirectOfficerRunPointer = {
|
|
509
|
-
version: 1,
|
|
510
|
-
kind: DIRECT_OFFICER_RUN_POINTER_KIND,
|
|
511
|
-
officer: options.officer,
|
|
512
|
-
sessionFile: options.sessionFile,
|
|
513
|
-
...(options.runDirectory !== undefined && options.runDirectory.trim() !== ""
|
|
514
|
-
? { runDirectory: options.runDirectory }
|
|
515
|
-
: {}),
|
|
516
|
-
};
|
|
517
|
-
writeFileSync(
|
|
518
|
-
join(nest, `${options.officer}.pointer.json`),
|
|
519
|
-
`${JSON.stringify(pointer)}\n`,
|
|
520
|
-
"utf8",
|
|
521
|
-
);
|
|
522
|
-
return pointer;
|
|
523
|
-
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
|
|
4
|
+
/** Typed parent-side pointer to an independent officer run 正本 (ADR 0079 / #675). */
|
|
5
|
+
export const DIRECT_OFFICER_RUN_POINTER_KIND = "direct-officer-run-pointer" as const;
|
|
6
|
+
|
|
7
|
+
export type DirectOfficerRunPointer = {
|
|
8
|
+
readonly version: 1;
|
|
9
|
+
readonly kind: typeof DIRECT_OFFICER_RUN_POINTER_KIND;
|
|
10
|
+
readonly officer: "inspector" | "notary" | "auditor" | "countersign";
|
|
11
|
+
/** Absolute path to the officer session.jsonl 正本. */
|
|
12
|
+
readonly sessionFile: string;
|
|
13
|
+
/** Officer run directory when known. */
|
|
14
|
+
readonly runDirectory?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Book a typed pointer under parent session/auditor-roles (same nest owner as
|
|
19
|
+
* createRecordSession). Never fabricates user/assistant/toolResult rows (#675).
|
|
20
|
+
* Directory placement stays with the archivist record entry (ADR 0018 / 0065).
|
|
21
|
+
*
|
|
22
|
+
* Stable leaf per officer under one parent (#753 gate-round accounting):
|
|
23
|
+
* same-parent re-summons upsert the same pointer instead of minting N files that
|
|
24
|
+
* each re-scan the full officer session and multiply terminal gate-round counts.
|
|
25
|
+
*/
|
|
26
|
+
export function bookDirectOfficerRunPointer(options: {
|
|
27
|
+
readonly parentSessionFile: string;
|
|
28
|
+
readonly officer: "inspector" | "notary" | "auditor" | "countersign";
|
|
29
|
+
readonly sessionFile: string;
|
|
30
|
+
readonly runDirectory?: string;
|
|
31
|
+
}): DirectOfficerRunPointer {
|
|
32
|
+
const nest = join(dirname(options.parentSessionFile), "auditor-roles");
|
|
33
|
+
mkdirSync(nest, { recursive: true });
|
|
34
|
+
const pointer: DirectOfficerRunPointer = {
|
|
35
|
+
version: 1,
|
|
36
|
+
kind: DIRECT_OFFICER_RUN_POINTER_KIND,
|
|
37
|
+
officer: options.officer,
|
|
38
|
+
sessionFile: options.sessionFile,
|
|
39
|
+
...(options.runDirectory !== undefined && options.runDirectory.trim() !== ""
|
|
40
|
+
? { runDirectory: options.runDirectory }
|
|
41
|
+
: {}),
|
|
42
|
+
};
|
|
43
|
+
writeFileSync(
|
|
44
|
+
join(nest, `${options.officer}.pointer.json`),
|
|
45
|
+
`${JSON.stringify(pointer)}\n`,
|
|
46
|
+
"utf8",
|
|
47
|
+
);
|
|
48
|
+
return pointer;
|
|
49
|
+
}
|