@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.
- package/README.md +129 -39
- package/README.zh-CN.md +128 -39
- package/dist/activation-ledger-topology.js +1 -1
- package/dist/archivist-record-entry.js +47 -26
- package/dist/auditor-dossier-tool.js +3 -1
- package/dist/compliance-transport.js +17 -26
- package/dist/doctor-contracts.js +1 -2
- package/dist/dossier-resolution.js +4 -8
- package/dist/engine-detour-tool.js +33 -8
- package/dist/engine-detour.js +1 -0
- package/dist/evidence-child-executor.js +277 -424
- package/dist/host-contracts.js +22 -0
- package/dist/institutional-resolution.js +88 -0
- package/dist/merger-contracts.js +1 -2
- package/dist/navigator-attendance.js +16 -0
- package/dist/notary-contracts.js +2 -3
- package/dist/package-contracts/fixer-output.js +1 -2
- package/dist/packaged-role-registry.js +102 -9
- package/dist/pi/in-process-session.js +671 -0
- package/dist/public-cli/config.js +7 -5
- package/dist/public-cli/main.js +3767 -4113
- package/dist/reviewer-child-executor.js +6 -1
- package/dist/session-opening-materials.js +10 -38
- package/dist/sitian-appender.js +146 -0
- package/dist/sitian-contracts.js +21 -0
- package/dist/sitian-facade.js +15 -0
- package/dist/sitian-reader.js +52 -0
- package/extensions/role-runtime.ts +9 -7
- package/package.json +5 -5
- package/resources/engines/cursor.md +12 -26
- package/resources/engines/hermes.md +2 -23
- package/resources/engines/opus.md +1 -16
- package/resources/navigator-route-playbook.md +2 -2
- package/scripts/build-package.mjs +0 -1
- package/souls/quality-law.md +1 -1
- package/src/activation-ledger-topology.ts +1 -1
- package/src/archivist-record-entry.ts +57 -45
- package/src/auditor-dossier-tool.ts +4 -2
- package/src/canonical-skill-binding.ts +37 -27
- package/src/collector-role.ts +11 -20
- package/src/collector-tool-schemas.ts +1 -4
- package/src/compliance-transport.ts +19 -44
- package/src/doctor-auditor.ts +1 -5
- package/src/doctor-contracts.ts +1 -4
- package/src/doctor-role.ts +31 -8
- package/src/dossier-resolution.ts +8 -8
- package/src/engine-detour-tool.ts +48 -19
- package/src/engine-detour.ts +3 -0
- package/src/evidence-child-executor.ts +277 -485
- package/src/factory-board.ts +3 -3
- package/src/gatekeeper-role.ts +21 -10
- package/src/host-contracts.ts +386 -0
- package/src/in-process-session.ts +1 -115
- package/src/institutional-resolution.ts +135 -0
- package/src/judge-auditor.ts +1 -5
- package/src/judge-role.ts +37 -51
- package/src/merger-contracts.ts +1 -4
- package/src/merger-role.ts +7 -11
- package/src/navigator-attendance.ts +20 -1
- package/src/notary-contracts.ts +8 -11
- package/src/notary-role.ts +7 -23
- package/src/notary-source-run.ts +4 -3
- package/src/package-contracts/fixer-output.ts +1 -4
- package/src/package-resources/method-skill-binding.ts +10 -34
- package/src/packaged-role-registry.ts +122 -12
- package/src/pi/adapter.ts +192 -0
- package/src/pi/durable-principal.ts +98 -0
- package/src/pi/in-process-session.ts +819 -0
- package/src/pi/known-failure.ts +77 -0
- package/src/pi/pi-normalization.ts +284 -0
- package/src/pi/role-turn-host.ts +459 -0
- package/src/public-cli/auto-resume.ts +42 -44
- package/src/public-cli/cli.ts +171 -257
- package/src/public-cli/coder-run.ts +115 -433
- package/src/public-cli/collector-run.ts +62 -314
- package/src/public-cli/config.ts +7 -5
- package/src/public-cli/doctor-run.ts +41 -52
- package/src/public-cli/fixer-run.ts +122 -427
- package/src/public-cli/invocation.ts +287 -96
- package/src/public-cli/judge-run.ts +92 -422
- package/src/public-cli/merger-run.ts +167 -429
- package/src/public-cli/notary-run.ts +41 -47
- package/src/public-cli/option-definitions.ts +136 -3
- package/src/public-cli/post-admission.ts +497 -0
- package/src/public-cli/public-run-credentials.ts +4 -4
- package/src/public-cli/reviewer-dispatch-rejection.ts +106 -0
- package/src/public-cli/reviewer-run.ts +132 -449
- package/src/public-cli/run-lifecycle.ts +339 -327
- package/src/public-cli/settlement.ts +251 -134
- package/src/public-cli/terminal.ts +3 -9
- package/src/public-cli/turn-request.ts +87 -0
- package/src/reviewer-child-executor.ts +9 -1
- package/src/reviewer-role.ts +11 -20
- package/src/role-runtime.ts +129 -78
- package/src/session-opening-materials.ts +25 -39
- package/src/sitian-appender.ts +171 -0
- package/src/sitian-contracts.ts +106 -0
- package/src/sitian-facade.ts +18 -0
- package/src/sitian-reader.ts +62 -0
- package/src/worker-role.ts +42 -40
- package/src/worker-submission-gates.ts +33 -0
- package/dist/archivist-role-run-coordinates.js +0 -28
- package/dist/package-contracts/terminating-infrastructure.js +0 -90
- package/resources/engines/sonnet.md +0 -27
- package/resources/engines/zcode.md +0 -63
- package/src/archivist-role-run-coordinates.ts +0 -45
- package/src/package-contracts/terminating-infrastructure.ts +0 -132
- package/src/public-cli/explicit-internal.ts +0 -427
- package/src/public-cli/one-shot-dispatch.ts +0 -300
package/src/role-runtime.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { writeSync } from "node:fs";
|
|
2
|
-
import type { ExtensionAPI
|
|
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 {
|
|
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?:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
508
|
+
auditDoctorCompliance?(options: { context: HostContext; signal?: AbortSignal }): Promise<ComplianceDecision>;
|
|
496
509
|
createCollectorClock?(): CollectorClock;
|
|
497
510
|
collectorPackageExtensionPath?: string;
|
|
498
|
-
createNavigatorAttendance?(options: { context:
|
|
499
|
-
loadNavigatorWorkContext?(options: { context:
|
|
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:
|
|
518
|
+
options: { context: HostContext; signal?: AbortSignal },
|
|
506
519
|
): Promise<ReviewerDispatchRunResult>;
|
|
507
520
|
shutdownReviewerAgent?(): Promise<void>;
|
|
508
|
-
transcriptFromContext(ctx:
|
|
521
|
+
transcriptFromContext(ctx: HostContext): string;
|
|
509
522
|
auditSoulCompliance(
|
|
510
|
-
options: { context:
|
|
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:
|
|
528
|
-
|
|
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:
|
|
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(
|
|
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 :
|
|
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
|
|
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
|
-
|
|
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
|
|
631
|
-
const
|
|
632
|
-
let
|
|
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
|
-
|
|
638
|
-
const role =
|
|
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
|
-
|
|
657
|
-
const role =
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
//
|
|
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
|
|
796
|
-
if (
|
|
797
|
-
|
|
798
|
-
return { details:
|
|
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
|
-
|
|
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
|
-
|
|
839
|
-
|
|
840
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
926
|
+
pendingSubmissionNonPassByToolCallId.clear();
|
|
891
927
|
observationFace.reset();
|
|
892
928
|
});
|
|
893
929
|
|
|
894
930
|
const hostActions = {
|
|
895
|
-
failInfrastructure(error: unknown, ctx:
|
|
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
|
-
|
|
902
|
-
|
|
937
|
+
bindSubmissionNonPass(toolCallId: string, result: SubmissionNonPassResult): void {
|
|
938
|
+
pendingSubmissionNonPassByToolCallId.set(toolCallId, result);
|
|
903
939
|
},
|
|
904
940
|
};
|
|
905
941
|
const judge = createJudgeRoleRuntime(
|
|
906
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
1096
|
+
roleHost.on("tool_execution_start", async (event, ctx) => {
|
|
1061
1097
|
await observe(() => observationFace.onStart(event), ctx);
|
|
1062
1098
|
});
|
|
1063
|
-
|
|
1099
|
+
roleHost.on("tool_execution_update", async (event, ctx) => {
|
|
1064
1100
|
await observe(() => observationFace.onUpdate(event), ctx);
|
|
1065
1101
|
});
|
|
1066
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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) =>
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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 (
|
|
1227
|
-
|
|
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)
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
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:
|
|
72
|
+
notary: NOTARY_SESSION_MATERIALS,
|
|
87
73
|
} as const;
|
|
88
74
|
|
|
89
75
|
export type GatekeeperSessionRole = keyof typeof GATEKEEPER_SESSION_MATERIALS;
|