@astrosheep/keiyaku 2.9.3 → 2.9.5

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 (282) hide show
  1. package/build/.tsbuildinfo +1 -1
  2. package/build/agents/call-terms.js +14 -94
  3. package/build/agents/harness/activity-values.js +16 -0
  4. package/build/agents/harness/event-persistence.js +218 -0
  5. package/build/agents/harness/execution-handle.js +26 -222
  6. package/build/agents/harness/index.js +3 -1
  7. package/build/agents/harness/outcome.js +68 -5
  8. package/build/agents/harness/projection.js +179 -124
  9. package/build/agents/harness/provider-adapter.js +58 -0
  10. package/build/agents/harness/router.js +11 -187
  11. package/build/agents/harness/runtime.js +17 -4
  12. package/build/agents/harness/session.js +15 -0
  13. package/build/agents/launch-snapshot/model.js +15 -236
  14. package/build/agents/launch-snapshot/parse-helpers.js +114 -0
  15. package/build/agents/launch-snapshot/resolve.js +17 -43
  16. package/build/agents/opencode-sdk.js +45 -12
  17. package/build/agents/providers/claude-agent-sdk/adapter.js +6 -17
  18. package/build/agents/providers/claude-agent-sdk/registration.js +214 -0
  19. package/build/agents/providers/claude-agent-sdk/session-schema.js +2 -0
  20. package/build/agents/providers/codex-app-server/adapter.js +82 -53
  21. package/build/agents/providers/codex-app-server/events.js +90 -11
  22. package/build/agents/providers/codex-app-server/identity.js +1 -0
  23. package/build/agents/providers/codex-app-server/policy.js +29 -0
  24. package/build/agents/providers/codex-app-server/registration.js +248 -0
  25. package/build/agents/providers/codex-app-server/session-schema.js +6 -0
  26. package/build/agents/providers/codex-app-server/session.js +23 -17
  27. package/build/agents/providers/codex-app-server/terms.js +1 -0
  28. package/build/agents/providers/opencode-sdk/adapter.js +94 -50
  29. package/build/agents/providers/opencode-sdk/events.js +8 -4
  30. package/build/agents/providers/opencode-sdk/registration.js +135 -0
  31. package/build/agents/providers/opencode-sdk/session-schema.js +2 -0
  32. package/build/agents/providers/opencode-sdk/session.js +21 -14
  33. package/build/agents/providers/opencode-sdk/terms.js +1 -0
  34. package/build/agents/providers/pi/adapter.js +36 -30
  35. package/build/agents/providers/pi/events.js +4 -3
  36. package/build/agents/providers/pi/registration.js +145 -0
  37. package/build/agents/providers/pi/session-schema.js +6 -0
  38. package/build/agents/providers/pi/terms.js +1 -0
  39. package/build/agents/providers/registration.js +7 -0
  40. package/build/agents/providers/registry.js +76 -0
  41. package/build/agents/selector.js +2 -29
  42. package/build/agents/types.js +10 -0
  43. package/build/artifact-directories.js +22 -0
  44. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  45. package/build/cli/commands/akuma/akuma/meta.js +1 -1
  46. package/build/cli/commands/akuma/list/meta-ls.js +1 -1
  47. package/build/cli/commands/akuma/view/handler.js +5 -4
  48. package/build/cli/commands/akuma/view/meta.js +1 -1
  49. package/build/cli/commands/akuma.js +6 -0
  50. package/build/cli/commands/contract/amend/handler.js +8 -2
  51. package/build/cli/commands/contract/amend/meta.js +4 -4
  52. package/build/cli/commands/contract/arc/handler.js +8 -2
  53. package/build/cli/commands/contract/arc/meta.js +4 -4
  54. package/build/cli/commands/contract/audit/handler.js +16 -0
  55. package/build/cli/commands/contract/audit/meta.js +16 -0
  56. package/build/cli/commands/contract/bind/handler.js +4 -3
  57. package/build/cli/commands/contract/bind/meta.js +44 -8
  58. package/build/cli/commands/contract/catalog.js +2 -0
  59. package/build/cli/commands/contract/forfeit/handler.js +4 -4
  60. package/build/cli/commands/contract/forfeit/meta.js +1 -1
  61. package/build/cli/commands/contract/log/handler.js +3 -3
  62. package/build/cli/commands/contract/log/meta.js +1 -1
  63. package/build/cli/commands/contract/petition/handler.js +5 -8
  64. package/build/cli/commands/contract/petition/meta.js +6 -6
  65. package/build/cli/commands/contract/renew/handler.js +20 -6
  66. package/build/cli/commands/contract/renew/meta.js +6 -5
  67. package/build/cli/commands/metadata.js +59 -0
  68. package/build/cli/commands/projection/call/handler.js +6 -5
  69. package/build/cli/commands/projection/call/meta.js +4 -1
  70. package/build/cli/commands/projection/kill/handler.js +16 -32
  71. package/build/cli/commands/projection/kill/meta.js +1 -1
  72. package/build/cli/commands/projection/revive/meta.js +4 -1
  73. package/build/cli/commands/projection/shared.js +28 -0
  74. package/build/cli/commands/projection/status/handler.js +1 -1
  75. package/build/cli/commands/projection/status/meta.js +1 -1
  76. package/build/cli/commands/projection/tell/handler.js +14 -30
  77. package/build/cli/commands/projection/tell/meta.js +4 -1
  78. package/build/cli/commands/projection/wait/handler.js +45 -22
  79. package/build/cli/commands/projection/wait/meta.js +8 -3
  80. package/build/cli/commands/registry.js +2 -54
  81. package/build/cli/commands/shared.js +10 -7
  82. package/build/cli/commands/skills/install/handler.js +2 -5
  83. package/build/cli/commands/skills/install/meta.js +1 -1
  84. package/build/cli/commands/skills/skills/handler.js +1 -1
  85. package/build/cli/commands/skills/skills/meta.js +1 -1
  86. package/build/cli/commands/system/completion/meta.js +1 -1
  87. package/build/cli/commands/system/dump-env/meta.js +1 -1
  88. package/build/cli/commands/system/guide/meta.js +1 -1
  89. package/build/cli/commands/task/add/handler.js +7 -15
  90. package/build/cli/commands/task/add/meta.js +4 -1
  91. package/build/cli/commands/task/catalog.js +2 -2
  92. package/build/cli/commands/task/doctor/meta.js +1 -1
  93. package/build/cli/commands/task/done/handler.js +4 -8
  94. package/build/cli/commands/task/done/meta.js +1 -1
  95. package/build/cli/commands/task/drop/handler.js +3 -6
  96. package/build/cli/commands/task/drop/meta.js +1 -1
  97. package/build/cli/commands/task/ls/handler.js +12 -8
  98. package/build/cli/commands/task/ls/meta.js +3 -3
  99. package/build/cli/commands/task/shared.js +14 -4
  100. package/build/cli/commands/task/{view → show}/handler.js +7 -8
  101. package/build/cli/commands/task/show/meta.js +8 -0
  102. package/build/cli/commands/task/start/handler.js +3 -6
  103. package/build/cli/commands/task/start/meta.js +1 -1
  104. package/build/cli/commands/task/stop/handler.js +4 -8
  105. package/build/cli/commands/task/stop/meta.js +1 -1
  106. package/build/cli/commands/task/task/handler.js +1 -1
  107. package/build/cli/commands/task/task/meta.js +1 -1
  108. package/build/cli/commands/task/update/handler.js +6 -11
  109. package/build/cli/commands/task/update/meta.js +4 -1
  110. package/build/cli/completion.js +1 -1
  111. package/build/cli/flags.js +37 -6
  112. package/build/cli/help.js +7 -14
  113. package/build/cli/index.js +112 -154
  114. package/build/cli/missing-subcommand.js +7 -0
  115. package/build/cli/output-stream-error-policy.js +19 -5
  116. package/build/cli/parse-flags.js +33 -7
  117. package/build/cli/parse-metadata.js +3 -9
  118. package/build/cli/parse-selectors.js +2 -1
  119. package/build/cli/parse.js +35 -5
  120. package/build/cli/projection-address.js +12 -12
  121. package/build/cli/render/address.js +41 -37
  122. package/build/cli/render/arc.js +24 -12
  123. package/build/cli/render/audit.js +121 -0
  124. package/build/cli/render/call.js +11 -5
  125. package/build/cli/render/errors.js +36 -1
  126. package/build/cli/render/format.js +4 -0
  127. package/build/cli/render/kanshi.js +6 -12
  128. package/build/cli/render/kill.js +9 -0
  129. package/build/cli/render/misc.js +58 -13
  130. package/build/cli/render/petition.js +20 -15
  131. package/build/cli/render/projection-activity.js +15 -6
  132. package/build/cli/render/shared.js +60 -21
  133. package/build/cli/render/skills.js +10 -0
  134. package/build/cli/render/status.js +56 -23
  135. package/build/cli/render/success-response.js +78 -0
  136. package/build/cli/render/task.js +66 -57
  137. package/build/cli/render/tell.js +5 -0
  138. package/build/cli/render/terminal-failure.js +63 -0
  139. package/build/cli/render/types.js +1 -0
  140. package/build/cli/render/wait.js +68 -38
  141. package/build/cli/types.js +1 -1
  142. package/build/config/akuma-loader.js +0 -5
  143. package/build/config/env-keys.js +3 -1
  144. package/build/config/env.js +3 -0
  145. package/build/config/provider-policy-ownership.js +3 -32
  146. package/build/config/settings/disease.js +8 -1
  147. package/build/config/settings/loader.js +11 -3
  148. package/build/config/settings/provider-instance-fields.js +51 -0
  149. package/build/config/settings/schema.js +6 -176
  150. package/build/core/addressing.js +66 -16
  151. package/build/core/amend.js +61 -18
  152. package/build/core/arc.js +33 -73
  153. package/build/core/audit.js +650 -0
  154. package/build/core/bind.js +82 -35
  155. package/build/core/call/call.js +15 -9
  156. package/build/core/call/context.js +6 -51
  157. package/build/core/call/execution.js +22 -22
  158. package/build/core/call/prompt.js +14 -1
  159. package/build/core/command-markdown.js +45 -0
  160. package/build/core/context.js +13 -7
  161. package/build/core/contract-view.js +22 -6
  162. package/build/core/draft.js +139 -19
  163. package/build/core/entry.js +3 -4
  164. package/build/core/forfeit.js +5 -2
  165. package/build/core/hints.js +1 -75
  166. package/build/core/identifier-slug.js +66 -0
  167. package/build/core/ids.js +25 -11
  168. package/build/core/ledger.js +20 -68
  169. package/build/core/markdown/lex.js +2 -1
  170. package/build/core/markdown/parser.js +0 -68
  171. package/build/core/markdown/render.js +68 -0
  172. package/build/core/markdown/sections.js +2 -1
  173. package/build/core/outcome-base.js +4 -0
  174. package/build/core/projection/generation/database.js +41 -52
  175. package/build/core/projection/generation/model.js +63 -2
  176. package/build/core/{projection-generation-continuation.js → projection/generation/projection-generation-continuation.js} +3 -3
  177. package/build/core/{projection-generation-execution.js → projection/generation/projection-generation-execution.js} +36 -53
  178. package/build/core/{projection-generation-identity.js → projection/generation/projection-generation-identity.js} +2 -2
  179. package/build/core/{projection-generation-launcher.js → projection/generation/projection-generation-launcher.js} +9 -7
  180. package/build/core/{projection-generation-process.js → projection/generation/projection-generation-process.js} +2 -1
  181. package/build/core/{projection-generation-runner.js → projection/generation/projection-generation-runner.js} +19 -3
  182. package/build/core/projection/generation/store.js +3 -3
  183. package/build/core/projection/generation/transitions.js +19 -3
  184. package/build/core/projection/index.js +27 -0
  185. package/build/core/{projection-activity.js → projection/projection-activity.js} +12 -0
  186. package/build/core/{projection-alias.js → projection/projection-alias.js} +13 -13
  187. package/build/core/projection/projection-coordinate.js +61 -0
  188. package/build/core/{projection-core.js → projection/projection-core.js} +9 -9
  189. package/build/core/{projection-execution-observer.js → projection/projection-execution-observer.js} +37 -78
  190. package/build/core/{projection-identity.js → projection/projection-identity.js} +1 -1
  191. package/build/core/{projection-kill.js → projection/projection-kill.js} +1 -1
  192. package/build/core/{projection-life-observer.js → projection/projection-life-observer.js} +16 -2
  193. package/build/core/{projection-mint.js → projection/projection-mint.js} +10 -10
  194. package/build/core/{projection-status.js → projection/projection-status.js} +163 -45
  195. package/build/core/projection/projection-terminal-failure.js +106 -0
  196. package/build/core/projection/projection-wait.js +275 -0
  197. package/build/core/projection/projection-wake.js +227 -0
  198. package/build/core/provider-loader-ports.js +4 -0
  199. package/build/core/ref-log.js +65 -40
  200. package/build/core/registry.js +63 -10
  201. package/build/core/renew-session.js +128 -0
  202. package/build/core/renew.js +448 -102
  203. package/build/core/scope.js +272 -68
  204. package/build/core/seal.js +165 -40
  205. package/build/core/{claim-delivery.js → settlement/claim-delivery.js} +8 -8
  206. package/build/core/{claim.js → settlement/claim.js} +32 -8
  207. package/build/core/settlement/index.js +19 -0
  208. package/build/core/{petition-claim-gates.js → settlement/petition-claim-gates.js} +24 -49
  209. package/build/core/{petition-forfeit.js → settlement/petition-forfeit.js} +2 -2
  210. package/build/core/{petition-head-guard.js → settlement/petition-head-guard.js} +2 -2
  211. package/build/core/settlement/petition-io.js +1 -0
  212. package/build/core/{petition-preview.js → settlement/petition-preview.js} +2 -2
  213. package/build/core/{petition.js → settlement/petition.js} +21 -12
  214. package/build/core/settlement/queue-read-model.js +143 -0
  215. package/build/core/settlement/queue-seat-allocation.js +156 -0
  216. package/build/core/settlement/queue.js +6 -0
  217. package/build/core/settlement/settlement.js +288 -0
  218. package/build/core/{verdict.js → settlement/verdict.js} +9 -37
  219. package/build/core/settlement/verification.js +59 -0
  220. package/build/core/status/board.js +62 -41
  221. package/build/core/status/ledger-batch.js +158 -0
  222. package/build/core/status/reconciliation.js +10 -4
  223. package/build/core/stored-agent-event.js +6 -2
  224. package/build/core/task/board.js +41 -4
  225. package/build/core/task/commands.js +45 -28
  226. package/build/core/task/document.js +5 -0
  227. package/build/core/task/index.js +12 -0
  228. package/build/core/task/model.js +8 -1
  229. package/build/core/task/query.js +172 -0
  230. package/build/core/task/settlement-git.js +265 -74
  231. package/build/core/task/settlement-policy.js +67 -29
  232. package/build/core/task/source-board.js +1 -0
  233. package/build/core/task/task-contract.js +444 -0
  234. package/build/core/{task-doctor.js → task/task-doctor.js} +1 -1
  235. package/build/core/{task-git-actor.js → task/task-git-actor.js} +5 -10
  236. package/build/core/{task-git-runtime.js → task/task-git-runtime.js} +3 -3
  237. package/build/core/{task-git-store.js → task/task-git-store.js} +90 -19
  238. package/build/core/task/task-repository.js +1 -0
  239. package/build/core/{task-store-repository.js → task/task-store-repository.js} +6 -4
  240. package/build/core/task/task-store.js +1 -0
  241. package/build/core/{task-worktree.js → task/task-worktree.js} +2 -1
  242. package/build/core/task/task.js +7 -0
  243. package/build/core/task/tree.js +37 -0
  244. package/build/core/task/validation.js +5 -2
  245. package/build/core/transcripts.js +15 -3
  246. package/build/core/worktree-path.js +43 -28
  247. package/build/flow-error.js +83 -39
  248. package/build/fs/path-coordinate.js +16 -0
  249. package/build/generated/version.js +3 -2
  250. package/build/git/branches.js +2 -1
  251. package/build/git/core.js +106 -2
  252. package/build/git/diff/parsers.js +23 -13
  253. package/build/git/diff/structured.js +127 -0
  254. package/build/git/path-coordinate.js +17 -0
  255. package/build/git/refs.js +57 -0
  256. package/build/git/streaming-batch.js +301 -0
  257. package/build/git/worktree.js +65 -0
  258. package/build/index.js +9 -9
  259. package/build/keiyaku.js +2 -36
  260. package/build/telemetry/logger.js +4 -2
  261. package/package.json +4 -10
  262. package/skills/keiyaku/SKILL.md +5 -4
  263. package/skills/keiyaku-akuma/SKILL.md +25 -11
  264. package/skills/keiyaku-task/SKILL.md +17 -8
  265. package/skills/keiyaku-workflow/SKILL.md +58 -9
  266. package/build/agents/providers/codex-sdk.js +0 -329
  267. package/build/cli/commands/task/view/meta.js +0 -8
  268. package/build/core/command-io.js +0 -174
  269. package/build/core/path-coordinate.js +0 -27
  270. package/build/core/petition-claim.js +0 -117
  271. package/build/core/petition-run.js +0 -83
  272. package/build/core/projection-coordinate.js +0 -63
  273. package/build/core/projection-wait.js +0 -165
  274. package/build/core/projection-wake.js +0 -136
  275. package/build/core/queue.js +0 -343
  276. package/build/core/task-contract.js +0 -237
  277. package/build/core/task.js +0 -4
  278. /package/build/{core/task-repository.js → agents/launch-snapshot/types.js} +0 -0
  279. /package/build/{core/task-store.js → agents/providers/claude-agent-sdk/terms.js} +0 -0
  280. /package/build/core/{projection-generation-runtime.js → projection/generation/projection-generation-runtime.js} +0 -0
  281. /package/build/core/{projection-life-protocol.js → projection/projection-life-protocol.js} +0 -0
  282. /package/build/core/{projection-runner-lock.js → projection/projection-runner-lock.js} +0 -0
