@akagilnc/pi-workflow-roles 0.1.5365 → 0.1.5375

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 (170) hide show
  1. package/dist/acp-host/production-host.js +5585 -5655
  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/compliance-transport.js +2 -1
  16. package/dist/countersign-contracts.js +2 -1
  17. package/dist/diarist-contracts.js +3 -2
  18. package/dist/doctor-contracts.js +1 -3
  19. package/dist/doctor-evidence.js +7 -8
  20. package/dist/dossier-resolution.js +1 -3
  21. package/dist/engine-detour-usage.js +1 -3
  22. package/dist/gatekeeper-role.js +2 -9
  23. package/dist/gleaner-left-contracts.js +2 -1
  24. package/dist/headless-host/description.js +14 -17
  25. package/dist/headless-host/production-host.js +1665 -1737
  26. package/dist/host-session-record.js +3 -2
  27. package/dist/inspector-contracts.js +2 -1
  28. package/dist/ledger-session-read.js +1 -3
  29. package/dist/merger-contracts.js +9 -9
  30. package/dist/migrate-book-topology.js +348 -314
  31. package/dist/navigator-attendance.js +9 -11
  32. package/dist/navigator-invocation-identity.js +3 -2
  33. package/dist/navigator-public-session.js +3 -5
  34. package/dist/navigator-session-contracts.js +8 -10
  35. package/dist/navigator-work-base.js +5 -8
  36. package/dist/notary-contracts.js +3 -5
  37. package/dist/package-contracts/auditor-output.js +1 -3
  38. package/dist/package-contracts/fixer-packet.js +3 -6
  39. package/dist/package-contracts/gatekeeper-output.js +1 -3
  40. package/dist/package-contracts/navigator-output.js +8 -8
  41. package/dist/package-contracts/terminating-infrastructure.js +5 -9
  42. package/dist/package-contracts/terminating-tools.js +4 -3
  43. package/dist/public-cli/analyst-run.js +3 -2
  44. package/dist/public-cli/auto-resume.js +4 -9
  45. package/dist/public-cli/cli.js +66 -64
  46. package/dist/public-cli/config.js +12 -11
  47. package/dist/public-cli/countersign-run.js +1 -3
  48. package/dist/public-cli/host-providers.js +7 -14
  49. package/dist/public-cli/instruction-seat-run.js +69 -43
  50. package/dist/public-cli/invocation.js +141 -136
  51. package/dist/public-cli/main.js +1919 -1968
  52. package/dist/public-cli/post-admission.js +89 -89
  53. package/dist/public-cli/role-seat-runtime.js +29 -0
  54. package/dist/public-cli/run-lifecycle.js +20 -32
  55. package/dist/public-cli/settlement.js +10 -26
  56. package/dist/public-cli/turn-request.js +14 -0
  57. package/dist/public-role-summons.js +23 -35
  58. package/dist/receipt-delivery-policy.js +1 -3
  59. package/dist/role-run-placement.js +7 -5
  60. package/dist/role-run-relocation.js +12 -19
  61. package/dist/run-terminal-artifacts.js +1 -11
  62. package/dist/run-ticket-number.js +2 -4
  63. package/dist/session-assistant-usage.js +2 -1
  64. package/dist/session-dialogue.js +1 -14
  65. package/dist/sitian-reader.js +2 -4
  66. package/dist/submission-correctable-error.js +2 -1
  67. package/dist/submission-errors.js +2 -1
  68. package/dist/submission-gate.js +2 -1
  69. package/dist/submission-ledger.js +4 -5
  70. package/dist/ticket-provenance-contracts.js +1 -3
  71. package/dist/ticket-provenance.js +2 -1
  72. package/dist/typed-provider-http.js +4 -7
  73. package/dist/unknown-value.js +28 -0
  74. package/dist/user-dialogue-stdin.js +2 -1
  75. package/package.json +1 -1
  76. package/src/acp-host/role-turn-host.ts +4 -2
  77. package/src/activation-ledger-git.ts +3 -2
  78. package/src/activation-ledger-topology.ts +4 -12
  79. package/src/analyst-entry.ts +3 -11
  80. package/src/analyst-gate-cycles-read.ts +3 -15
  81. package/src/analyst-index.ts +3 -13
  82. package/src/analyst-ledger.ts +2 -16
  83. package/src/analyst-metric-families/acceptance-success-rework.ts +2 -4
  84. package/src/analyst-metric-families/round-timeline.ts +2 -4
  85. package/src/analyst-page.ts +2 -2
  86. package/src/archivist-record-topology.ts +2 -2
  87. package/src/audit-escalation.ts +2 -1
  88. package/src/auditor-soul.ts +3 -1
  89. package/src/book-topology-migration-placement.ts +8 -21
  90. package/src/book-topology-migrators-t11.ts +16 -15
  91. package/src/book-topology-mixed-volume-migrators.ts +3 -6
  92. package/src/book-topology-record-class-migrators.ts +7 -10
  93. package/src/book-topology-runs-migrator.ts +4 -3
  94. package/src/collector-config.ts +7 -9
  95. package/src/compliance-transport.ts +2 -1
  96. package/src/countersign-contracts.ts +2 -1
  97. package/src/diarist-contracts.ts +3 -2
  98. package/src/doctor-contracts.ts +3 -1
  99. package/src/doctor-evidence.ts +8 -7
  100. package/src/doctor-role.ts +4 -2
  101. package/src/dossier-resolution.ts +2 -4
  102. package/src/engine-detour-tool.ts +0 -2
  103. package/src/engine-detour-usage.ts +2 -4
  104. package/src/external-host-turn-loop.ts +3 -1
  105. package/src/factory-board.ts +1 -8
  106. package/src/gatekeeper-role.ts +2 -10
  107. package/src/gleaner-left-contracts.ts +3 -1
  108. package/src/headless-host/description.ts +15 -18
  109. package/src/headless-host/role-turn-host.ts +10 -9
  110. package/src/host-session-record.ts +4 -2
  111. package/src/inspector-contracts.ts +2 -1
  112. package/src/ledger-session-read.ts +2 -5
  113. package/src/merger-contracts.ts +10 -9
  114. package/src/navigator-attendance.ts +10 -12
  115. package/src/navigator-invocation-identity.ts +3 -2
  116. package/src/navigator-public-session.ts +4 -6
  117. package/src/navigator-session-contracts.ts +8 -11
  118. package/src/navigator-work-base.ts +5 -8
  119. package/src/navigator-work-context.ts +2 -1
  120. package/src/notary-contracts.ts +4 -6
  121. package/src/oauth-keepalive.ts +5 -18
  122. package/src/package-contracts/auditor-output.ts +2 -4
  123. package/src/package-contracts/fixer-packet.ts +4 -6
  124. package/src/package-contracts/gatekeeper-output.ts +2 -4
  125. package/src/package-contracts/judge-output.ts +0 -1
  126. package/src/package-contracts/navigator-output.ts +8 -8
  127. package/src/package-contracts/terminating-infrastructure.ts +6 -20
  128. package/src/package-contracts/terminating-tools.ts +8 -12
  129. package/src/pi/adapter.ts +0 -1
  130. package/src/pi/role-turn-host.ts +0 -1
  131. package/src/public-cli/analyst-run.ts +4 -2
  132. package/src/public-cli/auto-resume.ts +5 -12
  133. package/src/public-cli/cli.ts +77 -83
  134. package/src/public-cli/config.ts +16 -27
  135. package/src/public-cli/countersign-run.ts +2 -4
  136. package/src/public-cli/host-providers.ts +10 -27
  137. package/src/public-cli/instruction-seat-run.ts +87 -58
  138. package/src/public-cli/invocation.ts +159 -172
  139. package/src/public-cli/post-admission.ts +115 -103
  140. package/src/public-cli/role-seat-runtime.ts +72 -0
  141. package/src/public-cli/run-lifecycle.ts +27 -67
  142. package/src/public-cli/settlement.ts +11 -33
  143. package/src/public-cli/terminal.ts +2 -17
  144. package/src/public-cli/turn-request.ts +26 -0
  145. package/src/public-role-summons.ts +24 -48
  146. package/src/receipt-delivery-policy.ts +1 -4
  147. package/src/reviewer-role.ts +0 -1
  148. package/src/role-envelope.ts +5 -3
  149. package/src/role-run-placement.ts +9 -10
  150. package/src/role-run-relocation.ts +13 -23
  151. package/src/role-runtime.ts +9 -15
  152. package/src/run-terminal-artifacts.ts +2 -16
  153. package/src/run-ticket-number.ts +4 -10
  154. package/src/session-assistant-usage.ts +3 -1
  155. package/src/session-dialogue.ts +1 -4
  156. package/src/session-identity.ts +0 -1
  157. package/src/sitian-reader.ts +2 -4
  158. package/src/submission-correctable-error.ts +3 -1
  159. package/src/submission-errors.ts +2 -1
  160. package/src/submission-gate.ts +3 -1
  161. package/src/submission-ledger.ts +5 -6
  162. package/src/ticket-provenance-contracts.ts +1 -4
  163. package/src/ticket-provenance.ts +3 -1
  164. package/src/ticket-snapshot.ts +2 -4
  165. package/src/ticket-trajectory.ts +1 -8
  166. package/src/typed-provider-http.ts +5 -15
  167. package/src/unknown-value.ts +34 -0
  168. package/src/user-dialogue-stdin.ts +3 -1
  169. package/src/worker-role.ts +2 -5
  170. package/src/worker-submission-gates.ts +5 -7
