@akagilnc/pi-workflow-roles 0.1.5504 → 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 (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
@@ -6,7 +6,6 @@
6
6
  * - Zero whitewashing, zero deduplication.
7
7
  * - Canonical rows after malformed lines are always reachable.
8
8
  */
9
- import { existsSync } from "node:fs";
10
9
  import { readFile } from "node:fs/promises";
11
10
 
12
11
  import type {
@@ -15,46 +14,77 @@ import type {
15
14
  SitianRecord,
16
15
  } from "./sitian-contracts.ts";
17
16
 
18
- import { isRecord, errorText } from "./unknown-value.ts";
17
+ import { isRecord, errorText, isEnoent } from "./unknown-value.ts";
19
18
 
20
- /** Read a Sitian record volume with full traversal and non-destructive diagnostics. */
21
- export async function readSitianRecords(recordFile: string): Promise<SitianReadResult> {
22
- if (!existsSync(recordFile)) {
23
- return { records: [], diagnostics: [] };
19
+ /** One non-blank JSONL line through the sole Sitian decoder. Blank → undefined. */
20
+ export type SitianLineDecode =
21
+ | { readonly ok: true; readonly record: SitianRecord }
22
+ | { readonly ok: false; readonly diagnostic: SitianMalformedDiagnostic };
23
+
24
+ /**
25
+ * Decode one Sitian JSONL line. Callers that must keep original bytes use this
26
+ * with the source line; volume readers fold it into records + diagnostics.
27
+ */
28
+ export function decodeSitianRecordLine(
29
+ line: string,
30
+ lineNumber: number,
31
+ ): SitianLineDecode | undefined {
32
+ if (!line.trim()) return undefined;
33
+ try {
34
+ const parsed = JSON.parse(line);
35
+ if (isRecord(parsed)) {
36
+ return { ok: true, record: parsed as unknown as SitianRecord };
37
+ }
38
+ const typeDesc = parsed === null ? "null" : Array.isArray(parsed) ? "array" : typeof parsed;
39
+ return {
40
+ ok: false,
41
+ diagnostic: {
42
+ kind: "malformed",
43
+ line: lineNumber,
44
+ raw: line,
45
+ error: `expected JSON object, got ${typeDesc}`,
46
+ },
47
+ };
48
+ } catch (error) {
49
+ return {
50
+ ok: false,
51
+ diagnostic: {
52
+ kind: "malformed",
53
+ line: lineNumber,
54
+ raw: line,
55
+ error: errorText(error),
56
+ },
57
+ };
24
58
  }
59
+ }
25
60
 
26
- const text = await readFile(recordFile, "utf8");
61
+ /**
62
+ * Sole decoder for one Sitian JSONL volume's text. Sync so the run-dossier
63
+ * renderer and the async file reader share one implementation.
64
+ */
65
+ export function parseSitianRecordText(text: string): SitianReadResult {
27
66
  const lines = text.split("\n");
28
-
29
67
  const records: SitianRecord[] = [];
30
68
  const diagnostics: SitianMalformedDiagnostic[] = [];
31
69
 
32
70
  for (let index = 0; index < lines.length; index += 1) {
33
- const line = lines[index]!;
34
- if (!line.trim()) continue;
35
-
36
- try {
37
- const parsed = JSON.parse(line);
38
- if (isRecord(parsed)) {
39
- records.push(parsed as unknown as SitianRecord);
40
- } else {
41
- const typeDesc = parsed === null ? "null" : Array.isArray(parsed) ? "array" : typeof parsed;
42
- diagnostics.push({
43
- kind: "malformed",
44
- line: index + 1,
45
- raw: line,
46
- error: `expected JSON object, got ${typeDesc}`,
47
- });
48
- }
49
- } catch (error) {
50
- diagnostics.push({
51
- kind: "malformed",
52
- line: index + 1,
53
- raw: line,
54
- error: errorText(error),
55
- });
56
- }
71
+ const decoded = decodeSitianRecordLine(lines[index]!, index + 1);
72
+ if (decoded === undefined) continue;
73
+ if (decoded.ok) records.push(decoded.record);
74
+ else diagnostics.push(decoded.diagnostic);
57
75
  }
58
76
 
59
77
  return { records, diagnostics };
60
78
  }
79
+
80
+ /** Read a Sitian record volume with full traversal and non-destructive diagnostics. */
81
+ export async function readSitianRecords(recordFile: string): Promise<SitianReadResult> {
82
+ // Direct read: only ENOENT is absence. EACCES/EPERM and other IO keep their cause
83
+ // (Node fs docs: do not existsSync/access before open — #1161 C3).
84
+ try {
85
+ return parseSitianRecordText(await readFile(recordFile, "utf8"));
86
+ } catch (error) {
87
+ if (isEnoent(error)) return { records: [], diagnostics: [] };
88
+ throw error;
89
+ }
90
+ }
@@ -91,7 +91,7 @@ export function createDefaultGateOfficerSummon(options: {
91
91
  }
92
92
 
93
93
  /**
94
- * Book a typed pointer under parent session/auditor-roles (archivist-owned write).
94
+ * Book a typed pointer in the parent run's current.json officers section.
95
95
  * Offline mocks without a real session leave no nested volume (lawful zero).
96
96
  */
97
97
  function bookDirectOfficerPointer(
@@ -3,7 +3,7 @@
3
3
  * 一册=一个追加式 records.jsonl。每轮新投影经司天台 appender 追加为不可变提交;
4
4
  * 纯追加、不回读历史去重、不折叠。
5
5
  */
6
- import { readFile, unlink } from "node:fs/promises";
6
+ import { readFile } from "node:fs/promises";
7
7
  import { join, resolve } from "node:path";
8
8
 
9
9
  import {
@@ -18,7 +18,7 @@ import {
18
18
  } from "./ledger-session-read.ts";
19
19
  import { isSafePositiveTicketNumber } from "./run-ticket-number.ts";
20
20
  import { runDirectoryOfSessionFile, sessionFileOf } from "./role-run-placement.ts";
21
- import { sitianVolumeRecordsFile } from "./sitian-appender.ts";
21
+ import { RUN_HISTORY_FILE } from "./run-dossier-files.ts";
22
22
  import { adaptSessionDialogue, nativeEventId } from "./session-dialogue.ts";
23
23
  import {
24
24
  appendSitianRecordBlock,
@@ -26,6 +26,7 @@ import {
26
26
  resolveSitianRecordPath,
27
27
  type SitianRecordInput,
28
28
  } from "./sitian-facade.ts";
29
+ import { decodeSitianRecordLine } from "./sitian-reader.ts";
29
30
  import {
30
31
  TICKET_PROVENANCE_KIND,
31
32
  type TicketProvenanceBound,
@@ -151,16 +152,28 @@ export async function rehomeUnboundTicketProvenance(
151
152
  cwd: string,
152
153
  home: string,
153
154
  ): Promise<void> {
154
- const source = sitianVolumeRecordsFile(runDirectory);
155
- let content: string;
155
+ const ticketInput = ticketProvenanceRecordInput(ticketNumber, cwd, home);
156
+
157
+ // Live staging: ticket-provenance rows in the run's history.jsonl. Append-only
158
+ // history keeps the staging rows; the ticket-root diary is the home.
159
+ // Old run/records.jsonl is not a runtime input (#1161); only migration tools read it.
160
+ let history: string;
156
161
  try {
157
- content = await readFile(source, "utf8");
162
+ history = await readFile(join(runDirectory, RUN_HISTORY_FILE), "utf8");
158
163
  } catch (error) {
159
164
  if (errnoCode(error) === "ENOENT") return;
160
165
  throw error;
161
166
  }
162
- appendSitianRecordBlock(ticketProvenanceRecordInput(ticketNumber, cwd, home), content);
163
- await unlink(source);
167
+ const block = history
168
+ .split("\n")
169
+ .flatMap((line, index) => {
170
+ const decoded = decodeSitianRecordLine(line, index + 1);
171
+ if (decoded === undefined || !decoded.ok) return [];
172
+ if (decoded.record.kind !== TICKET_PROVENANCE_KIND) return [];
173
+ return [`${line}\n`];
174
+ })
175
+ .join("");
176
+ if (block !== "") appendSitianRecordBlock(ticketInput, block);
164
177
  }
165
178
 
166
179
  /**
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Station resolution (four-layer fallback):
8
8
  * 1) terminating tool name inside the session (ak_<role>_output)
9
- * 2) invocation.json role
9
+ * 2) current.json invocation role
10
10
  * 3) run-directory name heuristic
11
11
  * 4) unknown (still listed; never dropped)
12
12
  *
@@ -18,6 +18,7 @@
18
18
  * write does not advertise refresh. Regeneration faults surface the original
19
19
  * cause via handle.closed / stop(). Caller stops the handle.
20
20
  */
21
+ import { readSectionSync } from "./run-dossier.ts";
21
22
  import { lstat, mkdir, readdir, readFile, realpath } from "node:fs/promises";
22
23
  import { basename, dirname, join, relative, resolve, sep } from "node:path";
23
24
  import { pathToFileURL } from "node:url";
@@ -286,9 +287,8 @@ type InvocationInfo = {
286
287
 
287
288
  async function readInvocation(runDir: string): Promise<InvocationInfo | undefined> {
288
289
  try {
289
- const raw = await readFile(join(runDir, "invocation.json"), "utf8");
290
- const parsed: unknown = JSON.parse(raw);
291
- if (!isRecord(parsed)) return undefined;
290
+ const parsed = readSectionSync(runDir, "invocation");
291
+ if (parsed === undefined) return undefined;
292
292
  const info: InvocationInfo = {};
293
293
  if (typeof parsed.role === "string" && parsed.role.trim()) info.role = parsed.role.trim();
294
294
  if (typeof parsed.thinking === "string" && parsed.thinking.trim()) info.thinking = parsed.thinking.trim();
@@ -432,7 +432,7 @@ async function parseRunDirectory(runDir: string, ledgerCoord: string): Promise<P
432
432
  ...(invocation?.role !== undefined ? { invocationRole: invocation.role } : {}),
433
433
  });
434
434
 
435
- // Session mechanical fields win; invocation.json fills gaps only.
435
+ // Session mechanical fields win; the invocation section fills gaps only.
436
436
  let { model, provider, thinking } = models;
437
437
  if (invocation) {
438
438
  if (!thinking && invocation.thinking) thinking = invocation.thinking;
@@ -660,12 +660,12 @@ ${stationBlocks || "<p data-empty=\"true\">no runs</p>"}
660
660
  * Factory board reuses this — no parallel receipt parser.
661
661
  *
662
662
  * Book-run attribution (#176 / #859): read typed `ticketNumber` from each run's
663
- * invocation.json under listBookRunDirectories (flat legacy `runs/` + subject-tree
663
+ * current.json invocation section under listBookRunDirectories (flat legacy `runs/` + subject-tree
664
664
  * `<ticket|unbound>/runs/`). Legacy `issues/<N>/runs` remains a read-only
665
665
  * compatibility entrance. Unbound runs never join a ticket.
666
666
  */
667
667
 
668
- /** One book run's typed binding projection from invocation.json. */
668
+ /** One book run's typed binding projection from the invocation section. */
669
669
  export type FlatRunTicketBinding = {
670
670
  readonly runDir: string;
671
671
  readonly runFolder: string;
@@ -678,10 +678,10 @@ export type FlatRunTicketBinding = {
678
678
 
679
679
  /**
680
680
  * Book-level run index built once per lane/render and reused per ticket.
681
- * Pure derived view of book runs' invocation.json files — not a parallel ledger.
681
+ * Pure derived view of book runs' current.json invocation sections — not a parallel ledger.
682
682
  */
683
683
  export type TicketTrajectoryBookIndex = {
684
- /** ticket number → runs bound via invocation.json ticketNumber. */
684
+ /** ticket number → runs bound via invocation ticketNumber. */
685
685
  readonly runsByTicket: ReadonlyMap<number, readonly FlatRunTicketBinding[]>;
686
686
  /** Runs with no typed ticketNumber (unknown/unbound seam). */
687
687
  readonly unboundRuns: readonly FlatRunTicketBinding[];
@@ -774,7 +774,7 @@ export async function loadTicketTrajectoryRuns(
774
774
  runs.push(parsed);
775
775
  }
776
776
 
777
- // Book runs (flat + subject-tree) bound by typed invocation.json ticketNumber.
777
+ // Book runs (flat + subject-tree) bound by typed invocation ticketNumber.
778
778
  const flatBindings = index.runsByTicket.get(issueNumber) ?? [];
779
779
  for (const binding of flatBindings) {
780
780
  const resolvedRunDir = resolve(binding.runDir);