@@ -1,19 +1,21 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { isErrnoException } from "../errno.js";
3
+ import { isErrnoException } from "../../errno.js";
4
4
  import { executionEventsPath, isProjectionId, projectionRoot, } from "./projection-core.js";
5
5
  import { readProjectionAlias } from "./projection-alias.js";
6
- import { ProjectionStateError } from "./atomic-publish.js";
7
- import { foldAgentActivity, } from "./projection-activity.js";
6
+ import { ProjectionStateError } from "../atomic-publish.js";
7
+ import { foldPublicProjectionActivity, } from "./projection-activity.js";
8
8
  import { readProjectionIdentity } from "./projection-identity.js";
9
- import { readProjectionTellCounts } from "./projection/tell/store.js";
10
- import { isProjectionGenerationSchemaV1Residue, ProjectionGenerationStoreError, } from "./projection/generation/store.js";
11
- import { PROJECTION_ADOPTION_TIMEOUT_MS } from "./projection-generation-launcher.js";
9
+ import { readProjectionTellCounts } from "./tell/store.js";
10
+ import { ProjectionGenerationStoreError, } from "./generation/store.js";
11
+ import { PROJECTION_ADOPTION_TIMEOUT_MS } from "./generation/projection-generation-launcher.js";
12
12
  import { observeProjectionLifeSnapshot } from "./projection-life-observer.js";
