@akagilnc/pi-workflow-roles 0.1.3565 → 0.1.3621
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 +4 -1
- package/README.zh-CN.md +1 -1
- package/dist/acp-host/production-host.js +18477 -8099
- package/dist/analyst-gate-cycles-read.js +387 -0
- package/dist/archivist-record-entry.js +22 -1
- package/dist/audit-escalation.js +6 -0
- package/dist/auditor-soul.js +74 -0
- package/dist/collector-config.js +86 -0
- package/dist/collector-evidence.js +316 -0
- package/dist/collector-github.js +527 -0
- package/dist/collector-ledger.js +853 -0
- package/dist/collector-tool-schemas.js +33 -0
- package/dist/compliance-transport.js +104 -52
- package/dist/diarist-contracts.js +72 -0
- package/dist/diarist-mechanical.js +411 -0
- package/dist/diarist-ticket-resolution.js +177 -0
- package/dist/diarist.js +311 -0
- package/dist/doctor-auditor.js +25 -0
- package/dist/doctor-contracts.js +2 -0
- package/dist/doctor-evidence.js +142 -0
- package/dist/gatekeeper-role.js +104 -81
- package/dist/host-transition-prior-native.js +78 -0
- package/dist/institutional-resolution.js +1 -95
- package/dist/judge-auditor.js +26 -0
- package/dist/ledger-session-read.js +227 -0
- package/dist/merger-git-state.js +78 -0
- package/dist/navigator-attendance.js +15 -7
- package/dist/navigator-public-session.js +170 -0
- package/dist/navigator-session-contracts.js +36 -40
- package/dist/notary-source-run.js +122 -0
- package/dist/package-contracts/auditor-output.js +66 -0
- package/dist/package-contracts/evidence-child-output.js +34 -0
- package/dist/package-contracts/judge-output.js +2 -0
- package/dist/package-contracts/terminating-tools.js +44 -3
- package/dist/package-resources/method-skill.js +254 -0
- package/dist/packaged-role-registry.js +56 -0
- package/dist/pi/durable-principal.js +61 -0
- package/dist/pi/in-process-session.js +25 -6
- package/dist/pi/known-failure.js +52 -0
- package/dist/pi/role-turn-host.js +430 -0
- package/dist/public-cli/auto-resume.js +414 -0
- package/dist/public-cli/cli-errors.js +8 -0
- package/dist/public-cli/cli-io.js +1 -0
- package/dist/public-cli/command-renderer.js +5 -0
- package/dist/public-cli/doctor-run.js +84 -0
- package/dist/public-cli/inspector-run.js +136 -0
- package/dist/public-cli/instruction-seat-run.js +256 -0
- package/dist/public-cli/invocation.js +2225 -0
- package/dist/public-cli/judge-run.js +118 -0
- package/dist/public-cli/load-production-acp-host.js +40 -0
- package/dist/public-cli/main.js +1511 -1124
- package/dist/public-cli/notary-run.js +161 -0
- package/dist/public-cli/option-definitions.js +1192 -0
- package/dist/public-cli/post-admission.js +667 -0
- package/dist/public-cli/public-run-credentials.js +49 -0
- package/dist/public-cli/registry.js +8 -2
- package/dist/public-cli/reviewer-dispatch-rejection.js +77 -0
- package/dist/public-cli/run-lifecycle.js +1344 -0
- package/dist/public-cli/seat-ticket-binding.js +113 -0
- package/dist/public-cli/settlement.js +3441 -0
- package/dist/public-cli/terminal.js +135 -0
- package/dist/public-cli/turn-request.js +25 -0
- package/dist/public-role-summons.js +301 -0
- package/dist/receipt-delivery-policy.js +10 -0
- package/dist/reviewer-child-executor.js +80 -8
- package/dist/reviewer-execution-ledger.js +2 -1
- package/dist/run-terminal-artifacts.js +195 -0
- package/dist/run-ticket-number.js +40 -0
- package/dist/session-assistant-usage.js +107 -0
- package/dist/shape-unreadable-failure.js +34 -0
- package/dist/submission-errors.js +3 -0
- package/dist/submission-ledger.js +388 -0
- package/dist/ticket-provenance-contracts.js +115 -0
- package/dist/ticket-provenance.js +340 -0
- package/extensions/role-runtime.ts +8 -0
- package/package.json +1 -1
- package/resources/diarist-collect.md +6 -10
- package/scripts/build-package.mjs +2 -1
- package/souls/diarist.md +9 -0
- package/souls/doctor-auditor.md +1 -0
- package/souls/evidence-child.md +1 -0
- package/src/acp-host/production-host.ts +4 -0
- package/src/acp-host/role-envelope.ts +4 -3
- package/src/acp-host/role-turn-host.ts +9 -1
- package/src/analyst-gate-cycles-read.ts +37 -2
- package/src/archivist-record-entry.ts +45 -1
- package/src/audit-escalation.ts +17 -0
- package/src/auditor-role.ts +22 -0
- package/src/auditor-soul.ts +42 -0
- package/src/compliance-transport.ts +177 -66
- package/src/diarist-contracts.ts +88 -0
- package/src/diarist-role.ts +60 -0
- package/src/diarist.ts +253 -179
- package/src/doctor-auditor.ts +10 -14
- package/src/doctor-contracts.ts +1 -0
- package/src/doctor-role.ts +2 -2
- package/src/evidence-child-role.ts +22 -0
- package/src/gatekeeper-pass-envelope.ts +109 -0
- package/src/gatekeeper-role.ts +145 -104
- package/src/host-contracts.ts +15 -2
- package/src/institutional-resolution.ts +8 -163
- package/src/judge-auditor.ts +10 -15
- package/src/judge-role.ts +8 -0
- package/src/navigator-attendance.ts +25 -9
- package/src/navigator-public-session.ts +217 -0
- package/src/navigator-session-contracts.ts +61 -54
- package/src/notary-source-run.ts +3 -1
- package/src/package-contracts/auditor-output.ts +82 -0
- package/src/package-contracts/evidence-child-output.ts +51 -0
- package/src/package-contracts/judge-output.ts +1 -0
- package/src/package-contracts/reviewer-output.ts +2 -2
- package/src/package-contracts/terminating-tools.ts +53 -3
- package/src/packaged-role-registry.ts +63 -0
- package/src/pi/adapter.ts +2 -1
- package/src/pi/in-process-session.ts +38 -12
- package/src/pi/role-turn-host.ts +42 -0
- package/src/public-cli/cli.ts +43 -6
- package/src/public-cli/countersign-run.ts +9 -129
- package/src/public-cli/diarist-run.ts +312 -0
- package/src/public-cli/instruction-seat-run.ts +289 -50
- package/src/public-cli/invocation.ts +87 -25
- package/src/public-cli/option-definitions.ts +79 -0
- package/src/public-cli/post-admission.ts +9 -3
- package/src/public-cli/registry.ts +7 -1
- package/src/public-cli/run-lifecycle.ts +53 -6
- package/src/public-cli/settlement.ts +170 -7
- package/src/public-cli/terminal.ts +4 -1
- package/src/public-role-summons.ts +431 -0
- package/src/receipt-delivery-policy.ts +10 -0
- package/src/reviewer-agent.ts +1 -1
- package/src/reviewer-child-executor.ts +114 -12
- package/src/reviewer-execution-ledger.ts +3 -2
- package/src/role-runtime.ts +275 -23
- package/src/session-assistant-usage.ts +119 -0
- package/src/session-opening-materials.ts +1 -1
- package/src/shape-unreadable-failure.ts +49 -0
- package/src/submission-errors.ts +3 -0
- package/src/ticket-provenance-contracts.ts +1 -0
- package/src/ticket-provenance.ts +0 -23
- package/dist/evidence-child-executor.js +0 -829
- package/src/diarist-llm-collector.ts +0 -324
- package/src/evidence-child-executor.ts +0 -1140
package/src/role-runtime.ts
CHANGED
|
@@ -70,6 +70,20 @@ import {
|
|
|
70
70
|
type InspectorRuntimeDependencies,
|
|
71
71
|
} from "./inspector-role.ts";
|
|
72
72
|
import { INSPECTOR_ACCEPTED_TEXT } from "./inspector-contracts.ts";
|
|
73
|
+
import {
|
|
74
|
+
DIARIST_TOOL_SPEC,
|
|
75
|
+
type DiaristRuntimeDependencies,
|
|
76
|
+
} from "./diarist-role.ts";
|
|
77
|
+
import {
|
|
78
|
+
DIARIST_ACCEPTED_TEXT,
|
|
79
|
+
DIARIST_SOURCES_FLAG,
|
|
80
|
+
projectDiaristSelections,
|
|
81
|
+
} from "./diarist-contracts.ts";
|
|
82
|
+
import {
|
|
83
|
+
commitDiaristSelections,
|
|
84
|
+
loadDiaristSourceCatalog,
|
|
85
|
+
type DiaristSourceCatalog,
|
|
86
|
+
} from "./diarist.ts";
|
|
73
87
|
import {
|
|
74
88
|
GATEKEEPER_TOOL_SPEC,
|
|
75
89
|
type GatekeeperRuntimeDependencies,
|
|
@@ -78,6 +92,16 @@ import {
|
|
|
78
92
|
NAVIGATOR_TOOL_SPEC,
|
|
79
93
|
type NavigatorRuntimeDependencies,
|
|
80
94
|
} from "./navigator-role.ts";
|
|
95
|
+
import {
|
|
96
|
+
AUDITOR_TOOL_SPEC,
|
|
97
|
+
type AuditorRuntimeDependencies,
|
|
98
|
+
} from "./auditor-role.ts";
|
|
99
|
+
import { AUDITOR_ACCEPTED_TEXT } from "./package-contracts/auditor-output.ts";
|
|
100
|
+
import {
|
|
101
|
+
EVIDENCE_CHILD_TOOL_SPEC,
|
|
102
|
+
type EvidenceChildRuntimeDependencies,
|
|
103
|
+
} from "./evidence-child-role.ts";
|
|
104
|
+
import { EVIDENCE_CHILD_ACCEPTED_TEXT } from "./package-contracts/evidence-child-output.ts";
|
|
81
105
|
import { GATEKEEPER_ACCEPTED_TEXT } from "./package-contracts/gatekeeper-output.ts";
|
|
82
106
|
import { NAVIGATOR_ACCEPTED_TEXT } from "./package-contracts/navigator-output.ts";
|
|
83
107
|
import { decorateSettlementWithNavigation, formatNavigatorReport, NAVIGATOR_EVENT_TYPE, navigatorSubjectKey, navigatorUnavailableError, subjectPath, type NavigatorAttendance, type NavigatorAttendanceOptions, type NavigatorEvent, type NavigatorPhase, type NavigatorReport, type NavigatorSettlement, type NavigatorSubjectProvenance, type NavigatorTargetRole, type NavigatorWorkContext } from "./navigator-attendance.ts";
|
|
@@ -159,6 +183,14 @@ const GLEANER_LEFT_TRANSPORT_FLAGS = Object.freeze([
|
|
|
159
183
|
}),
|
|
160
184
|
] as const);
|
|
161
185
|
|
|
186
|
+
/** Diarist private transport: frozen source catalog path (ADR 0018 / #708). */
|
|
187
|
+
const DIARIST_TRANSPORT_FLAGS = Object.freeze([
|
|
188
|
+
Object.freeze({
|
|
189
|
+
name: DIARIST_SOURCES_FLAG.name,
|
|
190
|
+
definition: DIARIST_SOURCES_FLAG.definition,
|
|
191
|
+
}),
|
|
192
|
+
] as const);
|
|
193
|
+
|
|
162
194
|
/**
|
|
163
195
|
* Decode private transport flags into frozen admitted inputs.
|
|
164
196
|
* Envelope-owned; necessary JSON decode only (public --authority-ref owns grammar).
|
|
@@ -327,14 +359,11 @@ export {
|
|
|
327
359
|
writeToolExecutionObservationRecord,
|
|
328
360
|
} from "./tool-execution-observation.ts";
|
|
329
361
|
export type { ToolExecutionObservationRecord, ToolExecutionObservationWriter } from "./tool-execution-observation.ts";
|
|
330
|
-
export { executeAuditorChild } from "./evidence-child-executor.ts";
|
|
331
|
-
export type { AuditorDecisionTool } from "./evidence-child-executor.ts";
|
|
332
362
|
export {
|
|
333
363
|
NOTARY_OUTPUT_TOOL,
|
|
334
364
|
INSPECTOR_OUTPUT_TOOL,
|
|
335
365
|
GatekeeperDecisionError,
|
|
336
366
|
createGatekeeperOutputTool,
|
|
337
|
-
createOfficerDecisionTool,
|
|
338
367
|
runGatekeeper,
|
|
339
368
|
} from "./gatekeeper-role.ts";
|
|
340
369
|
export type { GatekeeperResult, GatekeeperSubject, GatekeeperNonPassResult, RunGatekeeperOptions } from "./gatekeeper-role.ts";
|
|
@@ -375,7 +404,13 @@ export { fixerPrerequisiteSchema, fixerPrerequisitesSchema, parseFixerPrerequisi
|
|
|
375
404
|
export type { FixerInvocationInput, FixerPrerequisite } from "./package-contracts/fixer-packet.ts";
|
|
376
405
|
export { AUDIT_ESCALATION_KIND, buildAuditEscalationResult, disposeComplianceDecision, isAuditEscalationResult, projectAuditEscalation } from "./audit-escalation.ts";
|
|
377
406
|
export type { AuditEscalationResult, AuditEscalationToolResult, ComplianceDecisionHandlers } from "./audit-escalation.ts";
|
|
378
|
-
export {
|
|
407
|
+
export {
|
|
408
|
+
AUDITOR_SOUL_ROLES,
|
|
409
|
+
AK_ROLE_AUDITOR_SUBJECT_ENV,
|
|
410
|
+
loadAuditorSoul,
|
|
411
|
+
loadAuditorSoulFromSubjectInput,
|
|
412
|
+
resolveAuditorSubject,
|
|
413
|
+
} from "./auditor-soul.ts";
|
|
379
414
|
export type { AuditorSoulRole } from "./auditor-soul.ts";
|
|
380
415
|
export { JUDGE_AUDIT_TOOL_NAME, SOUL_AUDIT_TOOL_NAME, createPiJudgeAuditor } from "./judge-auditor.ts";
|
|
381
416
|
export { DOCTOR_AUDIT_TOOL_NAME, createPiDoctorAuditor } from "./doctor-auditor.ts";
|
|
@@ -439,6 +474,9 @@ type ActivationRuntime = {
|
|
|
439
474
|
inspector: { activate(): Promise<void> };
|
|
440
475
|
gatekeeper: { activate(): Promise<void> };
|
|
441
476
|
navigator: { activate(): Promise<void> };
|
|
477
|
+
auditor: { activate(): Promise<void> };
|
|
478
|
+
evidenceChild: { activate(): Promise<void> };
|
|
479
|
+
diarist: { activate(): Promise<void> };
|
|
442
480
|
merger(): Promise<void>;
|
|
443
481
|
};
|
|
444
482
|
|
|
@@ -487,6 +525,9 @@ function activationStage(role: PackagedRole, runtime: ActivationRuntime): { id:
|
|
|
487
525
|
case "inspector": return { id: "load-and-install", run: async () => runtime.inspector.activate() };
|
|
488
526
|
case "gatekeeper": return { id: "load-and-install", run: async () => runtime.gatekeeper.activate() };
|
|
489
527
|
case "navigator": return { id: "load-and-install", run: async () => runtime.navigator.activate() };
|
|
528
|
+
case "auditor": return { id: "load-and-install", run: async () => runtime.auditor.activate() };
|
|
529
|
+
case "evidence-child": return { id: "load-and-install", run: async () => runtime.evidenceChild.activate() };
|
|
530
|
+
case "diarist": return { id: "load-and-install", run: async () => runtime.diarist.activate() };
|
|
490
531
|
case "merger": return { id: "prepare-git-and-install", run: async () => runtime.merger() };
|
|
491
532
|
}
|
|
492
533
|
}
|
|
@@ -594,6 +635,9 @@ export type RoleRuntimeDependencies = {
|
|
|
594
635
|
loadInspectorSoul?(): Promise<string>;
|
|
595
636
|
loadGatekeeperSoul?(): Promise<string>;
|
|
596
637
|
loadNavigatorSoul?(): Promise<string>;
|
|
638
|
+
loadAuditorSoul?(): Promise<string>;
|
|
639
|
+
loadEvidenceChildSoul?(): Promise<string>;
|
|
640
|
+
loadDiaristSoul?(): Promise<string>;
|
|
597
641
|
loadDoctorCase?(path: string): Promise<import("./doctor-contracts.ts").DoctorCase>;
|
|
598
642
|
loadMergerSoul?(): Promise<string>;
|
|
599
643
|
loadMergerInput?(path: string): Promise<unknown>;
|
|
@@ -707,13 +751,17 @@ export async function projectClosedSubmissionLifecycle(
|
|
|
707
751
|
await settle(publicNavigatorSettlement(projection.role, phase, closure));
|
|
708
752
|
}
|
|
709
753
|
|
|
710
|
-
/**
|
|
754
|
+
/**
|
|
755
|
+
* Optional pre-accept hook on the shared filed-officer envelope (ADR 0075).
|
|
756
|
+
* May return a details projection (envelope-owned machine facts recorded next to
|
|
757
|
+
* the submitted parameters); undefined keeps the parameters as submitted.
|
|
758
|
+
*/
|
|
711
759
|
type FiledOfficerBeforeAccept = (input: {
|
|
712
760
|
readonly toolCallId: string;
|
|
713
761
|
readonly parameters: unknown;
|
|
714
762
|
readonly signal: AbortSignal | undefined;
|
|
715
763
|
readonly ctx: HostContext;
|
|
716
|
-
}) => Promise<
|
|
764
|
+
}) => Promise<unknown>;
|
|
717
765
|
|
|
718
766
|
/**
|
|
719
767
|
* Shared registration envelope for filed officers (ADR 0018 / #572):
|
|
@@ -749,14 +797,15 @@ function createFiledOfficerRuntime(
|
|
|
749
797
|
parameters: spec.tool.parameters as never,
|
|
750
798
|
async execute(toolCallId, parameters, signal, _onUpdate, ctx): Promise<HostToolResult<unknown>> {
|
|
751
799
|
if (soul === undefined) throw new Error(`${spec.role} 职分未装载`);
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
800
|
+
const projected =
|
|
801
|
+
spec.beforeAccept === undefined
|
|
802
|
+
? undefined
|
|
803
|
+
: await spec.beforeAccept({ toolCallId, parameters, signal, ctx });
|
|
755
804
|
// Accept-as-is + terminate only. Shape is not an admission gate
|
|
756
805
|
// (第 0 条 / ADR 0055); sole-final barrier is ledger-owned (#575).
|
|
757
806
|
return {
|
|
758
807
|
content: [{ type: "text" as const, text: spec.acceptedText }],
|
|
759
|
-
details: parameters,
|
|
808
|
+
details: projected === undefined ? parameters : projected,
|
|
760
809
|
terminate: true as const,
|
|
761
810
|
};
|
|
762
811
|
},
|
|
@@ -849,6 +898,125 @@ export function createNavigatorRoleRuntime(
|
|
|
849
898
|
);
|
|
850
899
|
}
|
|
851
900
|
|
|
901
|
+
/** #675: public 审刑院 seat on the shared filed-officer envelope. */
|
|
902
|
+
export function createAuditorRoleRuntime(
|
|
903
|
+
roleHost: RoleHost,
|
|
904
|
+
dependencies: AuditorRuntimeDependencies,
|
|
905
|
+
) {
|
|
906
|
+
const base = createFiledOfficerRuntime(
|
|
907
|
+
roleHost,
|
|
908
|
+
{
|
|
909
|
+
role: "auditor",
|
|
910
|
+
tool: AUDITOR_TOOL_SPEC,
|
|
911
|
+
acceptedText: AUDITOR_ACCEPTED_TEXT,
|
|
912
|
+
soulTag: "auditor",
|
|
913
|
+
},
|
|
914
|
+
dependencies,
|
|
915
|
+
);
|
|
916
|
+
return {
|
|
917
|
+
async activate() {
|
|
918
|
+
await base.activate();
|
|
919
|
+
// Same tools whether nested or direct (#675): dossier tool always registered.
|
|
920
|
+
// Source run: only the shared --source-run input face (never own-run fallback).
|
|
921
|
+
const { createAuditorDossierTool, AUDITOR_DOSSIER_TOOL_NAME } =
|
|
922
|
+
await import("./auditor-dossier-tool.ts");
|
|
923
|
+
const { AK_ROLE_AUDITOR_SOURCE_RUN_ENV } = await import("./auditor-soul.ts");
|
|
924
|
+
const already = roleHost.getAllTools().some((tool) => tool.name === AUDITOR_DOSSIER_TOOL_NAME);
|
|
925
|
+
if (already) return;
|
|
926
|
+
const sourceRun =
|
|
927
|
+
typeof process.env[AK_ROLE_AUDITOR_SOURCE_RUN_ENV] === "string"
|
|
928
|
+
&& process.env[AK_ROLE_AUDITOR_SOURCE_RUN_ENV].trim() !== ""
|
|
929
|
+
? process.env[AK_ROLE_AUDITOR_SOURCE_RUN_ENV].trim()
|
|
930
|
+
: undefined;
|
|
931
|
+
roleHost.registerTool(createAuditorDossierTool(sourceRun) as never);
|
|
932
|
+
},
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* #708: 起居郎 public seat on the shared filed-officer envelope.
|
|
938
|
+
* Semantic collection happened in this role's own turn; the accept hook runs the
|
|
939
|
+
* mechanical band (verbatim reverse-verify → idempotent sitian append →
|
|
940
|
+
* watermark) and records the resulting sitian facts next to the receipt.
|
|
941
|
+
* Machine facts never come from model self-report (锚定宪法).
|
|
942
|
+
*/
|
|
943
|
+
export function createDiaristRoleRuntime(
|
|
944
|
+
roleHost: RoleHost,
|
|
945
|
+
dependencies: DiaristRuntimeDependencies,
|
|
946
|
+
getSourcesFlag: () => unknown,
|
|
947
|
+
) {
|
|
948
|
+
// This turn's catalog, snapshotted at activate: the candidateIndexes the role
|
|
949
|
+
// saw and the rows accept commits are the same bytes. Accept never re-reads
|
|
950
|
+
// the file, so anything written to it mid-turn cannot become a diary entry.
|
|
951
|
+
let catalog: DiaristSourceCatalog | undefined;
|
|
952
|
+
const runtime = createFiledOfficerRuntime(
|
|
953
|
+
roleHost,
|
|
954
|
+
{
|
|
955
|
+
role: "diarist",
|
|
956
|
+
tool: DIARIST_TOOL_SPEC,
|
|
957
|
+
acceptedText: DIARIST_ACCEPTED_TEXT,
|
|
958
|
+
soulTag: "diarist",
|
|
959
|
+
beforeAccept: async ({ parameters }) => {
|
|
960
|
+
const submitted =
|
|
961
|
+
parameters !== null && typeof parameters === "object" && !Array.isArray(parameters)
|
|
962
|
+
? (parameters as Record<string, unknown>)
|
|
963
|
+
: undefined;
|
|
964
|
+
// True-unbound summons: no ticket, no catalog, no diary committed — so
|
|
965
|
+
// this turn produced no sitian facts. A self-reported `sitian` is not
|
|
966
|
+
// one (锚定宪法); drop it rather than let it ride into decisiveFacts.
|
|
967
|
+
// Carrying the field is not a reason to bounce the receipt (第 0 条).
|
|
968
|
+
if (catalog === undefined) {
|
|
969
|
+
if (submitted === undefined || !("sitian" in submitted)) return undefined;
|
|
970
|
+
const stripped = { ...submitted };
|
|
971
|
+
delete stripped.sitian;
|
|
972
|
+
return stripped;
|
|
973
|
+
}
|
|
974
|
+
const facts = await commitDiaristSelections({
|
|
975
|
+
catalog,
|
|
976
|
+
selections: projectDiaristSelections(parameters),
|
|
977
|
+
});
|
|
978
|
+
return { ...(submitted ?? { receipt: parameters }), sitian: facts };
|
|
979
|
+
},
|
|
980
|
+
},
|
|
981
|
+
dependencies,
|
|
982
|
+
);
|
|
983
|
+
return {
|
|
984
|
+
async activate() {
|
|
985
|
+
catalog = loadFrozenDiaristCatalog(getSourcesFlag);
|
|
986
|
+
return runtime.activate();
|
|
987
|
+
},
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/** Envelope-owned decode + one-shot load of the frozen catalog (ADR 0018). */
|
|
992
|
+
function loadFrozenDiaristCatalog(
|
|
993
|
+
getFlag: () => unknown,
|
|
994
|
+
): DiaristSourceCatalog | undefined {
|
|
995
|
+
const raw = getFlag();
|
|
996
|
+
if (raw === undefined) return undefined;
|
|
997
|
+
if (typeof raw !== "string" || raw.trim() === "") {
|
|
998
|
+
throw new Error("Diarist source catalog flag must be a nonempty path");
|
|
999
|
+
}
|
|
1000
|
+
return loadDiaristSourceCatalog(raw);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/** #675: public evidence-child seat on the shared filed-officer envelope. */
|
|
1004
|
+
export function createEvidenceChildRoleRuntime(
|
|
1005
|
+
roleHost: RoleHost,
|
|
1006
|
+
dependencies: EvidenceChildRuntimeDependencies,
|
|
1007
|
+
) {
|
|
1008
|
+
return createFiledOfficerRuntime(
|
|
1009
|
+
roleHost,
|
|
1010
|
+
{
|
|
1011
|
+
role: "evidence-child",
|
|
1012
|
+
tool: EVIDENCE_CHILD_TOOL_SPEC,
|
|
1013
|
+
acceptedText: EVIDENCE_CHILD_ACCEPTED_TEXT,
|
|
1014
|
+
soulTag: "evidence-child",
|
|
1015
|
+
},
|
|
1016
|
+
dependencies,
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
852
1020
|
export function createCountersignRoleRuntime(
|
|
853
1021
|
roleHost: RoleHost,
|
|
854
1022
|
dependencies: CountersignRuntimeDependencies,
|
|
@@ -905,6 +1073,9 @@ export function createRoleRuntimeExtension(
|
|
|
905
1073
|
for (const flag of GLEANER_LEFT_TRANSPORT_FLAGS) {
|
|
906
1074
|
roleHost.registerFlag(flag.name, flag.definition);
|
|
907
1075
|
}
|
|
1076
|
+
for (const flag of DIARIST_TRANSPORT_FLAGS) {
|
|
1077
|
+
roleHost.registerFlag(flag.name, flag.definition);
|
|
1078
|
+
}
|
|
908
1079
|
|
|
909
1080
|
let admitted = false;
|
|
910
1081
|
let selectedRole: string | undefined;
|
|
@@ -927,6 +1098,9 @@ export function createRoleRuntimeExtension(
|
|
|
927
1098
|
// terminating-tool rejections and mechanical delivery requests share two turns.
|
|
928
1099
|
let receiptDelivery = createReceiptDeliveryPolicy();
|
|
929
1100
|
let noReceiptRecorded = false;
|
|
1101
|
+
// Public-run fetch observation (in-process-session statusAwareFetch face).
|
|
1102
|
+
let priorFetch: typeof globalThis.fetch | undefined;
|
|
1103
|
+
let fetchWrapped = false;
|
|
930
1104
|
const settleNavigatorProjection = async (settlement: NavigatorSettlement | undefined) => {
|
|
931
1105
|
const attendance = navigatorAttendance;
|
|
932
1106
|
if (settlement === undefined || attendance === undefined) return;
|
|
@@ -937,6 +1111,9 @@ export function createRoleRuntimeExtension(
|
|
|
937
1111
|
return;
|
|
938
1112
|
}
|
|
939
1113
|
const settlePromise = attendance.settle(settlement);
|
|
1114
|
+
// Attach catch immediately so a late rejection after grace timeout cannot
|
|
1115
|
+
// surface as unhandledRejection / stale-ctx after session dispose (#675).
|
|
1116
|
+
void settlePromise.catch(() => undefined);
|
|
940
1117
|
const raced = await raceNavigatorGrace(settlePromise, NAVIGATOR_POST_ROLE_GRACE_MS);
|
|
941
1118
|
if (raced.status !== "timeout") return;
|
|
942
1119
|
if (pendingNavigatorPresentation === undefined) {
|
|
@@ -963,7 +1140,6 @@ export function createRoleRuntimeExtension(
|
|
|
963
1140
|
pendingNavigatorPresentation = { event, report };
|
|
964
1141
|
}
|
|
965
1142
|
await attendance.dispose();
|
|
966
|
-
void settlePromise.catch(() => undefined);
|
|
967
1143
|
})();
|
|
968
1144
|
pendingNavigatorSettlement = pending;
|
|
969
1145
|
await pending;
|
|
@@ -1190,6 +1366,11 @@ export function createRoleRuntimeExtension(
|
|
|
1190
1366
|
roleHost.on("session_shutdown", async () => {
|
|
1191
1367
|
// #351: stop OAuth keepalive first so shutdown yields zero further ticks.
|
|
1192
1368
|
envelopeHost.stopKeepalive();
|
|
1369
|
+
if (fetchWrapped && priorFetch !== undefined) {
|
|
1370
|
+
globalThis.fetch = priorFetch;
|
|
1371
|
+
priorFetch = undefined;
|
|
1372
|
+
fetchWrapped = false;
|
|
1373
|
+
}
|
|
1193
1374
|
// Flush any still-pending affirmative attendance before teardown. Accepted
|
|
1194
1375
|
// grace-timeout paths normally emit on agent_settled; abort can skip that hook.
|
|
1195
1376
|
const presentation = pendingNavigatorPresentation;
|
|
@@ -1358,6 +1539,28 @@ export function createRoleRuntimeExtension(
|
|
|
1358
1539
|
return dependencies.loadNavigatorSoul();
|
|
1359
1540
|
},
|
|
1360
1541
|
});
|
|
1542
|
+
const auditor = createAuditorRoleRuntime(roleHost, {
|
|
1543
|
+
async loadSoul() {
|
|
1544
|
+
if (!dependencies.loadAuditorSoul) throw new Error("Auditor runtime dependencies are not configured");
|
|
1545
|
+
return dependencies.loadAuditorSoul();
|
|
1546
|
+
},
|
|
1547
|
+
});
|
|
1548
|
+
const evidenceChild = createEvidenceChildRoleRuntime(roleHost, {
|
|
1549
|
+
async loadSoul() {
|
|
1550
|
+
if (!dependencies.loadEvidenceChildSoul) throw new Error("Evidence-child runtime dependencies are not configured");
|
|
1551
|
+
return dependencies.loadEvidenceChildSoul();
|
|
1552
|
+
},
|
|
1553
|
+
});
|
|
1554
|
+
const diarist = createDiaristRoleRuntime(
|
|
1555
|
+
roleHost,
|
|
1556
|
+
{
|
|
1557
|
+
async loadSoul() {
|
|
1558
|
+
if (!dependencies.loadDiaristSoul) throw new Error("Diarist runtime dependencies are not configured");
|
|
1559
|
+
return dependencies.loadDiaristSoul();
|
|
1560
|
+
},
|
|
1561
|
+
},
|
|
1562
|
+
() => envelopeHost.host.getFlag(DIARIST_SOURCES_FLAG.name),
|
|
1563
|
+
);
|
|
1361
1564
|
let sessionMergerGitState = dependencies.mergerGitState;
|
|
1362
1565
|
const merger = createMergerRoleRuntime(roleHost, {
|
|
1363
1566
|
async loadSoul() { if (!dependencies.loadMergerSoul) throw new Error("Merger runtime dependencies are not configured"); return dependencies.loadMergerSoul(); },
|
|
@@ -1443,20 +1646,19 @@ export function createRoleRuntimeExtension(
|
|
|
1443
1646
|
});
|
|
1444
1647
|
|
|
1445
1648
|
// Public Role run: record typed non-success HTTP for error evidence + v1 resume.
|
|
1446
|
-
//
|
|
1447
|
-
//
|
|
1448
|
-
|
|
1649
|
+
// Same observation owner as in-process-session statusAwareFetch → typed-provider-http
|
|
1650
|
+
// sidecar (settlement already merges observation.httpStatus into knownFailure).
|
|
1651
|
+
// after_provider_response covers the success-path onResponse face; fetch wrap covers
|
|
1652
|
+
// non-2xx Responses where openai-completions throws before onResponse (#675).
|
|
1653
|
+
const recordHttpObservation = async (
|
|
1654
|
+
status: number,
|
|
1655
|
+
provider: string,
|
|
1656
|
+
ctx: HostContext,
|
|
1657
|
+
): Promise<void> => {
|
|
1449
1658
|
const runDir = process.env.AK_ROLE_RUN_DIR;
|
|
1450
1659
|
if (typeof runDir !== "string" || runDir.trim() === "") return;
|
|
1451
|
-
const provider = ctx.model?.provider;
|
|
1452
|
-
if (typeof provider !== "string" || provider.trim() === "") return;
|
|
1453
|
-
const status = event.status;
|
|
1454
|
-
if (typeof status !== "number") return;
|
|
1455
1660
|
try {
|
|
1456
|
-
await recordTypedProviderHttpStatus(runDir, {
|
|
1457
|
-
httpStatus: status,
|
|
1458
|
-
provider,
|
|
1459
|
-
});
|
|
1661
|
+
await recordTypedProviderHttpStatus(runDir, { httpStatus: status, provider });
|
|
1460
1662
|
} catch (error) {
|
|
1461
1663
|
if (
|
|
1462
1664
|
status >= 200 &&
|
|
@@ -1469,9 +1671,49 @@ export function createRoleRuntimeExtension(
|
|
|
1469
1671
|
}
|
|
1470
1672
|
failInfrastructure(error, ctx);
|
|
1471
1673
|
}
|
|
1674
|
+
};
|
|
1675
|
+
roleHost.on("after_provider_response", async (event, ctx) => {
|
|
1676
|
+
const status = event.status;
|
|
1677
|
+
if (typeof status !== "number") return;
|
|
1678
|
+
const fromCtx = ctx.model?.provider;
|
|
1679
|
+
const provider =
|
|
1680
|
+
typeof fromCtx === "string" && fromCtx.trim() !== ""
|
|
1681
|
+
? fromCtx
|
|
1682
|
+
: "unknown";
|
|
1683
|
+
await recordHttpObservation(status, provider, ctx);
|
|
1472
1684
|
});
|
|
1473
1685
|
|
|
1474
1686
|
roleHost.on("session_start", async (event, ctx) => {
|
|
1687
|
+
// Scope fetch observation to this public run (in-process-session statusAwareFetch face).
|
|
1688
|
+
if (!fetchWrapped && typeof globalThis.fetch === "function") {
|
|
1689
|
+
priorFetch = globalThis.fetch.bind(globalThis);
|
|
1690
|
+
const underlying = priorFetch;
|
|
1691
|
+
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
1692
|
+
const response = await underlying(input, init);
|
|
1693
|
+
const runDir = process.env.AK_ROLE_RUN_DIR;
|
|
1694
|
+
if (
|
|
1695
|
+
typeof runDir === "string"
|
|
1696
|
+
&& runDir.trim() !== ""
|
|
1697
|
+
&& typeof response?.status === "number"
|
|
1698
|
+
&& (response.status < 200 || response.status >= 300)
|
|
1699
|
+
) {
|
|
1700
|
+
const provider =
|
|
1701
|
+
typeof ctx.model?.provider === "string" && ctx.model.provider.trim() !== ""
|
|
1702
|
+
? ctx.model.provider
|
|
1703
|
+
: "unknown";
|
|
1704
|
+
try {
|
|
1705
|
+
await recordTypedProviderHttpStatus(runDir, {
|
|
1706
|
+
httpStatus: response.status,
|
|
1707
|
+
provider,
|
|
1708
|
+
});
|
|
1709
|
+
} catch {
|
|
1710
|
+
// Observation must not break the provider stream (same as in-process-session).
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
return response;
|
|
1714
|
+
}) as typeof globalThis.fetch;
|
|
1715
|
+
fetchWrapped = true;
|
|
1716
|
+
}
|
|
1475
1717
|
admitted = false;
|
|
1476
1718
|
selectedRole = undefined;
|
|
1477
1719
|
activeReviewerParent = undefined;
|
|
@@ -1524,6 +1766,9 @@ export function createRoleRuntimeExtension(
|
|
|
1524
1766
|
inspector,
|
|
1525
1767
|
gatekeeper,
|
|
1526
1768
|
navigator,
|
|
1769
|
+
auditor,
|
|
1770
|
+
evidenceChild,
|
|
1771
|
+
diarist,
|
|
1527
1772
|
merger: async () => {
|
|
1528
1773
|
if (dependencies.mergerGitState === undefined) {
|
|
1529
1774
|
sessionMergerGitState = dependencies.createMergerGitState?.(ctx.cwd);
|
|
@@ -1542,7 +1787,14 @@ export function createRoleRuntimeExtension(
|
|
|
1542
1787
|
const ledgerHome = resolveActivationLedgerHomeForPath(sessionFile);
|
|
1543
1788
|
const session = durableSessionPointer(ctx.sessionManager);
|
|
1544
1789
|
|
|
1545
|
-
|
|
1790
|
+
// Same public activation face for every role (#675 / owner 09-06): no nested-env
|
|
1791
|
+
// skip, no work-role whitelist. Attendance attaches when the envelope supplies it.
|
|
1792
|
+
// Navigator seat never re-attaches itself — prepare turns already ARE the navigator
|
|
1793
|
+
// public activation (prevents summonPublicRole navigator ↔ attendance recursion).
|
|
1794
|
+
if (
|
|
1795
|
+
dependencies.createNavigatorAttendance !== undefined
|
|
1796
|
+
&& entry.role !== "navigator"
|
|
1797
|
+
) {
|
|
1546
1798
|
let work: NavigatorWorkContext;
|
|
1547
1799
|
let contextError: unknown;
|
|
1548
1800
|
if (dependencies.loadNavigatorWorkContext === undefined) {
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sum assistant-message usage rows from a session.jsonl (nested public summons).
|
|
3
|
+
* Accumulates real cost when rows carry it; never invents cost zeros (#675).
|
|
4
|
+
*/
|
|
5
|
+
import type { Usage } from "@earendil-works/pi-ai";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
|
|
8
|
+
import type { PublicSummonResult } from "./public-role-summons.ts";
|
|
9
|
+
|
|
10
|
+
export async function readAssistantUsageFromSessionFile(
|
|
11
|
+
sessionFile: string,
|
|
12
|
+
): Promise<Usage | undefined> {
|
|
13
|
+
const { readFile } = await import("node:fs/promises");
|
|
14
|
+
let text: string;
|
|
15
|
+
try {
|
|
16
|
+
text = await readFile(sessionFile, "utf8");
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if ((error as NodeJS.ErrnoException)?.code === "ENOENT") return undefined;
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
let rows: Array<{ type?: string; message?: { role?: string; usage?: Usage } }>;
|
|
22
|
+
try {
|
|
23
|
+
rows = text
|
|
24
|
+
.trim()
|
|
25
|
+
.split("\n")
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
.map((line) => JSON.parse(line) as {
|
|
28
|
+
type?: string;
|
|
29
|
+
message?: { role?: string; usage?: Usage };
|
|
30
|
+
});
|
|
31
|
+
} catch (error) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`session usage parse failed (${sessionFile}): ${error instanceof Error ? error.message : String(error)}`,
|
|
34
|
+
{ cause: error },
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
let total = 0;
|
|
38
|
+
let input = 0;
|
|
39
|
+
let output = 0;
|
|
40
|
+
let cacheRead = 0;
|
|
41
|
+
let cacheWrite = 0;
|
|
42
|
+
let costInput = 0;
|
|
43
|
+
let costOutput = 0;
|
|
44
|
+
let costCacheRead = 0;
|
|
45
|
+
let costCacheWrite = 0;
|
|
46
|
+
let costTotal = 0;
|
|
47
|
+
let sawCost = false;
|
|
48
|
+
for (const row of rows) {
|
|
49
|
+
if (row.type !== "message" || row.message?.role !== "assistant") continue;
|
|
50
|
+
const usage = row.message.usage;
|
|
51
|
+
if (usage === undefined) continue;
|
|
52
|
+
total += usage.totalTokens ?? 0;
|
|
53
|
+
input += usage.input ?? 0;
|
|
54
|
+
output += usage.output ?? 0;
|
|
55
|
+
cacheRead += usage.cacheRead ?? 0;
|
|
56
|
+
cacheWrite += usage.cacheWrite ?? 0;
|
|
57
|
+
if (usage.cost !== undefined) {
|
|
58
|
+
sawCost = true;
|
|
59
|
+
costInput += usage.cost.input ?? 0;
|
|
60
|
+
costOutput += usage.cost.output ?? 0;
|
|
61
|
+
costCacheRead += usage.cost.cacheRead ?? 0;
|
|
62
|
+
costCacheWrite += usage.cost.cacheWrite ?? 0;
|
|
63
|
+
costTotal += usage.cost.total ?? 0;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (total <= 0 && input <= 0 && output <= 0) return undefined;
|
|
67
|
+
const base = {
|
|
68
|
+
input,
|
|
69
|
+
output,
|
|
70
|
+
cacheRead,
|
|
71
|
+
cacheWrite,
|
|
72
|
+
totalTokens: total > 0 ? total : input + output + cacheRead + cacheWrite,
|
|
73
|
+
};
|
|
74
|
+
// Cost only when session rows carried it — unknown stays absent (not zero).
|
|
75
|
+
if (!sawCost) return base as Usage;
|
|
76
|
+
return {
|
|
77
|
+
...base,
|
|
78
|
+
cost: {
|
|
79
|
+
input: costInput,
|
|
80
|
+
output: costOutput,
|
|
81
|
+
cacheRead: costCacheRead,
|
|
82
|
+
cacheWrite: costCacheWrite,
|
|
83
|
+
total: costTotal,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the independent role run's session.jsonl from a public summon result.
|
|
90
|
+
* Prefers explicit runDirectory, then terminal artifacts, then no_receipt runPointer.
|
|
91
|
+
*/
|
|
92
|
+
export function sessionFileFromPublicSummon(
|
|
93
|
+
summoned: PublicSummonResult,
|
|
94
|
+
): string | undefined {
|
|
95
|
+
if (typeof summoned.runDirectory === "string" && summoned.runDirectory.trim() !== "") {
|
|
96
|
+
return join(summoned.runDirectory, "session", "session.jsonl");
|
|
97
|
+
}
|
|
98
|
+
const fromArtifacts = summoned.terminal?.artifacts
|
|
99
|
+
?.map((a) => (a as { path?: string }).path)
|
|
100
|
+
.find((p): p is string => typeof p === "string" && p.endsWith("session.jsonl"));
|
|
101
|
+
if (fromArtifacts !== undefined) return fromArtifacts;
|
|
102
|
+
const outcome = summoned.terminal?.roleOutcome;
|
|
103
|
+
if (outcome === undefined) return undefined;
|
|
104
|
+
const facts = (outcome as { decisiveFacts?: Record<string, unknown> }).decisiveFacts;
|
|
105
|
+
const pointer = facts?.runPointer;
|
|
106
|
+
if (typeof pointer === "string" && pointer.trim() !== "") {
|
|
107
|
+
return join(pointer, "session", "session.jsonl");
|
|
108
|
+
}
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Shared usage projection for nested public summons — undefined when unknown. */
|
|
113
|
+
export async function usageFromPublicSummon(
|
|
114
|
+
summoned: PublicSummonResult,
|
|
115
|
+
): Promise<Usage | undefined> {
|
|
116
|
+
const sessionFile = sessionFileFromPublicSummon(summoned);
|
|
117
|
+
if (sessionFile === undefined) return undefined;
|
|
118
|
+
return readAssistantUsageFromSessionFile(sessionFile);
|
|
119
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* errors. Main-role materials derive from PUBLIC_ROLE_RECORDS (including
|
|
4
4
|
* gatekeeper and navigator). Province map reuses that public gatekeeper record
|
|
5
5
|
* plus officer material constants (notary shared). Auditor composition stays in
|
|
6
|
-
* loadAuditorSoul; loaders share joinPackageMaterials.
|
|
6
|
+
* loadAuditorSoul(subject); loaders share joinPackageMaterials.
|
|
7
7
|
*/
|
|
8
8
|
import { existsSync } from "node:fs";
|
|
9
9
|
import { readFile } from "node:fs/promises";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single authoritative shape-unreadable marker (#675 / ADR 0055).
|
|
3
|
+
* Settlement stamps it; consumers only read it — never re-derive from cause=output.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const SHAPE_UNREADABLE_KEY = "shapeUnreadable" as const;
|
|
7
|
+
|
|
8
|
+
export type ShapeUnreadableStamp = {
|
|
9
|
+
readonly [SHAPE_UNREADABLE_KEY]: true;
|
|
10
|
+
readonly candidate: unknown;
|
|
11
|
+
readonly acceptedReceipt: false;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
15
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Settlement seat path: stamp retained candidate as shape-unreadable (not infrastructure). */
|
|
19
|
+
export function stampShapeUnreadableDetails(candidate: unknown): ShapeUnreadableStamp {
|
|
20
|
+
return {
|
|
21
|
+
[SHAPE_UNREADABLE_KEY]: true,
|
|
22
|
+
candidate,
|
|
23
|
+
acceptedReceipt: false,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Marker presence, decided independently of the candidate value: a stamped
|
|
29
|
+
* `undefined` candidate is a shape-unreadable decision, not an absent marker.
|
|
30
|
+
*/
|
|
31
|
+
export type RetainedShapeUnreadable = { readonly candidate: unknown };
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Read the settlement-stamped shape candidate from a failure decisiveFacts payload.
|
|
35
|
+
* Looks at root and secondaryEvidence only for the typed marker — no cause inference.
|
|
36
|
+
*/
|
|
37
|
+
export function retainedShapeUnreadable(decisiveFacts: unknown): RetainedShapeUnreadable | undefined {
|
|
38
|
+
if (!isRecord(decisiveFacts)) return undefined;
|
|
39
|
+
if (decisiveFacts[SHAPE_UNREADABLE_KEY] === true) {
|
|
40
|
+
return { candidate: decisiveFacts.candidate };
|
|
41
|
+
}
|
|
42
|
+
const secondary = isRecord(decisiveFacts.secondaryEvidence)
|
|
43
|
+
? decisiveFacts.secondaryEvidence
|
|
44
|
+
: undefined;
|
|
45
|
+
if (secondary !== undefined && secondary[SHAPE_UNREADABLE_KEY] === true) {
|
|
46
|
+
return { candidate: secondary.candidate };
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
package/src/submission-errors.ts
CHANGED
|
@@ -5,6 +5,9 @@ function gatekeeperNonPassMessage(result: GatekeeperNonPassResult): string {
|
|
|
5
5
|
const findings = result.findings.length === 0 ? "(无 findings)" : result.findings.join("; ");
|
|
6
6
|
return `门下省打回重写,findings:${findings}`;
|
|
7
7
|
}
|
|
8
|
+
if (result.status === "unreadable") {
|
|
9
|
+
return `门下省官回执形状不可读(${result.officer}):${result.reason}`;
|
|
10
|
+
}
|
|
8
11
|
return `门下省 ${result.status}(${result.stage}):${result.reason}`;
|
|
9
12
|
}
|
|
10
13
|
|
|
@@ -66,6 +66,7 @@ export type TicketProvenanceEntry = {
|
|
|
66
66
|
* Separated by recordClass discriminator — never disguised as a source entry.
|
|
67
67
|
*/
|
|
68
68
|
export type TicketProvenanceDiagnosticKind =
|
|
69
|
+
/** Historical rows only — the diarist turn's own failure settles the run (#708). */
|
|
69
70
|
| "collector-failed"
|
|
70
71
|
| "issue-source-failed"
|
|
71
72
|
| "quote-verify-failed";
|