@astrosheep/keiyaku 4.0.0 → 4.0.2

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 (308) hide show
  1. package/README.md +133 -8
  2. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +2 -2
  3. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +4 -4
  4. package/build/integrations/marketplace/plugins/keiyaku/opencode.js +2 -2
  5. package/build/integrations/marketplace/plugins/keiyaku/package.json +2 -2
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +13 -5
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +91 -32
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +134 -0
  9. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/agents/openai.yaml +4 -0
  10. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +41 -6
  11. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +262 -46
  12. package/build/src/akuma/abort.d.ts +2 -0
  13. package/build/src/akuma/abort.js +50 -0
  14. package/build/src/akuma/akuma.d.ts +50 -33
  15. package/build/src/akuma/akuma.js +175 -127
  16. package/build/src/akuma/archetype.d.ts +14 -5
  17. package/build/src/akuma/archetype.js +85 -39
  18. package/build/src/akuma/body.d.ts +3 -5
  19. package/build/src/akuma/body.js +490 -183
  20. package/build/src/akuma/coordinate.d.ts +9 -0
  21. package/build/src/akuma/coordinate.js +22 -0
  22. package/build/src/akuma/heart/facts.d.ts +88 -45
  23. package/build/src/akuma/heart/facts.js +19 -8
  24. package/build/src/akuma/heart/index.d.ts +64 -55
  25. package/build/src/akuma/heart/index.js +172 -252
  26. package/build/src/akuma/heart/rows.d.ts +38 -83
  27. package/build/src/akuma/heart/rows.js +117 -155
  28. package/build/src/akuma/heart/schema.d.ts +1 -1
  29. package/build/src/akuma/heart/schema.js +61 -22
  30. package/build/src/akuma/heart/soul.d.ts +8 -0
  31. package/build/src/akuma/heart/soul.js +136 -0
  32. package/build/src/akuma/heart/storage.d.ts +47 -0
  33. package/build/src/akuma/heart/storage.js +224 -0
  34. package/build/src/akuma/heart/tells.d.ts +12 -0
  35. package/build/src/akuma/heart/tells.js +79 -0
  36. package/build/src/akuma/heart/timeline.d.ts +15 -0
  37. package/build/src/akuma/heart/timeline.js +86 -0
  38. package/build/src/akuma/identity.d.ts +2 -2
  39. package/build/src/akuma/identity.js +9 -9
  40. package/build/src/akuma/index.d.ts +10 -3
  41. package/build/src/akuma/index.js +7 -1
  42. package/build/src/akuma/projection.d.ts +176 -0
  43. package/build/src/akuma/projection.js +256 -0
  44. package/build/src/akuma/provider-recipe.d.ts +24 -0
  45. package/build/src/akuma/provider-recipe.js +100 -0
  46. package/build/src/akuma/provider.d.ts +67 -13
  47. package/build/src/akuma/provider.js +172 -105
  48. package/build/src/akuma/providers/acp/core.d.ts +19 -0
  49. package/build/src/akuma/providers/acp/core.js +159 -0
  50. package/build/src/akuma/providers/acp/events.d.ts +20 -0
  51. package/build/src/akuma/providers/acp/events.js +73 -0
  52. package/build/src/akuma/providers/acp/index.d.ts +12 -0
  53. package/build/src/akuma/providers/acp/index.js +94 -0
  54. package/build/src/akuma/providers/{claude.d.ts → claude/events.d.ts} +9 -41
  55. package/build/src/akuma/providers/claude/events.js +238 -0
  56. package/build/src/akuma/providers/claude/index.d.ts +47 -0
  57. package/build/src/akuma/providers/claude/index.js +279 -0
  58. package/build/src/akuma/providers/claude/input.d.ts +13 -0
  59. package/build/src/akuma/providers/claude/input.js +109 -0
  60. package/build/src/akuma/providers/codex-app-server/events.d.ts +1 -1
  61. package/build/src/akuma/providers/codex-app-server/events.js +15 -12
  62. package/build/src/akuma/providers/codex-app-server/index.d.ts +15 -13
  63. package/build/src/akuma/providers/codex-app-server/index.js +77 -33
  64. package/build/src/akuma/providers/grok-build/index.d.ts +4 -0
  65. package/build/src/akuma/providers/grok-build/index.js +70 -0
  66. package/build/src/akuma/providers/index.d.ts +5 -3
  67. package/build/src/akuma/providers/index.js +35 -56
  68. package/build/src/akuma/providers/opencode-sdk/events.d.ts +54 -0
  69. package/build/src/akuma/providers/opencode-sdk/events.js +235 -0
  70. package/build/src/akuma/providers/opencode-sdk/index.d.ts +33 -0
  71. package/build/src/akuma/providers/opencode-sdk/index.js +308 -0
  72. package/build/src/akuma/providers/opencode-sdk/session.d.ts +26 -0
  73. package/build/src/akuma/providers/opencode-sdk/session.js +63 -0
  74. package/build/src/akuma/providers/pi/events.d.ts +33 -0
  75. package/build/src/akuma/providers/pi/events.js +166 -0
  76. package/build/src/akuma/providers/pi/index.d.ts +11 -0
  77. package/build/src/akuma/providers/pi/index.js +191 -0
  78. package/build/src/akuma/publication.d.ts +3 -3
  79. package/build/src/akuma/publication.js +28 -40
  80. package/build/src/akuma/requests.d.ts +9 -6
  81. package/build/src/akuma/requests.js +95 -65
  82. package/build/src/alias/index.d.ts +4 -3
  83. package/build/src/alias/index.js +12 -13
  84. package/build/src/body/decode.js +7 -1
  85. package/build/src/body/region.d.ts +2 -0
  86. package/build/src/body/region.js +77 -7
  87. package/build/src/body/render.js +2 -1
  88. package/build/src/body/shape.js +7 -1
  89. package/build/src/body/verification.js +19 -4
  90. package/build/src/cli/accepted.d.ts +18 -12
  91. package/build/src/cli/accepted.js +73 -34
  92. package/build/src/cli/actor.js +3 -3
  93. package/build/src/cli/commands/akuma-invoke.d.ts +44 -19
  94. package/build/src/cli/commands/akuma-invoke.js +101 -52
  95. package/build/src/cli/commands/akuma.d.ts +27 -9
  96. package/build/src/cli/commands/akuma.js +151 -80
  97. package/build/src/cli/commands/contract.d.ts +155 -0
  98. package/build/src/cli/commands/contract.js +20 -0
  99. package/build/src/cli/commands/install.js +2 -0
  100. package/build/src/cli/commands/task-invoke.d.ts +19 -4
  101. package/build/src/cli/commands/task-invoke.js +65 -17
  102. package/build/src/cli/commands/task-query.d.ts +5 -0
  103. package/build/src/cli/commands/task-query.js +247 -0
  104. package/build/src/cli/commands/task.d.ts +3 -1
  105. package/build/src/cli/commands/task.js +52 -14
  106. package/build/src/cli/coordinates.d.ts +18 -0
  107. package/build/src/cli/coordinates.js +120 -0
  108. package/build/src/cli/draft.d.ts +8 -0
  109. package/build/src/cli/draft.js +96 -0
  110. package/build/src/cli/invoke.d.ts +9 -3
  111. package/build/src/cli/invoke.js +287 -108
  112. package/build/src/cli/main.js +54 -7
  113. package/build/src/cli/parse.d.ts +21 -117
  114. package/build/src/cli/parse.js +79 -84
  115. package/build/src/cli/render/akuma-tool-command.d.ts +6 -0
  116. package/build/src/cli/render/akuma-tool-command.js +144 -0
  117. package/build/src/cli/render/akuma-tool.d.ts +4 -2
  118. package/build/src/cli/render/akuma-tool.js +35 -5
  119. package/build/src/cli/render/akuma.d.ts +3 -1
  120. package/build/src/cli/render/akuma.js +247 -212
  121. package/build/src/cli/render/audit.d.ts +3 -0
  122. package/build/src/cli/render/audit.js +104 -0
  123. package/build/src/cli/render/catalog.d.ts +2 -0
  124. package/build/src/cli/render/catalog.js +16 -0
  125. package/build/src/cli/render/contract.d.ts +3 -3
  126. package/build/src/cli/render/contract.js +200 -54
  127. package/build/src/cli/render/kanshi.js +272 -129
  128. package/build/src/cli/render/receipt.d.ts +19 -0
  129. package/build/src/cli/render/receipt.js +106 -0
  130. package/build/src/cli/render/refusal.d.ts +16 -2
  131. package/build/src/cli/render/refusal.js +88 -3
  132. package/build/src/cli/render/region.d.ts +2 -0
  133. package/build/src/cli/render/region.js +17 -0
  134. package/build/src/cli/render/task.d.ts +2 -1
  135. package/build/src/cli/render/task.js +223 -60
  136. package/build/src/cli/render/terminal.d.ts +20 -0
  137. package/build/src/cli/render/terminal.js +121 -0
  138. package/build/src/cli/render/text.js +11 -3
  139. package/build/src/cli/result.d.ts +126 -19
  140. package/build/src/cli/usage.d.ts +1 -0
  141. package/build/src/cli/usage.js +3 -0
  142. package/build/src/contract-worktree.d.ts +44 -0
  143. package/build/src/contract-worktree.js +251 -0
  144. package/build/src/coordination/durable-file.d.ts +5 -1
  145. package/build/src/coordination/durable-file.js +57 -10
  146. package/build/src/coordination/sqlite-transaction-lock.d.ts +4 -0
  147. package/build/src/coordination/sqlite-transaction-lock.js +22 -5
  148. package/build/src/core/facts/codec.js +23 -4
  149. package/build/src/core/facts/eligibility.d.ts +1 -10
  150. package/build/src/core/facts/eligibility.js +0 -49
  151. package/build/src/core/facts/fold.js +1 -6
  152. package/build/src/core/facts/gate.d.ts +1 -0
  153. package/build/src/core/facts/gate.js +1 -0
  154. package/build/src/core/facts/observation.d.ts +3 -0
  155. package/build/src/core/facts/observation.js +12 -0
  156. package/build/src/core/facts/types.d.ts +10 -3
  157. package/build/src/core/subject.js +2 -2
  158. package/build/src/core/verbs/amend.d.ts +1 -1
  159. package/build/src/core/verbs/amend.js +3 -18
  160. package/build/src/core/verbs/bind.js +2 -16
  161. package/build/src/core/verbs/deliver.d.ts +1 -1
  162. package/build/src/core/verbs/deliver.js +14 -4
  163. package/build/src/core/verbs/placement.d.ts +1 -1
  164. package/build/src/core/verbs/placement.js +7 -9
  165. package/build/src/dispatch/index.d.ts +5 -3
  166. package/build/src/dispatch/index.js +30 -21
  167. package/build/src/duration.d.ts +10 -0
  168. package/build/src/duration.js +22 -0
  169. package/build/src/git/admission.d.ts +2 -2
  170. package/build/src/git/admission.js +33 -23
  171. package/build/src/git/hooks.d.ts +15 -0
  172. package/build/src/git/hooks.js +70 -18
  173. package/build/src/git/identity.d.ts +5 -1
  174. package/build/src/git/identity.js +23 -4
  175. package/build/src/git/integration.d.ts +48 -0
  176. package/build/src/git/integration.js +250 -0
  177. package/build/src/git/observe.d.ts +29 -15
  178. package/build/src/git/observe.js +185 -76
  179. package/build/src/git/read-observation.d.ts +41 -0
  180. package/build/src/git/read-observation.js +361 -0
  181. package/build/src/git/reconcile.d.ts +17 -5
  182. package/build/src/git/reconcile.js +305 -109
  183. package/build/src/git/repository.d.ts +41 -18
  184. package/build/src/git/repository.js +220 -114
  185. package/build/src/git/scratch.d.ts +17 -0
  186. package/build/src/git/scratch.js +73 -0
  187. package/build/src/git/target-placement.d.ts +92 -0
  188. package/build/src/git/target-placement.js +375 -0
  189. package/build/src/git/tender.d.ts +49 -0
  190. package/build/src/git/tender.js +63 -0
  191. package/build/src/git/terminal-seal.d.ts +24 -0
  192. package/build/src/git/terminal-seal.js +89 -0
  193. package/build/src/git/tree.d.ts +2 -2
  194. package/build/src/git/tree.js +4 -0
  195. package/build/src/git/workspace.d.ts +53 -0
  196. package/build/src/git/workspace.js +139 -0
  197. package/build/src/identity/selector.d.ts +6 -0
  198. package/build/src/identity/selector.js +19 -0
  199. package/build/src/index.d.ts +4 -2
  200. package/build/src/index.js +2 -1
  201. package/build/src/kanshi/index.d.ts +2 -2
  202. package/build/src/kanshi/index.js +1 -1
  203. package/build/src/kanshi/read.d.ts +7 -3
  204. package/build/src/kanshi/read.js +245 -45
  205. package/build/src/kanshi/report.d.ts +81 -6
  206. package/build/src/kanshi/select.d.ts +5 -0
  207. package/build/src/kanshi/select.js +33 -1
  208. package/build/src/library/address.d.ts +57 -0
  209. package/build/src/library/address.js +165 -0
  210. package/build/src/library/akuma-creation.d.ts +16 -7
  211. package/build/src/library/akuma-creation.js +96 -22
  212. package/build/src/library/audit.d.ts +18 -0
  213. package/build/src/library/audit.js +44 -0
  214. package/build/src/library/bind.d.ts +21 -0
  215. package/build/src/library/bind.js +56 -0
  216. package/build/src/library/catalog.d.ts +58 -0
  217. package/build/src/library/catalog.js +69 -0
  218. package/build/src/library/configuration.d.ts +6 -15
  219. package/build/src/library/configuration.js +14 -17
  220. package/build/src/library/contract.d.ts +23 -49
  221. package/build/src/library/contract.js +171 -185
  222. package/build/src/library/delivery.d.ts +24 -0
  223. package/build/src/library/delivery.js +21 -0
  224. package/build/src/library/fleet.d.ts +65 -0
  225. package/build/src/library/fleet.js +153 -0
  226. package/build/src/library/input.d.ts +3 -0
  227. package/build/src/library/input.js +19 -0
  228. package/build/src/library/keiyaku.d.ts +18 -3
  229. package/build/src/library/keiyaku.js +12 -1
  230. package/build/src/library/mutation.d.ts +39 -0
  231. package/build/src/library/mutation.js +37 -0
  232. package/build/src/library/reconcile.d.ts +29 -0
  233. package/build/src/library/reconcile.js +179 -0
  234. package/build/src/library/refusal.d.ts +21 -0
  235. package/build/src/library/refusal.js +29 -0
  236. package/build/src/library/region.d.ts +14 -1
  237. package/build/src/library/region.js +18 -4
  238. package/build/src/library/repo.d.ts +4 -13
  239. package/build/src/library/repo.js +14 -24
  240. package/build/src/protocol/attempt.d.ts +16 -3
  241. package/build/src/protocol/attempt.js +41 -10
  242. package/build/src/protocol/bind.d.ts +9 -2
  243. package/build/src/protocol/bind.js +48 -40
  244. package/build/src/protocol/intent.d.ts +57 -20
  245. package/build/src/protocol/intent.js +55 -79
  246. package/build/src/protocol/operations.d.ts +152 -43
  247. package/build/src/protocol/operations.js +482 -166
  248. package/build/src/protocol/outcome.d.ts +14 -3
  249. package/build/src/protocol/outcome.js +11 -3
  250. package/build/src/protocol/placement.d.ts +41 -0
  251. package/build/src/protocol/placement.js +147 -0
  252. package/build/src/protocol/read/documents.d.ts +2 -2
  253. package/build/src/protocol/read/documents.js +4 -4
  254. package/build/src/protocol/read/status.d.ts +18 -5
  255. package/build/src/protocol/read/status.js +73 -13
  256. package/build/src/protocol/run.d.ts +49 -7
  257. package/build/src/protocol/run.js +53 -25
  258. package/build/src/runtime/proc/line-rpc.d.ts +4 -8
  259. package/build/src/runtime/proc/line-rpc.js +22 -28
  260. package/build/src/runtime/proc/run.d.ts +23 -21
  261. package/build/src/runtime/proc/run.js +123 -89
  262. package/build/src/runtime/proc/stdio.d.ts +18 -0
  263. package/build/src/runtime/proc/stdio.js +58 -0
  264. package/build/src/settings.d.ts +7 -1
  265. package/build/src/settings.js +32 -17
  266. package/build/src/settlement/fence.d.ts +5 -0
  267. package/build/src/settlement/fence.js +14 -0
  268. package/build/src/settlement/holder.d.ts +35 -4
  269. package/build/src/settlement/holder.js +127 -28
  270. package/build/src/settlement/settle.d.ts +9 -1
  271. package/build/src/settlement/settle.js +74 -37
  272. package/build/src/task/board.d.ts +23 -5
  273. package/build/src/task/board.js +17 -10
  274. package/build/src/task/compose.d.ts +2 -2
  275. package/build/src/task/compose.js +18 -10
  276. package/build/src/task/context.d.ts +3 -3
  277. package/build/src/task/context.js +15 -20
  278. package/build/src/task/document.d.ts +2 -1
  279. package/build/src/task/document.js +15 -5
  280. package/build/src/task/index.d.ts +27 -15
  281. package/build/src/task/index.js +57 -35
  282. package/build/src/task/operations.d.ts +38 -13
  283. package/build/src/task/operations.js +80 -46
  284. package/build/src/task/query.d.ts +67 -0
  285. package/build/src/task/query.js +279 -0
  286. package/build/src/task/store.d.ts +5 -7
  287. package/build/src/task/store.js +43 -28
  288. package/build/src/verification/declaration.d.ts +1 -0
  289. package/build/src/verification/execution.d.ts +51 -0
  290. package/build/src/verification/execution.js +108 -0
  291. package/build/src/workspace-place.d.ts +41 -0
  292. package/build/src/workspace-place.js +386 -0
  293. package/build/src/world.d.ts +24 -0
  294. package/build/src/world.js +136 -0
  295. package/package.json +8 -4
  296. package/build/src/akuma/activity.d.ts +0 -66
  297. package/build/src/akuma/activity.js +0 -139
  298. package/build/src/akuma/providers/claude.js +0 -305
  299. package/build/src/context-root.d.ts +0 -4
  300. package/build/src/context-root.js +0 -15
  301. package/build/src/git/delivery.d.ts +0 -20
  302. package/build/src/git/delivery.js +0 -109
  303. package/build/src/git/verification.d.ts +0 -14
  304. package/build/src/git/verification.js +0 -56
  305. package/build/src/protocol/read/audit.d.ts +0 -25
  306. package/build/src/protocol/read/audit.js +0 -39
  307. package/build/src/verification/producer.d.ts +0 -23
  308. package/build/src/verification/producer.js +0 -54
