@akagilnc/pi-workflow-roles 0.1.5368 → 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 (188) hide show
  1. package/dist/acp-host/production-host.js +1192 -1195
  2. package/dist/activation-ledger-git.js +2 -2
  3. package/dist/activation-ledger-topology.js +1 -7
  4. package/dist/analyst-entry.js +4 -8
  5. package/dist/analyst-gate-cycles-read.js +3 -11
  6. package/dist/analyst-index.js +3 -9
  7. package/dist/analyst-ledger.js +1 -11
  8. package/dist/analyst-metric-families/acceptance-success-rework.js +1 -3
  9. package/dist/analyst-metric-families/round-timeline.js +1 -3
  10. package/dist/analyst-page.js +2 -2
  11. package/dist/archivist-record-topology.js +2 -2
  12. package/dist/audit-escalation.js +2 -1
  13. package/dist/auditor-soul.js +2 -1
  14. package/dist/collector-config.js +6 -8
  15. package/dist/collector-tool-schemas.js +18 -0
  16. package/dist/compliance-transport.js +2 -1
  17. package/dist/countersign-contracts.js +2 -1
  18. package/dist/diarist-contracts.js +29 -2
  19. package/dist/doctor-contracts.js +1 -3
  20. package/dist/doctor-evidence.js +7 -8
  21. package/dist/dossier-resolution.js +1 -3
  22. package/dist/engine-detour-usage.js +1 -3
  23. package/dist/filed-submission.js +28 -0
  24. package/dist/gatekeeper-role.js +8 -19
  25. package/dist/gleaner-left-contracts.js +18 -1
  26. package/dist/headless-host/description.js +14 -17
  27. package/dist/headless-host/production-host.js +1210 -1215
  28. package/dist/host-session-record.js +3 -2
  29. package/dist/inspector-contracts.js +2 -1
  30. package/dist/judge-role.js +8 -16
  31. package/dist/ledger-session-read.js +1 -3
  32. package/dist/merger-contracts.js +9 -9
  33. package/dist/migrate-book-topology.js +610 -321
  34. package/dist/navigator-attendance.js +9 -11
  35. package/dist/navigator-invocation-identity.js +3 -2
  36. package/dist/navigator-public-session.js +3 -5
  37. package/dist/navigator-session-contracts.js +8 -10
  38. package/dist/navigator-work-base.js +5 -8
  39. package/dist/notary-contracts.js +3 -5
  40. package/dist/package-contracts/auditor-output.js +1 -3
  41. package/dist/package-contracts/fixer-packet.js +3 -6
  42. package/dist/package-contracts/gatekeeper-output.js +1 -3
  43. package/dist/package-contracts/navigator-output.js +8 -8
  44. package/dist/package-contracts/reviewer-output.js +16 -0
  45. package/dist/package-contracts/terminating-infrastructure.js +5 -9
  46. package/dist/package-contracts/terminating-tools.js +4 -3
  47. package/dist/package-contracts/worker-output.js +18 -2
  48. package/dist/public-cli/analyst-run.js +3 -2
  49. package/dist/public-cli/auto-resume.js +3 -8
  50. package/dist/public-cli/cli.js +9 -8
  51. package/dist/public-cli/config.js +8 -11
  52. package/dist/public-cli/countersign-run.js +1 -3
  53. package/dist/public-cli/host-providers.js +7 -14
  54. package/dist/public-cli/instruction-seat-run.js +12 -14
  55. package/dist/public-cli/invocation.js +9 -8
  56. package/dist/public-cli/main.js +1820 -1599
  57. package/dist/public-cli/post-admission.js +4 -3
  58. package/dist/public-cli/run-lifecycle.js +14 -26
  59. package/dist/public-cli/settlement.js +10 -26
  60. package/dist/public-role-summons.js +3 -2
  61. package/dist/receipt-delivery-policy.js +1 -3
  62. package/dist/role-run-placement.js +3 -5
  63. package/dist/role-run-relocation.js +12 -19
  64. package/dist/role-submission-declarations.js +131 -0
  65. package/dist/run-terminal-artifacts.js +1 -11
  66. package/dist/run-ticket-number.js +2 -4
  67. package/dist/secretariat-contracts.js +23 -1
  68. package/dist/session-assistant-usage.js +2 -1
  69. package/dist/session-dialogue.js +1 -14
  70. package/dist/sitian-reader.js +2 -4
  71. package/dist/submission-correctable-error.js +2 -1
  72. package/dist/submission-errors.js +2 -1
  73. package/dist/submission-gate.js +6 -8
  74. package/dist/submission-ledger.js +4 -5
  75. package/dist/ticket-provenance-contracts.js +1 -3
  76. package/dist/ticket-provenance.js +2 -1
  77. package/dist/typed-provider-http.js +4 -7
  78. package/dist/unknown-value.js +28 -0
  79. package/dist/user-dialogue-stdin.js +2 -1
  80. package/package.json +1 -1
  81. package/src/acp-host/role-turn-host.ts +4 -2
  82. package/src/activation-ledger-git.ts +3 -2
  83. package/src/activation-ledger-topology.ts +4 -12
  84. package/src/analyst-entry.ts +3 -11
  85. package/src/analyst-gate-cycles-read.ts +3 -15
  86. package/src/analyst-index.ts +3 -13
  87. package/src/analyst-ledger.ts +2 -16
  88. package/src/analyst-metric-families/acceptance-success-rework.ts +2 -4
  89. package/src/analyst-metric-families/round-timeline.ts +2 -4
  90. package/src/analyst-page.ts +2 -2
  91. package/src/archivist-record-topology.ts +2 -2
  92. package/src/audit-escalation.ts +2 -1
  93. package/src/auditor-role.ts +2 -8
  94. package/src/auditor-soul.ts +3 -1
  95. package/src/book-topology-migration-placement.ts +5 -16
  96. package/src/book-topology-migrators-t11.ts +16 -15
  97. package/src/book-topology-mixed-volume-migrators.ts +3 -6
  98. package/src/book-topology-record-class-migrators.ts +7 -10
  99. package/src/book-topology-runs-migrator.ts +4 -3
  100. package/src/collector-config.ts +7 -9
  101. package/src/collector-role.ts +5 -28
  102. package/src/compliance-transport.ts +2 -1
  103. package/src/countersign-contracts.ts +2 -1
  104. package/src/countersign-role.ts +2 -7
  105. package/src/diarist-contracts.ts +40 -2
  106. package/src/diarist-role.ts +4 -44
  107. package/src/doctor-contracts.ts +3 -1
  108. package/src/doctor-evidence.ts +8 -7
  109. package/src/doctor-role.ts +20 -13
  110. package/src/dossier-resolution.ts +2 -4
  111. package/src/engine-detour-tool.ts +0 -2
  112. package/src/engine-detour-usage.ts +2 -4
  113. package/src/external-host-turn-loop.ts +3 -1
  114. package/src/factory-board.ts +1 -8
  115. package/src/filed-submission.ts +44 -0
  116. package/src/gatekeeper-role.ts +8 -24
  117. package/src/gleaner-left-contracts.ts +29 -2
  118. package/src/gleaner-left-role.ts +4 -32
  119. package/src/headless-host/description.ts +15 -18
  120. package/src/headless-host/role-turn-host.ts +10 -9
  121. package/src/host-session-record.ts +4 -2
  122. package/src/inspector-contracts.ts +2 -1
  123. package/src/inspector-role.ts +2 -7
  124. package/src/judge-role.ts +12 -24
  125. package/src/ledger-session-read.ts +2 -5
  126. package/src/merger-contracts.ts +10 -9
  127. package/src/merger-role.ts +5 -8
  128. package/src/navigator-attendance.ts +10 -12
  129. package/src/navigator-invocation-identity.ts +3 -2
  130. package/src/navigator-public-session.ts +4 -6
  131. package/src/navigator-role.ts +2 -8
  132. package/src/navigator-session-contracts.ts +8 -11
  133. package/src/navigator-work-base.ts +5 -8
  134. package/src/navigator-work-context.ts +2 -1
  135. package/src/notary-contracts.ts +4 -6
  136. package/src/notary-role.ts +5 -20
  137. package/src/oauth-keepalive.ts +5 -18
  138. package/src/package-contracts/auditor-output.ts +2 -4
  139. package/src/package-contracts/fixer-packet.ts +4 -6
  140. package/src/package-contracts/gatekeeper-output.ts +2 -4
  141. package/src/package-contracts/judge-output.ts +0 -1
  142. package/src/package-contracts/navigator-output.ts +8 -8
  143. package/src/package-contracts/reviewer-output.ts +25 -0
  144. package/src/package-contracts/terminating-infrastructure.ts +6 -20
  145. package/src/package-contracts/terminating-tools.ts +8 -12
  146. package/src/package-contracts/worker-output.ts +43 -5
  147. package/src/pi/adapter.ts +0 -1
  148. package/src/pi/role-turn-host.ts +0 -1
  149. package/src/public-cli/analyst-run.ts +4 -2
  150. package/src/public-cli/auto-resume.ts +4 -11
  151. package/src/public-cli/cli.ts +10 -10
  152. package/src/public-cli/config.ts +8 -27
  153. package/src/public-cli/countersign-run.ts +2 -4
  154. package/src/public-cli/host-providers.ts +10 -27
  155. package/src/public-cli/instruction-seat-run.ts +14 -15
  156. package/src/public-cli/invocation.ts +11 -10
  157. package/src/public-cli/post-admission.ts +5 -3
  158. package/src/public-cli/run-lifecycle.ts +20 -41
  159. package/src/public-cli/settlement.ts +11 -33
  160. package/src/public-role-summons.ts +4 -3
  161. package/src/receipt-delivery-policy.ts +1 -4
  162. package/src/reviewer-role.ts +7 -36
  163. package/src/role-envelope.ts +5 -3
  164. package/src/role-run-placement.ts +4 -10
  165. package/src/role-run-relocation.ts +13 -23
  166. package/src/role-runtime.ts +16 -50
  167. package/src/role-submission-declarations.ts +143 -0
  168. package/src/run-terminal-artifacts.ts +2 -16
  169. package/src/run-ticket-number.ts +4 -10
  170. package/src/secretariat-contracts.ts +35 -0
  171. package/src/secretariat-role.ts +4 -40
  172. package/src/session-assistant-usage.ts +3 -1
  173. package/src/session-dialogue.ts +1 -4
  174. package/src/session-identity.ts +0 -1
  175. package/src/sitian-reader.ts +2 -4
  176. package/src/submission-correctable-error.ts +3 -1
  177. package/src/submission-errors.ts +2 -1
  178. package/src/submission-gate.ts +10 -9
  179. package/src/submission-ledger.ts +5 -6
  180. package/src/ticket-provenance-contracts.ts +1 -4
  181. package/src/ticket-provenance.ts +3 -1
  182. package/src/ticket-snapshot.ts +2 -4
  183. package/src/ticket-trajectory.ts +1 -8
  184. package/src/typed-provider-http.ts +5 -15
  185. package/src/unknown-value.ts +34 -0
  186. package/src/user-dialogue-stdin.ts +3 -1
  187. package/src/worker-role.ts +63 -83
  188. package/src/worker-submission-gates.ts +8 -7
