@astrosheep/keiyaku 2.9.9 → 2.9.10

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 (230) hide show
  1. package/README.md +50 -182
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/harness/control-types.js +18 -1
  4. package/build/agents/harness/index.js +2 -0
  5. package/build/agents/harness/outcome-error.js +81 -0
  6. package/build/agents/harness/projection.js +2 -1
  7. package/build/agents/harness/router.js +5 -1
  8. package/build/agents/providers/claude-agent-sdk/adapter.js +18 -4
  9. package/build/agents/providers/claude-agent-sdk/registration.js +11 -1
  10. package/build/agents/providers/claude-agent-sdk/session.js +19 -5
  11. package/build/agents/providers/codex-app-server/adapter.js +11 -6
  12. package/build/agents/providers/codex-app-server/events.js +67 -6
  13. package/build/agents/providers/codex-app-server/registration.js +11 -1
  14. package/build/agents/providers/opencode-sdk/adapter.js +8 -1
  15. package/build/agents/providers/opencode-sdk/events.js +24 -2
  16. package/build/agents/providers/opencode-sdk/registration.js +11 -1
  17. package/build/agents/providers/pi/checkpoint.js +9 -1
  18. package/build/agents/providers/pi/events.js +2 -0
  19. package/build/agents/providers/pi/registration.js +14 -1
  20. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  21. package/build/cli/commands/akuma/akuma/meta.js +3 -3
  22. package/build/cli/commands/akuma/catalog.js +2 -2
  23. package/build/cli/commands/akuma/{view → show}/handler.js +5 -5
  24. package/build/cli/commands/akuma/{view → show}/meta.js +3 -3
  25. package/build/cli/commands/akuma.js +1 -1
  26. package/build/cli/commands/contract/amend/handler.js +5 -4
  27. package/build/cli/commands/contract/arc/handler.js +5 -4
  28. package/build/cli/commands/contract/audit/handler.js +2 -2
  29. package/build/cli/commands/contract/bind/handler.js +12 -3
  30. package/build/cli/commands/contract/bind/meta.js +45 -15
  31. package/build/cli/commands/contract/forfeit/handler.js +9 -5
  32. package/build/cli/commands/contract/log/handler.js +2 -2
  33. package/build/cli/commands/contract/petition/handler.js +34 -7
  34. package/build/cli/commands/contract/renew/handler.js +7 -7
  35. package/build/cli/commands/contract/renew/meta.js +3 -3
  36. package/build/cli/commands/metadata.js +10 -6
  37. package/build/cli/commands/projection/call/meta.js +1 -1
  38. package/build/cli/commands/projection/catalog.js +2 -0
  39. package/build/cli/commands/projection/history/handler.js +21 -0
  40. package/build/cli/commands/projection/history/meta.js +9 -0
  41. package/build/cli/commands/projection/shared.js +3 -4
  42. package/build/cli/commands/projection/status/handler.js +3 -3
  43. package/build/cli/commands/projection/wait/handler.js +20 -11
  44. package/build/cli/commands/task/add/handler.js +14 -5
  45. package/build/cli/commands/task/add/meta.js +2 -2
  46. package/build/cli/commands/task/catalog.js +4 -0
  47. package/build/cli/commands/task/done/handler.js +1 -4
  48. package/build/cli/commands/task/done/meta.js +1 -1
  49. package/build/cli/commands/task/drop/meta.js +1 -1
  50. package/build/cli/commands/task/hold/handler.js +3 -0
  51. package/build/cli/commands/task/hold/meta.js +8 -0
  52. package/build/cli/commands/task/log/handler.js +1 -1
  53. package/build/cli/commands/task/log/meta.js +2 -2
  54. package/build/cli/commands/task/resume/handler.js +3 -0
  55. package/build/cli/commands/task/resume/meta.js +8 -0
  56. package/build/cli/commands/task/shared.js +39 -14
  57. package/build/cli/commands/task/show/handler.js +1 -1
  58. package/build/cli/commands/task/show/meta.js +1 -1
  59. package/build/cli/commands/task/start/meta.js +1 -1
  60. package/build/cli/commands/task/stop/handler.js +1 -4
  61. package/build/cli/commands/task/stop/meta.js +1 -1
  62. package/build/cli/commands/task/update/handler.js +11 -3
  63. package/build/cli/commands/task/update/meta.js +3 -3
  64. package/build/cli/completion.js +3 -2
  65. package/build/cli/flags.js +51 -14
  66. package/build/cli/help.js +4 -1
  67. package/build/cli/index.js +40 -25
  68. package/build/cli/parse-flags.js +40 -7
  69. package/build/cli/parse-metadata.js +5 -2
  70. package/build/cli/parse-selectors.js +2 -3
  71. package/build/cli/parse.js +40 -6
  72. package/build/cli/projection-address.js +12 -10
  73. package/build/cli/render/amendment-warning.js +8 -0
  74. package/build/cli/render/arc.js +6 -2
  75. package/build/cli/render/audit.js +400 -81
  76. package/build/cli/render/kanshi.js +6 -1
  77. package/build/cli/render/lifecycle.js +56 -0
  78. package/build/cli/render/misc.js +24 -28
  79. package/build/cli/render/petition.js +5 -0
  80. package/build/cli/render/projection-activity.js +3 -2
  81. package/build/cli/render/projection-history.js +64 -0
  82. package/build/cli/render/shared.js +23 -9
  83. package/build/cli/render/status.js +9 -16
  84. package/build/cli/render/task.js +51 -26
  85. package/build/cli/render/terminal-failure.js +3 -1
  86. package/build/cli/render/wait.js +3 -5
  87. package/build/cli/subagent-guard.js +1 -1
  88. package/build/cli/types.js +1 -1
  89. package/build/config/env-keys.js +2 -0
  90. package/build/config/env.js +23 -0
  91. package/build/config/settings/schema.js +3 -1
  92. package/build/core/address-carrier.js +89 -0
  93. package/build/core/addressing.js +255 -196
  94. package/build/core/amend.js +115 -38
  95. package/build/core/amendment-criteria.js +17 -0
  96. package/build/core/amendment-warning.js +10 -0
  97. package/build/core/arc.js +184 -83
  98. package/build/core/audit/candidate.js +110 -9
  99. package/build/core/audit/coordinates.js +120 -65
  100. package/build/core/audit/evidence.js +19 -23
  101. package/build/core/audit/facade.js +34 -11
  102. package/build/core/audit/report.js +5 -5
  103. package/build/core/bind-reconciliation.js +390 -0
  104. package/build/core/bind-workspace.js +12 -20
  105. package/build/core/bind.js +242 -273
  106. package/build/core/call/call.js +1 -0
  107. package/build/core/call/context.js +46 -71
  108. package/build/core/call/prompt.js +6 -3
  109. package/build/core/context.js +49 -35
  110. package/build/core/contract-carrier-runtime.js +408 -0
  111. package/build/core/contract-carrier.js +270 -0
  112. package/build/core/contract-view.js +33 -27
  113. package/build/core/contract.js +5 -1
  114. package/build/core/derived-replay.js +207 -0
  115. package/build/core/draft.js +125 -57
  116. package/build/core/forfeit.js +113 -56
  117. package/build/core/identifier-slug.js +63 -13
  118. package/build/core/ids.js +4 -4
  119. package/build/core/lifecycle-history.js +35 -0
  120. package/build/core/lifecycle-recovery.js +117 -0
  121. package/build/core/lifecycle-runner.js +377 -0
  122. package/build/core/log.js +37 -11
  123. package/build/core/outcome-base.js +181 -0
  124. package/build/core/process-group.js +139 -0
  125. package/build/core/projection/generation/database.js +82 -7
  126. package/build/core/projection/generation/model.js +9 -36
  127. package/build/core/projection/generation/projection-generation-continuation.js +10 -22
  128. package/build/core/projection/generation/projection-generation-execution.js +6 -5
  129. package/build/core/projection/index.js +5 -4
  130. package/build/core/projection/projection-activity.js +27 -8
  131. package/build/core/projection/projection-alias.js +23 -10
  132. package/build/core/projection/projection-core.js +21 -7
  133. package/build/core/projection/projection-execution-observer.js +91 -39
  134. package/build/core/projection/projection-history.js +474 -0
  135. package/build/core/projection/projection-runner-lock.js +12 -0
  136. package/build/core/projection/projection-status-observation.js +365 -0
  137. package/build/core/projection/projection-status.js +21 -358
  138. package/build/core/projection/projection-terminal-failure.js +13 -8
  139. package/build/core/projection/projection-wait.js +7 -2
  140. package/build/core/projection/tell/launch-store.js +244 -0
  141. package/build/core/projection/tell/model.js +3 -3
  142. package/build/core/projection/tell/store.js +70 -118
  143. package/build/core/registry.js +22 -87
  144. package/build/core/renew-build.js +424 -0
  145. package/build/core/renew-plan.js +239 -0
  146. package/build/core/renew-rewrite.js +77 -0
  147. package/build/core/renew.js +295 -520
  148. package/build/core/repository-ledger/accepted-fold-read.js +19 -0
  149. package/build/core/repository-ledger/accepted-tail.js +116 -0
  150. package/build/core/repository-ledger/admission.js +38 -0
  151. package/build/core/repository-ledger/authoritative-transaction.js +1 -0
  152. package/build/core/repository-ledger/claim-evidence.js +147 -0
  153. package/build/core/repository-ledger/codec.js +297 -0
  154. package/build/core/repository-ledger/current-state-store.js +853 -0
  155. package/build/core/repository-ledger/fold-repository.js +375 -0
  156. package/build/core/repository-ledger/fold.js +248 -0
  157. package/build/core/repository-ledger/git-object-store.js +243 -0
  158. package/build/core/repository-ledger/history-read.js +15 -0
  159. package/build/core/repository-ledger/incremental-read.js +153 -0
  160. package/build/core/repository-ledger/index.js +15 -0
  161. package/build/core/repository-ledger/inventory-plan.js +75 -0
  162. package/build/core/repository-ledger/inventory.js +310 -0
  163. package/build/core/repository-ledger/publication-admission.js +125 -0
  164. package/build/core/repository-ledger/publication.js +353 -0
  165. package/build/core/repository-ledger/read-model.js +469 -0
  166. package/build/core/repository-ledger/transaction-membership.js +78 -0
  167. package/build/core/repository-ledger/traversal.js +404 -0
  168. package/build/core/repository-ledger/write-transaction.js +339 -0
  169. package/build/core/seal.js +168 -161
  170. package/build/core/settlement/claim-delivery.js +42 -215
  171. package/build/core/settlement/claim.js +235 -153
  172. package/build/core/settlement/index.js +4 -4
  173. package/build/core/settlement/petition-claim-gates.js +52 -33
  174. package/build/core/settlement/petition-forfeit.js +12 -5
  175. package/build/core/settlement/petition-head-guard.js +8 -5
  176. package/build/core/settlement/petition-preview.js +68 -56
  177. package/build/core/settlement/petition-window.js +50 -0
  178. package/build/core/settlement/petition.js +252 -131
  179. package/build/core/settlement/queue-read-model.js +72 -95
  180. package/build/core/settlement/queue-seat-allocation.js +35 -126
  181. package/build/core/settlement/queue.js +1 -1
  182. package/build/core/settlement/settlement.js +172 -80
  183. package/build/core/settlement/verdict.js +112 -66
  184. package/build/core/settlement/verification-coordination.js +305 -0
  185. package/build/core/settlement/verification-supervisor.js +275 -0
  186. package/build/core/settlement/verification.js +398 -31
  187. package/build/core/status/board.js +114 -119
  188. package/build/core/status/lifecycle.js +7 -80
  189. package/build/core/structured-query.js +23 -0
  190. package/build/core/target-ref.js +1 -1
  191. package/build/core/task/board.js +59 -24
  192. package/build/core/task/commands.js +181 -33
  193. package/build/core/task/coordinate.js +56 -0
  194. package/build/core/task/document.js +25 -9
  195. package/build/core/task/index.js +7 -4
  196. package/build/core/task/query.js +17 -27
  197. package/build/core/task/settlement-git.js +164 -179
  198. package/build/core/task/settlement-policy.js +15 -11
  199. package/build/core/task/source-board.js +31 -13
  200. package/build/core/task/task-bind-preparation.js +193 -0
  201. package/build/core/task/task-contract.js +102 -193
  202. package/build/core/task/task-file-transaction.js +171 -0
  203. package/build/core/task/task-git-runtime.js +76 -14
  204. package/build/core/task/task-git-store.js +68 -169
  205. package/build/core/task/task-store-repository.js +13 -6
  206. package/build/core/task/task-worktree.js +6 -33
  207. package/build/core/task/task.js +1 -1
  208. package/build/core/task/tree.js +3 -3
  209. package/build/core/task/validation.js +4 -3
  210. package/build/core/transcripts.js +93 -1
  211. package/build/core/worktree-bootstrap.js +44 -36
  212. package/build/core/worktree-path.js +210 -77
  213. package/build/flow-error.js +7 -7
  214. package/build/generated/version.js +2 -2
  215. package/build/git/core.js +76 -13
  216. package/build/git/refs.js +168 -46
  217. package/build/git/streaming-batch.js +73 -13
  218. package/package.json +1 -1
  219. package/skills/keiyaku/SKILL.md +1 -1
  220. package/skills/keiyaku-akuma/SKILL.md +3 -3
  221. package/skills/keiyaku-task/SKILL.md +4 -2
  222. package/skills/keiyaku-workflow/SKILL.md +88 -203
  223. package/build/core/entry.js +0 -305
  224. package/build/core/ledger-batch.js +0 -194
  225. package/build/core/ledger.js +0 -84
  226. package/build/core/projection/leash.js +0 -87
  227. package/build/core/ref-log.js +0 -119
  228. package/build/core/renew-session.js +0 -128
  229. package/build/core/status/ledger-batch.js +0 -1
  230. package/build/core/status/reconciliation.js +0 -137
