@akagilnc/pi-workflow-roles 0.1.5375 → 0.1.5382

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/acp-host/production-host.js +479 -425
  2. package/dist/collector-tool-schemas.js +18 -0
  3. package/dist/diarist-contracts.js +26 -0
  4. package/dist/filed-submission.js +28 -0
  5. package/dist/gatekeeper-role.js +6 -10
  6. package/dist/gleaner-left-contracts.js +16 -0
  7. package/dist/headless-host/production-host.js +495 -441
  8. package/dist/judge-role.js +8 -16
  9. package/dist/migrate-book-topology.js +262 -7
  10. package/dist/package-contracts/reviewer-output.js +16 -0
  11. package/dist/package-contracts/worker-output.js +18 -2
  12. package/dist/public-cli/instruction-seat-run.js +9 -12
  13. package/dist/public-cli/main.js +283 -26
  14. package/dist/role-submission-declarations.js +131 -0
  15. package/dist/secretariat-contracts.js +23 -1
  16. package/dist/submission-gate.js +5 -8
  17. package/package.json +1 -1
  18. package/src/auditor-role.ts +2 -8
  19. package/src/collector-role.ts +5 -28
  20. package/src/countersign-role.ts +2 -7
  21. package/src/diarist-contracts.ts +37 -0
  22. package/src/diarist-role.ts +4 -44
  23. package/src/doctor-role.ts +16 -11
  24. package/src/filed-submission.ts +44 -0
  25. package/src/gatekeeper-role.ts +6 -14
  26. package/src/gleaner-left-contracts.ts +26 -1
  27. package/src/gleaner-left-role.ts +4 -32
  28. package/src/inspector-role.ts +2 -7
  29. package/src/judge-role.ts +12 -24
  30. package/src/merger-role.ts +5 -8
  31. package/src/navigator-role.ts +2 -8
  32. package/src/notary-role.ts +5 -20
  33. package/src/package-contracts/reviewer-output.ts +25 -0
  34. package/src/package-contracts/worker-output.ts +43 -5
  35. package/src/public-cli/instruction-seat-run.ts +10 -13
  36. package/src/reviewer-role.ts +7 -35
  37. package/src/role-runtime.ts +7 -35
  38. package/src/role-submission-declarations.ts +143 -0
  39. package/src/secretariat-contracts.ts +35 -0
  40. package/src/secretariat-role.ts +4 -40
  41. package/src/submission-gate.ts +8 -9
  42. package/src/worker-role.ts +62 -79
  43. package/src/worker-submission-gates.ts +3 -0
@@ -1,4 +1,6 @@
1
- import type { RoleHost, HostContext, HostToolResult } from "./host-contracts.ts";
1
+ import type { RoleHost, HostContext } from "./host-contracts.ts";
2
+ import { registerFiledSubmissionTool } from "./filed-submission.ts";
3
+ import { roleSubmissionDeclaration } from "./role-submission-declarations.ts";
2
4
  /**
3
5
  * Public Notary role runtime — direct officer seat (not through Gatekeeper province).
4
6
  * Caller supplies only a source-run locator; Notary self-fetches authoritative materials.
@@ -10,7 +12,6 @@ import {
10
12
  NOTARY_OUTPUT_TOOL_NAME,
11
13
  NOTARY_SOURCE_RUN_FLAG,
12
14
  NOTARY_TICKET_FLAG,
13
- notaryOutputSchema,
14
15
  type NotarySourceRunLocator,
15
16
  } from "./notary-contracts.ts";
16
17
 
@@ -156,24 +157,8 @@ export function createNotaryRoleRuntime(
156
157
 
157
158
  if (!registered) {
158
159
  registered = true;
159
- pi.registerTool({
160
- name: NOTARY_OUTPUT_TOOL_NAME,
161
- label: "符宝郎输出",
162
- description: "提交引文保真与票面对齐的 converged/continue/escalate 决议。",
163
- promptSnippet: "提交符宝郎决议",
164
- parameters: notaryOutputSchema,
165
- async execute(toolCallId: string, parameters: unknown, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext, ): Promise<HostToolResult<unknown>> {
166
- if (activation === undefined) {
167
- throw new Error("符宝郎未激活");
168
- }
169
- // #753: handler only records — params as submitted, no findings/disposition rewrite.
170
- // #541 infra declaration + sole-final barrier are ledger-owned (#575).
171
- return {
172
- content: [],
173
- details: parameters,
174
- terminate: true as const,
175
- };
176
- },
160
+ registerFiledSubmissionTool(pi, roleSubmissionDeclaration("notary"), {
161
+ readyError: () => (activation === undefined ? "符宝郎未激活" : undefined),
177
162
  });
178
163
  // Evidence assembly only (ADR 0018): soul + bound projection.
179
164
  // Ticket value is envelope-admitted at activate — not re-read from flags here.
@@ -1,7 +1,32 @@
1
1
  /** Package-owned Reviewer intent — original role payload only (#836 删 8). */