@@ -0,0 +1,179 @@
1
+ import { AuthorityCorruptionError } from "../core/facts/errors.js";
2
+ import { reconcileAllOperation, reconcileObservationFailure, reconcileOperation, stateOperation, worldContractStates, } from "../protocol/operations.js";
3
+ import { settle, settleAll } from "../settlement/settle.js";
4
+ import { projectContractWorktree, } from "../contract-worktree.js";
5
+ import { appointManagedWorktrees, placeRegisterPath, releaseManagedWorktrees, } from "../workspace-place.js";
6
+ function registerLag(scope, error) {
7
+ return {
8
+ kind: "contract-file-failed",
9
+ worktree: scope.primaryWorktree,
10
+ path: placeRegisterPath(scope),
11
+ diagnostic: error instanceof Error ? error.message : String(error),
12
+ };
13
+ }
14
+ function emptySettlement() {
15
+ return { actions: [], lags: [] };
16
+ }
17
+ function isManagedWorktree(state) {
18
+ return state.coordinates.workspace === "worktree";
19
+ }
20
+ function isManagedTerminal(state) {
21
+ return state !== null && isManagedWorktree(state) && state.terminal !== null;
22
+ }
23
+ function appointableManagedContracts(states) {
24
+ return states
25
+ .filter((state) => isManagedWorktree(state) && state.terminal === null)
26
+ .map((state) => state.id);
27
+ }
28
+ async function appointPlaces(scope, states) {
29
+ return await appointManagedWorktrees(scope, appointableManagedContracts(states));
30
+ }
31
+ function releaseEligible(state, cleanup, appointed) {
32
+ return cleanup !== undefined
33
+ && cleanup.lag.length === 0
34
+ && isManagedTerminal(state)
35
+ && appointed;
36
+ }
37
+ async function releaseAppointments(scope, contracts) {
38
+ try {
39
+ await releaseManagedWorktrees(scope, contracts);
40
+ return undefined;
41
+ }
42
+ catch (error) {
43
+ if (error instanceof AuthorityCorruptionError || error instanceof TypeError)
44
+ throw error;
45
+ return registerLag(scope, error);
46
+ }
47
+ }
48
+ async function observeState(scope, channel, contractId) {
49
+ try {
50
+ return { state: await stateOperation({ scope, channel, contractId }) };
51
+ }
52
+ catch (error) {
53
+ if (error instanceof AuthorityCorruptionError || error instanceof TypeError)
54
+ throw error;
55
+ return { failed: reconcileObservationFailure(error) };
56
+ }
57
+ }
58
+ async function appointForContract(scope, state) {
59
+ if (!isManagedWorktree(state))
60
+ return {};
61
+ try {
62
+ const register = await appointPlaces(scope, [state]);
63
+ const place = register.byContract.get(state.id)?.place;
64
+ if (place === undefined) {
65
+ if (state.terminal !== null)
66
+ return { register };
67
+ throw new Error(`Place appointment was not recorded: ${state.id}`);
68
+ }
69
+ return { place, register };
70
+ }
71
+ catch (error) {
72
+ if (error instanceof AuthorityCorruptionError || error instanceof TypeError)
73
+ throw error;
74
+ return { lag: registerLag(scope, error) };
75
+ }
76
+ }
77
+ export async function completeReconcile(input) {
78
+ const observed = await observeState(input.scope, input.channel, input.contractId);
79
+ if ("failed" in observed) {
80
+ return { effects: observed.failed.effects, lag: observed.failed.lag, settlement: emptySettlement() };
81
+ }
82
+ const appointment = await appointForContract(input.scope, observed.state);
83
+ if ("lag" in appointment) {
84
+ return { effects: [], lag: [appointment.lag], settlement: emptySettlement() };
85
+ }
86
+ const appointed = appointment.place === undefined ? {} : { place: appointment.place };
87
+ const retained = await reconcileOperation({
88
+ ...input,
89
+ retainTerminalWorktree: true,
90
+ ...appointed,
91
+ });
92
+ const projection = await projectContractWorktree(input.scope, retained.state, appointment.register);
93
+ const settlement = await settle({
94
+ repository: input.scope,
95
+ channel: input.channel,
96
+ state: retained.state,
97
+ effects: retained.report.effects,
98
+ });
99
+ const cleanup = isManagedTerminal(retained.state)
100
+ ? await reconcileOperation({ ...input, ...appointed })
101
+ : null;
102
+ const release = releaseEligible(retained.state, cleanup?.report, appointment.place !== undefined)
103
+ ? await releaseAppointments(input.scope, [input.contractId])
104
+ : undefined;
105
+ return {
106
+ effects: [...retained.report.effects, ...projection.effects, ...(cleanup?.report.effects ?? [])],
107
+ lag: [...retained.report.lag, ...projection.lag, ...(cleanup?.report.lag ?? []), ...(release === undefined ? [] : [release])],
108
+ settlement,
109
+ };
110
+ }
111
+ function attachReleaseLag(contracts, released, lag) {
112
+ const affected = new Set(released);
113
+ return {
114
+ contracts: contracts.map((contract) => affected.has(contract.contractId)
115
+ ? { ...contract, report: { ...contract.report, lag: [...contract.report.lag, lag] } }
116
+ : contract),
117
+ };
118
+ }
119
+ export async function completeRepoReconcile(input) {
120
+ const states = await worldContractStates(input);
121
+ let appointed;
122
+ try {
123
+ appointed = await appointPlaces(input.scope, states);
124
+ }
125
+ catch (error) {
126
+ if (error instanceof AuthorityCorruptionError || error instanceof TypeError)
127
+ throw error;
128
+ const lag = registerLag(input.scope, error);
129
+ const contracts = [];
130
+ for (const state of states) {
131
+ contracts.push({
132
+ contractId: state.id,
133
+ report: isManagedWorktree(state)
134
+ ? { effects: [], lag: [lag], settlement: emptySettlement() }
135
+ : await completeReconcile({ ...input, contractId: state.id }),
136
+ });
137
+ }
138
+ return { contracts };
139
+ }
140
+ const places = new Map(appointed.appointments.map((appointment) => [appointment.contract, appointment.place]));
141
+ const retained = await reconcileAllOperation({
142
+ ...input,
143
+ retainTerminalWorktree: true,
144
+ places,
145
+ });
146
+ const settlements = await settleAll({
147
+ repository: input.scope,
148
+ channel: input.channel,
149
+ contracts: retained.contracts.map((contract) => ({
150
+ state: contract.state,
151
+ effects: contract.report.effects,
152
+ })),
153
+ });
154
+ const cleanup = retained.contracts.some((contract) => isManagedTerminal(contract.state))
155
+ ? await reconcileAllOperation({ ...input, places })
156
+ : null;
157
+ const later = cleanup === null
158
+ ? null
159
+ : new Map(cleanup.contracts.map((contract) => [contract.contractId, contract.report]));
160
+ const released = [];
161
+ const contracts = [];
162
+ for (const [index, contract] of retained.contracts.entries()) {
163
+ const report = later?.get(contract.contractId);
164
+ if (releaseEligible(contract.state, report, places.has(contract.contractId))) {
165
+ released.push(contract.contractId);
166
+ }
167
+ const projection = await projectContractWorktree(input.scope, contract.state, appointed);
168
+ contracts.push({
169
+ contractId: contract.contractId,
170
+ report: {
171
+ effects: [...contract.report.effects, ...projection.effects, ...(report?.effects ?? [])],
172
+ lag: [...contract.report.lag, ...projection.lag, ...(report?.lag ?? [])],
173
+ settlement: settlements[index],
174
+ },
175
+ });
176
+ }
177
+ const lag = await releaseAppointments(input.scope, released);
178
+ return lag === undefined ? { contracts } : attachReleaseLag(contracts, released, lag);
179
+ }
@@ -0,0 +1,21 @@
1
+ import type { ContractId } from "../core/facts/types.js";
2
+ import type { IntentOutcome, IntentRefusal, IntentRetry } from "../protocol/operations.js";
3
+ import type { AcceptedIntent } from "./mutation.js";
4
+ export type ContractAppointmentRefusal = Readonly<{
5
+ kind: "here-worktree-appointed";
6
+ path: string;
7
+ contract?: ContractId;
8
+ }>;
9
+ export type KeiyakuRefusal = IntentRefusal | ContractAppointmentRefusal;
10
+ export type KeiyakuRetryReason = IntentRetry;
11
+ export declare class KeiyakuRefused extends Error {
12
+ readonly refusal: KeiyakuRefusal;
13
+ constructor(refusal: KeiyakuRefusal);
14
+ get code(): KeiyakuRefusal["kind"];
15
+ }
16
+ export declare class KeiyakuRetry extends Error {
17
+ readonly reason: KeiyakuRetryReason;
18
+ constructor(reason: KeiyakuRetryReason);
19
+ get code(): KeiyakuRetryReason["kind"];
20
+ }
21
+ export declare function requireAccepted<Value, Refusal extends KeiyakuRefusal>(result: IntentOutcome<Value, Refusal>): AcceptedIntent<Value>;
@@ -0,0 +1,29 @@
1
+ export class KeiyakuRefused extends Error {
2
+ refusal;
3
+ constructor(refusal) {
4
+ super(`Keiyaku refused: ${refusal.kind}`);
5
+ this.refusal = refusal;
6
+ this.name = "KeiyakuRefused";
7
+ }
8
+ get code() {
9
+ return this.refusal.kind;
10
+ }
11
+ }
12
+ export class KeiyakuRetry extends Error {
13
+ reason;
14
+ constructor(reason) {
15
+ super(reason.kind === "publication-failed" ? reason.diagnostic : `Keiyaku retry required: ${reason.kind}`);
16
+ this.reason = reason;
17
+ this.name = "KeiyakuRetry";
18
+ }
19
+ get code() {
20
+ return this.reason.kind;
21
+ }
22
+ }
23
+ export function requireAccepted(result) {
24
+ if (result.kind === "refused")
25
+ throw new KeiyakuRefused(result.refusal);
26
+ if (result.kind === "retry")
27
+ throw new KeiyakuRetry(result.reason);
28
+ return result;
29
+ }
@@ -1,5 +1,7 @@
1
1
  import type { ContractId } from "../core/facts/types.js";
