@akagilnc/pi-workflow-roles 0.1.5873 → 0.1.5880

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.
@@ -5597,7 +5597,8 @@ var init_registry = __esm({
5597
5597
  "help",
5598
5598
  "resume",
5599
5599
  "new",
5600
- "setup"
5600
+ "setup",
5601
+ "dialogue"
5601
5602
  ];
5602
5603
  PUBLIC_DETERMINISTIC_COMMANDS = ["analyst"];
5603
5604
  }
@@ -6664,6 +6665,14 @@ var init_option_definitions = __esm({
6664
6665
  "ak-role new notary --source-run 01abc\u2026@countersign",
6665
6666
  "ak-role --host grok-build new inspector --attach ./change.patch"
6666
6667
  ]
6668
+ },
6669
+ dialogue: {
6670
+ command: "dialogue",
6671
+ 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.",
6672
+ usage: ["ak-role dialogue <session.jsonl>"],
6673
+ examples: [
6674
+ "ak-role dialogue ~/.claude/projects/-Users-me-repo/0b1c\u2026.jsonl > /tmp/dialogue.jsonl"
6675
+ ]
6667
6676
  }
6668
6677
  };
6669
6678
  PUBLIC_COMMAND_HELP = {
@@ -15798,6 +15807,44 @@ var init_instruction_seat_run = __esm({
15798
15807
  }
15799
15808
  });
15800
15809
 
15810
+ // src/public-cli/dialogue-command.ts
15811
+ async function readSessionDialogue(path, home) {
15812
+ assertDialogueSessionSourcePath(path, home);
15813
+ const sessionLines = await readLedgerSessionJsonlLines(path);
15814
+ const dialogue = adaptSessionDialogue(sessionLines.map((entry) => entry.row));
15815
+ const seenIds = /* @__PURE__ */ new Set();
15816
+ const out = [];
15817
+ sessionLines.forEach((entry, index) => {
15818
+ if (entry.row === void 0) {
15819
+ out.push({ line: entry.line, raw: entry.raw });
15820
+ return;
15821
+ }
15822
+ for (const event of dialogue[index] ?? []) {
15823
+ const firstId = event.id !== void 0 && !seenIds.has(event.id);
15824
+ if (event.id !== void 0) seenIds.add(event.id);
15825
+ out.push({
15826
+ line: entry.line,
15827
+ ...firstId ? { id: event.id } : {},
15828
+ speaker: event.speaker,
15829
+ text: event.text
15830
+ });
15831
+ }
15832
+ });
15833
+ return out;
15834
+ }
15835
+ function renderSessionDialogue(lines) {
15836
+ return lines.map((line2) => `${JSON.stringify(line2)}
15837
+ `).join("");
15838
+ }
15839
+ var init_dialogue_command = __esm({
15840
+ "src/public-cli/dialogue-command.ts"() {
15841
+ "use strict";
15842
+ init_ledger_session_read();
15843
+ init_session_dialogue();
15844
+ init_ticket_provenance();
15845
+ }
15846
+ });
15847
+
15801
15848
  // src/analyst-book-key.ts
15802
15849
  import { statSync as statSync6 } from "node:fs";
15803
15850
  import { isAbsolute as isAbsolute7 } from "node:path";
@@ -18933,6 +18980,11 @@ async function runAkRole(argv, env) {
18933
18980
  return { exitCode: 0 };
18934
18981
  }
18935
18982
  const home = resolveHome(env);