@@ -3,7 +3,7 @@ import { existsSync } from "node:fs";
3
3
  import { realpath } from "node:fs/promises";
4
4
  import { join } from "node:path";
5
5
  import { promisify } from "node:util";
6
- import { pickEngineAxis } from "./package-resources/engine-material.js";
6
+ import { isRecord, errorText } from "./unknown-value.js";
7
7
  const AK_ROLE_PACKAGE_ROOT_ENV = "AK_ROLE_PACKAGE_ROOT";
8
8
  const execFileAsync = promisify(execFile);
9
9
  function createCapturingIo() {
@@ -69,12 +69,6 @@ async function resolveSummonHome(options) {
69
69
  if (fromCwd !== void 0 && fromCwd.length > 0) return fromCwd;
70
70
  return packageMachineHome();
71
71
  }
72
- function projectSeatEngine(seat) {
73
- return pickEngineAxis(seat);
74
- }
75
- function projectSeatHost(seat) {
76
- return seat.host === void 0 ? {} : { host: seat.host };
77
- }
78
72
  function hostSelectionFailureFromUnknown(error) {
79
73
  if (typeof error !== "object" || error === null) return void 0;
80
74
  if (error.name !== "HostSelectionError") return void 0;
@@ -85,44 +79,36 @@ async function createSummonEnv(options, afterHost) {
85
79
  const [
86
80
  { piDurablePrincipalAuthority },
87
81
  { appendPiSessionCustomEntry },
88
- { resolveRoleTurnHost },
89
- { missingResolvedSeatModelMessage, resolvedSeatWithModel }
82
+ { openRoleSeatRuntime },
83
+ { missingResolvedSeatModelMessage }
90
84
  ] = await Promise.all([
91
85
  import("./pi/durable-principal.js"),
92
86
  import("./pi/role-turn-host.js"),
93
- import("./public-cli/role-turn-host-resolution.js"),
87
+ import("./public-cli/role-seat-runtime.js"),
94
88
  import("./public-cli/config.js")
95
89
  ]);
96
90
  const principalAuthority = options.principalAuthority ?? piDurablePrincipalAuthority;
97
- const roleTurnHost = resolveRoleTurnHost(
98
- {
91
+ const opened = openRoleSeatRuntime({
92
+ resolution: {
99
93
  packageRoot: options.packageRoot,
100
94
  ...options.roleTurnHost === void 0 ? {} : { roleTurnHost: options.roleTurnHost },
101
95
  ...options.hostAdapters === void 0 ? {} : { hostAdapters: options.hostAdapters },
102
96
  ...options.extraPiArgs === void 0 || options.extraPiArgs.length === 0 ? {} : { extraPiArgs: options.extraPiArgs },
103
97
  ...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }
104
98
  },
105
- { role: options.role, seat: options.seat, principalAuthority }
106
- );
107
- afterHost?.();
108
- const seatWithModel = resolvedSeatWithModel(options.seat);
109
- if (seatWithModel === void 0) {
99
+ principalAuthority,
100
+ role: options.role,
101
+ seat: options.seat,
102
+ home: options.home,
103
+ ...afterHost === void 0 ? {} : { afterHost },
104
+ ...options.hostFacingModel === void 0 ? {} : { hostFacingModel: options.hostFacingModel }
105
+ });
106
+ if (!opened.ok) {
110
107
  return {
111
108
  ok: false,
112
109
  missingModelMessage: missingResolvedSeatModelMessage(options.role)
113
110
  };
114
111
  }
115
- const hostName = seatWithModel.host ?? "pi";
116
- let hostFacingSelection = options.hostFacingModel;
117
- if (hostFacingSelection === void 0) {
118
- const { loadHostProvidersTable, projectHostFacingProvider } = await import("./public-cli/host-providers.js");
119
- hostFacingSelection = projectHostFacingProvider(
120
- seatWithModel.selection,
121
- hostName,
122
- loadHostProvidersTable(options.home),
123
- options.home
124
- );
125
- }
126
112
  return {
127
113
  ok: true,
128
114
  env: {
@@ -131,12 +117,13 @@ async function createSummonEnv(options, afterHost) {
131
117
  agentDir: options.agentDir,
132
118
  sessionAppender: appendPiSessionCustomEntry,
133
119
  packageRoot: options.packageRoot,
134
- roleTurnHost,
120
+ roleTurnHost: opened.roleTurnHost,
135
121
  cwd: options.cwd,
136
122
  credentials: options.credentials,
137
- ...hostFacingSelection === void 0 ? {} : { model: hostFacingSelection },
138
- ...projectSeatEngine(seatWithModel),
139
- ...projectSeatHost(seatWithModel),
123
+ ...opened.model === void 0 ? {} : { model: opened.model },
124
+ ...opened.engine === void 0 ? {} : { engine: opened.engine },
125
+ ...opened.engineModel === void 0 ? {} : { engineModel: opened.engineModel },
126
+ host: opened.host,
140
127
  ...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }
141
128
  }
142
129
  };
@@ -144,13 +131,14 @@ async function createSummonEnv(options, afterHost) {
144
131
  async function summonPublicRole(options) {
145
132
  const packageRoot = resolveSummonsPackageRoot(options.packageRoot);
146
133
  const home = await resolveSummonHome(options);
147
- const agentDir = options.agentDir ?? process.env.PI_CODING_AGENT_DIR ?? join(home, ".pi", "agent");
148
134
  const {
149
135
  loadCredentialProviders,
150
136
  loadPublicCliConfig,
151
137
  resolveEffectiveSeat,
138
+ resolvePublicAgentDir,
152
139
  validatePublicCliConfigAxes
153
140
  } = await import("./public-cli/config.js");
141
+ const agentDir = resolvePublicAgentDir(options.agentDir, home);
154
142
  const credentials = options.credentials ?? await loadCredentialProviders(agentDir);
155
143
  const config = await loadPublicCliConfig(home);
156
144
  validatePublicCliConfigAxes(config, packageRoot);
@@ -304,7 +292,7 @@ async function summonParallelReviewerLenses(options) {
304
292
  if (error instanceof AggregateError) {
305
293
  return [error.message, ...error.errors.map(describeFailure)].join("\n");
306
294
  }
307
- return error instanceof Error ? error.message : String(error);
295
+ return errorText(error);
308
296
  };
309
297
  const failedResult = (error) => ({
310
298
  exitCode: 1,
@@ -485,7 +473,7 @@ async function summonGateOfficer(options) {
485
473
  const correlationId = parentRunId;
486
474
  const { isSafePositiveTicketNumber, readBoardTicketNumber } = await import("./run-ticket-number.js");
487
475
  const parentTicket = await readBoardTicketNumber(options.sourceRunDirectory);
488
- const submittedTicket = options.submission !== null && typeof options.submission === "object" && !Array.isArray(options.submission) ? options.submission.ticketNumber : void 0;
476
+ const submittedTicket = isRecord(options.submission) ? options.submission.ticketNumber : void 0;
489
477
  const courtTicket = isSafePositiveTicketNumber(submittedTicket) ? submittedTicket : parentTicket;
490
478
  const instruction = options.reask ?? gateReviewInstruction ?? "";
491
479
  return summonPublicRole({
@@ -1,8 +1,6 @@
1
+ import { isRecord } from "./unknown-value.js";
1
2
  const RECEIPT_DELIVERY_TURN_LIMIT = 2;
2
3
  const NO_RECEIPT_LIFECYCLE_ENTRY_TYPE = "ak-no-receipt-lifecycle";
3
- function isRecord(value) {
4
- return typeof value === "object" && value !== null && !Array.isArray(value);
5
- }
6
4
  function parseNoReceiptLifecycleFacts(input) {
7
5
  if (!isRecord(input) || typeof input.terminalToolCalled !== "boolean" || input.deliveryTurns !== RECEIPT_DELIVERY_TURN_LIMIT || input.sessionCompletion !== "settled-without-accepted-receipt" || input.acceptedReceipt !== false || typeof input.runPointer !== "string" || input.runPointer.trim() === "" || typeof input.attemptPointer !== "string" || input.attemptPointer.trim() === "" || !Array.isArray(input.rejectedReceipts) || !input.rejectedReceipts.every((item) => isRecord(item) && typeof item.reason === "string")) {
8
6
  throw new TypeError("malformed no-receipt lifecycle facts");
@@ -5,9 +5,7 @@ import {
5
5
  ensureRealDirectoryTree
6
6
  } from "./activation-ledger-topology.js";
7
7
  import { requireSafePositiveTicketNumber } from "./run-ticket-number.js";
8
- function isMissingPathError(error) {
9
- return error instanceof Error && "code" in error && error.code === "ENOENT";
10
- }
8
+ import { isEnoent } from "./unknown-value.js";
11
9
  async function listBookRunDirectories(bookDir) {
12
10
  const out = [];
13
11
  const seen = /* @__PURE__ */ new Set();
@@ -16,7 +14,7 @@ async function listBookRunDirectories(bookDir) {
16
14
  try {
17
15
  entries = await readdir(runsDir, { withFileTypes: true });
18
16
  } catch (error) {
19
- if (isMissingPathError(error)) return;
17
+ if (isEnoent(error)) return;
20
18
  throw error;
21
19
  }
22
20
  for (const entry of entries) {
@@ -32,7 +30,7 @@ async function listBookRunDirectories(bookDir) {
32
30
  try {
33
31
  subjects = await readdir(bookDir, { withFileTypes: true });
34
32
  } catch (error) {
35
- if (isMissingPathError(error)) return out.sort();
33
+ if (isEnoent(error)) return out.sort();
36
34
  throw error;
37
35
  }
38
36
  for (const subject of subjects) {
@@ -64,6 +62,9 @@ function roleRunPlacement(ledgerHome, input) {
64
62
  function roleRunArtifactsDirectory(runDirectory) {
65
63
  return join(runDirectory, "artifacts");
66
64
  }
65
+ function isUnboundRunDirectory(runDirectory) {
66
+ return runDirectory.replaceAll("\\", "/").includes("/unbound/runs/");
67
+ }
67
68
  function ensureRoleRunDirectory(ledgerHome, directory) {
68
69
  return ensureRealDirectoryTree(ledgerHome, directory);
69
70
  }
@@ -78,6 +79,7 @@ function ensureRoleRunPlacement(ledgerHome, placement) {
78
79
  export {
79
80
  ensureRoleRunDirectory,
80
81
  ensureRoleRunPlacement,
82
+ isUnboundRunDirectory,
81
83
  listBookRunDirectories,
82
84
  roleRunArtifactsDirectory,
83
85
  roleRunPlacement
@@ -10,6 +10,7 @@ import { existsSync } from "node:fs";
10
10
  import { readdir, readFile, writeFile } from "node:fs/promises";
11
11
  import { join, sep } from "node:path";
12
12
  import { AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE } from "./compliance-transport.js";
13
+ import { isRecord, isEnoent } from "./unknown-value.js";
13
14
  const ADMITTED_PAGE_FIELDS = [
14
15
  "runDirectory",
15
16
  "admittedRequestPath",
@@ -39,11 +40,6 @@ const OFFICER_POINTER_FIELDS = ["sessionFile", "runDirectory"];
39
40
  const SITIAN_RECORD_FIELDS = ["sessionParent"];
40
41
  const SESSION_HEADER_FIELDS = ["parentSession"];
41
42
  const BINDING_PARENT_FIELDS = ["sessionFile"];
42
- function isEnoent(error) {
43
- return (error instanceof Error &&
44
- "code" in error &&
45
- error.code === "ENOENT");
46
- }
47
43
  /** Rewrite one path value that points at or under oldRunDirectory. */
48
44
  export function rewriteRunDirectoryPathValue(value, oldRunDirectory, newRunDirectory) {
49
45
  if (typeof value !== "string")
@@ -111,18 +107,15 @@ function collectRewrites(input) {
111
107
  }
112
108
  return out;
113
109
  }
114
- function isPlainObject(value) {
115
- return value !== null && typeof value === "object" && !Array.isArray(value);
116
- }
117
110
  /** Typed notary/source-run locator: only runDirectory is a path. */
118
111
  function rewriteSourceRunLocator(value, rewrites) {
119
- if (!isPlainObject(value))
112
+ if (!isRecord(value))
120
113
  return;
121
114
  rewriteRunDirectoryPathFieldsAgainstRewrites(value, SOURCE_RUN_LOCATOR_FIELDS, rewrites);
122
115
  }
123
116
  /** Same-ticket summons materials: typed source-run + frozen attachment pointers. */
124
117
  function rewriteSummonsMaterials(value, rewrites) {
125
- if (!isPlainObject(value))
118
+ if (!isRecord(value))
126
119
  return;
127
120
  rewriteRunDirectoryPathFieldsAgainstRewrites(value, SUMMONS_PATH_FIELDS, rewrites);
128
121
  rewriteSourceRunLocator(value.sourceRun, rewrites);
@@ -137,12 +130,12 @@ export function rewriteAdmittedRoleRunPage(page, rewrites) {
137
130
  rewriteSourceRunLocator(page.sourceRun, rewrites);
138
131
  if (Array.isArray(page.attachments)) {
139
132
  for (const attachment of page.attachments) {
140
- if (isPlainObject(attachment)) {
133
+ if (isRecord(attachment)) {
141
134
  rewriteRunDirectoryPathFieldsAgainstRewrites(attachment, ["frozenPath"], rewrites);
142
135
  }
143
136
  }
144
137
  }
145
- if (isPlainObject(page.principal)) {
138
+ if (isRecord(page.principal)) {
146
139
  rewriteRunDirectoryPathFieldsAgainstRewrites(page.principal, ["sessionDirectory", "sessionFile"], rewrites);
147
140
  }
148
141
  }
@@ -150,7 +143,7 @@ async function rewriteOfficerPointerFile(path, rewrites) {
150
143
  if (!existsSync(path))
151
144
  return;
152
145
  const page = JSON.parse(await readFile(path, "utf8"));
153
- if (!isPlainObject(page))
146
+ if (!isRecord(page))
154
147
  return;
155
148
  // Only the typed direct-officer pointer shape — never arbitrary .pointer.json.
156
149
  if (page.kind !== "direct-officer-run-pointer")
@@ -190,7 +183,7 @@ async function rewriteSitianRecordsJsonl(path, rewrites) {
190
183
  out.push(line);
191
184
  continue;
192
185
  }
193
- if (!isPlainObject(parsed)) {
186
+ if (!isRecord(parsed)) {
194
187
  out.push(line);
195
188
  continue;
196
189
  }
@@ -241,7 +234,7 @@ async function rewriteSessionTranscriptBindings(path, rewrites) {
241
234
  out.push(line);
242
235
  continue;
243
236
  }
244
- if (!isPlainObject(parsed)) {
237
+ if (!isRecord(parsed)) {
245
238
  out.push(line);
246
239
  continue;
247
240
  }
@@ -254,8 +247,8 @@ async function rewriteSessionTranscriptBindings(path, rewrites) {
254
247
  }
255
248
  else if (parsed.type === "custom" &&
256
249
  parsed.customType === AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE &&
257
- isPlainObject(parsed.data) &&
258
- isPlainObject(parsed.data.parent)) {
250
+ isRecord(parsed.data) &&
251
+ isRecord(parsed.data.parent)) {
259
252
  const parent = parsed.data.parent;
260
253
  const before = parent.sessionFile;
261
254
  rewriteRunDirectoryPathFieldsAgainstRewrites(parent, BINDING_PARENT_FIELDS, rewrites);
@@ -343,11 +336,11 @@ export async function rewriteRoleRunDurablePages(input) {
343
336
  const page = JSON.parse(await readFile(statePath, "utf8"));
344
337
  const before = JSON.stringify(page);
345
338
  rewriteRunDirectoryPathFieldsAgainstRewrites(page, RUN_STATE_PAGE_FIELDS, rewrites);
346
- if (isPlainObject(page.principal)) {
339
+ if (isRecord(page.principal)) {
347
340
  rewriteRunDirectoryPathFieldsAgainstRewrites(page.principal, ["sessionDirectory", "sessionFile"], rewrites);
348
341
  }
349
342
  // Open court summons: source-run locators + frozen attachment path pointers.
350
- if (isPlainObject(page.currentCourt)) {
343
+ if (isRecord(page.currentCourt)) {
351
344
  rewriteSummonsMaterials(page.currentCourt.summons, rewrites);
352
345
  }
353
346
  if (JSON.stringify(page) !== before) {
@@ -9,6 +9,7 @@
9
9
  import { readdir, readFile } from "node:fs/promises";
10
10
  import { basename, dirname, join } from "node:path";
11
11
  import { roleRunArtifactsDirectory } from "./role-run-placement.js";
12
+ import { isRecord, errorText, isMissingPathError } from "./unknown-value.js";
12
13
  export const RUN_TERMINAL_ARTIFACT_FILES = [
13
14
  "report.json",
14
15
  "error.json",
@@ -26,17 +27,6 @@ export const RUN_TERMINAL_ERROR_FALLBACK_RELATIVE_PATHS = [
26
27
  ];
27
28
  /** Unique open-ended failure names: error.<uuid>.json (publisher stem + uuid). */
28
29
  const UNIQUE_ERROR_FALLBACK_NAME = /^error\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.json$/i;
29
- function isMissingPathError(error) {
30
- return (error instanceof Error
31
- && "code" in error
32
- && (error.code === "ENOENT" || error.code === "ENOTDIR"));
33
- }
34
- function errorText(error) {
35
- return error instanceof Error ? error.message : String(error);
36
- }
37
- function isRecord(value) {
38
- return typeof value === "object" && value !== null && !Array.isArray(value);
39
- }
40
30
  /**
41
31
  * Minimum producer-owned face shared by settlement terminal artifacts
42
32
  * (report / error / audit-incomplete). Consumer-driven: enough to identify a
@@ -1,9 +1,7 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
+ import { isEnoent, isRecord } from "./unknown-value.js";
3
4
  const MIGRATION_TICKET_DERIVATION_PAGE = "migration-ticket-derivation.json";
4
- function isEnoent(error) {
5
- return error instanceof Error && "code" in error && error.code === "ENOENT";
6
- }
7
5
  function isSafePositiveTicketNumber(value) {
8
6
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
9
7
  }
@@ -30,7 +28,7 @@ function ticketFromRecord(record) {
30
28
  async function readJsonObject(path) {
31
29
  try {
32
30
  const raw = JSON.parse(await readFile(path, "utf8"));
33
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
31
+ if (!isRecord(raw)) {
34
32
  return void 0;
35
33
  }
36
34
  return raw;
@@ -1,4 +1,5 @@
1
1
  import { join } from "node:path";
2
+ import { errorText } from "./unknown-value.js";
2
3
  export async function readAssistantUsageFromSessionFile(sessionFile) {
3
4
  const { readFile } = await import("node:fs/promises");
4
5
  let text;
@@ -19,7 +20,7 @@ export async function readAssistantUsageFromSessionFile(sessionFile) {
19
20
  .map((line) => JSON.parse(line));
20
21
  }
21
22
  catch (error) {
22
- throw new Error(`session usage parse failed (${sessionFile}): ${error instanceof Error ? error.message : String(error)}`, { cause: error });
23
+ throw new Error(`session usage parse failed (${sessionFile}): ${errorText(error)}`, { cause: error });
23
24
  }
24
25
  let total = 0;
25
26
  let input = 0;
@@ -1,17 +1,4 @@
1
- /**
2
- * 对话事实适配(ADR 0075 `dialogue-only` / `no-tool-output` / `speaker-required`)。
3
- *
4
- * 「对话」=人类输入内容 + 助手回话内容,由各宿主的**结构化会话事件**适配而来。
5
- * 排除按来源、不按正文:说话人自己写下的文字一律原样保留,其中引用了 ADR 段落
6
- * 或工具结果也不删(#901 Out of Scope「不做正文过滤」)。
7
- *
8
- * 不入录的是工具结果载荷、附件记录与注入块——它们是独立的机器事件,不是谁说的话。
9
- *
10
- * 适配规则按宿主并列,不硬套某一宿主的类型名;一行产出 0..n 条对话事实。
11
- */
12
- function isRecord(value) {
13
- return typeof value === "object" && value !== null && !Array.isArray(value);
14
- }
1
+ import { isRecord } from "./unknown-value.js";
15
2
  /**
16
3
  * 原生 id:行级或消息体级主键的单一实现(bound 解析与落盘共用)。
17
4
  * Pi/CC:行上 `uuid`/`id`;Codex:`response_item.payload.id`(或 `message.id`)。
@@ -1,8 +1,6 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { readFile } from "node:fs/promises";
3
- function isRecord(value) {
4
- return typeof value === "object" && value !== null && !Array.isArray(value);
5
- }
3
+ import { isRecord, errorText } from "./unknown-value.js";
6
4
  async function readSitianRecords(recordFile) {
7
5
  if (!existsSync(recordFile)) {
8
6
  return { records: [], diagnostics: [] };
@@ -32,7 +30,7 @@ async function readSitianRecords(recordFile) {
32
30
  kind: "malformed",
33
31
  line: index + 1,
34
32
  raw: line,
35
- error: error instanceof Error ? error.message : String(error)
33
+ error: errorText(error)
36
34
  });
37
35
  }
38
36
  }
@@ -1,4 +1,5 @@
1
1
  import { GatekeeperDecisionError, ParentQueueReaskError, WorkerCommitReminderError, WorkerPrefixReminderError, WorkerUnfinishedReasonReminderError, } from "./submission-errors.js";
2
+ import { errorText } from "./unknown-value.js";
2
3
  /** Shared, unforgeable identity for submission errors that the same session may correct. */
3
4
  const correctableSubmissionErrorBrand = Symbol("ak-roles.correctable-submission-error");
4
5
  export class CorrectableSubmissionError extends Error {
@@ -25,7 +26,7 @@ export function isCorrectableExecuteError(error) {
25
26
  * Consumed by the ACP envelope tool catch; host adapters wrap transport shape only.
26
27
  */
27
28
  export function projectCorrectableExecuteRejection(error) {
28
- const diagnostic = error instanceof Error ? error.message : String(error);
29
+ const diagnostic = errorText(error);
29
30
  if (error instanceof GatekeeperDecisionError) {
30
31
  return { diagnostic, details: { ...error.result } };
31
32
  }
@@ -1,4 +1,5 @@
1
1
  import { readableGateItem } from "./readable-gate-item.js";
2
+ import { isRecord } from "./unknown-value.js";
2
3
  /**
3
4
  * Tool-result text the parent model sees (#753 / #750 evidence).
4
5
  * continue | escalate → officer receipt verbatim (JSON when structured).
@@ -61,7 +62,7 @@ export function unreadableDiscriminatorNotice(field, received) {
61
62
  }
62
63
  /** The received discriminator only — never the rest of the receipt. */
63
64
  export function receivedDiscriminator(receipt, field) {
64
- if (receipt === null || typeof receipt !== "object" || Array.isArray(receipt))
65
+ if (!isRecord(receipt))
65
66
  return undefined;
66
67
  if (!Object.hasOwn(receipt, field))
67
68
  return undefined;
@@ -10,7 +10,7 @@ export function latestQueueStatus(terminal) {
10
10
  if (outcome.kind !== "accepted")
11
11
  return undefined;
12
12
  const latest = outcome.payloads?.[outcome.payloads.length - 1];
13
- if (latest !== null && typeof latest === "object" && !Array.isArray(latest)) {
13
+ if (isRecord(latest)) {
14
14
  const status = latest.status;
15
15
  if (typeof status === "string" && status.trim() !== "")
16
16
  return status;
@@ -29,6 +29,7 @@ export function latestQueuePayload(terminal) {
29
29
  return payloads.length === 0 ? undefined : payloads[payloads.length - 1];
30
30
  }
31
31
  import { sessionFileFromPublicSummon } from "./session-assistant-usage.js";
32
+ import { isRecord } from "./unknown-value.js";
32
33
  /**
33
34
  * Shared-envelope default officer summon (ADR 0018).
34
35
  * Role modules must not own this drive seam — only project results.
@@ -7,6 +7,7 @@ import { findRunDirectoryById } from "./public-cli/run-lifecycle.js";
7
7
  import { isCorrectableExecuteError } from "./submission-correctable-error.js";
8
8
  import { failOnInfrastructureFailureDeclaration, infrastructureFailureDiagnostic } from "./package-contracts/terminating-infrastructure.js";
9
9
  import { REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "./review-submission.js";
10
+ import { isRecord, errorText } from "./unknown-value.js";
10
11
  /**
11
12
  * Admitted run identity for the ledger subject.
12
13
  * Prefer AK_ROLE_RUN_DIR via sole runDirectory→runId parser (public-CLI correlation);
@@ -464,9 +465,7 @@ export function createSubmissionLedgerHost(host, outputTools, failInfrastructure
464
465
  // host-failure seam. Tool identity comes from the registry map.
465
466
  const reviewEscalate = tool.name === REVIEW_SUBMISSION_OUTPUT_TOOL_NAME
466
467
  && infrastructureFailureDiagnostic(params) !== undefined
467
- && params !== null
468
- && typeof params === "object"
469
- && !Array.isArray(params)
468
+ && isRecord(params)
470
469
  && params.status === "escalate";
471
470
  if (!reviewEscalate) {
472
471
  failOnInfrastructureFailureDeclaration(params, {
@@ -485,7 +484,7 @@ export function createSubmissionLedgerHost(host, outputTools, failInfrastructure
485
484
  toolCallId,
486
485
  outcome: "correctable-rejection",
487
486
  code: "typed-bounce",
488
- diagnostic: error instanceof Error ? error.message : String(error),
487
+ diagnostic: errorText(error),
489
488
  role,
490
489
  accepted: params,
491
490
  });
@@ -497,7 +496,7 @@ export function createSubmissionLedgerHost(host, outputTools, failInfrastructure
497
496
  attemptId,
498
497
  toolCallId,
499
498
  outcome: "infrastructure",
500
- diagnostic: error instanceof Error ? error.message : String(error),
499
+ diagnostic: errorText(error),
501
500
  role,
502
501
  accepted: params,
503
502
  });
@@ -1,3 +1,4 @@
1
+ import { isRecord } from "./unknown-value.js";
1
2
  /**
2
3
  * 起居录(ticket-provenance)typed 形状 —— ADR 0075 / ADR 0081 / #1090。
3
4
  *
@@ -7,9 +8,6 @@
7
8
  */
8
9
  /** Sitian kind for per-ticket court diary volumes. */
9
10
  export const TICKET_PROVENANCE_KIND = "ticket-provenance";
10
- function isRecord(value) {
11
- return typeof value === "object" && value !== null && !Array.isArray(value);
12
- }
13
11
  function positiveInteger(value) {
14
12
  if (typeof value === "number" && Number.isSafeInteger(value) && value >= 1)
15
13
  return value;
@@ -11,6 +11,7 @@ import { isSafePositiveTicketNumber } from "./run-ticket-number.js";
11
11
  import { adaptSessionDialogue, nativeEventId } from "./session-dialogue.js";
12
12
  import { appendSitianRecordBlock, appendSitianRecord, resolveSitianRecordPath, } from "./sitian-facade.js";
13
13
  import { TICKET_PROVENANCE_KIND, } from "./ticket-provenance-contracts.js";
14
+ import { errorText } from "./unknown-value.js";
14
15
  /**
15
16
  * Typed input failure for diarist bounds/session path (reask, not infrastructure).
16
17
  * Accept hook discriminates with instanceof — never Error.message prefixes.
@@ -199,7 +200,7 @@ async function projectSessionRanges(input) {
199
200
  throw error;
200
201
  const code = errnoCode(error);
201
202
  if (code === "ENOENT" || code === "ENOTDIR" || code === "EISDIR") {
202
- const detail = error instanceof Error ? error.message : String(error);
203
+ const detail = errorText(error);
203
204
  throw new TicketProvenanceInputError(`session unreadable: ${input.session.path} (${detail})`, { cause: error });
204
205
  }
205
206
  throw error;
@@ -1,5 +1,6 @@
1
1
  import { readFile, unlink, writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
+ import { isEnoent, isRecord } from "./unknown-value.js";
3
4
  const TYPED_HTTP_FILE = "typed-provider-http.json";
4
5
  function typedProviderHttpPath(runDirectory) {
5
6
  return join(runDirectory, TYPED_HTTP_FILE);
@@ -8,9 +9,7 @@ async function clearTypedProviderHttpObservation(runDirectory) {
8
9
  try {
9
10
  await unlink(typedProviderHttpPath(runDirectory));
10
11
  } catch (error) {
11
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
12
- return;
13
- }
12
+ if (isEnoent(error)) return;
14
13
  throw error;
15
14
  }
16
15
  }
@@ -35,13 +34,11 @@ async function readLatestTypedProviderHttpObservation(runDirectory) {
35
34
  try {
36
35
  text = await readFile(typedProviderHttpPath(runDirectory), "utf8");
37
36
  } catch (error) {
38
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
39
- return void 0;
40
- }
37
+ if (isEnoent(error)) return void 0;
41
38
  throw error;
42
39
  }
43
40
  const raw = JSON.parse(text);
44
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
41
+ if (!isRecord(raw)) {
45
42
  throw new Error("typed provider HTTP observation must be a JSON object");
46
43
  }
47
44
  const record = raw;
@@ -0,0 +1,28 @@
1
+ import { Guard } from "typebox/guard";
2
+ /**
3
+ * Sole readers for a plain object, a Node errno, and an Error message.
4
+ * Callers choose which codes count; ENOTDIR is not ENOENT.
5
+ * Plain objects are TypeBox `Guard.IsObjectNotArray`.
6
+ */
7
+ export function isRecord(value) {
8
+ return Guard.IsObjectNotArray(value);
9
+ }
10
+ export function errnoCode(error) {
11
+ return error !== null && typeof error === "object" && "code" in error
12
+ && typeof error.code === "string"
13
+ ? error.code
14
+ : undefined;
15
+ }
16
+ /** `Error.message`, otherwise `String(value)`. */
17
+ export function errorText(error) {
18
+ return error instanceof Error ? error.message : String(error);
19
+ }
20
+ /** True only for ENOENT on an Error. ENOTDIR and every other code stay distinct. */
21
+ export function isEnoent(error) {
22
+ return error instanceof Error && errnoCode(error) === "ENOENT";
23
+ }
24
+ /** ENOENT or ENOTDIR on an Error. */
25
+ export function isMissingPathError(error) {
26
+ const code = errnoCode(error);
27
+ return error instanceof Error && (code === "ENOENT" || code === "ENOTDIR");
28
+ }
@@ -3,12 +3,13 @@
3
3
  * Pi's piped-stdin reader trims the wrapper; the body field keeps original bytes.
4
4
  * No size cap and no path/pointer substitute — the whole dialogue rides the envelope.
5
5
  */
6
+ import { isRecord } from "./unknown-value.js";
6
7
  export const USER_DIALOGUE_STDIN_KIND = "ak-user-dialogue";
7
8
  export function encodeUserDialogueStdin(body) {
8
9
  return JSON.stringify({ kind: USER_DIALOGUE_STDIN_KIND, body });
9
10
  }
10
11
  function isUserDialogueEnvelope(value) {
11
- if (value === null || typeof value !== "object" || Array.isArray(value))
12
+ if (!isRecord(value))
12
13
  return false;
13
14
  const record = value;
14
15
  return record.kind === USER_DIALOGUE_STDIN_KIND && typeof record.body === "string";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.5365",
3
+ "version": "0.1.5375",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -27,6 +27,8 @@ import {
27
27
  } from "../prepared-role-turn.ts";
28
28
  import { acpModelId, type AcpHostDescription } from "./description.ts";
29
29
 
30
+ import { isRecord } from "../unknown-value.ts";
31
+
30
32
  /**
31
33
  * #959: collect free-form agent text from ACP session/update stream.
32
34
  * Used only when the navigator seat spoke prose without calling the output tool.
@@ -42,7 +44,7 @@ function acpAgentTextChunk(params: Readonly<Record<string, unknown>>): string |
42
44
  let textFallback: unknown;
43
45
 
44
46
  const nested = params.update;
45
- if (typeof nested === "object" && nested !== null && !Array.isArray(nested)) {
47
+ if (isRecord(nested)) {
46
48
  const record = nested as Record<string, unknown>;
47
49
  kind = record.sessionUpdate;
48
50
  content = record.content;
@@ -55,7 +57,7 @@ function acpAgentTextChunk(params: Readonly<Record<string, unknown>>): string |
55
57
 
56
58
  if (kind !== "agent_message_chunk" && kind !== "agent_message") return undefined;
57
59
  if (typeof content === "string" && content.length > 0) return content;
58
- if (typeof content === "object" && content !== null && !Array.isArray(content)) {
60
+ if (isRecord(content)) {
59
61
  const record = content as Record<string, unknown>;
60
62
  if (typeof record.text === "string" && record.text.length > 0) return record.text;
61
63
  }
@@ -1,6 +1,8 @@
1
1
  import { execFileSync } from "node:child_process";
2
2
  import { basename, dirname, isAbsolute, resolve } from "node:path";
3
3
 
4
+ import { errnoCode } from "./unknown-value.ts";
5
+
4
6
  /** Git discovery env vars that must not influence book-key resolution from cwd. */
5
7
  const GIT_DISCOVERY_ENV_KEYS = [
6
8
  "GIT_DIR",
@@ -60,8 +62,7 @@ export class ActivationGitRepositoryRequiredError extends Error {
60
62
 
61
63
  /** Spawn/OS failure identity for the git child — not a repository-status result. */
62
64
  function isGitSpawnInfrastructureError(error: unknown): boolean {
63
- if (error === null || typeof error !== "object" || !("code" in error)) return false;
64
- const code = (error as { code: unknown }).code;
65
+ const code = errnoCode(error);
65
66
  return code === "ENOENT" || code === "EACCES" || code === "EPERM";
66
67
  }
67
68