@astrosheep/keiyaku 2.9.8 → 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 +10 -7
  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 +242 -79
  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 +65 -29
  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 +13 -35
  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 +45 -69
  87. package/build/cli/subagent-guard.js +1 -1
  88. package/build/cli/types.js +1 -1
  89. package/build/config/env-keys.js +3 -1
  90. package/build/config/env.js +24 -1
  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 +13 -6
  129. package/build/core/projection/index.js +5 -4
  130. package/build/core/projection/projection-activity.js +29 -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 +35 -337
  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 +125 -127
  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
@@ -5,17 +5,18 @@
5
5
  */
6
6
  export { heartPath } from "./heart.js";
7
7
  export { AGENT_ACTIVITY_WINDOW_SIZE, foldAgentActivity, } from "./projection-activity.js";
8
- export { assertProjectionAlias, isProjectionAlias, moveProjectionAlias, projectionAliasRoot, readProjectionAlias, } from "./projection-alias.js";
8
+ export { assertProjectionAlias, isProjectionAlias, moveProjectionAlias, projectionAliasRoot, projectionAliasRootAtCoordinate, readProjectionAlias, readProjectionAliasAtRoot, } from "./projection-alias.js";
9
9
  export { resolveProjectionCoordinate } from "./projection-coordinate.js";
10
- export { appendGenerationEvent, executionEventsPath, isProjectionId, isPublishedProjection, isoFromNowMs, projectionDir, projectionRoot, readProjectionEvents, } from "./projection-core.js";
10
+ export { appendGenerationEvent, executionEventsPath, isProjectionId, isPublishedProjection, isoFromNowMs, projectionDir, projectionDirAtCoordinate, projectionRoot, readProjectionEvents, readGenerationEventsWithRetention, } from "./projection-core.js";
11
11
  export { readProjectionIdentity } from "./projection-identity.js";
12
12
  export { interruptProjectionGeneration, killProjectionGeneration } from "./projection-kill.js";
13
13
  export { observeProjectionLife, observeProjectionLifeSnapshot, } from "./projection-life-observer.js";
14
14
  export { mintProjection, mintRepositoryProjection, ProjectionMintCollisionError, } from "./projection-mint.js";
15
15
  export { acquireProjectionRunnerLock, acquireProjectionRunnerSettlementLock, acquireProjectionRunnerTransferTarget, initializeProjectionRunnerLockDatabase, isProjectionRunnerLockPlatformSupported, observeProjectionRunnerLock, projectionRunnerLockPath, } from "./projection-runner-lock.js";
16
16
  export { hasStrandableProjectionGeneration, prioritizeProjectionStatusRows, readAkumaNonterminalProjectionStatus, readExactProjectionStatus, readOperationalProjectionStatusBoard, } from "./projection-status.js";
17
+ export { PROJECTION_HISTORY_FILTER_COMPLETION, PROJECTION_HISTORY_FILTER_FIELDS, PROJECTION_HISTORY_FILTER_GRAMMAR, parseProjectionHistoryFilter, parseProjectionHistorySort, readProjectionHistoryAddress, readExactProjectionHistory, readProjectionHistory, resolveProjectionHistoryAddress, selectProjectionHistory, } from "./projection-history.js";
17
18
  export { projectTerminalFailure, } from "./projection-terminal-failure.js";
18
- export { snapshotCommissionProjectionIds, waitForProjection, waitForProjectionSet, } from "./projection-wait.js";
19
+ export { snapshotCommissionProjectionIds, snapshotCommissionProjectionIdsAtCoordinate, waitForProjection, waitForProjectionSet, } from "./projection-wait.js";
19
20
  export { assertTellEffortSupported, assertProjectionTellable, tellProjection, } from "./projection-wake.js";
20
21
  export { openProjectionGenerationStore, openProjectionGenerationStoreReadOnly, ProjectionGenerationStore, } from "./generation/store.js";