2
2
 
3
+ import { Type } from "typebox";
4
+
5
+ import { openToolObject } from "../open-tool-schema.ts";
6
+ import { withTerminatingOutputDeclarations } from "./terminating-infrastructure.ts";
7
+
3
8
  export const REVIEWER_OUTPUT_TOOL_NAME = "ak_reviewer_output";
4
9
 
10
+ const REVIEWER_AMENDMENTS_DESCRIPTION =
11
+ "已运行 lens 的 amendments 承载 candidates、逐条处置与 verdict 的完整报告(非仅 verdict 行);显式单轴时另一轴可省略(completeness / correctness)。hard-stop/usage error 为 refused 时,已产出报告仍照录对应 lens 字段。" as const;
12
+ // #1134: `status` alone is the trajectory field (#917 §6 two normal lens verdicts →
13
+ // completed; hard-stop / usage error → refused); its words ride the description.
14
+ // amendments/diagnostic are LLM/human-read narrative content returned as
15
+ // submitted — declaration keeps name + semantic description only, no nested
16
+ // object shape, no string type, no required constraint.
17
+ const REVIEWER_STATUS_DESCRIPTION =
18
+ "completed | refused。两种正常 lens verdict(completeness / correctness)均 completed;hard-stop 与 usage error 为 refused(已产出报告仍照录进所选 lens amendments)。" as const;
19
+ const reviewerOutputObject = Type.Object({
20
+ status: Type.Unknown({ description: REVIEWER_STATUS_DESCRIPTION }),
21
+ amendments: Type.Unknown({ description: REVIEWER_AMENDMENTS_DESCRIPTION }),
22
+ diagnostic: Type.Unknown({
23
+ description: "拒绝诊断说明;hard-stop/usage error 原因进此字段,已产出报告另照录 amendments",
24
+ }),
25
+ });
26
+ export const reviewerOutputSchema = withTerminatingOutputDeclarations(
27
+ openToolObject(reviewerOutputObject),
28
+ );
29
+
5
30
  /** Seat-owned per-lens finding body text (completeness / correctness). */
6
31
  export type ReviewerAmendments = Readonly<
7
32
  Partial<Record<"completeness" | "correctness", string>>
@@ -1,19 +1,39 @@
1
1
  /** Package-owned independent Coder and Fixer output leaves. */
2
2
 
3
- export {
3
+ import { Type } from "typebox";
4
+
5
+ import { openToolObject } from "../open-tool-schema.ts";
6
+ import {
4
7
  FIXER_OUTPUT_TOOL_NAME,
5
8
  fixerOutputSchema,
9
+ PARTIALLY_COMPLETED_DEFINITION,
6
10
  validateFixerOutput,
11
+ type FixerBlocker,
12
+ type FixerClassResult,
13
+ type FixerOutput,
14
+ type FixerPhase,
7
15
  } from "./fixer-output.ts";
16
+ import { fixerPrerequisiteSchema, fixerPrerequisitesSchema, parseFixerPrerequisites, validateFixerPrerequisites } from "./fixer-packet.ts";
17
+ import type { FixerInvocationInput, FixerPrerequisite } from "./fixer-packet.ts";
18
+ import { withTerminatingOutputDeclarations } from "./terminating-infrastructure.ts";
19
+
20
+ export {
21
+ FIXER_OUTPUT_TOOL_NAME,
22
+ fixerOutputSchema,
23
+ validateFixerOutput,
24
+ fixerPrerequisiteSchema,
25
+ fixerPrerequisitesSchema,
26
+ parseFixerPrerequisites,
27
+ validateFixerPrerequisites,
28
+ };
8
29
  export type {
9
30
  FixerBlocker,
10
31
  FixerClassResult,
11
32
  FixerOutput,
12
33
  FixerPhase,
13
- } from "./fixer-output.ts";
14
- export { fixerPrerequisiteSchema, fixerPrerequisitesSchema, parseFixerPrerequisites, validateFixerPrerequisites } from "./fixer-packet.ts";
15
- export type { FixerInvocationInput, FixerPrerequisite } from "./fixer-packet.ts";
16
- import { validateFixerOutput, type FixerOutput } from "./fixer-output.ts";
34
+ FixerInvocationInput,
35
+ FixerPrerequisite,
36
+ };
17
37
 
18
38
  export const CODER_OUTPUT_TOOL_NAME = "ak_coder_output";
19
39
  export type WorkerRoleLabel = "Coder" | "Fixer";