@@ -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,31 +14,11 @@ 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
  };
42
20
  export type ReviewerRoleHostActions = { failInfrastructure(error: unknown, ctx: HostContext, toolCallId?: string): never };
43
21
 
44
-
45
22
  export type ReviewerActivation = Readonly<{
46
23
  fixedBaseRevision: string;
47
24
  soul: string;
@@ -71,15 +48,9 @@ export function createReviewerRoleRuntime(
71
48
 
72
49
  if (!registered) {
73
50
  registered = true;
74
- pi.registerTool({ name: REVIEWER_OUTPUT_TOOL_NAME, label: "御史台输出", description: "提交御史台终局回执。", promptSnippet: "提交御史台终局回执", parameters: reviewerOutputSchema,
75
- async execute(_id: string, parameters: unknown): Promise<HostToolResult<unknown>> {
76
- if (!soul) throw new Error("御史台输入未装载");
77
- return {
78
- content: [],
79
- details: parameters,
80
- terminate: true as const,
81
- };
82
- } });
51
+ registerFiledSubmissionTool(pi, roleSubmissionDeclaration("reviewer"), {
52
+ readyError: () => (soul === undefined || soul.length === 0 ? "御史台输入未装载" : undefined),
53
+ });
83
54
  }
84
55
  const activatedSoul = soul;
85
56
  const activatedBase = fixedBaseRevision;
@@ -35,6 +35,8 @@ import {
35
35
  } from "./navigator-invocation-identity.ts";
36
36
  import { serializeThrownValue } from "./serialize-thrown-value.ts";
37
37
 
38
+ import { isRecord, errorText } from "./unknown-value.ts";
39
+
38
40
  export { projectActivationFlags };
39
41
 
40
42
  type Handler = HostEventRegistration[1];
@@ -158,7 +160,7 @@ export async function prepareRoleEnvelope(options: {
158
160
  try {
159
161
  await appendFile(sessionFile, line, "utf8");
160
162
  } catch (error) {
161
- const diagnostic = error instanceof Error ? error.message : String(error);
163
+ const diagnostic = errorText(error);
162
164
  armDurableWriteFailure?.(customType, diagnostic);
163
165
  }
164
166
  });
@@ -291,7 +293,7 @@ export async function prepareRoleEnvelope(options: {
291
293
  content: ContentPart[],
292
294
  ): void {
293
295
  if (infrastructureRoundFailure !== undefined) return;
294
- const record = typeof details === "object" && details !== null && !Array.isArray(details)
296
+ const record = isRecord(details)
295
297
  ? details as Record<string, unknown>
296
298
  : undefined;
297
299
  const isInfra = record !== undefined && (
@@ -336,7 +338,7 @@ export async function prepareRoleEnvelope(options: {
336
338
  content: ContentPart[];
337
339
  details: Record<string, unknown>;
338
340
  } {
339
- const diagnostic = error instanceof Error ? error.message : String(error);
341
+ const diagnostic = errorText(error);
340
342
  const content: ContentPart[] = [{ type: "text", text: diagnostic }];
341
343
  const errorCode = typeof (error as unknown as { code?: unknown })?.code === "string"
342
344
  ? (error as unknown as { code: string }).code
@@ -7,6 +7,8 @@ import {
7
7
  } from "./activation-ledger-topology.ts";
8
8
  import { requireSafePositiveTicketNumber } from "./run-ticket-number.ts";
9
9
 
10
+ import { isEnoent } from "./unknown-value.ts";
11
+
10
12
  export type RoleRunSubject =
11
13
  | { readonly ticketNumber: number }
12
14
  | { readonly unbound: true };
@@ -19,14 +21,6 @@ export type RoleRunPlacement = {
19
21
  readonly attachmentsDirectory: string;
20
22
  };
21
23
 
22
- function isMissingPathError(error: unknown): boolean {
23
- return (
24
- error instanceof Error &&
25
- "code" in error &&
26
- (error as NodeJS.ErrnoException).code === "ENOENT"
27
- );
28
- }
29
-
30
24
  /**
31
25
  * Sole book-level run directory walk: flat legacy `runs/` plus each
32
26
  * `subject/runs/` child (ticket / unbound). One authority for read-side
@@ -42,7 +36,7 @@ export async function listBookRunDirectories(bookDir: string): Promise<string[]>
42
36
  try {
43
37
  entries = await readdir(runsDir, { withFileTypes: true });
44
38
  } catch (error) {
45
- if (isMissingPathError(error)) return;
39
+ if (isEnoent(error)) return;
46
40
  throw error;
47
41
  }
48
42
  for (const entry of entries) {
@@ -60,7 +54,7 @@ export async function listBookRunDirectories(bookDir: string): Promise<string[]>
60
54
  try {
61
55
  subjects = await readdir(bookDir, { withFileTypes: true });
62
56
  } catch (error) {
63
- if (isMissingPathError(error)) return out.sort();
57
+ if (isEnoent(error)) return out.sort();
64
58
  throw error;
65
59
  }
66
60
  for (const subject of subjects) {
@@ -12,6 +12,8 @@ import { join, sep } from "node:path";
12
12
 
13
13
  import { AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE } from "./compliance-transport.ts";
14
14
 
15
+ import { isRecord, isEnoent } from "./unknown-value.ts";
16
+
15
17
  const ADMITTED_PAGE_FIELDS = [
16
18
  "runDirectory",
17
19
  "admittedRequestPath",
@@ -50,14 +52,6 @@ export type RunDirectoryPathRewrite = {
50
52
  readonly newRunDirectory: string;
51
53
  };
52
54
 
53
- function isEnoent(error: unknown): boolean {
54
- return (
55
- error instanceof Error &&
56
- "code" in error &&
57
- (error as NodeJS.ErrnoException).code === "ENOENT"
58
- );
59
- }
60
-
61
55
  /** Rewrite one path value that points at or under oldRunDirectory. */
62
56
  export function rewriteRunDirectoryPathValue(
63
57
  value: unknown,
@@ -158,16 +152,12 @@ function collectRewrites(input: {
158
152
  return out;
159
153
  }
160
154
 
161
- function isPlainObject(value: unknown): value is Record<string, unknown> {
162
- return value !== null && typeof value === "object" && !Array.isArray(value);
163
- }
164
-
165
155
  /** Typed notary/source-run locator: only runDirectory is a path. */
166
156
  function rewriteSourceRunLocator(
167
157
  value: unknown,
168
158
  rewrites: readonly RunDirectoryPathRewrite[],
169
159
  ): void {
170
- if (!isPlainObject(value)) return;
160
+ if (!isRecord(value)) return;
171
161
  rewriteRunDirectoryPathFieldsAgainstRewrites(
172
162
  value,
173
163
  SOURCE_RUN_LOCATOR_FIELDS,
@@ -180,7 +170,7 @@ function rewriteSummonsMaterials(
180
170
  value: unknown,
181
171
  rewrites: readonly RunDirectoryPathRewrite[],
182
172
  ): void {
183
- if (!isPlainObject(value)) return;
173
+ if (!isRecord(value)) return;
184
174
  rewriteRunDirectoryPathFieldsAgainstRewrites(
185
175
  value,
186
176
  SUMMONS_PATH_FIELDS,
@@ -204,12 +194,12 @@ export function rewriteAdmittedRoleRunPage(
204
194
  rewriteSourceRunLocator(page.sourceRun, rewrites);
205
195
  if (Array.isArray(page.attachments)) {
206
196
  for (const attachment of page.attachments) {
207
- if (isPlainObject(attachment)) {
197
+ if (isRecord(attachment)) {
208
198
  rewriteRunDirectoryPathFieldsAgainstRewrites(attachment, ["frozenPath"], rewrites);
209
199
  }
210
200
  }
211
201
  }
212
- if (isPlainObject(page.principal)) {
202
+ if (isRecord(page.principal)) {
213
203
  rewriteRunDirectoryPathFieldsAgainstRewrites(
214
204
  page.principal,
215
205
  ["sessionDirectory", "sessionFile"],
@@ -224,7 +214,7 @@ async function rewriteOfficerPointerFile(
224
214
  ): Promise<void> {
225
215
  if (!existsSync(path)) return;
226
216
  const page = JSON.parse(await readFile(path, "utf8")) as unknown;
227
- if (!isPlainObject(page)) return;
217
+ if (!isRecord(page)) return;
228
218
  // Only the typed direct-officer pointer shape — never arbitrary .pointer.json.
229
219
  if (page.kind !== "direct-officer-run-pointer") return;
230
220
  const before = JSON.stringify(page);
@@ -267,7 +257,7 @@ async function rewriteSitianRecordsJsonl(
267
257
  out.push(line);
268
258
  continue;
269
259
  }
270
- if (!isPlainObject(parsed)) {
260
+ if (!isRecord(parsed)) {
271
261
  out.push(line);
272
262
  continue;
273
263
  }
@@ -325,7 +315,7 @@ async function rewriteSessionTranscriptBindings(
325
315
  out.push(line);
326
316
  continue;
327
317
  }
328
- if (!isPlainObject(parsed)) {
318
+ if (!isRecord(parsed)) {
329
319
  out.push(line);
330
320
  continue;
331
321
  }
@@ -341,8 +331,8 @@ async function rewriteSessionTranscriptBindings(
341
331
  } else if (
342
332
  parsed.type === "custom" &&
343
333
  parsed.customType === AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE &&
344
- isPlainObject(parsed.data) &&
345
- isPlainObject(parsed.data.parent)
334
+ isRecord(parsed.data) &&
335
+ isRecord(parsed.data.parent)
346
336
  ) {
347
337
  const parent = parsed.data.parent;
348
338
  const before = parent.sessionFile;
@@ -454,7 +444,7 @@ export async function rewriteRoleRunDurablePages(input: {
454
444
  RUN_STATE_PAGE_FIELDS,
455
445
  rewrites,
456
446
  );
457
- if (isPlainObject(page.principal)) {
447
+ if (isRecord(page.principal)) {
458
448
  rewriteRunDirectoryPathFieldsAgainstRewrites(
459
449
  page.principal,
460
450
  ["sessionDirectory", "sessionFile"],
@@ -462,7 +452,7 @@ export async function rewriteRoleRunDurablePages(input: {
462
452
  );
463
453
  }
464
454
  // Open court summons: source-run locators + frozen attachment path pointers.
465
- if (isPlainObject(page.currentCourt)) {
455
+ if (isRecord(page.currentCourt)) {
466
456
  rewriteSummonsMaterials(page.currentCourt.summons, rewrites);
467
457
  }
468
458
  if (JSON.stringify(page) !== before) {
@@ -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";
@@ -188,7 +189,7 @@ function decodeReviewerAdmittedInputs(getFlag: (name: string) => unknown): Revie
188
189
  parsed = JSON.parse(rawAuthorityRefs);
189
190
  } catch (error) {
190
191
  throw new Error(
191
- `Reviewer authority refs transport error: JSON decode failed: ${error instanceof Error ? error.message : String(error)}`,
192
+ `Reviewer authority refs transport error: JSON decode failed: ${errorText(error)}`,
192
193
  );
193
194
  }
194
195
  if (!Array.isArray(parsed) || parsed.some((ref) => typeof ref !== "string")) {
@@ -297,6 +298,8 @@ export type { GatekeeperResult, GatekeeperSubject, SubmissionGateNonPassResult,
297
298
  import { ParentQueueReaskError, unreadableDiscriminatorNotice } from "./submission-errors.ts";
298
299
  import { REVIEW_QUEUE_STATUSES } from "./review-submission.ts";
299
300
 
301
+ import { isRecord, errorText, isEnoent } from "./unknown-value.ts";
302
+
300
303
  export {
301
304
  DOCTOR_EVIDENCE_TOOL_NAME,
302
305
  DOCTOR_OUTPUT_TOOL_NAME,
@@ -507,7 +510,7 @@ export function publicNavigatorSettlement(role: string, phase: NavigatorPhase, e
507
510
  return { kind: "role_infrastructure_failure", role, phase };
508
511
  }
509
512
  // accepted/human — project role/phase status; classifier already rejected infra/contradiction.
510
- const details = typeof event.details === "object" && event.details !== null && !Array.isArray(event.details)
513
+ const details = isRecord(event.details)
511
514
  ? event.details as Record<string, unknown>
512
515
  : {};
513
516
  const status = typeof details.status === "string"
@@ -545,18 +548,6 @@ export async function projectClosedSubmissionLifecycle(
545
548
  }
546
549
  }
547
550
 
548
- /**
549
- * Optional pre-accept hook on the shared filed-officer envelope (ADR 0075).
550
- * May return a details projection (envelope-owned machine facts recorded next to
551
- * the submitted parameters); undefined keeps the parameters as submitted.
552
- */
553
- type FiledOfficerBeforeAccept = (input: {
554
- readonly toolCallId: string;
555
- readonly parameters: unknown;
556
- readonly signal: AbortSignal | undefined;
557
- readonly ctx: HostContext;
558
- }) => Promise<unknown>;
559
-
560
551
  /**
561
552
  * Shared registration envelope for filed officers (ADR 0018 / #572):
562
553
  * activate, tool register, before_agent_start prompt, inventory check.
@@ -567,9 +558,9 @@ function createFiledOfficerRuntime(
567
558
  roleHost: RoleHost,
568
559
  spec: {
569
560
  role: PackagedRole;
570
- tool: { name: string; label: string; description: string; promptSnippet: string; parameters: unknown };
561
+ tool: RoleSubmissionDeclaration;
571
562
  soulTag: string;
572
- beforeAccept?: FiledOfficerBeforeAccept;
563
+ beforeAccept?: FiledSubmissionBeforeAccept;
573
564
  },
574
565
  dependencies: { loadSoul(): Promise<string> },
575
566
  ) {
@@ -582,26 +573,9 @@ function createFiledOfficerRuntime(
582
573
  soul = loaded;
583
574
  if (!registered) {
584
575
  registered = true;
585
- roleHost.registerTool({
586
- name: spec.tool.name,
587
- label: spec.tool.label,
588
- description: spec.tool.description,
589
- promptSnippet: spec.tool.promptSnippet,
590
- parameters: spec.tool.parameters as never,
591
- async execute(toolCallId, parameters, signal, _onUpdate, ctx): Promise<HostToolResult<unknown>> {
592
- if (soul === undefined) throw new Error(`${spec.role} 职分未装载`);
593
- const projected =
594
- spec.beforeAccept === undefined
595
- ? undefined
596
- : await spec.beforeAccept({ toolCallId, parameters, signal, ctx });
597
- // Accept-as-is + terminate only. Shape is not an admission gate
598
- // (第 0 条 / ADR 0055); sole-final barrier is ledger-owned (#575).
599
- return {
600
- content: [],
601
- details: projected === undefined ? parameters : projected,
602
- terminate: true as const,
603
- };
604
- },
576
+ registerFiledSubmissionTool(roleHost, spec.tool, {
577
+ readyError: () => (soul === undefined ? `${spec.role} 职分未装载` : undefined),
578
+ ...(spec.beforeAccept === undefined ? {} : { beforeAccept: spec.beforeAccept }),
605
579
  });
606
580
  roleHost.on("before_agent_start", (event) => {
607
581
  if (soul === undefined) throw new Error(`${spec.role} 职分未装载`);
@@ -710,7 +684,7 @@ export function createNavigatorRoleRuntime(
710
684
  dependencies.recordRoutePlaybookReadFailure?.(undefined);
711
685
  if (content.trim() !== "") parts.push(content);
712
686
  } catch (error) {
713
- const message = error instanceof Error ? error.message : String(error);
687
+ const message = errorText(error);
714
688
  if (message.trim() !== "") {
715
689
  dependencies.recordRoutePlaybookReadFailure?.(message);
716
690
  parts.push(message);
@@ -854,7 +828,7 @@ export function createDiaristRoleRuntime(
854
828
  soulTag: "diarist",
855
829
  beforeAccept: async ({ parameters, ctx }) => {
856
830
  const submitted =
857
- parameters !== null && typeof parameters === "object" && !Array.isArray(parameters)
831
+ isRecord(parameters)
858
832
  ? (parameters as Record<string, unknown>)
859
833
  : undefined;
860
834
  // Routing belongs to the public seam after this tool call has returned.
@@ -1073,7 +1047,7 @@ export function createRoleRuntimeExtension(
1073
1047
  ): void => {
1074
1048
  if (attendance === undefined) return;
1075
1049
  const recordDisposeFailure = (error: unknown): void => {
1076
- const diagnostic = error instanceof Error ? error.message : String(error);
1050
+ const diagnostic = errorText(error);
1077
1051
  try {
1078
1052
  sitianReport({
1079
1053
  level: "event",
@@ -1087,7 +1061,7 @@ export function createRoleRuntimeExtension(
1087
1061
  try {
1088
1062
  envelopeHost.appendEntry?.("ak-navigator-dispose-failure", {
1089
1063
  diagnostic,
1090
- recordFailure: recordError instanceof Error ? recordError.message : String(recordError),
1064
+ recordFailure: errorText(recordError),
1091
1065
  });
1092
1066
  } catch {
1093
1067
  // Failure already diagnosed; recording must not create unhandled rejection (#959).
@@ -1655,15 +1629,7 @@ export function createRoleRuntimeExtension(
1655
1629
  try {
1656
1630
  await recordTypedProviderHttpStatus(runDir, { httpStatus: status, provider });
1657
1631
  } catch (error) {
1658
- if (
1659
- status >= 200 &&
1660
- status < 300 &&
1661
- error instanceof Error &&
1662
- "code" in error &&
1663
- (error as { code?: unknown }).code === "ENOENT"
1664
- ) {
1665
- return;
1666
- }
1632
+ if (status >= 200 && status < 300 && isEnoent(error)) return;
1667
1633
  failInfrastructure(error, ctx);
1668
1634
  }
1669
1635
  };
@@ -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
+ }
@@ -11,6 +11,8 @@ import { basename, dirname, join } from "node:path";
11
11
 
12
12
  import { roleRunArtifactsDirectory } from "./role-run-placement.ts";
13
13
 
14
+ import { isRecord, errorText, isMissingPathError } from "./unknown-value.ts";
15
+
14
16
  export const RUN_TERMINAL_ARTIFACT_FILES = [
15
17
  "report.json",
16
18
  "error.json",
@@ -49,22 +51,6 @@ export type RunTerminalArtifactRead =
49
51
  readonly reason: string;
50
52
  };
51
53
 
52
- function isMissingPathError(error: unknown): boolean {
53
- return (
54
- error instanceof Error
55
- && "code" in error
56
- && (error.code === "ENOENT" || error.code === "ENOTDIR")
57
- );
58
- }
59
-
60
- function errorText(error: unknown): string {
61
- return error instanceof Error ? error.message : String(error);
62
- }
63
-
64
- function isRecord(value: unknown): value is Record<string, unknown> {
65
- return typeof value === "object" && value !== null && !Array.isArray(value);
66
- }
67
-
68
54
  /**
69
55
  * Minimum producer-owned face shared by settlement terminal artifacts
70
56
  * (report / error / audit-incomplete). Consumer-driven: enough to identify a
@@ -8,18 +8,12 @@
8
8
  import { readFile } from "node:fs/promises";
9
9
  import { join } from "node:path";
10
10
 
11
+ import { isEnoent, isRecord } from "./unknown-value.ts";
12
+
11
13
  /** Sole on-disk page for worktree-basename ticket derivation (#865). */
12
14
  export const MIGRATION_TICKET_DERIVATION_PAGE =
13
15
  "migration-ticket-derivation.json" as const;
14
16
 
15
- function isEnoent(error: unknown): boolean {
16
- return (
17
- error instanceof Error &&
18
- "code" in error &&
19
- (error as NodeJS.ErrnoException).code === "ENOENT"
20
- );
21
- }
22
-
23
17
  /** Sole safe-positive ticket invariant (bind / admission / placement / readers). */
24
18
  export function isSafePositiveTicketNumber(value: unknown): value is number {
25
19
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
@@ -69,10 +63,10 @@ async function readJsonObject(
69
63
  ): Promise<Record<string, unknown> | undefined> {
70
64
  try {
71
65
  const raw: unknown = JSON.parse(await readFile(path, "utf8"));
72
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
66
+ if (!isRecord(raw)) {
73
67
  return undefined;
74
68
  }
75
- return raw as Record<string, unknown>;
69
+ return raw;
76
70
  } catch (error) {
77
71
  if (isEnoent(error)) return undefined;
78
72
  throw error;
@@ -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