18983
+ if (parsed.command === "dialogue") {
18984
+ if (parsed.args.length !== 1) throw new CliUsageError("usage: ak-role dialogue <session.jsonl>");
18985
+ io.stdout(renderSessionDialogue(await readSessionDialogue(parsed.args[0], home)));
18986
+ return { exitCode: 0 };
18987
+ }
18936
18988
  if (parsed.command === "roles") {
18937
18989
  if (parsed.args.length > 0) {
18938
18990
  throw new CliUsageError("roles takes no arguments");
@@ -19123,6 +19175,7 @@ var init_cli = __esm({
19123
19175
  init_invocation();
19124
19176
  init_option_definitions();
19125
19177
  init_auto_resume();
19178
+ init_dialogue_command();
19126
19179
  init_instruction_seat_run();
19127
19180
  init_countersign_run();
19128
19181
  init_analyst_run();
@@ -4906,7 +4906,8 @@ var init_registry = __esm({
4906
4906
  "help",
4907
4907
  "resume",
4908
4908
  "new",
4909
- "setup"
4909
+ "setup",
4910
+ "dialogue"
4910
4911
  ];
4911
4912
  PUBLIC_DETERMINISTIC_COMMANDS = ["analyst"];
4912
4913
  }
@@ -7898,6 +7899,14 @@ var init_option_definitions = __esm({
7898
7899
  "ak-role new notary --source-run 01abc\u2026@countersign",
7899
7900
  "ak-role --host grok-build new inspector --attach ./change.patch"
7900
7901
  ]
7902
+ },
7903
+ dialogue: {
7904
+ command: "dialogue",
7905
+ 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.",
7906
+ usage: ["ak-role dialogue <session.jsonl>"],
7907
+ examples: [
7908
+ "ak-role dialogue ~/.claude/projects/-Users-me-repo/0b1c\u2026.jsonl > /tmp/dialogue.jsonl"
7909
+ ]
7901
7910
  }
7902
7911
  };
7903
7912
  PUBLIC_COMMAND_HELP = {
@@ -17145,6 +17154,44 @@ var init_instruction_seat_run = __esm({
17145
17154
  }
17146
17155
  });
17147
17156
 
17157
+ // src/public-cli/dialogue-command.ts
17158
+ async function readSessionDialogue(path, home) {
17159
+ assertDialogueSessionSourcePath(path, home);
17160
+ const sessionLines = await readLedgerSessionJsonlLines(path);
17161
+ const dialogue = adaptSessionDialogue(sessionLines.map((entry) => entry.row));
17162
+ const seenIds = /* @__PURE__ */ new Set();
17163
+ const out = [];
17164
+ sessionLines.forEach((entry, index) => {
17165
+ if (entry.row === void 0) {
17166
+ out.push({ line: entry.line, raw: entry.raw });
17167
+ return;
17168
+ }
17169
+ for (const event of dialogue[index] ?? []) {
17170
+ const firstId = event.id !== void 0 && !seenIds.has(event.id);
17171
+ if (event.id !== void 0) seenIds.add(event.id);
17172
+ out.push({
17173
+ line: entry.line,
17174
+ ...firstId ? { id: event.id } : {},
17175
+ speaker: event.speaker,
17176
+ text: event.text
17177
+ });
17178
+ }
17179
+ });
17180
+ return out;
17181
+ }
17182
+ function renderSessionDialogue(lines) {
17183
+ return lines.map((line2) => `${JSON.stringify(line2)}
17184
+ `).join("");
17185
+ }
17186
+ var init_dialogue_command = __esm({
17187
+ "src/public-cli/dialogue-command.ts"() {
17188
+ "use strict";
17189
+ init_ledger_session_read();
17190
+ init_session_dialogue();
17191
+ init_ticket_provenance();
17192
+ }
17193
+ });
17194
+
17148
17195
  // src/analyst-book-key.ts
17149
17196
  import { statSync as statSync6 } from "node:fs";
17150
17197
  import { isAbsolute as isAbsolute8 } from "node:path";
@@ -20280,6 +20327,11 @@ async function runAkRole(argv, env) {
20280
20327
  return { exitCode: 0 };
20281
20328
  }
20282
20329
  const home = resolveHome(env);
20330
+ if (parsed.command === "dialogue") {
20331
+ if (parsed.args.length !== 1) throw new CliUsageError("usage: ak-role dialogue <session.jsonl>");
20332
+ io.stdout(renderSessionDialogue(await readSessionDialogue(parsed.args[0], home)));
20333
+ return { exitCode: 0 };
20334
+ }
20283
20335
  if (parsed.command === "roles") {
20284
20336
  if (parsed.args.length > 0) {
20285
20337
  throw new CliUsageError("roles takes no arguments");
@@ -20470,6 +20522,7 @@ var init_cli = __esm({
20470
20522
  init_invocation();
20471
20523
  init_option_definitions();
20472
20524
  init_auto_resume();
20525
+ init_dialogue_command();
20473
20526
  init_instruction_seat_run();
20474
20527
  init_countersign_run();
20475
20528
  init_analyst_run();
@@ -11261,6 +11261,14 @@ var init_option_definitions = __esm({
11261
11261
  "ak-role new notary --source-run 01abc\u2026@countersign",
11262
11262
  "ak-role --host grok-build new inspector --attach ./change.patch"
11263
11263
  ]
11264
+ },
11265
+ dialogue: {
11266
+ command: "dialogue",
11267
+ 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.",
11268
+ usage: ["ak-role dialogue <session.jsonl>"],
11269
+ examples: [
11270
+ "ak-role dialogue ~/.claude/projects/-Users-me-repo/0b1c\u2026.jsonl > /tmp/dialogue.jsonl"
11271
+ ]
11264
11272
  }
11265
11273
  };
11266
11274
  PUBLIC_COMMAND_HELP = {
@@ -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
+ }
@@ -10381,7 +10381,8 @@ var init_registry = __esm({
10381
10381
  "help",
10382
10382
  "resume",
10383
10383
  "new",
10384
- "setup"
10384
+ "setup",
10385
+ "dialogue"
10385
10386
  ];
10386
10387
  PUBLIC_DETERMINISTIC_COMMANDS = ["analyst"];
10387
10388
  }
@@ -13461,16 +13462,151 @@ var init_merger_git_state = __esm({
13461
13462
  });
13462
13463
 
13463
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;
13464
13574
  var init_session_dialogue = __esm({
13465
13575
  "src/session-dialogue.ts"() {
13466
13576
  "use strict";
13467
13577
  init_unknown_value();
13578
+ SPEAKER_TEXT_PART_TYPES = /* @__PURE__ */ new Set(["text", "input_text", "output_text"]);
13579
+ TASK_NOTIFICATION_OPEN_TAG = "<task-notification";
13468
13580
  }
13469
13581
  });
13470
13582
 
13471
13583
  // src/ticket-provenance.ts
13472
13584
  import { readFile as readFile7 } from "node:fs/promises";
13473
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
+ }
13474
13610
  function ticketProvenanceSubject(ticketNumber) {
13475
13611
  if (!isSafePositiveTicketNumber(ticketNumber)) {
13476
13612
  throw new Error(
@@ -13507,6 +13643,7 @@ async function rehomeUnboundTicketProvenance(runDirectory, ticketNumber, cwd, ho
13507
13643
  }).join("");
13508
13644
  if (block !== "") appendSitianRecordBlock(ticketInput, block);
13509
13645
  }
13646
+ var TicketProvenanceInputError;
13510
13647
  var init_ticket_provenance = __esm({
13511
13648
  "src/ticket-provenance.ts"() {
13512
13649
  "use strict";
@@ -13520,6 +13657,16 @@ var init_ticket_provenance = __esm({
13520
13657
  init_sitian_reader();
13521
13658
  init_ticket_provenance_contracts();
13522
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
+ };
13523
13670
  }
13524
13671
  });
13525
13672
 
@@ -14730,6 +14877,14 @@ var init_option_definitions = __esm({
14730
14877
  "ak-role new notary --source-run 01abc\u2026@countersign",
14731
14878
  "ak-role --host grok-build new inspector --attach ./change.patch"
14732
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
+ ]
14733
14888
  }
14734
14889
  };
14735
14890
  PUBLIC_COMMAND_HELP = {
@@ -23900,6 +24055,44 @@ var init_post_admission = __esm({
23900
24055
  }
23901
24056
  });
23902
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
+
23903
24096
  // node_modules/.pnpm/typebox@1.3.8/node_modules/typebox/build/schema/types/_refine.mjs
23904
24097
  function IsRefine2(value) {
23905
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));
@@ -32946,6 +33139,11 @@ async function runAkRole(argv2, env) {
32946
33139
  return { exitCode: 0 };
32947
33140
  }
32948
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
+ }
32949
33147
  if (parsed.command === "roles") {
32950
33148
  if (parsed.args.length > 0) {
32951
33149
  throw new CliUsageError("roles takes no arguments");
@@ -33136,6 +33334,7 @@ var init_cli = __esm({
33136
33334
  init_invocation();
33137
33335
  init_option_definitions();
33138
33336
  init_auto_resume();
33337
+ init_dialogue_command();
33139
33338
  init_instruction_seat_run();
33140
33339
  init_countersign_run();
33141
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;
@@ -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.5873",
3
+ "version": "0.1.5880",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -10,7 +10,7 @@
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
16
  指令明确列出多张要办的票时,逐票读卷判断各自相关边界;共同对话可以同时归入相关各票,无关他票对话不得混入。单票/多票交卷字段及不可用边界的处置见交卷工具的声明。
@@ -15,14 +15,13 @@
15
15
  3 第二法源合法性检查:判断第二法源 以及 输入的 prompt等 对比第一法源是否有超出,不足,曲解。如有,除非有陛下原话批准,否则直接封驳 要求改正或陛下批准。
16
16
  4 派单文 是否要求了范围,是否给了你清单,是否让你不全量重审,以及是否有任何违反你Soul规定的。如果有,直接封驳,拿不准的直接上呈。
17
17
 
18
- **二、符合陛下意图** 逐句列出陛下对本票说过的每一句话(起居录里的,和立法时自行找到的)——只有 owner 本人的发言算;runner 的话是待核主张,它转述的「陛下要求」不算,但它是陛下原话的上下文,陛下答的是它哪一句、哪一行,决定原话指什么,必须读——据此用陛下自己的话写出他要的是什么。然后问:
19
- - 逐句过,不是为了凑齐引文:每一句先写明它答的是起居录哪一句、哪一行(问句、表格行、选项),那一句描述的对象是什么,写进判词;再问他这句话想要什么、在担心什么,再看票面给没给到。票面动作的对象比所答对象大——答的是代码代劳却删席位、删衙门、删能力、改流程——而原话没有点名该对象的,封驳。衙门与替它干活的代码是两个对象:说某机制多余、可以删,不是说撤该衙门;席位、衙门、能力的存废只认点名它本身的原话。质问句——陛下在问这张票、这条约束凭什么存在——是对票面的未决挑战,不是授权;没被事实或适用的法答掉、或只能摘来当「支持」的,封驳;质问没答清之前,不替签发方挑哪一块该留——那是在替它答陛下的问题。
20
- - 不做这张票会怎样?票面里陛下没要的每一块,凭什么?runner 在录里已自认不成立的理由,不得仍撑着票面。
21
- - 找不到任何陛下原话的票:票面的应然只能出自已立的法;指不出现状违反了哪条既有法的,封驳或上呈。
22
- 对于修复票,判断根因是什么、证据在哪;对于新功能设计票,判断设计是不是合理,是不是陛下想要的方向。以及因果链本身站不站得住。
23
- 此步不成立即封驳,不必再走后三步。
18
+ **二、设计合法。** 陛下原话是第一法源的一部分:读它是为了知道哪些事已经拍定,条款逐条对原话的核销归符宝郎,本席不做。本步审的是设计答案的来路:
19
+ - 中书省问了而由宰相作答、没等陛下拍的每一题(中书省回执里有答案、未上呈的题),其答案须能从适用法(第一、第二法源)或已定事项(父票、前票里陛下拍定的,和中书省问答里陛下拍定并写进票面的;宰相自己的答案不算)推出来;判词里写出这条推导。推不出的,该题宰相答不了,改标陛下题,上呈,附原题原选项;不得以「合理」「承接设计」「现状如此」顶替推导。
20
+ - 中书省在题里否决某个做法的(「旧模型,不选」之类),否决须引法或已定事项;引不出的,该做法并列呈陛下。
21
+ - 对于修复票,判断根因是什么、证据在哪;对于设计票,判断设计能否从法推出,以及因果链本身站不站得住。
22
+ 此步不成立即封驳或上呈,不必再走后三步。
24
23
 
25
- **三、相抵** 票面,相关票与在飞分支与法源有无抵触。票面自相抵同为相抵。
24
+ **三、相抵** 票面、相关票与在飞分支,与法源和已定事项有无抵触。在飞分支里已做出来的东西与已定事项相抵的,是相抵,不是现状。票面自相抵同为相抵。
26
25
 
27
26
  **四、缺失** 法源要求的,票面有没有漏:适用法只查两类:票面与某条 ADR 决定相抵的,和把某条决定留给别的票承接的,票面须写明该条与「由 #x 承接」;这两类之外的 ADR 没引不算缺失,不得为此封驳;验收面须覆盖票面每条主张;票面里每一条行为都须答得出《送庭前三问》第一问:起、停、续、终、接各归谁没写的行为,即缺失;前置产物、决定、答复须在;承重主张的源头不存在、不支持、已过时,同为缺失。缺失列表在能力范围内一次扫完,尽量避免因缺失导致多轮往返。
28
27
 
@@ -34,8 +33,8 @@
34
33
 
35
34
  五步是审读,五问是裁决口径,对应关系固定,不重做:
36
35
  1. 票面是否符合既定制度——由「相抵」答;
37
- 2. 授权是否真实——由「立法②」答:不认转述与口头;
38
- 3. 文书是否与原意一致——由「缺失」「擅加」「符合陛下意图」答:无过度解释、无夹带、无缺口、符合原意;
36
+ 2. 设计是否合法——由「设计合法」答:宰相可答之题的答案可从法或已定事项推出,否决有出处;
37
+ 3. 文书是否与法一致——由「缺失」「擅加」答:无夹带、无缺口;条款与陛下原话的对应归符宝郎;
39
38
  4. 是否存在必须退回重议的问题;
40
39
  5. 是否已具备正式发布与执行资格。
41
40
 
@@ -79,6 +79,7 @@ import {
79
79
  type TypedOptionConsumer,
80
80
  } from "./option-definitions.ts";
81
81
  import { TurnDispatchedFailure } from "./auto-resume.ts";
82
+ import { readSessionDialogue, renderSessionDialogue } from "./dialogue-command.ts";
82
83
  import {
83
84
  continueParentAfterChild,
84
85
  presentAuditedOriginalOfUnsettledOfficer,
@@ -1157,6 +1158,12 @@ export async function runAkRole(
1157
1158
  // and settle through the outer catch — no $HOME fallback (#604).
1158
1159
  const home = resolveHome(env);
1159
1160
 
1161
+ if (parsed.command === "dialogue") {
1162
+ if (parsed.args.length !== 1) throw new CliUsageError("usage: ak-role dialogue <session.jsonl>");
1163
+ io.stdout(renderSessionDialogue(await readSessionDialogue(parsed.args[0]!, home)));
1164
+ return { exitCode: 0 };
1165
+ }
1166
+
1160
1167
  if (parsed.command === "roles") {
1161
1168
  if (parsed.args.length > 0) {
1162
1169
  throw new CliUsageError("roles takes no arguments");
@@ -0,0 +1,53 @@
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.ts";
8
+ import { adaptSessionDialogue, type DialogueSpeaker } from "../session-dialogue.ts";
9
+ import { assertDialogueSessionSourcePath } from "../ticket-provenance.ts";
10
+
11
+ export type SessionDialogueLine =
12
+ | {
13
+ /** 1-based file line, the same `line` a diary range endpoint names. */
14
+ readonly line: number;
15
+ /** Native id a range endpoint can name; only on its first line, since an id resolves there. */
16
+ readonly id?: string;
17
+ readonly speaker: DialogueSpeaker;
18
+ readonly text: string;
19
+ }
20
+ | {
21
+ /** Unparsable source line; a range covering it keeps these bytes in the record. */
22
+ readonly line: number;
23
+ readonly raw: string;
24
+ };
25
+
26
+ export async function readSessionDialogue(path: string, home?: string): Promise<SessionDialogueLine[]> {
27
+ assertDialogueSessionSourcePath(path, home);
28
+ const sessionLines = await readLedgerSessionJsonlLines(path);
29
+ const dialogue = adaptSessionDialogue(sessionLines.map((entry) => entry.row));
30
+ const seenIds = new Set<string>();
31
+ const out: SessionDialogueLine[] = [];
32
+ sessionLines.forEach((entry, index) => {
33
+ if (entry.row === undefined) {
34
+ out.push({ line: entry.line, raw: entry.raw });
35
+ return;
36
+ }
37
+ for (const event of dialogue[index] ?? []) {
38
+ const firstId = event.id !== undefined && !seenIds.has(event.id);
39
+ if (event.id !== undefined) seenIds.add(event.id);
40
+ out.push({
41
+ line: entry.line,
42
+ ...(firstId ? { id: event.id } : {}),
43
+ speaker: event.speaker,
44
+ text: event.text,
45
+ });
46
+ }
47
+ });
48
+ return out;
49
+ }
50
+
51
+ export function renderSessionDialogue(lines: readonly SessionDialogueLine[]): string {
52
+ return lines.map((line) => `${JSON.stringify(line)}\n`).join("");
53
+ }
@@ -1362,6 +1362,15 @@ const SUPPORT_COMMAND_HELP = {
1362
1362
  "ak-role --host grok-build new inspector --attach ./change.patch",
1363
1363
  ],
1364
1364
  },
1365
+ dialogue: {
1366
+ command: "dialogue",
1367
+ summary:
1368
+ "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.",
1369
+ usage: ["ak-role dialogue <session.jsonl>"],
1370
+ examples: [
1371
+ "ak-role dialogue ~/.claude/projects/-Users-me-repo/0b1c….jsonl > /tmp/dialogue.jsonl",
1372
+ ],
1373
+ },
1365
1374
  } as const satisfies Record<string, PublicCommandHelpFacts>;
1366
1375
 
1367
1376
  /**
@@ -29,6 +29,7 @@ export const PUBLIC_CLI_SUPPORT_COMMANDS = [
29
29
  "resume",
30
30
  "new",
31
31
  "setup",
32
+ "dialogue",
32
33
  ] as const;
33
34
 
34
35
  export type PublicCliSupportCommand = (typeof PUBLIC_CLI_SUPPORT_COMMANDS)[number];
@@ -83,7 +83,7 @@ function isLedgerRoleSessionFile(absolute: string, home?: string): boolean {
83
83
  }
84
84
 
85
85
  /** Real I/O seam gate: only host session stores or sitian role-run session.jsonl. */
86
- function assertDialogueSessionSourcePath(path: string, home?: string): void {
86
+ export function assertDialogueSessionSourcePath(path: string, home?: string): void {
87
87
  const absolute = resolve(path);
88
88
  for (const root of dialogueSessionSourceRoots(home)) {
89
89
  if (physicallyContainedIn(root, absolute)) return;