@@ -31,3 +51,21 @@ export function validateAcceptedCoderDetails(output: unknown): CoderOutput {
31
51
  export function validateAcceptedWorkerDetails(output: unknown, roleLabel: WorkerRoleLabel = "Coder"): WorkerOutput {
32
52
  return roleLabel === "Fixer" ? validateFixerOutput(output) : validateAcceptedCoderDetails(output);
33
53
  }
54
+
55
+ // status 的合法词写在 description。WORKER_DONE_STATUSES 只让 completed /
56
+ // partially_completed 进入提交闸。unfinished 且未见理由说明时,运行时同 run 催全
57
+ // (ADR 0050);理由在不在不按 JSON 类型判,也不在派发前用长度拒收。
58
+ // report / remainingScope / reason 的声明只留字段名和语义。
59
+ const CODER_STATUS_DESCRIPTION =
60
+ `planned | completed | refused | partially_completed | unfinished。unfinished:缺前置或违宪约束致本局未完成。${PARTIALLY_COMPLETED_DEFINITION}` as const;
61
+ const coderOutputObject = Type.Object({
62
+ status: Type.Unknown({ description: CODER_STATUS_DESCRIPTION }),
63
+ report: Type.Unknown({ description: "如实结果报告" }),
64
+ remainingScope: Type.Unknown({ description: "本局后剩余工作" }),
65
+ reason: Type.Unknown({
66
+ description: "阻断原因:缺前置或违宪约束。缺待决 owner 决定或答复属缺前置。",
67
+ }),
68
+ });
69
+ export const coderOutputSchema = withTerminatingOutputDeclarations(
70
+ openToolObject(coderOutputObject),
71
+ );
@@ -11,6 +11,8 @@ import { createPiDoctorAuditor } from "../doctor-auditor.ts";
11
11
 
12
12
  import type { DurablePrincipalAuthority, HostContext, RoleTurnRequest } from "../host-contracts.ts";
13
13
  import { officerConclusionReask, gateOfficerForSubject } from "../gatekeeper-role.ts";
14
+ import { receivedDiscriminator } from "../submission-errors.ts";
15
+ import { REVIEW_QUEUE_STATUSES } from "../review-submission.ts";
14
16
  import { runJudgeGates } from "../judge-role.ts";
15
17
  import { WORKER_DONE_STATUSES } from "../worker-submission-contracts.ts";
16
18
  import { SECRETARIAT_GATE_OFFICER_ENTRY_TYPE } from "../secretariat-contracts.ts";
@@ -116,7 +118,6 @@ type SeatRunResult = {
116
118
 
117
119
  const AUDITED_ROLES = new Set<PackagedRole>(["judge", "fixer", "coder", "secretariat", "countersign", "doctor"]);
118
120
  const SECRETARIAT_STATUS_REASK = "secretariatStatus 不是 converged、escalate 之一。请重新交卷,secretariatStatus 写明其一。";
119
- const COUNTERSIGN_STATUS_REASK = "status 不是 converged、continue、escalate 三态之一。请重新交卷,status 写明其一。";
120
121
  const WORKER_ROUTING_STATUSES: ReadonlySet<string> = new Set([
121
122
  "planned", "completed", "refused", "partially_completed", "unfinished",
122
123
  ]);
@@ -127,11 +128,10 @@ const DOCTOR_STATUS_REASK =
127
128
  "status 不是 completed、refused 之一。请重新交卷,status 写明其一。";
128
129
  const POST_SUBMISSION_ROUTING: Partial<Record<PackagedRole, {
129
130
  readonly statuses: ReadonlySet<string>;
130
- readonly reask: string;
131
+ readonly reask?: string;
131
132
  }>> = {
132
133
  judge: {
133
- statuses: new Set(["converged", "continue", "escalate"]),
134
- reask: "status 不是 converged、continue、escalate 三态之一。请重新交卷,status 写明其一。",
134
+ statuses: REVIEW_QUEUE_STATUSES,
135
135
  },
136
136
  coder: {
137
137
  statuses: WORKER_ROUTING_STATUSES,
@@ -161,12 +161,10 @@ function unreadablePostSubmissionStatus(
161
161
  const route = POST_SUBMISSION_ROUTING[admitted.role];
162
162
  if (route === undefined) return undefined;
163
163
  const payload = terminal.roleOutcome.payloads?.at(-1);
164
- const status = isRecord(payload)
165
- ? (payload as Record<string, unknown>).status
166
- : undefined;
164
+ const status = receivedDiscriminator(payload, "status");
167
165
  return typeof status === "string" && route.statuses.has(status)
168
166
  ? undefined
169
- : route.reask;
167
+ : route.reask ?? officerConclusionReask(status);
170
168
  }
171
169
 
172
170
  async function reaskUnreadablePostSubmissionStatus(
@@ -908,7 +906,6 @@ export async function runPublicInstructionSeatResume(
908
906
  return resume();
909
907
  }
910
908
 
911
- const QUEUE_CONCLUSIONS = new Set(["converged", "continue", "escalate"]);
912
909
  const GATE_CHILD_ROLES = new Set(["notary", "auditor", "inspector", "countersign"]);
913
910
 
914
911
  /**
@@ -933,7 +930,7 @@ async function queueConclusionFromChild(
933
930
  undefined,
934
931
  );
935
932
  const status = latestQueueStatus(terminal);
936
- if (terminal !== undefined && status !== undefined && QUEUE_CONCLUSIONS.has(status)) {
933
+ if (terminal !== undefined && typeof status === "string" && REVIEW_QUEUE_STATUSES.has(status)) {
937
934
  return { admitted: current, terminal, status };
938
935
  }
939
936
  const reasked = await runPublicInstructionSeatResume({
@@ -944,7 +941,7 @@ async function queueConclusionFromChild(
944
941
  return { stop: reasked };
945
942
  }
946
943
  const reaskedStatus = latestQueueStatus(reasked.terminal);
947
- if (reaskedStatus !== undefined && QUEUE_CONCLUSIONS.has(reaskedStatus)) {
944
+ if (typeof reaskedStatus === "string" && REVIEW_QUEUE_STATUSES.has(reaskedStatus)) {
948
945
  return { admitted: reasked.admitted, terminal: reasked.terminal, status: reaskedStatus };
949
946
  }
950
947
  if (latestPayloadEscalated(reasked.terminal.roleOutcome)) return { stop: reasked };
@@ -1006,8 +1003,8 @@ async function auditSubmittedRole(
1006
1003
  return runPublicInstructionSeatResume({ runId: admitted.runId, message: SECRETARIAT_STATUS_REASK },
1007
1004
  { ...env, autoResumeLimit: 0 }, io);
1008
1005
  }
1009
- if (admitted.role === "countersign" && (typeof status !== "string" || !QUEUE_CONCLUSIONS.has(status))) {
1010
- return runPublicInstructionSeatResume({ runId: admitted.runId, message: COUNTERSIGN_STATUS_REASK },
1006
+ if (admitted.role === "countersign" && (typeof status !== "string" || !REVIEW_QUEUE_STATUSES.has(status))) {
1007
+ return runPublicInstructionSeatResume({ runId: admitted.runId, message: officerConclusionReask(status) },
1011
1008
  { ...env, autoResumeLimit: 0 }, io);
1012
1009
  }
1013
1010
  // Self-escalation is a valid open routing state for these seats only.
@@ -1,12 +1,9 @@
1
- import type { RoleHost, HostContext, HostToolResult } from "./host-contracts.ts";
2
- import { Type } from "typebox";
3
- import { openToolObject } from "./open-tool-schema.ts";
4
- import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
1
+ import type { RoleHost, HostContext } from "./host-contracts.ts";
5
2
 
6
- import { REVIEWER_OUTPUT_TOOL_NAME, type ReviewerIntent } from "./package-contracts/reviewer-output.ts";
3
+ import { registerFiledSubmissionTool } from "./filed-submission.ts";
4
+ import { roleSubmissionDeclaration } from "./role-submission-declarations.ts";
7
5
 
8
- export { REVIEWER_OUTPUT_TOOL_NAME };
9
- export type { ReviewerIntent };
6
+ export { REVIEWER_OUTPUT_TOOL_NAME, reviewerOutputSchema, type ReviewerIntent } from "./package-contracts/reviewer-output.ts";
10
7
 
11
8
  /** Frozen admitted inputs the behavior layer may consume — no flag surface. */
12
9
  export type ReviewerAdmittedInputs = Readonly<{
@@ -17,25 +14,6 @@ export type ReviewerAdmittedInputs = Readonly<{
17
14
  ticketNumber?: number;
18
15
  }>;
19
16
 
20
- const REVIEWER_AMENDMENTS_DESCRIPTION =
21
- "已运行 lens 的 amendments 承载 candidates、逐条处置与 verdict 的完整报告(非仅 verdict 行);显式单轴时另一轴可省略(completeness / correctness)。hard-stop/usage error 为 refused 时,已产出报告仍照录对应 lens 字段。" as const;
22
- // #1134: `status` alone is the trajectory field (#917 §6 two normal lens verdicts →
23
- // completed; hard-stop / usage error → refused); its words ride the description.
24
- // amendments/diagnostic are LLM/human-read narrative content returned as
25
- // submitted — declaration keeps name + semantic description only, no nested
26
- // object shape, no string type, no required constraint.
27
- const REVIEWER_STATUS_DESCRIPTION =
28
- "completed | refused。两种正常 lens verdict(completeness / correctness)均 completed;hard-stop 与 usage error 为 refused(已产出报告仍照录进所选 lens amendments)。" as const;
29
- const reviewerOutputObject = Type.Object({
30
- status: Type.Unknown({ description: REVIEWER_STATUS_DESCRIPTION }),
31
- amendments: Type.Unknown({ description: REVIEWER_AMENDMENTS_DESCRIPTION }),
32
- diagnostic: Type.Unknown({
33
- description: "拒绝诊断说明;hard-stop/usage error 原因进此字段,已产出报告另照录 amendments",
34
- }),
35
- });
36
- export const reviewerOutputSchema = withTerminatingOutputDeclarations(
37
- openToolObject(reviewerOutputObject),
38
- );
39
17
  export type ReviewerRoleDependencies = {
40
18
  loadSoul(): Promise<string>;
41
19
  };
@@ -70,15 +48,9 @@ export function createReviewerRoleRuntime(
70
48
 
71
49
  if (!registered) {
72
50
  registered = true;
73
- pi.registerTool({ name: REVIEWER_OUTPUT_TOOL_NAME, label: "御史台输出", description: "提交御史台终局回执。", promptSnippet: "提交御史台终局回执", parameters: reviewerOutputSchema,
74
- async execute(_id: string, parameters: unknown): Promise<HostToolResult<unknown>> {
75
- if (!soul) throw new Error("御史台输入未装载");
76
- return {
77
- content: [],
78
- details: parameters,
79
- terminate: true as const,
80
- };
81
- } });
51
+ registerFiledSubmissionTool(pi, roleSubmissionDeclaration("reviewer"), {
52
+ readyError: () => (soul === undefined || soul.length === 0 ? "御史台输入未装载" : undefined),
53
+ });
82
54
  }
83
55
  const activatedSoul = soul;
84
56
  const activatedBase = fixedBaseRevision;
@@ -6,7 +6,6 @@ import {
6
6
 
7
7
  runDirectoryFromHostContext,
8
8
  type HostContext,
9
- type HostToolResult,
10
9
  type RoleEnvelopeHost,
11
10
  type RoleHost,
12
11
  } from "./host-contracts.ts";
@@ -14,6 +13,8 @@ import {
14
13
  import { Value } from "typebox/value";
15
14
  import { sitianReport } from "./sitian-facade.ts";
16
15
  import { createSubmissionLedgerHost, sealAcceptedSubmission } from "./submission-ledger.ts";
16
+ import { registerFiledSubmissionTool, type FiledSubmissionBeforeAccept } from "./filed-submission.ts";
17
+ import type { RoleSubmissionDeclaration } from "./role-submission-declarations.ts";
17
18
 
18
19
  import { activationTraceRecordSchema, namedActivationCause, type ActivationTraceRecord, type ActivationTraceWriter } from "./activation-trace.ts";
19
20
  import { homeFromRunDirectory } from "./activation-ledger-topology.ts";
@@ -547,18 +548,6 @@ export async function projectClosedSubmissionLifecycle(
547
548
  }
548
549
  }
549
550
 
550
- /**
551
- * Optional pre-accept hook on the shared filed-officer envelope (ADR 0075).
552
- * May return a details projection (envelope-owned machine facts recorded next to
553
- * the submitted parameters); undefined keeps the parameters as submitted.
554
- */
555
- type FiledOfficerBeforeAccept = (input: {
556
- readonly toolCallId: string;
557
- readonly parameters: unknown;
558
- readonly signal: AbortSignal | undefined;
559
- readonly ctx: HostContext;
560
- }) => Promise<unknown>;
561
-
562
551
  /**
563
552
  * Shared registration envelope for filed officers (ADR 0018 / #572):
564
553
  * activate, tool register, before_agent_start prompt, inventory check.
@@ -569,9 +558,9 @@ function createFiledOfficerRuntime(
569
558
  roleHost: RoleHost,
570
559
  spec: {
571
560
  role: PackagedRole;
572
- tool: { name: string; label: string; description: string; promptSnippet: string; parameters: unknown };
561
+ tool: RoleSubmissionDeclaration;
573
562
  soulTag: string;
574
- beforeAccept?: FiledOfficerBeforeAccept;
563
+ beforeAccept?: FiledSubmissionBeforeAccept;
575
564
  },
576
565
  dependencies: { loadSoul(): Promise<string> },
577
566
  ) {
@@ -584,26 +573,9 @@ function createFiledOfficerRuntime(
584
573
  soul = loaded;
585
574
  if (!registered) {
586
575
  registered = true;
587
- roleHost.registerTool({
588
- name: spec.tool.name,
589
- label: spec.tool.label,
590
- description: spec.tool.description,
591
- promptSnippet: spec.tool.promptSnippet,
592
- parameters: spec.tool.parameters as never,
593
- async execute(toolCallId, parameters, signal, _onUpdate, ctx): Promise<HostToolResult<unknown>> {
594
- if (soul === undefined) throw new Error(`${spec.role} 职分未装载`);
595
- const projected =
596
- spec.beforeAccept === undefined
597
- ? undefined
598
- : await spec.beforeAccept({ toolCallId, parameters, signal, ctx });
599
- // Accept-as-is + terminate only. Shape is not an admission gate
600
- // (第 0 条 / ADR 0055); sole-final barrier is ledger-owned (#575).
601
- return {
602
- content: [],
603
- details: projected === undefined ? parameters : projected,
604
- terminate: true as const,
605
- };
606
- },
576
+ registerFiledSubmissionTool(roleHost, spec.tool, {
577
+ readyError: () => (soul === undefined ? `${spec.role} 职分未装载` : undefined),
578
+ ...(spec.beforeAccept === undefined ? {} : { beforeAccept: spec.beforeAccept }),
607
579
  });
608
580
  roleHost.on("before_agent_start", (event) => {
609
581
  if (soul === undefined) throw new Error(`${spec.role} 职分未装载`);
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Field guidance for each public seat's submission tool (#1031).
3
+ * The tool name is the role registry's outputTool. This module does not restate it.
4
+ * Review seats keep the #1028 schema. Other seats keep their own field guidance.
5
+ * Execute lives in filed-submission.ts.
6
+ */
7
+ import { packagedRoleOutputTool, type PackagedRole } from "./packaged-role-registry.ts";
8
+ import { reviewSubmissionSchema } from "./review-submission.ts";
9
+ import { collectorOutputArgsSchema } from "./collector-tool-schemas.ts";
10
+ import { diaristOutputSchema } from "./diarist-contracts.ts";
11
+ import { doctorSubmissionSchema, DOCTOR_OUTPUT_TOOL_DESCRIPTION } from "./doctor-contracts.ts";
12
+ import { fixerOutputSchema } from "./package-contracts/fixer-output.ts";
13
+ import { gatekeeperOutputSchema } from "./package-contracts/gatekeeper-output.ts";
14
+ import { gleanerLeftOutputSchema } from "./gleaner-left-contracts.ts";
15
+ import { mergerOutputSchema } from "./merger-contracts.ts";
16
+ import { navigatorOutputSchema } from "./package-contracts/navigator-output.ts";
17
+ import { reviewerOutputSchema } from "./package-contracts/reviewer-output.ts";
18
+ import { secretariatVerdictSchema } from "./secretariat-contracts.ts";
19
+ import { coderOutputSchema } from "./package-contracts/worker-output.ts";
20
+
21
+ export type RoleSubmissionDeclaration = {
22
+ readonly role: PackagedRole;
23
+ readonly name: string;
24
+ readonly label: string;
25
+ readonly description: string;
26
+ readonly promptSnippet?: string;
27
+ readonly parameters: unknown;
28
+ };
29
+
30
+ type SubmissionFace = Omit<RoleSubmissionDeclaration, "role" | "name">;
31
+
32
+ const SUBMISSION_FACE = {
33
+ judge: {
34
+ label: "大理寺输出",
35
+ description: "提交大理寺终局判词;交卷调用结束后,由公开调用接缝按判词状态执行适用审核。",
36
+ promptSnippet: "提交大理寺终局判词",
37
+ parameters: reviewSubmissionSchema,
38
+ },
39
+ fixer: {
40
+ label: "修内司输出",
41
+ description: "提交修内司终局回执。",
42
+ promptSnippet: "提交修内司终局回执",
43
+ parameters: fixerOutputSchema,
44
+ },
45
+ coder: {
46
+ label: "将作监输出",
47
+ description: "提交将作监终局回执。",
48
+ promptSnippet: "提交将作监终局回执",
49
+ parameters: coderOutputSchema,
50
+ },
51
+ reviewer: {
52
+ label: "御史台输出",
53
+ description: "提交御史台终局回执。",
54
+ promptSnippet: "提交御史台终局回执",
55
+ parameters: reviewerOutputSchema,
56
+ },
57
+ collector: {
58
+ label: "通进司输出",
59
+ description: "提交通进司回执。",
60
+ promptSnippet: "提交通进司回执",
61
+ parameters: collectorOutputArgsSchema,
62
+ },
63
+ doctor: {
64
+ label: "太医署输出",
65
+ description: DOCTOR_OUTPUT_TOOL_DESCRIPTION,
66
+ parameters: doctorSubmissionSchema,
67
+ },
68
+ merger: {
69
+ label: "合并输出",
70
+ description: "提交合并结果;输出分支为 completed 与 escalate。",
71
+ promptSnippet: "提交合并结果",
72
+ parameters: mergerOutputSchema,
73
+ },
74
+ notary: {
75
+ label: "符宝郎输出",
76
+ description: "提交引文保真与票面对齐的 converged/continue/escalate 决议。",
77
+ promptSnippet: "提交符宝郎决议",
78
+ parameters: reviewSubmissionSchema,
79
+ },
80
+ countersign: {
81
+ label: "给事中输出",
82
+ description: "给事中决议。",
83
+ promptSnippet: "给事中决议",
84
+ parameters: reviewSubmissionSchema,
85
+ },
86
+ secretariat: {
87
+ label: "中书省输出",
88
+ description: "中书省终局回执。",
89
+ promptSnippet: "中书省终局回执",
90
+ parameters: secretariatVerdictSchema,
91
+ },
92
+ "gleaner-left": {
93
+ label: "左拾遗输出",
94
+ description: "左拾遗弹章。",
95
+ promptSnippet: "左拾遗弹章",
96
+ parameters: gleanerLeftOutputSchema,
97
+ },
98
+ inspector: {
99
+ label: "台院输出",
100
+ description: "台院终局回执,状态为 converged、continue 或 escalate。",
101
+ promptSnippet: "台院终局回执",
102
+ parameters: reviewSubmissionSchema,
103
+ },
104
+ gatekeeper: {
105
+ label: "门下省决议",
106
+ description: "门下省终局决议。status 为 dispatch 或 pass。",
107
+ promptSnippet: "门下省决议",
108
+ parameters: gatekeeperOutputSchema,
109
+ },
110
+ navigator: {
111
+ label: "游奕使建议",
112
+ description: "游奕使终局回执:散文建议,原样呈现。",
113
+ promptSnippet: "游奕使建议",
114
+ parameters: navigatorOutputSchema,
115
+ },
116
+ auditor: {
117
+ label: "审刑院输出",
118
+ description: "审刑院终局回执,status 为 converged、continue 或 escalate。",
119
+ promptSnippet: "审刑院终局回执",
120
+ parameters: reviewSubmissionSchema,
121
+ },
122
+ diarist: {
123
+ label: "起居郎输出",
124
+ description: "起居郎交单票 sessions 或多票逐票 ticketSessions 边界;认不出本庭对象则 escalate。",
125
+ promptSnippet: "起居郎交边界",
126
+ parameters: diaristOutputSchema,
127
+ },
128
+ } as const satisfies Record<PackagedRole, SubmissionFace>;
129
+
130
+ /** Tool name comes from the registry record. Label and schema stay on this face. */
131
+ export function roleSubmissionDeclaration(role: PackagedRole): RoleSubmissionDeclaration {
132
+ const name = packagedRoleOutputTool(role);
133
+ if (name === undefined) throw new Error(`${role} output tool is not declared`);
134
+ const face = SUBMISSION_FACE[role];
135
+ return {
136
+ role,
137
+ name,
138
+ label: face.label,
139
+ description: face.description,
140
+ ...("promptSnippet" in face ? { promptSnippet: face.promptSnippet } : {}),
141
+ parameters: face.parameters,
142
+ };
143
+ }
@@ -7,7 +7,42 @@
7
7
  * is defaulted, rewritten, or dropped (ADR 0055).
8
8
  */
9
9
 
10
+ import { Type } from "typebox";
11
+
12
+ import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
13
+
10
14
  export const SECRETARIAT_OUTPUT_TOOL_NAME = "ak_secretariat_output";
15
+
16
+ /**
17
+ * 中书省终局回执形状。
18
+ * #1134: `secretariatStatus` alone is the trajectory field
19
+ * (src/packaged-role-registry.ts receiptStatusKey; the public settlement reads
20
+ * converged | escalate to route); its words ride the description. note/evidence/
21
+ * decisionGate are declared name + semantic description only — the string type, the
22
+ * number type, the nested question/options object shape and the required constraint
23
+ * are deleted, because the package declaration IS the host's pre-dispatch validator.
24
+ * (ticketNumber keeps its own declaration: it is the declared seat identity the
25
+ * activation flag and settlement read, not narrative content.)
26
+ */
27
+ export const secretariatVerdictSchema = withTerminatingOutputDeclarations(
28
+ Type.Object(
29
+ {
30
+ secretariatStatus: Type.Unknown({
31
+ description: "converged | escalate",
32
+ }),
33
+ ticketNumber: Type.Optional(
34
+ Type.Unknown({ description: "本票号;署时指向最终正文所在票" }),
35
+ ),
36
+ note: Type.Unknown({ description: "附注" }),
37
+ evidence: Type.Optional(Type.Unknown({ description: "留存证据" })),
38
+ decisionGate: Type.Unknown({
39
+ description: "需陛下处置的问题与选项;可写 question 与 options,原样留存。",
40
+ }),
41
+ },
42
+ { additionalProperties: true },
43
+ ),
44
+ );
45
+ (secretariatVerdictSchema as unknown as { required: string[] }).required = [];
11
46
  /**
12
47
  * #969 durable custom entry: 给事中 terminal (署|上呈) for seat settlement projection.
13
48
  * Carries original receipt bytes + nested runId. Envelope persists custom entries
@@ -4,54 +4,18 @@
4
4
  * shared envelope — src/role-runtime.ts (ADR 0018 / #924).
5
5
  */
6
6
  import type { Static } from "typebox";
7
- import { Type } from "typebox";
8
7
 
9
- import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
10
- import { SECRETARIAT_OUTPUT_TOOL_NAME, type SecretariatVerdict } from "./secretariat-contracts.ts";
8
+ import { SECRETARIAT_OUTPUT_TOOL_NAME, secretariatVerdictSchema, type SecretariatVerdict } from "./secretariat-contracts.ts";
9
+ import { roleSubmissionDeclaration } from "./role-submission-declarations.ts";
11
10
 
12
11
  export { SECRETARIAT_OUTPUT_TOOL_NAME } from "./secretariat-contracts.ts";
13
12
  export type { SecretariatVerdict };
14
13
 
15
- /**
16
- * 中书省终局回执形状。
17
- * #1134: `secretariatStatus` alone is the trajectory field
18
- * (src/packaged-role-registry.ts:406 receiptStatusKey; the public settlement reads
19
- * converged | escalate to route); its words ride the description. note/evidence/
20
- * decisionGate are declared name + semantic description only — the string type, the
21
- * number type, the nested question/options object shape and the required constraint
22
- * are deleted, because the package declaration IS the host's pre-dispatch validator.
23
- * (ticketNumber keeps its own declaration: it is the declared seat identity the
24
- * activation flag and settlement read, not narrative content.)
25
- */
26
- export const secretariatVerdictSchema = withTerminatingOutputDeclarations(
27
- Type.Object(
28
- {
29
- secretariatStatus: Type.Unknown({
30
- description: "converged | escalate",
31
- }),
32
- ticketNumber: Type.Optional(
33
- Type.Unknown({ description: "本票号;署时指向最终正文所在票" }),
34
- ),
35
- note: Type.Unknown({ description: "附注" }),
36
- evidence: Type.Optional(Type.Unknown({ description: "留存证据" })),
37
- decisionGate: Type.Unknown({
38
- description: "需陛下处置的问题与选项;可写 question 与 options,原样留存。",
39
- }),
40
- },
41
- { additionalProperties: true },
42
- ),
43
- );
44
- (secretariatVerdictSchema as unknown as { required: string[] }).required = [];
14
+ export { secretariatVerdictSchema } from "./secretariat-contracts.ts";
45
15
 
46
16
  export type SecretariatVerdictParameters = Static<typeof secretariatVerdictSchema>;
47
17
 
48
- export const SECRETARIAT_OUTPUT_TOOL_SPEC = {
49
- name: SECRETARIAT_OUTPUT_TOOL_NAME,
50
- label: "中书省输出",
51
- description: "中书省终局回执。",
52
- promptSnippet: "中书省终局回执",
53
- parameters: secretariatVerdictSchema,
54
- } as const;
18
+ export const SECRETARIAT_OUTPUT_TOOL_SPEC = roleSubmissionDeclaration("secretariat");
55
19
 
56
20
  export type SecretariatRuntimeDependencies = {
57
21
  loadSoul(): Promise<string>;
@@ -28,21 +28,20 @@ import {
28
28
  } from "./gatekeeper-role.ts";
29
29
  import type { PublicSummonResult } from "./public-role-summons.ts";
30
30
  import type { TerminalResult } from "./public-cli/terminal.ts";
31
+ import { receivedDiscriminator } from "./submission-errors.ts";
32
+ import type { ReviewQueueWord } from "./review-submission.ts";
31
33
 
32
- /** Queue word of the latest this-terminal payload. History does not outrank it. */
33
- export function latestQueueStatus(terminal: TerminalResult | undefined): string | undefined {
34
+ /** Received discriminator of the latest payload, retained verbatim for re-ask. */
35
+ export function latestQueueStatus(terminal: TerminalResult | undefined): unknown {
34
36
  const outcome = terminal?.roleOutcome;
35
37
  if (outcome === undefined) return undefined;
36
38
  if (outcome.kind === "audit_escalation") return "escalate";
37
39
  if (outcome.kind !== "accepted") return undefined;
38
40
  const latest = outcome.payloads?.[outcome.payloads.length - 1];
39
- if (isRecord(latest)) {
40
- const status = (latest as { status?: unknown }).status;
41
- if (typeof status === "string" && status.trim() !== "") return status;
41
+ if (isRecord(latest) && Object.hasOwn(latest, "status")) {
42
+ return receivedDiscriminator(latest, "status");
42
43
  }
43
- return typeof outcome.status === "string" && outcome.status.trim() !== ""
44
- ? outcome.status
45
- : undefined;
44
+ return outcome.status;
46
45
  }
47
46
 
48
47
  export function latestQueuePayload(terminal: TerminalResult | undefined): unknown {
@@ -131,7 +130,7 @@ function bookDirectOfficerPointer(
131
130
  * public terminal without a second authority.
132
131
  */
133
132
  export type SubmissionGateOutcome = {
134
- readonly status: "converged" | "continue" | "escalate";
133
+ readonly status: ReviewQueueWord;
135
134
  readonly officer: GateOfficer;
136
135
  readonly receipt: unknown;
137
136
  readonly runId?: string;