@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,5 +1,6 @@
1
1
  import { execFileSync } from "node:child_process";
2
2
  import { basename, dirname, isAbsolute, resolve } from "node:path";
3
+ import { errnoCode } from "./unknown-value.js";
3
4
  const GIT_DISCOVERY_ENV_KEYS = [
4
5
  "GIT_DIR",
5
6
  "GIT_COMMON_DIR",
@@ -31,8 +32,7 @@ class ActivationGitRepositoryRequiredError extends Error {
31
32
  }
32
33
  }
33
34
  function isGitSpawnInfrastructureError(error) {
34
- if (error === null || typeof error !== "object" || !("code" in error)) return false;
35
- const code = error.code;
35
+ const code = errnoCode(error);
36
36
  return code === "ENOENT" || code === "EACCES" || code === "EPERM";
37
37
  }
38
38
  function gitChildExitedNonzero(error) {
@@ -6,6 +6,7 @@ import {
6
6
  } from "node:fs";
7
7
  import { userInfo } from "node:os";
8
8
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
9
+ import { errnoCode, errorText } from "./unknown-value.js";
9
10
  class ActivationLedgerError extends Error {
10
11
  code = "AK_ACTIVATION_LEDGER";
11
12
  constructor(message, options) {
@@ -83,13 +84,6 @@ function physicalPathIdentity(path) {
83
84
  function physicallyContainedIn(root, candidate) {
84
85
  return pathContainedIn(physicalPathIdentity(root), physicalPathIdentity(candidate));
85
86
  }
86
- function errnoCode(error) {
87
- return error !== null && typeof error === "object" && "code" in error && typeof error.code === "string" ? error.code : void 0;
88
- }
89
- function errorText(error) {
90
- if (!(error instanceof Error)) return String(error);
91
- return error.message;
92
- }
93
87
  function assertPhysicalLedgerRoot(absoluteRoot) {
94
88
  let st;
95
89
  try {
@@ -19,6 +19,7 @@ import { scanAnalystIssueRuns, } from "./analyst-ledger.js";
19
19
  import { mergeAnalystLibraryIndexRows, rowFromIssueMetricsPage, } from "./analyst-index.js";
20
20
  import { buildAnalystModelGroupsPage, } from "./analyst-model-groups.js";
21
21
  import { assertAnalystChangedLinesInput, buildAnalystIssueMetricsPage, analystIssuePagePath, writeAnalystIssueMetricsPage, } from "./analyst-page.js";
22
+ import { isMissingPathError } from "./unknown-value.js";
22
23
  /** #338 compute-if-missing failure — issue identity + real cause (CLI → ControlledFailure). */
23
24
  export class AnalystIssueComputeError extends Error {
24
25
  code = "analyst-issue-compute-failed";
@@ -44,16 +45,11 @@ export class AnalystIssueComputeError extends Error {
44
45
  }
45
46
  }
46
47
  }
47
- function isMissingPathError(error) {
48
- return (error instanceof Error
49
- && "code" in error
50
- && (error.code === "ENOENT" || error.code === "ENOTDIR"));
51
- }
52
48
  /**
53
49
  * Sole sweep-mode input contract (#298/#329/#337).
54
50
  * Schema is the single definition; TS types are derived (no parallel hand shape).
55
51
  * projectRoot = string (not nonempty); changedLines optional finite non-negative;
56
- * 0 remains typed 空缺; no extra keys.
52
+ * 0 remains typed 空缺. Extra keys are not a reject reason.
57
53
  */
58
54
  export const analystSweepModeInputSchema = Type.Object({
59
55
  mode: Type.Literal("sweep"),
@@ -61,8 +57,8 @@ export const analystSweepModeInputSchema = Type.Object({
61
57
  projectRoot: Type.String(),
62
58
  /** 排除后改动行数 — omit or 0 → typed 空缺; finite ≥ 0 only. */
63
59
  changedLines: Type.Optional(Type.Number({ minimum: 0, maximum: Number.MAX_VALUE })),
64
- }, { additionalProperties: false })),
65
- }, { additionalProperties: false });
60
+ })),
61
+ });
66
62
  /**
67
63
  * #338 retrieval primitive (sync): use persisted page when present; otherwise
68
64
  * await the sole issue compute kernel (runAnalystIssueMode) which writes via the
@@ -29,6 +29,7 @@ import { readdir } from "node:fs/promises";
29
29
  import { join } from "node:path";
30
30
  import { AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE, } from "./compliance-transport.js";
31
31
  import { extractSessionTimestampSpan, intervalRowsAroundAnchor, readLedgerSessionJsonl, } from "./ledger-session-read.js";
32
+ import { isRecord, errorText, isEnoent } from "./unknown-value.js";
32
33
  const DISPATCH_TOOLS = new Set(["ak_menxia_output", "ak_gatekeeper_output"]);
33
34
  /** Officer terminating tool → current English officer identity. */