13
- import { selectCurrentGeneration } from "./projection-life-protocol.js";
14
- import { invalidActivityEventDiagnostic, selectValidStoredAgentEvents } from "./stored-agent-event.js";
13
+ import { selectCurrentGeneration, } from "./projection-life-protocol.js";
14
+ import { invalidActivityEventDiagnostic, selectValidStoredAgentEvents } from "../stored-agent-event.js";
15
+ import { worktreeContainsCoordinate } from "../worktree-path.js";
16
+ import { projectTerminalFailure, } from "./projection-terminal-failure.js";
17
+ import { extractTerminalTimestamp } from "./generation/model.js";
15
18
  const MAX_STATUS_FILE_BYTES = 256 * 1024;
16
- export const DEFAULT_PROJECTION_STATUS_ROW_BUDGET = 20;
17
19
  const STATUS_GROUP = {
18
20
  "startup-timeout": 0,
19
21
  lost: 0,
@@ -25,13 +27,15 @@ const STATUS_GROUP = {
25
27
  done: 3,
26
28
  killed: 4,
27
29
  dismissed: 4,
30
+ incompatible: 5,
28
31
  };
29
32
  function isOmissibleProjectionStatusState(state) {
30
33
  return state === "failed"
31
34
  || state === "dead"
32
35
  || state === "done"
33
36
  || state === "killed"
34
- || state === "dismissed";
37
+ || state === "dismissed"
38
+ || state === "incompatible";
35
39
  }
36
40
  function rowSortTime(row) {
37
41
  if (row.activityAtMs !== undefined && Number.isFinite(row.activityAtMs)) {
@@ -63,23 +67,16 @@ export function prioritizeProjectionStatusRows(rows, showAll) {
63
67
  const ordered = [...rows].sort(compareProjectionStatusRows);
64
68
  if (showAll)
65
69
  return { rows: ordered };
66
- const protectedCount = ordered.filter((row) => STATUS_GROUP[row.state] < 2).length;
67
- let terminalBudget = Math.max(0, DEFAULT_PROJECTION_STATUS_ROW_BUDGET - protectedCount);
68
- const visible = [];
70
+ const visible = ordered.filter((row) => !isOmissibleProjectionStatusState(row.state));
69
71
  const byState = {
70
72
  failed: 0,
71
73
  dead: 0,
72
74
  done: 0,
73
75
  killed: 0,
74
76
  dismissed: 0,
77
+ incompatible: 0,
75
78
  };
76
79
  for (const row of ordered) {
77
- if (STATUS_GROUP[row.state] < 2 || terminalBudget > 0) {
78
- visible.push(row);
79
- if (STATUS_GROUP[row.state] >= 2)
80
- terminalBudget -= 1;
81
- continue;
82
- }
83
80
  if (isOmissibleProjectionStatusState(row.state))
84
81
  byState[row.state] += 1;
85
82
  }
@@ -97,10 +94,9 @@ class ProjectionStatusFileError extends Error {
97
94
  }
98
95
  }
99
96
  function oversizedProjectionFileDiagnostic(filePath) {
100
- const label = path.basename(filePath);
101
97
  return {
102
98
  kind: "oversized-projection-file",
103
- detail: `${label} exceeds status read limit · newest complete events retained`,
99
+ fileName: path.basename(filePath),
104
100
  };
105
101
  }
106
102
  function recordProjectionStatusFileError(error, diagnostics) {
@@ -247,84 +243,130 @@ function readStoredEvents(projectionDir, executionId, diagnostics) {
247
243
  }
248
244
  return values;
249
245
  }
250
- function latestValidEventAtMs(events) {
246
+ function latestValidEvent(events) {
251
247
  let latest;
252
248
  for (const event of events) {
253
249
  const atMs = Date.parse(event.at);
254
250
  if (!Number.isFinite(atMs))
255
251
  continue;
256
- if (latest === undefined || atMs > latest)
257
- latest = atMs;
252
+ if (latest === undefined || atMs > latest.atMs)
253
+ latest = { at: event.at, atMs };
258
254
  }
259
255
  return latest;
260
256
  }
261
- function readActivityFacts(projectionDir, executionId, nowMs, diagnostics, fallbackAtMs) {
262
- if (!executionId)
263
- return {};
257
+ function readActivityFacts(projectionDir, executionId, nowMs, diagnostics, fallbackAt) {
258
+ const fallbackAtMs = fallbackAt === undefined ? undefined : Date.parse(fallbackAt);
259
+ const validFallback = fallbackAt !== undefined && Number.isFinite(fallbackAtMs)
260
+ ? { at: fallbackAt, atMs: fallbackAtMs }
261
+ : undefined;
262
+ if (!executionId) {
263
+ return validFallback
264
+ ? { activityAt: validFallback.at, activityAtMs: validFallback.atMs, activityAgeMs: Math.max(0, nowMs - validFallback.atMs) }
265
+ : {};
266
+ }
264
267
  const stored = readStoredEvents(projectionDir, executionId, diagnostics);
265
268
  const { events, malformed } = selectValidStoredAgentEvents(stored);
266
269
  const invalidActivity = invalidActivityEventDiagnostic(malformed);
267
270
  if (invalidActivity)
268
271
  diagnostics.push(invalidActivity);
269
- const activityAtMs = latestValidEventAtMs(events) ?? fallbackAtMs;
272
+ const latest = latestValidEvent(events) ?? validFallback;
270
273
  return {
271
- ...(events.length > 0 ? { activity: foldAgentActivity(events) } : {}),
272
- ...(activityAtMs !== undefined
274
+ ...(events.length > 0 ? { activity: foldPublicProjectionActivity(events) } : {}),
275
+ ...(latest !== undefined
273
276
  ? {
274
- activityAtMs,
275
- activityAgeMs: Math.max(0, nowMs - activityAtMs),
277
+ activityAt: latest.at,
278
+ activityAtMs: latest.atMs,
279
+ activityAgeMs: Math.max(0, nowMs - latest.atMs),
276
280
  }
277
281
  : {}),
278
282
  };
279
283
  }
284
+ function terminalTimestamp(current) {
285
+ return extractTerminalTimestamp(current?.verdict?.facts);
286
+ }
280
287
  function readProjectionRow(projectionDir, id, alias, nowMs) {
281
288
  const diagnostics = [];
289
+ const snapshot = observeProjectionLifeSnapshot(projectionDir, {
290
+ nowMs,
291
+ startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
292
+ });
293
+ if (snapshot.compatibility === "incompatible") {
294
+ return {
295
+ id,
296
+ ...(alias ? { alias } : {}),
297
+ state: "incompatible",
298
+ observation: snapshot,
299
+ tellCounts: emptyTellCounts(),
300
+ diagnostics,
301
+ };
302
+ }
282
303
  const identityFacts = readIdentityFacts(projectionDir, diagnostics);
283
304
  const tellCounts = identityFacts.identity
284
305
  ? readHeartTellCounts(projectionDir, diagnostics)
285
306
  : emptyTellCounts();
286
307
  if (!identityFacts.identity) {
308
+ const diagnostic = diagnostics.map((entry) => entry.detail).find((detail) => detail !== undefined)
309
+ ?? "projection identity could not be observed";
287
310
  return {
288
311
  id,
312
+ ...(alias ? { alias } : {}),
289
313
  state: "unknown",
314
+ observation: {
315
+ compatibility: "current",
316
+ lifecycle: { phase: "unknown", diagnostic },
317
+ },
290
318
  tellCounts,
291
319
  diagnostics,
292
320
  };
293
321
  }
294
- const observation = observeProjectionLifeSnapshot(projectionDir, {
295
- nowMs,
296
- startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
297
- });
298
- const life = observation.life;
299
- const current = observation.records
300
- ? selectCurrentGeneration(observation.records)
322
+ const life = snapshot.life;
323
+ const current = snapshot.records
324
+ ? selectCurrentGeneration(snapshot.records)
301
325
  : null;
302
326
  const adoptedAt = current?.adoption?.facts.adoptedAt;
303
327
  const adoptedAtMs = typeof adoptedAt === "string"
304
328
  ? Date.parse(adoptedAt)
305
329
  : Number.NaN;
306
- const activityFacts = readActivityFacts(projectionDir, life.executionId, nowMs, diagnostics, Number.isFinite(adoptedAtMs) ? adoptedAtMs : undefined);
330
+ const activityFacts = readActivityFacts(projectionDir, life.executionId, nowMs, diagnostics, typeof adoptedAt === "string" && Number.isFinite(adoptedAtMs) ? adoptedAt : identityFacts.mintedAt);
331
+ const mintedAtMs = Date.parse(identityFacts.mintedAt ?? "");
332
+ const terminalAt = terminalTimestamp(current);
333
+ const durationAnchorMs = current?.verdict
334
+ ? (terminalAt === undefined ? Number.NaN : Date.parse(terminalAt))
335
+ : nowMs;
336
+ const durationMs = Number.isFinite(mintedAtMs) && Number.isFinite(durationAnchorMs)
337
+ ? Math.max(0, durationAnchorMs - mintedAtMs)
338
+ : undefined;
339
+ const displayAgeMs = activityFacts.activityAgeMs
340
+ ?? (Number.isFinite(mintedAtMs) ? Math.max(0, nowMs - mintedAtMs) : undefined);
307
341
  if (life.phase === "unknown" && life.diagnostic) {
308
342
  diagnostics.push({ kind: "unknown-lifecycle", detail: life.diagnostic });
309
343
  }
310
344
  const phase = life.phase;
311
345
  const state = phase === "active" || phase === "quiescent" ? "out" : phase;
346
+ const terminalFailure = current?.verdict
347
+ ? projectTerminalFailure(current.verdict.facts.state, current.verdict.facts)
348
+ : undefined;
312
349
  return {
313
350
  id,
314
351
  ...(alias ? { alias } : {}),
315
352
  state,
353
+ observation: { compatibility: "current", lifecycle: life },
316
354
  akuma: identityFacts.akuma,
317
355
  provider: identityFacts.provider,
318
356
  pid: identityFacts.pid,
319
357
  mintedAt: identityFacts.mintedAt,
358
+ ...(terminalAt ? { terminalAt } : {}),
359
+ ...(durationMs !== undefined ? { durationMs } : {}),
320
360
  ...(identityFacts.workspaceRoot ? { workspaceRoot: identityFacts.workspaceRoot } : {}),
321
361
  ...activityFacts,
362
+ ...(displayAgeMs !== undefined ? { activityAgeMs: displayAgeMs } : {}),
363
+ ...(terminalFailure ? { terminalFailure } : {}),
322
364
  tellCounts,
323
365
  diagnostics,
324
366
  };
325
367
  }
326
- export function readProjectionStatusBoard(cwd, nowMs, options = {}) {
327
- const root = projectionRoot(cwd);
368
+ export async function readProjectionStatusBoard(cwd, nowMs, options = {}) {
369
+ const root = await projectionRoot(cwd);
328
370
  try {
329
371
  if (!fs.lstatSync(root).isDirectory())
330
372
  return null;
@@ -349,7 +391,7 @@ export function readProjectionStatusBoard(cwd, nowMs, options = {}) {
349
391
  for (const alias of fs.readdirSync(aliasRoot).sort()) {
350
392
  if (alias.startsWith("."))
351
393
  continue;
352
- const id = readProjectionAlias(cwd, alias);
394
+ const id = await readProjectionAlias(cwd, alias);
353
395
  if (id)
354
396
  aliases.set(id, alias);
355
397
  }
@@ -382,10 +424,86 @@ export function readProjectionStatusBoard(cwd, nowMs, options = {}) {
382
424
  catch {
383
425
  continue;
384
426
  }
385
- if (isProjectionGenerationSchemaV1Residue(dir))
386
- continue;
387
427
  rows.push(readProjectionRow(dir, id, aliases.get(id), nowMs));
388
428
  }
389
429
  }
390
430
  return rows.length > 0 ? prioritizeProjectionStatusRows(rows, options.showAll === true) : null;
391
431
  }
432
+ /**
433
+ * Renew-only lifecycle guard. Immutable identity is checked before lifecycle,
434
+ * so an unrelated or unattributable projection cannot block a commission.
435
+ */
436
+ export async function hasStrandableProjectionGeneration(input) {
437
+ const root = await projectionRoot(input.cwd);
438
+ let akumas;
439
+ try {
440
+ if (!fs.lstatSync(root).isDirectory())
441
+ return false;
442
+ akumas = fs.readdirSync(root);
443
+ }
444
+ catch (error) {
445
+ if (isErrnoException(error) && error.code === "ENOENT")
446
+ return false;
447
+ throw error;
448
+ }
449
+ for (const akuma of akumas.sort()) {
450
+ if (akuma.startsWith("."))
451
+ continue;
452
+ const akumaDir = path.join(root, akuma);
453
+ try {
454
+ if (!fs.lstatSync(akumaDir).isDirectory())
455
+ continue;
456
+ }
457
+ catch {
458
+ continue;
459
+ }
460
+ let hexes;
461
+ try {
462
+ hexes = fs.readdirSync(akumaDir);
463
+ }
464
+ catch {
465
+ continue;
466
+ }
467
+ for (const hex of hexes.sort()) {
468
+ const projectionId = `${akuma}/${hex}`;
469
+ const projectionDir = path.join(akumaDir, hex);
470
+ if (!isProjectionId(projectionId))
471
+ continue;
472
+ try {
473
+ if (!fs.lstatSync(projectionDir).isDirectory())
474
+ continue;
475
+ }
476
+ catch {
477
+ continue;
478
+ }
479
+ let identity;
480
+ try {
481
+ identity = readProjectionIdentity(projectionDir);
482
+ }
483
+ catch {
484
+ // Corrupt identity has no trustworthy commission/worktree join and
485
+ // therefore cannot become a repository-wide renew interlock.
486
+ continue;
487
+ }
488
+ if (identity.binding.kind !== "commission"
489
+ || identity.binding.commissionId !== input.commissionId
490
+ || !worktreeContainsCoordinate(input.deliveryWorktree, identity.workspaceRoot)) {
491
+ continue;
492
+ }
493
+ const observation = observeProjectionLifeSnapshot(projectionDir, {
494
+ nowMs: input.nowMs,
495
+ startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
496
+ });
497
+ if (observation.compatibility !== "current")
498
+ continue;
499
+ const phase = observation.life.phase;
500
+ if (phase === "active"
501
+ || phase === "minting"
502
+ || phase === "startup-timeout"
503
+ || phase === "unknown") {
504
+ return true;
505
+ }
506
+ }
507
+ }
508
+ return false;
509
+ }
@@ -0,0 +1,106 @@
1
+ import { constants as osConstants } from "node:os";
2
+ import { RESPONSE_ARTIFACT_ID_RE } from "../response-artifact-id.js";
3
+ import { OUTCOME_ERROR_VERSION } from "../../agents/types.js";
4
+ const HARNESS_FAILURE_CODES = new Set([
5
+ "SUBAGENT_TIMEOUT",
6
+ "SUBAGENT_TRANSPORT_ERROR",
7
+ "SUBAGENT_POLICY_BUDGET",
8
+ "SUBAGENT_NON_ZERO_EXIT",
9
+ "SUBAGENT_SPAWN_ERROR",
10
+ "SUBAGENT_EXEC_ERROR",
11
+ "SUBAGENT_PROVIDER_UNAVAILABLE",
12
+ ]);
13
+ const SYSTEM_SIGNALS = new Set(Object.keys(osConstants.signals));
14
+ function objectValue(value) {
15
+ return value !== null && typeof value === "object" && !Array.isArray(value)
16
+ ? value
17
+ : undefined;
18
+ }
19
+ function failureCode(value) {
20
+ return typeof value === "string" && HARNESS_FAILURE_CODES.has(value)
21
+ ? value
22
+ : undefined;
23
+ }
24
+ function nonNegativeNumber(value) {
25
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0
26
+ ? value
27
+ : undefined;
28
+ }
29
+ function exitCode(value) {
30
+ return typeof value === "number" && Number.isSafeInteger(value) ? value : undefined;
31
+ }
32
+ function signal(value) {
33
+ return typeof value === "string" && SYSTEM_SIGNALS.has(value)
34
+ ? value
35
+ : undefined;
36
+ }
37
+ function artifactId(source) {
38
+ for (const value of [source.artifactId, source.sourceArtifactId]) {
39
+ if (typeof value === "string" && RESPONSE_ARTIFACT_ID_RE.test(value))
40
+ return value;
41
+ }
42
+ return undefined;
43
+ }
44
+ function providerFailureCause(value) {
45
+ const source = objectValue(value);
46
+ return source?.kind === "rate_limit_exhausted"
47
+ ? { kind: "rate_limit_exhausted" }
48
+ : undefined;
49
+ }
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
+ */
55
+ export function projectTerminalFailure(state, facts) {
56
+ if (state !== "failed" && state !== "lost-runner" && state !== "launch-failed")
57
+ return undefined;
58
+ const source = objectValue(facts);
59
+ if (!source)
60
+ return undefined;
61
+ const error = objectValue(source.error);
62
+ const publicArtifactId = artifactId(source);
63
+ // Runner verdict authority wins classification even when harness evidence is
64
+ // also attached to the verdict.
65
+ if (state === "launch-failed" || state === "lost-runner") {
66
+ const phase = state === "lost-runner"
67
+ ? "adoption"
68
+ : source.stage === "spawn" ? "launch" : "adoption";
69
+ const code = phase === "launch"
70
+ ? "SUBAGENT_SPAWN_ERROR"
71
+ : "SUBAGENT_EXEC_ERROR";
72
+ const timeoutMs = nonNegativeNumber(error?.timeoutMs ?? source.timeoutMs);
73
+ const publicExitCode = exitCode(error?.exitCode ?? source.exitCode);
74
+ const publicSignal = signal(error?.signal ?? source.signal);
75
+ return {
76
+ class: "runner",
77
+ code,
78
+ phase,
79
+ ...(timeoutMs !== undefined ? { timeoutMs } : {}),
80
+ ...(publicExitCode !== undefined ? { exitCode: publicExitCode } : {}),
81
+ ...(publicSignal ? { signal: publicSignal } : {}),
82
+ ...(publicArtifactId ? { artifactId: publicArtifactId } : {}),
83
+ };
84
+ }
85
+ if (error?.version !== OUTCOME_ERROR_VERSION)
86
+ return undefined;
87
+ const publicSignal = signal(error?.signal ?? source.signal);
88
+ const publicExitCode = exitCode(error?.exitCode ?? source.exitCode);
89
+ const code = failureCode(error?.code ?? source.code)
90
+ ?? (publicSignal ? "SUBAGENT_EXEC_ERROR" : undefined)
91
+ ?? (publicExitCode !== undefined ? "SUBAGENT_NON_ZERO_EXIT" : undefined);
92
+ if (!code || code === "SUBAGENT_SPAWN_ERROR")
93
+ return undefined;
94
+ const timeoutMs = nonNegativeNumber(error?.timeoutMs ?? source.timeoutMs);
95
+ const publicCause = providerFailureCause(error?.publicCause);
96
+ return {
97
+ class: "harness",
98
+ code,
99
+ phase: source.phase === "adoption" ? "adoption" : "runtime",
100
+ ...(timeoutMs !== undefined ? { timeoutMs } : {}),
101
+ ...(publicExitCode !== undefined ? { exitCode: publicExitCode } : {}),
102
+ ...(publicSignal ? { signal: publicSignal } : {}),
103
+ ...(publicArtifactId ? { artifactId: publicArtifactId } : {}),
104
+ ...(publicCause ? { publicCause } : {}),
105
+ };
106
+ }