@akagilnc/pi-workflow-roles 0.1.5598 → 0.1.5614

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 (43) hide show
  1. package/dist/acp-host/production-host.js +406 -366
  2. package/dist/compliance-transport.js +2 -5
  3. package/dist/doctor-auditor.js +1 -1
  4. package/dist/gatekeeper-role.js +1 -1
  5. package/dist/headless-host/production-host.js +370 -330
  6. package/dist/migrate-book-topology.js +301 -291
  7. package/dist/navigator-public-session.js +4 -4
  8. package/dist/navigator-work-base.js +5 -30
  9. package/dist/packaged-role-registry.js +4 -2
  10. package/dist/pi/role-turn-host.js +10 -1
  11. package/dist/public-cli/instruction-seat-run.js +133 -38
  12. package/dist/public-cli/invocation.js +23 -26
  13. package/dist/public-cli/main.js +250 -179
  14. package/dist/public-cli/option-definitions.js +19 -0
  15. package/dist/public-cli/post-admission.js +23 -22
  16. package/dist/public-cli/run-lifecycle.js +2 -14
  17. package/dist/public-cli/turn-request.js +4 -9
  18. package/dist/public-role-summons.js +5 -3
  19. package/dist/role-run-placement.js +7 -1
  20. package/package.json +1 -1
  21. package/scripts/build-package.mjs +2 -3
  22. package/src/audited-run-identity.ts +30 -0
  23. package/src/compliance-transport.ts +1 -4
  24. package/src/doctor-auditor.ts +1 -1
  25. package/src/gatekeeper-role.ts +1 -1
  26. package/src/host-contracts.ts +6 -1
  27. package/src/navigator-public-session.ts +9 -5
  28. package/src/navigator-work-base.ts +19 -34
  29. package/src/notary-role.ts +12 -21
  30. package/src/packaged-role-registry.ts +4 -4
  31. package/src/pi/role-turn-host.ts +11 -1
  32. package/src/public-cli/instruction-seat-run.ts +162 -42
  33. package/src/public-cli/invocation.ts +35 -26
  34. package/src/public-cli/option-definitions.ts +20 -0
  35. package/src/public-cli/post-admission.ts +28 -22
  36. package/src/public-cli/run-lifecycle.ts +2 -17
  37. package/src/public-cli/turn-request.ts +9 -8
  38. package/src/public-role-summons.ts +17 -6
  39. package/src/role-envelope.ts +4 -0
  40. package/src/role-run-placement.ts +10 -1
  41. package/src/role-runtime.ts +54 -31
  42. package/dist/auditor-dossier-tool.js +0 -47
  43. package/src/auditor-dossier-tool.ts +0 -63
@@ -121,6 +121,11 @@ export type PublicSummonRequest = {
121
121
  * (#946 / ADR 0082).
122
122
  */
123
123
  readonly stationChild?: boolean;
124
+ /**
125
+ * Navigator nest work-context locator (#1166). Request-scoped internal path —
126
+ * never dialogue content; never shared parent process.env across await.
127
+ */
128
+ readonly navigatorWorkContextPath?: string;
124
129
  };
125
130
 
126
131
  const execFileAsync = promisify(execFile);
@@ -421,6 +426,10 @@ export async function summonPublicRole(
421
426
  ? {}
422
427
  : { correlationId: options.correlationId }),
423
428
  ...(options.createRunId === undefined ? {} : { createRunId: options.createRunId }),
429
+ ...(options.navigatorWorkContextPath === undefined
430
+ || options.navigatorWorkContextPath.trim() === ""
431
+ ? {}
432
+ : { navigatorWorkContextPath: options.navigatorWorkContextPath }),
424
433
  },
425
434
  };