34
35
  const OFFICER_TOOL_TO_FACE = {
@@ -44,9 +45,6 @@ const OFFICER_ARG_ALIASES = {
44
45
  fubaolang: "notary",
45
46
  notary: "notary",
46
47
  };
47
- function isRecord(value) {
48
- return typeof value === "object" && value !== null && !Array.isArray(value);
49
- }
50
48
  function isParentAttemptBindingRow(row) {
51
49
  return row.type === "custom" && row.customType === AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE;
52
50
  }
@@ -54,12 +52,6 @@ function isParentAttemptBindingRow(row) {
54
52
  function intervalRowsForGateCall(rows, callRowIndex) {
55
53
  return intervalRowsAroundAnchor(rows, callRowIndex, isParentAttemptBindingRow).rows;
56
54
  }
57
- /** Only true absence (ENOENT). ENOTDIR is damaged topology — must stay loud. */
58
- function isMissingDirectoryError(error) {
59
- return (error instanceof Error
60
- && "code" in error
61
- && error.code === "ENOENT");
62
- }
63
55
  function normalizeOfficerArg(raw) {
64
56
  if (typeof raw !== "string")
65
57
  return undefined;
@@ -325,7 +317,7 @@ async function resolveOfficerSessionFromPointerFile(pointerPath) {
325
317
  raw = JSON.parse(await readFile(pointerPath, "utf8"));
326
318
  }
327
319
  catch (error) {
328
- throw new Error(`direct officer run pointer unreadable in ${pointerPath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
320
+ throw new Error(`direct officer run pointer unreadable in ${pointerPath}: ${errorText(error)}`, { cause: error });
329
321
  }
330
322
  if (!isRecord(raw) || raw.kind !== "direct-officer-run-pointer" || raw.version !== 1) {
331
323
  throw new Error(`direct officer run pointer has unknown shape in ${pointerPath}`);
@@ -367,7 +359,7 @@ export async function readAnalystGateCyclesFromAuditorRoles(auditorRolesDirector
367
359
  .sort();
368
360
  }
369
361
  catch (error) {
370
- if (isMissingDirectoryError(error))
362
+ if (isEnoent(error))
371
363
  continue;
372
364
  throw error;
373
365
  }
@@ -20,17 +20,14 @@
20
20
  */
21
21
  import { open, readFile, unlink } from "node:fs/promises";
22
22
  import { dirname, join } from "node:path";
23
+ import { setTimeout as sleep } from "node:timers/promises";
23
24
  import { writeFileAtomically } from "./atomic-write.js";
24
25
  import { assertLedgerFileInsideHome, ensureRealDirectoryTree, physicalPathIdentity, } from "./activation-ledger-topology.js";
25
26
  import { resolveAnalystBookKey } from "./analyst-book-key.js";
27
+ import { isMissingPathError } from "./unknown-value.js";
26
28
  const LIBRARY_INDEX_LOCK_NAME = ".library-index.lock";
27
29
  const LIBRARY_INDEX_LOCK_TIMEOUT_MS = 30_000;
28
30
  const LIBRARY_INDEX_LOCK_RETRY_MS = 15;
29
- function sleep(ms) {
30
- return new Promise((resolve) => {
31
- setTimeout(resolve, ms);
32
- });
33
- }
34
31
  /**
35
32
  * Exclusive create lock for the library-index read→upsert→write critical section.
36
33
  * Same-directory sibling of the index file; not a second index and not a daemon.
@@ -198,11 +195,8 @@ export async function readAnalystLibraryIndexPage(ledgerHome) {
198
195
  raw = await readFile(path, "utf8");
199
196
  }
200
197
  catch (error) {
201
- if (error instanceof Error
202
- && "code" in error
203
- && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
198
+ if (isMissingPathError(error))
204
199
  return undefined;
205
- }
206
200
  throw error;
207
201
  }
208
202
  const parsed = JSON.parse(raw);
@@ -22,17 +22,7 @@ import { readRunTicketNumber } from "./run-ticket-number.js";
22
22
  import { extractSessionModelSequence, extractSessionTimestampSpan, extractSessionToolIntervals, LedgerSessionJsonlError, readLedgerSessionJsonl, } from "./ledger-session-read.js";
23
23
  import { readRunTerminalArtifact, } from "./run-terminal-artifacts.js";
24
24
  import { readAnalystGateCyclesFromAuditorRoles, } from "./analyst-gate-cycles-read.js";
25
- function isMissingPathError(error) {
26
- return (error instanceof Error
27
- && "code" in error
28
- && (error.code === "ENOENT" || error.code === "ENOTDIR"));
29
- }
30
- function errorText(error) {
31
- return error instanceof Error ? error.message : String(error);
32
- }
33
- function isRecord(value) {
34
- return typeof value === "object" && value !== null && !Array.isArray(value);
35
- }
25
+ import { isRecord, errorText, isMissingPathError } from "./unknown-value.js";
36
26
  /** Existing public-cli run-state live phases — not a new state machine. */
37
27
  const LIVE_RUN_STATES = new Set(["admitted", "running", "resumable"]);
38
28
  /**
@@ -12,6 +12,7 @@
12
12
  import { compareAnalystLegs, sumAnalystRunWallMs } from "../analyst-ledger.js";
13
13
  import { medianNumber } from "../analyst-median.js";
14
14
  import { packagedAnalystTerminal } from "../packaged-role-registry.js";
15
+ import { isRecord } from "../unknown-value.js";
15
16
  const WORKER_ROLES = new Set(["coder", "fixer"]);
16
17
  /** Lawful acceptance vocabulary by role (PRD 受理终态映射). */
17
18
  const ACCEPTED_STATUS = {
@@ -32,9 +33,6 @@ const SUCCESS_STATUS = {
32
33
  doctor: new Set(["completed"]),
33
34
  merger: new Set(["completed"]),
34
35
  };
35
- function isRecord(value) {
36
- return typeof value === "object" && value !== null && !Array.isArray(value);
37
- }
38
36
  /** Locate collector groups array: receipt.groups or top-level groups. */
39
37
  function findCollectorGroups(body) {
40
38
  if (Array.isArray(body.groups))
@@ -1,7 +1,5 @@
1
1
  import { extractStatus } from "./acceptance-success-rework.js";
2
- function isRecord(value) {
3
- return typeof value === "object" && value !== null && !Array.isArray(value);
4
- }
2
+ import { isRecord } from "../unknown-value.js";
5
3
  function readClassCount(body) {
6
4
  if (!isRecord(body.outcome))
7
5
  return undefined;
@@ -6,8 +6,8 @@
6
6
  * top-level sections via directory discovery — B/C waves add family files
7
7
  * without forking the page writer or editing a shared registry list.
8
8
  */
9
- import { createHash } from "node:crypto";
10
9
  import { dirname, join } from "node:path";
10
+ import { sha256Hex } from "./sha256.js";
11
11
  import { writeFileAtomically } from "./atomic-write.js";
12
12
  import { assertLedgerFileInsideHome, ensureRealDirectoryTree, physicalPathIdentity, } from "./activation-ledger-topology.js";
13
13
  import { compareAnalystLegs, sumAnalystRunWallMs } from "./analyst-ledger.js";
@@ -21,7 +21,7 @@ export function analystIssuePageKey(address) {
21
21
  else if (address.scopeRootIdentity !== undefined) {
22
22
  parts.push("root", physicalPathIdentity(address.scopeRootIdentity));
23
23
  }
24
- return createHash("sha256").update(parts.join("\0")).digest("hex").slice(0, 32);
24
+ return sha256Hex(parts.join("\0")).slice(0, 32);
25
25
  }
26
26
  export function analystIssuePagePath(ledgerHome, address) {
27
27
  return join(ledgerHome, "analyst", "issues", `${analystIssuePageKey(address)}.json`);
@@ -1,5 +1,5 @@
1
- import { createHash } from "node:crypto";
2
1
  import { join } from "node:path";
2
+ import { sha256Hex } from "./sha256.js";
3
3
  import { resolveBookKeyFromGit } from "./activation-ledger-git.js";
4
4
  import {
5
5
  activationBookDirectory,
@@ -16,7 +16,7 @@ function resolveNavigatorWorkSubjectPlacement(input) {
16
16
  }
17
17
  }
18
18
  ledgerHome ??= resolveActivationLedgerHome(input.home);
19
- const digest = createHash("sha256").update(input.subject).digest("hex").slice(0, 32);
19
+ const digest = sha256Hex(input.subject).slice(0, 32);
20
20
  return {
21
21
  ledgerHome,
22
22
  sessionDir: join(
@@ -1,6 +1,7 @@
1
+ import { isRecord } from "./unknown-value.js";
1
2
  /** Historical audit-escalation discriminator retained for reading pre-#1057 run artifacts. */
2
3
  export const AUDIT_ESCALATION_KIND = "audit_escalation";
3
4
  export function isAuditEscalationResult(value) {
4
- return typeof value === "object" && value !== null && !Array.isArray(value)
5
+ return isRecord(value)
5
6
  && value.kind === AUDIT_ESCALATION_KIND;
6
7
  }
@@ -1,5 +1,6 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { basename, join } from "node:path";
3
+ import { isRecord } from "./unknown-value.js";
3
4
  import { joinPackageMaterials, readPackageMaterial, } from "./session-opening-materials.js";
4
5
  /**
5
6
  * Active auditor seats.
@@ -100,7 +101,7 @@ export async function readAuditorResumeBinding(runDirectory) {
100
101
  return undefined;
101
102
  throw error;
102
103
  }
103
- if (raw === null || typeof raw !== "object" || Array.isArray(raw))
104
+ if (!isRecord(raw))
104
105
  return undefined;
105
106
  const record = raw;
106
107
  if (typeof record.sourceRunPath !== "string" || record.sourceRunPath.trim() === "")
@@ -1,5 +1,6 @@
1
- import { createHash } from "node:crypto";
2
1
  import { readFile } from "node:fs/promises";
2
+ import { sha256Hex } from "./sha256.js";
3
+ import { isRecord } from "./unknown-value.js";
3
4
  export const COLLECTOR_OWNER_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/;
4
5
  export const COLLECTOR_REPO_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,98}[A-Za-z0-9])?$/;
5
6
  function fail(message, cause) {
@@ -38,15 +39,12 @@ export function parseCollectorPrNumber(raw) {
38
39
  fail("Collector pull request number must be a positive safe integer");
39
40
  return value;
40
41
  }
41
- function record(value) {
42
- return typeof value === "object" && value !== null && !Array.isArray(value);
43
- }
44
42
  function canonicalManifest(requests) {
45
43
  return `${JSON.stringify({ requests: requests.map((request) => ({ id: request.id, body: request.requestBody })) })}\n`;
46
44
  }
47
45
  export function emptyCollectorManifest() {
48
46
  const canonicalJson = canonicalManifest([]);
49
- return { requests: [], canonicalJson, digest: createHash("sha256").update(canonicalJson).digest("hex") };
47
+ return { requests: [], canonicalJson, digest: sha256Hex(canonicalJson) };
50
48
  }
51
49
  /** Optional request configuration. It names requests, never expected observers. */
52
50
  export async function loadCollectorManifest(path) {
@@ -64,7 +62,7 @@ export async function loadCollectorManifest(path) {
64
62
  catch (error) {
65
63
  fail("Collector request manifest must be UTF-8 JSON", error);
66
64
  }
67
- if (!record(parsed))
65
+ if (!isRecord(parsed))
68
66
  fail("Collector request manifest must be an object");
69
67
  const rawRequests = parsed.requests ?? [];
70
68
  if (!Array.isArray(rawRequests))
@@ -72,7 +70,7 @@ export async function loadCollectorManifest(path) {
72
70
  const requests = [];
73
71
  const ids = new Set();
74
72
  for (const [index, item] of rawRequests.entries()) {
75
- if (!record(item) || typeof item.id !== "string" || item.id.length === 0 || typeof item.body !== "string" || item.body.trim() === "")
73
+ if (!isRecord(item) || typeof item.id !== "string" || item.id.length === 0 || typeof item.body !== "string" || item.body.trim() === "")
76
74
  fail(`Collector request manifest requests[${index}] is invalid`);
77
75
  if (ids.has(item.id))
78
76
  fail(`Collector request manifest has duplicate request id "${item.id}"`);
@@ -80,5 +78,5 @@ export async function loadCollectorManifest(path) {
80
78
  requests.push({ id: item.id, requestBody: item.body });
81
79
  }
82
80
  const canonicalJson = canonicalManifest(requests);
83
- return { requests, canonicalJson, digest: createHash("sha256").update(canonicalJson).digest("hex"), sourcePath: path };
81
+ return { requests, canonicalJson, digest: sha256Hex(canonicalJson), sourcePath: path };
84
82
  }
@@ -0,0 +1,18 @@
1
+ import { Type } from "typebox";
2
+ import { openToolObject } from "./open-tool-schema.js";
3
+ import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.js";
4
+ /**
5
+ * #1088: LLM gathers evidence via host CLI and submits the receipt.
6
+ * Field declarations guide the model — host must not pure-shape-reject (第 0 条 / ADR 0057).
7
+ * Presence of `groups` remains the Collector terminal discriminator for settlement/analyst.
8
+ */
9
+ export const collectorOutputBaseSchema = openToolObject(Type.Object({
10
+ groups: Type.Unsafe({
11
+ description: "按机器身份归组的出席、材料与逐条 findings(LLM 自行从宿主 CLI 取证整理;代码不代收不归并)。",
12
+ }),
13
+ unfinishedReasons: Type.Unknown({
14
+ description: "未完成原因;取证受限时如实报告。",
15
+ }),
16
+ }));
17
+ /** Runtime does not enrich findings; the model submits the receipt and signals sole-final. */
18
+ export const collectorOutputArgsSchema = withTerminatingOutputDeclarations(collectorOutputBaseSchema);
@@ -3,6 +3,7 @@ import { officerConclusionReask } from "./gatekeeper-role.js";
3
3
  import { receivedDiscriminator } from "./submission-errors.js";
4
4
  import { coalesceSubmissionRows } from "./public-cli/terminal.js";
5
5
  import { readableGateItem } from "./readable-gate-item.js";
6
+ import { isRecord } from "./unknown-value.js";
6
7
  const AUDITOR_DOSSIER_PROMPT = "\u5377\u5B97\u6307\u9488\uFF1A";
7
8
  const COMPLIANCE_RESPONSE_ENTRY_TYPE = "ak_compliance_response";
8
9
  const AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE = "ak_auditor_parent_attempt_binding";
@@ -17,7 +18,7 @@ function readListField(value) {
17
18
  return Array.isArray(value) ? value : value === void 0 ? [] : [value];
18
19
  }
19
20
  function tryReadComplianceCandidate(arguments_, usage) {
20
- if (typeof arguments_ !== "object" || arguments_ === null || Array.isArray(arguments_)) {
21
+ if (!isRecord(arguments_)) {
21
22
  return void 0;
22
23
  }
23
24
  const args = arguments_;
@@ -1,7 +1,8 @@
1
1
  import { REVIEW_QUEUE_STATUSES, REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "./review-submission.js";
2
+ import { isRecord } from "./unknown-value.js";
2
3
  const COUNTERSIGN_OUTPUT_TOOL_NAME = REVIEW_SUBMISSION_OUTPUT_TOOL_NAME;
3
4
  function validateRecordedCountersignOutput(verdict) {
4
- if (verdict === null || typeof verdict !== "object" || Array.isArray(verdict)) {
5
+ if (!isRecord(verdict)) {
5
6
  throw new Error("Countersign verdict has no execution discriminator");
6
7
  }
7
8
  let status;
@@ -6,11 +6,15 @@
6
6
  *
7
7
  * #901: LLM submits bounds; the mechanical layer projects source bytes.
8
8
  */
9
+ import { Type } from "typebox";
10
+ import { openToolObject } from "./open-tool-schema.js";
11
+ import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.js";
9
12
  import { projectTicketProvenanceSessions, } from "./ticket-provenance-contracts.js";
10
13
  import { isSafePositiveTicketNumber } from "./run-ticket-number.js";
14
+ import { isRecord } from "./unknown-value.js";
11
15
  export const DIARIST_OUTPUT_TOOL_NAME = "ak_diarist_output";
12
16
  export function validateRecordedDiaristOutput(value) {
13
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
17
+ if (!isRecord(value)) {
14
18
  throw new Error("Diarist output has no execution discriminator");
15
19
  }
16
20
  let status;
@@ -42,7 +46,7 @@ export function projectDiaristTicketSessions(value) {
42
46
  return undefined;
43
47
  const perTicket = new Map();
44
48
  for (const entry of raw) {
45
- if (entry === null || typeof entry !== "object" || Array.isArray(entry))
49
+ if (!isRecord(entry))
46
50
  return undefined;
47
51
  const { ticketNumber, sessions: rawSessions } = entry;
48
52
  if (!isSafePositiveTicketNumber(ticketNumber))
@@ -54,3 +58,26 @@ export function projectDiaristTicketSessions(value) {
54
58
  }
55
59
  return Array.from(perTicket, ([ticketNumber, sessions]) => ({ ticketNumber, sessions }));
56
60
  }
61
+ /**
62
+ * 起居郎交卷形状。
63
+ * #901:交边界(sessions);正文与不可解析原字节由机械投影。
64
+ * status 的合法词(completed | escalate)写在 description,结算认这些词。
65
+ * sessions / ticketSessions 是投影所读的边界:卷路径或端点读不出时走既有 reask,不中止本轮。
66
+ * 声明只留字段名和语义说明,不留类型、嵌套、长度、必填。
67
+ */
68
+ const DIARIST_SESSIONS_DESCRIPTION = "对话边界。每卷含 path(会话卷绝对或可读路径)与 ranges(本卷本轮各区间)。每段区间含 from / to;起点与终点各写 id(原生 id)或 line(本轮行号),二选一。空列表=本轮无对话可划。端点无法指名时走 reask,不中止。";
69
+ export const diaristOutputSchema = withTerminatingOutputDeclarations(openToolObject(Type.Object({
70
+ status: Type.Unknown({
71
+ description: "completed | escalate",
72
+ }),
73
+ ticketNumber: Type.Optional(Type.Unknown({
74
+ description: "本庭对象票号(正整数)或 null/省略=真无票;下游走 typed 键;认不出用 status=escalate,不洗成无录。机械层不重判。",
75
+ })),
76
+ reason: Type.Unknown({
77
+ description: "status 为 escalate 时:认不出本庭对象的原因",
78
+ }),
79
+ sessions: Type.Optional(Type.Unknown({ description: DIARIST_SESSIONS_DESCRIPTION })),
80
+ ticketSessions: Type.Unknown({
81
+ description: `多票庭逐票对话边界;每票含 ticketNumber(本条边界所归票号,正整数)与 sessions(${DIARIST_SESSIONS_DESCRIPTION})。一次交卷各票各自分录。与单票 sessions 二选一。`,
82
+ }),
83
+ })));
@@ -2,6 +2,7 @@ import { Type } from "typebox";
2
2
  import { canonicalJson } from "./canonical-json.js";
3
3
  import { openToolObject } from "./open-tool-schema.js";
4
4
  import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.js";
5
+ import { isRecord } from "./unknown-value.js";
5
6
  const DOCTOR_EVIDENCE_TOOL_NAME = "ak_doctor_evidence";
6
7
  const DOCTOR_OUTPUT_TOOL_NAME = "ak_doctor_output";
7
8
  const DOCTOR_AUDIT_TOOL_NAME = "ak_doctor_audit_decision";
@@ -35,9 +36,6 @@ const doctorEvidenceReadSchema = Type.Object({ evidenceId: Type.String({ minLeng
35
36
  class DoctorSubmissionContractError extends Error {
36
37
  name = "DoctorSubmissionContractError";
37
38
  }
38
- function isRecord(value) {
39
- return typeof value === "object" && value !== null && !Array.isArray(value);
40
- }
41
39
  function read(value, key) {
42
40
  if (!isRecord(value)) return void 0;
43
41
  try {
@@ -2,7 +2,7 @@ import { readdir, readFile, realpath, stat } from "node:fs/promises";
2
2
  import { dirname, relative, resolve, sep } from "node:path";
3
3
  import { sha256Hex } from "./sha256.js";
4
4
  import { AcceptedDetailsContractError, acceptedFacts, isTerminatingToolName, validateAcceptedDetails } from "./package-contracts/terminating-tools.js";
5
- function record(value) { return typeof value === "object" && value !== null && !Array.isArray(value); }
5
+ import { isRecord, isMissingPathError } from "./unknown-value.js";
6
6
  async function discoverCaseFiles(root) { const found = []; async function walk(dir, depth) { for (const item of await readdir(dir, { withFileTypes: true })) {
7
7
  const path = resolve(dir, item.name);
8
8
  if (item.isDirectory()) {
@@ -15,7 +15,6 @@ function sourceList(count, sources) { return { count, sources: [...new Set(sourc
15
15
  function accumulate(metric, value, source) { metric.count += value; if (value)
16
16
  metric.sources.push(source); }
17
17
  function timestamp(row) { return typeof row.timestamp === "string" && Number.isFinite(Date.parse(row.timestamp)) ? row.timestamp : undefined; }
18
- function isMissingPathError(error) { return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"); }
19
18
  async function stableRunsIdentity(root) { let cursor = root; while (true) {
20
19
  try {
21
20
  const git = await stat(resolve(cursor, ".git"));
@@ -38,7 +37,7 @@ function deriveSession(content, id) {
38
37
  if (line.trim()) {
39
38
  try {
40
39
  const row = JSON.parse(line);
41
- if (!record(row)) {
40
+ if (!isRecord(row)) {
42
41
  degradationReasons.push(`non-object session row in ${id}`);
43
42
  break;
44
43
  }
@@ -59,21 +58,21 @@ function deriveSession(content, id) {
59
58
  let accepted, observedCommit, turns = 0, calls = 0, tokens = 0;
60
59
  const statuses = [], commits = [];
61
60
  for (const row of rows) {
62
- if (row.type === "custom" && row.customType === "ak-role-submission-closure" && record(row.data) && row.data.isError === false && typeof row.data.toolName === "string" && isTerminatingToolName(row.data.toolName))
61
+ if (row.type === "custom" && row.customType === "ak-role-submission-closure" && isRecord(row.data) && row.data.isError === false && typeof row.data.toolName === "string" && isTerminatingToolName(row.data.toolName))
63
62
  accepted = row;
64
- const message = record(row.message) ? row.message : undefined;
63
+ const message = isRecord(row.message) ? row.message : undefined;
65
64
  if (message?.role === "assistant") {
66
65
  for (const part of Array.isArray(message.content) ? message.content : [])
67
- if (record(part) && part.type === "toolCall")
66
+ if (isRecord(part) && part.type === "toolCall")
68
67
  calls++;
69
68
  if (typeof message.responseId === "string") {
70
69
  turns++;
71
- const usage = record(message.usage) ? message.usage : undefined;
70
+ const usage = isRecord(message.usage) ? message.usage : undefined;
72
71
  if (usage && typeof usage.output === "number")
73
72
  tokens += usage.output;
74
73
  }
75
74
  }
76
- if (message?.role === "toolResult" && message.isError !== true && typeof message.toolName === "string" && isTerminatingToolName(message.toolName) && record(message.details)) {
75
+ if (message?.role === "toolResult" && message.isError !== true && typeof message.toolName === "string" && isTerminatingToolName(message.toolName) && isRecord(message.details)) {
77
76
  let details;
78
77
  try {
79
78
  details = validateAcceptedDetails(message.toolName, message.details);
@@ -7,6 +7,7 @@
7
7
  import { existsSync, statSync } from "node:fs";
8
8
  import { resolve } from "node:path";
9
9
  import { runDirectoryFromHostContext } from "./host-contracts.js";
10
+ import { isRecord } from "./unknown-value.js";
10
11
  export const AUDIT_RUN_DIR_ENV = "AK_ROLE_RUN_DIR";
11
12
  export const DOCTOR_CANDIDATE_ENTRY_TYPE = "ak_doctor_audit_candidate";
12
13
  function isHostContext(value) {
@@ -45,9 +46,6 @@ export function resolveAuditDossier(source) {
45
46
  }
46
47
  return { status: "ok", runDirectory };
47
48
  }
48
- function isRecord(value) {
49
- return typeof value === "object" && value !== null && !Array.isArray(value);
50
- }
51
49
  /**
52
50
  * Doctor candidate testimony must be recorded before audit.
53
51
  */
@@ -20,6 +20,7 @@ import { readFileSync } from "node:fs";
20
20
  import { dirname, join } from "node:path";
21
21
  import { ENGINE_DETOUR_TOOL_NAME } from "./engine-detour.js";
22
22
  import { readSitianRecords, resolveSitianRecordPath, sitianReport, } from "./sitian-facade.js";
23
+ import { isRecord } from "./unknown-value.js";
23
24
  /** Sitian event kind for one detour-tool call (volume under session/). */
24
25
  export const ENGINE_DETOUR_CALL_KIND = "engine-detour-call";
25
26
  /** decisiveFacts key — name states detour-tool scope, not full engine usage. */
@@ -99,9 +100,6 @@ export function reportEngineDetourCall(input) {
99
100
  recordPointer: pointer,
100
101
  };
101
102
  }
102
- function isRecord(value) {
103
- return typeof value === "object" && value !== null && !Array.isArray(value);
104
- }
105
103
  function callFactFromSitianPayload(payload, pointer) {
106
104
  if (!isRecord(payload))
107
105
  return undefined;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * One host-neutral submission execute for every public seat.
3
+ * Records nothing itself — the ledger seam already appends the original payload.
4
+ * Ends this tool call. Does not summon an auditor and does not end the role run.
5
+ * Shape is not an admission gate (ADR 0055). Seat reminders stay on beforeAccept.
6
+ */
7
+ export function registerFiledSubmissionTool(roleHost, tool, options) {
8
+ roleHost.registerTool({
9
+ name: tool.name,
10
+ label: tool.label,
11
+ description: tool.description,
12
+ ...(tool.promptSnippet === undefined ? {} : { promptSnippet: tool.promptSnippet }),
13
+ parameters: tool.parameters,
14
+ async execute(toolCallId, parameters, signal, _onUpdate, ctx) {
15
+ const blocked = options.readyError();
16
+ if (blocked !== undefined)
17
+ throw new Error(blocked);
18
+ const projected = options.beforeAccept === undefined
19
+ ? undefined
20
+ : await options.beforeAccept({ toolCallId, parameters, signal, ctx });
21
+ return {
22
+ content: [],
23
+ details: projected === undefined ? parameters : projected,
24
+ terminate: true,
25
+ };
26
+ },
27
+ });
28
+ }
@@ -3,8 +3,10 @@ import { packagedGateStageLabel } from "./packaged-role-registry.js";
3
3
  import { GatekeeperDecisionError, receivedDiscriminator, unreadableDiscriminatorNotice } from "./submission-errors.js";
4
4
  import { INSPECTOR_OUTPUT_TOOL_NAME } from "./inspector-contracts.js";
5
5
  import { REVIEW_QUEUE_STATUSES, REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "./review-submission.js";
6
- import { GATEKEEPER_OUTPUT_TOOL_NAME, gatekeeperOutputSchema, } from "./package-contracts/gatekeeper-output.js";
6
+ import { GATEKEEPER_OUTPUT_TOOL_NAME } from "./package-contracts/gatekeeper-output.js";
7
+ import { roleSubmissionDeclaration } from "./role-submission-declarations.js";
7
8
  import { runIdFromRunDirectory } from "./run-terminal-artifacts.js";
9
+ import { isRecord } from "./unknown-value.js";
8
10
  export const INSPECTOR_OUTPUT_TOOL = INSPECTOR_OUTPUT_TOOL_NAME;
9
11
  export const NOTARY_OUTPUT_TOOL = REVIEW_SUBMISSION_OUTPUT_TOOL_NAME;
10
12
  function gateSeatLabel(stage) {
@@ -28,30 +30,16 @@ export { GatekeeperDecisionError } from "./submission-errors.js";
28
30
  * registration envelope — src/role-runtime.ts (ADR 0018). Schema authority is
29
31
  * the shared contract module (with infrastructure-failure declaration).
30
32
  */
31
- export const GATEKEEPER_TOOL_SPEC = {
32
- name: GATEKEEPER_OUTPUT_TOOL_NAME,
33
- label: "门下省决议",
34
- description: "门下省终局决议。status 为 dispatch 或 pass。",
35
- promptSnippet: "门下省决议",
36
- parameters: gatekeeperOutputSchema,
37
- };
33
+ export const GATEKEEPER_TOOL_SPEC = roleSubmissionDeclaration("gatekeeper");
38
34
  function failureReason(error) {
39
35
  if (error instanceof AggregateError)
40
36
  return error.errors.map(failureReason).join("; ");
41
37
  return error instanceof Error ? `${error.name}: ${error.message}` : String(error);
42
38
  }
43
- function isRecord(value) {
44
- return typeof value === "object" && value !== null && !Array.isArray(value);
45
- }
46
39
  /** Original decision bytes — no sentinel replacement (#836). */
47
40
  function retainedReceipt(decision) {
48
41
  return decision;
49
42
  }
50
- function readRecord(value) {
51
- if (typeof value !== "object" || value === null || Array.isArray(value))
52
- return undefined;
53
- return value;
54
- }
55
43
  /**
56
44
  * Read only the conclusion field for queueing (#753).
57
45
  * converged | continue | escalate → queue signal + raw receipt.
@@ -65,9 +53,10 @@ function reviewQueueStatus(status) {
65
53
  }
66
54
  function projectOfficerDecision(officer, decision, fallbackStatus) {
67
55
  const receipt = retainedReceipt(decision);
68
- const record = readRecord(decision);
69
- const status = (record !== undefined && typeof record.status === "string" ? record.status : undefined)
70
- ?? fallbackStatus;
56
+ const record = isRecord(decision) ? decision : undefined;
57
+ const status = record !== undefined && Object.hasOwn(record, "status")
58
+ ? receivedDiscriminator(record, "status")
59
+ : fallbackStatus;
71
60
  const queueStatus = typeof status === "string" ? reviewQueueStatus(status) : undefined;
72
61
  if (queueStatus === "converged") {
73
62
  return { status: "converged", officer, receipt };