@akagilnc/pi-workflow-roles 0.1.5153 → 0.1.5196
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 +2 -2
- package/README.zh-CN.md +2 -2
- package/dist/acp-host/production-host.js +13086 -13034
- package/dist/archivist-record-entry.js +4 -20
- package/dist/archivist-record-pointer.js +28 -0
- package/dist/audit-escalation.js +3 -119
- package/dist/auditor-soul.js +32 -0
- package/dist/doctor-auditor.js +26 -0
- package/dist/headless-host/production-host.js +11218 -11166
- package/dist/judge-role.js +73 -0
- package/dist/migrate-book-topology.js +8 -0
- package/dist/package-contracts/judge-output.js +1 -18
- package/dist/package-contracts/terminating-tools.js +2 -2
- package/dist/public-cli/cli.js +2 -2
- package/dist/public-cli/countersign-run.js +11 -0
- package/dist/public-cli/instruction-seat-run.js +428 -48
- package/dist/public-cli/invocation.js +10 -5
- package/dist/public-cli/main.js +10049 -8911
- package/dist/public-cli/settlement.js +28 -8
- package/dist/public-role-summons.js +6 -4
- package/dist/submission-errors.js +1 -1
- package/dist/submission-gate.js +165 -0
- package/dist/submission-ledger.js +0 -27
- package/dist/worker-submission-contracts.js +8 -0
- package/package.json +1 -1
- package/src/archivist-record-entry.ts +5 -49
- package/src/archivist-record-pointer.ts +49 -0
- package/src/audit-escalation.ts +4 -217
- package/src/auditor-soul.ts +33 -0
- package/src/doctor-role.ts +11 -5
- package/src/gatekeeper-role.ts +2 -2
- package/src/host-contracts.ts +0 -25
- package/src/judge-role.ts +63 -90
- package/src/package-contracts/judge-output.ts +0 -17
- package/src/package-contracts/terminating-tools.ts +0 -2
- package/src/pi/adapter.ts +1 -33
- package/src/public-cli/cli.ts +2 -2
- package/src/public-cli/countersign-run.ts +11 -0
- package/src/public-cli/instruction-seat-run.ts +433 -16
- package/src/public-cli/invocation.ts +9 -3
- package/src/public-cli/post-admission.ts +0 -1
- package/src/public-cli/settlement.ts +32 -8
- package/src/public-role-summons.ts +9 -6
- package/src/role-envelope.ts +0 -32
- package/src/role-runtime-dependencies.ts +0 -4
- package/src/role-runtime.ts +15 -218
- package/src/submission-errors.ts +1 -1
- package/src/submission-gate.ts +46 -10
- package/src/submission-ledger.ts +0 -27
- package/src/worker-role.ts +23 -102
- package/src/worker-submission-contracts.ts +8 -0
- package/src/worker-submission-gates.ts +2 -2
|
@@ -174,16 +174,18 @@ function roleOutcomeFromRows(
|
|
|
174
174
|
(row) => row.kind === "accepted" || row.kind === "audit-escalation",
|
|
175
175
|
);
|
|
176
176
|
if (terminal.length === 0) return undefined;
|
|
177
|
-
//
|
|
177
|
+
// Full sequence stays on payloads. The queue kind follows the latest terminal
|
|
178
|
+
// row only — an earlier escalation must not outrank a later acceptance or
|
|
179
|
+
// keep that earlier receipt (#1057).
|
|
178
180
|
const payloads = mine.map((row) => row.accepted);
|
|
179
|
-
|
|
180
|
-
|
|
181
|
+
const latest = terminal[terminal.length - 1]!;
|
|
182
|
+
if (latest.kind === "audit-escalation") {
|
|
181
183
|
return {
|
|
182
184
|
kind: "audit_escalation", role, status: "audit_escalation", payloads,
|
|
183
|
-
...(
|
|
184
|
-
auditEscalationReceipt:
|
|
185
|
-
...(Object.hasOwn(
|
|
186
|
-
} }),
|
|
185
|
+
...(Object.hasOwn(latest, "auditReceipt") ? { decisiveFacts: {
|
|
186
|
+
auditEscalationReceipt: latest.auditReceipt,
|
|
187
|
+
...(Object.hasOwn(latest, "auditOfficer") ? { auditEscalationOfficer: latest.auditOfficer } : {}),
|
|
188
|
+
} } : {}),
|
|
187
189
|
};
|
|
188
190
|
}
|
|
189
191
|
return { kind: "accepted", role, payloads };
|
|
@@ -914,7 +916,7 @@ function sessionReadFailure(
|
|
|
914
916
|
* Read the exact bound Pi session file principal.
|
|
915
917
|
* Does not scan the session directory for "latest" — resume identity is the file.
|
|
916
918
|
*/
|
|
917
|
-
async function readBoundSessionEntries(
|
|
919
|
+
export async function readBoundSessionEntries(
|
|
918
920
|
sessionFile: string,
|
|
919
921
|
): Promise<SessionEntry[]> {
|
|
920
922
|
const text = await readFile(sessionFile, "utf8");
|
|
@@ -1847,6 +1849,8 @@ function snapshotAuditDetails(details: Record<string, unknown>): Record<string,
|
|
|
1847
1849
|
* terminal identity; the retained response must be this seat's real escalate
|
|
1848
1850
|
* decision and its projected audit-owned fields must agree with it.
|
|
1849
1851
|
*/
|
|
1852
|
+
// Historical pre-#1057 session artifacts may still contain this old projection;
|
|
1853
|
+
// no current submission path writes it, but resume/settlement must read it.
|
|
1850
1854
|
function boundAuditEscalationForResult(
|
|
1851
1855
|
entries: readonly SessionEntry[],
|
|
1852
1856
|
resultIndex: number,
|
|
@@ -3089,6 +3093,26 @@ async function applySecretariatCountersignTerminal(
|
|
|
3089
3093
|
return settled;
|
|
3090
3094
|
}
|
|
3091
3095
|
|
|
3096
|
+
/**
|
|
3097
|
+
* After the audit gate returns, attach gate rounds and the secretariat officer
|
|
3098
|
+
* fact onto the terminal this turn already settled. Does not publish again —
|
|
3099
|
+
* a second publish would append another attempt-history row for the same attempt.
|
|
3100
|
+
*/
|
|
3101
|
+
export async function attachPostAuditProjection(
|
|
3102
|
+
admitted: AdmittedRoleInvocation,
|
|
3103
|
+
authority: DurablePrincipalAuthority,
|
|
3104
|
+
terminal: TerminalResult,
|
|
3105
|
+
): Promise<TerminalResult> {
|
|
3106
|
+
const { sessionDirectory, sessionFile } = coordinatesFromAdmitted(authority, admitted);
|
|
3107
|
+
const gate = await extractGateFactFromSessionDirectory(sessionDirectory, {
|
|
3108
|
+
runDirectory: admitted.runDirectory,
|
|
3109
|
+
parentSessionFile: sessionFile,
|
|
3110
|
+
});
|
|
3111
|
+
const withGate = gate === undefined ? terminal : { ...terminal, gate };
|
|
3112
|
+
if (admitted.role !== "secretariat") return withGate;
|
|
3113
|
+
return applySecretariatCountersignTerminal(admitted, authority, withGate);
|
|
3114
|
+
}
|
|
3115
|
+
|
|
3092
3116
|
/** One failed attempt to place a durable failure artifact (path is private layout). */
|
|
3093
3117
|
type PublicationAttempt = {
|
|
3094
3118
|
readonly path: string;
|
|
@@ -535,8 +535,8 @@ export async function summonPublicRole(
|
|
|
535
535
|
}
|
|
536
536
|
|
|
537
537
|
const stderr = captured?.stderrText();
|
|
538
|
-
//
|
|
539
|
-
// terminal's independent run, not the
|
|
538
|
+
// An escalated officer can return its court diarist's escalation verbatim.
|
|
539
|
+
// The terminal's independent run, not the officer, is the gate pointer.
|
|
540
540
|
const runDirectory = result.terminal !== undefined
|
|
541
541
|
&& result.terminal.roleOutcome.role !== options.role
|
|
542
542
|
&& typeof result.terminal.runId === "string"
|
|
@@ -805,6 +805,8 @@ export async function summonGateOfficer(options: {
|
|
|
805
805
|
const { readableGateItem } = await import("./readable-gate-item.ts");
|
|
806
806
|
gateReviewInstruction = readableGateItem(options.submission);
|
|
807
807
|
}
|
|
808
|
+
const { runIdFromRunDirectory } = await import("./run-terminal-artifacts.ts");
|
|
809
|
+
const parentRunId = runIdFromRunDirectory(options.sourceRunDirectory);
|
|
808
810
|
const common = {
|
|
809
811
|
cwd: options.cwd,
|
|
810
812
|
...(home === undefined ? {} : { home }),
|
|
@@ -818,6 +820,9 @@ export async function summonGateOfficer(options: {
|
|
|
818
820
|
...(options.roleTurnHost === undefined ? {} : { roleTurnHost: options.roleTurnHost }),
|
|
819
821
|
...(options.hostAdapters === undefined ? {} : { hostAdapters: options.hostAdapters }),
|
|
820
822
|
...(options.createRunId === undefined ? {} : { createRunId: options.createRunId }),
|
|
823
|
+
...(parentRunId === undefined || parentRunId.trim() === ""
|
|
824
|
+
? {}
|
|
825
|
+
: { correlationId: parentRunId }),
|
|
821
826
|
} as const;
|
|
822
827
|
const { packagedGateSummon } = await import("./packaged-role-registry.ts");
|
|
823
828
|
const gateSummon = packagedGateSummon(options.officer);
|
|
@@ -851,15 +856,13 @@ export async function summonGateOfficer(options: {
|
|
|
851
856
|
// no code-authored summons prose (#924). Gate resume key = parentRunPath
|
|
852
857
|
// (sourceRunDirectory); board ticket handoff is bind-only, never a resume
|
|
853
858
|
// lookup (#987 Result 7). Receipt ticketNumber stays payload content.
|
|
854
|
-
// correlationId only on this officer — notary/auditor/inspector stay untouched.
|
|
855
859
|
// Gate resume key is the parent run directory. The board ticket only binds.
|
|
856
|
-
|
|
857
|
-
const correlationId = runIdFromRunDirectory(options.sourceRunDirectory);
|
|
858
|
-
if (correlationId === undefined || correlationId.trim() === "") {
|
|
860
|
+
if (parentRunId === undefined || parentRunId.trim() === "") {
|
|
859
861
|
throw new Error(
|
|
860
862
|
`countersign gate summon requires parent runId from sourceRunDirectory: ${options.sourceRunDirectory}`,
|
|
861
863
|
);
|
|
862
864
|
}
|
|
865
|
+
const correlationId = parentRunId;
|
|
863
866
|
const { isSafePositiveTicketNumber, readBoardTicketNumber } = await import("./run-ticket-number.ts");
|
|
864
867
|
const parentTicket = await readBoardTicketNumber(options.sourceRunDirectory);
|
|
865
868
|
const submittedTicket = options.submission !== null && typeof options.submission === "object" && !Array.isArray(options.submission)
|
package/src/role-envelope.ts
CHANGED
|
@@ -9,10 +9,6 @@ import {
|
|
|
9
9
|
ENGINE_MODEL_FLAG_NAME,
|
|
10
10
|
normalizeEngineName,
|
|
11
11
|
} from "./engine-detour.ts";
|
|
12
|
-
import {
|
|
13
|
-
createDefaultGateOfficerSummon,
|
|
14
|
-
requireSubmissionGate,
|
|
15
|
-
} from "./submission-gate.ts";
|
|
16
12
|
import type {
|
|
17
13
|
HostContext,
|
|
18
14
|
HostEventRegistration,
|
|
@@ -228,34 +224,6 @@ export async function prepareRoleEnvelope(options: {
|
|
|
228
224
|
// seat remains reachable through MCP.
|
|
229
225
|
setActiveTools(names) { preferredTools = [...names]; },
|
|
230
226
|
getActiveTools() { return [...preferredTools]; },
|
|
231
|
-
async requireSubmissionGate(gateOptions) {
|
|
232
|
-
const packageRoot =
|
|
233
|
-
typeof options.dependencies.packageRoot === "string"
|
|
234
|
-
? options.dependencies.packageRoot
|
|
235
|
-
: undefined;
|
|
236
|
-
return requireSubmissionGate({
|
|
237
|
-
context: gateOptions.context,
|
|
238
|
-
subject: gateOptions.subject,
|
|
239
|
-
...(gateOptions.signal === undefined ? {} : { signal: gateOptions.signal }),
|
|
240
|
-
hostActions: {
|
|
241
|
-
failInfrastructure: (error, _context, toolCallId) =>
|
|
242
|
-
gateOptions.hostActions.failInfrastructure(error, gateOptions.context, toolCallId),
|
|
243
|
-
bindSubmissionNonPass: gateOptions.hostActions.bindSubmissionNonPass,
|
|
244
|
-
},
|
|
245
|
-
toolCallId: gateOptions.toolCallId,
|
|
246
|
-
...(gateOptions.submission === undefined ? {} : { submission: gateOptions.submission }),
|
|
247
|
-
// #969: package root + home reach nested 给事中/符宝郎 summons.
|
|
248
|
-
// hostAdapters (when set on deps) forward nested seat selection — production unset.
|
|
249
|
-
summonOfficer: createDefaultGateOfficerSummon({
|
|
250
|
-
cwd: gateOptions.context.cwd ?? request.cwd,
|
|
251
|
-
home: request.home,
|
|
252
|
-
...(packageRoot === undefined ? {} : { packageRoot }),
|
|
253
|
-
...(options.dependencies.hostAdapters === undefined
|
|
254
|
-
? {}
|
|
255
|
-
: { hostAdapters: options.dependencies.hostAdapters }),
|
|
256
|
-
}),
|
|
257
|
-
});
|
|
258
|
-
},
|
|
259
227
|
on(...registration: HostEventRegistration) {
|
|
260
228
|
const [event, handler] = registration;
|
|
261
229
|
const list = handlers.get(event) ?? [];
|
|
@@ -2,7 +2,6 @@ import { readFile } from "node:fs/promises";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { createGhCollectorGitHubTransport } from "./collector-github.ts";
|
|
5
|
-
import { createPiDoctorAuditor } from "./doctor-auditor.ts";
|
|
6
5
|
import { loadDoctorCase } from "./doctor-evidence.ts";
|
|
7
6
|
import { createNativeNavigatorSessionFactory, createNavigatorAttendance } from "./navigator-attendance.ts";
|
|
8
7
|
import { loadNavigatorWorkContext } from "./navigator-work-context.ts";
|
|
@@ -43,7 +42,6 @@ export function createRoleRuntimeDependencies(packageRoot: string): RoleRuntimeD
|
|
|
43
42
|
// import.meta.url — headless/acp production-host bundles live under dist/*/
|
|
44
43
|
// and would otherwise look for dist/resources/ (#962).
|
|
45
44
|
const collectorHandbookSeedPath = join(packageRoot, "resources/collector-bot-handbook.md");
|
|
46
|
-
const doctorAuditor = createPiDoctorAuditor();
|
|
47
45
|
const navigatorSessionFactory = createNativeNavigatorSessionFactory();
|
|
48
46
|
return {
|
|
49
47
|
packageRoot,
|
|
@@ -56,8 +54,6 @@ export function createRoleRuntimeDependencies(packageRoot: string): RoleRuntimeD
|
|
|
56
54
|
loadDoctorCase,
|
|
57
55
|
loadNotarySourceRun: loadNotarySourceRunLocator,
|
|
58
56
|
loadMergerInput: async (path) => JSON.parse(await readFile(path, "utf8")),
|
|
59
|
-
// #590: doctor compliance still on disposeCompliance path; judge→auditor is gate queue (#756).
|
|
60
|
-
auditDoctorCompliance: (options) => doctorAuditor(options),
|
|
61
57
|
loadNavigatorWorkContext: (options) => loadNavigatorWorkContext({
|
|
62
58
|
context: options.context,
|
|
63
59
|
role: options.role,
|
package/src/role-runtime.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { readFileSync, writeSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { readableGateItem } from "./readable-gate-item.ts";
|
|
5
4
|
import {
|
|
6
5
|
ExplicitInternalActivationError,
|
|
7
6
|
|
|
@@ -110,8 +109,7 @@ import {
|
|
|
110
109
|
} from "./navigator-invocation-identity.ts";
|
|
111
110
|
import { recordTypedProviderHttpStatus } from "./typed-provider-http.ts";
|
|
112
111
|
import { NAVIGATOR_POST_ROLE_GRACE_MS, raceNavigatorGrace } from "./public-cli/settlement.ts";
|
|
113
|
-
import { PACKAGED_ROLE_REGISTRY, isOfficerReviewSeat, packagedRoleActivationFlags, packagedRoleMetadata, packagedRoleOutputTool, packagedRolePhaseFlag, type PackagedRole } from "./packaged-role-registry.ts";
|
|
114
|
-
import { isAuditEscalationProjection } from "./audit-escalation.ts";
|
|
112
|
+
import { PACKAGED_ROLE_REGISTRY, isOfficerReviewSeat, packagedRoleActivationFlags, packagedRoleInputFlag, packagedRoleMetadata, packagedRoleOutputTool, packagedRolePhaseFlag, type PackagedRole } from "./packaged-role-registry.ts";
|
|
115
113
|
import {
|
|
116
114
|
createJudgeRoleRuntime,
|
|
117
115
|
} from "./judge-role.ts";
|
|
@@ -346,8 +344,6 @@ export { fixerOutputSchema, validateFixerOutput } from "./package-contracts/fixe
|
|
|
346
344
|
export type { FixerBlocker, FixerClassResult, FixerPhase, FixerTestEvidence } from "./package-contracts/fixer-output.ts";
|
|
347
345
|
export { fixerPrerequisiteSchema, fixerPrerequisitesSchema, parseFixerPrerequisites, validateFixerPrerequisites } from "./package-contracts/fixer-packet.ts";
|
|
348
346
|
export type { FixerInvocationInput, FixerPrerequisite } from "./package-contracts/fixer-packet.ts";
|
|
349
|
-
export { AUDIT_ESCALATION_KIND, buildAuditEscalationResult, disposeComplianceDecision, isAuditEscalationResult, projectAuditEscalation } from "./audit-escalation.ts";
|
|
350
|
-
export type { AuditEscalationResult, AuditEscalationToolResult, ComplianceDecisionHandlers } from "./audit-escalation.ts";
|
|
351
347
|
export {
|
|
352
348
|
AUDITOR_SOUL_ROLES,
|
|
353
349
|
AK_ROLE_AUDITOR_SUBJECT_ENV,
|
|
@@ -460,8 +456,8 @@ export type RoleRuntimeDependencies = {
|
|
|
460
456
|
/**
|
|
461
457
|
* Composition-root adapter table for nested gate officer summons (#969).
|
|
462
458
|
* Production leaves unset (default pi + packaged externals). Tests inject
|
|
463
|
-
* faux nested hosts so
|
|
464
|
-
*
|
|
459
|
+
* faux nested hosts so the public entry's reviewer summons use the same
|
|
460
|
+
* host resolution as production.
|
|
465
461
|
*/
|
|
466
462
|
hostAdapters?: readonly import("./public-cli/role-turn-host-resolution.ts").NamedRoleTurnHostAdapter[];
|
|
467
463
|
/** Non-identity opening materials delivered in the ordinary role brief. */
|
|
@@ -476,7 +472,6 @@ export type RoleRuntimeDependencies = {
|
|
|
476
472
|
loadNotarySourceRun?(path: string): Promise<import("./notary-contracts.ts").NotarySourceRunLocator>;
|
|
477
473
|
loadDoctorCase?(path: string): Promise<import("./doctor-contracts.ts").DoctorCase>;
|
|
478
474
|
loadMergerInput?(path: string): Promise<unknown>;
|
|
479
|
-
auditDoctorCompliance?(options: { context: HostContext; signal?: AbortSignal; submission: unknown }): Promise<ComplianceDecision>;
|
|
480
475
|
createCollectorClock?(): CollectorClock;
|
|
481
476
|
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>;
|
|
482
477
|
loadNavigatorWorkContext?(options: { context: HostContext; role: string; phase: NavigatorPhase; getFlag?: (name: string) => unknown }): Promise<NavigatorWorkContext>;
|
|
@@ -550,11 +545,6 @@ export function publicNavigatorSettlement(role: string, phase: NavigatorPhase, e
|
|
|
550
545
|
const details = typeof event.details === "object" && event.details !== null && !Array.isArray(event.details)
|
|
551
546
|
? event.details as Record<string, unknown>
|
|
552
547
|
: {};
|
|
553
|
-
// Live Navigator consumes only the audit-owned projection; persisted/replayed
|
|
554
|
-
// records are re-authenticated by settlement against retained audit evidence.
|
|
555
|
-
if (isAuditEscalationProjection(event.details)) {
|
|
556
|
-
return { kind: "human_decision", role, phase, status: "audit_escalation" };
|
|
557
|
-
}
|
|
558
548
|
const status = typeof details.status === "string"
|
|
559
549
|
? details.status
|
|
560
550
|
: receiptStatusFromRegistry(details);
|
|
@@ -597,7 +587,7 @@ type FiledOfficerBeforeAccept = (input: {
|
|
|
597
587
|
* Shared registration envelope for filed officers (ADR 0018 / #572):
|
|
598
588
|
* activate, tool register, before_agent_start prompt, inventory check.
|
|
599
589
|
* Role module keeps label/soul/spec shape only; sole-final barrier is ledger-owned.
|
|
600
|
-
* Optional beforeAccept
|
|
590
|
+
* Optional beforeAccept projects local submission facts before the tool returns.
|
|
601
591
|
*/
|
|
602
592
|
function createFiledOfficerRuntime(
|
|
603
593
|
roleHost: RoleHost,
|
|
@@ -606,7 +596,6 @@ function createFiledOfficerRuntime(
|
|
|
606
596
|
tool: { name: string; label: string; description: string; promptSnippet: string; parameters: unknown };
|
|
607
597
|
soulTag: string;
|
|
608
598
|
beforeAccept?: FiledOfficerBeforeAccept;
|
|
609
|
-
relayGatePass?: true;
|
|
610
599
|
},
|
|
611
600
|
dependencies: { loadSoul(): Promise<string> },
|
|
612
601
|
) {
|
|
@@ -631,25 +620,11 @@ function createFiledOfficerRuntime(
|
|
|
631
620
|
spec.beforeAccept === undefined
|
|
632
621
|
? undefined
|
|
633
622
|
: await spec.beforeAccept({ toolCallId, parameters, signal, ctx });
|
|
634
|
-
if (isAuditEscalationProjection(projected)) {
|
|
635
|
-
return { content: [], details: projected, terminate: true as const };
|
|
636
|
-
}
|
|
637
|
-
if (projected !== null && typeof projected === "object" && "status" in projected
|
|
638
|
-
&& projected.status === "continue" && "receipt" in projected) {
|
|
639
|
-
return {
|
|
640
|
-
content: [{ type: "text" as const, text: readableGateItem(projected.receipt) }],
|
|
641
|
-
details: projected,
|
|
642
|
-
terminate: false,
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
const pass = spec.relayGatePass === true && projected !== null && typeof projected === "object" && "officer" in projected && "receipt" in projected
|
|
646
|
-
? projected
|
|
647
|
-
: undefined;
|
|
648
623
|
// Accept-as-is + terminate only. Shape is not an admission gate
|
|
649
624
|
// (第 0 条 / ADR 0055); sole-final barrier is ledger-owned (#575).
|
|
650
625
|
return {
|
|
651
|
-
content:
|
|
652
|
-
details:
|
|
626
|
+
content: [],
|
|
627
|
+
details: projected === undefined ? parameters : projected,
|
|
653
628
|
terminate: true as const,
|
|
654
629
|
};
|
|
655
630
|
},
|
|
@@ -887,8 +862,6 @@ function readDiaristRunCoordinates(ctx: HostContext): {
|
|
|
887
862
|
/** Plain-language re-ask when diarist bounds cannot be used (#901 / reask-not-explode). */
|
|
888
863
|
const DIARIST_BOUNDS_REASK =
|
|
889
864
|
"边界无法使用。请重交 sessions:每卷 path + ranges,每端以原生 id 或本轮行号二选一指名。" as const;
|
|
890
|
-
const DIARIST_ROUTING_STATUSES = new Set(["completed", "escalate"]);
|
|
891
|
-
|
|
892
865
|
/**
|
|
893
866
|
* #708 / #779 / #901: 起居郎 public seat on the shared filed-officer envelope.
|
|
894
867
|
* LLM judges ticket + dialogue bounds; mechanical layer reprojects the unique
|
|
@@ -910,13 +883,11 @@ export function createDiaristRoleRuntime(
|
|
|
910
883
|
parameters !== null && typeof parameters === "object" && !Array.isArray(parameters)
|
|
911
884
|
? (parameters as Record<string, unknown>)
|
|
912
885
|
: undefined;
|
|
913
|
-
//
|
|
914
|
-
//
|
|
886
|
+
// Routing belongs to the public seam after this tool call has returned.
|
|
887
|
+
// Only completed receipts need local diary projection; other statuses
|
|
888
|
+
// pass through verbatim for that seam to route.
|
|
915
889
|
const status = submitted?.status;
|
|
916
|
-
if (
|
|
917
|
-
throw new ParentQueueReaskError(unreadableDiscriminatorNotice("status", submitted?.status));
|
|
918
|
-
}
|
|
919
|
-
if (status === "escalate") return parameters;
|
|
890
|
+
if (status !== "completed") return parameters;
|
|
920
891
|
const assertion = readDiaristTicketAssertion(submitted);
|
|
921
892
|
const coords = readDiaristRunCoordinates(ctx);
|
|
922
893
|
// #836 7.3: pre-bound ticket is material for the LLM, not an override.
|
|
@@ -957,126 +928,17 @@ export function createDiaristRoleRuntime(
|
|
|
957
928
|
);
|
|
958
929
|
}
|
|
959
930
|
|
|
960
|
-
/** Secretariat
|
|
961
|
-
const SECRETARIAT_QUEUE_STATUSES = new Set(["converged", "escalate"]);
|
|
962
|
-
|
|
963
|
-
/**
|
|
964
|
-
* Secretariat output is terminal; converged submissions enter the shared
|
|
965
|
-
* submission gate on every host that mounts gatekeeper actions.
|
|
966
|
-
*/
|
|
931
|
+
/** Secretariat files before the public post-submission audit starts. */
|
|
967
932
|
export function createSecretariatRoleRuntime(
|
|
968
933
|
roleHost: RoleHost,
|
|
969
934
|
dependencies: SecretariatRuntimeDependencies,
|
|
970
|
-
hostActions?: import("./host-contracts.ts").HostGatekeeperActions,
|
|
971
935
|
) {
|
|
972
|
-
const beforeAccept: FiledOfficerBeforeAccept | undefined =
|
|
973
|
-
hostActions !== undefined
|
|
974
|
-
? async ({ toolCallId, parameters, signal, ctx }) => {
|
|
975
|
-
if (roleHost.requireSubmissionGate === undefined) {
|
|
976
|
-
const error = new Error("host cannot mount the secretariat submission gate");
|
|
977
|
-
error.name = "InfrastructureFailure";
|
|
978
|
-
return hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
979
|
-
}
|
|
980
|
-
const record =
|
|
981
|
-
parameters !== null && typeof parameters === "object" && !Array.isArray(parameters)
|
|
982
|
-
? (parameters as Record<string, unknown>)
|
|
983
|
-
: undefined;
|
|
984
|
-
const status =
|
|
985
|
-
record !== undefined && typeof record.secretariatStatus === "string"
|
|
986
|
-
? record.secretariatStatus
|
|
987
|
-
: undefined;
|
|
988
|
-
if (status === undefined || !SECRETARIAT_QUEUE_STATUSES.has(status)) {
|
|
989
|
-
throw new ParentQueueReaskError(
|
|
990
|
-
unreadableDiscriminatorNotice("secretariatStatus", record?.secretariatStatus),
|
|
991
|
-
);
|
|
992
|
-
}
|
|
993
|
-
if (status === "escalate") {
|
|
994
|
-
// Parent escalate → throw to caller as-is; countersign does not attend.
|
|
995
|
-
return undefined;
|
|
996
|
-
}
|
|
997
|
-
try {
|
|
998
|
-
const pass = await roleHost.requireSubmissionGate!({
|
|
999
|
-
context: ctx,
|
|
1000
|
-
subject: { kind: "secretariat_verdict" },
|
|
1001
|
-
...(signal === undefined ? {} : { signal }),
|
|
1002
|
-
hostActions,
|
|
1003
|
-
toolCallId,
|
|
1004
|
-
// #879: this-turn typed payload — identity-bound at submit site.
|
|
1005
|
-
submission: parameters,
|
|
1006
|
-
});
|
|
1007
|
-
// Book 给事中 署 snapshot for seat settlement projection (receipt + runId).
|
|
1008
|
-
// Ledger accepted stays LLM params (#836); public terminal reads this entry.
|
|
1009
|
-
if (
|
|
1010
|
-
pass !== undefined
|
|
1011
|
-
&& pass !== null
|
|
1012
|
-
&& typeof pass === "object"
|
|
1013
|
-
&& pass.status === "converged"
|
|
1014
|
-
&& "receipt" in pass
|
|
1015
|
-
) {
|
|
1016
|
-
const {
|
|
1017
|
-
SECRETARIAT_GATE_OFFICER_ENTRY_TYPE,
|
|
1018
|
-
} = await import("./secretariat-contracts.ts");
|
|
1019
|
-
const runId =
|
|
1020
|
-
typeof pass.runId === "string" && pass.runId.trim() !== ""
|
|
1021
|
-
? pass.runId
|
|
1022
|
-
: undefined;
|
|
1023
|
-
ctx.sessionManager.appendCustomEntry?.(SECRETARIAT_GATE_OFFICER_ENTRY_TYPE, {
|
|
1024
|
-
officer: "countersign",
|
|
1025
|
-
receipt: pass.receipt,
|
|
1026
|
-
...(runId === undefined ? {} : { runId }),
|
|
1027
|
-
});
|
|
1028
|
-
}
|
|
1029
|
-
return pass;
|
|
1030
|
-
} catch (error) {
|
|
1031
|
-
// 给事中上呈 ends parent with officer receipt (no retry / 不擅改).
|
|
1032
|
-
if (
|
|
1033
|
-
error instanceof GatekeeperDecisionError
|
|
1034
|
-
&& error.result.status === "escalate"
|
|
1035
|
-
) {
|
|
1036
|
-
const receipt = error.result.receipt;
|
|
1037
|
-
const receiptRecord =
|
|
1038
|
-
receipt !== null && typeof receipt === "object" && !Array.isArray(receipt)
|
|
1039
|
-
? (receipt as Record<string, unknown>)
|
|
1040
|
-
: undefined;
|
|
1041
|
-
const runId =
|
|
1042
|
-
typeof error.result.runId === "string" && error.result.runId.trim() !== ""
|
|
1043
|
-
? error.result.runId
|
|
1044
|
-
: undefined;
|
|
1045
|
-
// Envelope persists custom entries only
|
|
1046
|
-
// (toolResult rows are memory-only on headless/ACP — #617/#959).
|
|
1047
|
-
const {
|
|
1048
|
-
SECRETARIAT_GATE_OFFICER_ENTRY_TYPE,
|
|
1049
|
-
} = await import("./secretariat-contracts.ts");
|
|
1050
|
-
ctx.sessionManager.appendCustomEntry?.(SECRETARIAT_GATE_OFFICER_ENTRY_TYPE, {
|
|
1051
|
-
officer: "countersign",
|
|
1052
|
-
receipt,
|
|
1053
|
-
...(runId === undefined ? {} : { runId }),
|
|
1054
|
-
});
|
|
1055
|
-
const { buildAuditEscalationResult } = await import("./audit-escalation.ts");
|
|
1056
|
-
return buildAuditEscalationResult(
|
|
1057
|
-
{
|
|
1058
|
-
status: "escalate",
|
|
1059
|
-
officer: "countersign",
|
|
1060
|
-
...(receiptRecord !== undefined
|
|
1061
|
-
&& Object.prototype.hasOwnProperty.call(receiptRecord, "decisionGate")
|
|
1062
|
-
? { decisionGate: receiptRecord.decisionGate }
|
|
1063
|
-
: {}),
|
|
1064
|
-
},
|
|
1065
|
-
receipt,
|
|
1066
|
-
);
|
|
1067
|
-
}
|
|
1068
|
-
throw error;
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
: undefined;
|
|
1072
936
|
const base = createFiledOfficerRuntime(
|
|
1073
937
|
roleHost,
|
|
1074
938
|
{
|
|
1075
939
|
role: "secretariat",
|
|
1076
|
-
relayGatePass: true,
|
|
1077
940
|
tool: SECRETARIAT_OUTPUT_TOOL_SPEC,
|
|
1078
941
|
soulTag: "secretariat",
|
|
1079
|
-
...(beforeAccept === undefined ? {} : { beforeAccept }),
|
|
1080
942
|
},
|
|
1081
943
|
dependencies,
|
|
1082
944
|
);
|
|
@@ -1114,64 +976,13 @@ export function createSecretariatRoleRuntime(
|
|
|
1114
976
|
export function createCountersignRoleRuntime(
|
|
1115
977
|
roleHost: RoleHost,
|
|
1116
978
|
dependencies: CountersignRuntimeDependencies,
|
|
1117
|
-
hostActions?: import("./host-contracts.ts").HostGatekeeperActions,
|
|
1118
979
|
) {
|
|
1119
|
-
// Notary inner gate difference only — lifecycle stays on the shared envelope.
|
|
1120
|
-
// Pointer-only summons: officer self-fetches from run dossier (#632 / ADR 0079).
|
|
1121
|
-
// #1028 queue: read shared status — escalate skips gate (thrown to caller);
|
|
1122
|
-
// unreadable status returns to countersign; else notary inner gate.
|
|
1123
|
-
const beforeAccept: FiledOfficerBeforeAccept | undefined =
|
|
1124
|
-
hostActions !== undefined
|
|
1125
|
-
? async ({ toolCallId, parameters, signal, ctx }) => {
|
|
1126
|
-
const record =
|
|
1127
|
-
parameters !== null && typeof parameters === "object" && !Array.isArray(parameters)
|
|
1128
|
-
? (parameters as Record<string, unknown>)
|
|
1129
|
-
: undefined;
|
|
1130
|
-
const status =
|
|
1131
|
-
record !== undefined && typeof record.status === "string"
|
|
1132
|
-
? record.status
|
|
1133
|
-
: undefined;
|
|
1134
|
-
if (status === undefined || !REVIEW_QUEUE_STATUSES.has(status)) {
|
|
1135
|
-
// Parent status unreadable → back to countersign itself; do not summon notary,
|
|
1136
|
-
// do not forge an officer bounce face (#753).
|
|
1137
|
-
throw new ParentQueueReaskError(unreadableDiscriminatorNotice("status", record?.status));
|
|
1138
|
-
}
|
|
1139
|
-
if (status === "escalate") {
|
|
1140
|
-
// Parent escalate → throw to caller as-is; notary does not attend (#753).
|
|
1141
|
-
return undefined;
|
|
1142
|
-
}
|
|
1143
|
-
if (roleHost.requireSubmissionGate === undefined) {
|
|
1144
|
-
const error = new Error("host cannot mount the countersign gate");
|
|
1145
|
-
error.name = "InfrastructureFailure";
|
|
1146
|
-
return hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
1147
|
-
}
|
|
1148
|
-
try {
|
|
1149
|
-
return await roleHost.requireSubmissionGate({
|
|
1150
|
-
context: ctx,
|
|
1151
|
-
subject: { kind: "countersign_verdict" },
|
|
1152
|
-
...(signal === undefined ? {} : { signal }),
|
|
1153
|
-
hostActions,
|
|
1154
|
-
toolCallId,
|
|
1155
|
-
// #879: this-turn typed payload — identity-bound at submit site.
|
|
1156
|
-
submission: parameters,
|
|
1157
|
-
});
|
|
1158
|
-
} catch (error) {
|
|
1159
|
-
if (error instanceof GatekeeperDecisionError && error.result.status === "escalate") {
|
|
1160
|
-
const { buildAuditEscalationResult } = await import("./audit-escalation.ts");
|
|
1161
|
-
return buildAuditEscalationResult({ status: "escalate", officer: "notary", conflicts: error.result.receipt }, parameters);
|
|
1162
|
-
}
|
|
1163
|
-
throw error;
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
: undefined;
|
|
1167
980
|
return createFiledOfficerRuntime(
|
|
1168
981
|
roleHost,
|
|
1169
982
|
{
|
|
1170
983
|
role: "countersign",
|
|
1171
|
-
relayGatePass: true,
|
|
1172
984
|
tool: COUNTERSIGN_TOOL_SPEC,
|
|
1173
985
|
soulTag: "countersign",
|
|
1174
|
-
...(beforeAccept === undefined ? {} : { beforeAccept }),
|
|
1175
986
|
},
|
|
1176
987
|
dependencies,
|
|
1177
988
|
);
|
|
@@ -1265,7 +1076,6 @@ export function createRoleRuntimeExtension(
|
|
|
1265
1076
|
const pendingInfrastructureFailures = new Map<string, PendingInfrastructureFailure>();
|
|
1266
1077
|
// Envelope-owned execute→tool_result bridge for submission non-pass (ADR 0018 / #525).
|
|
1267
1078
|
const pendingSubmissionNonPassByToolCallId = new Map<string, SubmissionGateNonPassResult>();
|
|
1268
|
-
const pendingPriorGatePassByToolCallId = new Map<string, unknown>();
|
|
1269
1079
|
let engineDetourRegistered = false;
|
|
1270
1080
|
// #288 primary-session thin adapter. The policy is the sole budget owner;
|
|
1271
1081
|
// terminating-tool rejections and mechanical delivery requests share two turns.
|
|
@@ -1483,11 +1293,6 @@ export function createRoleRuntimeExtension(
|
|
|
1483
1293
|
return { readingMaterial: caseDossier };
|
|
1484
1294
|
});
|
|
1485
1295
|
roleHost.on("tool_result", async (event) => {
|
|
1486
|
-
const priorGatePass = pendingPriorGatePassByToolCallId.get(event.toolCallId);
|
|
1487
|
-
pendingPriorGatePassByToolCallId.delete(event.toolCallId);
|
|
1488
|
-
const priorPassContent = priorGatePass !== undefined && event.isError
|
|
1489
|
-
? [{ type: "text" as const, text: readableGateItem(priorGatePass) }, ...(event.content ?? [])]
|
|
1490
|
-
: undefined;
|
|
1491
1296
|
const role = selectedRole;
|
|
1492
1297
|
if (role === undefined) return;
|
|
1493
1298
|
// #676 E / J1: collector operational bookkeeping on the shared tool_result seam.
|
|
@@ -1522,16 +1327,15 @@ export function createRoleRuntimeExtension(
|
|
|
1522
1327
|
// Persist typed infrastructure-failure fact onto the role session toolResult so
|
|
1523
1328
|
// exact-session restart shares the same durable completion classification.
|
|
1524
1329
|
if (infrastructureDetails !== undefined) {
|
|
1525
|
-
return { isError: true
|
|
1330
|
+
return { isError: true };
|
|
1526
1331
|
}
|
|
1527
1332
|
// Submission non-pass: throw kept message text for the model; project the
|
|
1528
1333
|
// envelope-bound structured result onto session details at this tool_result seam.
|
|
1529
1334
|
const submissionNonPass = pendingSubmissionNonPassByToolCallId.get(event.toolCallId);
|
|
1530
1335
|
if (submissionNonPass !== undefined) {
|
|
1531
1336
|
pendingSubmissionNonPassByToolCallId.delete(event.toolCallId);
|
|
1532
|
-
return { details: submissionNonPass, isError: true
|
|
1337
|
+
return { details: submissionNonPass, isError: true };
|
|
1533
1338
|
}
|
|
1534
|
-
if (priorPassContent !== undefined) return { content: priorPassContent, isError: true };
|
|
1535
1339
|
});
|
|
1536
1340
|
// Queue receipt delivery before `agent_settled`: that event means Pi has
|
|
1537
1341
|
// already decided no queued continuation will run, so a triggerTurn there is
|
|
@@ -1694,7 +1498,6 @@ export function createRoleRuntimeExtension(
|
|
|
1694
1498
|
disposeNavigatorAttendanceNonBlocking(attendanceToDispose);
|
|
1695
1499
|
pendingInfrastructureFailures.clear();
|
|
1696
1500
|
pendingSubmissionNonPassByToolCallId.clear();
|
|
1697
|
-
pendingPriorGatePassByToolCallId.clear();
|
|
1698
1501
|
observationFace.reset();
|
|
1699
1502
|
});
|
|
1700
1503
|
|
|
@@ -1708,9 +1511,6 @@ export function createRoleRuntimeExtension(
|
|
|
1708
1511
|
bindSubmissionNonPass(toolCallId: string, result: SubmissionGateNonPassResult): void {
|
|
1709
1512
|
pendingSubmissionNonPassByToolCallId.set(toolCallId, result);
|
|
1710
1513
|
},
|
|
1711
|
-
bindPriorGatePass(toolCallId: string, receipt: unknown): void {
|
|
1712
|
-
pendingPriorGatePassByToolCallId.set(toolCallId, receipt);
|
|
1713
|
-
},
|
|
1714
1514
|
};
|
|
1715
1515
|
const requireRoleSoul = (role: PackagedRole): Promise<string> => dependencies.loadRoleSoul(role);
|
|
1716
1516
|
const judge = createJudgeRoleRuntime(
|
|
@@ -1718,7 +1518,6 @@ export function createRoleRuntimeExtension(
|
|
|
1718
1518
|
{
|
|
1719
1519
|
loadSoul: () => requireRoleSoul("judge"),
|
|
1720
1520
|
},
|
|
1721
|
-
hostActions,
|
|
1722
1521
|
);
|
|
1723
1522
|
const fixer = createFixerRoleRuntime(
|
|
1724
1523
|
roleHost,
|
|
@@ -1756,7 +1555,6 @@ export function createRoleRuntimeExtension(
|
|
|
1756
1555
|
const doctor = createDoctorRoleRuntime(roleHost, {
|
|
1757
1556
|
loadSoul: () => requireRoleSoul("doctor"),
|
|
1758
1557
|
async loadCase(path) { if (!dependencies.loadDoctorCase) throw new Error("Doctor runtime dependencies are not configured"); return dependencies.loadDoctorCase(path); },
|
|
1759
|
-
async auditCompliance(options) { if (!dependencies.auditDoctorCompliance) throw new Error("Doctor runtime dependencies are not configured"); return dependencies.auditDoctorCompliance(options); },
|
|
1760
1558
|
}, hostActions);
|
|
1761
1559
|
const notary = createNotaryRoleRuntime(roleHost, {
|
|
1762
1560
|
loadSoul: () => requireRoleSoul("notary"),
|
|
@@ -1767,7 +1565,7 @@ export function createRoleRuntimeExtension(
|
|
|
1767
1565
|
}, hostActions);
|
|
1768
1566
|
const countersign = createCountersignRoleRuntime(roleHost, {
|
|
1769
1567
|
loadSoul: () => requireRoleSoul("countersign"),
|
|
1770
|
-
}
|
|
1568
|
+
});
|
|
1771
1569
|
const gleanerLeftRuntime = createGleanerLeftRoleRuntime(roleHost, {
|
|
1772
1570
|
loadSoul: () => requireRoleSoul("gleaner-left"),
|
|
1773
1571
|
});
|
|
@@ -1802,7 +1600,7 @@ export function createRoleRuntimeExtension(
|
|
|
1802
1600
|
);
|
|
1803
1601
|
const secretariat = createSecretariatRoleRuntime(roleHost, {
|
|
1804
1602
|
loadSoul: () => requireRoleSoul("secretariat"),
|
|
1805
|
-
}
|
|
1603
|
+
});
|
|
1806
1604
|
const merger = createMergerRoleRuntime(roleHost, {
|
|
1807
1605
|
loadSoul: () => requireRoleSoul("merger"),
|
|
1808
1606
|
async loadInput(path) { if (!dependencies.loadMergerInput) throw new Error("Merger runtime dependencies are not configured"); return dependencies.loadMergerInput(path); },
|
|
@@ -2049,7 +1847,6 @@ export function createRoleRuntimeExtension(
|
|
|
2049
1847
|
pendingNavigatorSettlement = undefined;
|
|
2050
1848
|
pendingInfrastructureFailures.clear();
|
|
2051
1849
|
pendingSubmissionNonPassByToolCallId.clear();
|
|
2052
|
-
pendingPriorGatePassByToolCallId.clear();
|
|
2053
1850
|
navigatorWorkContext = undefined;
|
|
2054
1851
|
// #351: OAuth keepalive is orthogonal to --ak-role; start before role early-return
|
|
2055
1852
|
// so role-less sessions (and reload after shutdown stop) still keep tokens alive.
|
package/src/submission-errors.ts
CHANGED
|
@@ -12,7 +12,7 @@ function serializeReceipt(receipt: unknown): string {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
function gatekeeperNonPassMessage(result: SubmissionGateNonPassResult): string {
|
|
15
|
-
if (result.status === "continue"
|
|
15
|
+
if (result.status === "continue") {
|
|
16
16
|
return serializeReceipt(result.receipt);
|
|
17
17
|
}
|
|
18
18
|
if (result.status === "transport_failure") {
|