@@ -1,20 +1,14 @@
1
1
  import fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { isErrnoException } from "../../errno.js";
4
- import { executionEventsPath, isProjectionId, projectionRoot, } from "./projection-core.js";
5
- import { readProjectionAlias } from "./projection-alias.js";
6
- import { ProjectionStateError } from "../atomic-publish.js";
7
- import { foldPublicProjectionActivity, } from "./projection-activity.js";
4
+ import { isProjectionId, projectionRoot, projectionRootAtCoordinate } from "./projection-core.js";
5
+ import { projectionAliasRootAtCoordinate, readProjectionAliasAtRoot, } from "./projection-alias.js";
6
+ import { resolveProjectionCoordinate } from "./projection-coordinate.js";
8
7
  import { readProjectionIdentity } from "./projection-identity.js";
9
- import { readPendingTellWindow, readProjectionTellCounts } from "./tell/store.js";
10
- import { ProjectionGenerationStoreError, } from "./generation/store.js";
11
8
  import { PROJECTION_ADOPTION_TIMEOUT_MS } from "./generation/projection-generation-launcher.js";
12
9
  import { observeProjectionLifeSnapshot, } from "./projection-life-observer.js";
13
- import { selectCurrentGeneration, } from "./projection-life-protocol.js";
14
- import { invalidActivityEventDiagnostic, selectValidStoredAgentEvents } from "../stored-agent-event.js";
15
10
  import { worktreeContainsCoordinate } from "../worktree-path.js";