21
22
  export { createProjectionExecutionId } from "./generation/projection-generation-identity.js";
@@ -24,4 +25,4 @@ export { projectionGenerationRunnerArgv, spawnProjectionGenerationRunner, } from
24
25
  export { runProjectionGeneration, } from "./generation/projection-generation-runner.js";
25
26
  export { executeProjectionGeneration, prepareProjectionGenerationExecution, } from "./generation/projection-generation-execution.js";
26
27
  export { runProjectionGenerationRuntime } from "./generation/projection-generation-runtime.js";
27
- export { claimFencedTells, claimTellToInflight, listTellIds, markTellConsumed, markTellSubmitted, readPendingTellWindow, readProjectionTellCounts, readTellOriginal, readTellSubmitted, writeInboxTell, } from "./tell/store.js";
28
+ export { bindTellLaunchReservation, claimAndReserveFencedTells, claimFencedTells, claimTellToInflight, listTellLaunchBatch, listTellLaunchAdmissions, isTellLaunchAdmissionMember, listTellIds, markTellSubmitted, recordTellReceipt, recordTellReceiptCapabilities, readTellReceiptCapabilities, readPendingTellWindow, readProjectionTellCounts, readTellOriginal, readTellSubmitted, writeInboxTell, } from "./tell/store.js";
@@ -40,7 +40,7 @@ function sortEvents(events) {
40
40
  .map(({ event, order }) => ({ event, order }));
41
41
  }
42
42
  /** Fold the provider-neutral event ledger once into the bounded UI activity model. */
