@astrosheep/keiyaku 2.9.2 → 2.9.4

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 (238) hide show
  1. package/build/.tsbuildinfo +1 -1
  2. package/build/agents/call-terms.js +13 -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 +25 -222
  6. package/build/agents/harness/index.js +3 -1
  7. package/build/agents/harness/outcome.js +47 -4
  8. package/build/agents/harness/projection.js +42 -59
  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 +50 -53
  21. package/build/agents/providers/codex-app-server/events.js +67 -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/registration.js +145 -0
  36. package/build/agents/providers/pi/session-schema.js +6 -0
  37. package/build/agents/providers/pi/terms.js +1 -0
  38. package/build/agents/providers/registration.js +7 -0
  39. package/build/agents/providers/registry.js +76 -0
  40. package/build/agents/selector.js +2 -29
  41. package/build/agents/types.js +8 -0
  42. package/build/artifact-directories.js +22 -0
  43. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  44. package/build/cli/commands/akuma/view/handler.js +5 -4
  45. package/build/cli/commands/akuma.js +6 -0
  46. package/build/cli/commands/contract/amend/handler.js +8 -2
  47. package/build/cli/commands/contract/arc/handler.js +7 -1
  48. package/build/cli/commands/contract/bind/handler.js +4 -3
  49. package/build/cli/commands/contract/bind/meta.js +34 -7
  50. package/build/cli/commands/contract/forfeit/handler.js +4 -4
  51. package/build/cli/commands/contract/log/handler.js +3 -3
  52. package/build/cli/commands/contract/petition/handler.js +4 -6
  53. package/build/cli/commands/contract/renew/handler.js +20 -6
  54. package/build/cli/commands/contract/renew/meta.js +5 -4
  55. package/build/cli/commands/metadata.js +58 -0
  56. package/build/cli/commands/projection/call/handler.js +6 -5
  57. package/build/cli/commands/projection/kill/handler.js +16 -32
  58. package/build/cli/commands/projection/shared.js +28 -0
  59. package/build/cli/commands/projection/status/handler.js +1 -1
  60. package/build/cli/commands/projection/tell/handler.js +14 -30
  61. package/build/cli/commands/projection/wait/handler.js +45 -22
  62. package/build/cli/commands/projection/wait/meta.js +7 -2
  63. package/build/cli/commands/registry.js +2 -54
  64. package/build/cli/commands/shared.js +10 -7
  65. package/build/cli/commands/skills/install/handler.js +2 -5
  66. package/build/cli/commands/skills/skills/handler.js +1 -1
  67. package/build/cli/commands/task/add/handler.js +5 -13
  68. package/build/cli/commands/task/catalog.js +2 -2
  69. package/build/cli/commands/task/done/handler.js +4 -8
  70. package/build/cli/commands/task/drop/handler.js +3 -6
  71. package/build/cli/commands/task/ls/handler.js +3 -5
  72. package/build/cli/commands/task/shared.js +14 -4
  73. package/build/cli/commands/task/{view → show}/handler.js +7 -8
  74. package/build/cli/commands/task/show/meta.js +8 -0
  75. package/build/cli/commands/task/start/handler.js +3 -6
  76. package/build/cli/commands/task/stop/handler.js +4 -8
  77. package/build/cli/commands/task/task/handler.js +1 -1
  78. package/build/cli/commands/task/update/handler.js +4 -8
  79. package/build/cli/completion.js +1 -1
  80. package/build/cli/flags.js +19 -3
  81. package/build/cli/help.js +4 -11
  82. package/build/cli/index.js +57 -83
  83. package/build/cli/missing-subcommand.js +7 -0
  84. package/build/cli/parse-flags.js +14 -5
  85. package/build/cli/parse-metadata.js +3 -3
  86. package/build/cli/parse-selectors.js +1 -1
  87. package/build/cli/parse.js +21 -0
  88. package/build/cli/projection-address.js +7 -12
  89. package/build/cli/render/address.js +41 -37
  90. package/build/cli/render/arc.js +24 -12
  91. package/build/cli/render/call.js +11 -5
  92. package/build/cli/render/errors.js +36 -1
  93. package/build/cli/render/format.js +4 -0
  94. package/build/cli/render/kanshi.js +6 -12
  95. package/build/cli/render/kill.js +9 -0
  96. package/build/cli/render/misc.js +55 -12
  97. package/build/cli/render/petition.js +20 -15
  98. package/build/cli/render/projection-activity.js +3 -0
  99. package/build/cli/render/shared.js +60 -21
  100. package/build/cli/render/skills.js +10 -0
  101. package/build/cli/render/status.js +37 -14
  102. package/build/cli/render/success-response.js +77 -0
  103. package/build/cli/render/task.js +54 -14
  104. package/build/cli/render/tell.js +5 -0
  105. package/build/cli/render/terminal-failure.js +58 -0
  106. package/build/cli/render/types.js +1 -0
  107. package/build/cli/render/wait.js +38 -36
  108. package/build/cli/types.js +1 -1
  109. package/build/config/akuma-loader.js +0 -5
  110. package/build/config/env-keys.js +1 -1
  111. package/build/config/provider-policy-ownership.js +3 -32
  112. package/build/config/settings/disease.js +8 -1
  113. package/build/config/settings/loader.js +11 -3
  114. package/build/config/settings/provider-instance-fields.js +51 -0
  115. package/build/config/settings/schema.js +6 -176
  116. package/build/core/addressing.js +66 -16
  117. package/build/core/amend.js +135 -18
  118. package/build/core/arc.js +19 -67
  119. package/build/core/bind.js +82 -35
  120. package/build/core/call/call.js +15 -9
  121. package/build/core/call/context.js +6 -51
  122. package/build/core/call/execution.js +21 -22
  123. package/build/core/call/prompt.js +14 -1
  124. package/build/core/command-markdown.js +45 -0
  125. package/build/core/context.js +13 -7
  126. package/build/core/contract-view.js +20 -3
  127. package/build/core/draft.js +123 -16
  128. package/build/core/forfeit.js +5 -2
  129. package/build/core/hints.js +1 -75
  130. package/build/core/identifier-slug.js +66 -0
  131. package/build/core/ids.js +25 -11
  132. package/build/core/ledger.js +20 -68
  133. package/build/core/markdown/parser.js +0 -68
  134. package/build/core/markdown/render.js +68 -0
  135. package/build/core/markdown/sections.js +2 -1
  136. package/build/core/outcome-base.js +4 -0
  137. package/build/core/projection/generation/database.js +2 -38
  138. package/build/core/projection/generation/model.js +42 -1
  139. package/build/core/{projection-generation-continuation.js → projection/generation/projection-generation-continuation.js} +3 -3
  140. package/build/core/{projection-generation-execution.js → projection/generation/projection-generation-execution.js} +19 -49
  141. package/build/core/{projection-generation-identity.js → projection/generation/projection-generation-identity.js} +2 -2
  142. package/build/core/{projection-generation-launcher.js → projection/generation/projection-generation-launcher.js} +5 -3
  143. package/build/core/{projection-generation-process.js → projection/generation/projection-generation-process.js} +2 -1
  144. package/build/core/{projection-generation-runner.js → projection/generation/projection-generation-runner.js} +18 -3
  145. package/build/core/projection/generation/store.js +2 -2
  146. package/build/core/projection/generation/transitions.js +1 -1
  147. package/build/core/projection/index.js +27 -0
  148. package/build/core/{projection-activity.js → projection/projection-activity.js} +12 -0
  149. package/build/core/{projection-alias.js → projection/projection-alias.js} +14 -19
  150. package/build/core/projection/projection-coordinate.js +61 -0
  151. package/build/core/{projection-core.js → projection/projection-core.js} +9 -9
  152. package/build/core/{projection-execution-observer.js → projection/projection-execution-observer.js} +2 -57
  153. package/build/core/{projection-identity.js → projection/projection-identity.js} +1 -1
  154. package/build/core/{projection-kill.js → projection/projection-kill.js} +1 -1
  155. package/build/core/{projection-life-observer.js → projection/projection-life-observer.js} +1 -1
  156. package/build/core/{projection-mint.js → projection/projection-mint.js} +10 -10
  157. package/build/core/{projection-status.js → projection/projection-status.js} +98 -25
  158. package/build/core/projection/projection-terminal-failure.js +104 -0
  159. package/build/core/projection/projection-wait.js +278 -0
  160. package/build/core/projection/projection-wake.js +203 -0
  161. package/build/core/provider-loader-ports.js +4 -0
  162. package/build/core/ref-log.js +65 -40
  163. package/build/core/registry.js +63 -10
  164. package/build/core/renew-session.js +128 -0
  165. package/build/core/renew.js +426 -99
  166. package/build/core/scope.js +143 -66
  167. package/build/core/seal.js +139 -35
  168. package/build/core/{claim-delivery.js → settlement/claim-delivery.js} +9 -14
  169. package/build/core/{claim.js → settlement/claim.js} +32 -8
  170. package/build/core/settlement/index.js +19 -0
  171. package/build/core/{petition-claim-gates.js → settlement/petition-claim-gates.js} +22 -18
  172. package/build/core/{petition-forfeit.js → settlement/petition-forfeit.js} +2 -2
  173. package/build/core/{petition-head-guard.js → settlement/petition-head-guard.js} +2 -2
  174. package/build/core/settlement/petition-io.js +1 -0
  175. package/build/core/{petition-preview.js → settlement/petition-preview.js} +1 -1
  176. package/build/core/{petition.js → settlement/petition.js} +12 -10
  177. package/build/core/settlement/queue-read-model.js +143 -0
  178. package/build/core/settlement/queue-seat-allocation.js +156 -0
  179. package/build/core/settlement/queue.js +6 -0
  180. package/build/core/settlement/settlement.js +295 -0
  181. package/build/core/{verdict.js → settlement/verdict.js} +9 -37
  182. package/build/core/status/board.js +24 -23
  183. package/build/core/status/ledger-batch.js +158 -0
  184. package/build/core/status/reconciliation.js +10 -4
  185. package/build/core/stored-agent-event.js +3 -0
  186. package/build/core/task/board.js +40 -4
  187. package/build/core/task/commands.js +30 -21
  188. package/build/core/task/index.js +12 -0
  189. package/build/core/task/settlement-git.js +157 -65
  190. package/build/core/task/settlement-policy.js +54 -24
  191. package/build/core/task/task-contract.js +370 -0
  192. package/build/core/{task-doctor.js → task/task-doctor.js} +1 -1
  193. package/build/core/{task-git-actor.js → task/task-git-actor.js} +5 -10
  194. package/build/core/{task-git-runtime.js → task/task-git-runtime.js} +3 -3
  195. package/build/core/{task-git-store.js → task/task-git-store.js} +92 -29
  196. package/build/core/task/task-repository.js +1 -0
  197. package/build/core/{task-store-repository.js → task/task-store-repository.js} +5 -4
  198. package/build/core/task/task-store.js +1 -0
  199. package/build/core/{task-worktree.js → task/task-worktree.js} +2 -1
  200. package/build/core/task/task.js +5 -0
  201. package/build/core/task/tree.js +37 -0
  202. package/build/core/task/validation.js +5 -2
  203. package/build/core/transcripts.js +15 -3
  204. package/build/core/worktree-path.js +43 -28
  205. package/build/flow-error.js +85 -39
  206. package/build/fs/durability.js +27 -0
  207. package/build/fs/path-coordinate.js +16 -0
  208. package/build/generated/version.js +3 -2
  209. package/build/git/branches.js +2 -1
  210. package/build/git/core.js +106 -2
  211. package/build/git/path-coordinate.js +17 -0
  212. package/build/git/refs.js +57 -0
  213. package/build/git/streaming-batch.js +301 -0
  214. package/build/git/worktree.js +65 -0
  215. package/build/index.js +3 -3
  216. package/build/keiyaku.js +2 -36
  217. package/package.json +4 -10
  218. package/skills/keiyaku/SKILL.md +3 -2
  219. package/skills/keiyaku-akuma/SKILL.md +10 -6
  220. package/skills/keiyaku-task/SKILL.md +1 -1
  221. package/skills/keiyaku-workflow/SKILL.md +25 -4
  222. package/build/agents/providers/codex-sdk.js +0 -329
  223. package/build/cli/commands/task/view/meta.js +0 -8
  224. package/build/core/command-io.js +0 -174
  225. package/build/core/path-coordinate.js +0 -27
  226. package/build/core/petition-claim.js +0 -117
  227. package/build/core/petition-run.js +0 -83
  228. package/build/core/projection-coordinate.js +0 -63
  229. package/build/core/projection-wait.js +0 -165
  230. package/build/core/projection-wake.js +0 -136
  231. package/build/core/queue.js +0 -343
  232. package/build/core/task-contract.js +0 -237
  233. package/build/core/task.js +0 -4
  234. /package/build/{core/task-repository.js → agents/launch-snapshot/types.js} +0 -0
  235. /package/build/{core/task-store.js → agents/providers/claude-agent-sdk/terms.js} +0 -0
  236. /package/build/core/{projection-generation-runtime.js → projection/generation/projection-generation-runtime.js} +0 -0
  237. /package/build/core/{projection-life-protocol.js → projection/projection-life-protocol.js} +0 -0
  238. /package/build/core/{projection-runner-lock.js → projection/projection-runner-lock.js} +0 -0
