@akagilnc/pi-workflow-roles 0.1.2521 → 0.1.2653-next.71811ca

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 (109) hide show
  1. package/README.md +129 -39
  2. package/README.zh-CN.md +128 -39
  3. package/dist/activation-ledger-topology.js +1 -1
  4. package/dist/archivist-record-entry.js +47 -26
  5. package/dist/auditor-dossier-tool.js +3 -1
  6. package/dist/compliance-transport.js +17 -26
  7. package/dist/doctor-contracts.js +1 -2
  8. package/dist/dossier-resolution.js +4 -8
  9. package/dist/engine-detour-tool.js +33 -8
  10. package/dist/engine-detour.js +1 -0
  11. package/dist/evidence-child-executor.js +277 -424
  12. package/dist/host-contracts.js +22 -0
  13. package/dist/institutional-resolution.js +88 -0
  14. package/dist/merger-contracts.js +1 -2
  15. package/dist/navigator-attendance.js +16 -0
  16. package/dist/notary-contracts.js +2 -3
  17. package/dist/package-contracts/fixer-output.js +1 -2
  18. package/dist/packaged-role-registry.js +102 -9
  19. package/dist/pi/in-process-session.js +671 -0
  20. package/dist/public-cli/config.js +7 -5
  21. package/dist/public-cli/main.js +3767 -4113
  22. package/dist/reviewer-child-executor.js +6 -1
  23. package/dist/session-opening-materials.js +10 -38
  24. package/dist/sitian-appender.js +146 -0
  25. package/dist/sitian-contracts.js +21 -0
  26. package/dist/sitian-facade.js +15 -0
  27. package/dist/sitian-reader.js +52 -0
  28. package/extensions/role-runtime.ts +9 -7
  29. package/package.json +5 -5
  30. package/resources/engines/cursor.md +12 -26
  31. package/resources/engines/hermes.md +2 -23
  32. package/resources/engines/opus.md +1 -16
  33. package/resources/navigator-route-playbook.md +2 -2
  34. package/scripts/build-package.mjs +0 -1
  35. package/souls/quality-law.md +1 -1
  36. package/src/activation-ledger-topology.ts +1 -1
  37. package/src/archivist-record-entry.ts +57 -45
  38. package/src/auditor-dossier-tool.ts +4 -2
  39. package/src/canonical-skill-binding.ts +37 -27
  40. package/src/collector-role.ts +11 -20
  41. package/src/collector-tool-schemas.ts +1 -4
  42. package/src/compliance-transport.ts +19 -44
  43. package/src/doctor-auditor.ts +1 -5
  44. package/src/doctor-contracts.ts +1 -4
  45. package/src/doctor-role.ts +31 -8
  46. package/src/dossier-resolution.ts +8 -8
  47. package/src/engine-detour-tool.ts +48 -19
  48. package/src/engine-detour.ts +3 -0
  49. package/src/evidence-child-executor.ts +277 -485
  50. package/src/factory-board.ts +3 -3
  51. package/src/gatekeeper-role.ts +21 -10
  52. package/src/host-contracts.ts +386 -0
  53. package/src/in-process-session.ts +1 -115
  54. package/src/institutional-resolution.ts +135 -0
  55. package/src/judge-auditor.ts +1 -5
  56. package/src/judge-role.ts +37 -51
  57. package/src/merger-contracts.ts +1 -4
  58. package/src/merger-role.ts +7 -11
  59. package/src/navigator-attendance.ts +20 -1
  60. package/src/notary-contracts.ts +8 -11
  61. package/src/notary-role.ts +7 -23
  62. package/src/notary-source-run.ts +4 -3
  63. package/src/package-contracts/fixer-output.ts +1 -4
  64. package/src/package-resources/method-skill-binding.ts +10 -34
  65. package/src/packaged-role-registry.ts +122 -12
  66. package/src/pi/adapter.ts +192 -0
  67. package/src/pi/durable-principal.ts +98 -0
  68. package/src/pi/in-process-session.ts +819 -0
  69. package/src/pi/known-failure.ts +77 -0
  70. package/src/pi/pi-normalization.ts +284 -0
  71. package/src/pi/role-turn-host.ts +459 -0
  72. package/src/public-cli/auto-resume.ts +42 -44
  73. package/src/public-cli/cli.ts +171 -257
  74. package/src/public-cli/coder-run.ts +115 -433
  75. package/src/public-cli/collector-run.ts +62 -314
  76. package/src/public-cli/config.ts +7 -5
  77. package/src/public-cli/doctor-run.ts +41 -52
  78. package/src/public-cli/fixer-run.ts +122 -427
  79. package/src/public-cli/invocation.ts +287 -96
  80. package/src/public-cli/judge-run.ts +92 -422
  81. package/src/public-cli/merger-run.ts +167 -429
  82. package/src/public-cli/notary-run.ts +41 -47
  83. package/src/public-cli/option-definitions.ts +136 -3
  84. package/src/public-cli/post-admission.ts +497 -0
  85. package/src/public-cli/public-run-credentials.ts +4 -4
  86. package/src/public-cli/reviewer-dispatch-rejection.ts +106 -0
  87. package/src/public-cli/reviewer-run.ts +132 -449
  88. package/src/public-cli/run-lifecycle.ts +339 -327
  89. package/src/public-cli/settlement.ts +251 -134
  90. package/src/public-cli/terminal.ts +3 -9
  91. package/src/public-cli/turn-request.ts +87 -0
  92. package/src/reviewer-child-executor.ts +9 -1
  93. package/src/reviewer-role.ts +11 -20
  94. package/src/role-runtime.ts +129 -78
  95. package/src/session-opening-materials.ts +25 -39
  96. package/src/sitian-appender.ts +171 -0
  97. package/src/sitian-contracts.ts +106 -0
  98. package/src/sitian-facade.ts +18 -0
  99. package/src/sitian-reader.ts +62 -0
  100. package/src/worker-role.ts +42 -40
  101. package/src/worker-submission-gates.ts +33 -0
  102. package/dist/archivist-role-run-coordinates.js +0 -28
  103. package/dist/package-contracts/terminating-infrastructure.js +0 -90
  104. package/resources/engines/sonnet.md +0 -27
  105. package/resources/engines/zcode.md +0 -63
  106. package/src/archivist-role-run-coordinates.ts +0 -45
  107. package/src/package-contracts/terminating-infrastructure.ts +0 -132
  108. package/src/public-cli/explicit-internal.ts +0 -427
  109. package/src/public-cli/one-shot-dispatch.ts +0 -300
