@akagilnc/pi-workflow-roles 0.1.5483 → 0.1.5571

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 (112) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/acp-host/production-host.js +6312 -6376
  4. package/dist/analyst-gate-cycles-read.js +28 -78
  5. package/dist/analyst-ledger.js +26 -42
  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-entry.js +4 -3
  9. package/dist/archivist-record-pointer.js +71 -36
  10. package/dist/atomic-write.js +2 -2
  11. package/dist/auditor-dossier-tool.js +11 -6
  12. package/dist/auditor-soul.js +5 -12
  13. package/dist/collector-config.js +6 -5
  14. package/dist/doctor-evidence.js +8 -4
  15. package/dist/engine-detour-usage.js +8 -22
  16. package/dist/headless-host/production-host.js +5969 -6011
  17. package/dist/host-descriptions.js +0 -4
  18. package/dist/host-session-record.js +266 -267
  19. package/dist/ledger-session-read.js +5 -0
  20. package/dist/migrate-book-topology.js +1369 -722
  21. package/dist/navigator-public-session.js +13 -17
  22. package/dist/notary-source-run.js +8 -16
  23. package/dist/packaged-role-registry.js +5 -56
  24. package/dist/pi/durable-principal.js +2 -3
  25. package/dist/pi/role-turn-host.js +79 -15
  26. package/dist/public-cli/auto-resume.js +8 -47
  27. package/dist/public-cli/instruction-seat-run.js +9 -7
  28. package/dist/public-cli/invocation.js +67 -100
  29. package/dist/public-cli/main.js +2689 -2532
  30. package/dist/public-cli/post-admission.js +96 -55
  31. package/dist/public-cli/run-lifecycle.js +44 -100
  32. package/dist/public-cli/settlement.js +184 -429
  33. package/dist/public-cli/terminal.js +0 -12
  34. package/dist/role-run-placement.js +53 -16
  35. package/dist/role-run-relocation.js +111 -52
  36. package/dist/run-dossier-files.js +30 -0
  37. package/dist/run-dossier.js +322 -0
  38. package/dist/run-terminal-artifacts.js +69 -202
  39. package/dist/run-ticket-number.js +49 -7
  40. package/dist/session-assistant-usage.js +9 -18
  41. package/dist/session-identity.js +49 -19
  42. package/dist/sitian-appender.js +29 -18
  43. package/dist/sitian-facade.js +11 -0
  44. package/dist/sitian-reader.js +44 -29
  45. package/dist/submission-gate.js +1 -1
  46. package/dist/submission-ledger.js +4 -4
  47. package/dist/ticket-provenance-contracts.js +2 -8
  48. package/dist/ticket-provenance.js +25 -9
  49. package/package.json +1 -1
  50. package/scripts/build-package.mjs +2 -0
  51. package/src/acp-host/production-host.ts +1 -1
  52. package/src/acp-host/role-turn-host.ts +11 -4
  53. package/src/analyst-gate-cycles-read.ts +31 -88
  54. package/src/analyst-ledger.ts +36 -51
  55. package/src/analyst-metric-families/acceptance-success-rework.ts +24 -1
  56. package/src/analyst-metric-families/gate-cycles.ts +1 -1
  57. package/src/analyst-metric-families/round-timeline.ts +2 -5
  58. package/src/analyst-page.ts +3 -3
  59. package/src/archivist-record-entry.ts +6 -5
  60. package/src/archivist-record-pointer.ts +92 -42
  61. package/src/atomic-write.ts +3 -1
  62. package/src/auditor-dossier-tool.ts +14 -8
  63. package/src/auditor-soul.ts +5 -10
  64. package/src/book-topology-migration-placement.ts +16 -3
  65. package/src/book-topology-record-class-migrators.ts +4 -2
  66. package/src/book-topology-runs-migrator.ts +6 -3
  67. package/src/collector-config.ts +5 -3
  68. package/src/doctor-contracts.ts +1 -1
  69. package/src/doctor-evidence.ts +7 -3
  70. package/src/engine-detour-tool.ts +1 -1
  71. package/src/engine-detour-usage.ts +12 -25
  72. package/src/external-host-turn-loop.ts +25 -0
  73. package/src/factory-board.ts +16 -81
  74. package/src/headless-host/production-host.ts +1 -4
  75. package/src/headless-host/role-turn-host.ts +56 -21
  76. package/src/host-descriptions.ts +0 -6
  77. package/src/host-session-record.ts +224 -140
  78. package/src/ledger-session-read.ts +6 -0
  79. package/src/navigator-public-session.ts +13 -18
  80. package/src/navigator-work-context.ts +3 -1
  81. package/src/notary-role.ts +2 -1
  82. package/src/notary-source-run.ts +8 -18
  83. package/src/packaged-role-registry.ts +1 -65
  84. package/src/pi/adapter.ts +50 -7
  85. package/src/pi/durable-principal.ts +2 -4
  86. package/src/pi/role-turn-host.ts +85 -14
  87. package/src/public-cli/auto-resume.ts +7 -43
  88. package/src/public-cli/instruction-seat-run.ts +13 -6
  89. package/src/public-cli/invocation.ts +90 -144
  90. package/src/public-cli/post-admission.ts +111 -72
  91. package/src/public-cli/run-lifecycle.ts +47 -115
  92. package/src/public-cli/settlement.ts +221 -589
  93. package/src/public-cli/terminal.ts +1 -61
  94. package/src/role-envelope.ts +4 -2
  95. package/src/role-run-placement.ts +65 -17
  96. package/src/role-run-relocation.ts +110 -70
  97. package/src/role-runtime.ts +46 -69
  98. package/src/run-dossier-files.ts +33 -0
  99. package/src/run-dossier.ts +357 -0
  100. package/src/run-terminal-artifacts.ts +84 -240
  101. package/src/run-ticket-number.ts +82 -14
  102. package/src/session-assistant-usage.ts +8 -20
  103. package/src/session-identity.ts +52 -21
  104. package/src/sitian-appender.ts +33 -26
  105. package/src/sitian-facade.ts +22 -0
  106. package/src/sitian-reader.ts +62 -32
  107. package/src/submission-gate.ts +1 -1
  108. package/src/submission-ledger.ts +4 -4
  109. package/src/ticket-provenance-contracts.ts +3 -6
  110. package/src/ticket-provenance.ts +23 -11
  111. package/src/ticket-snapshot.ts +3 -2
  112. package/src/ticket-trajectory.ts +95 -109