@@ -0,0 +1,61 @@
1
+ import * as path from "node:path";
2
+ import { ProjectionStateError } from "../atomic-publish.js";
3
+ import { createGit, isGitExecutableUnavailable, NOT_GIT_REPOSITORY_PATTERNS, } from "../../git/core.js";
4
+ import { normalizeFilesystemCoordinate } from "../../fs/path-coordinate.js";
5
+ import { gitCommonRootPath, resolveGitCommonRoot } from "../../git/path-coordinate.js";
6
+ /**
7
+ * Resolve projection storage root and authority from one Git probe.
8
+ *
9
+ * A non-bare repository uses the parent of its absolute common Git directory,
10
+ * so every linked worktree shares one coordinate. Repo-free callers retain the
11
+ * existing cwd-local authority until its replacement is legislated.
12
+ */
13
+ export async function resolveProjectionCoordinate(cwd) {
14
+ cwd = normalizeFilesystemCoordinate(cwd);
15
+ const git = createGit(cwd);
16
+ let response;
17
+ try {
18
+ response = await git.raw([
19
+ "rev-parse",
20
+ "--is-bare-repository",
21
+ "--path-format=absolute",
22
+ "--git-common-dir",
23
+ ]);
24
+ }
25
+ catch (error) {
26
+ if (isGitExecutableUnavailable(error)) {
27
+ return { physicalRoot: cwd, authority: { kind: "user" } };
28
+ }
29
+ const message = error instanceof Error ? error.message : String(error);
30
+ if (NOT_GIT_REPOSITORY_PATTERNS.some((pattern) => message.includes(pattern))) {
31
+ return { physicalRoot: cwd, authority: { kind: "user" } };
32
+ }
33
+ throw new ProjectionStateError(`cannot resolve projection repository coordinate: ${message}`);
34
+ }
35
+ const [bare, commonDir] = response.trim().split(/\r?\n/);
36
+ const stableRoot = commonDir === undefined ? undefined : gitCommonRootPath(commonDir);
37
+ if (bare !== "false" || !commonDir || !stableRoot || !path.isAbsolute(stableRoot)) {
38
+ return { physicalRoot: cwd, authority: { kind: "user" } };
39
+ }
40
+ let physicalRoot;
41
+ try {
42
+ physicalRoot = await resolveGitCommonRoot(commonDir);
43
+ }
44
+ catch (error) {
45
+ throw new ProjectionStateError(`cannot resolve projection repository root ${stableRoot}`, { cause: error });
46
+ }
47
+ return {
48
+ physicalRoot,
49
+ authority: { kind: "repository", stableRoot: physicalRoot },
50
+ };
51
+ }
52
+ /**
53
+ * Select the filesystem authority for repository projection state.
54
+ *
55
+ * A non-bare repository uses the parent of its absolute common Git directory,
56
+ * so every linked worktree shares one coordinate. Repo-free callers retain the
57
+ * existing cwd-local authority until its replacement is legislated.
58
+ */
59
+ export async function projectionCoordinateRoot(cwd) {
60
+ return (await resolveProjectionCoordinate(cwd)).physicalRoot;
61
+ }
@@ -1,25 +1,25 @@
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 { projectionCoordinateRoot } from "./projection-coordinate.js";
5
- import { ProjectionStateError, } from "./atomic-publish.js";
6
- export { atomicPublishFile, ProjectionStateError, randomHex8 } from "./atomic-publish.js";
7
- export { isTellCausallyConsumed, } from "./projection/tell/model.js";
8
- export { claimAllInbox, claimFencedTells, claimTellToInflight, demoteSubmittedTellsForReplay, listTellIds, markTellConsumed, markTellSubmitted, readTellOriginal, readTellSubmitted, snapshotPendingTells, writeInboxTell, } from "./projection/tell/store.js";
5
+ import { ProjectionStateError, } from "../atomic-publish.js";
6
+ export { atomicPublishFile, ProjectionStateError, randomHex8 } from "../atomic-publish.js";
7
+ export { isTellCausallyConsumed, } from "./tell/model.js";
8
+ export { claimAllInbox, claimFencedTells, claimTellToInflight, demoteSubmittedTellsForReplay, listTellIds, markTellConsumed, markTellSubmitted, readTellOriginal, readTellSubmitted, snapshotPendingTells, writeInboxTell, } from "./tell/store.js";
9
9
  /** Relative segments for `.keiyaku/projection`. */
