@astrosheep/keiyaku 4.1.0 → 4.1.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 (278) hide show
  1. package/README.md +1 -1
  2. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -0
  3. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +3 -0
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -6
  5. package/build/src/akuma/akuma-errors.d.ts +6 -0
  6. package/build/src/akuma/akuma-errors.js +9 -0
  7. package/build/src/akuma/akuma-handle.d.ts +39 -0
  8. package/build/src/akuma/akuma-handle.js +263 -0
  9. package/build/src/akuma/akuma-observe.d.ts +19 -0
  10. package/build/src/akuma/akuma-observe.js +74 -0
  11. package/build/src/akuma/akuma-product-symbols.d.ts +1 -0
  12. package/build/src/akuma/akuma-product-symbols.js +1 -0
  13. package/build/src/akuma/akuma-product.d.ts +18 -0
  14. package/build/src/akuma/akuma-product.js +154 -0
  15. package/build/src/akuma/akuma.d.ts +37 -81
  16. package/build/src/akuma/akuma.js +39 -447
  17. package/build/src/akuma/allowed.d.ts +1 -0
  18. package/build/src/akuma/allowed.js +6 -0
  19. package/build/src/akuma/archetype.js +8 -5
  20. package/build/src/akuma/body-supervisor.d.ts +26 -0
  21. package/build/src/akuma/body-supervisor.js +89 -0
  22. package/build/src/akuma/body.d.ts +45 -4
  23. package/build/src/akuma/body.js +145 -35
  24. package/build/src/akuma/coordinate.js +17 -7
  25. package/build/src/akuma/heart/facts.js +1 -1
  26. package/build/src/akuma/heart/index.d.ts +1 -1
  27. package/build/src/akuma/heart/index.js +2 -4
  28. package/build/src/akuma/heart/request-authority.js +4 -4
  29. package/build/src/akuma/heart/request-rows.js +27 -16
  30. package/build/src/akuma/heart/rows.js +79 -41
  31. package/build/src/akuma/heart/soul.js +1 -1
  32. package/build/src/akuma/heart/storage.d.ts +1 -0
  33. package/build/src/akuma/heart/storage.js +76 -11
  34. package/build/src/akuma/heart/tells.js +35 -18
  35. package/build/src/akuma/heart/timeline.js +19 -11
  36. package/build/src/akuma/identity.d.ts +1 -0
  37. package/build/src/akuma/identity.js +1 -0
  38. package/build/src/akuma/index.d.ts +3 -3
  39. package/build/src/akuma/index.js +1 -1
  40. package/build/src/akuma/nuke.js +106 -14
  41. package/build/src/akuma/projection-read.d.ts +17 -0
  42. package/build/src/akuma/projection-read.js +135 -0
  43. package/build/src/akuma/projection.d.ts +1 -16
  44. package/build/src/akuma/projection.js +77 -154
  45. package/build/src/akuma/provider-recipe.js +11 -5
  46. package/build/src/akuma/provider.js +105 -62
  47. package/build/src/akuma/providers/acp/config.js +5 -6
  48. package/build/src/akuma/providers/acp/core.js +17 -10
  49. package/build/src/akuma/providers/acp/events.js +38 -29
  50. package/build/src/akuma/providers/acp/index.js +13 -9
  51. package/build/src/akuma/providers/claude/events.js +8 -12
  52. package/build/src/akuma/providers/claude/index.d.ts +1 -0
  53. package/build/src/akuma/providers/claude/index.js +55 -26
  54. package/build/src/akuma/providers/claude/input.js +6 -2
  55. package/build/src/akuma/providers/codex-app-server/events.js +61 -36
  56. package/build/src/akuma/providers/codex-app-server/index.js +25 -13
  57. package/build/src/akuma/providers/grok-build/index.js +22 -25
  58. package/build/src/akuma/providers/index.js +2 -18
  59. package/build/src/akuma/providers/opencode-sdk/events.js +32 -18
  60. package/build/src/akuma/providers/opencode-sdk/index.js +26 -17
  61. package/build/src/akuma/providers/opencode-sdk/session.js +21 -5
  62. package/build/src/akuma/providers/pi/events.js +26 -19
  63. package/build/src/akuma/providers/pi/index.js +58 -40
  64. package/build/src/akuma/publication.js +9 -9
  65. package/build/src/akuma/request-execution.d.ts +6 -0
  66. package/build/src/akuma/request-execution.js +86 -0
  67. package/build/src/akuma/request-serve.d.ts +12 -1
  68. package/build/src/akuma/request-serve.js +19 -122
  69. package/build/src/akuma/request-wire.d.ts +2 -2
  70. package/build/src/akuma/request-wire.js +75 -102
  71. package/build/src/akuma/requests.js +2 -2
  72. package/build/src/akuma/{body-turn.d.ts → turn-drive.d.ts} +2 -25
  73. package/build/src/akuma/{body-turn.js → turn-drive.js} +20 -103
  74. package/build/src/akuma-body.js +6 -5
  75. package/build/src/body/amend.d.ts +4 -1
  76. package/build/src/body/amend.js +14 -6
  77. package/build/src/body/decode.js +2 -4
  78. package/build/src/body/envelope.js +2 -4
  79. package/build/src/body/region.d.ts +1 -2
  80. package/build/src/body/region.js +2 -70
  81. package/build/src/body/render.js +17 -12
  82. package/build/src/body/shape.js +1 -7
  83. package/build/src/cli/accepted.d.ts +1 -1
  84. package/build/src/cli/accepted.js +8 -1
  85. package/build/src/cli/commands/akuma-invoke.js +18 -6
  86. package/build/src/cli/commands/akuma.d.ts +0 -6
  87. package/build/src/cli/commands/akuma.js +11 -24
  88. package/build/src/cli/commands/amend.d.ts +2 -2
  89. package/build/src/cli/commands/amend.js +2 -4
  90. package/build/src/cli/commands/bind.d.ts +4 -4
  91. package/build/src/cli/commands/bind.js +16 -6
  92. package/build/src/cli/commands/contract-invoke.d.ts +25 -0
  93. package/build/src/cli/commands/contract-invoke.js +193 -0
  94. package/build/src/cli/commands/contract.d.ts +109 -10
  95. package/build/src/cli/commands/contract.js +339 -16
  96. package/build/src/cli/commands/install.d.ts +1 -1
  97. package/build/src/cli/commands/install.js +23 -15
  98. package/build/src/cli/commands/task-invoke.js +187 -67
  99. package/build/src/cli/commands/task-query.js +48 -24
  100. package/build/src/cli/commands/task.js +150 -38
  101. package/build/src/cli/coordinates.js +2 -4
  102. package/build/src/cli/invoke.js +92 -208
  103. package/build/src/cli/main.js +4 -2
  104. package/build/src/cli/parse.d.ts +3 -90
  105. package/build/src/cli/parse.js +27 -233
  106. package/build/src/cli/render/akuma-activity.d.ts +38 -0
  107. package/build/src/cli/render/akuma-activity.js +362 -0
  108. package/build/src/cli/render/akuma-tool-command.js +6 -7
  109. package/build/src/cli/render/akuma-tool.js +10 -8
  110. package/build/src/cli/render/akuma.d.ts +1 -1
  111. package/build/src/cli/render/akuma.js +61 -353
  112. package/build/src/cli/render/audit.js +5 -15
  113. package/build/src/cli/render/board.d.ts +1 -0
  114. package/build/src/cli/render/board.js +17 -0
  115. package/build/src/cli/render/catalog.js +81 -8
  116. package/build/src/cli/render/contract-history.d.ts +2 -0
  117. package/build/src/cli/render/contract-history.js +83 -0
  118. package/build/src/cli/render/contract-observation.d.ts +10 -0
  119. package/build/src/cli/render/contract-observation.js +58 -0
  120. package/build/src/cli/render/contract.d.ts +1 -2
  121. package/build/src/cli/render/contract.js +41 -142
  122. package/build/src/cli/render/kanshi.js +239 -215
  123. package/build/src/cli/render/receipt.js +2 -9
  124. package/build/src/cli/render/refusal.js +10 -15
  125. package/build/src/cli/render/region.js +22 -6
  126. package/build/src/cli/render/settings.js +4 -4
  127. package/build/src/cli/render/task.js +34 -14
  128. package/build/src/cli/render/terminal.js +16 -5
  129. package/build/src/cli/result.d.ts +3 -3
  130. package/build/src/cli/runtime.d.ts +6 -1
  131. package/build/src/cli/runtime.js +84 -63
  132. package/build/src/cli/selectors.js +2 -4
  133. package/build/src/cli/usage.js +6 -5
  134. package/build/src/contract-worktree.d.ts +1 -23
  135. package/build/src/contract-worktree.js +79 -155
  136. package/build/src/coordination/durable-file.js +7 -3
  137. package/build/src/coordination/sqlite-transaction-lock.js +29 -12
  138. package/build/src/core/facts/codec.js +7 -3
  139. package/build/src/core/facts/types.d.ts +1 -1
  140. package/build/src/core/subject.js +16 -9
  141. package/build/src/core/verbs/abandon.d.ts +1 -1
  142. package/build/src/core/verbs/abandon.js +1 -1
  143. package/build/src/core/verbs/amend.d.ts +1 -1
  144. package/build/src/core/verbs/amend.js +7 -7
  145. package/build/src/core/verbs/attestation.js +4 -1
  146. package/build/src/core/verbs/bind.d.ts +1 -1
  147. package/build/src/core/verbs/bind.js +1 -1
  148. package/build/src/core/verbs/placement.d.ts +1 -1
  149. package/build/src/core/verbs/placement.js +10 -8
  150. package/build/src/dispatch/index.js +8 -6
  151. package/build/src/duration.js +5 -1
  152. package/build/src/git/admission.js +5 -3
  153. package/build/src/git/hooks.d.ts +8 -0
  154. package/build/src/git/hooks.js +63 -14
  155. package/build/src/git/identity.js +1 -4
  156. package/build/src/git/integration.d.ts +2 -3
  157. package/build/src/git/integration.js +32 -39
  158. package/build/src/git/nuke.d.ts +1 -1
  159. package/build/src/git/nuke.js +110 -57
  160. package/build/src/git/observe.d.ts +13 -1
  161. package/build/src/git/observe.js +51 -44
  162. package/build/src/git/process.js +4 -2
  163. package/build/src/git/read-observation.js +10 -8
  164. package/build/src/git/reconcile.d.ts +22 -2
  165. package/build/src/git/reconcile.js +38 -274
  166. package/build/src/git/repository.d.ts +2 -0
  167. package/build/src/git/repository.js +13 -22
  168. package/build/src/git/scratch.d.ts +8 -5
  169. package/build/src/git/scratch.js +29 -2
  170. package/build/src/git/target-placement.d.ts +9 -10
  171. package/build/src/git/target-placement.js +59 -62
  172. package/build/src/git/tender.js +13 -46
  173. package/build/src/git/terminal-reconcile.d.ts +21 -0
  174. package/build/src/git/terminal-reconcile.js +278 -0
  175. package/build/src/git/terminal-seal.js +15 -12
  176. package/build/src/git/tree.js +5 -2
  177. package/build/src/git/workspace.d.ts +24 -2
  178. package/build/src/git/workspace.js +120 -16
  179. package/build/src/identity/coordinates.js +3 -1
  180. package/build/src/identity/selector.js +6 -3
  181. package/build/src/kanshi/fleet.d.ts +6 -0
  182. package/build/src/kanshi/fleet.js +27 -0
  183. package/build/src/kanshi/index.d.ts +1 -1
  184. package/build/src/kanshi/read.js +60 -48
  185. package/build/src/kanshi/report.d.ts +11 -23
  186. package/build/src/kanshi/select.js +25 -24
  187. package/build/src/library/address.d.ts +2 -1
  188. package/build/src/library/address.js +7 -8
  189. package/build/src/library/akuma-creation.js +20 -12
  190. package/build/src/library/audit.d.ts +0 -1
  191. package/build/src/library/audit.js +1 -2
  192. package/build/src/library/bind.d.ts +53 -5
  193. package/build/src/library/bind.js +98 -22
  194. package/build/src/library/catalog.d.ts +1 -0
  195. package/build/src/library/catalog.js +6 -3
  196. package/build/src/library/configuration.js +3 -1
  197. package/build/src/library/continuation.d.ts +15 -4
  198. package/build/src/library/continuation.js +24 -16
  199. package/build/src/library/contract-bind.d.ts +26 -0
  200. package/build/src/library/contract-bind.js +106 -0
  201. package/build/src/library/contract-operations.d.ts +77 -0
  202. package/build/src/library/contract-operations.js +130 -0
  203. package/build/src/library/contract-types.d.ts +95 -0
  204. package/build/src/library/contract-types.js +1 -0
  205. package/build/src/library/contract.d.ts +19 -154
  206. package/build/src/library/contract.js +46 -261
  207. package/build/src/library/fleet.d.ts +1 -1
  208. package/build/src/library/fleet.js +19 -11
  209. package/build/src/library/input.d.ts +2 -0
  210. package/build/src/library/input.js +14 -0
  211. package/build/src/library/keiyaku.d.ts +8 -7
  212. package/build/src/library/keiyaku.js +3 -3
  213. package/build/src/library/nuke.js +14 -5
  214. package/build/src/library/reconcile.d.ts +9 -4
  215. package/build/src/library/reconcile.js +30 -24
  216. package/build/src/library/refusal.d.ts +13 -5
  217. package/build/src/library/refusal.js +7 -0
  218. package/build/src/library/region.d.ts +8 -7
  219. package/build/src/library/region.js +31 -11
  220. package/build/src/library/repo.js +2 -2
  221. package/build/src/markdown/lex.js +9 -2
  222. package/build/src/markdown/parse.js +11 -7
  223. package/build/src/markdown/query.js +4 -2
  224. package/build/src/protocol/amend.js +3 -1
  225. package/build/src/protocol/attempt.d.ts +7 -3
  226. package/build/src/protocol/attempt.js +9 -5
  227. package/build/src/protocol/audit.d.ts +1 -1
  228. package/build/src/protocol/audit.js +4 -14
  229. package/build/src/protocol/bind.d.ts +19 -6
  230. package/build/src/protocol/bind.js +28 -13
  231. package/build/src/protocol/completion.d.ts +0 -1
  232. package/build/src/protocol/completion.js +1 -3
  233. package/build/src/protocol/deliver.d.ts +1 -3
  234. package/build/src/protocol/deliver.js +15 -39
  235. package/build/src/protocol/intent.d.ts +1 -0
  236. package/build/src/protocol/intent.js +9 -7
  237. package/build/src/protocol/operations.d.ts +3 -7
  238. package/build/src/protocol/operations.js +5 -3
  239. package/build/src/protocol/placement.d.ts +0 -1
  240. package/build/src/protocol/placement.js +6 -6
  241. package/build/src/protocol/read/observation.d.ts +11 -0
  242. package/build/src/protocol/read/observation.js +37 -0
  243. package/build/src/protocol/read/status.d.ts +23 -12
  244. package/build/src/protocol/read/status.js +90 -72
  245. package/build/src/protocol/reconcile.d.ts +1 -0
  246. package/build/src/protocol/reconcile.js +2 -3
  247. package/build/src/protocol/reintegrate.js +1 -1
  248. package/build/src/protocol/review.d.ts +0 -1
  249. package/build/src/protocol/review.js +15 -22
  250. package/build/src/protocol/run.d.ts +1 -0
  251. package/build/src/protocol/run.js +4 -5
  252. package/build/src/runtime/proc/launch.d.ts +33 -0
  253. package/build/src/runtime/proc/launch.js +98 -0
  254. package/build/src/runtime/proc/line-rpc.js +9 -3
  255. package/build/src/runtime/proc/run.d.ts +12 -6
  256. package/build/src/runtime/proc/run.js +78 -21
  257. package/build/src/runtime/proc/stdio.js +7 -10
  258. package/build/src/settings.js +6 -2
  259. package/build/src/settlement/holder.js +13 -10
  260. package/build/src/settlement/settle.js +39 -6
  261. package/build/src/task/board.js +12 -6
  262. package/build/src/task/compose.js +85 -22
  263. package/build/src/task/context.js +21 -8
  264. package/build/src/task/document.js +46 -10
  265. package/build/src/task/identity.js +5 -2
  266. package/build/src/task/index.d.ts +0 -1
  267. package/build/src/task/index.js +119 -34
  268. package/build/src/task/input.js +29 -7
  269. package/build/src/task/mutation.js +61 -19
  270. package/build/src/task/operations.d.ts +6 -1
  271. package/build/src/task/operations.js +83 -29
  272. package/build/src/task/query.js +55 -26
  273. package/build/src/task/store.d.ts +3 -1
  274. package/build/src/task/store.js +81 -17
  275. package/build/src/verification/execution.js +14 -11
  276. package/build/src/workspace-place.js +19 -7
  277. package/build/src/world.js +16 -6
  278. package/package.json +8 -3
