@akagilnc/pi-workflow-roles 0.1.4035 → 0.1.4193
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 +2368 -1155
- package/dist/archivist-record-entry.js +204 -20
- package/dist/archivist-record-topology.js +36 -16
- package/dist/auditor-dossier-tool.js +2 -1
- package/dist/diarist-contracts.js +96 -0
- package/dist/doctor-auditor.js +1 -1
- package/dist/doctor-evidence.js +3 -2
- package/dist/dossier-resolution.js +21 -8
- package/dist/gatekeeper-role.js +34 -27
- package/dist/headless-host/description.js +7 -8
- package/dist/headless-host/production-host.js +2375 -1149
- package/dist/host-contracts.js +16 -0
- package/dist/inspector-contracts.js +8 -0
- package/dist/migrate-book-topology.js +278917 -0
- package/dist/navigator-public-session.js +22 -25
- package/dist/notary-source-run.js +7 -4
- package/dist/packaged-role-registry.js +1 -1
- package/dist/pi/durable-principal.js +9 -6
- package/dist/pi/role-turn-host.js +43 -13
- package/dist/public-cli/auto-resume.js +2 -5
- package/dist/public-cli/case-dossier-delivery.js +60 -1
- package/dist/public-cli/diarist-run.js +27 -25
- package/dist/public-cli/inspector-run.js +17 -3
- package/dist/public-cli/instruction-seat-run.js +17 -17
- package/dist/public-cli/invocation.js +127 -63
- package/dist/public-cli/main.js +1685 -696
- package/dist/public-cli/notary-run.js +7 -4
- package/dist/public-cli/option-definitions.js +2 -2
- package/dist/public-cli/post-admission.js +113 -71
- package/dist/public-cli/run-lifecycle.js +209 -58
- package/dist/public-cli/seat-ticket-binding.js +3 -6
- package/dist/public-cli/settlement.js +29 -10
- package/dist/public-role-summons.js +8 -0
- package/dist/role-run-placement.js +84 -0
- package/dist/role-run-relocation.js +358 -0
- package/dist/run-terminal-artifacts.js +2 -1
- package/dist/run-ticket-number.js +51 -32
- package/dist/sitian-appender.js +11 -26
- package/dist/submission-ledger.js +106 -25
- package/dist/ticket-provenance.js +3 -1
- package/dist/user-dialogue-stdin.js +33 -0
- package/extensions/role-runtime.ts +1 -0
- package/package.json +3 -2
- package/resources/836-deleted-machine-instruction-inventory.md +1 -1
- package/scripts/build-package.mjs +34 -2
- package/souls/fixer.md +3 -2
- package/src/analyst-ledger.ts +14 -15
- package/src/archivist-record-entry.ts +285 -38
- package/src/archivist-record-topology.ts +43 -18
- package/src/auditor-dossier-tool.ts +2 -1
- package/src/book-topology-migration-placement.ts +493 -0
- package/src/book-topology-migration.ts +220 -0
- package/src/book-topology-migrators-t11.ts +856 -0
- package/src/book-topology-mixed-volume-migrators.ts +470 -0
- package/src/book-topology-partition-migrators.ts +25 -0
- package/src/book-topology-record-class-migrators.ts +779 -0
- package/src/book-topology-runs-migrator.ts +244 -0
- package/src/diarist-contracts.ts +116 -0
- package/src/diarist-role.ts +6 -0
- package/src/diarist.ts +2 -0
- package/src/doctor-auditor.ts +1 -1
- package/src/doctor-evidence.ts +4 -1
- package/src/doctor-role.ts +1 -1
- package/src/dossier-resolution.ts +26 -8
- package/src/external-host-turn-loop.ts +9 -0
- package/src/gatekeeper-pass-envelope.ts +6 -0
- package/src/gatekeeper-role.ts +47 -28
- package/src/headless-host/description.ts +8 -11
- package/src/headless-host/role-turn-host.ts +20 -5
- package/src/host-contracts.ts +22 -3
- package/src/inspector-contracts.ts +7 -0
- package/src/judge-role.ts +4 -0
- package/src/navigator-public-session.ts +33 -30
- package/src/navigator-work-context.ts +3 -4
- package/src/notary-role.ts +1 -0
- package/src/notary-source-run.ts +9 -4
- package/src/packaged-role-registry.ts +1 -1
- package/src/pi/adapter.ts +19 -4
- package/src/pi/durable-principal.ts +9 -13
- package/src/pi/role-turn-host.ts +48 -14
- package/src/public-cli/auto-resume.ts +2 -6
- package/src/public-cli/case-dossier-delivery.ts +86 -1
- package/src/public-cli/countersign-run.ts +182 -32
- package/src/public-cli/diarist-run.ts +34 -26
- package/src/public-cli/inspector-run.ts +26 -2
- package/src/public-cli/instruction-seat-run.ts +24 -20
- package/src/public-cli/invocation.ts +194 -58
- package/src/public-cli/notary-run.ts +13 -4
- package/src/public-cli/option-definitions.ts +2 -2
- package/src/public-cli/post-admission.ts +147 -78
- package/src/public-cli/run-lifecycle.ts +258 -65
- package/src/public-cli/seat-ticket-binding.ts +3 -6
- package/src/public-cli/settlement.ts +38 -10
- package/src/public-role-summons.ts +31 -7
- package/src/role-envelope.ts +9 -30
- package/src/role-run-placement.ts +128 -0
- package/src/role-run-relocation.ts +491 -0
- package/src/role-runtime-dependencies.ts +1 -0
- package/src/role-runtime.ts +106 -24
- package/src/run-terminal-artifacts.ts +3 -1
- package/src/run-ticket-number.ts +80 -20
- package/src/sitian-appender.ts +25 -28
- package/src/submission-ledger.ts +148 -30
- package/src/ticket-provenance.ts +4 -1
- package/src/ticket-trajectory.ts +22 -35
- package/src/user-dialogue-stdin.ts +37 -0
- package/src/worker-role.ts +8 -4
- package/src/worker-submission-gates.ts +6 -6
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single authority for rewriting durable run pages after a run directory moves.
|
|
3
|
+
* Live unbound→ticket relocate and book-topology migration both call this.
|
|
4
|
+
*
|
|
5
|
+
* Scope is typed machine-consumed path fields only — never free text, never
|
|
6
|
+
* frozen attachment/artifact bytes. Nested walk is confined to package-owned
|
|
7
|
+
* `session/` seams.
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync } from "node:fs";
|
|
10
|
+
import { readdir, readFile, writeFile } from "node:fs/promises";
|
|
11
|
+
import { join, sep } from "node:path";
|
|
12
|
+
|
|
13
|
+
import { AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE } from "./compliance-transport.ts";
|
|
14
|
+
|
|
15
|
+
const ADMITTED_PAGE_FIELDS = [
|
|
16
|
+
"runDirectory",
|
|
17
|
+
"admittedRequestPath",
|
|
18
|
+
"sessionDirectory",
|
|
19
|
+
"sessionFile",
|
|
20
|
+
"taskPath",
|
|
21
|
+
"packetPath",
|
|
22
|
+
"prerequisitesPath",
|
|
23
|
+
"requestManifestPath",
|
|
24
|
+
"mergerInputPath",
|
|
25
|
+
"sourceRunPath",
|
|
26
|
+
] as const;
|
|
27
|
+
|
|
28
|
+
const INVOCATION_PAGE_FIELDS = [
|
|
29
|
+
"runDirectory",
|
|
30
|
+
"sessionDirectory",
|
|
31
|
+
"sessionFile",
|
|
32
|
+
] as const;
|
|
33
|
+
|
|
34
|
+
const RUN_STATE_PAGE_FIELDS = [
|
|
35
|
+
"runDirectory",
|
|
36
|
+
"admittedRequestPath",
|
|
37
|
+
"sessionDirectory",
|
|
38
|
+
"sessionFile",
|
|
39
|
+
] as const;
|
|
40
|
+
|
|
41
|
+
const SOURCE_RUN_LOCATOR_FIELDS = ["runDirectory"] as const;
|
|
42
|
+
const SUMMONS_PATH_FIELDS = ["sourceRunPath"] as const;
|
|
43
|
+
const CURRENT_SESSION_FIELDS = ["sessionFile"] as const;
|
|
44
|
+
const OFFICER_POINTER_FIELDS = ["sessionFile", "runDirectory"] as const;
|
|
45
|
+
const SITIAN_RECORD_FIELDS = ["sessionParent"] as const;
|
|
46
|
+
const SESSION_HEADER_FIELDS = ["parentSession"] as const;
|
|
47
|
+
const BINDING_PARENT_FIELDS = ["sessionFile"] as const;
|
|
48
|
+
|
|
49
|
+
export type RunDirectoryPathRewrite = {
|
|
50
|
+
readonly oldRunDirectory: string;
|
|
51
|
+
readonly newRunDirectory: string;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
function isEnoent(error: unknown): boolean {
|
|
55
|
+
return (
|
|
56
|
+
error instanceof Error &&
|
|
57
|
+
"code" in error &&
|
|
58
|
+
(error as NodeJS.ErrnoException).code === "ENOENT"
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Rewrite one path value that points at or under oldRunDirectory. */
|
|
63
|
+
export function rewriteRunDirectoryPathValue(
|
|
64
|
+
value: unknown,
|
|
65
|
+
oldRunDirectory: string,
|
|
66
|
+
newRunDirectory: string,
|
|
67
|
+
): unknown {
|
|
68
|
+
if (typeof value !== "string") return value;
|
|
69
|
+
if (value === oldRunDirectory) return newRunDirectory;
|
|
70
|
+
const prefix = `${oldRunDirectory}${sep}`;
|
|
71
|
+
if (value.startsWith(prefix)) {
|
|
72
|
+
return `${newRunDirectory}${value.slice(oldRunDirectory.length)}`;
|
|
73
|
+
}
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Apply the longest matching oldRunDirectory rewrite. Longer keys win so a
|
|
79
|
+
* nested run path is not partially claimed by a shorter sibling prefix.
|
|
80
|
+
*/
|
|
81
|
+
export function rewriteRunDirectoryPathValueAgainstRewrites(
|
|
82
|
+
value: unknown,
|
|
83
|
+
rewrites: readonly RunDirectoryPathRewrite[],
|
|
84
|
+
): unknown {
|
|
85
|
+
if (typeof value !== "string" || rewrites.length === 0) return value;
|
|
86
|
+
let best: RunDirectoryPathRewrite | undefined;
|
|
87
|
+
for (const rewrite of rewrites) {
|
|
88
|
+
if (
|
|
89
|
+
value === rewrite.oldRunDirectory ||
|
|
90
|
+
value.startsWith(`${rewrite.oldRunDirectory}${sep}`)
|
|
91
|
+
) {
|
|
92
|
+
if (
|
|
93
|
+
best === undefined ||
|
|
94
|
+
rewrite.oldRunDirectory.length > best.oldRunDirectory.length
|
|
95
|
+
) {
|
|
96
|
+
best = rewrite;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (best === undefined) return value;
|
|
101
|
+
return rewriteRunDirectoryPathValue(
|
|
102
|
+
value,
|
|
103
|
+
best.oldRunDirectory,
|
|
104
|
+
best.newRunDirectory,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** In-place rewrite of selected fields on one record (single pair). */
|
|
109
|
+
export function rewriteRunDirectoryPathFields(
|
|
110
|
+
record: Record<string, unknown>,
|
|
111
|
+
fields: readonly string[],
|
|
112
|
+
oldRunDirectory: string,
|
|
113
|
+
newRunDirectory: string,
|
|
114
|
+
): void {
|
|
115
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(record, fields, [
|
|
116
|
+
{ oldRunDirectory, newRunDirectory },
|
|
117
|
+
]);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** In-place rewrite of selected fields against a rewrite set. */
|
|
121
|
+
export function rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
122
|
+
record: Record<string, unknown>,
|
|
123
|
+
fields: readonly string[],
|
|
124
|
+
rewrites: readonly RunDirectoryPathRewrite[],
|
|
125
|
+
): void {
|
|
126
|
+
for (const field of fields) {
|
|
127
|
+
if (field in record) {
|
|
128
|
+
record[field] = rewriteRunDirectoryPathValueAgainstRewrites(
|
|
129
|
+
record[field],
|
|
130
|
+
rewrites,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function collectRewrites(input: {
|
|
137
|
+
readonly oldRunDirectory: string;
|
|
138
|
+
readonly newRunDirectory: string;
|
|
139
|
+
readonly crossRunRewrites?: readonly RunDirectoryPathRewrite[];
|
|
140
|
+
}): readonly RunDirectoryPathRewrite[] {
|
|
141
|
+
const own: RunDirectoryPathRewrite = {
|
|
142
|
+
oldRunDirectory: input.oldRunDirectory,
|
|
143
|
+
newRunDirectory: input.newRunDirectory,
|
|
144
|
+
};
|
|
145
|
+
if (
|
|
146
|
+
input.crossRunRewrites === undefined ||
|
|
147
|
+
input.crossRunRewrites.length === 0
|
|
148
|
+
) {
|
|
149
|
+
return [own];
|
|
150
|
+
}
|
|
151
|
+
// Own pair first; duplicates of the same old path keep the first (own) target.
|
|
152
|
+
const seen = new Set<string>([own.oldRunDirectory]);
|
|
153
|
+
const out: RunDirectoryPathRewrite[] = [own];
|
|
154
|
+
for (const rewrite of input.crossRunRewrites) {
|
|
155
|
+
if (seen.has(rewrite.oldRunDirectory)) continue;
|
|
156
|
+
seen.add(rewrite.oldRunDirectory);
|
|
157
|
+
out.push(rewrite);
|
|
158
|
+
}
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
163
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Typed notary/source-run locator: only runDirectory is a path. */
|
|
167
|
+
function rewriteSourceRunLocator(
|
|
168
|
+
value: unknown,
|
|
169
|
+
rewrites: readonly RunDirectoryPathRewrite[],
|
|
170
|
+
): void {
|
|
171
|
+
if (!isPlainObject(value)) return;
|
|
172
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
173
|
+
value,
|
|
174
|
+
SOURCE_RUN_LOCATOR_FIELDS,
|
|
175
|
+
rewrites,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Same-ticket summons materials: typed source-run + frozen attachment pointers. */
|
|
180
|
+
function rewriteSummonsMaterials(
|
|
181
|
+
value: unknown,
|
|
182
|
+
rewrites: readonly RunDirectoryPathRewrite[],
|
|
183
|
+
): void {
|
|
184
|
+
if (!isPlainObject(value)) return;
|
|
185
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
186
|
+
value,
|
|
187
|
+
SUMMONS_PATH_FIELDS,
|
|
188
|
+
rewrites,
|
|
189
|
+
);
|
|
190
|
+
rewriteSourceRunLocator(value.sourceRun, rewrites);
|
|
191
|
+
// Bare-resume frozen attachment pointers (string[]), not free text / bytes.
|
|
192
|
+
if (Array.isArray(value.attachmentPaths)) {
|
|
193
|
+
value.attachmentPaths = value.attachmentPaths.map((path) =>
|
|
194
|
+
rewriteRunDirectoryPathValueAgainstRewrites(path, rewrites),
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function rewriteJsonObjectFile(
|
|
200
|
+
path: string,
|
|
201
|
+
fields: readonly string[],
|
|
202
|
+
rewrites: readonly RunDirectoryPathRewrite[],
|
|
203
|
+
): Promise<void> {
|
|
204
|
+
if (!existsSync(path)) return;
|
|
205
|
+
const page = JSON.parse(await readFile(path, "utf8")) as unknown;
|
|
206
|
+
if (!isPlainObject(page)) return;
|
|
207
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(page, fields, rewrites);
|
|
208
|
+
await writeFile(path, `${JSON.stringify(page, null, 2)}\n`, "utf8");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function rewriteOfficerPointerFile(
|
|
212
|
+
path: string,
|
|
213
|
+
rewrites: readonly RunDirectoryPathRewrite[],
|
|
214
|
+
): Promise<void> {
|
|
215
|
+
if (!existsSync(path)) return;
|
|
216
|
+
const page = JSON.parse(await readFile(path, "utf8")) as unknown;
|
|
217
|
+
if (!isPlainObject(page)) return;
|
|
218
|
+
// Only the typed direct-officer pointer shape — never arbitrary .pointer.json.
|
|
219
|
+
if (page.kind !== "direct-officer-run-pointer") return;
|
|
220
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
221
|
+
page,
|
|
222
|
+
OFFICER_POINTER_FIELDS,
|
|
223
|
+
rewrites,
|
|
224
|
+
);
|
|
225
|
+
await writeFile(path, `${JSON.stringify(page)}\n`, "utf8");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function rewriteSitianRecordsJsonl(
|
|
229
|
+
path: string,
|
|
230
|
+
rewrites: readonly RunDirectoryPathRewrite[],
|
|
231
|
+
): Promise<void> {
|
|
232
|
+
if (!existsSync(path)) return;
|
|
233
|
+
const raw = await readFile(path, "utf8");
|
|
234
|
+
if (raw.length === 0) return;
|
|
235
|
+
const endsWithNewline = raw.endsWith("\n");
|
|
236
|
+
const lines = raw.split("\n");
|
|
237
|
+
// split keeps a trailing empty slot when file ends with \n — preserve it.
|
|
238
|
+
let changed = false;
|
|
239
|
+
const out: string[] = [];
|
|
240
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
241
|
+
const line = lines[i]!;
|
|
242
|
+
if (line === "" && i === lines.length - 1 && endsWithNewline) {
|
|
243
|
+
out.push("");
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (line.trim() === "") {
|
|
247
|
+
out.push(line);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
let parsed: unknown;
|
|
251
|
+
try {
|
|
252
|
+
parsed = JSON.parse(line);
|
|
253
|
+
} catch {
|
|
254
|
+
out.push(line);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (!isPlainObject(parsed)) {
|
|
258
|
+
out.push(line);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
if (!("sessionParent" in parsed)) {
|
|
262
|
+
out.push(line);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
const before = parsed.sessionParent;
|
|
266
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
267
|
+
parsed,
|
|
268
|
+
SITIAN_RECORD_FIELDS,
|
|
269
|
+
rewrites,
|
|
270
|
+
);
|
|
271
|
+
if (parsed.sessionParent !== before) changed = true;
|
|
272
|
+
out.push(JSON.stringify(parsed));
|
|
273
|
+
}
|
|
274
|
+
if (!changed) return;
|
|
275
|
+
const body = out.join("\n");
|
|
276
|
+
await writeFile(
|
|
277
|
+
path,
|
|
278
|
+
endsWithNewline && !body.endsWith("\n") ? `${body}\n` : body,
|
|
279
|
+
"utf8",
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Session transcript typed locators only: header.parentSession and
|
|
285
|
+
* ak_auditor_parent_attempt_binding data.parent.sessionFile. Never message text.
|
|
286
|
+
*/
|
|
287
|
+
async function rewriteSessionTranscriptBindings(
|
|
288
|
+
path: string,
|
|
289
|
+
rewrites: readonly RunDirectoryPathRewrite[],
|
|
290
|
+
): Promise<void> {
|
|
291
|
+
if (!existsSync(path)) return;
|
|
292
|
+
const raw = await readFile(path, "utf8");
|
|
293
|
+
if (raw.length === 0) return;
|
|
294
|
+
const endsWithNewline = raw.endsWith("\n");
|
|
295
|
+
const lines = raw.split("\n");
|
|
296
|
+
let changed = false;
|
|
297
|
+
const out: string[] = [];
|
|
298
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
299
|
+
const line = lines[i]!;
|
|
300
|
+
if (line === "" && i === lines.length - 1 && endsWithNewline) {
|
|
301
|
+
out.push("");
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
if (line.trim() === "") {
|
|
305
|
+
out.push(line);
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
let parsed: unknown;
|
|
309
|
+
try {
|
|
310
|
+
parsed = JSON.parse(line);
|
|
311
|
+
} catch {
|
|
312
|
+
out.push(line);
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (!isPlainObject(parsed)) {
|
|
316
|
+
out.push(line);
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
let lineChanged = false;
|
|
320
|
+
if (parsed.type === "session" && "parentSession" in parsed) {
|
|
321
|
+
const before = parsed.parentSession;
|
|
322
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
323
|
+
parsed,
|
|
324
|
+
SESSION_HEADER_FIELDS,
|
|
325
|
+
rewrites,
|
|
326
|
+
);
|
|
327
|
+
if (parsed.parentSession !== before) lineChanged = true;
|
|
328
|
+
} else if (
|
|
329
|
+
parsed.type === "custom" &&
|
|
330
|
+
parsed.customType === AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE &&
|
|
331
|
+
isPlainObject(parsed.data) &&
|
|
332
|
+
isPlainObject(parsed.data.parent)
|
|
333
|
+
) {
|
|
334
|
+
const parent = parsed.data.parent;
|
|
335
|
+
const before = parent.sessionFile;
|
|
336
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
337
|
+
parent,
|
|
338
|
+
BINDING_PARENT_FIELDS,
|
|
339
|
+
rewrites,
|
|
340
|
+
);
|
|
341
|
+
if (parent.sessionFile !== before) lineChanged = true;
|
|
342
|
+
}
|
|
343
|
+
if (lineChanged) changed = true;
|
|
344
|
+
out.push(lineChanged ? JSON.stringify(parsed) : line);
|
|
345
|
+
}
|
|
346
|
+
if (!changed) return;
|
|
347
|
+
const body = out.join("\n");
|
|
348
|
+
await writeFile(
|
|
349
|
+
path,
|
|
350
|
+
endsWithNewline && !body.endsWith("\n") ? `${body}\n` : body,
|
|
351
|
+
"utf8",
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Package-owned nested seams under session/ only:
|
|
357
|
+
* current-session.json, direct-officer *.pointer.json, sitian records.jsonl,
|
|
358
|
+
* session transcript typed parent bindings. Never walks attachments/ or artifacts/.
|
|
359
|
+
*/
|
|
360
|
+
async function rewriteNestedMachinePathPages(
|
|
361
|
+
pagesDirectory: string,
|
|
362
|
+
rewrites: readonly RunDirectoryPathRewrite[],
|
|
363
|
+
): Promise<void> {
|
|
364
|
+
const sessionRoot = join(pagesDirectory, "session");
|
|
365
|
+
async function walk(directory: string): Promise<void> {
|
|
366
|
+
let entries;
|
|
367
|
+
try {
|
|
368
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
369
|
+
} catch (error) {
|
|
370
|
+
if (isEnoent(error)) return;
|
|
371
|
+
throw error;
|
|
372
|
+
}
|
|
373
|
+
for (const entry of entries) {
|
|
374
|
+
const path = join(directory, entry.name);
|
|
375
|
+
if (entry.isDirectory()) {
|
|
376
|
+
await walk(path);
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if (!entry.isFile()) continue;
|
|
380
|
+
if (entry.name === "current-session.json") {
|
|
381
|
+
await rewriteJsonObjectFile(path, CURRENT_SESSION_FIELDS, rewrites);
|
|
382
|
+
} else if (entry.name.endsWith(".pointer.json")) {
|
|
383
|
+
await rewriteOfficerPointerFile(path, rewrites);
|
|
384
|
+
} else if (entry.name === "records.jsonl") {
|
|
385
|
+
await rewriteSitianRecordsJsonl(path, rewrites);
|
|
386
|
+
} else if (entry.name.endsWith(".jsonl")) {
|
|
387
|
+
await rewriteSessionTranscriptBindings(path, rewrites);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
await walk(sessionRoot);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Rewrite admitted-request / invocation / run-state path fields (attachment
|
|
396
|
+
* frozenPath / summons.attachmentPaths pointers only), then nested package-owned
|
|
397
|
+
* session seams, after the run directory has already been moved or copied.
|
|
398
|
+
* `pagesDirectory` is where the pages now live; path strings still naming an
|
|
399
|
+
* old run directory become the matching new directory. `crossRunRewrites`
|
|
400
|
+
* covers officer/parent pointers that landed under a different final placement.
|
|
401
|
+
*/
|
|
402
|
+
export async function rewriteRoleRunDurablePages(input: {
|
|
403
|
+
readonly pagesDirectory: string;
|
|
404
|
+
readonly oldRunDirectory: string;
|
|
405
|
+
readonly newRunDirectory: string;
|
|
406
|
+
readonly crossRunRewrites?: readonly RunDirectoryPathRewrite[];
|
|
407
|
+
}): Promise<void> {
|
|
408
|
+
const { pagesDirectory } = input;
|
|
409
|
+
const rewrites = collectRewrites(input);
|
|
410
|
+
|
|
411
|
+
const admittedPath = join(pagesDirectory, "admitted-request.json");
|
|
412
|
+
if (existsSync(admittedPath)) {
|
|
413
|
+
const page = JSON.parse(await readFile(admittedPath, "utf8")) as Record<
|
|
414
|
+
string,
|
|
415
|
+
unknown
|
|
416
|
+
>;
|
|
417
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
418
|
+
page,
|
|
419
|
+
ADMITTED_PAGE_FIELDS,
|
|
420
|
+
rewrites,
|
|
421
|
+
);
|
|
422
|
+
// Nested notary typed locator — same value family as top-level sourceRunPath.
|
|
423
|
+
rewriteSourceRunLocator(page.sourceRun, rewrites);
|
|
424
|
+
if (Array.isArray(page.attachments)) {
|
|
425
|
+
for (const attachment of page.attachments) {
|
|
426
|
+
if (isPlainObject(attachment)) {
|
|
427
|
+
// Pointer only — never open or mutate frozen attachment bytes.
|
|
428
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
429
|
+
attachment,
|
|
430
|
+
["frozenPath"],
|
|
431
|
+
rewrites,
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
// Nested principal wire (sessionDirectory/sessionFile) when present.
|
|
437
|
+
if (isPlainObject(page.principal)) {
|
|
438
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
439
|
+
page.principal,
|
|
440
|
+
["sessionDirectory", "sessionFile"],
|
|
441
|
+
rewrites,
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
await writeFile(admittedPath, `${JSON.stringify(page, null, 2)}\n`, "utf8");
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const invocationPath = join(pagesDirectory, "invocation.json");
|
|
448
|
+
if (existsSync(invocationPath)) {
|
|
449
|
+
const page = JSON.parse(await readFile(invocationPath, "utf8")) as Record<
|
|
450
|
+
string,
|
|
451
|
+
unknown
|
|
452
|
+
>;
|
|
453
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
454
|
+
page,
|
|
455
|
+
INVOCATION_PAGE_FIELDS,
|
|
456
|
+
rewrites,
|
|
457
|
+
);
|
|
458
|
+
await writeFile(
|
|
459
|
+
invocationPath,
|
|
460
|
+
`${JSON.stringify(page, null, 2)}\n`,
|
|
461
|
+
"utf8",
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const statePath = join(pagesDirectory, "run-state.json");
|
|
466
|
+
if (existsSync(statePath)) {
|
|
467
|
+
const page = JSON.parse(await readFile(statePath, "utf8")) as Record<
|
|
468
|
+
string,
|
|
469
|
+
unknown
|
|
470
|
+
>;
|
|
471
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
472
|
+
page,
|
|
473
|
+
RUN_STATE_PAGE_FIELDS,
|
|
474
|
+
rewrites,
|
|
475
|
+
);
|
|
476
|
+
if (isPlainObject(page.principal)) {
|
|
477
|
+
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
478
|
+
page.principal,
|
|
479
|
+
["sessionDirectory", "sessionFile"],
|
|
480
|
+
rewrites,
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
// Open court summons: source-run locators + frozen attachment path pointers.
|
|
484
|
+
if (isPlainObject(page.currentCourt)) {
|
|
485
|
+
rewriteSummonsMaterials(page.currentCourt.summons, rewrites);
|
|
486
|
+
}
|
|
487
|
+
await writeFile(statePath, `${JSON.stringify(page, null, 2)}\n`, "utf8");
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
await rewriteNestedMachinePathPages(pagesDirectory, rewrites);
|
|
491
|
+
}
|
|
@@ -26,6 +26,7 @@ export function createRoleRuntimeDependencies(packageRoot: string): RoleRuntimeD
|
|
|
26
26
|
const doctorAuditor = createPiDoctorAuditor();
|
|
27
27
|
const navigatorSessionFactory = createNativeNavigatorSessionFactory();
|
|
28
28
|
return {
|
|
29
|
+
packageRoot,
|
|
29
30
|
loadJudgeSoul: () => loadMainRoleSessionMaterials("judge"),
|
|
30
31
|
loadFixerSoul: () => loadMainRoleSessionMaterials("fixer"),
|
|
31
32
|
loadFixPacket: (path) => readFile(path, "utf8"),
|