@akagilnc/pi-workflow-roles 0.1.5504 → 0.1.5575

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.
Files changed (89) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/acp-host/production-host.js +5965 -5994
  4. package/dist/analyst-gate-cycles-read.js +23 -55
  5. package/dist/analyst-ledger.js +21 -35
  6. package/dist/analyst-metric-families/acceptance-success-rework.js +25 -1
  7. package/dist/analyst-metric-families/round-timeline.js +2 -5
  8. package/dist/archivist-record-pointer.js +38 -30
  9. package/dist/auditor-dossier-tool.js +4 -3
  10. package/dist/auditor-soul.js +3 -11
  11. package/dist/doctor-evidence.js +3 -3
  12. package/dist/engine-detour-usage.js +6 -14
  13. package/dist/headless-host/production-host.js +5316 -5324
  14. package/dist/host-descriptions.js +0 -4
  15. package/dist/host-session-record.js +266 -267
  16. package/dist/migrate-book-topology.js +1314 -696
  17. package/dist/notary-source-run.js +1 -1
  18. package/dist/packaged-role-registry.js +5 -44
  19. package/dist/pi/role-turn-host.js +53 -3
  20. package/dist/public-cli/auto-resume.js +8 -47
  21. package/dist/public-cli/instruction-seat-run.js +5 -3
  22. package/dist/public-cli/invocation.js +51 -56
  23. package/dist/public-cli/main.js +1916 -1748
  24. package/dist/public-cli/post-admission.js +96 -55
  25. package/dist/public-cli/run-lifecycle.js +27 -53
  26. package/dist/public-cli/settlement.js +157 -302
  27. package/dist/role-run-placement.js +1 -11
  28. package/dist/role-run-relocation.js +72 -50
  29. package/dist/run-dossier-files.js +30 -0
  30. package/dist/run-dossier.js +322 -0
  31. package/dist/run-terminal-artifacts.js +68 -197
  32. package/dist/run-ticket-number.js +16 -3
  33. package/dist/session-identity.js +49 -18
  34. package/dist/sitian-appender.js +13 -16
  35. package/dist/sitian-facade.js +11 -0
  36. package/dist/sitian-reader.js +44 -29
  37. package/dist/submission-gate.js +1 -1
  38. package/dist/ticket-provenance.js +22 -7
  39. package/package.json +1 -1
  40. package/scripts/build-package.mjs +2 -0
  41. package/src/acp-host/production-host.ts +1 -1
  42. package/src/acp-host/role-turn-host.ts +9 -2
  43. package/src/analyst-gate-cycles-read.ts +26 -60
  44. package/src/analyst-ledger.ts +27 -42
  45. package/src/analyst-metric-families/acceptance-success-rework.ts +24 -1
  46. package/src/analyst-metric-families/gate-cycles.ts +1 -1
  47. package/src/analyst-metric-families/round-timeline.ts +2 -5
  48. package/src/analyst-page.ts +3 -3
  49. package/src/archivist-record-entry.ts +2 -2
  50. package/src/archivist-record-pointer.ts +50 -36
  51. package/src/auditor-dossier-tool.ts +7 -5
  52. package/src/auditor-soul.ts +3 -9
  53. package/src/book-topology-migration-placement.ts +16 -3
  54. package/src/book-topology-record-class-migrators.ts +4 -2
  55. package/src/book-topology-runs-migrator.ts +6 -3
  56. package/src/doctor-contracts.ts +1 -1
  57. package/src/doctor-evidence.ts +2 -2
  58. package/src/engine-detour-tool.ts +1 -1
  59. package/src/engine-detour-usage.ts +6 -16
  60. package/src/external-host-turn-loop.ts +25 -0
  61. package/src/headless-host/production-host.ts +1 -4
  62. package/src/headless-host/role-turn-host.ts +54 -20
  63. package/src/host-descriptions.ts +0 -6
  64. package/src/host-session-record.ts +224 -140
  65. package/src/notary-source-run.ts +1 -1
  66. package/src/packaged-role-registry.ts +1 -50
  67. package/src/pi/adapter.ts +50 -7
  68. package/src/pi/role-turn-host.ts +51 -3
  69. package/src/public-cli/auto-resume.ts +7 -43
  70. package/src/public-cli/instruction-seat-run.ts +9 -2
  71. package/src/public-cli/invocation.ts +61 -87
  72. package/src/public-cli/post-admission.ts +111 -72
  73. package/src/public-cli/run-lifecycle.ts +27 -72
  74. package/src/public-cli/settlement.ts +193 -418
  75. package/src/public-cli/terminal.ts +1 -1
  76. package/src/role-run-placement.ts +2 -13
  77. package/src/role-run-relocation.ts +71 -68
  78. package/src/role-runtime.ts +6 -10
  79. package/src/run-dossier-files.ts +33 -0
  80. package/src/run-dossier.ts +357 -0
  81. package/src/run-terminal-artifacts.ts +83 -237
  82. package/src/run-ticket-number.ts +41 -11
  83. package/src/session-identity.ts +52 -20
  84. package/src/sitian-appender.ts +17 -18
  85. package/src/sitian-facade.ts +22 -0
  86. package/src/sitian-reader.ts +62 -32
  87. package/src/submission-gate.ts +1 -1
  88. package/src/ticket-provenance.ts +20 -7
  89. package/src/ticket-trajectory.ts +10 -10