@@ -1,9 +1,13 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import { releaseContractWorktree, reserveContractWorktree, withContractWorktreeAppointment, } from "../contract-worktree.js";
2
+ import { decodeContractDocument } from "../body/decode.js";
3
3
  import { contractIdFromSegment } from "../core/facts/types.js";
4
+ import { AuthorityCorruptionError } from "../core/facts/errors.js";
4
5
  import { fitIdentityStem, normalizeIdentityStem } from "../identity/normalize.js";
5
6
  import { bindOperation } from "../protocol/bind.js";
6
- import { claimTaskHolder } from "../settlement/holder.js";
7
+ import { stateOperation } from "../protocol/operations.js";
8
+ import { claimTaskHolder, claimTaskHolderWithFence } from "../settlement/holder.js";
9
+ import { KeiyakuRefused } from "./refusal.js";
10
+ import { contractTerms, documentDerivation } from "./input.js";
7
11
  function candidateId(title, collision) {
8
12
  const stem = fitIdentityStem({ stem: normalizeIdentityStem({ source: title }) || "contract", maxBytes: 48 });
9
13
  const suffix = collision === 0 ? "" : `-${randomBytes(8).toString("hex")}`;
@@ -16,11 +20,15 @@ async function attempt(input, id) {
16
20
  terms: input.terms,
17
21
  verification: input.verification,
18
22
  workspace: input.workspace,
23
+ ...(input.coordinates === undefined ? {} : { coordinates: input.coordinates }),
24
+ ...(input.source === undefined ? {} : { source: input.source }),
19
25
  contractId: id,
20
- ...(input.target === undefined ? {} : { target: input.target }),
21
- ...(input.task === undefined ? {} : {
22
- decorateOffer: ({ contractId: owner }) => [claimTaskHolder(input.task, owner)],
23
- }),
26
+ ...(input.targetSelection === undefined ? {} : { targetSelection: input.targetSelection }),
27
+ ...(input.task === undefined
28
+ ? {}
29
+ : {
30
+ decorateOffer: ({ contractId: owner }) => [claimTaskHolder(input.task, owner)],
31
+ }),
24
32
  ...(input.actor === undefined ? {} : { actor: input.actor }),
25
33
  });