16
- import { projectTerminalFailure, } from "./projection-terminal-failure.js";
17
- import { extractTerminalTimestamp } from "./generation/model.js";
11
+ import { readProjectionRow, readProjectionSummary } from "./projection-status-observation.js";
18
12
  const MAX_STATUS_FILE_BYTES = 256 * 1024;
19
13
  const STATUS_GROUP = {
20
14
  "startup-timeout": 0,
@@ -84,349 +78,13 @@ export function prioritizeProjectionStatusRows(rows) {
84
78
  export function requiresProjectionStatusDetails(state) {
85
79
  return !isOmissibleProjectionStatusState(state);
86
80
  }
87
- class ProjectionStatusFileError extends Error {
88
- diagnostic;
89
- constructor(kind, filePath) {
90
- const label = path.basename(filePath);
91
- const detail = `${label} is not a regular file`;
92
- super(detail);
93
- this.name = "ProjectionStatusFileError";
94
- this.diagnostic = { kind, detail };
95
- }
96
- }
97
- function oversizedProjectionFileDiagnostic(filePath) {
98
- return {
99
- kind: "oversized-projection-file",
100
- fileName: path.basename(filePath),
101
- };
102
- }
103
- function recordProjectionStatusFileError(error, diagnostics) {
104
- if (!(error instanceof ProjectionStatusFileError))
105
- return false;
106
- diagnostics.push(error.diagnostic);
107
- return true;
108
- }
109
- function readBoundedRegularFileTail(filePath) {
110
- const before = fs.lstatSync(filePath);
111
- if (!before.isFile())
112
- throw new ProjectionStatusFileError("non-regular-projection-file", filePath);
113
- let fd;
114
- try {
115
- fd = fs.openSync(filePath, fs.constants.O_RDONLY | fs.constants.O_NONBLOCK | fs.constants.O_NOFOLLOW);
116
- }
117
- catch (error) {
118
- if (isErrnoException(error) && error.code === "ELOOP") {
119
- throw new ProjectionStatusFileError("non-regular-projection-file", filePath);
120
- }
121
- throw error;
122
- }
123
- try {
124
- const opened = fs.fstatSync(fd);
125
- if (!opened.isFile())
126
- throw new ProjectionStatusFileError("non-regular-projection-file", filePath);
127
- const snapshotSize = opened.size;
128
- const truncated = snapshotSize > MAX_STATUS_FILE_BYTES;
129
- const readLength = Math.min(snapshotSize, MAX_STATUS_FILE_BYTES);
130
- const readOffset = Math.max(0, snapshotSize - MAX_STATUS_FILE_BYTES);
131
- const buffer = Buffer.allocUnsafe(readLength);
132
- let total = 0;
133
- while (total < readLength) {
134
- const bytesRead = fs.readSync(fd, buffer, total, readLength - total, readOffset + total);
135
- if (bytesRead === 0)
136
- break;
137
- total += bytesRead;
138
- }
139
- let tail = buffer.subarray(0, total);
140
- if (truncated) {
141
- const firstLf = tail.indexOf(0x0a);
142
- if (firstLf < 0) {
143
- return { content: "", truncated: true };
144
- }
145
- tail = tail.subarray(firstLf + 1);
146
- }
147
- return { content: tail.toString("utf8"), truncated };
148
- }
149
- finally {
150
- fs.closeSync(fd);
151
- }
152
- }
153
- function displayedTellCounts(counts) {
154
- return {
155
- inbox: counts.inbox,
156
- inflight: counts.inflight,
157
- submitted: counts.submitted,
158
- consumed: counts.consumed,
159
- };
160
- }
161
- function emptyTellCounts() {
162
- return { inbox: 0, inflight: 0, submitted: 0, consumed: 0 };
163
- }
164
- function readIdentityFacts(projectionDir, diagnostics) {
165
- try {
166
- const identity = readProjectionIdentity(projectionDir);
167
- return {
168
- identity,
169
- pid: identity.pid,
170
- akuma: identity.akuma,
171
- provider: identity.provider,
172
- binding: identity.binding,
173
- mintedAt: identity.mintedAt,
174
- workspaceRoot: identity.workspaceRoot,
175
- };
176
- }
177
- catch (error) {
178
- if (isErrnoException(error) && error.code === "ENOENT") {
179
- diagnostics.push({ kind: "missing-identity", detail: "heart identity missing" });
180
- return { identity: null, pid: undefined, akuma: undefined, provider: undefined, binding: undefined, mintedAt: undefined, workspaceRoot: undefined };
181
- }
182
- if (error instanceof ProjectionGenerationStoreError) {
183
- diagnostics.push({ kind: "invalid-identity", detail: error.message });
184
- return { identity: null, pid: undefined, akuma: undefined, provider: undefined, binding: undefined, mintedAt: undefined, workspaceRoot: undefined };
185
- }
186
- if (error instanceof ProjectionStateError) {
187
- diagnostics.push({ kind: "invalid-identity", detail: error.message });
188
- return { identity: null, pid: undefined, akuma: undefined, provider: undefined, binding: undefined, mintedAt: undefined, workspaceRoot: undefined };
189
- }
190
- diagnostics.push({ kind: "unreadable-identity", detail: `heart identity unreadable: ${String(error)}` });
191
- return { identity: null, pid: undefined, akuma: undefined, provider: undefined, binding: undefined, mintedAt: undefined, workspaceRoot: undefined };
192
- }
193
- }
194
- function readHeartTellCounts(projectionDir, diagnostics) {
195
- try {
196
- return displayedTellCounts(readProjectionTellCounts(projectionDir));
197
- }
198
- catch (error) {
199
- if (error instanceof ProjectionGenerationStoreError || error instanceof ProjectionStateError) {
200
- diagnostics.push({ kind: "unreadable-tells", detail: error.message });
201
- return emptyTellCounts();
202
- }
203
- diagnostics.push({ kind: "unreadable-tells", detail: `heart tells unreadable: ${String(error)}` });
204
- return emptyTellCounts();
205
- }
206
- }
207
- function readPendingTells(projectionDir, diagnostics) {
208
- try {
209
- // Kanshi is an operator surface: unconsumed tells are actionable facts, not
210
- // a count to hide behind. This is one heart read and does not enumerate a
211
- // second projection topology.
212
- return readPendingTellWindow(projectionDir, Number.MAX_SAFE_INTEGER).window;
213
- }
214
- catch (error) {
215
- if (error instanceof ProjectionGenerationStoreError || error instanceof ProjectionStateError) {
216
- diagnostics.push({ kind: "unreadable-tells", detail: error.message });
217
- return [];
218
- }
219
- diagnostics.push({ kind: "unreadable-tells", detail: `heart tells unreadable: ${String(error)}` });
220
- return [];
221
- }
222
- }
223
- function readStoredEvents(projectionDir, executionId, diagnostics) {
224
- let content;
225
- let truncated = false;
226
- const eventsPath = executionEventsPath(projectionDir, executionId);
227
- try {
228
- const read = readBoundedRegularFileTail(eventsPath);
229
- content = read.content;
230
- truncated = read.truncated;
231
- }
232
- catch (error) {
233
- if (isErrnoException(error) && error.code === "ENOENT")
234
- return [];
235
- if (recordProjectionStatusFileError(error, diagnostics))
236
- return [];
237
- diagnostics.push({ kind: "unreadable-activity", detail: `activity ledger unreadable: ${String(error)}` });
238
- return [];
239
- }
240
- if (truncated) {
241
- diagnostics.push(oversizedProjectionFileDiagnostic(eventsPath));
242
- }
243
- // Parse only LF-terminated records. An unterminated final line is ignored even
244
- // when it happens to parse: it may be a concurrent append fragment.
245
- const lines = content.split("\n");
246
- const values = [];
247
- let malformed = 0;
248
- for (const line of lines.slice(0, -1)) {
249
- try {
250
- values.push(JSON.parse(line));
251
- }
252
- catch {
253
- malformed += 1;
254
- }
255
- }
256
- if (malformed > 0) {
257
- diagnostics.push({
258
- kind: "invalid-activity-event",
259
- detail: `${malformed} persisted activity event${malformed === 1 ? "" : "s"} malformed`,
260
- });
261
- }
262
- return values;
263
- }
264
- function latestValidEvent(events) {
265
- let latest;
266
- for (const event of events) {
267
- const atMs = Date.parse(event.at);
268
- if (!Number.isFinite(atMs))
269
- continue;
270
- if (latest === undefined || atMs > latest.atMs)
271
- latest = { at: event.at, atMs };
272
- }
273
- return latest;
274
- }
275
- function readActivityFacts(projectionDir, executionId, nowMs, diagnostics, fallbackAt) {
276
- const fallbackAtMs = fallbackAt === undefined ? undefined : Date.parse(fallbackAt);
277
- const validFallback = fallbackAt !== undefined && Number.isFinite(fallbackAtMs)
278
- ? { at: fallbackAt, atMs: fallbackAtMs }
279
- : undefined;
280
- if (!executionId) {
281
- return validFallback
282
- ? { activityAt: validFallback.at, activityAtMs: validFallback.atMs, activityAgeMs: Math.max(0, nowMs - validFallback.atMs) }
283
- : {};
284
- }
285
- const stored = readStoredEvents(projectionDir, executionId, diagnostics);
286
- const { events, malformed } = selectValidStoredAgentEvents(stored);
287
- const invalidActivity = invalidActivityEventDiagnostic(malformed);
288
- if (invalidActivity)
289
- diagnostics.push(invalidActivity);
290
- const latest = latestValidEvent(events) ?? validFallback;
291
- return {
292
- ...(events.length > 0 ? { activity: foldPublicProjectionActivity(events) } : {}),
293
- ...(latest !== undefined
294
- ? {
295
- activityAt: latest.at,
296
- activityAtMs: latest.atMs,
297
- activityAgeMs: Math.max(0, nowMs - latest.atMs),
298
- }
299
- : {}),
300
- };
301
- }
302
- function terminalTimestamp(current) {
303
- return extractTerminalTimestamp(current?.verdict?.facts);
304
- }
305
- function readProjectionSummary(projectionDir, id, alias, nowMs) {
306
- const diagnostics = [];
307
- const snapshot = observeProjectionLifeSnapshot(projectionDir, {
308
- nowMs,
309
- startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
310
- });
311
- if (snapshot.compatibility === "incompatible") {
312
- return {
313
- id,
314
- ...(alias ? { alias } : {}),
315
- projectionDir,
316
- snapshot,
317
- identityFacts: null,
318
- diagnostics,
319
- state: "incompatible",
320
- };
321
- }
322
- const identityFacts = readIdentityFacts(projectionDir, diagnostics);
323
- const life = snapshot.life;
324
- const phase = life.phase;
325
- const state = phase === "active" || phase === "quiescent" ? "out" : phase;
326
- return {
327
- id,
328
- ...(alias ? { alias } : {}),
329
- projectionDir,
330
- snapshot,
331
- identityFacts,
332
- diagnostics,
333
- state,
334
- };
335
- }
336
- function readProjectionRow(summary, nowMs, readDetails) {
337
- const { id, alias, projectionDir, snapshot } = summary;
338
- const diagnostics = [...summary.diagnostics];
339
- if (snapshot.compatibility === "incompatible") {
340
- return {
341
- id,
342
- observedAtMs: nowMs,
343
- ...(alias ? { alias } : {}),
344
- state: "incompatible",
345
- observation: snapshot,
346
- tellCounts: emptyTellCounts(),
347
- diagnostics,
348
- };
349
- }
350
- const identityFacts = summary.identityFacts;
351
- if (!identityFacts.identity) {
352
- const diagnostic = diagnostics.map((entry) => entry.detail).find((detail) => detail !== undefined)
353
- ?? "projection identity could not be observed";
354
- return {
355
- id,
356
- observedAtMs: nowMs,
357
- ...(alias ? { alias } : {}),
358
- state: "unknown",
359
- observation: {
360
- compatibility: "current",
361
- lifecycle: { phase: "unknown", diagnostic },
362
- },
363
- tellCounts: emptyTellCounts(),
364
- diagnostics,
365
- };
366
- }
367
- const life = snapshot.life;
368
- const current = snapshot.records ? selectCurrentGeneration(snapshot.records) : null;
369
- const adoptedAt = current?.adoption?.facts.adoptedAt;
370
- const adoptedAtMs = typeof adoptedAt === "string"
371
- ? Date.parse(adoptedAt)
372
- : Number.NaN;
373
- const tellCounts = readDetails
374
- ? readHeartTellCounts(projectionDir, diagnostics)
375
- : emptyTellCounts();
376
- const pendingTells = readDetails
377
- ? readPendingTells(projectionDir, diagnostics)
378
- : [];
379
- const activityFacts = readDetails
380
- ? readActivityFacts(projectionDir, life.executionId, nowMs, diagnostics, typeof adoptedAt === "string" && Number.isFinite(adoptedAtMs) ? adoptedAt : identityFacts.mintedAt)
381
- : {};
382
- const mintedAtMs = Date.parse(identityFacts.mintedAt ?? "");
383
- const terminalAt = terminalTimestamp(current);
384
- const durationAnchorMs = current?.verdict
385
- ? (terminalAt === undefined ? Number.NaN : Date.parse(terminalAt))
386
- : nowMs;
387
- const durationMs = Number.isFinite(mintedAtMs) && Number.isFinite(durationAnchorMs)
388
- ? Math.max(0, durationAnchorMs - mintedAtMs)
389
- : undefined;
390
- const displayAgeMs = activityFacts.activityAgeMs
391
- ?? (Number.isFinite(mintedAtMs) ? Math.max(0, nowMs - mintedAtMs) : undefined);
392
- if (life.phase === "unknown" && life.diagnostic) {
393
- diagnostics.push({ kind: "unknown-lifecycle", detail: life.diagnostic });
394
- }
395
- const phase = life.phase;
396
- const state = phase === "active" || phase === "quiescent" ? "out" : phase;
397
- const terminalFailure = current?.verdict
398
- ? projectTerminalFailure(current.verdict.facts.state, current.verdict.facts)
399
- : undefined;
400
- return {
401
- id,
402
- observedAtMs: nowMs,
403
- ...(alias ? { alias } : {}),
404
- state,
405
- observation: { compatibility: "current", lifecycle: life },
406
- akuma: identityFacts.akuma,
407
- provider: identityFacts.provider,
408
- binding: identityFacts.binding,
409
- pid: identityFacts.pid,
410
- mintedAt: identityFacts.mintedAt,
411
- ...(terminalAt ? { terminalAt } : {}),
412
- ...(durationMs !== undefined ? { durationMs } : {}),
413
- ...(identityFacts.workspaceRoot ? { workspaceRoot: identityFacts.workspaceRoot } : {}),
414
- ...activityFacts,
415
- ...(displayAgeMs !== undefined ? { activityAgeMs: displayAgeMs } : {}),
416
- ...(terminalFailure ? { terminalFailure } : {}),
417
- tellCounts,
418
- ...(pendingTells.length > 0 ? { pendingTells } : {}),
419
- diagnostics,
420
- };
421
- }
422
- async function readProjectionStatusAliases(cwd, root) {
81
+ async function readProjectionStatusAliases(root) {
423
82
  const aliases = new Map();
424
- const aliasRoot = path.join(path.dirname(path.dirname(root)), ".keiyaku", "projection-alias");
425
83
  try {
426
- for (const alias of fs.readdirSync(aliasRoot).sort()) {
84
+ for (const alias of fs.readdirSync(root).sort()) {
427
85
  if (alias.startsWith("."))
428
86
  continue;
429
- const id = await readProjectionAlias(cwd, alias);
87
+ const id = await readProjectionAliasAtRoot(root, alias);
430
88
  if (id)
431
89
  aliases.set(id, alias);
432
90
  }
@@ -449,8 +107,8 @@ function readProjectionTopologyEntries(directory) {
449
107
  throw error;
450
108
  }
451
109
  }
452
- async function readProjectionRootTopology(cwd) {
453
- const root = await projectionRoot(cwd);
110
+ function readProjectionRootTopology(coordinate) {
111
+ const root = projectionRootAtCoordinate(coordinate);
454
112
  const akumas = readProjectionTopologyEntries(root);
455
113
  return akumas ? { root, akumas } : null;
456
114
  }
@@ -472,7 +130,9 @@ function readAkumaProjectionStatusSummaries(root, akuma, aliases, nowMs) {
472
130
  catch {
473
131
  continue;
474
132
  }
475
- summaries.push(readProjectionSummary(dir, id, aliases.get(id), nowMs));
133
+ summaries.push(readProjectionSummary(dir, id, aliases.get(id), nowMs, {
134
+ requiresIdentity: requiresProjectionStatusDetails,
135
+ }));
476
136
  }
477
137
  return summaries;
478
138
  }
@@ -481,11 +141,12 @@ function isNonterminalProjectionStatusSummary(summary) {
481
141
  && requiresProjectionStatusDetails(summary.state);
482
142
  }
483
143
  export async function readOperationalProjectionStatusBoard(cwd, nowMs) {
484
- const topology = await readProjectionRootTopology(cwd);
144
+ const coordinate = await resolveProjectionCoordinate(cwd);
145
+ const topology = readProjectionRootTopology(coordinate);
485
146
  if (!topology)
486
147
  return null;
487
148
  const { root, akumas } = topology;
488
- const aliases = await readProjectionStatusAliases(cwd, root);
149
+ const aliases = await readProjectionStatusAliases(projectionAliasRootAtCoordinate(coordinate));
489
150
  const summaries = [];
490
151
  for (const akuma of akumas) {
491
152
  if (akuma.startsWith("."))
@@ -505,7 +166,8 @@ export async function readOperationalProjectionStatusBoard(cwd, nowMs) {
505
166
  export async function readExactProjectionStatus(cwd, nowMs, projectionId) {
506
167
  if (!isProjectionId(projectionId))
507
168
  return null;
508
- const root = await projectionRoot(cwd);
169
+ const coordinate = await resolveProjectionCoordinate(cwd);
170
+ const root = projectionRootAtCoordinate(coordinate);
509
171
  try {
510
172
  if (!fs.lstatSync(root).isDirectory())
511
173
  return null;
@@ -526,13 +188,14 @@ export async function readExactProjectionStatus(cwd, nowMs, projectionId) {
526
188
  return null;
527
189
  throw error;
528
190
  }
529
- const aliases = await readProjectionStatusAliases(cwd, root);
191
+ const aliases = await readProjectionStatusAliases(projectionAliasRootAtCoordinate(coordinate));
530
192
  return readProjectionRow(readProjectionSummary(dir, projectionId, aliases.get(projectionId), nowMs), nowMs, true);
531
193
  }
532
194
  export async function readAkumaNonterminalProjectionStatus(cwd, nowMs, akuma) {
533
195
  if (!isProjectionId(`${akuma}/00000000`))
534
196
  return null;
535
- const root = await projectionRoot(cwd);
197
+ const coordinate = await resolveProjectionCoordinate(cwd);
198
+ const root = projectionRootAtCoordinate(coordinate);
536
199
  try {
537
200
  if (!fs.lstatSync(root).isDirectory())
538
201
  return null;
@@ -542,7 +205,7 @@ export async function readAkumaNonterminalProjectionStatus(cwd, nowMs, akuma) {
542
205
  return null;
543
206
  throw error;
544
207
  }
545
- const aliases = await readProjectionStatusAliases(cwd, root);
208
+ const aliases = await readProjectionStatusAliases(projectionAliasRootAtCoordinate(coordinate));
546
209
  const summaries = readAkumaProjectionStatusSummaries(root, akuma, aliases, nowMs);
547
210
  if (summaries === null)
548
211
  return null;
@@ -43,15 +43,19 @@ function artifactId(source) {
43
43
  }
44
44
  function providerFailureCause(value) {
45
45
  const source = objectValue(value);
46
- return source?.kind === "rate_limit_exhausted"
47
- ? { kind: "rate_limit_exhausted" }
48
- : undefined;
46
+ return source && isHarnessFailureValue(source) ? source : undefined;
47
+ }
48
+ function isHarnessFailureValue(value) {
49
+ if (value === null || typeof value === "boolean" || typeof value === "string")
50
+ return true;
51
+ if (typeof value === "number")
52
+ return Number.isFinite(value);
53
+ if (Array.isArray(value))
54
+ return value.every(isHarnessFailureValue);
55
+ const object = objectValue(value);
56
+ return object !== undefined && Object.values(object).every(isHarnessFailureValue);
49
57
  }
50
- /**
51
- * Convert artifact-rich verdict facts into the strict public allowlist. Raw
52
- * messages, output, diagnostics, sessions, provider payloads, and unknown
53
- * strings have no path through this function.
54
- */
58
+ /** Convert the public harness terminal failure into the shared projection face. */
55
59
  export function projectTerminalFailure(state, facts) {
56
60
  if (state !== "failed" && state !== "lost-runner" && state !== "launch-failed")
57
61
  return undefined;
@@ -97,6 +101,7 @@ export function projectTerminalFailure(state, facts) {
97
101
  class: "harness",
98
102
  code,
99
103
  phase: source.phase === "adoption" ? "adoption" : "runtime",
104
+ ...(typeof error.message === "string" ? { message: error.message } : {}),
100
105
  ...(timeoutMs !== undefined ? { timeoutMs } : {}),
101
106
  ...(publicExitCode !== undefined ? { exitCode: publicExitCode } : {}),
102
107
  ...(publicSignal ? { signal: publicSignal } : {}),
@@ -1,7 +1,8 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { foldPublicProjectionActivity } from "./projection-activity.js";
4
- import { readGenerationEvents, isProjectionId, projectionRoot, } from "./projection-core.js";
4
+ import { readGenerationEvents, isProjectionId, projectionRootAtCoordinate, } from "./projection-core.js";
5
+ import { resolveProjectionCoordinate } from "./projection-coordinate.js";
5
6
  import { readProjectionIdentity } from "./projection-identity.js";
6
7
  import { RESPONSE_ARTIFACT_ID_RE } from "../response-artifact-id.js";
7
8
  import { extractTerminalTimestamp } from "./generation/model.js";
@@ -15,7 +16,11 @@ import { tellProjection } from "./projection-wake.js";
15
16
  import { FlowError } from "../../flow-error.js";
16
17
  /** Return the commission-bound projection IDs visible at one admission instant. */
17
18
  export async function snapshotCommissionProjectionIds(cwd, commissionId) {
18
- const root = await projectionRoot(cwd);
19
+ return snapshotCommissionProjectionIdsAtCoordinate(await resolveProjectionCoordinate(cwd), commissionId);
20
+ }
21
+ /** Scan one already-resolved projection coordinate for commission-bound IDs. */
22
+ export async function snapshotCommissionProjectionIdsAtCoordinate(coordinate, commissionId) {
23
+ const root = projectionRootAtCoordinate(coordinate);
19
24
  let akumas;
20
25
  try {
21
26
  akumas = fs.readdirSync(root).sort();