10
10
  export const PROJECTION_ROOT_SEGMENTS = [".keiyaku", "projection"];
11
11
  export const PROJECTION_ID_PATTERN = /^[a-zA-Z0-9]+(?:[.-][a-zA-Z0-9]+)*\/[0-9a-f]{8}$/;
12
12
  export function isProjectionId(value) {
13
13
  return PROJECTION_ID_PATTERN.test(value);
14
14
  }
15
- export function projectionRoot(cwd) {
16
- return path.join(projectionCoordinateRoot(cwd), ...PROJECTION_ROOT_SEGMENTS);
15
+ export async function projectionRoot(cwd) {
16
+ return path.join(await projectionCoordinateRoot(cwd), ...PROJECTION_ROOT_SEGMENTS);
17
17
  }
18
- export function projectionDir(cwd, id) {
18
+ export async function projectionDir(cwd, id) {
19
19
  if (!isProjectionId(id))
20
20
  throw new ProjectionStateError(`invalid projection id: ${id}`);
21
21
  const [akuma, hex] = id.split("/");
22
- return path.join(projectionRoot(cwd), akuma, hex);
22
+ return path.join(await projectionRoot(cwd), akuma, hex);
23
23
  }
24
24
  export function encodeProjectionJson(value) {
25
25
  return `${JSON.stringify(value)}\n`;
@@ -1,59 +1,5 @@
1
- import { claimAllInbox, claimFencedTells, listTellIds, markTellSubmitted, markTellConsumed, readTellSubmitted, readTellOriginal, isTellCausallyConsumed, appendGenerationEvent, } from "./projection-core.js";
2
- import { openProjectionTellObserver, } from "./projection/tell/database.js";
3
- function frameTellBodies(projectionDirectory, tellIds) {
4
- const originals = tellIds.map((tellId) => readTellOriginal(projectionDirectory, "inflight", tellId));
5
- const frames = originals
6
- .map((original, index) => `--- tell ${tellIds[index]} ---\n${original.text}`)
7
- .join("\n\n");
8
- const effort = originals.reduce((value, original) => original.effort ?? value, undefined);
9
- return { frames, ...(effort ? { effort } : {}) };
10
- }
11
- function operatorBodyForContinuation(prompt, tellFrames, sessionContinuationPromptPrefix) {
12
- return sessionContinuationPromptPrefix === undefined
13
- ? `${prompt}\n\n${tellFrames}`
14
- : `${sessionContinuationPromptPrefix}${tellFrames}`;
15
- }
16
- /** Tier-2 boundary snapshot. No tells leaves prompt byte-identical. */
17
- export function snapshotReplayPrompt(projectionDirectory, prompt, tellFence, executionId, options = {}) {
18
- const tellIds = claimFencedTells(projectionDirectory, tellFence, executionId);
19
- if (tellIds.length === 0)
20
- return { prompt, tellIds };
21
- const { frames, effort } = frameTellBodies(projectionDirectory, tellIds);
22
- const nextPrompt = operatorBodyForContinuation(prompt, frames, options.sessionContinuationPromptPrefix);
23
- return { prompt: nextPrompt, tellIds, ...(effort ? { effort } : {}) };
24
- }
25
- /** Tier-2 has no steer acknowledgement: actual driver start is its ordinal-zero submission point. */
26
- export function markReplayStarted(projectionDirectory, executionId, tellIds) {
27
- for (const tellId of tellIds) {
28
- if (listTellIds(projectionDirectory, "inflight").includes(tellId)) {
29
- markTellSubmitted(projectionDirectory, tellId, { executionId, fence: { kind: "ordinal", ordinal: 0 } });
30
- }
31
- }
32
- }
33
- /** A successful replay terminal is checkpoint ordinal one; failures intentionally do not consume. */
34
- export function markReplayCompleted(projectionDirectory, executionId) {
35
- for (const tellId of listTellIds(projectionDirectory, "submitted")) {
36
- const submitted = readTellSubmitted(projectionDirectory, tellId);
37
- if (submitted.executionId === executionId && submitted.fence && isTellCausallyConsumed(submitted.fence, { kind: "ordinal", ordinal: 1 })) {
38
- markTellConsumed(projectionDirectory, tellId);
39
- }
40
- }
41
- }
42
- /**
43
- * Tier-2's only submission acknowledgement. A created driver is deliberately
44
- * insufficient: observe the first provider event, or a successful completion
45
- * from a provider that emitted no events.
46
- */
47
- export function createReplayRunStartGate(projectionDirectory, executionId, tellIds) {
48
- let started = false;
49
- const start = () => {
50
- if (started)
51
- return;
52
- markReplayStarted(projectionDirectory, executionId, tellIds);
53
- started = true;
54
- };
55
- return { observeProviderEvent: start, observeSuccessfulCompletion: start };
56
- }
1
+ import { claimAllInbox, listTellIds, markTellSubmitted, markTellConsumed, readTellSubmitted, readTellOriginal, isTellCausallyConsumed, appendGenerationEvent, } from "./projection-core.js";
2
+ import { openProjectionTellObserver, } from "./tell/database.js";
57
3
  const LIVE_TELL_DELIVERY_THROTTLE_MS = 200;
58
4
  function submitEligible(projectionDirectory, executionId, control, requireInbox, submitting, observer, pendingCompletionEvidence) {
59
5
  if (requireInbox && !observer?.hasInbox())
@@ -232,7 +178,6 @@ export function createProjectionExecutionObserver(input, dependencies = {}) {
232
178
  let closed = false;
233
179
  const observer = {
234
180
  onEvent(event) {
235
- input.replayStartGate?.observeProviderEvent();
236
181
  appendEvent(input.projectionDirectory, input.executionId, event);
237
182
  },
238
183
  onCheckpoint(control, checkpoint) {
@@ -1,4 +1,4 @@
1
- import { openProjectionGenerationStoreReadOnly, } from "./projection/generation/store.js";
1
+ import { openProjectionGenerationStoreReadOnly, } from "./generation/store.js";
2
2
  /** Read the immutable projection identity from the heart database. */
3
3
  export function readProjectionIdentity(projectionDirectory) {
4
4
  const heart = openProjectionGenerationStoreReadOnly(projectionDirectory);
@@ -1,4 +1,4 @@
1
- import { openProjectionGenerationStore } from "./projection/generation/store.js";
1
+ import { openProjectionGenerationStore } from "./generation/store.js";
2
2
  import { observeProjectionRunnerLock } from "./projection-runner-lock.js";
3
3
  function settleReleasedLeash(store, projectionDirectory, executionId) {
4
4
  const observation = observeProjectionRunnerLock(projectionDirectory);
@@ -1,4 +1,4 @@
1
- import { openProjectionGenerationStoreReadOnly, } from "./projection/generation/store.js";
1
+ import { openProjectionGenerationStoreReadOnly, } from "./generation/store.js";
2
2
  import { deriveProjectionLife, selectCurrentGeneration, } from "./projection-life-protocol.js";
3
3
  import { observeProjectionRunnerLock } from "./projection-runner-lock.js";
4
4
  function errorDetail(error) {
@@ -1,10 +1,10 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { isErrnoException } from "../errno.js";
4
- import { ProjectionStateError, randomHex8 } from "./atomic-publish.js";
3
+ import { isErrnoException } from "../../errno.js";
4
+ import { ProjectionStateError, randomHex8 } from "../atomic-publish.js";
5
5
  import { isoFromNowMs, PROJECTION_ROOT_SEGMENTS, projectionDir, projectionRoot, } from "./projection-core.js";
6
- import { parseExecutionAuthoritySelection, parseExecutionPact, parseExecutionPactFields, } from "./execution-pact.js";
7
- import { openProjectionGenerationStore } from "./projection/generation/store.js";
6
+ import { parseExecutionAuthoritySelection, parseExecutionPact, parseExecutionPactFields, } from "../execution-pact.js";
7
+ import { openProjectionGenerationStore } from "./generation/store.js";
8
8
  import { initializeProjectionRunnerLockDatabase } from "./projection-runner-lock.js";
9
9
  export class ProjectionMintCollisionError extends Error {
10
10
  code = "PROJECTION_MINT_COLLISION";
@@ -38,7 +38,7 @@ function removeEmptyAkumaRoot(dir) {
38
38
  * Validate mint inputs that can fail, then reserve one private staging directory.
39
39
  * Dot-prefixed staging is never a published projection address.
40
40
  */
41
- function reserveProjectionDirectory(input) {
41
+ async function reserveProjectionDirectory(input) {
42
42
  const slug = assertMintSlug(input.slug);
43
43
  if (!Number.isInteger(input.pid) || input.pid < 0) {
44
44
  throw new ProjectionStateError("projection pid must be a non-negative integer");
@@ -50,7 +50,7 @@ function reserveProjectionDirectory(input) {
50
50
  if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {
51
51
  throw new ProjectionStateError("maxAttempts must be a positive integer");
52
52
  }
53
- const root = projectionRoot(input.cwd);
53
+ const root = await projectionRoot(input.cwd);
54
54
  fs.mkdirSync(root, { recursive: true });
55
55
  const akumaRoot = path.join(root, slug);
56
56
  const createdAkumaRoot = !fs.existsSync(akumaRoot);
@@ -63,7 +63,7 @@ function reserveProjectionDirectory(input) {
63
63
  throw new ProjectionStateError(`projection id random segment must be 8 hex chars, got ${hex}`);
64
64
  }
65
65
  const id = `${slug}/${hex}`;
66
- const dir = projectionDir(input.cwd, id);
66
+ const dir = await projectionDir(input.cwd, id);
67
67
  const stagingDir = path.join(akumaRoot, `.mint-${hex}`);
68
68
  if (fs.existsSync(dir))
69
69
  continue;
@@ -132,7 +132,7 @@ function publishReservedProjection(pact, reserved) {
132
132
  * Mint a user-authority projection directory.
133
133
  * Order (closed): validate → reserve staging → heart + leash → atomic rename.
134
134
  */
135
- export function mintProjection(input) {
135
+ export async function mintProjection(input) {
136
136
  const authority = parseExecutionAuthoritySelection(input.authority, "projection identity", "user");
137
137
  const fields = parseExecutionPactFields(buildMintPactFields(input));
138
138
  const pact = parseExecutionPact({
@@ -146,7 +146,7 @@ export function mintProjection(input) {
146
146
  workspaceRoot: fields.workspaceRoot,
147
147
  ...(fields.effectivePolicy !== undefined ? { effectivePolicy: fields.effectivePolicy } : {}),
148
148
  });
149
- const reserved = reserveProjectionDirectory(input);
149
+ const reserved = await reserveProjectionDirectory(input);
150
150
  return publishReservedProjection(pact, reserved);
151
151
  }
152
152
  /**
@@ -156,7 +156,7 @@ export function mintProjection(input) {
156
156
  export async function mintRepositoryProjection(input) {
157
157
  parseExecutionAuthoritySelection(input.authority, "projection identity", "repository");
158
158
  const fields = parseExecutionPactFields(buildMintPactFields(input));
159
- const reserved = reserveProjectionDirectory(input);
159
+ const reserved = await reserveProjectionDirectory(input);
160
160
  const pact = parseExecutionPact({
161
161
  version: fields.version,
162
162
  pid: fields.pid,
@@ -1,19 +1,20 @@
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
13
  import { selectCurrentGeneration } from "./projection-life-protocol.js";
14
- import { invalidActivityEventDiagnostic, selectValidStoredAgentEvents } from "./stored-agent-event.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";
15
17
  const MAX_STATUS_FILE_BYTES = 256 * 1024;
16
- export const DEFAULT_PROJECTION_STATUS_ROW_BUDGET = 20;
17
18
  const STATUS_GROUP = {
18
19
  "startup-timeout": 0,
19
20
  lost: 0,
@@ -63,9 +64,7 @@ export function prioritizeProjectionStatusRows(rows, showAll) {
63
64
  const ordered = [...rows].sort(compareProjectionStatusRows);
64
65
  if (showAll)
65
66
  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 = [];
67
+ const visible = ordered.filter((row) => !isOmissibleProjectionStatusState(row.state));
69
68
  const byState = {
70
69
  failed: 0,
71
70
  dead: 0,
@@ -74,12 +73,6 @@ export function prioritizeProjectionStatusRows(rows, showAll) {
74
73
  dismissed: 0,
75
74
  };
76
75
  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
76
  if (isOmissibleProjectionStatusState(row.state))
84
77
  byState[row.state] += 1;
85
78
  }
@@ -97,10 +90,9 @@ class ProjectionStatusFileError extends Error {
97
90
  }
98
91
  }
99
92
  function oversizedProjectionFileDiagnostic(filePath) {
100
- const label = path.basename(filePath);
101
93
  return {
102
94
  kind: "oversized-projection-file",
103
- detail: `${label} exceeds status read limit · newest complete events retained`,
95
+ fileName: path.basename(filePath),
104
96
  };
105
97
  }
106
98
  function recordProjectionStatusFileError(error, diagnostics) {
@@ -268,7 +260,7 @@ function readActivityFacts(projectionDir, executionId, nowMs, diagnostics, fallb
268
260
  diagnostics.push(invalidActivity);
269
261
  const activityAtMs = latestValidEventAtMs(events) ?? fallbackAtMs;
270
262
  return {
271
- ...(events.length > 0 ? { activity: foldAgentActivity(events) } : {}),
263
+ ...(events.length > 0 ? { activity: foldPublicProjectionActivity(events) } : {}),
272
264
  ...(activityAtMs !== undefined
273
265
  ? {
274
266
  activityAtMs,
@@ -304,11 +296,17 @@ function readProjectionRow(projectionDir, id, alias, nowMs) {
304
296
  ? Date.parse(adoptedAt)
305
297
  : Number.NaN;
306
298
  const activityFacts = readActivityFacts(projectionDir, life.executionId, nowMs, diagnostics, Number.isFinite(adoptedAtMs) ? adoptedAtMs : undefined);
299
+ const mintedAtMs = Date.parse(identityFacts.mintedAt ?? "");
300
+ const displayAgeMs = activityFacts.activityAgeMs
301
+ ?? (Number.isFinite(mintedAtMs) ? Math.max(0, nowMs - mintedAtMs) : undefined);
307
302
  if (life.phase === "unknown" && life.diagnostic) {
308
303
  diagnostics.push({ kind: "unknown-lifecycle", detail: life.diagnostic });
309
304
  }
310
305
  const phase = life.phase;
311
306
  const state = phase === "active" || phase === "quiescent" ? "out" : phase;
307
+ const terminalFailure = current?.verdict
308
+ ? projectTerminalFailure(current.verdict.facts.state, current.verdict.facts)
309
+ : undefined;
312
310
  return {
313
311
  id,
314
312
  ...(alias ? { alias } : {}),
@@ -319,12 +317,14 @@ function readProjectionRow(projectionDir, id, alias, nowMs) {
319
317
  mintedAt: identityFacts.mintedAt,
320
318
  ...(identityFacts.workspaceRoot ? { workspaceRoot: identityFacts.workspaceRoot } : {}),
321
319
  ...activityFacts,
320
+ ...(displayAgeMs !== undefined ? { activityAgeMs: displayAgeMs } : {}),
321
+ ...(terminalFailure ? { terminalFailure } : {}),
322
322
  tellCounts,
323
323
  diagnostics,
324
324
  };
325
325
  }
326
- export function readProjectionStatusBoard(cwd, nowMs, options = {}) {
327
- const root = projectionRoot(cwd);
326
+ export async function readProjectionStatusBoard(cwd, nowMs, options = {}) {
327
+ const root = await projectionRoot(cwd);
328
328
  try {
329
329
  if (!fs.lstatSync(root).isDirectory())
330
330
  return null;
@@ -349,7 +349,7 @@ export function readProjectionStatusBoard(cwd, nowMs, options = {}) {
349
349
  for (const alias of fs.readdirSync(aliasRoot).sort()) {
350
350
  if (alias.startsWith("."))
351
351
  continue;
352
- const id = readProjectionAlias(cwd, alias);
352
+ const id = await readProjectionAlias(cwd, alias);
353
353
  if (id)
354
354
  aliases.set(id, alias);
355
355
  }
@@ -382,10 +382,83 @@ export function readProjectionStatusBoard(cwd, nowMs, options = {}) {
382
382
  catch {
383
383
  continue;
384
384
  }
385
- if (isProjectionGenerationSchemaV1Residue(dir))
386
- continue;
387
385
  rows.push(readProjectionRow(dir, id, aliases.get(id), nowMs));
388
386
  }
389
387
  }
390
388
  return rows.length > 0 ? prioritizeProjectionStatusRows(rows, options.showAll === true) : null;
391
389
  }
390
+ /**
391
+ * Renew-only lifecycle guard. Immutable identity is checked before lifecycle,
392
+ * so an unrelated or unattributable projection cannot block a commission.
393
+ */
394
+ export async function hasStrandableProjectionGeneration(input) {
395
+ const root = await projectionRoot(input.cwd);
396
+ let akumas;
397
+ try {
398
+ if (!fs.lstatSync(root).isDirectory())
399
+ return false;
400
+ akumas = fs.readdirSync(root);
401
+ }
402
+ catch (error) {
403
+ if (isErrnoException(error) && error.code === "ENOENT")
404
+ return false;
405
+ throw error;
406
+ }
407
+ for (const akuma of akumas.sort()) {
408
+ if (akuma.startsWith("."))
409
+ continue;
410
+ const akumaDir = path.join(root, akuma);
411
+ try {
412
+ if (!fs.lstatSync(akumaDir).isDirectory())
413
+ continue;
414
+ }
415
+ catch {
416
+ continue;
417
+ }
418
+ let hexes;
419
+ try {
420
+ hexes = fs.readdirSync(akumaDir);
421
+ }
422
+ catch {
423
+ continue;
424
+ }
425
+ for (const hex of hexes.sort()) {
426
+ const projectionId = `${akuma}/${hex}`;
427
+ const projectionDir = path.join(akumaDir, hex);
428
+ if (!isProjectionId(projectionId))
429
+ continue;
430
+ try {
431
+ if (!fs.lstatSync(projectionDir).isDirectory())
432
+ continue;
433
+ }
434
+ catch {
435
+ continue;
436
+ }
437
+ let identity;
438
+ try {
439
+ identity = readProjectionIdentity(projectionDir);
440
+ }
441
+ catch {
442
+ // Corrupt identity has no trustworthy commission/worktree join and
443
+ // therefore cannot become a repository-wide renew interlock.
444
+ continue;
445
+ }
446
+ if (identity.binding.kind !== "commission"
447
+ || identity.binding.commissionId !== input.commissionId
448
+ || !worktreeContainsCoordinate(input.deliveryWorktree, identity.workspaceRoot)) {
449
+ continue;
450
+ }
451
+ const phase = observeProjectionLifeSnapshot(projectionDir, {
452
+ nowMs: input.nowMs,
453
+ startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
454
+ }).life.phase;
455
+ if (phase === "active"
456
+ || phase === "minting"
457
+ || phase === "startup-timeout"
458
+ || phase === "unknown") {
459
+ return true;
460
+ }
461
+ }
462
+ }
463
+ return false;
464
+ }
@@ -0,0 +1,104 @@
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_NON_ZERO_EXIT",
7
+ "SUBAGENT_SPAWN_ERROR",
8
+ "SUBAGENT_EXEC_ERROR",
9
+ "SUBAGENT_PROVIDER_UNAVAILABLE",
10
+ ]);
11
+ const SYSTEM_SIGNALS = new Set(Object.keys(osConstants.signals));
12
+ function objectValue(value) {
13
+ return value !== null && typeof value === "object" && !Array.isArray(value)
14
+ ? value
15
+ : undefined;
16
+ }
17
+ function failureCode(value) {
18
+ return typeof value === "string" && HARNESS_FAILURE_CODES.has(value)
19
+ ? value
20
+ : undefined;
21
+ }
22
+ function nonNegativeNumber(value) {
23
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0
24
+ ? value
25
+ : undefined;
26
+ }
27
+ function exitCode(value) {
28
+ return typeof value === "number" && Number.isSafeInteger(value) ? value : undefined;
29
+ }
30
+ function signal(value) {
31
+ return typeof value === "string" && SYSTEM_SIGNALS.has(value)
32
+ ? value
33
+ : undefined;
34
+ }
35
+ function artifactId(source) {
36
+ for (const value of [source.artifactId, source.sourceArtifactId]) {
37
+ if (typeof value === "string" && RESPONSE_ARTIFACT_ID_RE.test(value))
38
+ return value;
39
+ }
40
+ return undefined;
41
+ }
42
+ function providerFailureCause(value) {
43
+ const source = objectValue(value);
44
+ return source?.kind === "rate_limit_exhausted"
45
+ ? { kind: "rate_limit_exhausted" }
46
+ : undefined;
47
+ }
48
+ /**
49
+ * Convert artifact-rich verdict facts into the strict public allowlist. Raw
50
+ * messages, output, diagnostics, sessions, provider payloads, and unknown
51
+ * strings have no path through this function.
52
+ */
53
+ export function projectTerminalFailure(state, facts) {
54
+ if (state !== "failed" && state !== "lost-runner" && state !== "launch-failed")
55
+ return undefined;
56
+ const source = objectValue(facts);
57
+ if (!source)
58
+ return undefined;
59
+ const error = objectValue(source.error);
60
+ const publicArtifactId = artifactId(source);
61
+ // Runner verdict authority wins classification even when harness evidence is
62
+ // also attached to the verdict.
63
+ if (state === "launch-failed" || state === "lost-runner") {
64
+ const phase = state === "lost-runner"
65
+ ? "adoption"
66
+ : source.stage === "spawn" ? "launch" : "adoption";
67
+ const code = phase === "launch"
68
+ ? "SUBAGENT_SPAWN_ERROR"
69
+ : "SUBAGENT_EXEC_ERROR";
70
+ const timeoutMs = nonNegativeNumber(error?.timeoutMs ?? source.timeoutMs);
71
+ const publicExitCode = exitCode(error?.exitCode ?? source.exitCode);
72
+ const publicSignal = signal(error?.signal ?? source.signal);
73
+ return {
74
+ class: "runner",
75
+ code,
76
+ phase,
77
+ ...(timeoutMs !== undefined ? { timeoutMs } : {}),
78
+ ...(publicExitCode !== undefined ? { exitCode: publicExitCode } : {}),
79
+ ...(publicSignal ? { signal: publicSignal } : {}),
80
+ ...(publicArtifactId ? { artifactId: publicArtifactId } : {}),
81
+ };
82
+ }
83
+ if (error?.version !== OUTCOME_ERROR_VERSION)
84
+ return undefined;
85
+ const publicSignal = signal(error?.signal ?? source.signal);
86
+ const publicExitCode = exitCode(error?.exitCode ?? source.exitCode);
87
+ const code = failureCode(error?.code ?? source.code)
88
+ ?? (publicSignal ? "SUBAGENT_EXEC_ERROR" : undefined)
89
+ ?? (publicExitCode !== undefined ? "SUBAGENT_NON_ZERO_EXIT" : undefined);
90
+ if (!code || code === "SUBAGENT_SPAWN_ERROR")
91
+ return undefined;
92
+ const timeoutMs = nonNegativeNumber(error?.timeoutMs ?? source.timeoutMs);
93
+ const publicCause = providerFailureCause(error?.publicCause);
94
+ return {
95
+ class: "harness",
96
+ code,
97
+ phase: source.phase === "adoption" ? "adoption" : "runtime",
98
+ ...(timeoutMs !== undefined ? { timeoutMs } : {}),
99
+ ...(publicExitCode !== undefined ? { exitCode: publicExitCode } : {}),
100
+ ...(publicSignal ? { signal: publicSignal } : {}),
101
+ ...(publicArtifactId ? { artifactId: publicArtifactId } : {}),
102
+ ...(publicCause ? { publicCause } : {}),
103
+ };
104
+ }