@@ -18,7 +18,7 @@ export function encodeTerminalField(value: string): string {
18
18
  }
19
19
 
20
20
  export type TerminalArtifactRef = {
21
- kind: "report" | "evidence" | "error";
21
+ kind: "report" | "error";
22
22
  /** Openable local reference (path). Layout is private; the ref value is the contract. */
23
23
  path: string;
24
24
  };
@@ -17,7 +17,6 @@ export type RoleRunPlacement = {
17
17
  readonly runDirectory: string;
18
18
  readonly sessionDirectory: string;
19
19
  readonly sessionFile: string;
20
- readonly artifactsDirectory: string;
21
20
  readonly attachmentsDirectory: string;
22
21
  };
23
22
 
@@ -150,16 +149,10 @@ export function roleRunPlacement(
150
149
  runDirectory,
151
150
  sessionDirectory,
152
151
  sessionFile: sessionFileIn(sessionDirectory),
153
- artifactsDirectory: roleRunArtifactsDirectory(runDirectory),
154
152
  attachmentsDirectory: join(runDirectory, "attachments"),
155
153
  };
156
154
  }
157
155
 
158
- /** The single artifacts subpath definition for new and resumed role runs. */
159
- export function roleRunArtifactsDirectory(runDirectory: string): string {
160
- return join(runDirectory, "artifacts");
161
- }
162
-
163
156
  /** Canonical unbound placement: a run directory under `<book>/unbound/runs/`. */