43
- export function foldAgentActivity(events) {
43
+ export function foldAgentActivity(events, retentionOverride) {
44
44
  const sorted = sortEvents(events);
45
45
  const latestActivity = new Map();
46
46
  const firstActivityCall = new Map();
@@ -56,13 +56,19 @@ export function foldAgentActivity(events) {
56
56
  let latestPlan;
57
57
  let diagnostic;
58
58
  let failure;
59
+ let compacted = false;
59
60
  for (const indexed of sorted) {
60
61
  const event = indexed.event;
61
62
  switch (event.type) {
62
63
  case "message":
63
- latestMessage.set(JSON.stringify([event.turn, event.id]), indexed);
64
+ {
65
+ const key = JSON.stringify([event.turn, event.id]);
66
+ compacted ||= latestMessage.has(key);
67
+ latestMessage.set(key, indexed);
68
+ }
64
69
  break;
65
70
  case "activity": {
71
+ compacted ||= latestActivity.has(event.id);
66
72
  latestActivity.set(event.id, indexed);
67
73
  if (event.phase !== "updated" && !firstActivityCall.has(event.id))
68
74
  firstActivityCall.set(event.id, event.call);
@@ -80,6 +86,7 @@ export function foldAgentActivity(events) {
80
86
  break;
81
87
  }
82
88
  case "thought": {
89
+ compacted ||= latestThought.has(event.id);
83
90
  latestThought.set(event.id, indexed);
84
91
  thoughtEventCount.set(event.id, (thoughtEventCount.get(event.id) ?? 0) + 1);
85
92
  const at = eventTime(event.at);
@@ -88,13 +95,14 @@ export function foldAgentActivity(events) {
88
95
  break;
89
96
  }
90
97
  case "warning": {
91
- const identity = warningIdentity(event.warning);
98
+ const identity = `${event.historyExecutionId ?? ""}:${warningIdentity(event.warning)}`;
92
99
  const prior = warnings.get(identity);
100
+ compacted ||= prior !== undefined;
93
101
  warnings.set(identity, { latest: indexed, count: (prior?.count ?? 0) + 1 });
94
102
  break;
95
103
  }
96
104
  case "usage":
97
- usageByTurn.set(event.turn, event);
105
+ usageByTurn.set(event.historyTurnKey ?? String(event.turn), event);
98
106
  break;
99
107
  case "plan":
100
108
  latestPlan = indexed;
@@ -174,7 +182,7 @@ export function foldAgentActivity(events) {
174
182
  continue;
175
183
  }
176
184
  if (event.type === "warning") {
177
- const aggregate = warnings.get(warningIdentity(event.warning));
185
+ const aggregate = warnings.get(`${event.historyExecutionId ?? ""}:${warningIdentity(event.warning)}`);
178
186
  if (aggregate?.latest !== indexed)
179
187
  continue;
180
188
  settled.push({
@@ -189,7 +197,7 @@ export function foldAgentActivity(events) {
189
197
  continue;
190
198
  }
191
199
  if (event.type === "turn" && event.phase === "completed") {
192
- const usage = usageByTurn.get(event.turn);
200
+ const usage = usageByTurn.get(event.historyTurnKey ?? String(event.turn));
193
201
  turns.push({
194
202
  kind: "turn",
195
203
  order: indexed.order,
@@ -211,11 +219,15 @@ export function foldAgentActivity(events) {
211
219
  settled.sort((left, right) => left.order - right.order);
212
220
  pinned.sort((left, right) => left.order - right.order);
213
221
  const window = settled.slice(-AGENT_ACTIVITY_WINDOW_SIZE);
222
+ const omittedSettledCount = Math.max(0, settled.length - window.length);
214
223
  const visibleOrders = [...window, ...pinned].map((row) => row.order);
215
224
  const relevantTurns = visibleOrders.length === 0
216
225
  ? turns.slice(-1)
217
226
  : turns.filter((row) => row.order >= Math.min(...visibleOrders));
218
227
  const timeline = [...window, ...relevantTurns, ...pinned].sort((left, right) => left.order - right.order);
228
+ const retainedTimeline = [...settled, ...turns, ...pinned].sort((left, right) => left.order - right.order);
229
+ const truncated = retainedTimeline.some((row) => (row.kind === "ran" || row.kind === "thought") && row.truncated);
230
+ const bounded = sorted.some(({ event }) => event.type === "truncation");
219
231
  let plan;
220
232
  if (latestPlan?.event.type === "plan") {
221
233
  const event = latestPlan.event;
@@ -232,8 +244,17 @@ export function foldAgentActivity(events) {
232
244
  }
233
245
  return {
234
246
  window,
247
+ omittedSettledCount,
235
248
  pinned,
236
249
  timeline,
250
+ retainedTimeline,
251
+ retention: {
252
+ state: retentionOverride ?? (bounded ? "bounded" : truncated ? "truncated" : compacted ? "compacted" : "complete"),
253
+ retainedCount: retainedTimeline.length,
254
+ range: retainedTimeline.length > 0
255
+ ? { first: retainedTimeline[0].order, last: retainedTimeline.at(-1).order }
256
+ : null,
257
+ },
237
258
  ledgerActivities,
238
259
  ledgerTurns: turns,
239
260
  ...(plan ? { plan } : {}),
@@ -246,7 +267,7 @@ export function foldAgentActivity(events) {
246
267
  * prose. Diagnostic and fail-event text remains available to artifact/internal
247
268
  * consumers through the full fold, but has no JSON or human projection path.
248
269
  */
249
- export function foldPublicProjectionActivity(events) {
250
- const { diagnostic: _diagnostic, failure: _failure, ...activity } = foldAgentActivity(events);
270
+ export function foldPublicProjectionActivity(events, retentionOverride) {
271
+ const { diagnostic: _diagnostic, failure: _failure, ...activity } = foldAgentActivity(events, retentionOverride);
251
272
  return activity;
252
273
  }
@@ -3,7 +3,7 @@ import * as path from "node:path";
3
3
  import { isErrnoException } from "../../errno.js";
4
4
  import { syncDirectorySync } from "../../fs/durability.js";
5
5
  import { ProjectionStateError, randomHex8 } from "../atomic-publish.js";
6
- import { projectionCoordinateRoot } from "./projection-coordinate.js";
6
+ import { resolveProjectionCoordinate } from "./projection-coordinate.js";
7
7
  import { isProjectionId } from "./projection-core.js";
8
8
  export const PROJECTION_ALIAS_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
9
9
  const MAX_ALIAS_REF_BYTES = 1024;
@@ -16,12 +16,18 @@ export function assertProjectionAlias(value) {
16
16
  }
17
17
  return value;
18
18
  }
19
+ export function projectionAliasRootAtCoordinate(coordinate) {
20
+ return path.join(coordinate.physicalRoot, ".keiyaku", "projection-alias");
21
+ }
19
22
  export async function projectionAliasRoot(cwd) {
20
- return path.join(await projectionCoordinateRoot(cwd), ".keiyaku", "projection-alias");
23
+ return projectionAliasRootAtCoordinate(await resolveProjectionCoordinate(cwd));
21
24
  }
22
- export async function projectionAliasPath(cwd, alias) {
25
+ function projectionAliasPathAtRoot(root, alias) {
23
26
  assertProjectionAlias(alias);
24
- return path.join(await projectionAliasRoot(cwd), alias);
27
+ return path.join(root, alias);
28
+ }
29
+ export async function projectionAliasPath(cwd, alias) {
30
+ return projectionAliasPathAtRoot(await projectionAliasRoot(cwd), alias);
25
31
  }
26
32
  function invalidAliasRef(filePath) {
27
33
  return new ProjectionStateError(`invalid projection alias ref: ${filePath}`);
@@ -62,8 +68,8 @@ function readBoundedRegularAliasRef(filePath) {
62
68
  }
63
69
  }
64
70
  /** Undefined means no ref; malformed refs fail closed with their exact sidecar path. */
65
- export async function readProjectionAlias(cwd, alias) {
66
- const filePath = await projectionAliasPath(cwd, alias);
71
+ export async function readProjectionAliasAtRoot(root, alias) {
72
+ const filePath = projectionAliasPathAtRoot(root, alias);
67
73
  const content = readBoundedRegularAliasRef(filePath);
68
74
  if (content === undefined)
69
75
  return undefined;
@@ -74,15 +80,18 @@ export async function readProjectionAlias(cwd, alias) {
74
80
  throw invalidAliasRef(filePath);
75
81
  return id;
76
82
  }
83
+ /** Cwd-facing compatibility entry point; multi-alias callers should reuse a resolved root. */
84
+ export async function readProjectionAlias(cwd, alias) {
85
+ return readProjectionAliasAtRoot(await projectionAliasRoot(cwd), alias);
86
+ }
77
87
  /** Atomically replace one alias ref. Aliases intentionally have no multi-ref transaction. */
78
- export async function moveProjectionAlias(cwd, alias, id) {
88
+ async function moveProjectionAliasAtRoot(root, alias, id) {
79
89
  assertProjectionAlias(alias);
80
90
  if (!isProjectionId(id))
81
91
  throw new ProjectionStateError(`invalid projection id: ${id}`);
82
- const previous = await readProjectionAlias(cwd, alias);
83
- const root = await projectionAliasRoot(cwd);
92
+ const previous = await readProjectionAliasAtRoot(root, alias);
84
93
  fs.mkdirSync(root, { recursive: true });
85
- const target = await projectionAliasPath(cwd, alias);
94
+ const target = projectionAliasPathAtRoot(root, alias);
86
95
  let temp;
87
96
  try {
88
97
  for (let attempt = 0; attempt < 16; attempt += 1) {
@@ -116,3 +125,7 @@ export async function moveProjectionAlias(cwd, alias, id) {
116
125
  }
117
126
  return previous;
118
127
  }
128
+ /** Cwd-facing compatibility entry point; writers with a resolved root avoid rediscovery. */
129
+ export async function moveProjectionAlias(cwd, alias, id) {
130
+ return moveProjectionAliasAtRoot(await projectionAliasRoot(cwd), alias, id);
131
+ }
@@ -1,25 +1,35 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { isErrnoException } from "../../errno.js";
4
- import { projectionCoordinateRoot } from "./projection-coordinate.js";
4
+ import { resolveProjectionCoordinate } from "./projection-coordinate.js";
5
5
  import { ProjectionStateError, } from "../atomic-publish.js";
6
6
  export { atomicPublishFile, ProjectionStateError, randomHex8 } from "../atomic-publish.js";
7
7
  export { isTellCausallyConsumed, } from "./tell/model.js";
8
- export { claimAllInbox, claimFencedTells, claimTellToInflight, listTellIds, markTellConsumed, markTellSubmitted, readTellOriginal, readTellSubmitted, snapshotPendingTells, writeInboxTell, } from "./tell/store.js";
8
+ export { bindTellLaunchReservation, claimAllInbox, claimAndReserveFencedTells, claimFencedTells, listTellLaunchBatch, listTellLaunchAdmissions, isTellLaunchAdmissionMember, claimTellToInflight, listTellIds, markTellSubmitted, recordTellReceiptCapabilities, recordTellReceipt, readTellReceiptCapabilities, 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
+ /** Derive one projection storage root from already-resolved repository authority. */
16
+ export function projectionRootAtCoordinate(coordinate) {
17
+ return path.join(coordinate.physicalRoot, ...PROJECTION_ROOT_SEGMENTS);
18
+ }
15
19
  export async function projectionRoot(cwd) {
16
- return path.join(await projectionCoordinateRoot(cwd), ...PROJECTION_ROOT_SEGMENTS);
20
+ return projectionRootAtCoordinate(await resolveProjectionCoordinate(cwd));
17
21
  }
18
- export async function projectionDir(cwd, id) {
22
+ /** Derive one projection directory from already-resolved repository authority. */
23
+ export function projectionDirAtCoordinate(coordinate, id) {
19
24
  if (!isProjectionId(id))
20
25
  throw new ProjectionStateError(`invalid projection id: ${id}`);
21
26
  const [akuma, hex] = id.split("/");
22
- return path.join(await projectionRoot(cwd), akuma, hex);
27
+ return path.join(projectionRootAtCoordinate(coordinate), akuma, hex);
28
+ }
29
+ export async function projectionDir(cwd, id) {
30
+ if (!isProjectionId(id))
31
+ throw new ProjectionStateError(`invalid projection id: ${id}`);
32
+ return projectionDirAtCoordinate(await resolveProjectionCoordinate(cwd), id);
23
33
  }
24
34
  export function encodeProjectionJson(value) {
25
35
  return `${JSON.stringify(value)}\n`;
@@ -59,13 +69,17 @@ export function appendGenerationEvent(projectionDirectory, executionId, event) {
59
69
  * Earlier complete lines remain strict ledger records.
60
70
  */
61
71
  export function readGenerationEvents(projectionDirectory, executionId) {
72
+ return readGenerationEventsWithRetention(projectionDirectory, executionId).events;
73
+ }
74
+ /** Distinguishes a deliberately empty ledger from an absent generation ledger. */
75
+ export function readGenerationEventsWithRetention(projectionDirectory, executionId) {
62
76
  let content;
63
77
  try {
64
78
  content = fs.readFileSync(executionEventsPath(projectionDirectory, executionId), "utf8");
65
79
  }
66
80
  catch (error) {
67
81
  if (isErrnoException(error) && error.code === "ENOENT")
68
- return [];
82
+ return { events: [], retention: "unavailable" };
69
83
  throw error;
70
84
  }
71
85
  const lines = content.split("\n");
@@ -83,7 +97,7 @@ export function readGenerationEvents(projectionDirectory, executionId) {
83
97
  // A concurrent append can leave only this final record incomplete.
84
98
  }
85
99
  }
86
- return events;
100
+ return { events, retention: "available" };
87
101
  }
88
102
  export function readProjectionEvents(projectionDirectory) {
89
103
  let entries;
@@ -1,10 +1,11 @@
1
- import { claimAllInbox, listTellIds, markTellSubmitted, markTellConsumed, readTellSubmitted, readTellOriginal, isTellCausallyConsumed, appendGenerationEvent, } from "./projection-core.js";
1
+ import { claimAllInbox, isTellLaunchAdmissionMember, listTellLaunchAdmissions, listTellIds, markTellSubmitted, bindTellLaunchReservation, recordTellReceiptCapabilities, recordTellReceipt, readTellSubmitted, readTellOriginal, isTellCausallyConsumed, appendGenerationEvent, } from "./projection-core.js";
2
2
  import { openProjectionTellObserver, } from "./tell/database.js";
3
3
  const LIVE_TELL_DELIVERY_THROTTLE_MS = 200;
4
4
  export function renderProjectionTellFrame(tellId, text) {
5
- return `--- tell ${tellId} ---\n${text}`;
5
+ void tellId;
6
+ return text;
6
7
  }
7
- function submitEligible(projectionDirectory, executionId, control, requireInbox, submitting, submissions, excludedTellIds, observer, pendingCompletionEvidence) {
8
+ function submitEligible(projectionDirectory, executionId, control, requireInbox, submitting, submissions, excludedTellIds, observer, pendingReceipts, pendingFenceReceipts) {
8
9
  if (requireInbox && !observer?.hasInbox())
9
10
  return;
10
11
  claimAllInbox(projectionDirectory);
@@ -27,27 +28,53 @@ function submitEligible(projectionDirectory, executionId, control, requireInbox,
27
28
  if (!ack)
28
29
  throw new Error("provider enqueue resolved without TellSubmitAck");
29
30
  markTellSubmitted(projectionDirectory, tellId, { executionId, fence: ack.fence });
30
- const pending = pendingCompletionEvidence.get(tellId);
31
- if (!pending)
32
- return;
33
- for (const evidence of pending) {
34
- if (isTellCausallyConsumed(ack.fence, evidence)) {
35
- markTellConsumed(projectionDirectory, tellId);
36
- break;
37
- }
38
- }
39
- pendingCompletionEvidence.delete(tellId);
31
+ reconcileAcknowledgedTell(projectionDirectory, executionId, tellId, ack.fence, pendingReceipts, pendingFenceReceipts);
40
32
  })
41
33
  .catch(() => {
42
34
  // Leave in inflight for the next checkpoint or ingress opportunity.
43
35
  // Reject means this crossing never accepted the tell; drop stale evidence.
44
- pendingCompletionEvidence.delete(tellId);
36
+ pendingReceipts.delete(tellId);
45
37
  })
46
38
  .finally(() => submitting.delete(tellId));
47
39
  submissions.add(submission);
48
40
  void submission.finally(() => submissions.delete(submission));
49
41
  }
50
42
  }
43
+ /**
44
+ * An acknowledgement establishes the submission fence. Native exact and fence
45
+ * evidence may arrive on either side of that acknowledgement, so both buffers
46
+ * reduce through this one monotonic path. Exact visibility runs first only to
47
+ * supply the required heart predecessor for an already-buffered consumption.
48
+ */
49
+ function reconcileAcknowledgedTell(projectionDirectory, executionId, tellId, submissionFence, pendingExactReceipts, pendingFenceReceipts) {
50
+ for (const receipt of pendingExactReceipts.get(tellId) ?? []) {
51
+ const launchInput = "launchInput" in receipt ? receipt.launchInput : undefined;
52
+ recordTellReceipt(projectionDirectory, {
53
+ tellId,
54
+ executionId,
55
+ stage: receipt.stage,
56
+ correlation: launchInput ? "inherited" : "exact",
57
+ ...(launchInput ? { fence: launchInput.fence } : {}),
58
+ });
59
+ }
60
+ pendingExactReceipts.delete(tellId);
61
+ const submitted = readTellSubmitted(projectionDirectory, tellId);
62
+ if (isTellLaunchAdmissionMember(projectionDirectory, executionId, tellId))
63
+ return;
64
+ for (const receipt of pendingFenceReceipts) {
65
+ if (receipt.correlation.kind !== "fence")
66
+ continue;
67
+ if (!isTellCausallyConsumed(submissionFence, receipt.correlation.fence))
68
+ continue;
69
+ recordTellReceipt(projectionDirectory, {
70
+ tellId,
71
+ executionId,
72
+ stage: receipt.stage,
73
+ correlation: submitted.replayedFrom?.length ? "inherited" : "fence",
74
+ fence: receipt.correlation.fence,
75
+ });
76
+ }
77
+ }
51
78
  export function createLiveTellDeliveryController(projectionDirectory, executionId, dependencies = {}) {
52
79
  const now = dependencies.now ?? Date.now;
53
80
  const setTimer = dependencies.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
@@ -55,9 +82,12 @@ export function createLiveTellDeliveryController(projectionDirectory, executionI
55
82
  const openObserver = dependencies.openObserver ?? openProjectionTellObserver;
56
83
  const submitting = new Set();
57
84
  const submissions = new Set();
58
- // In-memory only: bridges completion-before-ack when a tell is still submitting.
59
- const pendingCompletionEvidence = new Map();
60
- const initial = new Set(dependencies.initialTellIds);
85
+ // In-memory only: bridges a native exact receipt that races its enqueue ack.
86
+ const pendingReceipts = new Map();
87
+ // Native completion can race the steer RPC acknowledgement. The receipt is
88
+ // retained until each acknowledged fence can be reconciled causally.
89
+ const pendingFenceReceipts = [];
90
+ const initial = new Set(dependencies.reservedLaunchTellIds ?? []);
61
91
  let observer;
62
92
  let timer;
63
93
  let nextOpportunityAt = 0;
@@ -102,7 +132,7 @@ export function createLiveTellDeliveryController(projectionDirectory, executionI
102
132
  try {
103
133
  observer ??= openObserver(projectionDirectory);
104
134
  if (observer.hasDataVersionChanged()) {
105
- submitEligible(projectionDirectory, executionId, control, true, submitting, submissions, initial, observer, pendingCompletionEvidence);
135
+ submitEligible(projectionDirectory, executionId, control, true, submitting, submissions, initial, observer, pendingReceipts, pendingFenceReceipts);
106
136
  }
107
137
  }
108
138
  catch {
@@ -140,29 +170,39 @@ export function createLiveTellDeliveryController(projectionDirectory, executionI
140
170
  return;
141
171
  latestControl = control;
142
172
  cancelOpportunity();
143
- for (const tellId of initial) {
144
- markTellSubmitted(projectionDirectory, tellId, {
173
+ submitEligible(projectionDirectory, executionId, control, false, submitting, submissions, initial, observer, pendingReceipts, pendingFenceReceipts);
174
+ },
175
+ receipt(receipt) {
176
+ if (closed)
177
+ return;
178
+ if (receipt.correlation.kind === "exact") {
179
+ const tellId = receipt.correlation.tellId;
180
+ const launchInput = "launchInput" in receipt ? receipt.launchInput : undefined;
181
+ if (submitting.has(tellId)) {
182
+ const pending = pendingReceipts.get(tellId) ?? [];
183
+ pending.push(receipt);
184
+ pendingReceipts.set(tellId, pending);
185
+ return;
186
+ }
187
+ recordTellReceipt(projectionDirectory, {
188
+ tellId,
145
189
  executionId,
146
- fence: checkpoint.fence,
190
+ stage: receipt.stage,
191
+ correlation: launchInput ? "inherited" : "exact",
192
+ ...(launchInput ? { fence: launchInput.fence } : {}),
147
193
  });
148
- initial.delete(tellId);
149
- }
150
- submitEligible(projectionDirectory, executionId, control, false, submitting, submissions, initial, observer, pendingCompletionEvidence);
151
- if (!checkpoint.consumptionEvidence)
152
194
  return;
153
- for (const tellId of listTellIds(projectionDirectory, "submitted")) {
154
- const submitted = readTellSubmitted(projectionDirectory, tellId);
155
- if (submitted.executionId === executionId && submitted.fence && isTellCausallyConsumed(submitted.fence, checkpoint.fence)) {
156
- markTellConsumed(projectionDirectory, tellId);
157
- }
158
195
  }
159
- // Capture still-in-flight enqueues so a late ack can still see this evidence.
160
- for (const tellId of submitting) {
161
- const pending = pendingCompletionEvidence.get(tellId);
162
- if (pending)
163
- pending.push(checkpoint.fence);
164
- else
165
- pendingCompletionEvidence.set(tellId, [checkpoint.fence]);
196
+ pendingFenceReceipts.push(receipt);
197
+ for (const state of ["submitted", "seen"]) {
198
+ for (const tellId of listTellIds(projectionDirectory, state)) {
199
+ const submitted = readTellSubmitted(projectionDirectory, tellId);
200
+ if (submitted.executionId !== executionId)
201
+ continue;
202
+ if (isTellLaunchAdmissionMember(projectionDirectory, executionId, tellId))
203
+ continue;
204
+ reconcileAcknowledgedTell(projectionDirectory, executionId, tellId, submitted.fence, pendingReceipts, pendingFenceReceipts);
205
+ }
166
206
  }
167
207
  },
168
208
  async close() {
@@ -171,7 +211,7 @@ export function createLiveTellDeliveryController(projectionDirectory, executionI
171
211
  cancelOpportunity();
172
212
  discardObserver();
173
213
  }
174
- // Do not drop pendingCompletionEvidence: in-flight acks may still submit/consume.
214
+ // Do not drop pendingReceipts: in-flight acks may still persist a native receipt.
175
215
  await Promise.all([...submissions]);
176
216
  },
177
217
  };
@@ -191,7 +231,7 @@ export function buildSyncCheckpointCallback(projectionDirectory, executionId) {
191
231
  */
192
232
  export function createProjectionExecutionObserver(input, dependencies = {}) {
193
233
  const appendEvent = dependencies.appendGenerationEvent ?? appendGenerationEvent;
194
- const tellController = createLiveTellDeliveryController(input.projectionDirectory, input.executionId, { initialTellIds: input.initialTellIds });
234
+ const tellController = createLiveTellDeliveryController(input.projectionDirectory, input.executionId, { reservedLaunchTellIds: input.reservedLaunchTellIds });
195
235
  let closePromise;
196
236
  const observer = {
197
237
  onEvent(event) {
@@ -200,6 +240,18 @@ export function createProjectionExecutionObserver(input, dependencies = {}) {
200
240
  onCheckpoint(control, checkpoint) {
201
241
  tellController.checkpoint(control, checkpoint);
202
242
  },
243
+ async onTellReceiptCapabilities(capabilities) {
244
+ recordTellReceiptCapabilities(input.projectionDirectory, input.executionId, capabilities);
245
+ },
246
+ async onLaunchAdmission(admission) {
247
+ bindTellLaunchReservation(input.projectionDirectory, { executionId: input.executionId, ...admission });
248
+ },
249
+ listLaunchAdmissions() {
250
+ return listTellLaunchAdmissions(input.projectionDirectory, input.executionId);
251
+ },
252
+ onTellReceipt(receipt) {
253
+ tellController.receipt(receipt);
254
+ },
203
255
  onDeliveryOpportunity(control) {
204
256
  tellController.requestDelivery(control);
205
257
  },