@akagilnc/pi-workflow-roles 0.1.5864 → 0.1.5877

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.
@@ -49,11 +49,14 @@ export function projectNotaryBoundFromFlags(getFlag) {
49
49
  ...(ticketNumber === undefined ? {} : { ticketNumber }),
50
50
  };
51
51
  }
52
- /** Assemble systemPrompt body from base + soul only (NO embedded bound JSON).
52
+ /** Assemble systemPrompt body from base + soul + the audited seat's Soul (NO embedded bound JSON).
53
53
  * Audited-run identity travels separately as `readingMaterial`; adapters fold
54
54
  * it into the provider-visible prompt at the send boundary. */
55
55
  export function assembleNotaryAgentStartPrompt(input) {
56
- return `${input.baseSystemPrompt}\n\n<notary_soul>\n${input.soul}\n</notary_soul>`;
56
+ const audited = input.audited === undefined
57
+ ? ""
58
+ : `\n\n<audited_seat_soul seat="${input.audited.seat}">\n${input.audited.soul}\n</audited_seat_soul>`;
59
+ return `${input.baseSystemPrompt}\n\n<notary_soul>\n${input.soul}\n</notary_soul>${audited}`;
57
60
  }
58
61
  export function createNotaryRoleRuntime(pi, dependencies, host) {
59
62
  let activation;
@@ -74,9 +77,11 @@ export function createNotaryRoleRuntime(pi, dependencies, host) {
74
77
  if (soul.length === 0)
75
78
  throw new Error("Notary soul is empty");
76
79
  const sourceRun = await dependencies.loadSourceRunLocator(path);
80
+ const auditedSoul = (await dependencies.loadAuditedSoul(sourceRun.role))?.trim();
77
81
  activation = {
78
82
  soul,
79
83
  sourceRun,
84
+ ...(auditedSoul === undefined || auditedSoul === "" ? {} : { auditedSoul }),
80
85
  ...(admitted?.ticketNumber === undefined
81
86
  ? {}
82
87
  : { ticketNumber: admitted.ticketNumber }),
@@ -96,6 +101,9 @@ export function createNotaryRoleRuntime(pi, dependencies, host) {
96
101
  systemPrompt: assembleNotaryAgentStartPrompt({
97
102
  baseSystemPrompt: event.systemPrompt,
98
103
  soul: activation.soul,
104
+ ...(activation.auditedSoul === undefined
105
+ ? {}
106
+ : { audited: { seat: activation.sourceRun.role, soul: activation.auditedSoul } }),
99
107
  }),
100
108
  readingMaterial: projectNotaryAuditedRunIdentity({
101
109
  sourceRun: activation.sourceRun,
@@ -26,6 +26,7 @@ const NOTARY_SESSION_MATERIALS = [
26
26
  "souls/notary.md",
27
27
  "souls/audit-law.md",
28
28
  "souls/ticket-law.md",
29
+ "souls/quality-law.md",
29
30
  "souls/gate-output-guide.md",
30
31
  "docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md"
31
32
  ];
@@ -41,6 +42,7 @@ const AUDITOR_PUBLIC_SESSION_MATERIALS = [
41
42
  "CLAUDE.md",
42
43
  "souls/judge-auditor.md",
43
44
  "souls/doctor-auditor.md",
45
+ "souls/judge.md",
44
46
  "souls/audit-law.md",
45
47
  "souls/quality-law.md",
46
48
  "docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md"
@@ -292,7 +294,7 @@ const PUBLIC_ROLE_RECORDS = [
292
294
  activationStage: "load-and-install",
293
295
  receiptStatusKey: "status",
294
296
  // #924: 公用《票面法》三席同装
295
- sessionMaterials: ["CLAUDE.md", "souls/countersign.md", "souls/ticket-law.md", "resources/countersign-ticket-issue.md", "docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md"]
297
+ sessionMaterials: ["CLAUDE.md", "souls/countersign.md", "souls/ticket-law.md", "souls/audit-law.md", "souls/quality-law.md", "resources/countersign-ticket-issue.md", "docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md"]
296
298
  },
297
299
  {
298
300
  role: "secretariat",
@@ -19,6 +19,7 @@ import { formatHostSelectionFailure, HostSelectionError, } from "./role-turn-hos
19
19
  import { admittedSourceRunPath, parseAnalystArgv, parsePublicSeatArgv, } from "./invocation.js";
20
20
  import { createTypedOptionConsumer, optionsForOwner, projectCommandHelp, projectOwnerOptions, PUBLIC_NAVIGATOR_HELP_NOTE, PUBLIC_ROLE_OPTION_OWNERS, renderHumanOwnerOptionLines, } from "./option-definitions.js";
21
21
  import { TurnDispatchedFailure } from "./auto-resume.js";
22
+ import { readSessionDialogue, renderSessionDialogue } from "./dialogue-command.js";
22
23
  import { continueParentAfterChild, presentAuditedOriginalOfUnsettledOfficer, runPublicInstructionSeat, runPublicInstructionSeatResume, } from "./instruction-seat-run.js";
23
24
  import { latestPayloadEscalated } from "./countersign-run.js";
24
25
  import { runPublicAnalyst } from "./analyst-run.js";
@@ -780,6 +781,12 @@ export async function runAkRole(argv, env) {
780
781
  // Profile home only after home-free paths return. Failures keep real identity
781
782
  // and settle through the outer catch — no $HOME fallback (#604).
782
783
  const home = resolveHome(env);
784
+ if (parsed.command === "dialogue") {
785
+ if (parsed.args.length !== 1)
786
+ throw new CliUsageError("usage: ak-role dialogue <session.jsonl>");
787
+ io.stdout(renderSessionDialogue(await readSessionDialogue(parsed.args[0], home)));
788
+ return { exitCode: 0 };
789
+ }
783
790
  if (parsed.command === "roles") {
784
791
  if (parsed.args.length > 0) {
785
792
  throw new CliUsageError("roles takes no arguments");
@@ -0,0 +1,37 @@
1
+ /**
2
+ * `ak-role dialogue <session>`: the speaker lines a diary range would project,
3
+ * one JSON object per line, so the diarist searches the dialogue instead of
4
+ * re-deriving the session format (owner 2026-10-21). Same read and adapter as
5
+ * the ticket-provenance projection — no second speaker rule.
6
+ */
7
+ import { readLedgerSessionJsonlLines } from "../ledger-session-read.js";
8
+ import { adaptSessionDialogue } from "../session-dialogue.js";
9
+ import { assertDialogueSessionSourcePath } from "../ticket-provenance.js";
10
+ export async function readSessionDialogue(path, home) {
11
+ assertDialogueSessionSourcePath(path, home);
12
+ const sessionLines = await readLedgerSessionJsonlLines(path);
13
+ const dialogue = adaptSessionDialogue(sessionLines.map((entry) => entry.row));
14
+ const seenIds = new Set();
15
+ const out = [];
16
+ sessionLines.forEach((entry, index) => {
17
+ if (entry.row === undefined) {
18
+ out.push({ line: entry.line, raw: entry.raw });
19
+ return;
20
+ }
21
+ for (const event of dialogue[index] ?? []) {
22
+ const firstId = event.id !== undefined && !seenIds.has(event.id);
23
+ if (event.id !== undefined)
24
+ seenIds.add(event.id);
25
+ out.push({
26
+ line: entry.line,
27
+ ...(firstId ? { id: event.id } : {}),
28
+ speaker: event.speaker,
29
+ text: event.text,
30
+ });
31
+ }
32
+ });
33
+ return out;
34
+ }
35
+ export function renderSessionDialogue(lines) {
36
+ return lines.map((line) => `${JSON.stringify(line)}\n`).join("");
37
+ }
@@ -9612,7 +9612,7 @@ var init_ticket_provenance_contracts = __esm({
9612
9612
  });
9613
9613
 
9614
9614
  // src/diarist-contracts.ts
9615
- var DIARIST_OUTPUT_TOOL_NAME, DIARIST_SESSIONS_DESCRIPTION, diaristOutputSchema;
9615
+ var DIARIST_OUTPUT_TOOL_NAME, DIARIST_BOUND_ENDPOINTS, DIARIST_SESSIONS_DESCRIPTION, diaristOutputSchema;
9616
9616
  var init_diarist_contracts = __esm({
9617
9617
  "src/diarist-contracts.ts"() {
9618
9618
  "use strict";
@@ -9623,7 +9623,8 @@ var init_diarist_contracts = __esm({
9623
9623
  init_run_ticket_number();
9624
9624
  init_unknown_value();
9625
9625
  DIARIST_OUTPUT_TOOL_NAME = "ak_diarist_output";
9626
- DIARIST_SESSIONS_DESCRIPTION = "\u5BF9\u8BDD\u8FB9\u754C\u3002\u6BCF\u5377\u542B path\uFF08\u4F1A\u8BDD\u5377\u7EDD\u5BF9\u6216\u53EF\u8BFB\u8DEF\u5F84\uFF09\u4E0E ranges\uFF08\u672C\u5377\u672C\u8F6E\u5404\u533A\u95F4\uFF09\u3002\u6BCF\u6BB5\u533A\u95F4\u542B from / to\uFF1B\u8D77\u70B9\u4E0E\u7EC8\u70B9\u5404\u5199 id\uFF08\u539F\u751F id\uFF09\u6216 line\uFF08\u672C\u8F6E\u884C\u53F7\uFF09\uFF0C\u4E8C\u9009\u4E00\u3002\u7A7A\u5217\u8868\uFF1D\u672C\u8F6E\u65E0\u5BF9\u8BDD\u53EF\u5212\u3002\u7AEF\u70B9\u65E0\u6CD5\u6307\u540D\u65F6\u8D70 reask\uFF0C\u4E0D\u4E2D\u6B62\u3002";
9626
+ DIARIST_BOUND_ENDPOINTS = "\u8D77\u70B9\u4E0E\u7EC8\u70B9\u5404\u5199 id \u6216 line\uFF0C\u4E8C\u9009\u4E00\uFF1Aid\uFF1D\u4F1A\u8BDD\u5377\u91CC\u90A3\u4E00\u884C JSON \u7684 uuid \u5B57\u6BB5\u503C\uFF08\u6CA1\u6709 uuid \u53D6 id\uFF1B\u9876\u5C42\u90FD\u6CA1\u6709\u518D\u53D6 message \u6216 payload \u91CC\u7684 uuid / id\uFF09\uFF0C\u540C\u4E00 id \u51FA\u73B0\u591A\u6B21\u65F6\u53D6\u7B2C\u4E00\u6B21\u51FA\u73B0\u7684\u90A3\u4E00\u884C\uFF1Bline\uFF1D\u90A3\u4E00\u884C\u5728\u6587\u4EF6\u91CC\u4ECE 1 \u6570\u8D77\u7684\u884C\u53F7\u3002\u4E24\u7AEF\u90FD\u542B\u5728\u533A\u95F4\u5185\u3002";
9627
+ DIARIST_SESSIONS_DESCRIPTION = `\u5BF9\u8BDD\u8FB9\u754C\u3002\u6BCF\u5377\u542B path\uFF08\u4F1A\u8BDD\u5377\u7EDD\u5BF9\u6216\u53EF\u8BFB\u8DEF\u5F84\uFF09\u4E0E ranges\uFF08\u672C\u5377\u672C\u8F6E\u5404\u533A\u95F4\uFF09\u3002\u6BCF\u6BB5\u533A\u95F4\u542B from / to\uFF1B${DIARIST_BOUND_ENDPOINTS}\u533A\u95F4\u91CC\u53EA\u6709\u8BF4\u8BDD\u4EBA\u7684\u539F\u6587\u8FDB\u5F55\uFF1A\u7528\u6237\u4E0E\u52A9\u624B\u6D88\u606F\u91CC\u7684\u6587\u672C\u3001\u771F\u4EBA\u7ECF\u961F\u5217\u7684\u63D2\u8BDD\uFF1B\u601D\u8003\u3001\u5DE5\u5177\u8C03\u7528\u3001\u5DE5\u5177\u7ED3\u679C\u548C\u673A\u5668\u901A\u77E5\u4E0D\u8FDB\u5F55\uFF0C\u533A\u95F4\u53EF\u4EE5\u76F4\u63A5\u8DE8\u8FC7\u5B83\u4EEC\uFF0C\u4E0D\u5FC5\u62C6\u6BB5\u3002\u6BCF\u6B21\u4EA4\u5377\u7684\u8FB9\u754C\u7167\u6295\u5F71\u8FFD\u52A0\u8FDB\u672C\u7968\u8D77\u5C45\u5F55\uFF0C\u4E0D\u53BB\u91CD\uFF0C\u4E5F\u4E0D\u6539\u5DF2\u6709\u7684\u5F55\u3002\u7A7A\u5217\u8868\uFF1D\u672C\u8F6E\u65E0\u5BF9\u8BDD\u53EF\u5212\u3002\u7AEF\u70B9\u65E0\u6CD5\u6307\u540D\u65F6\u8D70 reask\uFF0C\u4E0D\u4E2D\u6B62\u3002`;
9627
9628
  diaristOutputSchema = withTerminatingOutputDeclarations(
9628
9629
  openToolObject(
9629
9630
  typebox_exports.Object({
@@ -9636,7 +9637,7 @@ var init_diarist_contracts = __esm({
9636
9637
  })
9637
9638
  ),
9638
9639
  reason: typebox_exports.Unknown({
9639
- description: "status \u4E3A escalate \u65F6\uFF1A\u8BA4\u4E0D\u51FA\u672C\u5EAD\u5BF9\u8C61\u7684\u539F\u56E0"
9640
+ description: "status \u4E3A escalate \u65F6\uFF1A\u4E0A\u5448\u7684\u539F\u56E0"
9640
9641
  }),
9641
9642
  sessions: typebox_exports.Optional(typebox_exports.Unknown({ description: DIARIST_SESSIONS_DESCRIPTION })),
9642
9643
  ticketSessions: typebox_exports.Unknown({
@@ -9888,6 +9889,7 @@ var init_packaged_role_registry = __esm({
9888
9889
  "souls/notary.md",
9889
9890
  "souls/audit-law.md",
9890
9891
  "souls/ticket-law.md",
9892
+ "souls/quality-law.md",
9891
9893
  "souls/gate-output-guide.md",
9892
9894
  "docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md"
9893
9895
  ];
@@ -9903,6 +9905,7 @@ var init_packaged_role_registry = __esm({
9903
9905
  "CLAUDE.md",
9904
9906
  "souls/judge-auditor.md",
9905
9907
  "souls/doctor-auditor.md",
9908
+ "souls/judge.md",
9906
9909
  "souls/audit-law.md",
9907
9910
  "souls/quality-law.md",
9908
9911
  "docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md"
@@ -10154,7 +10157,7 @@ var init_packaged_role_registry = __esm({
10154
10157
  activationStage: "load-and-install",
10155
10158
  receiptStatusKey: "status",
10156
10159
  // #924: 公用《票面法》三席同装
10157
- sessionMaterials: ["CLAUDE.md", "souls/countersign.md", "souls/ticket-law.md", "resources/countersign-ticket-issue.md", "docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md"]
10160
+ sessionMaterials: ["CLAUDE.md", "souls/countersign.md", "souls/ticket-law.md", "souls/audit-law.md", "souls/quality-law.md", "resources/countersign-ticket-issue.md", "docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md"]
10158
10161
  },
10159
10162
  {
10160
10163
  role: "secretariat",
@@ -10378,7 +10381,8 @@ var init_registry = __esm({
10378
10381
  "help",
10379
10382
  "resume",
10380
10383
  "new",
10381
- "setup"
10384
+ "setup",
10385
+ "dialogue"
10382
10386
  ];
10383
10387
  PUBLIC_DETERMINISTIC_COMMANDS = ["analyst"];
10384
10388
  }
@@ -11584,7 +11588,7 @@ var init_role_submission_declarations = __esm({
11584
11588
  },
11585
11589
  diarist: {
11586
11590
  label: "\u8D77\u5C45\u90CE\u8F93\u51FA",
11587
- description: "\u8D77\u5C45\u90CE\u4EA4\u5355\u7968 sessions \u6216\u591A\u7968\u9010\u7968 ticketSessions \u8FB9\u754C\uFF1B\u8BA4\u4E0D\u51FA\u672C\u5EAD\u5BF9\u8C61\u5219 escalate\u3002",
11591
+ description: "\u8D77\u5C45\u90CE\u4EA4\u5355\u7968 sessions \u6216\u591A\u7968\u9010\u7968 ticketSessions \u8FB9\u754C\uFF0C\u6216 escalate\u3002",
11588
11592
  promptSnippet: "\u8D77\u5C45\u90CE\u4EA4\u8FB9\u754C",
11589
11593
  parameters: diaristOutputSchema
11590
11594
  }
@@ -13458,16 +13462,151 @@ var init_merger_git_state = __esm({
13458
13462
  });
13459
13463
 
13460
13464
  // src/session-dialogue.ts
13465
+ function nativeEventId(row) {
13466
+ for (const key of ["uuid", "id"]) {
13467
+ const value = row[key];
13468
+ if (typeof value === "string" && value !== "") return value;
13469
+ }
13470
+ for (const nestKey of ["message", "payload"]) {
13471
+ const nested = row[nestKey];
13472
+ if (!isRecord(nested)) continue;
13473
+ for (const key of ["uuid", "id"]) {
13474
+ const value = nested[key];
13475
+ if (typeof value === "string" && value !== "") return value;
13476
+ }
13477
+ }
13478
+ return void 0;
13479
+ }
13480
+ function textParts(content) {
13481
+ if (typeof content === "string") return content === "" ? [] : [content];
13482
+ if (!Array.isArray(content)) return [];
13483
+ const out = [];
13484
+ for (const part of content) {
13485
+ if (!isRecord(part) || !SPEAKER_TEXT_PART_TYPES.has(String(part.type))) continue;
13486
+ const text = part.text;
13487
+ if (typeof text === "string" && text !== "") out.push(text);
13488
+ }
13489
+ return out;
13490
+ }
13491
+ function responseItemMessage(row) {
13492
+ if (row.type !== "response_item" || !isRecord(row.payload)) return void 0;
13493
+ if (row.payload.type !== "message") return void 0;
13494
+ return row.payload;
13495
+ }
13496
+ function messageBody(row) {
13497
+ if (isRecord(row.message)) return row.message;
13498
+ return responseItemMessage(row);
13499
+ }
13500
+ function speakerOf(message) {
13501
+ if (message.role === "assistant") return "runner";
13502
+ if (message.role === "user") return "owner";
13503
+ return void 0;
13504
+ }
13505
+ function codexContentItemKinds(message) {
13506
+ const pass = message.internal_chat_message_metadata_passthrough;
13507
+ if (!isRecord(pass) || !Array.isArray(pass.content_item_kinds)) return void 0;
13508
+ const kinds = [];
13509
+ for (const kind of pass.content_item_kinds) {
13510
+ if (typeof kind === "string" && kind !== "") kinds.push(kind);
13511
+ }
13512
+ return kinds;
13513
+ }
13514
+ function isCodexOwnerResponseItemUser(message) {
13515
+ if (message.role !== "user") return false;
13516
+ const kinds = codexContentItemKinds(message);
13517
+ if (kinds === void 0) return false;
13518
+ return kinds.some((kind) => kind.startsWith("user."));
13519
+ }
13520
+ function fromQueueEvent(row) {
13521
+ if (row.type !== "queue-operation" || row.operation !== "enqueue") return [];
13522
+ const content = row.content;
13523
+ if (typeof content !== "string" || content === "") return [];
13524
+ const id = nativeEventId(row);
13525
+ return [{ speaker: "owner", text: content, ...id === void 0 ? {} : { id } }];
13526
+ }
13527
+ function hasFixedOpenTagPrefix(content, tag) {
13528
+ if (!content.startsWith(tag)) return false;
13529
+ const next = content.charAt(tag.length);
13530
+ return next === "" || next === ">" || next === " " || next === " " || next === "\n" || next === "\r";
13531
+ }
13532
+ function isFixedNonOwnerEnqueueShape(content) {
13533
+ return hasFixedOpenTagPrefix(content, TASK_NOTIFICATION_OPEN_TAG);
13534
+ }
13535
+ function materializationOriginKind(row) {
13536
+ if (!isRecord(row.origin)) return void 0;
13537
+ const kind = row.origin.kind;
13538
+ return typeof kind === "string" && kind !== "" ? kind : void 0;
13539
+ }
13540
+ function isNonOwnerOriginKind(kind) {
13541
+ if (kind === void 0) return false;
13542
+ return kind !== "human";
13543
+ }
13544
+ function fromMessageEvent(row) {
13545
+ const message = messageBody(row);
13546
+ if (message === void 0) return [];
13547
+ if (message.role === "toolResult") return [];
13548
+ const speaker = speakerOf(message);
13549
+ if (speaker === void 0) return [];
13550
+ if (row.type === "response_item" && speaker === "owner") {
13551
+ if (!isCodexOwnerResponseItemUser(message)) return [];
13552
+ }
13553
+ const parts = textParts(message.content);
13554
+ if (parts.length === 0) return [];
13555
+ const text = parts.join("");
13556
+ if (text === "") return [];
13557
+ const id = nativeEventId(row);
13558
+ return [{ speaker, text, ...id === void 0 ? {} : { id } }];
13559
+ }
13560
+ function adaptSessionDialogue(rows) {
13561
+ return rows.map((row) => {
13562
+ if (row === void 0) return [];
13563
+ const originKind = materializationOriginKind(row);
13564
+ if (isNonOwnerOriginKind(originKind)) return [];
13565
+ if (row.type === "queue-operation" && row.operation === "enqueue" && typeof row.content === "string" && isFixedNonOwnerEnqueueShape(row.content)) {
13566
+ return [];
13567
+ }
13568
+ const queued = fromQueueEvent(row);
13569
+ if (queued.length > 0) return queued;
13570
+ return fromMessageEvent(row);
13571
+ });
13572
+ }
13573
+ var SPEAKER_TEXT_PART_TYPES, TASK_NOTIFICATION_OPEN_TAG;
13461
13574
  var init_session_dialogue = __esm({
13462
13575
  "src/session-dialogue.ts"() {
13463
13576
  "use strict";
13464
13577
  init_unknown_value();
13578
+ SPEAKER_TEXT_PART_TYPES = /* @__PURE__ */ new Set(["text", "input_text", "output_text"]);
13579
+ TASK_NOTIFICATION_OPEN_TAG = "<task-notification";
13465
13580
  }
13466
13581
  });
13467
13582
 
13468
13583
  // src/ticket-provenance.ts
13469
13584
  import { readFile as readFile7 } from "node:fs/promises";
13470
13585
  import { join as join12, resolve as resolve6 } from "node:path";
13586
+ function dialogueSessionSourceRoots(home) {
13587
+ const machineHome = typeof home === "string" && home.trim() !== "" ? home : packageMachineHome();
13588
+ return [
13589
+ join12(machineHome, ".claude", "projects"),
13590
+ join12(machineHome, ".codex", "sessions"),
13591
+ join12(machineHome, ".pi", "agent", "sessions")
13592
+ ];
13593
+ }
13594
+ function isLedgerRoleSessionFile(absolute, home) {
13595
+ const machineHome = typeof home === "string" && home.trim() !== "" ? home : packageMachineHome();
13596
+ const ledgerHome = resolveActivationLedgerHome(machineHome);
13597
+ if (!physicallyContainedIn(ledgerHome, absolute)) return false;
13598
+ return sessionFileOf(runDirectoryOfSessionFile(absolute)) === absolute;
13599
+ }
13600
+ function assertDialogueSessionSourcePath(path, home) {
13601
+ const absolute = resolve6(path);
13602
+ for (const root of dialogueSessionSourceRoots(home)) {
13603
+ if (physicallyContainedIn(root, absolute)) return;
13604
+ }
13605
+ if (isLedgerRoleSessionFile(absolute, home)) return;
13606
+ throw new TicketProvenanceInputError(
13607
+ `session unreadable: ${path} (outside authorized source roots)`
13608
+ );
13609
+ }
13471
13610
  function ticketProvenanceSubject(ticketNumber) {
13472
13611
  if (!isSafePositiveTicketNumber(ticketNumber)) {
13473
13612
  throw new Error(
@@ -13504,6 +13643,7 @@ async function rehomeUnboundTicketProvenance(runDirectory, ticketNumber, cwd, ho
13504
13643
  }).join("");
13505
13644
  if (block !== "") appendSitianRecordBlock(ticketInput, block);
13506
13645
  }
13646
+ var TicketProvenanceInputError;
13507
13647
  var init_ticket_provenance = __esm({
13508
13648
  "src/ticket-provenance.ts"() {
13509
13649
  "use strict";
@@ -13517,6 +13657,16 @@ var init_ticket_provenance = __esm({
13517
13657
  init_sitian_reader();
13518
13658
  init_ticket_provenance_contracts();
13519
13659
  init_unknown_value();
13660
+ TicketProvenanceInputError = class extends Error {
13661
+ code = "ticket-provenance-input";
13662
+ constructor(message, options) {
13663
+ super(
13664
+ message,
13665
+ options?.cause === void 0 ? void 0 : { cause: options.cause }
13666
+ );
13667
+ this.name = "TicketProvenanceInputError";
13668
+ }
13669
+ };
13520
13670
  }
13521
13671
  });
13522
13672
 
@@ -14727,6 +14877,14 @@ var init_option_definitions = __esm({
14727
14877
  "ak-role new notary --source-run 01abc\u2026@countersign",
14728
14878
  "ak-role --host grok-build new inspector --attach ./change.patch"
14729
14879
  ]
14880
+ },
14881
+ dialogue: {
14882
+ command: "dialogue",
14883
+ summary: "Print what a diary range projects from a session volume, one JSON object per line: speaker lines {line, id, speaker: owner|runner, text} and unparsable lines {line, raw}. line and id are what a range endpoint names; a repeated id is shown on its first line only, where it resolves. Same authorized session roots as the projection.",
14884
+ usage: ["ak-role dialogue <session.jsonl>"],
14885
+ examples: [
14886
+ "ak-role dialogue ~/.claude/projects/-Users-me-repo/0b1c\u2026.jsonl > /tmp/dialogue.jsonl"
14887
+ ]
14730
14888
  }
14731
14889
  };
14732
14890
  PUBLIC_COMMAND_HELP = {
@@ -19021,6 +19179,7 @@ var init_auditor_soul = __esm({
19021
19179
  judge: [
19022
19180
  "CLAUDE.md",
19023
19181
  "souls/judge-auditor.md",
19182
+ "souls/judge.md",
19024
19183
  "souls/audit-law.md",
19025
19184
  "souls/quality-law.md",
19026
19185
  "docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md"
@@ -23896,6 +24055,44 @@ var init_post_admission = __esm({
23896
24055
  }
23897
24056
  });
23898
24057
 
24058
+ // src/public-cli/dialogue-command.ts
24059
+ async function readSessionDialogue(path, home) {
24060
+ assertDialogueSessionSourcePath(path, home);
24061
+ const sessionLines = await readLedgerSessionJsonlLines(path);
24062
+ const dialogue = adaptSessionDialogue(sessionLines.map((entry) => entry.row));
24063
+ const seenIds = /* @__PURE__ */ new Set();
24064
+ const out = [];
24065
+ sessionLines.forEach((entry, index) => {
24066
+ if (entry.row === void 0) {
24067
+ out.push({ line: entry.line, raw: entry.raw });
24068
+ return;
24069
+ }
24070
+ for (const event of dialogue[index] ?? []) {
24071
+ const firstId = event.id !== void 0 && !seenIds.has(event.id);
24072
+ if (event.id !== void 0) seenIds.add(event.id);
24073
+ out.push({
24074
+ line: entry.line,
24075
+ ...firstId ? { id: event.id } : {},
24076
+ speaker: event.speaker,
24077
+ text: event.text
24078
+ });
24079
+ }
24080
+ });
24081
+ return out;
24082
+ }
24083
+ function renderSessionDialogue(lines) {
24084
+ return lines.map((line2) => `${JSON.stringify(line2)}
24085
+ `).join("");
24086
+ }
24087
+ var init_dialogue_command = __esm({
24088
+ "src/public-cli/dialogue-command.ts"() {
24089
+ "use strict";
24090
+ init_ledger_session_read();
24091
+ init_session_dialogue();
24092
+ init_ticket_provenance();
24093
+ }
24094
+ });
24095
+
23899
24096
  // node_modules/.pnpm/typebox@1.3.8/node_modules/typebox/build/schema/types/_refine.mjs
23900
24097
  function IsRefine2(value) {
23901
24098
  return guard_exports.HasPropertyKey(value, "~refine") && guard_exports.IsArray(value["~refine"]) && guard_exports.Every(value["~refine"], 0, (value2) => guard_exports.IsObject(value2) && guard_exports.HasPropertyKey(value2, "check") && guard_exports.HasPropertyKey(value2, "error") && guard_exports.IsFunction(value2.check) && guard_exports.IsFunction(value2.error));
@@ -32942,6 +33139,11 @@ async function runAkRole(argv2, env) {
32942
33139
  return { exitCode: 0 };
32943
33140
  }
32944
33141
  const home = resolveHome(env);
33142
+ if (parsed.command === "dialogue") {
33143
+ if (parsed.args.length !== 1) throw new CliUsageError("usage: ak-role dialogue <session.jsonl>");
33144
+ io.stdout(renderSessionDialogue(await readSessionDialogue(parsed.args[0], home)));
33145
+ return { exitCode: 0 };
33146
+ }
32945
33147
  if (parsed.command === "roles") {
32946
33148
  if (parsed.args.length > 0) {
32947
33149
  throw new CliUsageError("roles takes no arguments");
@@ -33132,6 +33334,7 @@ var init_cli = __esm({
33132
33334
  init_invocation();
33133
33335
  init_option_definitions();
33134
33336
  init_auto_resume();
33337
+ init_dialogue_command();
33135
33338
  init_instruction_seat_run();
33136
33339
  init_countersign_run();
33137
33340
  init_analyst_run();
@@ -1141,6 +1141,14 @@ const SUPPORT_COMMAND_HELP = {
1141
1141
  "ak-role --host grok-build new inspector --attach ./change.patch",
1142
1142
  ],
1143
1143
  },
1144
+ dialogue: {
1145
+ command: "dialogue",
1146
+ summary: "Print what a diary range projects from a session volume, one JSON object per line: speaker lines {line, id, speaker: owner|runner, text} and unparsable lines {line, raw}. line and id are what a range endpoint names; a repeated id is shown on its first line only, where it resolves. Same authorized session roots as the projection.",
1147
+ usage: ["ak-role dialogue <session.jsonl>"],
1148
+ examples: [
1149
+ "ak-role dialogue ~/.claude/projects/-Users-me-repo/0b1c….jsonl > /tmp/dialogue.jsonl",
1150
+ ],
1151
+ },
1144
1152
  };
1145
1153
  /**
1146
1154
  * Sole public help-copy owner keyed by command/topic id.
@@ -14,7 +14,8 @@ const PUBLIC_CLI_SUPPORT_COMMANDS = [
14
14
  "help",
15
15
  "resume",
16
16
  "new",
17
- "setup"
17
+ "setup",
18
+ "dialogue"
18
19
  ];
19
20
  function seatModelOnly(seat) {
20
21
  if (seat?.provider === void 0 || seat.model === void 0) return void 0;
@@ -30,7 +30,7 @@ import { INSPECTOR_SOURCE_RUN_FLAG } from "./inspector-contracts.js";
30
30
  import { DIARIST_TOOL_SPEC, } from "./diarist-role.js";
31
31
  import { SECRETARIAT_OUTPUT_TOOL_SPEC, } from "./secretariat-role.js";
32
32
  import { SECRETARIAT_OUTPUT_TOOL_NAME } from "./secretariat-contracts.js";
33
- import { projectDiaristSessions, projectDiaristTicketSessions, } from "./diarist-contracts.js";
33
+ import { DIARIST_BOUND_ENDPOINTS, projectDiaristSessions, projectDiaristTicketSessions, } from "./diarist-contracts.js";
34
34
  import { commitDiaristProjection } from "./diarist.js";
35
35
  import { TicketProvenanceInputError } from "./ticket-provenance.js";
36
36
  import { GATEKEEPER_TOOL_SPEC, } from "./gatekeeper-role.js";
@@ -480,7 +480,7 @@ function readDiaristRunCoordinates(ctx) {
480
480
  };
481
481
  }
482
482
  /** Plain-language re-ask when diarist bounds cannot be used (#901 / reask-not-explode). */
483
- const DIARIST_BOUNDS_REASK = "边界无法使用。多票请逐票重交 ticketSessions,单票重交 sessions;每卷 path + ranges,每端以原生 id 或本轮行号二选一指名。";
483
+ const DIARIST_BOUNDS_REASK = `边界无法使用。多票请逐票重交 ticketSessions,单票重交 sessions;每卷 path + ranges。${DIARIST_BOUND_ENDPOINTS}`;
484
484
  /**
485
485
  * #708 / #779 / #901: 起居郎 public seat on the shared filed-officer envelope.
486
486
  * LLM judges ticket + dialogue bounds; mechanical layer reprojects the unique
@@ -1038,6 +1038,9 @@ deliveryRequestLimit) {
1038
1038
  throw new Error("Notary runtime dependencies are not configured");
1039
1039
  return dependencies.loadNotarySourceRun(path);
1040
1040
  },
1041
+ async loadAuditedSoul(role) {
1042
+ return packagedRoleMetadata(role) === undefined ? undefined : requireRoleSoul(role);
1043
+ },
1041
1044
  }, hostActions);
1042
1045
  const countersign = createCountersignRoleRuntime(roleHost, {
1043
1046
  loadSoul: () => requireRoleSoul("countersign"),
@@ -109,7 +109,7 @@ const SUBMISSION_FACE = {
109
109
  },
110
110
  diarist: {
111
111
  label: "起居郎输出",
112
- description: "起居郎交单票 sessions 或多票逐票 ticketSessions 边界;认不出本庭对象则 escalate。",
112
+ description: "起居郎交单票 sessions 或多票逐票 ticketSessions 边界,或 escalate。",
113
113
  promptSnippet: "起居郎交边界",
114
114
  parameters: diaristOutputSchema,
115
115
  },
@@ -54,7 +54,7 @@ function isLedgerRoleSessionFile(absolute, home) {
54
54
  return sessionFileOf(runDirectoryOfSessionFile(absolute)) === absolute;
55
55
  }
56
56
  /** Real I/O seam gate: only host session stores or sitian role-run session.jsonl. */
57
- function assertDialogueSessionSourcePath(path, home) {
57
+ export function assertDialogueSessionSourcePath(path, home) {
58
58
  const absolute = resolve(path);
59
59
  for (const root of dialogueSessionSourceRoots(home)) {
60
60
  if (physicallyContainedIn(root, absolute))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.5864",
3
+ "version": "0.1.5877",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -31,7 +31,7 @@
31
31
 
32
32
  ## 工序
33
33
 
34
- 从任务材料与 `--pr` / `--request-manifest` 材料判定目标后,用宿主 CLI 读取有评审的 PR,按各 reviewer 已记录的完成形态收集 findings,整理并交卷。未绑定 PR 时用 `gh` 等自行定位;无法确定时不猜,交给调用方明确目标。证据归属与不确定性的报告原则见 [通进司 Soul](../souls/collector.md),字段见 [交卷契约](../src/package-contracts/collector-output.ts)。
34
+ 从任务材料与 `--pr` / `--request-manifest` 材料判定目标后,用宿主 CLI 读取有评审的 PR,按各 reviewer 已记录的完成形态收集 findings,整理并交卷。未绑定 PR 时用 `gh` 等自行定位;无法确定时不猜,交给调用方明确目标。证据归属与不确定性的报告原则见通进司 Soul,字段见交卷工具的声明。
35
35
 
36
36
  ## 修正规则
37
37
 
@@ -10,10 +10,10 @@
10
10
 
11
11
  你是 LLM,自己读以判断边界:
12
12
 
13
- - 本仓相关会话卷(路径里的 `_` 在目录名里常写作 `-`;主 clone 与 worktree 可能共用或分列,自行辨认)
13
+ - 本仓相关会话卷(路径里的 `_` 在目录名里常写作 `-`;主 clone 与 worktree 可能共用或分列,自行辨认)。读卷用 `ak-role dialogue <会话卷>`:每行一句说话(`line`、`id`、`speaker`、`text`),正是区间投影会收进录的那些行;`line` 与 `id` 就是交卷区间端点的写法,给了 `id` 的行才能用 id 指名
14
14
  - 本票票面与评论:用 `gh` 或等价方式读 GitHub issue
15
15
 
16
- 指令明确列出多张要办的票时,逐票读卷判断各自相关边界;共同对话可以同时归入相关各票,无关他票对话不得混入。单票/多票交卷字段及不可用边界的处置见 [交卷工具 schema](../src/diarist-role.ts)。
16
+ 指令明确列出多张要办的票时,逐票读卷判断各自相关边界;共同对话可以同时归入相关各票,无关他票对话不得混入。单票/多票交卷字段及不可用边界的处置见交卷工具的声明。
17
17
 
18
18
  ## 何谓本票对话
19
19
 
@@ -34,6 +34,7 @@
34
34
  - 认得出本庭对象:`status: completed` 且 `ticketNumber: N`,并交边界。
35
35
  - 真无票对象(方案/派单/处置案等):`status: completed` 且 `ticketNumber: null`(或省略)。真无票是合法结果,不入录。
36
36
  - 本庭明确要新建票、但 issue 尚未建号:不是认票失败。以 `status: completed`、`ticketNumber: null` 交出已辨明的本案对话边界;起居录先随无号 run 暂存,建号后由既有归卷流程补归该号。不要借用旁票号。
37
- - 认不出本庭对象是哪一张:`status: escalate` 并写明 `reason`。识别不了就上抛,不得省略票号假装无录,也不得猜一个号。
37
+ - 认不出本庭对象是哪一张:不得省略票号假装无录,也不得猜一个号;何时上呈依 Soul,上呈时写明 `reason`。
38
38
  - 不得从旁及的他票或任何猜测里挑一个号顶替认不出的身份。
39
39
  - 传召文自然会提轮次、决定编号、commit sha、邻票号;要办的是指令明确列出的票,不是文中出现的每一个数字。
40
+ - 召你的那一席会把它自己收到的派单原样转给你,里面要求写票、改票、判卷的话是给那一席的;你只认票、交边界,不改票面、不替它办。
@@ -1,16 +1,17 @@
1
1
  /**
2
2
  * Shared audited-run identity for review-officer startup materials (#1166 / ADR 0087).
3
- * Value form is `<runId>@<席>` only — no directory path.
3
+ * Value form is `<runId>@<席>` only — no directory path. The field is named
4
+ * `auditedRun` so the reader sees it names the run under review, not itself.
4
5
  */
5
6
  import { basename } from "node:path";
6
7
 
7
8
  import { formatRunLeaf, parseRunLeaf } from "./role-run-placement.ts";
8
9
 
9
- export const AUDITED_RUN_IDENTITY_KIND = "audited-run-identity" as const;
10
+ export const AUDITED_RUN_IDENTITY_KIND = "audited-run" as const;
10
11
 
11
12
  export type AuditedRunIdentityMaterial = {
12
13
  readonly kind: typeof AUDITED_RUN_IDENTITY_KIND;
13
- readonly identity: string;
14
+ readonly auditedRun: string;
14
15
  };
15
16
 
16
17
  /** Identity leaf from a run directory basename; undefined when the leaf is not `runId@role`. */
@@ -21,7 +22,7 @@ export function auditedRunIdentityFromDirectory(runDirectory: string): string |
21
22
  }
22
23
 
23
24
  export function auditedRunIdentityMaterial(identity: string): AuditedRunIdentityMaterial {
24
- return { kind: AUDITED_RUN_IDENTITY_KIND, identity };
25
+ return { kind: AUDITED_RUN_IDENTITY_KIND, auditedRun: identity };
25
26
  }
26
27
 
27
28
  /** Identity from locator fields already in hand (notary source-run). */
@@ -43,7 +43,8 @@ function auditorSoulRelativePath(role: AuditorSoulRole): string {
43
43
  }
44
44
 
45
45
  /**
46
- * #470 auditor session materials. Judge carries audit-law + quality-law; doctor does
46
+ * #470 auditor session materials. Judge carries the audited judge Soul + audit-law +
47
+ * quality-law (verdicts cite judge.md; audit-law makes the auditor check the cited line); doctor does
47
48
  * not (御批四: 参审席 = 大理寺主会话 + 其审计席; 太医线不动).
48
49
  * Reviewer auditor roster removed with #495 S6 gate retirement.
49
50
  * #675 owner: no generic auditor.md — subject selects this table.
@@ -52,6 +53,7 @@ export const AUDITOR_SESSION_MATERIALS = {
52
53
  judge: [
53
54
  "CLAUDE.md",
54
55
  "souls/judge-auditor.md",
56
+ "souls/judge.md",
55
57
  "souls/audit-law.md",
56
58
  "souls/quality-law.md",
57
59
  "docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md",