164
157
  export function isUnboundRunDirectory(runDirectory: string): boolean {
165
158
  return runDirectory.replaceAll("\\", "/").includes("/unbound/runs/");
@@ -177,10 +170,6 @@ export function ensureRoleRunPlacement(
177
170
  ledgerHome: string,
178
171
  placement: RoleRunPlacement,
179
172
  ): void {
180
- for (const directory of [
181
- placement.sessionDirectory,
182
- placement.attachmentsDirectory,
183
- ]) {
184
- ensureRoleRunDirectory(ledgerHome, directory);
185
- }
173
+ // attachments/ appears only when something is frozen into it.
174
+ ensureRoleRunDirectory(ledgerHome, placement.sessionDirectory);
186
175
  }
@@ -6,7 +6,6 @@
6
6
  * frozen attachment/artifact bytes. Nested walk is confined to package-owned
7
7
  * `session/` seams.
8
8
  */
9
- import { existsSync } from "node:fs";
10
9
  import { readdir, readFile, writeFile } from "node:fs/promises";
11
10
  import { dirname, join, sep } from "node:path";
12
11
 
@@ -16,11 +15,12 @@ import { findRoleRunDirectory, roleRunPlacement } from "./role-run-placement.ts"
16
15
 
17
16
  import { AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE } from "./compliance-transport.ts";
18
17
 
18
+ import { readPageSync, updateSectionSync } from "./run-dossier.ts";
19
19
  import { isRecord, isEnoent } from "./unknown-value.ts";
20
20
 
21
21
  const ADMITTED_PAGE_FIELDS = [
22
22
  "runDirectory",
23
- "admittedRequestPath",
23
+ "admittedRequestPath", // pre-#1161 pages only
24
24
  "sessionDirectory",
25
25
  "sessionFile",
26
26
  "taskPath",
@@ -39,7 +39,7 @@ const INVOCATION_PAGE_FIELDS = [
39
39
 
40
40
  const RUN_STATE_PAGE_FIELDS = [
41
41
  "runDirectory",
42
- "admittedRequestPath",
42
+ "admittedRequestPath", // pre-#1161 pages only
43
43
  "sessionDirectory",
44
44
  "sessionFile",
45
45
  ] as const;
@@ -245,32 +245,29 @@ export function rewriteAdmittedRoleRunPage(
245
245
  }
246
246
  }
247
247
 
248
- async function rewriteOfficerPointerFile(
248
+ /** Pre-#1161 `session/auditor-roles/<officer>.pointer.json` (migrator input only). */
249
+ async function rewriteLegacyOfficerPointerFile(
249
250
  path: string,
250
251
  rewrites: readonly RunDirectoryPathRewrite[],
251
252
  ): Promise<void> {
252
- if (!existsSync(path)) return;
253
253
  const page = JSON.parse(await readFile(path, "utf8")) as unknown;
254
- if (!isRecord(page)) return;
255
- // Only the typed direct-officer pointer shape — never arbitrary .pointer.json.
256
- if (page.kind !== "direct-officer-run-pointer") return;
254
+ if (!isRecord(page) || page.kind !== "direct-officer-run-pointer") return;
257
255
  const before = JSON.stringify(page);
258
- rewriteRunDirectoryPathFieldsAgainstRewrites(
259
- page,
260
- OFFICER_POINTER_FIELDS,
261
- rewrites,
262
- );
263
- if (JSON.stringify(page) !== before) {
264
- await writeFile(path, `${JSON.stringify(page)}\n`, "utf8");
265
- }
256
+ rewriteRunDirectoryPathFieldsAgainstRewrites(page, OFFICER_POINTER_FIELDS, rewrites);
257
+ if (JSON.stringify(page) !== before) await writeFile(path, `${JSON.stringify(page)}\n`, "utf8");
266
258
  }
267
259
 
268
260
  async function rewriteSitianRecordsJsonl(
269
261
  path: string,
270
262
  rewrites: readonly RunDirectoryPathRewrite[],
271
263
  ): Promise<void> {
272
- if (!existsSync(path)) return;
273
- const raw = await readFile(path, "utf8");
264
+ let raw: string;
265
+ try {
266
+ raw = await readFile(path, "utf8");
267
+ } catch (error) {
268
+ if (isEnoent(error)) return;
269
+ throw error;
270
+ }
274
271
  if (raw.length === 0) return;
275
272
  const endsWithNewline = raw.endsWith("\n");
276
273
  const lines = raw.split("\n");
@@ -328,8 +325,13 @@ async function rewriteSessionTranscriptBindings(
328
325
  path: string,
329
326
  rewrites: readonly RunDirectoryPathRewrite[],
330
327
  ): Promise<void> {
331
- if (!existsSync(path)) return;
332
- const raw = await readFile(path, "utf8");
328
+ let raw: string;
329
+ try {
330
+ raw = await readFile(path, "utf8");
331
+ } catch (error) {
332
+ if (isEnoent(error)) return;
333
+ throw error;
334
+ }
333
335
  if (raw.length === 0) return;
334
336
  const endsWithNewline = raw.endsWith("\n");
335
337
  const lines = raw.split("\n");
@@ -394,8 +396,8 @@ async function rewriteSessionTranscriptBindings(
394
396
 
395
397
  /**
396
398
  * Package-owned nested seams under session/ only:
397
- * direct-officer *.pointer.json, sitian records.jsonl,
398
- * session transcript typed parent bindings. Never walks attachments/ or artifacts/.
399
+ * sitian records.jsonl,
400
+ * session transcript typed parent bindings. Never walks attachments/.
399
401
  */
400
402
  async function rewriteNestedMachinePathPages(
401
403
  pagesDirectory: string,
@@ -418,7 +420,7 @@ async function rewriteNestedMachinePathPages(
418
420
  }
419
421
  if (!entry.isFile()) continue;
420
422
  if (entry.name.endsWith(".pointer.json")) {
421
- await rewriteOfficerPointerFile(path, rewrites);
423
+ await rewriteLegacyOfficerPointerFile(path, rewrites);
422
424
  } else if (entry.name === "records.jsonl") {
423
425
  await rewriteSitianRecordsJsonl(path, rewrites);
424
426
  } else if (entry.name.endsWith(".jsonl")) {
@@ -430,7 +432,7 @@ async function rewriteNestedMachinePathPages(
430
432
  }
431
433
 
432
434
  /**
433
- * Rewrite admitted-request / invocation / run-state path fields (attachment
435
+ * Rewrite the admitted / invocation / runState fact rows (path fields: attachment
434
436
  * frozenPath / summons.attachmentPaths pointers only), then nested package-owned
435
437
  * session seams. Callers may rewrite before or after the filesystem move/copy:
436
438
  * `pagesDirectory` is where the pages currently live on disk; path strings that
@@ -446,54 +448,55 @@ export async function rewriteRoleRunDurablePages(input: {
446
448
  const { pagesDirectory } = input;
447
449
  const rewrites = collectRewrites(input);
448
450
 
449
- const admittedPath = join(pagesDirectory, "admitted-request.json");
450
- if (existsSync(admittedPath)) {
451
- const page = JSON.parse(await readFile(admittedPath, "utf8")) as Record<
452
- string,
453
- unknown
454
- >;
455
- const before = JSON.stringify(page);
456
- rewriteAdmittedRoleRunPage(page, rewrites);
457
- if (JSON.stringify(page) !== before) {
458
- await writeFile(admittedPath, `${JSON.stringify(page, null, 2)}\n`, "utf8");
459
- }
451
+ // One rewriter per page kind; each keeps its own field list.
452
+ const pageRewriters: Readonly<Record<"admitted" | "invocation" | "runState", (page: Record<string, unknown>) => void>> = {
453
+ admitted: (page) => rewriteAdmittedRoleRunPage(page, rewrites),
454
+ invocation: (page) =>
455
+ rewriteRunDirectoryPathFieldsAgainstRewrites(page, INVOCATION_PAGE_FIELDS, rewrites),
456
+ runState: (page) => {
457
+ rewriteRunDirectoryPathFieldsAgainstRewrites(page, RUN_STATE_PAGE_FIELDS, rewrites);
458
+ if (isRecord(page.principal)) {
459
+ rewriteRunDirectoryPathFieldsAgainstRewrites(
460
+ page.principal,
461
+ ["sessionDirectory", "sessionFile"],
462
+ rewrites,
463
+ );
464
+ }
465
+ // Open court summons: source-run locators + frozen attachment path pointers.
466
+ if (isRecord(page.currentCourt)) {
467
+ rewriteSummonsMaterials(page.currentCourt.summons, rewrites);
468
+ }
469
+ },
470
+ };
471
+ // A page is rewritten only when its fact row exists — never gate on the rendering.
472
+ for (const [section, rewrite] of Object.entries(pageRewriters) as [keyof typeof pageRewriters, (page: Record<string, unknown>) => void][]) {
473
+ if (readPageSync(pagesDirectory, section) === undefined) continue;
474
+ updateSectionSync(pagesDirectory, section, (current) => {
475
+ const page = structuredClone(current);
476
+ rewrite(page);
477
+ return JSON.stringify(page) === JSON.stringify(current) ? undefined : page;
478
+ });
460
479
  }
461
-
462
- const invocationPath = join(pagesDirectory, "invocation.json");
463
- if (existsSync(invocationPath)) {
464
- const page = JSON.parse(await readFile(invocationPath, "utf8")) as Record<string, unknown>;
465
- const before = JSON.stringify(page);
466
- rewriteRunDirectoryPathFieldsAgainstRewrites(page, INVOCATION_PAGE_FIELDS, rewrites);
467
- if (JSON.stringify(page) !== before) {
468
- await writeFile(invocationPath, `${JSON.stringify(page, null, 2)}\n`, "utf8");
480
+ // Runs from before #1161 (the one-shot book-topology migrator's input) keep these
481
+ // as separate page files; the same rewriters apply. Nothing live writes them.
482
+ for (const [file, rewrite] of [
483
+ ["admitted-request.json", pageRewriters.admitted],
484
+ ["invocation.json", pageRewriters.invocation],
485
+ ["run-state.json", pageRewriters.runState],
486
+ ] as const) {
487
+ const path = join(pagesDirectory, file);
488
+ let raw: string;
489
+ try {
490
+ raw = await readFile(path, "utf8");
491
+ } catch (error) {
492
+ if (isEnoent(error)) continue;
493
+ throw error;
469
494
  }
470
- }
471
-
472
- const statePath = join(pagesDirectory, "run-state.json");
473
- if (existsSync(statePath)) {
474
- const page = JSON.parse(await readFile(statePath, "utf8")) as Record<
475
- string,
476
- unknown
477
- >;
495
+ const page = JSON.parse(raw) as Record<string, unknown>;
478
496
  const before = JSON.stringify(page);
479
- rewriteRunDirectoryPathFieldsAgainstRewrites(
480
- page,
481
- RUN_STATE_PAGE_FIELDS,
482
- rewrites,
483
- );
484
- if (isRecord(page.principal)) {
485
- rewriteRunDirectoryPathFieldsAgainstRewrites(
486
- page.principal,
487
- ["sessionDirectory", "sessionFile"],
488
- rewrites,
489
- );
490
- }
491
- // Open court summons: source-run locators + frozen attachment path pointers.
492
- if (isRecord(page.currentCourt)) {
493
- rewriteSummonsMaterials(page.currentCourt.summons, rewrites);
494
- }
497
+ rewrite(page);
495
498
  if (JSON.stringify(page) !== before) {
496
- await writeFile(statePath, `${JSON.stringify(page, null, 2)}\n`, "utf8");
499
+ await writeFile(path, `${JSON.stringify(page, null, 2)}\n`, "utf8");
497
500
  }
498
501
  }
499
502
 
@@ -1,4 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ import { readPageSync, RUN_STATE_FILE } from "./run-dossier.ts";
2
3
  import { readFileSync, writeSync } from "node:fs";
3
4
  import { join } from "node:path";
4
5
  import {
@@ -89,7 +90,6 @@ import {
89
90
  } from "./diarist-contracts.ts";
90
91
  import { commitDiaristProjection } from "./diarist.ts";
91
92
  import { TicketProvenanceInputError } from "./ticket-provenance.ts";
92
- import { bindTicketNumberOnRunDirectory } from "./public-cli/invocation.ts";
93
93
  import {
94
94
  GATEKEEPER_TOOL_SPEC,
95
95
  type GatekeeperRuntimeDependencies,
@@ -773,10 +773,9 @@ function readRoleRunCoordinates(ctx: HostContext, label: string): {
773
773
  } {
774
774
  const runDirectory = runDirectoryFromHostContext(ctx);
775
775
  if (runDirectory === undefined) throw new Error(`${label} requires AK_ROLE_RUN_DIR`);
776
- const admittedPath = join(runDirectory, "admitted-request.json");
777
- const admitted = JSON.parse(readFileSync(admittedPath, "utf8")) as Record<string, unknown>;
778
- if (typeof admitted.projectRoot !== "string" || admitted.projectRoot.trim() === "") {
779
- throw new Error(`${label} admitted-request missing projectRoot (${admittedPath})`);
776
+ const admitted = readPageSync(runDirectory, "admitted");
777
+ if (typeof admitted?.projectRoot !== "string" || admitted.projectRoot.trim() === "") {
778
+ throw new Error(`${label} state.jsonl admitted-request row missing projectRoot (${join(runDirectory, RUN_STATE_FILE)})`);
780
779
  }
781
780
  return {
782
781
  runDirectory,
@@ -838,11 +837,8 @@ export function createDiaristRoleRuntime(
838
837
  const coords = readDiaristRunCoordinates(ctx);
839
838
  // #836 7.3: pre-bound ticket is material for the LLM, not an override.
840
839
  const ticketNumber = assertion.kind === "ticket" ? assertion.ticketNumber : undefined;
841
- if (ticketNumber !== undefined) {
842
- if (coords.boundTicketNumber === undefined) {
843
- await bindTicketNumberOnRunDirectory(coords.runDirectory, ticketNumber);
844
- }
845
- }
840
+ // The ticket binds in the public call's own process after the turn, from the sealed
841
+ // ticketNumber (settlement seam); this leg never writes the run's current.json.
846
842
  // Strict-schema hosts emit ticketSessions: null for a single ticket.
847
843
  const multiTicket = submitted?.ticketSessions != null;
848
844
  const singleSessions = submitted && !multiTicket
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Names in a run directory (docs/dossier-topology.md, #1161). Constants only, so
3
+ * the appender and the `current.json` renderer can both import them.
4
+ */
5
+ export const RUN_CURRENT_FILE = "current.json" as const;
6
+ export const RUN_HISTORY_FILE = "history.jsonl" as const;
7
+ export const RUN_LOG_FILE = "log.jsonl" as const;
8
+ export const RUN_STATE_FILE = "state.jsonl" as const;
9
+
10
+ /**
11
+ * Record kinds that make a leg's history: they land in `history.jsonl`. The
12
+ * original submission ledger and attempt history, the prompt/schema each turn was
13
+ * started with, and officer pointers.
14
+ */
15
+ export const RUN_HISTORY_KINDS: ReadonlySet<string> = new Set([
16
+ "candidate", "roundContext", "outcome", "sealed", "post-seal-anomaly",
17
+ "attempt-history", "turn-delivery", "officer-pointer",
18
+ ]);
19
+
20
+ /** History record kind of a booked officer pointer (appender routes it to history.jsonl). */
21
+ export const OFFICER_POINTER_RECORD_KIND = "officer-pointer" as const;
22
+
23
+ /**
24
+ * The facts a run cannot go on without land in `state.jsonl`: the four whole pages only the
25
+ * public call knows, and the native session id a host binds (a resume needs it). A
26
+ * different volume than the submission ledger and than the auxiliary stream, as the
27
+ * files they replaced were (a page write failure throws; a ledger volume that cannot
28
+ * be written does not stop a resume; a stream line that cannot be written is noted).
29
+ * Every other kind lands in `log.jsonl`.
30
+ */
31
+ export const RUN_STATE_KINDS: ReadonlySet<string> = new Set([
32
+ "invocation", "admitted-request", "run-state", "terminal", "host-session-id",
33
+ ]);