426
435
  } catch (error) {
@@ -746,13 +755,13 @@ export async function summonGateOfficer(options: {
746
755
  /**
747
756
  * Plain-language re-ask when the prior officer reply was not three-state (#753 / #756).
748
757
  * Officers: reviewReask → same-ticket resume summons.instruction.
749
- * Never concatenated into argv (inspector parentRunPath is the pure 卷宗指针).
758
+ * Never concatenated into argv (binding stays on --source-run / parentRunPath).
750
759
  */
751
760
  readonly reask?: string;
752
761
  /**
753
762
  * In-flight parent 交卷 body (tool-call arguments). Relayed verbatim as officer
754
763
  * dialogue content when reask is absent (#879). Binding pointer stays on
755
- * --source-run / 卷宗指针 / ticket argv — never a content substitute.
764
+ * --source-run / parentRunPath — never a content substitute.
756
765
  */
757
766
  readonly submission?: unknown;
758
767
  /** Pi-adapter inject — forwarded to summonPublicRole (not a parent-host override). */
@@ -808,6 +817,7 @@ export async function summonGateOfficer(options: {
808
817
  if (gateSummon === "subject-source") {
809
818
  // #756: judge compliance path — same queue law as notary/inspector.
810
819
  // Binding pointer on argv; dialogue content rides gateReviewInstruction.
820
+ // #1166: no code-authored 卷宗指针 instruction — identity is startup material.
811
821
  return summonPublicRole({
812
822
  role: options.officer,
813
823
  argv: [
@@ -815,7 +825,8 @@ export async function summonGateOfficer(options: {
815
825
  "judge",
816
826
  "--source-run",
817
827
  options.sourceRunDirectory,
818
- `卷宗指针:${options.sourceRunDirectory}`,
828
+ "--project",
829
+ options.cwd,
819
830
  ],
820
831
  ...common,
821
832
  });
@@ -850,11 +861,11 @@ export async function summonGateOfficer(options: {
850
861
  ...(courtTicket === undefined ? {} : { boundTicketNumber: courtTicket }),
851
862
  });
852
863
  }
853
- // Inspector: argv stays pure 卷宗指针 (#747 parentRunPath lookup key).
854
- // Content (body/reask) rides env → summons.instruction / first-mint prompt.
864
+ // Inspector: bind via --source-run (same face as notary); dialogue rides env.
865
+ // #1166: no code-authored 卷宗指针 instruction — identity is startup material.
855
866
  return summonPublicRole({
856
867
  role: options.officer,
857
- argv: [`卷宗指针:${options.sourceRunDirectory}`],
868
+ argv: ["--source-run", options.sourceRunDirectory, "--project", options.cwd],
858
869
  ...common,
859
870
  });
860
871
  }
@@ -178,6 +178,10 @@ export async function prepareRoleEnvelope(options: {
178
178
  ...(request.courtAttemptId === undefined ? {} : { courtAttemptId: request.courtAttemptId }),
179
179
  ...(request.invocationScopeId === undefined ? {} : { invocationScopeId: request.invocationScopeId }),
180
180
  ...(request.host === undefined || request.host.trim() === "" ? {} : { host: request.host.trim() }),
181
+ ...(request.navigatorWorkContextPath === undefined
182
+ || request.navigatorWorkContextPath.trim() === ""
183
+ ? {}
184
+ : { navigatorWorkContextPath: request.navigatorWorkContextPath }),
181
185
  sessionManager: {
182
186
  getLeafEntry: () => sessionEntries.at(-1) as ReturnType<HostContext["sessionManager"]["getLeafEntry"]>,
183
187
  getLeafId: () => runId,
@@ -1,5 +1,5 @@
1
1
  import { readdir } from "node:fs/promises";
2
- import { basename, dirname, join } from "node:path";
2
+ import { basename, dirname, join, resolve } from "node:path";
3
3
 
4
4
  import {
5
5
  activationBookDirectory,
@@ -54,6 +54,15 @@ export function runDirectoryOfSessionFile(sessionFile: string): string {
54
54
  return dirname(dirname(sessionFile));
55
55
  }
56
56
 
57
+ /** Climb session/session.jsonl → run directory when the host exposes a session file. */
58
+ export function auditorRunDirectory(context: {
59
+ readonly sessionManager?: { getSessionFile?: () => string | undefined };
60
+ }): string | undefined {
61
+ const sessionFile = context.sessionManager?.getSessionFile?.();
62
+ if (sessionFile === undefined) return undefined;
63
+ return resolve(runDirectoryOfSessionFile(sessionFile));
64
+ }
65
+
57
66
  /** `<run>/session` → run directory. */
58
67
  export function runDirectoryFromSessionDirectory(sessionDirectory: string): string {
59
68
  return dirname(sessionDirectory);
@@ -106,8 +106,13 @@ import {
106
106
  type AuditorRuntimeDependencies,
107
107
  } from "./auditor-role.ts";
108
108
 
109
+ import {
110
+ auditedRunIdentityFromDirectory,
111
+ auditedRunIdentityMaterial,
112
+ } from "./audited-run-identity.ts";
109
113
  import { formatNavigatorReport, NAVIGATOR_EVENT_TYPE, NAVIGATOR_ROUTE_PLAYBOOK_FAILURE_ENTRY, navigatorSubjectKey, navigatorUnavailableError, subjectPath, type NavigatorAttendance, type NavigatorAttendanceOptions, type NavigatorEvent, type NavigatorPhase, type NavigatorReport, type NavigatorSettlement, type NavigatorSubjectProvenance, type NavigatorWorkContext } from "./navigator-attendance.ts";
110
114
  import { loadNavigatorWorkBaseSuffix } from "./navigator-work-base.ts";
115
+ import { AK_ROLE_AUDITOR_SOURCE_RUN_ENV } from "./auditor-soul.ts";
111
116
  import {
112
117
  buildNavigatorInfrastructureFailureFact,
113
118
  classifyPackagedRoleTerminalResult,
@@ -690,7 +695,7 @@ export function createNavigatorRoleRuntime(
690
695
  const loadRoutePlaybook = dependencies.loadRoutePlaybook
691
696
  ?? (() => readPackageMaterial("resources/navigator-route-playbook.md"));
692
697
  let playbookRead: Promise<string> | undefined;
693
- roleHost.on("before_agent_start", async (event) => {
698
+ roleHost.on("before_agent_start", async (event, ctx) => {
694
699
  const basePrompt = typeof event.systemPrompt === "string" ? event.systemPrompt : "";
695
700
  const parts: string[] = [];
696
701
  try {
@@ -705,8 +710,10 @@ export function createNavigatorRoleRuntime(
705
710
  parts.push(message);
706
711
  }
707
712
  }
708
- const prompt = typeof event.prompt === "string" ? event.prompt : "";
709
- const work = await loadNavigatorWorkBaseSuffix(prompt);
713
+ const work = await loadNavigatorWorkBaseSuffix(
714
+ ctx.sessionManager?.getSessionDir?.(),
715
+ ctx.navigatorWorkContextPath,
716
+ );
710
717
  if (work !== undefined && work.trim() !== "") parts.push(work);
711
718
  if (parts.length === 0) return;
712
719
  const text = parts.join("\n\n");
@@ -723,7 +730,7 @@ export function createAuditorRoleRuntime(
723
730
  roleHost: RoleHost,
724
731
  dependencies: AuditorRuntimeDependencies,
725
732
  ) {
726
- const base = createFiledOfficerRuntime(
733
+ return createFiledOfficerRuntime(
727
734
  roleHost,
728
735
  {
729
736
  role: "auditor",
@@ -732,24 +739,39 @@ export function createAuditorRoleRuntime(
732
739
  },
733
740
  dependencies,
734
741
  );
735
- return {
736
- async activate() {
737
- await base.activate();
738
- // Same tools whether nested or direct (#675): dossier tool always registered.
739
- // Source run: only the shared --source-run input face (never own-run fallback).
740
- const { createAuditorDossierTool, AUDITOR_DOSSIER_TOOL_NAME } =
741
- await import("./auditor-dossier-tool.ts");
742
- const { AK_ROLE_AUDITOR_SOURCE_RUN_ENV } = await import("./auditor-soul.ts");
743
- const already = roleHost.getAllTools().some((tool) => tool.name === AUDITOR_DOSSIER_TOOL_NAME);
744
- if (already) return;
745
- const sourceRun =
746
- typeof process.env[AK_ROLE_AUDITOR_SOURCE_RUN_ENV] === "string"
747
- && process.env[AK_ROLE_AUDITOR_SOURCE_RUN_ENV].trim() !== ""
748
- ? process.env[AK_ROLE_AUDITOR_SOURCE_RUN_ENV].trim()
749
- : undefined;
750
- roleHost.registerTool(createAuditorDossierTool(sourceRun) as never);
751
- },
752
- };
742
+ }
743
+
744
+ function isAuditedIdentitySeat(role: string): boolean {
745
+ return role === "notary" || role === "inspector" || role === "auditor" || role === "countersign";
746
+ }
747
+
748
+ /** Resolve audited-run directory for identity material — this-leg flags / admitted page first. */
749
+ function resolveAuditedSourceRunDirectory(
750
+ roleHost: RoleHost,
751
+ ctx: HostContext,
752
+ role: string,
753
+ ): string | undefined {
754
+ const notary = roleHost.getFlag(NOTARY_SOURCE_RUN_FLAG.name);
755
+ if (typeof notary === "string" && notary.trim() !== "") return notary.trim();
756
+ const inspector = roleHost.getFlag(INSPECTOR_SOURCE_RUN_FLAG.name);
757
+ if (typeof inspector === "string" && inspector.trim() !== "") return inspector.trim();
758
+ // This-leg admitted binding wins over shared auditor env (#1166 J3).
759
+ const runDirectory = runDirectoryFromHostContext(ctx);
760
+ if (runDirectory !== undefined) {
761
+ const admitted = readPageSync(runDirectory, "admitted");
762
+ if (typeof admitted?.sourceRunPath === "string" && admitted.sourceRunPath.trim() !== "") {
763
+ return admitted.sourceRunPath.trim();
764
+ }
765
+ }
766
+ // Auditor env is request-scoped soul binding for the auditor seat only.
767
+ if (role === "auditor") {
768
+ const auditor =
769
+ typeof process.env[AK_ROLE_AUDITOR_SOURCE_RUN_ENV] === "string"
770
+ ? process.env[AK_ROLE_AUDITOR_SOURCE_RUN_ENV].trim()
771
+ : "";
772
+ if (auditor !== "") return auditor;
773
+ }
774
+ return undefined;
753
775
  }
754
776
 
755
777
  /**
@@ -1220,15 +1242,16 @@ export function createRoleRuntimeExtension(
1220
1242
  if (reading === undefined) return;
1221
1243
  return { readingMaterial: reading };
1222
1244
  });
1223
- roleHost.on("before_agent_start", () => {
1224
- const path = roleHost.getFlag(INSPECTOR_SOURCE_RUN_FLAG.name);
1225
- if (typeof path !== "string" || path.trim() === "") return;
1226
- return {
1227
- readingMaterial: {
1228
- kind: "inspector-parent-binding" as const,
1229
- sourceRunPath: path,
1230
- },
1231
- };
1245
+ // #1166 / ADR 0087: four audit seats share one audited-run identity material.
1246
+ // Notary also emits identity from its own before_agent_start; skip double fold.
1247
+ roleHost.on("before_agent_start", (_event, ctx) => {
1248
+ const role = selectedRole ?? roleHost.getFlag(ROLE_FLAG.name);
1249
+ if (typeof role !== "string" || !isAuditedIdentitySeat(role) || role === "notary") return;
1250
+ const sourceDirectory = resolveAuditedSourceRunDirectory(roleHost, ctx, role);
1251
+ if (sourceDirectory === undefined) return;
1252
+ const identity = auditedRunIdentityFromDirectory(sourceDirectory);
1253
+ if (identity === undefined) return;
1254
+ return { readingMaterial: auditedRunIdentityMaterial(identity) };
1232
1255
  });
1233
1256
  roleHost.on("tool_result", async (event, ctx) => {
1234
1257
  const role = selectedRole;
@@ -1,47 +0,0 @@
1
- import { join, resolve } from "node:path";
2
- import { RUN_HISTORY_FILE, RUN_LOG_FILE, runCurrentPath } from "./run-dossier.js";
3
- import {
4
- runDirectoryOfSessionFile,
5
- sessionFileOf
6
- } from "./role-run-placement.js";
7
- import { Type } from "typebox";
8
- const AUDITOR_DOSSIER_TOOL_NAME = "ak_get_run_dossier";
9
- function auditorRunDirectory(context) {
10
- const sessionFile = context.sessionManager?.getSessionFile?.();
11
- if (sessionFile === void 0) return void 0;
12
- return resolve(runDirectoryOfSessionFile(sessionFile));
13
- }
14
- function createAuditorDossierTool(runDirectory) {
15
- return {
16
- name: AUDITOR_DOSSIER_TOOL_NAME,
17
- description: "\u5B9A\u4F4D\u672C\u5BA1\u8BA1\u5E2D\u7ED1\u5B9A\u7684 run \u5377\u5B97\u53CA\u5176\u8BC1\u636E\u5165\u53E3\u3002",
18
- // #836 r16 class 3: execute() ignores `_params` entirely — a closed root
19
- // only rejects the role for saying more.
20
- parameters: Type.Object({}, { additionalProperties: true }),
21
- async execute(_id, _params) {
22
- if (runDirectory === void 0) {
23
- return {
24
- content: [{ type: "text", text: "\u672C\u5BA1\u8BA1\u5E2D\u65E0\u7ED1\u5B9A run \u5377\u5B97\u8BB0\u5F55\u3002" }],
25
- details: void 0
26
- };
27
- }
28
- const details = {
29
- runDirectory,
30
- current: runCurrentPath(runDirectory),
31
- parentSessionCandidate: sessionFileOf(runDirectory),
32
- attachments: join(runDirectory, "attachments"),
33
- log: join(runDirectory, RUN_LOG_FILE),
34
- history: join(runDirectory, RUN_HISTORY_FILE)
35
- };
36
- return {
37
- content: [{ type: "text", text: JSON.stringify(details) }],
38
- details
39
- };
40
- }
41
- };
42
- }
43
- export {
44
- AUDITOR_DOSSIER_TOOL_NAME,
45
- auditorRunDirectory,
46
- createAuditorDossierTool
47
- };
@@ -1,63 +0,0 @@
1
- import { join, resolve } from "node:path";
2
-
3
- import { RUN_HISTORY_FILE, RUN_LOG_FILE, runCurrentPath } from "./run-dossier.ts";
4
- import type { AgentToolResult, ExtensionContext } from "@earendil-works/pi-coding-agent";
5
- import type { HostContext } from "./host-contracts.ts";
6
- import {
7
- runDirectoryOfSessionFile,
8
- sessionFileOf,
9
- } from "./role-run-placement.ts";
10
- import { Type } from "typebox";
11
-
12
- export const AUDITOR_DOSSIER_TOOL_NAME = "ak_get_run_dossier" as const;
13
-
14
- export type AuditorDossierLocation = {
15
- readonly runDirectory: string;
16
- readonly current: string;
17
- readonly parentSessionCandidate: string;
18
- readonly attachments: string;
19
- readonly log: string;
20
- readonly history: string;
21
- };
22
-
23
- /** Resolve the exact run binding already carried by the parent record session. */
24
- export function auditorRunDirectory(context: ExtensionContext | HostContext): string | undefined {
25
- const sessionFile = context.sessionManager?.getSessionFile?.();
26
- if (sessionFile === undefined) return undefined;
27
- // Sole layout: <run>/session/session.jsonl → climb two levels to the run directory.
28
- return resolve(runDirectoryOfSessionFile(sessionFile));
29
- }
30
-
31
- /** The one shared, run-bound dossier locator exposed to every auditor seat. */
32
- export function createAuditorDossierTool(
33
- runDirectory: string | undefined,
34
- ) {
35
- return {
36
- name: AUDITOR_DOSSIER_TOOL_NAME,
37
- description: "定位本审计席绑定的 run 卷宗及其证据入口。",
38
- // #836 r16 class 3: execute() ignores `_params` entirely — a closed root
39
- // only rejects the role for saying more.
40
- parameters: Type.Object({}, { additionalProperties: true }),
41
- async execute(_id: string, _params: unknown): Promise<AgentToolResult<AuditorDossierLocation | undefined>> {
42
- if (runDirectory === undefined) {
43
- return {
44
- content: [{ type: "text", text: "本审计席无绑定 run 卷宗记录。" }],
45
- details: undefined,
46
- };
47
- }
48
- // #836: whole run directory pointer — officer finds materials; no leaf preference (A7.2).
49
- const details: AuditorDossierLocation = {
50
- runDirectory,
51
- current: runCurrentPath(runDirectory),
52
- parentSessionCandidate: sessionFileOf(runDirectory),
53
- attachments: join(runDirectory, "attachments"),
54
- log: join(runDirectory, RUN_LOG_FILE),
55
- history: join(runDirectory, RUN_HISTORY_FILE),
56
- };
57
- return {
58
- content: [{ type: "text", text: JSON.stringify(details) }],
59
- details,
60
- };
61
- },
62
- };
63
- }