26
34
  }
@@ -28,29 +36,97 @@ async function attemptCandidates(input) {
28
36
  let result;
29
37
  for (let collision = 0; collision <= 3; collision += 1) {
30
38
  const id = candidateId(input.title, collision);
31
- let reserved = false;
32
- if (input.workspace === "here") {
33
- const reservation = await reserveContractWorktree(input.scope, id);
34
- if (reservation.kind !== "reserved") {
35
- return { kind: "refused", refusal: {
36
- kind: "here-worktree-appointed",
37
- path: reservation.path,
38
- ...(reservation.kind === "appointed" ? { contract: reservation.contract } : {}),
39
- } };
40
- }
41
- reserved = true;
42
- }
43
39
  result = await attempt(input, id);
44
40
  if (result.kind === "accepted")
45
41
  return result;
46
- if (reserved)
47
- await releaseContractWorktree(input.scope, id);
48
42
  if (result.kind !== "refused" || result.refusal.kind !== "contract-exists")
49
43
  return result;
50
44
  }
51
45
  return result;
52
46
  }
53
47
  export async function admitBindWithAppointment(input) {
54
- const action = () => attemptCandidates(input);
55
- return input.workspace === "here" ? await withContractWorktreeAppointment(input.scope, action) : action();
48
+ return await attemptCandidates(input);
49
+ }
50
+ export async function admitForkBindWithAppointment(input) {
51
+ let source;
52
+ try {
53
+ source = await stateOperation({ scope: input.scope, channel: input.channel, contractId: input.sourceId });
54
+ }
55
+ catch (error) {
56
+ if (error instanceof Error && error.message === `contract does not exist: ${input.sourceId}`) {
57
+ throw new KeiyakuRefused({ kind: "fork-source-missing", contractId: input.sourceId });
58
+ }
59
+ if (error instanceof AuthorityCorruptionError) {
60
+ throw new KeiyakuRefused({ kind: "fork-source-invalid", contractId: input.sourceId });
61
+ }
62
+ throw error;
63
+ }
64
+ const sourceObject = (await input.channel.readObjects([source.coordinates.start])).get(source.coordinates.start);
65
+ if (sourceObject?.kind !== "present" || sourceObject.type !== "commit") {
66
+ throw new KeiyakuRefused({ kind: "fork-source-unavailable", contractId: input.sourceId });
67
+ }
68
+ const currentSource = await stateOperation({
69
+ scope: input.scope,
70
+ channel: input.channel,
71
+ contractId: input.sourceId,
72
+ });
73
+ if (currentSource.head !== source.head ||
74
+ currentSource.coordinates.start !== source.coordinates.start ||
75
+ currentSource.terms.document.key !== source.terms.document.key) {
76
+ throw new KeiyakuRefused({ kind: "fork-source-moved", contractId: input.sourceId });
77
+ }
78
+ let sourceDocument;
79
+ try {
80
+ sourceDocument = decodeContractDocument(source.terms.document.bytes);
81
+ }
82
+ catch (error) {
83
+ if (error instanceof TypeError) {
84
+ throw new KeiyakuRefused({ kind: "fork-source-invalid", contractId: input.sourceId });
85
+ }
86
+ throw error;
87
+ }
88
+ const markdown = sourceDocument.document.bytes.replace(/^\s*#\s*[^\r\n]*(?:\r?\n|$)/u, `# Fork · ${sourceDocument.title}${sourceDocument.document.bytes.includes("\r\n") ? "\r\n" : "\n"}`);
89
+ const document = decodeContractDocument(markdown);
90
+ const terms = contractTerms(document, source.terms.gates, source.terms.after);
91
+ const admission = await attemptCandidates({
92
+ scope: input.scope,
93
+ channel: input.channel,
94
+ title: document.title,
95
+ terms,
96
+ verification: documentDerivation(document, terms.gates).verification,
97
+ workspace: "worktree",
98
+ targetSelection: input.target === undefined
99
+ ? source.coordinates.target === undefined
100
+ ? { kind: "targetless" }
101
+ : { kind: "explicit", target: source.coordinates.target }
102
+ : { kind: "explicit", target: input.target },
103
+ coordinates: { start: source.coordinates.start },
104
+ source: {
105
+ contractId: input.sourceId,
106
+ head: source.head,
107
+ start: source.coordinates.start,
108
+ document: source.terms.document.key,
109
+ },
110
+ ...(input.actor === undefined ? {} : { actor: input.actor }),
111
+ });
112
+ return { admission, document };
113
+ }
114
+ export async function admitMarkdownBind(input) {
115
+ return await admitBindWithAppointment(input);
116
+ }
117
+ export async function prepareMarkdownBind(input) {
118
+ const terms = contractTerms(input.document, input.gates, input.after);
119
+ const admitCandidate = () => admitMarkdownBind({
120
+ scope: input.scope,
121
+ channel: input.channel,
122
+ title: input.document.title,
123
+ terms,
124
+ verification: documentDerivation(input.document, terms.gates).verification,
125
+ workspace: input.workspace,
126
+ ...(input.targetSelection === undefined ? {} : { targetSelection: input.targetSelection }),
127
+ ...(input.task === undefined ? {} : { task: input.task }),
128
+ ...(input.actor === undefined ? {} : { actor: input.actor }),
129
+ });
130
+ const admission = input.task === undefined ? null : await claimTaskHolderWithFence(input.scope, input.task, admitCandidate);
131
+ return { admission, result: admission === null ? await admitCandidate() : admission.result };
56
132
  }
@@ -43,6 +43,7 @@ export type Catalog = Readonly<{
43
43
  kind: "contracts";
44
44
  root: string;
45
45
  state: string | null;
46
+ observedAt: string;
46
47
  rows: ContractBoard["rows"];
47
48
  }> | Readonly<{
48
49
  kind: "archetypes";
@@ -40,8 +40,11 @@ function queryValue(value) {
40
40
  export async function listCatalog(input) {
41
41
  const values = requireInput(input, "Keiyaku.ls input");
42
42
  const query = queryValue(values.query);
43
- const allowed = query.kind === "contracts" ? ["query", "repo"]
44
- : query.kind === "archetypes" ? ["query", "home"] : ["query", "path"];
43
+ const allowed = query.kind === "contracts"
44
+ ? ["query", "repo"]
45
+ : query.kind === "archetypes"
46
+ ? ["query", "home"]
47
+ : ["query", "path"];
45
48
  for (const key of Object.keys(values)) {
46
49
  if (!allowed.includes(key))
47
50
  throw new TypeError(`Keiyaku.ls input has unknown field: ${key}`);
@@ -50,7 +53,7 @@ export async function listCatalog(input) {
50
53
  if (!(values.repo instanceof Repo))
51
54
  throw new TypeError("repo must be a Repo");
52
55
  const board = await listKeiyaku({ repo: values.repo });
53
- return { kind: "contracts", root: board.root, state: board.state, rows: board.rows };
56
+ return { kind: "contracts", root: board.root, state: board.state, observedAt: board.observedAt, rows: board.rows };
54
57
  }
55
58
  if (query.kind === "archetypes") {
56
59
  const home = optionalHome(values.home);
@@ -81,7 +81,9 @@ function command(value, coordinate, ErrorType) {
81
81
  }
82
82
  if (value.argv[0].trim().length === 0)
83
83
  throw new ErrorType(`${coordinate}.argv[0] must be nonblank`);
84
- if (!Number.isSafeInteger(value.timeoutMs) || value.timeoutMs < 1 || value.timeoutMs > 2_147_483_647) {
84
+ if (!Number.isSafeInteger(value.timeoutMs) ||
85
+ value.timeoutMs < 1 ||
86
+ value.timeoutMs > 2_147_483_647) {
85
87
  throw new ErrorType(`${coordinate}.timeoutMs must be an integer from 1 through 2147483647`);
86
88
  }
87
89
  return Object.freeze({ argv: Object.freeze([...value.argv]), timeoutMs: value.timeoutMs });
@@ -1,14 +1,13 @@
1
1
  import type { ContractId, ContractState } from "../core/facts/types.js";
2
2
  import { type GitDecodeChannel } from "../git/read-observation.js";
3
- import type { DocumentDerivation, HereWorkspaceResolver, PlacementStop, RepositoryScope } from "../protocol/operations.js";
3
+ import type { DocumentDerivation, PlacementStop, RepositoryScope } from "../protocol/operations.js";
4
4
  import type { AcceptedIntent } from "./mutation.js";
5
5
  export type ContinuationReport = Readonly<{
6
- attempted: number;
7
6
  claimed: readonly ContractId[];
8
7
  stopped: readonly Readonly<{
9
8
  contractId: ContractId;
10
9
  stop: PlacementStop | Readonly<{
11
- kind: "already-terminal" | "delivery-missing";
10
+ kind: "already-terminal";
12
11
  }>;
13
12
  }>[];
14
13
  }>;
@@ -18,7 +17,19 @@ export declare function continueDeliveredDependents<Value extends object>(input:
18
17
  contractId: ContractId;
19
18
  accepted: AcceptedIntent<Value>;
20
19
  deriveDocument: (state: ContractState) => DocumentDerivation;
21
- resolveHereWorkspace?: HereWorkspaceResolver;
20
+ actor?: import("../core/facts/types.js").ActorId;
21
+ signal?: AbortSignal;
22
+ }>): Promise<AcceptedIntent<Value & Readonly<{
23
+ continuation?: ContinuationReport;
24
+ }>>>;
25
+ export declare function continueAcceptedCompletion<Value extends Readonly<{
26
+ completion?: unknown;
27
+ }>>(input: Readonly<{
28
+ scope: RepositoryScope;
29
+ channel: GitDecodeChannel;
30
+ contractId: ContractId;
31
+ accepted: AcceptedIntent<Value>;
32
+ deriveDocument: (state: ContractState) => DocumentDerivation;
22
33
  actor?: import("../core/facts/types.js").ActorId;
23
34
  signal?: AbortSignal;
24
35
  }>): Promise<AcceptedIntent<Value & Readonly<{
@@ -1,19 +1,22 @@
1
1
  import { observeActiveContractWorld } from "../git/observe.js";
2
2
  import { withGitReadObservation } from "../git/read-observation.js";
3
3
  import { continueDeliveryOperation } from "../protocol/deliver.js";
4
- function directDependents(world, claimed, attempted, newlyClaimed) {
5
- const dependents = [];
6
- for (const [contractId, record] of world.contracts) {
4
+ function reverseDependents(world) {
5
+ const index = new Map();
6
+ for (const record of world.contracts.values()) {
7
7
  const state = record.state;
8
- if (state === null
9
- || attempted.has(contractId)
10
- || !state.terms.after.includes(claimed)
11
- || state.delivery === null
12
- || !state.terms.after.every((dependency) => newlyClaimed.has(dependency) || world.eligibility.get(dependency)?.terminal?.kind === "claimed"))
8
+ if (state === null || state.delivery === null)
13
9
  continue;
14
- dependents.push({ state, journal: record.entries });
10
+ const dependent = { state, journal: record.entries };
11
+ for (const prerequisite of state.terms.after) {
12
+ const dependents = index.get(prerequisite) ?? [];
13
+ dependents.push(dependent);
14
+ index.set(prerequisite, dependents);
15
+ }
15
16
  }
16
- return dependents.sort((left, right) => left.state.id.localeCompare(right.state.id));
17
+ for (const dependents of index.values())
18
+ dependents.sort((left, right) => left.state.id.localeCompare(right.state.id));
19
+ return index;
17
20
  }
18
21
  function appendAccepted(primary, child) {
19
22
  const effects = [...(primary.physical?.effects ?? []), ...(child.physical?.effects ?? [])];
@@ -26,6 +29,7 @@ function appendAccepted(primary, child) {
26
29
  }
27
30
  export async function continueDeliveredDependents(input) {
28
31
  const world = await withGitReadObservation(input.scope, input.channel, async (observation) => await observeActiveContractWorld(observation));
32
+ const dependents = reverseDependents(world);
29
33
  const pending = [input.contractId];
30
34
  const attempted = new Set();
31
35
  const newlyClaimed = new Set(pending);
@@ -34,8 +38,11 @@ export async function continueDeliveredDependents(input) {
34
38
  let accepted = input.accepted;
35
39
  while (pending.length > 0) {
36
40
  const parent = pending.shift();
37
- for (const candidate of directDependents(world, parent, attempted, newlyClaimed)) {
41
+ for (const candidate of dependents.get(parent) ?? []) {
38
42
  const contractId = candidate.state.id;
43
+ if (attempted.has(contractId) ||
44
+ !candidate.state.terms.after.every((dependency) => newlyClaimed.has(dependency) || world.eligibility.get(dependency)?.terminal?.kind === "claimed"))
45
+ continue;
39
46
  attempted.add(contractId);
40
47
  const child = await continueDeliveryOperation({
41
48
  scope: input.scope,
@@ -43,7 +50,6 @@ export async function continueDeliveredDependents(input) {
43
50
  state: candidate.state,
44
51
  journal: candidate.journal,
45
52
  deriveDocument: input.deriveDocument,
46
- ...(input.resolveHereWorkspace === undefined ? {} : { resolveHereWorkspace: input.resolveHereWorkspace }),
47
53
  ...(input.actor === undefined ? {} : { actor: input.actor }),
48
54
  ...(input.signal === undefined ? {} : { signal: input.signal }),
49
55
  });
@@ -60,9 +66,6 @@ export async function continueDeliveredDependents(input) {
60
66
  if ("refusal" in stop && stop.refusal.kind === "terminal") {
61
67
  stopped.push({ contractId, stop: { kind: "already-terminal" } });
62
68
  }
63
- else if ("refusal" in stop && stop.refusal.kind === "delivery-missing") {
64
- stopped.push({ contractId, stop: { kind: "delivery-missing" } });
65
- }
66
69
  else {
67
70
  stopped.push({ contractId, stop });
68
71
  }
@@ -72,7 +75,12 @@ export async function continueDeliveredDependents(input) {
72
75
  ...accepted,
73
76
  value: {
74
77
  ...accepted.value,
75
- ...(attempted.size === 0 ? {} : { continuation: { attempted: attempted.size, claimed, stopped } }),
78
+ ...(attempted.size === 0 ? {} : { continuation: { claimed, stopped } }),
76
79
  },
77
80
  };
78
81
  }
82
+ export async function continueAcceptedCompletion(input) {
83
+ if (input.accepted.value.completion === undefined)
84
+ return input.accepted;
85
+ return await continueDeliveredDependents(input);
86
+ }
@@ -0,0 +1,26 @@
1
+ import { type ContractId } from "../core/facts/types.js";
2
+ import { type MutationResult } from "./mutation.js";
3
+ import { type RegionObservation } from "./region.js";
4
+ import { Repo } from "./repo.js";
5
+ import type { WorktreeHooks } from "./configuration.js";
6
+ import type { RepositoryScope } from "../protocol/operations.js";
7
+ type BindInput = Readonly<{
8
+ repo: Repo;
9
+ markdown?: unknown;
10
+ forkOf?: unknown;
11
+ task?: unknown;
12
+ target?: unknown;
13
+ workspace?: unknown;
14
+ actor?: unknown;
15
+ after?: unknown;
16
+ gates?: unknown;
17
+ hooks?: WorktreeHooks;
18
+ }>;
19
+ type BindResult<Handle> = Readonly<Omit<MutationResult<Handle>, "value"> & {
20
+ keiyaku: Handle;
21
+ } & RegionObservation>;
22
+ type HandleFactory<Handle> = (contractId: ContractId, scope: RepositoryScope) => Handle;
23
+ export declare function bindKeiyaku<Handle>(input: BindInput, createHandle: HandleFactory<Handle>): Promise<BindResult<Handle>>;
24
+ /** Internal CLI composition; not exported from the package root. */
25
+ export declare function bindFromCli<Handle>(input: BindInput, createHandle: HandleFactory<Handle>): Promise<BindResult<Handle>>;
26
+ export {};
@@ -0,0 +1,106 @@
1
+ import { decodeContractDocument } from "../body/decode.js";
2
+ import { contractId } from "../core/facts/types.js";
3
+ import { withGitDecodeChannel } from "../git/read-observation.js";
4
+ import { admitForkBindWithAppointment, prepareMarkdownBind } from "./bind.js";
5
+ import { actorOption, normalizedGates, normalizedList, requireInput, requireMarkdown, taskOption } from "./input.js";
6
+ import { completionInput, completeHolderMutation, completeMutation } from "./mutation.js";
7
+ import { requireAccepted } from "./refusal.js";
8
+ import { observeRegion } from "./region.js";
9
+ import { scopeForRepo } from "./repo.js";
10
+ import { worktreeHooksOption } from "./configuration.js";
11
+ function acceptedBindResult(result, region) {
12
+ const { facts, head, effects, lags, settlement } = result;
13
+ return { facts, head, keiyaku: result.value, effects, lags, settlement, ...region };
14
+ }
15
+ export async function bindKeiyaku(input, createHandle) {
16
+ const values = requireInput(input, "Keiyaku.bind input");
17
+ const hooks = worktreeHooksOption(values.hooks);
18
+ const forkOf = values.forkOf;
19
+ if (forkOf !== undefined) {
20
+ for (const key of Object.keys(values)) {
21
+ if (!["repo", "forkOf", "target", "workspace", "actor", "hooks"].includes(key)) {
22
+ throw new TypeError(`fork bind input has unknown field: ${key}`);
23
+ }
24
+ }
25
+ if (typeof forkOf !== "string")
26
+ throw new TypeError("forkOf must be a ContractId");
27
+ let sourceId;
28
+ try {
29
+ sourceId = contractId(forkOf);
30
+ }
31
+ catch (error) {
32
+ throw new TypeError(error instanceof Error ? error.message : "forkOf must be a ContractId");
33
+ }
34
+ const sourceScope = scopeForRepo(values.repo);
35
+ if ((values.workspace ?? "worktree") !== "worktree")
36
+ throw new TypeError("workspace must be worktree");
37
+ const target = values.target;
38
+ if (target !== undefined && typeof target !== "string")
39
+ throw new TypeError("target must be a string");
40
+ const actor = actorOption(values.actor);
41
+ return withGitDecodeChannel(sourceScope, async (channel) => {
42
+ const fork = await admitForkBindWithAppointment({
43
+ scope: sourceScope,
44
+ channel,
45
+ sourceId,
46
+ ...(target === undefined ? {} : { target }),
47
+ ...actor,
48
+ });
49
+ const admission = requireAccepted(fork.admission);
50
+ const id = admission.value.contractId;
51
+ const toHandle = ({ contractId: contract }) => createHandle(contract, sourceScope);
52
+ const result = await completeMutation({
53
+ ...completionInput(sourceScope, channel, id, toHandle, hooks),
54
+ accepted: admission,
55
+ });
56
+ return acceptedBindResult(result, await observeRegion(sourceScope, channel, id, fork.document.region));
57
+ });
58
+ }
59
+ return bindMarkdownFromValues(values, "targetless", createHandle);
60
+ }
61
+ /** Internal CLI composition; not exported from the package root. */
62
+ export async function bindFromCli(input, createHandle) {
63
+ const values = requireInput(input, "Keiyaku.bind input");
64
+ return values.forkOf !== undefined
65
+ ? bindKeiyaku(input, createHandle)
66
+ : bindMarkdownFromValues(values, "current-branch", createHandle);
67
+ }
68
+ async function bindMarkdownFromValues(values, omittedTarget, createHandle) {
69
+ const hooks = worktreeHooksOption(values.hooks);
70
+ const scope = scopeForRepo(values.repo);
71
+ const task = taskOption(values.task);
72
+ const document = decodeContractDocument(requireMarkdown(values.markdown));
73
+ if ((values.workspace ?? "worktree") !== "worktree")
74
+ throw new TypeError("workspace must be worktree");
75
+ const target = values.target;
76
+ if (target !== undefined && typeof target !== "string")
77
+ throw new TypeError("target must be a string");
78
+ const actor = actorOption(values.actor);
79
+ const gates = normalizedGates(values.gates);
80
+ const after = normalizedList(values.after, "after", contractId);
81
+ const targetSelection = target !== undefined ? { kind: "explicit", target } : { kind: omittedTarget };
82
+ return withGitDecodeChannel(scope, async (channel) => {
83
+ const admission = await prepareMarkdownBind({
84
+ scope,
85
+ channel,
86
+ document,
87
+ gates,
88
+ after,
89
+ workspace: "worktree",
90
+ targetSelection,
91
+ ...(task === undefined ? {} : { task }),
92
+ ...actor,
93
+ });
94
+ const accepted = requireAccepted(admission.admission === null ? admission.result : admission.admission.result);
95
+ const id = accepted.value.contractId;
96
+ const toHandle = ({ contractId: contract }) => createHandle(contract, scope);
97
+ const result = admission.admission === null
98
+ ? await completeMutation({ ...completionInput(scope, channel, id, toHandle, hooks), accepted })
99
+ : await completeHolderMutation({
100
+ completion: completionInput(scope, channel, id, toHandle, hooks),
101
+ admission: admission.admission,
102
+ requireAccepted,
103
+ });
104
+ return acceptedBindResult(result, await observeRegion(scope, channel, id, document.region));
105
+ });
106
+ }
@@ -0,0 +1,77 @@
1
+ import { type RepositoryScope } from "../protocol/operations.js";
2
+ import { type IntegrationConflictMaterialized } from "../protocol/deliver.js";
3
+ import { type ReviewValue } from "../protocol/review.js";
4
+ import { type ContinuationReport } from "./continuation.js";
5
+ import { type MutationResult } from "./mutation.js";
6
+ import { type KeiyakuRefusal, type KeiyakuRetryReason } from "./refusal.js";
7
+ import { actorOption } from "./input.js";
8
+ import { type WorktreeHooks } from "./configuration.js";
9
+ import { Repo } from "./repo.js";
10
+ import { type ContractId } from "../core/facts/types.js";
11
+ import type { UpstreamRequestOutcome } from "../akuma/requests.js";
12
+ import type { DeliveryValue } from "./delivery.js";
13
+ export type DeliveryExecutionInput = Readonly<{
14
+ scope: RepositoryScope;
15
+ contractId: ContractId;
16
+ actor?: ReturnType<typeof actorOption>["actor"];
17
+ message?: string;
18
+ requireBranchesToBeUpToDate: boolean;
19
+ includeDirty: boolean;
20
+ materializeConflict: boolean;
21
+ signal?: AbortSignal;
22
+ hooks: WorktreeHooks;
23
+ }>;
24
+ export type ReviewExecutionInput = Readonly<{
25
+ scope: RepositoryScope;
26
+ contractId: ContractId;
27
+ actor?: ReturnType<typeof actorOption>["actor"];
28
+ verdict: AttestationVerdict;
29
+ summary?: string;
30
+ hooks: WorktreeHooks;
31
+ }>;
32
+ export type AttestationVerdict = "satisfied" | "unsatisfied";
33
+ export type Review = ReviewValue & Readonly<{
34
+ continuation?: ContinuationReport;
35
+ }>;
36
+ export type ForwardedMutationReceipt<Value> = Readonly<{
37
+ kind: "accepted";
38
+ result: MutationResult<Value>;
39
+ }> | Readonly<{
40
+ kind: "refused";
41
+ refusal: KeiyakuRefusal;
42
+ }> | Readonly<{
43
+ kind: "retry";
44
+ reason: KeiyakuRetryReason;
45
+ }>;
46
+ export type ForwardedDeliveryReceipt = ForwardedMutationReceipt<DeliveryValue> | IntegrationConflictMaterialized;
47
+ export type ForwardedReviewReceipt = ForwardedMutationReceipt<Review>;
48
+ export declare function executeLocalDelivery(input: DeliveryExecutionInput): Promise<MutationResult<DeliveryValue> | IntegrationConflictMaterialized>;
49
+ export declare function executeLocalReview(input: ReviewExecutionInput): Promise<MutationResult<Review>>;
50
+ export declare function forwardedReceipt<Receipt>(outcome: UpstreamRequestOutcome, action: string): Receipt;
51
+ export declare function requireForwarded(receipt: ForwardedDeliveryReceipt): MutationResult<DeliveryValue> | IntegrationConflictMaterialized;
52
+ export declare function requireForwarded(receipt: ForwardedReviewReceipt): MutationResult<Review>;
53
+ export declare function executeForwardedDeliver(input: Readonly<{
54
+ repo: Repo;
55
+ contractId: string;
56
+ requester: string;
57
+ message?: string;
58
+ includeDirty: boolean;
59
+ materializeConflict: boolean;
60
+ requireBranchesToBeUpToDate: boolean;
61
+ hooks: WorktreeHooks;
62
+ signal?: AbortSignal;
63
+ }>): Promise<Readonly<{
64
+ result: ForwardedDeliveryReceipt;
65
+ deliveryFactId?: string;
66
+ }>>;
67
+ export declare function executeForwardedReview(input: Readonly<{
68
+ repo: Repo;
69
+ contractId: string;
70
+ requester: string;
71
+ verdict: AttestationVerdict;
72
+ summary?: string;
73
+ hooks: WorktreeHooks;
74
+ }>): Promise<Readonly<{
75
+ result: ForwardedReviewReceipt;
76
+ reviewFactId?: string;
77
+ }>>;
@@ -0,0 +1,130 @@
1
+ import { decodeContractDocument } from "../body/decode.js";
2
+ import { deliverOperation } from "../protocol/deliver.js";
3
+ import { reviewOperation } from "../protocol/review.js";
4
+ import { continueAcceptedCompletion } from "./continuation.js";
5
+ import { completionInput, completeMutation } from "./mutation.js";
6
+ import { forwardedMutationFailure, KeiyakuRefused, KeiyakuRetry, requireAccepted, } from "./refusal.js";
7
+ import { actorOption, documentDerivation, optionalNonblank } from "./input.js";
8
+ import { scopeForRepo } from "./repo.js";
9
+ import { contractId } from "../core/facts/types.js";
10
+ import { withGitDecodeChannel } from "../git/read-observation.js";
11
+ function derivedDocument(state) {
12
+ return documentDerivation(decodeContractDocument(state.terms.document.bytes), state.terms.gates, state.id);
13
+ }
14
+ function operationContext(scope, channel, contractId) {
15
+ return { scope, channel, contractId, deriveDocument: derivedDocument };
16
+ }
17
+ export async function executeLocalDelivery(input) {
18
+ return withGitDecodeChannel(input.scope, async (channel) => {
19
+ const operation = operationContext(input.scope, channel, input.contractId);
20
+ const outcome = await deliverOperation({
21
+ ...operation,
22
+ ...(input.actor === undefined ? {} : { actor: input.actor }),
23
+ ...(input.message === undefined ? {} : { message: input.message }),
24
+ requireBranchesToBeUpToDate: input.requireBranchesToBeUpToDate,
25
+ includeDirty: input.includeDirty,
26
+ materializeConflict: input.materializeConflict,
27
+ ...(input.signal === undefined ? {} : { signal: input.signal }),
28
+ });
29
+ if (outcome.kind === "integration-conflict-materialized")
30
+ return outcome;
31
+ const accepted = requireAccepted(outcome);
32
+ const continued = await continueAcceptedCompletion({
33
+ ...operation,
34
+ accepted,
35
+ ...(input.actor === undefined ? {} : { actor: input.actor }),
36
+ ...(input.signal === undefined ? {} : { signal: input.signal }),
37
+ });
38
+ return completeMutation({
39
+ ...completionInput(input.scope, channel, input.contractId, (delivery) => delivery, input.hooks),
40
+ accepted: continued,
41
+ });
42
+ });
43
+ }
44
+ export async function executeLocalReview(input) {
45
+ return withGitDecodeChannel(input.scope, async (channel) => {
46
+ const operation = operationContext(input.scope, channel, input.contractId);
47
+ const accepted = requireAccepted(await reviewOperation({
48
+ ...operation,
49
+ verdict: input.verdict,
50
+ ...(input.summary === undefined ? {} : { summary: input.summary }),
51
+ ...(input.actor === undefined ? {} : { actor: input.actor }),
52
+ }));
53
+ const continued = await continueAcceptedCompletion({
54
+ ...operation,
55
+ accepted,
56
+ ...(input.actor === undefined ? {} : { actor: input.actor }),
57
+ });
58
+ return completeMutation({
59
+ ...completionInput(input.scope, channel, input.contractId, (review) => review, input.hooks),
60
+ accepted: continued,
61
+ });
62
+ });
63
+ }
64
+ export function forwardedReceipt(outcome, action) {
65
+ if (outcome.kind === "failed") {
66
+ throw new Error(outcome.failure.kind === "failed"
67
+ ? outcome.failure.diagnostic
68
+ : `Unexpected Akuma target failure for ${action}: ${outcome.failure.id}`);
69
+ }
70
+ return outcome.result;
71
+ }
72
+ export function requireForwarded(receipt) {
73
+ if (receipt.kind === "refused")
74
+ throw new KeiyakuRefused(receipt.refusal);
75
+ if (receipt.kind === "retry")
76
+ throw new KeiyakuRetry(receipt.reason);
77
+ return receipt.kind === "accepted" ? receipt.result : receipt;
78
+ }
79
+ export async function executeForwardedDeliver(input) {
80
+ const id = contractId(input.contractId);
81
+ const actor = actorOption(input.requester).actor;
82
+ const message = optionalNonblank(input.message, "deliver message");
83
+ try {
84
+ const result = await executeLocalDelivery({
85
+ scope: scopeForRepo(input.repo),
86
+ contractId: id,
87
+ ...(actor === undefined ? {} : { actor }),
88
+ ...(message === undefined ? {} : { message }),
89
+ requireBranchesToBeUpToDate: input.requireBranchesToBeUpToDate,
90
+ includeDirty: input.includeDirty,
91
+ materializeConflict: input.materializeConflict,
92
+ ...(input.signal === undefined ? {} : { signal: input.signal }),
93
+ hooks: input.hooks,
94
+ });
95
+ if (!("facts" in result))
96
+ return { result };
97
+ const delivery = result.facts.find((fact) => fact.kind === "deliver");
98
+ if (delivery === undefined)
99
+ throw new Error("accepted delivery is missing its journal fact");
100
+ return { result: { kind: "accepted", result }, deliveryFactId: delivery.entry };
101
+ }
102
+ catch (error) {
103
+ return forwardedMutationFailure(error);
104
+ }
105
+ }
106
+ export async function executeForwardedReview(input) {
107
+ const id = contractId(input.contractId);
108
+ const actor = actorOption(input.requester).actor;
109
+ if (input.verdict !== "satisfied" && input.verdict !== "unsatisfied") {
110
+ throw new TypeError("verdict must be satisfied or unsatisfied");
111
+ }
112
+ const summary = optionalNonblank(input.summary, "review summary");
113
+ try {
114
+ const result = await executeLocalReview({
115
+ scope: scopeForRepo(input.repo),
116
+ contractId: id,
117
+ ...(actor === undefined ? {} : { actor }),
118
+ verdict: input.verdict,
119
+ ...(summary === undefined ? {} : { summary }),
120
+ hooks: input.hooks,
121
+ });
122
+ const review = result.facts.find((fact) => fact.kind === "attestation");
123
+ if (review === undefined)
124
+ throw new Error("accepted review is missing its journal fact");
125
+ return { result: { kind: "accepted", result }, reviewFactId: review.entry };
126
+ }
127
+ catch (error) {
128
+ return forwardedMutationFailure(error);
129
+ }
130
+ }