@@ -16,9 +16,7 @@
16
16
  * output path outside every ledger and declares the bound; one-shot write does
17
17
  * not advertise refresh. Snapshot bindings stay fixed; each tick re-reads ledgers.
18
18
  */
19
- import { randomUUID } from "node:crypto";
20
- import { lstat, mkdir, realpath, rename, rm, writeFile } from "node:fs/promises";
21
- import { basename, dirname, join, relative, resolve, sep } from "node:path";
19
+ import { resolve } from "node:path";
22
20
 
23
21
  import { packagedBoardPlacement } from "./packaged-role-registry.ts";
24
22
  import {
@@ -29,6 +27,8 @@ import {
29
27
  } from "./human-format.ts";
30
28
  import {
31
29
  DEFAULT_REFRESH_BOUNDARY_SECONDS,
30
+ assertOutputOutsideLedgers,
31
+ writePageOutsideLedgers,
32
32
  buildTicketTrajectoryBookIndex,
33
33
  loadTicketTrajectoryRuns,
34
34
  loadUnboundTrajectoryRuns,
@@ -41,7 +41,7 @@ import {
41
41
  } from "./ticket-trajectory.ts";
42
42
  import type { BoardSnapshot, SnapshotTicket, TicketIssueState } from "./ticket-snapshot.ts";
43
43
 
44
- import { isRecord, isMissingPathError } from "./unknown-value.ts";
44
+ import { isRecord } from "./unknown-value.ts";
45
45
 
46
46
  /** Unaccepted latest-run mtime bands (page-visible thresholds). */
47
47
  export const UNACCEPTED_FLYING_MS = 2 * 60 * 1000;
@@ -322,11 +322,6 @@ function attr(value: string): string {
322
322
  return escapeHtml(value);
323
323
  }
324
324
 
325
- function isPathInside(parent: string, child: string): boolean {
326
- const rel = relative(parent, child);
327
- return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !rel.startsWith(".."));
328
- }
329
-
330
325
  function activeBlockedBy(ticket: SnapshotTicket): number[] {
331
326
  if (ticket.state === "closed") return [];
332
327
  return ticket.blockedBy
@@ -1654,76 +1649,7 @@ ${boardPageScript()}
1654
1649
  `;
1655
1650
  }
1656
1651
 
1657
- async function assertOutputOutsideAllLedgers(
1658
- books: readonly FactoryBoardBook[],
1659
- outputPath: string,
1660
- ): Promise<{ ledgerRoots: string[]; outputAbsolute: string }> {
1661
- const outputAbsolute = resolve(outputPath);
1662
- const ledgerRoots: string[] = [];
1663
- for (const book of books) {
1664
- const ledgerResolved = resolve(book.ledgerDir);
1665
- let ledgerRoot: string;
1666
- try {
1667
- ledgerRoot = await realpath(ledgerResolved);
1668
- } catch (error) {
1669
- if (!isMissingPathError(error)) throw error;
1670
- ledgerRoot = ledgerResolved;
1671
- }
1672
- ledgerRoots.push(ledgerRoot);
1673
-
1674
- const missingTail: string[] = [];
1675
- let cursor = outputAbsolute;
1676
- for (;;) {
1677
- try {
1678
- await lstat(cursor);
1679
- break;
1680
- } catch (error) {
1681
- if (!isMissingPathError(error)) throw error;
1682
- const parent = dirname(cursor);
1683
- if (parent === cursor) break;
1684
- missingTail.push(basename(cursor));
1685
- cursor = parent;
1686
- }
1687
- }
1688
- let realPrefix: string;
1689
- try {
1690
- realPrefix = await realpath(cursor);
1691
- } catch (error) {
1692
- if (!isMissingPathError(error)) throw error;
1693
- realPrefix = resolve(cursor);
1694
- }
1695
- const prospectiveReal =
1696
- missingTail.length === 0 ? realPrefix : resolve(realPrefix, ...missingTail.reverse());
1697
- if (
1698
- isPathInside(ledgerRoot, prospectiveReal) ||
1699
- isPathInside(ledgerRoot, realPrefix) ||
1700
- isPathInside(ledgerRoot, outputAbsolute)
1701
- ) {
1702
- throw new Error("factory board outputPath must be outside every ledger directory");
1703
- }
1704
- }
1705
- return { ledgerRoots, outputAbsolute };
1706
- }
1707
-
1708
- async function writeHtmlAtomically(outputAbsolute: string, html: string, ledgerRoots: string[]): Promise<string> {
1709
- const parent = dirname(outputAbsolute);
1710
- await mkdir(parent, { recursive: true });
1711
- const parentReal = await realpath(parent);
1712
- for (const root of ledgerRoots) {
1713
- if (isPathInside(root, parentReal) || isPathInside(root, resolve(parentReal, basename(outputAbsolute)))) {
1714
- throw new Error("factory board outputPath must be outside every ledger directory");
1715
- }
1716
- }
1717
- const temporary = join(parent, `.factory-board-${randomUUID()}.html.tmp`);
1718
- try {
1719
- await writeFile(temporary, html, "utf8");
1720
- await rename(temporary, outputAbsolute);
1721
- } catch (error) {
1722
- await rm(temporary, { force: true }).catch(() => undefined);
1723
- throw error;
1724
- }
1725
- return realpath(outputAbsolute);
1726
- }
1652
+ const BOARD_OUTPUT_REFUSAL = "factory board outputPath must be outside every ledger directory";
1727
1653
 
1728
1654
  export async function writeFactoryBoardPage(input: {
1729
1655
  books: readonly FactoryBoardBook[];
@@ -1732,7 +1658,11 @@ export async function writeFactoryBoardPage(input: {
1732
1658
  outputPath: string;
1733
1659
  refreshBoundarySeconds?: number;
1734
1660
  }): Promise<{ outputPath: string; html: string }> {
1735
- const gate = await assertOutputOutsideAllLedgers(input.books, input.outputPath);
1661
+ const gate = await assertOutputOutsideLedgers(
1662
+ input.books.map((book) => book.ledgerDir),
1663
+ input.outputPath,
1664
+ BOARD_OUTPUT_REFUSAL,
1665
+ );
1736
1666
  const html = await renderFactoryBoardHtml(
1737
1667
  input.books,
1738
1668
  input.view,
@@ -1741,7 +1671,12 @@ export async function writeFactoryBoardPage(input: {
1741
1671
  ? { refreshBoundarySeconds: input.refreshBoundarySeconds }
1742
1672
  : undefined,
1743
1673
  );
1744
- const outputPath = await writeHtmlAtomically(gate.outputAbsolute, html, gate.ledgerRoots);
1674
+ const outputPath = await writePageOutsideLedgers(
1675
+ gate.ledgerRoots,
1676
+ gate.outputAbsolute,
1677
+ html,
1678
+ BOARD_OUTPUT_REFUSAL,
1679
+ );
1745
1680
  return { outputPath, html };
1746
1681
  }
1747
1682
 
@@ -36,10 +36,7 @@ export function createProductionHeadlessRoleTurnHost(
36
36
  options: ProductionHeadlessHostOptions,
37
37
  ): RoleTurnHost {
38
38
  const { packageRoot, principalAuthority, description, hostName } = options;
39
- const sessionIdentity = createSessionIdentityAuthority(
40
- principalAuthority,
41
- description.sessionBindingFile,
42
- );
39
+ const sessionIdentity = createSessionIdentityAuthority(principalAuthority, hostName);
43
40
  const roleRuntimeDependencies = createRoleRuntimeDependencies(packageRoot);
44
41
 
45
42
  const innerFor = (operatorHome: string): RoleTurnHost =>
@@ -6,10 +6,12 @@
6
6
  import { spawn, spawnSync } from "node:child_process";
7
7
  import { randomUUID } from "node:crypto";
8
8
  import { existsSync } from "node:fs";
9
- import { writeFile } from "node:fs/promises";
9
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
10
+ import { tmpdir } from "node:os";
10
11
  import { dirname, isAbsolute, join, resolve } from "node:path";
11
12
 
12
13
  import type { RoleTurnHost, RoleTurnRequest, RoleTurnResult } from "../host-contracts.ts";
14
+ import { sessionDirectoryOf } from "../role-run-placement.ts";
13
15
  import {
14
16
  createSerializedRoleTurnHost,
15
17
  driveExternalRoleTurnRounds,
@@ -17,6 +19,7 @@ import {
17
19
  externalHostFailure as failure,
18
20
  hostAbortedError,
19
21
  isHostAbortedError,
22
+ recordTurnDelivery,
20
23
  } from "../external-host-turn-loop.ts";
21
24
  import { describeErrorIdentity } from "../public-cli/run-lifecycle.ts";
22
25
  import { projectThrownFailureLeaf, retainPackageFault } from "../public-cli/settlement.ts";
@@ -461,31 +464,51 @@ export function createHeadlessRoleTurnHost(config: HeadlessRoleTurnHostConfig):
461
464
 
462
465
  // config.env owns package-root/child env; do not re-spread process.env over it.
463
466
  const env: NodeJS.ProcessEnv = { ...process.env, ...(config.env ?? {}) };
464
- const systemPromptPath = join(request.runDirectory, "headless-system-prompt.txt");
465
- await writeFile(systemPromptPath, systemPrompt, "utf8");
467
+ // CLI start-up inputs (system prompt / schema / MCP config files) are not
468
+ // dossier: they live in a throwaway directory, and what was delivered is
469
+ // recorded once per actual start as a history.jsonl `turn-delivery` row.
470
+ const sessionParent = config.sessionIdentity.resolveSessionFile(request.principal);
471
+ const inputsDirectory = await mkdtemp(join(tmpdir(), "ak-role-headless-"));
472
+ const systemPromptPath = join(inputsDirectory, "system-prompt.txt");
466
473
  let mcpConfigPath: string | undefined;
467
474
  let outputSchemaPath: string | undefined;
468
- if (codex) {
469
- // #1148: Codex --output-schema requires a native strict transport schema.
470
- // Project from the unique open declaration; do not alter package receipt rules.
471
- if (prepared.terminatingToolName !== NAVIGATOR_OUTPUT_TOOL_NAME) {
472
- outputSchemaPath = join(request.runDirectory, "headless-output-schema.json");
475
+ let startedWithSchema: unknown;
476
+ // A setup failure after mkdtemp must not leave the prompt material behind.
477
+ try {
478
+ await writeFile(systemPromptPath, systemPrompt, "utf8");
479
+ let deliveredSchema: unknown;
480
+ if (codex) {
481
+ // #1148: Codex --output-schema requires a native strict transport schema.
482
+ // Project from the unique open declaration; do not alter package receipt rules.
483
+ if (prepared.terminatingToolName !== NAVIGATOR_OUTPUT_TOOL_NAME) {
484
+ outputSchemaPath = join(inputsDirectory, "output-schema.json");
485
+ deliveredSchema = closeJsonSchemaForCodex(prepared.jsonSchema);
486
+ await writeFile(outputSchemaPath, `${JSON.stringify(deliveredSchema, null, 2)}\n`, "utf8");
487
+ }
488
+ } else {
489
+ mcpConfigPath = join(inputsDirectory, "mcp-config.json");
473
490
  await writeFile(
474
- outputSchemaPath,
475
- `${JSON.stringify(closeJsonSchemaForCodex(prepared.jsonSchema), null, 2)}\n`,
491
+ mcpConfigPath,
492
+ `${JSON.stringify(headlessMcpConfigDocument(prepared.mcpServers), null, 2)}\n`,
476
493
  "utf8",
477
494
  );
495
+ // #959: navigator prose exit — no closed JSON schema on claude either.
496
+ if (prepared.terminatingToolName !== NAVIGATOR_OUTPUT_TOOL_NAME) deliveredSchema = prepared.jsonSchema;
478
497
  }
479
- } else {
480
- mcpConfigPath = join(request.runDirectory, "headless-mcp-config.json");
481
- await writeFile(
482
- mcpConfigPath,
483
- `${JSON.stringify(headlessMcpConfigDocument(prepared.mcpServers), null, 2)}\n`,
484
- "utf8",
485
- );
498
+ startedWithSchema = deliveredSchema;
499
+ } catch (setupError) {
500
+ try {
501
+ await rm(inputsDirectory, { recursive: true, force: true });
502
+ } catch (cleanupError) {
503
+ await retainPackageFault({
504
+ runDirectory: request.runDirectory,
505
+ diagnostic: `headless turn inputs cleanup failed beside setup failure: ${describeErrorIdentity(cleanupError)}`,
506
+ error: cleanupError,
507
+ });
508
+ }
509
+ throw setupError;
486
510
  }
487
511
 
488
- const sessionParent = config.sessionIdentity.resolveSessionFile(request.principal);
489
512
  let exitedSessionId: string | undefined;
490
513
  try {
491
514
  outcome = await driveExternalRoleTurnRounds(prepared, request, {
@@ -534,6 +557,11 @@ export function createHeadlessRoleTurnHost(config: HeadlessRoleTurnHostConfig):
534
557
  }) !== undefined;
535
558
  }
536
559
 
560
+ // What this CLI start was given: one history record per start.
561
+ await recordTurnDelivery(request.runDirectory, {
562
+ systemPrompt,
563
+ ...(startedWithSchema === undefined ? {} : { outputSchema: startedWithSchema }),
564
+ }, "headless-host");
537
565
  let spawned: HeadlessSpawnResult;
538
566
  try {
539
567
  spawned = await spawnHeadlessTurn({
@@ -714,12 +742,19 @@ export function createHeadlessRoleTurnHost(config: HeadlessRoleTurnHostConfig):
714
742
  },
715
743
  });
716
744
  } finally {
745
+ try {
746
+ await rm(inputsDirectory, { recursive: true, force: true });
747
+ } catch (error) {
748
+ await retainPackageFault({
749
+ runDirectory: request.runDirectory,
750
+ diagnostic: `headless turn inputs cleanup failed beside host terminal: ${describeErrorIdentity(error)}`,
751
+ error,
752
+ });
753
+ }
717
754
  try {
718
755
  if (exitedSessionId !== undefined && exitedSessionId !== "") copyAndRecordHostDossier({
719
756
  host: config.hostName, sessionId: exitedSessionId, cwd: request.cwd,
720
- sessionDirectory: join(request.runDirectory, "session"), sessionParent,
721
- continuation: request.continuation,
722
- ...(request.model !== undefined ? { model: request.model } : {}),
757
+ sessionDirectory: sessionDirectoryOf(request.runDirectory), sessionParent,
723
758
  ...(request.home !== undefined ? { home: request.home } : {}),
724
759
  });
725
760
  } catch (error) {
@@ -15,8 +15,6 @@ import type { HeadlessHostDescription } from "./headless-host/description.ts";
15
15
  export type HostIdentityDescription = Readonly<{
16
16
  /** Binary path segments relative to the operator home. */
17
17
  binaryFromHome: readonly string[];
18
- /** Durable host session binding filename beside the session principal. */
19
- sessionBindingFile: string;
20
18
  }>;
21
19
 
22
20
  /** Resolve a host description's agent binary under the operator home. */
@@ -45,7 +43,6 @@ export const HOST_DESCRIPTIONS: Readonly<Record<string, AcpHostDescription>> = O
45
43
  }),
46
44
  modelPassing: "set_model",
47
45
  setModelId: "bare",
48
- sessionBindingFile: "grok-acp-session.json",
49
46
  }),
50
47
  /**
51
48
  * Operator home `~/.hermes`, native session/load resume, `acp` subcommand.
@@ -63,7 +60,6 @@ export const HOST_DESCRIPTIONS: Readonly<Record<string, AcpHostDescription>> = O
63
60
  }),
64
61
  modelPassing: "set_model",
65
62
  setModelId: "provider:model",
66
- sessionBindingFile: "hermes-acp-session.json",
67
63
  seatProfileSoul: Object.freeze({
68
64
  flag: "-p",
69
65
  namePrefix: "ak-",
@@ -84,7 +80,6 @@ export const HEADLESS_HOST_DESCRIPTIONS: Readonly<Record<string, HeadlessHostDes
84
80
  "claude": Object.freeze({
85
81
  protocol: "claude-print",
86
82
  binaryFromHome: Object.freeze([".local", "bin", "claude"]),
87
- sessionBindingFile: "claude-headless-session.json",
88
83
  fixedArgs: Object.freeze([
89
84
  // Live NDJSON events → sitian host-session records (#811); last line is the result receipt.
90
85
  "--output-format", "stream-json",
@@ -109,7 +104,6 @@ export const HEADLESS_HOST_DESCRIPTIONS: Readonly<Record<string, HeadlessHostDes
109
104
  "codex": Object.freeze({
110
105
  protocol: "codex-exec",
111
106
  binaryFromHome: Object.freeze([".local", "bin", "codex"]),
112
- sessionBindingFile: "codex-headless-session.json",
113
107
  }),
114
108
  });
115
109