2
+ import type { GitDecodeChannel } from "../git/read-observation.js";
2
3
  import { type RepositoryScope } from "../protocol/operations.js";
4
+ import type { ContractDocumentProjection } from "../protocol/operations.js";
3
5
  export type RegionOverlap = Readonly<{
4
6
  contract: ContractId;
5
7
  patterns: readonly Readonly<{
@@ -14,4 +16,15 @@ export type RegionObservation = Readonly<{
14
16
  overlapFailure: string;
15
17
  overlaps?: never;
16
18
  }>;
17
- export declare function observeRegion(scope: RepositoryScope, self: ContractId, mine: readonly string[]): RegionObservation;
19
+ export type RegionDeclarationRead = Readonly<{
20
+ contract: ContractId;
21
+ patterns: readonly string[];
22
+ }>;
23
+ export declare function readRegionDeclarations(documents: readonly ContractDocumentProjection[]): readonly RegionDeclarationRead[];
24
+ export declare function regionIntersections(left: readonly string[], right: readonly string[]): readonly Readonly<{
25
+ left: string;
26
+ right: string;
27
+ }>[];
28
+ export declare function regionPathMatches(patterns: readonly string[], path: string): readonly string[];
29
+ export declare function validateRegionPath(path: unknown): asserts path is string;
30
+ export declare function observeRegion(scope: RepositoryScope, channel: GitDecodeChannel, self: ContractId, mine: readonly string[]): Promise<RegionObservation>;
@@ -1,13 +1,27 @@
1
1
  import { decodeContractDocument } from "../body/decode.js";
2
- import { regionsOverlap } from "../body/region.js";
3
- import { documentsOperation } from "../protocol/operations.js";
2
+ import { assertCanonicalRegionPath, regionPatternsMatchPath, regionsOverlap } from "../body/region.js";
3
+ import { documentsOperationAt } from "../protocol/operations.js";
4
+ export function readRegionDeclarations(documents) {
5
+ return documents.map((document) => ({ contract: document.contract, patterns: decodeContractDocument(document.documentBytes).region }));
6
+ }
7
+ export function regionIntersections(left, right) {
8
+ return regionsOverlap(left, right).map(([leftPattern, rightPattern]) => ({ left: leftPattern, right: rightPattern }));
9
+ }
10
+ export function regionPathMatches(patterns, path) {
11
+ return regionPatternsMatchPath(patterns, path);
12
+ }
13
+ export function validateRegionPath(path) {
14
+ if (typeof path !== "string")
15
+ throw new Error("Region path must be a string");
16
+ assertCanonicalRegionPath(path);
17
+ }
4
18
  function diagnostic(error) {
5
19
  return error instanceof Error ? error.message : String(error);
6
20
  }
7
- export function observeRegion(scope, self, mine) {
21
+ export async function observeRegion(scope, channel, self, mine) {
8
22
  try {
9
23
  const overlaps = [];
10
- for (const peer of documentsOperation({ scope })) {
24
+ for (const peer of await documentsOperationAt(scope, channel)) {
11
25
  if (peer.contract === self)
12
26
  continue;
13
27
  try {
@@ -1,8 +1,8 @@
1
- import { NoGitWorldError, type ReconcileReport as ProtocolReconcileReport, type RepositoryScope } from "../protocol/operations.js";
2
- import { type SettlementReport } from "../settlement/settle.js";
1
+ import { NoGitWorldError, type RepositoryScope } from "../protocol/operations.js";
3
2
  import { type WorktreeHooks } from "./configuration.js";
4
- import type { ContractId } from "../core/facts/types.js";
3
+ import { type RepoContractReconcileReport, type RepoReconcileReport } from "./reconcile.js";
5
4
  export { NoGitWorldError };
5
+ export type { RepoContractReconcileReport, RepoReconcileReport };
6
6
  export type RepoAtInput = Readonly<{
7
7
  path?: string;
8
8
  }>;
@@ -10,15 +10,6 @@ export type ReconcileInput = Readonly<{
10
10
  hooks?: WorktreeHooks;
11
11
  retryHooks?: boolean;
12
12
  }>;
13
- export type RepoContractReconcileReport = Readonly<ProtocolReconcileReport & {
14
- settlement: SettlementReport;
15
- }>;
16
- export type RepoReconcileReport = Readonly<{
17
- contracts: readonly Readonly<{
18
- contractId: ContractId;
19
- report: RepoContractReconcileReport;
20
- }>[];
21
- }>;
22
13
  export declare function reconcileInput(input: ReconcileInput | undefined): Readonly<{
23
14
  hooks: WorktreeHooks;
24
15
  retryHooks: boolean;
@@ -26,7 +17,7 @@ export declare function reconcileInput(input: ReconcileInput | undefined): Reado
26
17
  export declare class Repo {
27
18
  readonly root: string;
28
19
  private constructor();
29
- static at(input?: RepoAtInput): Repo;
20
+ static at(input?: RepoAtInput): Promise<Repo>;
30
21
  currentBranch(): Promise<string | null>;
31
22
  reconcile(input?: ReconcileInput): Promise<RepoReconcileReport>;
32
23
  }
@@ -1,11 +1,12 @@
1
- import { currentBranchOperation, NoGitWorldError, reconcileAllOperation, scopeOperation, } from "../protocol/operations.js";
2
- import { settleAll } from "../settlement/settle.js";
1
+ import { currentBranchOperation, NoGitWorldError, scopeOperation, } from "../protocol/operations.js";
3
2
  import { optionalNonblank, requireInput } from "./input.js";
4
3
  import { worktreeHooksOption } from "./configuration.js";
4
+ import { withGitDecodeChannel } from "../git/read-observation.js";
5
+ import { completeRepoReconcile, } from "./reconcile.js";
5
6
  export { NoGitWorldError };
6
7
  const REPO_SCOPES = new WeakMap();
7
- function resolvePinnedScope(path) {
8
- return scopeOperation({ coordinate: path === undefined ? process.cwd() : path });
8
+ async function resolvePinnedScope(path) {
9
+ return await scopeOperation({ coordinate: path === undefined ? process.cwd() : path });
9
10
  }
10
11
  export function reconcileInput(input) {
11
12
  const values = input === undefined ? undefined : requireInput(input, "reconcile input");
@@ -23,32 +24,21 @@ export class Repo {
23
24
  this.root = scope.primaryWorktree;
24
25
  REPO_SCOPES.set(this, scope);
25
26
  }
26
- static at(input) {
27
+ static async at(input) {
27
28
  const values = input === undefined ? undefined : requireInput(input, "Repo.at input");
28
- return new Repo(resolvePinnedScope(optionalNonblank(values?.path, "repository path")));
29
+ return new Repo(await resolvePinnedScope(optionalNonblank(values?.path, "repository path")));
29
30
  }
30
31
  async currentBranch() {
31
- return currentBranchOperation({ scope: scopeForRepo(this) });
32
+ return await currentBranchOperation({ scope: scopeForRepo(this) });
32
33
  }
33
34
  async reconcile(input) {
34
35
  const scope = scopeForRepo(this);
35
- const reconciled = await reconcileAllOperation({ scope, ...reconcileInput(input) });
36
- const settlements = await settleAll({
37
- repository: scope,
38
- contracts: reconciled.contracts.map((contract) => ({
39
- state: contract.state,
40
- effects: contract.report.effects,
41
- })),
42
- });
43
- return {
44
- contracts: reconciled.contracts.map((contract, index) => ({
45
- contractId: contract.contractId,
46
- report: {
47
- ...contract.report,
48
- settlement: settlements[index],
49
- },
50
- })),
51
- };
36
+ const options = reconcileInput(input);
37
+ return await withGitDecodeChannel(scope, (channel) => completeRepoReconcile({
38
+ scope,
39
+ channel,
40
+ ...options,
41
+ }));
52
42
  }
53
43
  }
54
44
  export function scopeForRepo(value) {
@@ -1,9 +1,14 @@
1
1
  import { type PublicationFailed } from "../git/admission.js";
2
2
  import { type GitDecisionObservation } from "../git/observe.js";
3
- import { type GitRepository } from "../git/repository.js";
3
+ import type { GitDecodeChannel } from "../git/read-observation.js";
4
+ import { type GitRefAssertion, type GitRepository } from "../git/repository.js";
4
5
  import type { AttemptContext } from "../core/decide.js";
5
6
  import type { Offer } from "../core/facts/offer.js";
6
- import { type ContractId, type ContractState, type JournalEntry } from "../core/facts/types.js";
7
+ import { type ContractId, type ContractState, type EntryUlid, type JournalEntry } from "../core/facts/types.js";
8
+ export declare function mintEntryUlids(count: number): readonly EntryUlid[];
9
+ export declare function mintAttempts(input: Readonly<{
10
+ entryCount: number;
11
+ }>): readonly AttemptContext[];
7
12
  export type UnknownAttemptClassification = Readonly<{
8
13
  kind: "accepted";
9
14
  }> | Readonly<{
@@ -29,7 +34,15 @@ export type DecidedOfferResult = AcceptedAdmission | AttemptTerminal | Readonly<
29
34
  kind: "redecide";
30
35
  }>;
31
36
  /** Admit one decided offer without making another legal decision. */
32
- export declare function admitDecidedOffer(repository: GitRepository, decisionObservation: GitDecisionObservation, attempt: AttemptContext, offer: Offer, primaryContract: ContractId): DecidedOfferResult;
37
+ export declare function admitDecidedOffer(input: Readonly<{
38
+ channel: GitDecodeChannel;
39
+ repository: GitRepository;
40
+ decisionObservation: GitDecisionObservation;
41
+ attempt: AttemptContext;
42
+ offer: Offer;
43
+ primaryContract: ContractId;
44
+ assertions?: readonly GitRefAssertion[];
45
+ }>): Promise<DecidedOfferResult>;
33
46
  /** Classify an unknown atomic-publication outcome using only a captured Git observation. */
34
47
  export declare function classifyUnknownAttempt(observation: Readonly<{
35
48
  contracts: GitDecisionObservation["journals"];
@@ -1,9 +1,35 @@
1
+ import { randomBytes } from "node:crypto";
1
2
  import { encodeEntry } from "../core/facts/codec.js";
2
3
  import { admit } from "../git/admission.js";
3
- import { observeContracts } from "../git/observe.js";
4
+ import { observeContractsForAdmissionAt } from "../git/observe.js";
4
5
  import { GIT_REF, readRef } from "../git/repository.js";
5
6
  import { foldJournal } from "../core/facts/fold.js";
6
7
  import { AuthorityCorruptionError } from "../core/facts/errors.js";
8
+ import { entryUlid, } from "../core/facts/types.js";
9
+ const ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
10
+ const MAX_SEMANTIC_ATTEMPTS = 3;
11
+ function nextEntryUlid() {
12
+ let value = "";
13
+ let time = BigInt(Date.now());
14
+ for (let index = 0; index < 10; index += 1) {
15
+ value = ALPHABET[Number(time & 31n)] + value;
16
+ time >>= 5n;
17
+ }
18
+ let random = BigInt(`0x${randomBytes(10).toString("hex")}`);
19
+ for (let index = 0; index < 16; index += 1) {
20
+ value += ALPHABET[Number(random & 31n)];
21
+ random >>= 5n;
22
+ }
23
+ return entryUlid(value);
24
+ }
25
+ export function mintEntryUlids(count) {
26
+ return Array.from({ length: count }, nextEntryUlid);
27
+ }
28
+ export function mintAttempts(input) {
29
+ return Array.from({ length: MAX_SEMANTIC_ATTEMPTS }, () => ({
30
+ entryUlids: mintEntryUlids(input.entryCount),
31
+ }));
32
+ }
7
33
  function offerEntries(offer) {
8
34
  return offer.facts.flatMap((append) => append.entries);
9
35
  }
@@ -51,17 +77,22 @@ function recoveredAcceptance(observation, offer, primaryContract) {
51
77
  journal: record.entries,
52
78
  };
53
79
  }
54
- function publicationPremiseMoved(repository, observation, offer) {
55
- if (readRef(repository, GIT_REF) !== observation.admission.snapshot.commit)
80
+ async function publicationPremiseMoved(repository, observation, offer, assertions) {
81
+ if (await readRef(repository, GIT_REF) !== observation.admission.snapshot.commit)
56
82
  return true;
83
+ for (const assertion of assertions)
84
+ if (await readRef(repository, assertion.ref) !== assertion.oid)
85
+ return true;
57
86
  return offer.target !== undefined
58
- && readRef(repository, offer.target.target) !== offer.target.expectedOid;
87
+ && await readRef(repository, offer.target.target) !== offer.target.expectedOid;
59
88
  }
60
89
  /** Admit one decided offer without making another legal decision. */
61
- export function admitDecidedOffer(repository, decisionObservation, attempt, offer, primaryContract) {
90
+ export async function admitDecidedOffer(input) {
91
+ const { channel, repository, decisionObservation, attempt, offer, primaryContract } = input;
92
+ const assertions = input.assertions ?? [];
62
93
  validateOffer(offer, attempt);
63
94
  const primary = primaryAppend(offer, primaryContract);
64
- const admission = admit(repository, offer, decisionObservation.admission);
95
+ const admission = await admit(repository, offer, decisionObservation.admission, assertions);
65
96
  if (admission.kind === "accepted") {
66
97
  return {
67
98
  kind: "accepted",
@@ -71,14 +102,14 @@ export function admitDecidedOffer(repository, decisionObservation, attempt, offe
71
102
  };
72
103
  }
73
104
  if (admission.kind === "publication-failed") {
74
- return publicationPremiseMoved(repository, decisionObservation, offer)
105
+ return await publicationPremiseMoved(repository, decisionObservation, offer, assertions)
75
106
  ? { kind: "redecide" }
76
107
  : admission;
77
108
  }
78
- const recovered = observeContracts(repository, offer.facts.map((append) => append.contractId));
79
- const classification = classifyUnknownAttempt(recovered, offer);
109
+ const recovered = await observeContractsForAdmissionAt(repository, channel, offer.facts.map((append) => append.contractId));
110
+ const classification = classifyUnknownAttempt({ contracts: recovered.journals }, offer);
80
111
  if (classification.kind === "accepted")
81
- return recoveredAcceptance(recovered, offer, primaryContract);
112
+ return recoveredAcceptance({ contracts: recovered.journals }, offer, primaryContract);
82
113
  return classification.kind === "collision" ? { kind: "collision" } : { kind: "redecide" };
83
114
  }
84
115
  /** Classify an unknown atomic-publication outcome using only a captured Git observation. */
@@ -1,4 +1,5 @@
1
1
  import type { GitRepository } from "../git/repository.js";
2
+ import type { GitDecodeChannel } from "../git/read-observation.js";
2
3
  import type { BindData, ActorId, ContractId } from "../core/facts/types.js";
3
4
  import { type BindRefusal } from "../core/verbs/bind.js";
4
5
  export type { BindRefusal } from "../core/verbs/bind.js";
@@ -9,9 +10,14 @@ export type TargetInputRefusal = Readonly<{
9
10
  kind: "invalid-target";
10
11
  }> | Readonly<{
11
12
  kind: "target-missing";
13
+ }> | Readonly<{
14
+ kind: "here-target-mismatch";
15
+ target: string;
16
+ branch: string | null;
12
17
  }>;
13
18
  type BindOperationInput = Readonly<{
14
19
  scope: GitRepository;
20
+ channel: GitDecodeChannel;
15
21
  title: string;
16
22
  terms: BindData["terms"];
17
23
  verification: VerificationDeclarationPreparation;
@@ -19,7 +25,8 @@ type BindOperationInput = Readonly<{
19
25
  workspace: "worktree" | "here";
20
26
  actor?: ActorId;
21
27
  decorateOffer?: CompanionDecorator;
28
+ contractId?: ContractId;
22
29
  }>;
23
- export declare function bindOperation(input: BindOperationInput): IntentOutcome<Readonly<{
30
+ export declare function bindOperation(input: BindOperationInput): Promise<IntentOutcome<Readonly<{
24
31
  contractId: ContractId;
25
- }>, BindRefusal | TargetInputRefusal | VerificationDeclarationRefusal>;
32
+ }>, BindRefusal | TargetInputRefusal | VerificationDeclarationRefusal>>;
@@ -1,62 +1,70 @@
1
- import { randomBytes } from "node:crypto";
2
1
  import { normalizeTargetBranch, observeBindCoordinates } from "../git/observe.js";
2
+ import { gitObjectIdForSnapshot } from "../git/identity.js";
3
3
  import { contractIdFromSegment } from "../core/facts/types.js";
4
4
  import { decideBind } from "../core/verbs/bind.js";
5
- import { fitIdentityStem, normalizeIdentityStem } from "../identity/normalize.js";
6
5
  import { admitIntent } from "./intent.js";
7
6
  import { complete } from "./outcome.js";
8
- const CONTRACT_ID_STEM_BYTES = 72;
9
- const CROCKFORD_BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
10
- function mintCollisionSuffix() {
11
- let random = BigInt(`0x${randomBytes(5).toString("hex")}`);
12
- let suffix = "";
13
- for (let index = 0; index < 8; index += 1) {
14
- suffix = CROCKFORD_BASE32[Number(random & 31n)] + suffix;
15
- random >>= 5n;
16
- }
17
- return suffix.toLowerCase();
7
+ import { fitIdentityStem, normalizeIdentityStem } from "../identity/normalize.js";
8
+ const COLLISION_ATTEMPTS = 3;
9
+ function contractIdFor(title, suffix) {
10
+ const stem = fitIdentityStem({ stem: normalizeIdentityStem({ source: title }) || "contract", maxBytes: 48 });
11
+ return contractIdFromSegment(suffix === undefined ? stem : `${stem}-${suffix}`);
18
12
  }
19
- function contractIdFromStem(stem, suffix) {
20
- return contractIdFromSegment(fitIdentityStem({
21
- stem,
22
- maxBytes: CONTRACT_ID_STEM_BYTES,
23
- ...(suffix === undefined ? {} : { suffix }),
24
- }));
13
+ async function bindPreparation(input, target, seed) {
14
+ if (input.verification.kind === "refused") {
15
+ return { kind: "prepared", input: { ...seed, preparation: input.verification } };
16
+ }
17
+ const observed = await observeBindCoordinates(input.scope, target);
18
+ if (observed === null)
19
+ return { kind: "refused", refusal: { kind: "target-missing" } };
20
+ if (input.workspace === "here" && target !== undefined && observed.branch !== target) {
21
+ return { kind: "refused", refusal: { kind: "here-target-mismatch", target, branch: observed.branch } };
22
+ }
23
+ const oid = gitObjectIdForSnapshot(observed.start);
24
+ const assertions = [{ ref: target ?? "HEAD", oid }];
25
+ return {
26
+ kind: "prepared",
27
+ input: {
28
+ ...seed,
29
+ preparation: {
30
+ kind: "prepared",
31
+ data: {
32
+ coordinates: {
33
+ start: observed.start,
34
+ ...(observed.target === undefined ? {} : { target: observed.target }),
35
+ workspace: input.workspace,
36
+ },
37
+ terms: input.terms,
38
+ },
39
+ },
40
+ },
41
+ assertions,
42
+ };
25
43
  }
26
- export function bindOperation(input) {
44
+ export async function bindOperation(input) {
27
45
  let target;
28
46
  if (input.target !== undefined) {
29
- const normalized = normalizeTargetBranch(input.scope, input.target);
47
+ const normalized = await normalizeTargetBranch(input.scope, input.target);
30
48
  if (normalized === null)
31
49
  return { kind: "refused", refusal: { kind: "invalid-target" } };
32
50
  target = normalized;
33
51
  }
34
- const observed = observeBindCoordinates(input.scope, target);
35
- if (observed === null)
36
- return { kind: "refused", refusal: { kind: "target-missing" } };
37
- const data = {
38
- coordinates: {
39
- start: observed.start,
40
- ...(observed.target === undefined ? {} : { target: observed.target }),
41
- workspace: input.workspace,
42
- },
43
- terms: input.terms,
44
- };
45
- const stem = normalizeIdentityStem({ source: input.title }) || "contract";
46
52
  const at = new Date().toISOString();
47
- const attempt = (id) => complete(admitIntent(input.scope, {
53
+ const attempt = async (id) => complete(await admitIntent(input.channel, input.scope, {
48
54
  contractId: id,
49
55
  ...(input.actor === undefined ? {} : { actor: input.actor }),
50
56
  at,
51
- preparation: input.verification.kind === "prepared"
52
- ? { kind: "prepared", data }
53
- : { kind: "refused", refusal: input.verification.refusal },
54
57
  }, decideBind, {
55
58
  observedContracts: [id, ...input.terms.after],
59
+ prepareInput: async (_observation, original) => bindPreparation(input, target, original),
56
60
  ...(input.decorateOffer === undefined ? {} : { decorateOffer: input.decorateOffer }),
57
61
  }), { contractId: id });
58
- const first = attempt(contractIdFromStem(stem));
59
- return first.kind === "refused" && first.refusal.kind === "contract-exists"
60
- ? attempt(contractIdFromStem(stem, mintCollisionSuffix()))
61
- : first;
62
+ if (input.contractId !== undefined)
63
+ return attempt(input.contractId);
64
+ let result = await attempt(contractIdFor(input.title));
65
+ for (let collision = 0; collision < COLLISION_ATTEMPTS && result.kind === "refused" && result.refusal.kind === "contract-exists"; collision += 1) {
66
+ result = await attempt(contractIdFor(input.title, randomBytes(8).toString("hex")));
67
+ }
68
+ return result;
62
69
  }
70
+ import { randomBytes } from "node:crypto";