@@ -1,6 +1,14 @@
1
1
  import { writeSync } from "node:fs";
2
- import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
+ import {
4
+ ExplicitInternalActivationError,
5
+ type HostContext,
6
+ type RoleHost,
7
+ } from "./host-contracts.ts";
8
+ import { recordReviewerDispatchRejectionSync } from "./public-cli/reviewer-dispatch-rejection.ts";
9
+ import { createPiRoleHostAdapter, toPiContext, type PiRoleHostAdapter } from "./pi/adapter.ts";
3
10
  import { Value } from "typebox/value";
11
+ import { sitianReport } from "./sitian-facade.ts";
4
12
 
5
13
  import { activationTraceRecordSchema, namedActivationCause, type ActivationTraceRecord, type ActivationTraceWriter } from "./activation-trace.ts";
6
14
  import {
@@ -39,7 +47,7 @@ import {
39
47
  import type { ComplianceDecision } from "./compliance-transport.ts";
40
48
  import { createDoctorRoleRuntime } from "./doctor-role.ts";
41
49
  import { createNotaryRoleRuntime } from "./notary-role.ts";
42
- import { decorateSettlementWithNavigation, formatNavigatorReport, NAVIGATOR_EVENT_TYPE, navigatorSubjectKey, navigatorUnavailableError, subjectPath, type NavigatorAttendance, type NavigatorEvent, type NavigatorPhase, type NavigatorReport, type NavigatorSettlement, type NavigatorSubjectProvenance, type NavigatorWorkContext } from "./navigator-attendance.ts";
50
+ import { decorateSettlementWithNavigation, formatNavigatorReport, NAVIGATOR_EVENT_TYPE, navigatorSubjectKey, navigatorUnavailableError, subjectPath, type NavigatorAttendance, type NavigatorAttendanceOptions, type NavigatorEvent, type NavigatorPhase, type NavigatorReport, type NavigatorSettlement, type NavigatorSubjectProvenance, type NavigatorWorkContext } from "./navigator-attendance.ts";
43
51
  import {
44
52
  buildNavigatorInfrastructureFailureFact,
45
53
  classifyPackagedRoleTerminalResult,
@@ -48,10 +56,6 @@ import {
48
56
  resolveLifecycleInvocationPrincipal,
49
57
  } from "./navigator-invocation-identity.ts";
50
58
  import { recordTypedProviderHttpStatus } from "./typed-provider-http.ts";
51
- import {
52
- ExplicitInternalActivationError,
53
- recordReviewerDispatchRejectionSync,
54
- } from "./public-cli/explicit-internal.ts";
55
59
  import { NAVIGATOR_POST_ROLE_GRACE_MS, raceNavigatorGrace } from "./public-cli/settlement.ts";
56
60
  import { PACKAGED_ROLE_REGISTRY, packagedRoleMetadata, packagedRoleOutputTool, packagedRolePhaseFlag, type PackagedRole } from "./packaged-role-registry.ts";
57
61
  import { isAuditEscalationProjection } from "./audit-escalation.ts";
@@ -197,7 +201,13 @@ import {
197
201
  FIXER_FLAG_DEFINITIONS,
198
202
  FIXER_OUTPUT_TOOL_NAME,
199
203
  FIXER_PHASES,
204
+ type CoderSkillExpansionEvidenceMissingResult,
200
205
  } from "./worker-role.ts";
206
+
207
+ /** One envelope map for Gatekeeper non-pass and other correct submission rejects (#525). */
208
+ type SubmissionNonPassResult =
209
+ | GatekeeperNonPassResult
210
+ | CoderSkillExpansionEvidenceMissingResult;
201
211
  import { JUDGE_OUTPUT_TOOL_NAME } from "./package-contracts/judge-output.ts";
202
212
  import { REVIEWER_OUTPUT_TOOL_NAME } from "./package-contracts/reviewer-output.ts";
203
213
  import { DOCTOR_OUTPUT_TOOL_NAME } from "./doctor-contracts.ts";
@@ -268,7 +278,7 @@ export {
268
278
  } from "./tool-execution-observation.ts";
269
279
  export type { ToolExecutionObservationRecord, ToolExecutionObservationWriter } from "./tool-execution-observation.ts";
270
280
  export { executeAuditorChild } from "./evidence-child-executor.ts";
271
- export type { AuditorCompletion, AuditorDecisionTool } from "./evidence-child-executor.ts";
281
+ export type { AuditorDecisionTool } from "./evidence-child-executor.ts";
272
282
  export {
273
283
  NOTARY_OUTPUT_TOOL,
274
284
  INSPECTOR_OUTPUT_TOOL,
@@ -300,10 +310,13 @@ export {
300
310
  } from "./reviewer-role.ts";
301
311
  export {
302
312
  CODER_OUTPUT_TOOL_NAME,
313
+ CODER_SKILL_EXPANSION_EVIDENCE_MISSING_CODE,
314
+ CoderSkillExpansionEvidenceMissingError,
303
315
  FIXER_FLAG_DEFINITIONS,
304
316
  FIXER_OUTPUT_TOOL_NAME,
305
317
  FIXER_PHASES,
306
318
  type CoderOutput,
319
+ type CoderSkillExpansionEvidenceMissingResult,
307
320
  type FixerOutput,
308
321
  type WorkerOutput,
309
322
  } from "./worker-role.ts";
@@ -344,19 +357,19 @@ export type { MergerGitState, ActiveMergerGitState, CompletedMergerGitState } fr
344
357
  export type { MergerRoleDependencies } from "./merger-role.ts";
345
358
 
346
359
  type WorkerArmable = {
347
- activate(context?: ExtensionContext): Promise<void>;
360
+ activate(context?: HostContext): Promise<void>;
348
361
  armSubmissionGate(cwd: string, parent?: { getSessionFile(): string | undefined }): void;
349
362
  };
350
363
 
351
364
  type ActivationRuntime = {
352
365
  event: { reason: string };
353
- context: ExtensionContext;
366
+ context: HostContext;
354
367
  judge: { activate(): Promise<void> };
355
368
  fixer: WorkerArmable;
356
369
  coder: WorkerArmable;
357
370
  reviewer: {
358
371
  activate(
359
- context: ExtensionContext,
372
+ context: HostContext,
360
373
  admitted: ReviewerAdmittedInputs,
361
374
  ): Promise<ReviewerActivation>;
362
375
  };
@@ -364,7 +377,7 @@ type ActivationRuntime = {
364
377
  decodeReviewerAdmitted(): ReviewerAdmittedInputs;
365
378
  /** Envelope stores live parent activation for agent_start prompt assembly. */
366
379
  bindReviewerParent(activation: ReviewerActivation): void;
367
- collector: { activate(context: ExtensionContext, event: { reason: string }): Promise<void> };
380
+ collector: { activate(context: HostContext, event: { reason: string }): Promise<void> };
368
381
  doctor: { activate(): Promise<void> };
369
382
  notary: { activate(): Promise<void> };
370
383
  merger(): Promise<void>;
@@ -492,22 +505,22 @@ export type RoleRuntimeDependencies = {
492
505
  loadMergerInput?(path: string): Promise<unknown>;
493
506
  mergerGitState?: MergerRoleDependencies["gitState"];
494
507
  createMergerGitState?(repositoryRoot: string): MergerRoleDependencies["gitState"];
495
- auditDoctorCompliance?(options: { context: ExtensionContext; signal?: AbortSignal }): Promise<ComplianceDecision>;
508
+ auditDoctorCompliance?(options: { context: HostContext; signal?: AbortSignal }): Promise<ComplianceDecision>;
496
509
  createCollectorClock?(): CollectorClock;
497
510
  collectorPackageExtensionPath?: string;
498
- createNavigatorAttendance?(options: { context: ExtensionContext; role: string; phase: NavigatorPhase; subjectKey: string; subject: string; authority: string; contextError?: unknown; invocationId: string; onEvent: (event: import("./navigator-attendance.ts").NavigatorEvent, report: import("./navigator-attendance.ts").NavigatorReport) => void | Promise<void> }): NavigatorAttendanceDependency | Promise<NavigatorAttendanceDependency>;
499
- loadNavigatorWorkContext?(options: { context: ExtensionContext; role: string; phase: NavigatorPhase }): Promise<NavigatorWorkContext>;
511
+ createNavigatorAttendance?(options: { context: HostContext; role: string; phase: NavigatorPhase; subjectKey: string; subject: string; authority: string; contextError?: unknown; invocationId: string; onEvent: (event: import("./navigator-attendance.ts").NavigatorEvent, report: import("./navigator-attendance.ts").NavigatorReport) => void | Promise<void> }): NavigatorAttendanceDependency | Promise<NavigatorAttendanceDependency>;
512
+ loadNavigatorWorkContext?(options: { context: HostContext; role: string; phase: NavigatorPhase }): Promise<NavigatorWorkContext>;
500
513
  loadCanonicalSkillBinding?(
501
514
  name: "tdd" | "code-review",
502
515
  ): Promise<AnyCanonicalSkillBinding>;
503
516
  runReviewerDispatch?(
504
517
  dispatch: AcceptedReviewerExecution,
505
- options: { context: ExtensionContext; signal?: AbortSignal },
518
+ options: { context: HostContext; signal?: AbortSignal },
506
519
  ): Promise<ReviewerDispatchRunResult>;
507
520
  shutdownReviewerAgent?(): Promise<void>;
508
- transcriptFromContext(ctx: ExtensionContext): string;
521
+ transcriptFromContext(ctx: HostContext): string;
509
522
  auditSoulCompliance(
510
- options: { context: ExtensionContext; signal?: AbortSignal },
523
+ options: { context: HostContext; signal?: AbortSignal },
511
524
  ): Promise<SoulAuditResult>;
512
525
  activationClock?(): string;
513
526
  activationTraceWriter?: (record: ActivationTraceRecord) => void | Promise<void>;
@@ -524,12 +537,11 @@ export type RoleRuntimeDependencies = {
524
537
  oauthKeepalive?: OAuthKeepaliveOptions;
525
538
  };
526
539
 
527
- function abortContext(ctx: ExtensionContext): void {
528
- const abort = (ctx as ExtensionContext & { abort?: () => void }).abort;
529
- if (typeof abort === "function") abort.call(ctx);
540
+ function abortContext(ctx: { abort(): void }): void {
541
+ ctx.abort();
530
542
  }
531
543
 
532
- function failInfrastructure(error: unknown, ctx: ExtensionContext): never {
544
+ function failInfrastructure(error: unknown, ctx: { mode: string; abort(): void }): never {
533
545
  abortContext(ctx);
534
546
  if (ctx.mode === "print" || ctx.mode === "json") process.exitCode = 1;
535
547
  throw error;
@@ -564,11 +576,11 @@ function buildPendingInfrastructureFailure(error: unknown): PendingInfrastructur
564
576
  };
565
577
  }
566
578
 
567
- function navigatorPhase(pi: ExtensionAPI, role: string): NavigatorPhase {
579
+ function navigatorPhase(roleHost: RoleHost, role: string): NavigatorPhase {
568
580
  const metadata = packagedRoleMetadata(role);
569
581
  if (metadata === undefined || metadata.phases[0] === null) return null;
570
582
  const phaseFlag = packagedRolePhaseFlag(role);
571
- const requested = phaseFlag === undefined ? undefined : pi.getFlag(phaseFlag);
583
+ const requested = phaseFlag === undefined ? undefined : roleHost.getFlag(phaseFlag);
572
584
  return requested === "apply" ? "apply" : "plan";
573
585
  }
574
586
 
@@ -604,12 +616,15 @@ export function publicNavigatorSettlement(role: string, phase: NavigatorPhase, e
604
616
 
605
617
  export function createRoleRuntimeExtension(
606
618
  dependencies: RoleRuntimeDependencies,
619
+ injectedPiHostAdapter?: PiRoleHostAdapter,
607
620
  ): (pi: ExtensionAPI) => void {
608
621
  return (pi) => {
609
- pi.registerFlag(ROLE_FLAG.name, ROLE_FLAG.definition);
622
+ const piHostAdapter = injectedPiHostAdapter ?? createPiRoleHostAdapter(pi);
623
+ const roleHost = piHostAdapter.host;
624
+ roleHost.registerFlag(ROLE_FLAG.name, ROLE_FLAG.definition);
610
625
  // Reviewer transport flags: shared envelope owns registration (ADR 0018).
611
626
  for (const flag of REVIEWER_TRANSPORT_FLAGS) {
612
- pi.registerFlag(flag.name, flag.definition);
627
+ roleHost.registerFlag(flag.name, flag.definition);
613
628
  }
614
629
 
615
630
  let admitted = false;
@@ -627,15 +642,15 @@ export function createRoleRuntimeExtension(
627
642
  let navigatorWorkContext: NavigatorWorkContext | undefined;
628
643
  /** toolCallId → fact+evidence; one-shot projected onto durable tool_result (#475). */
629
644
  const pendingInfrastructureFailures = new Map<string, PendingInfrastructureFailure>();
630
- // Envelope-owned execute→tool_result bridge for Gatekeeper non-pass (ADR 0018).
631
- const pendingGatekeeperNonPassByToolCallId = new Map<string, GatekeeperNonPassResult>();
632
- let engineDetourRegistered = false;
645
+ // Envelope-owned execute→tool_result bridge for submission non-pass (ADR 0018 / #525).
646
+ const pendingSubmissionNonPassByToolCallId = new Map<string, SubmissionNonPassResult>();
647
+ let engineDetourRegistration: ReturnType<typeof registerEngineDetourTool> | undefined;
633
648
  // #288 primary-session thin adapter. The policy is the sole budget owner;
634
649
  // terminating-tool rejections and mechanical delivery requests share two turns.
635
650
  let receiptDelivery = createReceiptDeliveryPolicy();
636
651
  let noReceiptRecorded = false;
637
- pi.on("input", (event) => {
638
- const role = pi.getFlag(ROLE_FLAG.name);
652
+ roleHost.on("input", (event) => {
653
+ const role = roleHost.getFlag(ROLE_FLAG.name);
639
654
  if (role !== undefined && !admitted) return { action: "handled" as const };
640
655
  // Envelope exclusively owns Reviewer Skill invocation transform + original-request capture.
641
656
  if (
@@ -653,8 +668,8 @@ export function createRoleRuntimeExtension(
653
668
  }
654
669
  return { action: "continue" as const };
655
670
  });
656
- pi.on("before_agent_start", (event, ctx) => {
657
- const role = pi.getFlag(ROLE_FLAG.name);
671
+ roleHost.on("before_agent_start", (event, ctx) => {
672
+ const role = roleHost.getFlag(ROLE_FLAG.name);
658
673
  if (role === undefined) return;
659
674
  if (!admitted || selectedRole !== role) {
660
675
  failInfrastructure(new ActivationBarrierError(role), ctx);
@@ -691,7 +706,10 @@ export function createRoleRuntimeExtension(
691
706
  if (!reviewerExpansionCaptured) {
692
707
  if (
693
708
  reviewerOriginalRequest === undefined
694
- || activeReviewerParent.skillBinding.captureExpansion(event.prompt, reviewerOriginalRequest) === undefined
709
+ || activeReviewerParent.skillBinding.captureExpansion(
710
+ roleHost.capabilities?.skillExpansion(event.prompt),
711
+ reviewerOriginalRequest,
712
+ ) === undefined
695
713
  ) {
696
714
  failInfrastructure(
697
715
  new Error("Canonical code-review Skill expansion did not match the captured request"),
@@ -710,7 +728,7 @@ export function createRoleRuntimeExtension(
710
728
  };
711
729
  }
712
730
  });
713
- pi.on("tool_result", async (event) => {
731
+ roleHost.on("tool_result", async (event) => {
714
732
  const role = selectedRole;
715
733
  if (role === undefined) return;
716
734
  const pendingInfra = pendingInfrastructureFailures.get(event.toolCallId);
@@ -729,14 +747,14 @@ export function createRoleRuntimeExtension(
729
747
  receiptDelivery.recordAccepted();
730
748
  } else if (isOutputTool && outputClassification?.kind === "nonterminal" && event.isError) {
731
749
  const reason = (event.content ?? [])
732
- .map((part) => part.type === "text" ? part.text : "")
750
+ .map((part) => part.type === "text" && "text" in part ? part.text : "")
733
751
  .join("")
734
752
  .trim() || "terminating tool rejected";
735
753
  receiptDelivery.recordRejected(reason);
736
754
  }
737
755
  const settlement = publicNavigatorSettlement(
738
756
  role,
739
- navigatorPhase(pi, role),
757
+ navigatorPhase(roleHost, role),
740
758
  classified,
741
759
  );
742
760
  if (settlement !== undefined) {
@@ -768,7 +786,7 @@ export function createRoleRuntimeExtension(
768
786
  disposition: "unavailable",
769
787
  invocationId: "post-role-grace-timeout",
770
788
  role,
771
- phase: navigatorPhase(pi, role),
789
+ phase: navigatorPhase(roleHost, role),
772
790
  subjectKey: workContext?.subjectKey ?? "",
773
791
  unavailableReason: "Navigator exceeded post-role delivery grace",
774
792
  unavailableSource: "unknown",
@@ -790,12 +808,12 @@ export function createRoleRuntimeExtension(
790
808
  if (infrastructureDetails !== undefined) {
791
809
  return { details: infrastructureDetails, isError: true };
792
810
  }
793
- // Gatekeeper non-pass: throw kept message text for the model; project the
811
+ // Submission non-pass: throw kept message text for the model; project the
794
812
  // envelope-bound structured result onto session details at this tool_result seam.
795
- const gatekeeperNonPass = pendingGatekeeperNonPassByToolCallId.get(event.toolCallId);
796
- if (gatekeeperNonPass !== undefined) {
797
- pendingGatekeeperNonPassByToolCallId.delete(event.toolCallId);
798
- return { details: gatekeeperNonPass, isError: true };
813
+ const submissionNonPass = pendingSubmissionNonPassByToolCallId.get(event.toolCallId);
814
+ if (submissionNonPass !== undefined) {
815
+ pendingSubmissionNonPassByToolCallId.delete(event.toolCallId);
816
+ return { details: submissionNonPass, isError: true };
799
817
  }
800
818
  // Recommendation rides the accepted settlement record's content so the one
801
819
  // mandatory last-ak_*_output extraction surfaces route/next/reason without
@@ -812,7 +830,7 @@ export function createRoleRuntimeExtension(
812
830
  // already decided no queued continuation will run, so a triggerTurn there is
813
831
  // too late for print/json sessions. `agent_end` is the last production seam
814
832
  // whose queued next turn is consumed before settlement.
815
- pi.on("agent_end", (event) => {
833
+ roleHost.on("agent_end", (event, ctx) => {
816
834
  const lastMessage = event.messages.at(-1);
817
835
  if (lastMessage?.role === "assistant"
818
836
  && (lastMessage.stopReason === "error" || lastMessage.stopReason === "aborted")) {
@@ -826,6 +844,16 @@ export function createRoleRuntimeExtension(
826
844
  // Keep the package-owned continuation off the public input lifecycle:
827
845
  // one-shot roles must not mistake this delivery request for later caller input.
828
846
  pi.appendEntry("ak-receipt-delivery-request");
847
+ try {
848
+ sitianReport({
849
+ level: "event",
850
+ kind: "receipt-delivery",
851
+ cwd: ctx.cwd,
852
+ sessionParent: ctx.sessionManager.getSessionFile(),
853
+ payload: { type: "ak-receipt-delivery-request" },
854
+ source: "role-runtime",
855
+ });
856
+ } catch {}
829
857
  pi.sendMessage({
830
858
  customType: "ak-receipt-delivery-prompt",
831
859
  content: RECEIPT_DELIVERY_PROMPT,
@@ -835,14 +863,22 @@ export function createRoleRuntimeExtension(
835
863
  const runPointer = process.env.AK_ROLE_RUN_DIR;
836
864
  if (runPointer !== undefined) {
837
865
  noReceiptRecorded = true;
838
- pi.appendEntry(
839
- NO_RECEIPT_LIFECYCLE_ENTRY_TYPE,
840
- receiptDelivery.facts({ runPointer, attemptPointer: `current:${runPointer}` }),
841
- );
866
+ const facts = receiptDelivery.facts({ runPointer, attemptPointer: `current:${runPointer}` });
867
+ pi.appendEntry(NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, facts);
868
+ try {
869
+ sitianReport({
870
+ level: "event",
871
+ kind: "no-receipt-lifecycle",
872
+ cwd: ctx.cwd,
873
+ sessionParent: ctx.sessionManager.getSessionFile(),
874
+ payload: facts,
875
+ source: "role-runtime",
876
+ });
877
+ } catch {}
842
878
  }
843
879
  }
844
880
  });
845
- pi.on("agent_settled", async () => {
881
+ roleHost.on("agent_settled", async () => {
846
882
  if (pendingNavigatorSettlement !== undefined) {
847
883
  await pendingNavigatorSettlement;
848
884
  }
@@ -864,7 +900,7 @@ export function createRoleRuntimeExtension(
864
900
  details: presentation.event,
865
901
  }, { triggerTurn: false });
866
902
  });
867
- pi.on("session_shutdown", async () => {
903
+ roleHost.on("session_shutdown", async () => {
868
904
  // #351: stop OAuth keepalive first so shutdown yields zero further ticks.
869
905
  oauthKeepalive.stop();
870
906
  // Flush any still-pending affirmative attendance before teardown. Accepted
@@ -887,23 +923,23 @@ export function createRoleRuntimeExtension(
887
923
  navigatorAttendance = undefined;
888
924
  pendingNavigatorSettlement = undefined;
889
925
  pendingInfrastructureFailures.clear();
890
- pendingGatekeeperNonPassByToolCallId.clear();
926
+ pendingSubmissionNonPassByToolCallId.clear();
891
927
  observationFace.reset();
892
928
  });
893
929
 
894
930
  const hostActions = {
895
- failInfrastructure(error: unknown, ctx: ExtensionContext, toolCallId?: string): never {
931
+ failInfrastructure(error: unknown, ctx: HostContext, toolCallId?: string): never {
896
932
  if (toolCallId !== undefined) {
897
933
  pendingInfrastructureFailures.set(toolCallId, buildPendingInfrastructureFailure(error));
898
934
  }
899
935
  failInfrastructure(error, ctx);
900
936
  },
901
- bindGatekeeperNonPass(toolCallId: string, result: GatekeeperNonPassResult): void {
902
- pendingGatekeeperNonPassByToolCallId.set(toolCallId, result);
937
+ bindSubmissionNonPass(toolCallId: string, result: SubmissionNonPassResult): void {
938
+ pendingSubmissionNonPassByToolCallId.set(toolCallId, result);
903
939
  },
904
940
  };
905
941
  const judge = createJudgeRoleRuntime(
906
- pi,
942
+ roleHost,
907
943
  {
908
944
  loadSoul: dependencies.loadJudgeSoul,
909
945
  auditSoulCompliance: dependencies.auditSoulCompliance,
@@ -911,7 +947,7 @@ export function createRoleRuntimeExtension(
911
947
  hostActions,
912
948
  );
913
949
  const fixer = createFixerRoleRuntime(
914
- pi,
950
+ roleHost,
915
951
  {
916
952
  async loadSoul() {
917
953
  if (dependencies.loadFixerSoul === undefined) {
@@ -929,7 +965,7 @@ export function createRoleRuntimeExtension(
929
965
  hostActions,
930
966
  );
931
967
  const coder = createCoderRoleRuntime(
932
- pi,
968
+ roleHost,
933
969
  {
934
970
  async loadSoul() {
935
971
  if (dependencies.loadCoderSoul === undefined) {
@@ -953,7 +989,7 @@ export function createRoleRuntimeExtension(
953
989
  hostActions,
954
990
  );
955
991
  const reviewer = createReviewerRoleRuntime(
956
- pi,
992
+ roleHost,
957
993
  {
958
994
  async loadSoul() {
959
995
  if (dependencies.loadReviewerSoul === undefined) {
@@ -984,12 +1020,12 @@ export function createRoleRuntimeExtension(
984
1020
  },
985
1021
  hostActions,
986
1022
  );
987
- const doctor = createDoctorRoleRuntime(pi, {
1023
+ const doctor = createDoctorRoleRuntime(roleHost, {
988
1024
  async loadSoul() { if (!dependencies.loadDoctorSoul) throw new Error("Doctor runtime dependencies are not configured"); return dependencies.loadDoctorSoul(); },
989
1025
  async loadCase(path) { if (!dependencies.loadDoctorCase) throw new Error("Doctor runtime dependencies are not configured"); return dependencies.loadDoctorCase(path); },
990
1026
  async auditCompliance(options) { if (!dependencies.auditDoctorCompliance) throw new Error("Doctor runtime dependencies are not configured"); return dependencies.auditDoctorCompliance(options); },
991
1027
  }, hostActions);
992
- const notary = createNotaryRoleRuntime(pi, {
1028
+ const notary = createNotaryRoleRuntime(roleHost, {
993
1029
  async loadSoul() {
994
1030
  if (!dependencies.loadNotarySoul) throw new Error("Notary runtime dependencies are not configured");
995
1031
  return dependencies.loadNotarySoul();
@@ -1000,7 +1036,7 @@ export function createRoleRuntimeExtension(
1000
1036
  },
1001
1037
  }, hostActions);
1002
1038
  let sessionMergerGitState = dependencies.mergerGitState;
1003
- const merger = createMergerRoleRuntime(pi, {
1039
+ const merger = createMergerRoleRuntime(roleHost, {
1004
1040
  async loadSoul() { if (!dependencies.loadMergerSoul) throw new Error("Merger runtime dependencies are not configured"); return dependencies.loadMergerSoul(); },
1005
1041
  async loadInput(path) { if (!dependencies.loadMergerInput) throw new Error("Merger runtime dependencies are not configured"); return dependencies.loadMergerInput(path); },
1006
1042
  gitState: {
@@ -1009,7 +1045,7 @@ export function createRoleRuntimeExtension(
1009
1045
  },
1010
1046
  }, hostActions);
1011
1047
  const collector = createCollectorRoleRuntime(
1012
- pi,
1048
+ roleHost,
1013
1049
  {
1014
1050
  async loadSoul() {
1015
1051
  if (dependencies.loadCollectorSoul === undefined) {
@@ -1049,7 +1085,7 @@ export function createRoleRuntimeExtension(
1049
1085
  // (abort + nonzero print/json exit) with the original cause before that swallow.
1050
1086
  const observe = async (
1051
1087
  run: () => void | Promise<void>,
1052
- ctx: ExtensionContext,
1088
+ ctx: HostContext,
1053
1089
  ): Promise<void> => {
1054
1090
  try {
1055
1091
  await run();
@@ -1057,20 +1093,20 @@ export function createRoleRuntimeExtension(
1057
1093
  failInfrastructure(error, ctx);
1058
1094
  }
1059
1095
  };
1060
- pi.on("tool_execution_start", async (event, ctx) => {
1096
+ roleHost.on("tool_execution_start", async (event, ctx) => {
1061
1097
  await observe(() => observationFace.onStart(event), ctx);
1062
1098
  });
1063
- pi.on("tool_execution_update", async (event, ctx) => {
1099
+ roleHost.on("tool_execution_update", async (event, ctx) => {
1064
1100
  await observe(() => observationFace.onUpdate(event), ctx);
1065
1101
  });
1066
- pi.on("tool_execution_end", async (event, ctx) => {
1102
+ roleHost.on("tool_execution_end", async (event, ctx) => {
1067
1103
  await observe(() => observationFace.onEnd(event), ctx);
1068
1104
  });
1069
1105
 
1070
1106
  // Public Role run: record typed non-success HTTP for error evidence + v1 resume.
1071
1107
  // 2xx clears prior observation (see recordTypedProviderHttpStatus). Non-success
1072
1108
  // write failure must surface — never silently drop authorized error evidence.
1073
- pi.on("after_provider_response", async (event, ctx) => {
1109
+ roleHost.on("after_provider_response", async (event, ctx) => {
1074
1110
  const runDir = process.env.AK_ROLE_RUN_DIR;
1075
1111
  if (typeof runDir !== "string" || runDir.trim() === "") return;
1076
1112
  const provider = ctx.model?.provider;
@@ -1096,7 +1132,7 @@ export function createRoleRuntimeExtension(
1096
1132
  }
1097
1133
  });
1098
1134
 
1099
- pi.on("session_start", async (event, ctx) => {
1135
+ roleHost.on("session_start", async (event, ctx) => {
1100
1136
  admitted = false;
1101
1137
  selectedRole = undefined;
1102
1138
  activeReviewerParent = undefined;
@@ -1108,12 +1144,13 @@ export function createRoleRuntimeExtension(
1108
1144
  pendingNavigatorPresentation = undefined;
1109
1145
  pendingNavigatorSettlement = undefined;
1110
1146
  pendingInfrastructureFailures.clear();
1111
- pendingGatekeeperNonPassByToolCallId.clear();
1147
+ pendingSubmissionNonPassByToolCallId.clear();
1148
+ engineDetourRegistration?.resetLatch();
1112
1149
  navigatorWorkContext = undefined;
1113
1150
  // #351: OAuth keepalive is orthogonal to --ak-role; start before role early-return
1114
1151
  // so role-less sessions (and reload after shutdown stop) still keep tokens alive.
1115
- oauthKeepalive.start(ctx);
1116
- const rawRole = pi.getFlag(ROLE_FLAG.name);
1152
+ oauthKeepalive.start(toPiContext(ctx));
1153
+ const rawRole = roleHost.getFlag(ROLE_FLAG.name);
1117
1154
  if (rawRole === undefined) return;
1118
1155
  const entry = PACKAGED_ROLE_REGISTRY.find(({ role }) => role === rawRole);
1119
1156
  if (entry === undefined) {
@@ -1130,7 +1167,7 @@ export function createRoleRuntimeExtension(
1130
1167
  coder,
1131
1168
  reviewer,
1132
1169
  decodeReviewerAdmitted() {
1133
- return decodeReviewerAdmittedInputs((name) => pi.getFlag(name));
1170
+ return decodeReviewerAdmittedInputs((name) => roleHost.getFlag(name));
1134
1171
  },
1135
1172
  bindReviewerParent(activation) {
1136
1173
  activeReviewerParent = activation;
@@ -1164,7 +1201,7 @@ export function createRoleRuntimeExtension(
1164
1201
  work = { subjectKey: fallbackSubjectKey, subject: `work subject: ${fallbackSubjectKey}`, authority: "", subjectProvenance: "placeholder" };
1165
1202
  } else {
1166
1203
  try {
1167
- work = await dependencies.loadNavigatorWorkContext({ context: ctx, role: entry.role, phase: navigatorPhase(pi, entry.role) });
1204
+ work = await dependencies.loadNavigatorWorkContext({ context: ctx, role: entry.role, phase: navigatorPhase(roleHost, entry.role) });
1168
1205
  contextError = work.contextError;
1169
1206
  } catch (error) {
1170
1207
  // Contract: README.md#Navigator-attendance — a failed context load continues with a typed placeholder work context; the original cause is retained in contextError for the typed unavailable report.
@@ -1178,8 +1215,8 @@ export function createRoleRuntimeExtension(
1178
1215
  // session_start is process activation: resume unfinished principal only when marker
1179
1216
  // role/phase/subjectKey still match; mint for contradictory marker, malformed nearest,
1180
1217
  // missing marker, or terminal already completed.
1181
- const sessionEntries = ctx.sessionManager.getEntries();
1182
- const invocationPhase = navigatorPhase(pi, entry.role);
1218
+ const sessionEntries = [...ctx.sessionManager.getEntries()];
1219
+ const invocationPhase = navigatorPhase(roleHost, entry.role);
1183
1220
  const lifecyclePrincipal = resolveLifecycleInvocationPrincipal(sessionEntries, {
1184
1221
  role: entry.role,
1185
1222
  phase: invocationPhase,
@@ -1187,12 +1224,23 @@ export function createRoleRuntimeExtension(
1187
1224
  });
1188
1225
  const invocationId = lifecyclePrincipal.invocationId;
1189
1226
  if (!lifecyclePrincipal.resume) {
1190
- pi.appendEntry(NAVIGATOR_INVOCATION_ENTRY, {
1227
+ const data = {
1191
1228
  invocationId,
1192
1229
  role: entry.role,
1193
1230
  phase: invocationPhase,
1194
1231
  subjectKey: work.subjectKey,
1195
- });
1232
+ };
1233
+ pi.appendEntry(NAVIGATOR_INVOCATION_ENTRY, data);
1234
+ try {
1235
+ sitianReport({
1236
+ level: "event",
1237
+ kind: "attendance",
1238
+ cwd: ctx.cwd,
1239
+ sessionParent: ctx.sessionManager.getSessionFile(),
1240
+ payload: { type: NAVIGATOR_INVOCATION_ENTRY, ...data },
1241
+ source: "role-runtime",
1242
+ });
1243
+ } catch {}
1196
1244
  }
1197
1245
  navigatorAttendance = await dependencies.createNavigatorAttendance({
1198
1246
  context: ctx,
@@ -1223,8 +1271,11 @@ export function createRoleRuntimeExtension(
1223
1271
  await executeActivationStage(entry.role, activationStage(entry.role, runtime), { clock, writeTrace });
1224
1272
  // #357 T2 / #378 / #380 / #391: any role+engine activation registers the package detour tool once.
1225
1273
  // Gate is env presence only — no per-engine execute branch; no role-module spawn.
1226
- if (!engineDetourRegistered) {
1227
- engineDetourRegistered = registerEngineDetourTool(pi, hostActions);
1274
+ if (engineDetourRegistration === undefined) {
1275
+ engineDetourRegistration = registerEngineDetourTool(roleHost, hostActions);
1276
+ if (!engineDetourRegistration.registered) {
1277
+ engineDetourRegistration = undefined;
1278
+ }
1228
1279
  }
1229
1280
  // Worker gates ①②: arm records baseline and runs private one-shot hook uninstall (ADR 0070).
1230
1281
  // Parent session feeds #216 createRecordSession so baseline/bounce survive resume.
@@ -1,14 +1,19 @@
1
1
  /**
2
- * Session opening materials (#443): factory constitution + role soul + role-owned
3
- * extras, composed at the three existing loader seams. Missing files fail as
4
- * native readFile errors — no exists/hash/empty guards, no second loader.
5
- *
6
- * Auditor composition stays owned by loadAuditorSoul (blank-soul identity);
7
- * main roles and gatekeeper family share joinPackageMaterials here.
2
+ * Session opening materials (#443 / #524). Missing files fail as native readFile
3
+ * errors. Main-role materials derive from PUBLIC_ROLE_RECORDS; gatekeeper seats
4
+ * stay independent except notary (shared NOTARY_SESSION_MATERIALS). Navigator is
5
+ * name-only here, not a public role record. Auditor composition stays in
6
+ * loadAuditorSoul; loaders share joinPackageMaterials.
8
7
  */
9
8
  import { readFile } from "node:fs/promises";
10
9
  import { fileURLToPath } from "node:url";
11
10
 
11
+ import {
12
+ NOTARY_SESSION_MATERIALS,
13
+ PUBLIC_ROLE_RECORDS,
14
+ type PackagedRole,
15
+ } from "./packaged-role-registry.ts";
16
+
12
17
  const packageRootUrl = new URL("..", import.meta.url);
13
18
 
14
19
  /** Self-locate one package-relative material. Native I/O errors propagate. */
@@ -27,38 +32,19 @@ export async function joinPackageMaterials(
27
32
  return chunks.join("\n\n");
28
33
  }
29
34
 
30
- /** Seven public main roles + Navigator. Ticket #443 injection roster. */
35
+ type PublicRoleMaterials = {
36
+ readonly [Role in PackagedRole]: Extract<
37
+ (typeof PUBLIC_ROLE_RECORDS)[number],
38
+ { role: Role }
39
+ >["sessionMaterials"];
40
+ };
41
+
42
+ /** Derived projection: public records + navigator name-only materials. */
31
43
  export const MAIN_ROLE_SESSION_MATERIALS = {
32
- judge: [
33
- "CLAUDE.md",
34
- "souls/judge.md",
35
- "souls/audit-law.md",
36
- "souls/quality-law.md",
37
- "souls/judge-output-guide.md",
38
- ],
39
- fixer: [
40
- "CLAUDE.md",
41
- "souls/fixer.md",
42
- "souls/quality-law.md",
43
- "souls/fixer-output-guide.md",
44
- ],
45
- coder: [
46
- "CLAUDE.md",
47
- "souls/coder.md",
48
- "souls/quality-law.md",
49
- "souls/coder-output-guide.md",
50
- ],
51
- reviewer: [
52
- "CLAUDE.md",
53
- "souls/reviewer.md",
54
- "souls/audit-law.md",
55
- "souls/quality-law.md",
56
- ],
57
- collector: ["CLAUDE.md", "souls/collector.md"],
58
- doctor: ["CLAUDE.md", "souls/doctor.md"],
59
- merger: ["CLAUDE.md", "souls/merger.md"],
60
- notary: ["CLAUDE.md", "souls/notary.md", "souls/gate-output-guide.md"],
61
- navigator: ["CLAUDE.md", "souls/navigator.md"],
44
+ ...(Object.fromEntries(
45
+ PUBLIC_ROLE_RECORDS.map((record) => [record.role, record.sessionMaterials]),
46
+ ) as PublicRoleMaterials),
47
+ navigator: ["CLAUDE.md", "souls/navigator.md"] as const,
62
48
  } as const;
63
49
 
64
50
  export type MainRoleSession = keyof typeof MAIN_ROLE_SESSION_MATERIALS;
@@ -69,7 +55,7 @@ export function loadMainRoleSessionMaterials(
69
55
  return joinPackageMaterials(MAIN_ROLE_SESSION_MATERIALS[role]);
70
56
  }
71
57
 
72
- /** Gatekeeper province + Inspector/Notary officer sessions. */
58
+ /** Gatekeeper province; notary reuses the public notary materials definition. */
73
59
  export const GATEKEEPER_SESSION_MATERIALS = {
74
60
  gatekeeper: [
75
61
  "CLAUDE.md",
@@ -83,7 +69,7 @@ export const GATEKEEPER_SESSION_MATERIALS = {
83
69
  "souls/quality-law.md",
84
70
  "souls/gate-output-guide.md",
85
71
  ],
86
- notary: ["CLAUDE.md", "souls/notary.md", "souls/gate-output-guide.md"],
72
+ notary: NOTARY_SESSION_MATERIALS,
87
73
  } as const;
88
74
 
89
75
  export type GatekeeperSessionRole = keyof typeof GATEKEEPER_SESSION_MATERIALS;