@astrosheep/keiyaku 4.0.3 → 4.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  2. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +4 -4
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +1 -1
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +15 -1
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +3 -1
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +34 -13
  9. package/build/src/akuma/akuma.d.ts +34 -12
  10. package/build/src/akuma/akuma.js +118 -63
  11. package/build/src/akuma/allowed.d.ts +7 -0
  12. package/build/src/akuma/allowed.js +33 -0
  13. package/build/src/akuma/archetype.d.ts +7 -4
  14. package/build/src/akuma/archetype.js +15 -16
  15. package/build/src/akuma/body.d.ts +6 -1
  16. package/build/src/akuma/body.js +36 -27
  17. package/build/src/akuma/heart/facts.d.ts +82 -14
  18. package/build/src/akuma/heart/index.d.ts +5 -18
  19. package/build/src/akuma/heart/index.js +14 -84
  20. package/build/src/akuma/heart/request-authority.d.ts +12 -0
  21. package/build/src/akuma/heart/request-authority.js +119 -0
  22. package/build/src/akuma/heart/request-rows.d.ts +16 -0
  23. package/build/src/akuma/heart/request-rows.js +88 -0
  24. package/build/src/akuma/heart/rows.d.ts +1 -26
  25. package/build/src/akuma/heart/rows.js +0 -50
  26. package/build/src/akuma/heart/schema.d.ts +1 -1
  27. package/build/src/akuma/heart/schema.js +22 -10
  28. package/build/src/akuma/heart/soul.js +2 -36
  29. package/build/src/akuma/heart/storage.d.ts +1 -0
  30. package/build/src/akuma/heart/storage.js +15 -0
  31. package/build/src/akuma/heart/timeline.d.ts +1 -5
  32. package/build/src/akuma/heart/timeline.js +8 -17
  33. package/build/src/akuma/index.d.ts +5 -2
  34. package/build/src/akuma/index.js +1 -0
  35. package/build/src/akuma/projection.d.ts +17 -3
  36. package/build/src/akuma/projection.js +40 -3
  37. package/build/src/akuma/provider-recipe.js +1 -11
  38. package/build/src/akuma/provider.d.ts +2 -7
  39. package/build/src/akuma/provider.js +2 -1
  40. package/build/src/akuma/providers/acp/core.d.ts +3 -1
  41. package/build/src/akuma/providers/acp/core.js +10 -6
  42. package/build/src/akuma/providers/acp/events.d.ts +11 -3
  43. package/build/src/akuma/providers/acp/events.js +78 -4
  44. package/build/src/akuma/providers/acp/index.js +0 -1
  45. package/build/src/akuma/providers/claude/events.js +62 -3
  46. package/build/src/akuma/providers/claude/index.d.ts +0 -4
  47. package/build/src/akuma/providers/claude/index.js +7 -3
  48. package/build/src/akuma/providers/codex-app-server/events.js +1 -13
  49. package/build/src/akuma/providers/codex-app-server/index.d.ts +2 -27
  50. package/build/src/akuma/providers/codex-app-server/index.js +0 -2
  51. package/build/src/akuma/providers/grok-build/index.d.ts +2 -1
  52. package/build/src/akuma/providers/grok-build/index.js +72 -3
  53. package/build/src/akuma/providers/opencode-sdk/events.js +71 -6
  54. package/build/src/akuma/providers/opencode-sdk/index.d.ts +2 -28
  55. package/build/src/akuma/providers/opencode-sdk/index.js +8 -4
  56. package/build/src/akuma/providers/opencode-sdk/session.d.ts +1 -1
  57. package/build/src/akuma/providers/opencode-sdk/session.js +1 -1
  58. package/build/src/akuma/providers/pi/events.js +17 -3
  59. package/build/src/akuma/providers/pi/index.js +11 -4
  60. package/build/src/akuma/providers/unified-patch.d.ts +4 -0
  61. package/build/src/akuma/providers/unified-patch.js +14 -0
  62. package/build/src/akuma/publication.d.ts +1 -1
  63. package/build/src/akuma/publication.js +1 -1
  64. package/build/src/akuma/request-serve.d.ts +87 -0
  65. package/build/src/akuma/request-serve.js +484 -0
  66. package/build/src/akuma/request-wire.d.ts +124 -0
  67. package/build/src/akuma/request-wire.js +380 -0
  68. package/build/src/akuma/requests.d.ts +41 -41
  69. package/build/src/akuma/requests.js +75 -374
  70. package/build/src/akuma-body.d.ts +8 -0
  71. package/build/src/akuma-body.js +84 -0
  72. package/build/src/body/arc.js +4 -29
  73. package/build/src/body/decode.js +5 -39
  74. package/build/src/body/envelope.d.ts +7 -0
  75. package/build/src/body/envelope.js +37 -0
  76. package/build/src/cli/accepted.d.ts +4 -1
  77. package/build/src/cli/accepted.js +39 -9
  78. package/build/src/cli/commands/akuma-invoke.d.ts +8 -7
  79. package/build/src/cli/commands/akuma-invoke.js +18 -10
  80. package/build/src/cli/commands/akuma.d.ts +11 -2
  81. package/build/src/cli/commands/akuma.js +67 -28
  82. package/build/src/cli/commands/amend.d.ts +1 -1
  83. package/build/src/cli/commands/amend.js +1 -1
  84. package/build/src/cli/commands/contract.d.ts +8 -8
  85. package/build/src/cli/commands/contract.js +3 -3
  86. package/build/src/cli/commands/install.js +2 -2
  87. package/build/src/cli/commands/task-invoke.js +98 -9
  88. package/build/src/cli/commands/task-query.d.ts +1 -1
  89. package/build/src/cli/commands/task-query.js +11 -5
  90. package/build/src/cli/coordinates.d.ts +2 -0
  91. package/build/src/cli/coordinates.js +12 -5
  92. package/build/src/cli/invoke.d.ts +1 -0
  93. package/build/src/cli/invoke.js +116 -75
  94. package/build/src/cli/main.d.ts +2 -0
  95. package/build/src/cli/main.js +29 -4
  96. package/build/src/cli/parse.d.ts +3 -2
  97. package/build/src/cli/parse.js +25 -8
  98. package/build/src/cli/render/akuma.d.ts +2 -2
  99. package/build/src/cli/render/akuma.js +138 -24
  100. package/build/src/cli/render/audit.js +18 -8
  101. package/build/src/cli/render/contract.d.ts +2 -0
  102. package/build/src/cli/render/contract.js +243 -36
  103. package/build/src/cli/render/kanshi.js +64 -29
  104. package/build/src/cli/render/receipt.d.ts +2 -1
  105. package/build/src/cli/render/receipt.js +37 -2
  106. package/build/src/cli/render/terminal.d.ts +0 -2
  107. package/build/src/cli/render/terminal.js +0 -29
  108. package/build/src/cli/render/text.js +3 -1
  109. package/build/src/cli/result.d.ts +52 -9
  110. package/build/src/cli/selectors.d.ts +1 -0
  111. package/build/src/cli/selectors.js +9 -6
  112. package/build/src/contract-worktree.d.ts +1 -2
  113. package/build/src/contract-worktree.js +7 -9
  114. package/build/src/core/facts/codec.js +9 -0
  115. package/build/src/core/facts/fold.js +13 -1
  116. package/build/src/core/facts/types.d.ts +8 -1
  117. package/build/src/core/subject.js +4 -3
  118. package/build/src/core/verbs/placement.d.ts +9 -1
  119. package/build/src/core/verbs/placement.js +24 -6
  120. package/build/src/core/verbs/reintegrate.d.ts +13 -0
  121. package/build/src/core/verbs/reintegrate.js +24 -0
  122. package/build/src/dispatch/index.d.ts +1 -1
  123. package/build/src/dispatch/index.js +50 -39
  124. package/build/src/git/admission.d.ts +2 -1
  125. package/build/src/git/hooks.d.ts +0 -4
  126. package/build/src/git/hooks.js +0 -36
  127. package/build/src/git/integration.d.ts +7 -8
  128. package/build/src/git/integration.js +62 -9
  129. package/build/src/git/observe.d.ts +2 -1
  130. package/build/src/git/observe.js +6 -5
  131. package/build/src/git/process.d.ts +26 -0
  132. package/build/src/git/process.js +94 -0
  133. package/build/src/git/read-observation.d.ts +2 -1
  134. package/build/src/git/read-observation.js +3 -2
  135. package/build/src/git/reconcile.d.ts +8 -2
  136. package/build/src/git/reconcile.js +63 -14
  137. package/build/src/git/repository.d.ts +2 -29
  138. package/build/src/git/repository.js +4 -118
  139. package/build/src/git/scratch.d.ts +1 -1
  140. package/build/src/git/scratch.js +1 -1
  141. package/build/src/git/target-placement.d.ts +1 -1
  142. package/build/src/git/target-placement.js +9 -7
  143. package/build/src/git/tender.d.ts +20 -5
  144. package/build/src/git/tender.js +45 -13
  145. package/build/src/git/terminal-seal.d.ts +16 -2
  146. package/build/src/git/terminal-seal.js +15 -5
  147. package/build/src/git/workspace.d.ts +2 -1
  148. package/build/src/git/workspace.js +13 -5
  149. package/build/src/index.d.ts +1 -2
  150. package/build/src/index.js +1 -1
  151. package/build/src/kanshi/index.d.ts +1 -1
  152. package/build/src/kanshi/index.js +1 -1
  153. package/build/src/kanshi/read.d.ts +9 -1
  154. package/build/src/kanshi/read.js +91 -37
  155. package/build/src/kanshi/report.d.ts +1 -0
  156. package/build/src/library/address.d.ts +5 -14
  157. package/build/src/library/address.js +27 -23
  158. package/build/src/library/akuma-creation.d.ts +4 -2
  159. package/build/src/library/akuma-creation.js +43 -26
  160. package/build/src/library/audit.d.ts +2 -1
  161. package/build/src/library/audit.js +1 -1
  162. package/build/src/library/bind.d.ts +2 -1
  163. package/build/src/library/bind.js +4 -4
  164. package/build/src/library/catalog.d.ts +1 -2
  165. package/build/src/library/catalog.js +11 -6
  166. package/build/src/library/configuration.d.ts +5 -1
  167. package/build/src/library/configuration.js +71 -18
  168. package/build/src/library/contract.d.ts +70 -3
  169. package/build/src/library/contract.js +213 -27
  170. package/build/src/library/delivery.d.ts +1 -1
  171. package/build/src/library/fleet.d.ts +70 -12
  172. package/build/src/library/fleet.js +236 -56
  173. package/build/src/library/input.d.ts +0 -1
  174. package/build/src/library/input.js +1 -5
  175. package/build/src/library/keiyaku.d.ts +4 -4
  176. package/build/src/library/keiyaku.js +1 -2
  177. package/build/src/library/mutation.d.ts +2 -1
  178. package/build/src/library/reconcile.d.ts +2 -1
  179. package/build/src/library/reconcile.js +3 -1
  180. package/build/src/library/region.d.ts +1 -1
  181. package/build/src/library/repo.d.ts +3 -1
  182. package/build/src/library/repo.js +8 -4
  183. package/build/src/protocol/abandon.d.ts +9 -0
  184. package/build/src/protocol/abandon.js +15 -0
  185. package/build/src/protocol/amend.d.ts +16 -0
  186. package/build/src/protocol/amend.js +77 -0
  187. package/build/src/protocol/arc.d.ts +6 -0
  188. package/build/src/protocol/arc.js +12 -0
  189. package/build/src/protocol/attempt.d.ts +2 -1
  190. package/build/src/protocol/audit.d.ts +53 -0
  191. package/build/src/protocol/audit.js +140 -0
  192. package/build/src/protocol/bind.d.ts +2 -3
  193. package/build/src/protocol/bind.js +2 -16
  194. package/build/src/protocol/completion.d.ts +32 -0
  195. package/build/src/protocol/completion.js +130 -0
  196. package/build/src/protocol/deliver.d.ts +37 -0
  197. package/build/src/protocol/deliver.js +160 -0
  198. package/build/src/protocol/intent.d.ts +1 -1
  199. package/build/src/protocol/intent.js +1 -1
  200. package/build/src/protocol/operations.d.ts +65 -198
  201. package/build/src/protocol/operations.js +21 -634
  202. package/build/src/protocol/placement.d.ts +4 -13
  203. package/build/src/protocol/placement.js +3 -15
  204. package/build/src/protocol/read/status.d.ts +4 -2
  205. package/build/src/protocol/read/status.js +27 -15
  206. package/build/src/protocol/reconcile.d.ts +35 -0
  207. package/build/src/protocol/reconcile.js +41 -0
  208. package/build/src/protocol/reintegrate.d.ts +31 -0
  209. package/build/src/protocol/reintegrate.js +74 -0
  210. package/build/src/protocol/review.d.ts +33 -0
  211. package/build/src/protocol/review.js +148 -0
  212. package/build/src/protocol/run.d.ts +1 -1
  213. package/build/src/runtime/proc/run.d.ts +1 -0
  214. package/build/src/runtime/proc/run.js +9 -2
  215. package/build/src/settlement/fence.d.ts +1 -1
  216. package/build/src/settlement/holder.d.ts +1 -1
  217. package/build/src/settlement/settle.d.ts +2 -1
  218. package/build/src/settlement/settle.js +4 -1
  219. package/build/src/task/board.d.ts +18 -4
  220. package/build/src/task/board.js +73 -29
  221. package/build/src/task/compose.d.ts +1 -1
  222. package/build/src/task/compose.js +11 -4
  223. package/build/src/task/index.d.ts +4 -4
  224. package/build/src/task/index.js +8 -131
  225. package/build/src/task/input.d.ts +18 -0
  226. package/build/src/task/input.js +189 -0
  227. package/build/src/task/mutation.d.ts +39 -0
  228. package/build/src/task/mutation.js +85 -0
  229. package/build/src/task/operations.d.ts +4 -19
  230. package/build/src/task/operations.js +64 -18
  231. package/build/src/task/query.d.ts +9 -2
  232. package/build/src/task/query.js +32 -64
  233. package/build/src/task/store.js +0 -2
  234. package/build/src/verification/execution.d.ts +1 -1
  235. package/build/src/verification/execution.js +2 -1
  236. package/build/src/workspace-place.d.ts +1 -4
  237. package/build/src/workspace-place.js +0 -15
  238. package/package.json +5 -3
@@ -1,15 +1,10 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { access, mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
3
- import { isAbsolute, join, resolve } from "node:path";
4
- import { HeldAkumaLeash, admitRequest, readNonterminalRequests, readRequest, readSoul, refuseRequest, reserveRequest, serveRequest, voidRequest, } from "./heart/index.js";
5
- import { parseAkuId, pathsForAkuId, archetypeName, worldRootForAkumaPaths, } from "./identity.js";
6
- import { BIRTH_TIMEOUT_MS, publishAkuma } from "./publication.js";
2
+ import { access, readFile } from "node:fs/promises";
7
3
  import { abortableDelay } from "./abort.js";
8
4
  import { AKUMA_REQUESTS_ENV } from "./provider.js";
9
- import { decodeProviderOptions, decodeReadonlyRestraint } from "./provider-recipe.js";
10
- import { resolveProviderExecution } from "./providers/index.js";
5
+ import { absolute, atomicJson, decodeReceipt, receiptPath, requestPath, requestPayload, } from "./request-wire.js";
6
+ import { isTaskMutationAction } from "../task/mutation.js";
11
7
  const POLL_MS = 25;
12
- const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
13
8
  export class AkumaBodyRequestError extends Error {
14
9
  outcome;
15
10
  diagnostic;
@@ -21,169 +16,6 @@ export class AkumaBodyRequestError extends Error {
21
16
  this.name = "AkumaBodyRequestError";
22
17
  }
23
18
  }
24
- function diagnostic(error) {
25
- return error instanceof Error ? error.message : String(error);
26
- }
27
- function object(value) {
28
- return value !== null && typeof value === "object" && !Array.isArray(value)
29
- ? value
30
- : null;
31
- }
32
- function exactKeys(value, expected) {
33
- const actual = Object.keys(value).sort();
34
- return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
35
- }
36
- function absolute(value) {
37
- return typeof value === "string" && isAbsolute(value) && resolve(value) === value;
38
- }
39
- function matchingConfinement(value, expected) {
40
- const confinement = object(value);
41
- if (confinement === null || confinement.kind !== expected.kind)
42
- return false;
43
- if (expected.kind === "unconfined")
44
- return exactKeys(confinement, ["kind"]);
45
- if (!exactKeys(confinement, ["kind", "writableRoots"]) || !Array.isArray(confinement.writableRoots))
46
- return false;
47
- return confinement.writableRoots.length === expected.writableRoots.length
48
- && confinement.writableRoots.every((root, index) => root === expected.writableRoots[index]);
49
- }
50
- function decodeRecipe(value, cwd) {
51
- const recipe = object(value);
52
- if (recipe === null)
53
- return null;
54
- const expectedKeys = [
55
- "confinement",
56
- ...(recipe.description === undefined ? [] : ["description"]),
57
- "options",
58
- "provider",
59
- ...(recipe.readonly === undefined ? [] : ["readonly"]),
60
- ];
61
- if (!exactKeys(recipe, expectedKeys))
62
- return null;
63
- if (recipe.description !== undefined
64
- && (typeof recipe.description !== "string" || recipe.description.trim().length === 0))
65
- return null;
66
- try {
67
- const selected = resolveProviderExecution(recipe.provider);
68
- const provider = selected.execution;
69
- const adapter = selected.adapter;
70
- const decodedOptions = decodeProviderOptions(recipe.options);
71
- const readonly = recipe.readonly === undefined ? undefined : decodeReadonlyRestraint(recipe.readonly);
72
- if ((decodedOptions.readonly === true) !== (readonly !== undefined))
73
- return null;
74
- const confinement = adapter.confinement({ cwd, options: decodedOptions });
75
- if (!matchingConfinement(recipe.confinement, confinement))
76
- return null;
77
- return Object.freeze({
78
- ...(recipe.description === undefined ? {} : { description: recipe.description }),
79
- provider,
80
- options: decodedOptions,
81
- ...(readonly === undefined ? {} : { readonly }),
82
- confinement,
83
- });
84
- }
85
- catch {
86
- return null;
87
- }
88
- }
89
- function decodeClaim(bytes, fileId) {
90
- let decoded;
91
- try {
92
- decoded = JSON.parse(bytes);
93
- }
94
- catch {
95
- return null;
96
- }
97
- const value = object(decoded);
98
- if (value === null)
99
- return null;
100
- const expected = [
101
- "archetype",
102
- "body",
103
- ...(value.cwd === undefined ? [] : ["cwd"]),
104
- "id",
105
- "recipe",
106
- "world",
107
- ];
108
- if (!exactKeys(value, expected))
109
- return null;
110
- if (value.id !== fileId || typeof value.id !== "string" || !UUID.test(value.id))
111
- return null;
112
- if (typeof value.body !== "string" || !absolute(value.world))
113
- return null;
114
- if (value.cwd !== undefined && !absolute(value.cwd))
115
- return null;
116
- try {
117
- archetypeName(value.archetype);
118
- }
119
- catch {
120
- return null;
121
- }
122
- const recipe = decodeRecipe(value.recipe, (value.cwd ?? value.world));
123
- if (recipe === null)
124
- return null;
125
- return {
126
- id: value.id,
127
- world: value.world,
128
- archetype: value.archetype,
129
- body: value.body,
130
- recipe,
131
- ...(value.cwd === undefined ? {} : { cwd: value.cwd }),
132
- };
133
- }
134
- function decodeReceipt(bytes, requestId) {
135
- let decoded;
136
- try {
137
- decoded = JSON.parse(bytes);
138
- }
139
- catch {
140
- return null;
141
- }
142
- const value = object(decoded);
143
- if (value === null || value.id !== requestId)
144
- return null;
145
- if (value.state === "served" && exactKeys(value, ["child", "id", "state"])) {
146
- try {
147
- return { id: requestId, state: "served", child: parseAkuId(value.child).id };
148
- }
149
- catch {
150
- return null;
151
- }
152
- }
153
- if (value.state === "refused" && exactKeys(value, ["diagnostic", "id", "state"])
154
- && typeof value.diagnostic === "string") {
155
- return { id: requestId, state: "refused", diagnostic: value.diagnostic };
156
- }
157
- if (value.state === "voided" && exactKeys(value, ["evidence", "id", "state"])
158
- && typeof value.evidence === "string") {
159
- return { id: requestId, state: "voided", evidence: value.evidence };
160
- }
161
- return null;
162
- }
163
- async function atomicJson(path, value) {
164
- const temporary = `${path}.${randomUUID()}.tmp`;
165
- try {
166
- await writeFile(temporary, `${JSON.stringify(value)}\n`, { flag: "wx" });
167
- await rename(temporary, path);
168
- }
169
- finally {
170
- await rm(temporary, { force: true });
171
- }
172
- }
173
- function receiptFor(fact) {
174
- if (fact.state === "served")
175
- return { id: fact.id, state: fact.state, child: fact.child };
176
- if (fact.state === "refused")
177
- return { id: fact.id, state: fact.state, diagnostic: fact.diagnostic };
178
- if (fact.state === "voided")
179
- return { id: fact.id, state: fact.state, evidence: fact.evidence };
180
- return null;
181
- }
182
- async function projectReceipt(directory, fact) {
183
- const receipt = receiptFor(fact);
184
- if (receipt !== null)
185
- await atomicJson(join(directory, `${fact.id}.receipt.json`), receipt);
186
- }
187
19
  export function injectedBodyRequests() {
188
20
  const directory = process.env[AKUMA_REQUESTS_ENV];
189
21
  if (directory === undefined)
@@ -192,227 +24,96 @@ export function injectedBodyRequests() {
192
24
  throw new Error(`${AKUMA_REQUESTS_ENV} must be an absolute normalized path`);
193
25
  return directory;
194
26
  }
195
- export async function requestBodyCall(input) {
196
- await atomicJson(join(input.directory, `${input.id}.request.json`), {
197
- id: input.id,
198
- world: input.world,
199
- archetype: input.archetype,
200
- body: input.body,
201
- recipe: input.recipe,
202
- ...(input.cwd === undefined ? {} : { cwd: input.cwd }),
27
+ async function requestBody(input) {
28
+ input.signal?.throwIfAborted();
29
+ await atomicJson(requestPath(input.directory, input.claim.id), {
30
+ id: input.claim.id,
31
+ action: input.claim.action,
32
+ payload: requestPayload(input.claim),
203
33
  });
204
- const receiptPath = join(input.directory, `${input.id}.receipt.json`);
34
+ const path = receiptPath(input.directory, input.claim.id);
205
35
  for (;;) {
206
36
  try {
207
- const receipt = decodeReceipt(await readFile(receiptPath, "utf8"), input.id);
37
+ const receipt = decodeReceipt(await readFile(path, "utf8"), input.claim.id, input.claim.action);
208
38
  if (receipt === null)
209
- throw new Error(`Akuma body request ${input.id} has an invalid receipt`);
210
- if (receipt.state === "served")
211
- return receipt.child;
39
+ throw new Error(`Akuma body request ${input.claim.id} has an invalid receipt`);
212
40
  if (receipt.state === "refused")
213
41
  throw new AkumaBodyRequestError("refused", receipt.diagnostic);
214
- throw new AkumaBodyRequestError("voided", receipt.evidence);
42
+ if (receipt.state === "voided")
43
+ throw new AkumaBodyRequestError("voided", receipt.evidence);
44
+ return receipt;
215
45
  }
216
46
  catch (error) {
217
47
  if (error.code !== "ENOENT")
218
48
  throw error;
219
49
  }
220
- await abortableDelay(POLL_MS);
50
+ if (!await access(input.directory).then(() => true, () => false)) {
51
+ throw new AkumaBodyRequestError("voided", "parent request channel closed before a receipt");
52
+ }
53
+ await abortableDelay(POLL_MS, input.signal);
221
54
  }
222
55
  }
223
- async function serveClaim(input) {
224
- input.signal.throwIfAborted();
225
- let fact = await admitRequest(input.paths, { ...input.claim, admittedAt: input.now() });
226
- const existing = receiptFor(fact);
227
- if (existing !== null) {
228
- if (!input.signal.aborted)
229
- await projectReceipt(input.directory, fact);
230
- return;
231
- }
232
- if (fact.state !== "admitted")
233
- throw new Error(`Akuma request ${fact.id} cannot be replayed from ${fact.state}`);
234
- const request = fact;
235
- const servingWorld = worldRootForAkumaPaths(input.paths);
236
- if (request.world !== servingWorld) {
237
- fact = await refuseRequest(input.paths, request.id, `request world ${request.world} does not match ${servingWorld}`);
238
- await projectReceipt(input.directory, fact);
239
- return;
240
- }
241
- const cwd = resolve(request.cwd ?? servingWorld);
242
- let child;
243
- try {
244
- input.signal.throwIfAborted();
245
- const published = await publishAkuma({
246
- worldPath: servingWorld,
247
- archetype: request.archetype,
248
- reserve: async (allocated) => { fact = await reserveRequest(input.paths, request.id, allocated.id); },
249
- signal: input.signal,
250
- launch: async (allocated) => await input.spawn({
251
- paths: allocated.paths,
252
- seed: {
253
- id: allocated.id,
254
- archetype: allocated.archetype,
255
- ...(request.recipe.description === undefined ? {} : { description: request.recipe.description }),
256
- provider: request.recipe.provider,
257
- options: request.recipe.options,
258
- ...(request.recipe.readonly === undefined ? {} : { readonly: request.recipe.readonly }),
259
- cwd,
260
- origin: { kind: "request", parent: input.parent.id, requestId: request.id },
261
- confinement: request.recipe.confinement,
262
- },
263
- initialBody: request.body,
264
- }),
265
- });
266
- child = published.id;
267
- }
268
- catch (error) {
269
- if (input.signal.aborted)
270
- return;
271
- const current = await readRequest(input.paths, request.id);
272
- if (current === null)
273
- throw error;
274
- fact = current.state === "admitted" || current.state === "reserved"
275
- ? await voidRequest(input.paths, request.id, diagnostic(error))
276
- : current;
277
- await projectReceipt(input.directory, fact);
278
- return;
56
+ export async function requestBodyCall(input) {
57
+ const { directory, ...claim } = input;
58
+ const receipt = await requestBody({ directory, claim: { ...claim, action: "akuma.call" } });
59
+ if (receipt.action !== "akuma.call" || receipt.state !== "served") {
60
+ throw new Error(`Akuma body request ${input.id} returned the wrong action`);
279
61
  }
280
- fact = await serveRequest(input.paths, request.id, child);
281
- await projectReceipt(input.directory, fact);
62
+ return receipt.child;
282
63
  }
283
- async function requestFiles(directory) {
284
- try {
285
- return (await readdir(directory))
286
- .filter((name) => name.endsWith(".request.json"))
287
- .sort();
288
- }
289
- catch (error) {
290
- if (error.code === "ENOENT")
291
- return [];
292
- throw error;
64
+ async function requestUpstream(directory, claim, signal) {
65
+ const receipt = await requestBody({ directory, claim, ...(signal === undefined ? {} : { signal }) });
66
+ if (receipt.action === "akuma.call" || receipt.state !== "served" || !("outcome" in receipt)) {
67
+ throw new Error(`Akuma body request ${claim.id} returned the wrong action`);
293
68
  }
69
+ return receipt.outcome;
294
70
  }
295
- export class BodyRequestPump {
296
- input;
297
- directory;
298
- failure;
299
- closeSignal = new AbortController();
300
- handled = new Set();
301
- running;
302
- cancelAdmission;
303
- constructor(input) {
304
- this.input = input;
305
- this.directory = join(input.paths.directory, "requests", String(input.bodySequence));
306
- this.cancelAdmission = () => this.stopAdmission();
307
- input.signal.addEventListener("abort", this.cancelAdmission, { once: true });
308
- if (input.signal.aborted)
309
- this.cancelAdmission();
310
- this.running = this.run();
311
- this.failure = this.running.then(() => new Promise(() => { }), (error) => Promise.reject(error));
312
- }
313
- static async open(input) {
314
- const directory = join(input.paths.directory, "requests", String(input.bodySequence));
315
- await mkdir(directory, { recursive: true });
316
- return new BodyRequestPump(input);
317
- }
318
- async run() {
319
- while (!this.closeSignal.signal.aborted) {
320
- for (const name of await requestFiles(this.directory)) {
321
- if (this.closeSignal.signal.aborted)
322
- return;
323
- const id = name.slice(0, -".request.json".length);
324
- if (this.handled.has(id))
325
- continue;
326
- const claim = decodeClaim(await readFile(join(this.directory, name), "utf8"), id);
327
- if (claim === null) {
328
- this.handled.add(id);
329
- continue;
330
- }
331
- await serveClaim({ directory: this.directory, claim, ...this.input, signal: this.closeSignal.signal });
332
- this.handled.add(id);
333
- }
334
- try {
335
- await abortableDelay(POLL_MS, this.closeSignal.signal);
336
- }
337
- catch {
338
- return;
339
- }
340
- }
341
- }
342
- async close() {
343
- this.stopAdmission();
344
- try {
345
- await this.running;
346
- }
347
- finally {
348
- this.input.signal.removeEventListener("abort", this.cancelAdmission);
349
- await rm(this.directory, { recursive: true, force: true });
350
- }
351
- }
352
- stopAdmission() {
353
- if (!this.closeSignal.signal.aborted)
354
- this.closeSignal.abort(new Error("Body request pump closed"));
71
+ export async function requestBodyTask(input) {
72
+ const id = input.id ?? randomUUID();
73
+ const receipt = await requestBody({
74
+ directory: input.directory,
75
+ claim: { id, action: input.request.action, world: input.world, request: input.request },
76
+ });
77
+ if (!isTaskMutationAction(receipt.action) || receipt.action !== input.request.action || receipt.state !== "served") {
78
+ throw new Error(`Akuma body request ${id} returned the wrong action`);
79
+ }
80
+ if ("reference" in receipt)
81
+ return receipt.reference;
82
+ if (receipt.outcome.kind === "returned")
83
+ return receipt.outcome.result;
84
+ throw new Error(receipt.outcome.failure.kind === "failed"
85
+ ? receipt.outcome.failure.diagnostic
86
+ : "Akuma body request parent is not born");
87
+ }
88
+ export async function requestBodyWait(input) {
89
+ const { directory, id = randomUUID(), ...claim } = input;
90
+ return await requestUpstream(directory, { ...claim, id, action: "akuma.wait" });
91
+ }
92
+ export async function requestBodyTell(input) {
93
+ const { directory, id = randomUUID(), ...claim } = input;
94
+ return await requestUpstream(directory, { ...claim, id, action: "akuma.tell" });
95
+ }
96
+ export async function requestBodyKill(input) {
97
+ const { directory, id = randomUUID(), ...claim } = input;
98
+ return await requestUpstream(directory, { ...claim, id, action: "akuma.kill" });
99
+ }
100
+ async function requestContract(input, action) {
101
+ const { directory, id = randomUUID(), signal, ...claim } = input;
102
+ const receipt = await requestBody({
103
+ directory,
104
+ claim: { ...claim, id, action },
105
+ ...(signal === undefined ? {} : { signal }),
106
+ });
107
+ if (receipt.action !== action || receipt.state !== "served") {
108
+ throw new Error(`Akuma body request ${id} returned the wrong action`);
355
109
  }
110
+ return "reference" in receipt
111
+ ? receipt.reference
112
+ : receipt.outcome;
356
113
  }
357
- export async function clearBodyRequestTransport(paths) {
358
- await rm(join(paths.directory, "requests"), { recursive: true, force: true });
359
- }
360
- function matchingRequestOrigin(soul, parent, requestId) {
361
- return soul.origin.kind === "request"
362
- && soul.origin.parent === parent
363
- && soul.origin.requestId === requestId;
364
- }
365
- async function settleObservedSoul(paths, parent, request, soul) {
366
- if (matchingRequestOrigin(soul, parent.id, request.id))
367
- await serveRequest(paths, request.id, request.child);
368
- else
369
- await voidRequest(paths, request.id, "reserved child origin does not match the request");
114
+ export async function requestBodyDeliver(input) {
115
+ return await requestContract(input, "contract.deliver");
370
116
  }
371
- async function settleReserved(paths, parent, request, now, signal) {
372
- const childPaths = pathsForAkuId(request.world, request.child);
373
- const deadline = performance.now() + BIRTH_TIMEOUT_MS;
374
- for (;;) {
375
- signal?.throwIfAborted();
376
- if (!await access(childPaths.directory).then(() => true, () => false)) {
377
- await voidRequest(paths, request.id, "reserved child directory is absent");
378
- return true;
379
- }
380
- const childSoul = await readSoul(childPaths);
381
- if (childSoul !== null) {
382
- await settleObservedSoul(paths, parent, request, childSoul);
383
- return true;
384
- }
385
- const leash = await HeldAkumaLeash.try(childPaths);
386
- if (leash !== null) {
387
- try {
388
- const settledSoul = await readSoul(childPaths);
389
- if (settledSoul !== null) {
390
- await settleObservedSoul(paths, parent, request, settledSoul);
391
- }
392
- else {
393
- await leash.sealIfUnborn(childPaths, { evidence: "request settlement", at: now() });
394
- await voidRequest(paths, request.id, "reserved child was sealed unborn");
395
- }
396
- }
397
- finally {
398
- leash.release();
399
- }
400
- return true;
401
- }
402
- if (performance.now() >= deadline)
403
- return false;
404
- await abortableDelay(POLL_MS, signal);
405
- }
406
- }
407
- export async function settleBodyRequests(paths, parent, now, signal) {
408
- let pending = false;
409
- for (const request of await readNonterminalRequests(paths)) {
410
- signal?.throwIfAborted();
411
- if (request.state === "admitted") {
412
- await voidRequest(paths, request.id, "body died before serving the request");
413
- }
414
- else if (request.state === "reserved" && !await settleReserved(paths, parent, request, now, signal))
415
- pending = true;
416
- }
417
- return pending ? "pending" : "settled";
117
+ export async function requestBodyReview(input) {
118
+ return await requestContract(input, "contract.review");
418
119
  }
@@ -0,0 +1,8 @@
1
+ import { type BodyLaunch } from "./akuma/body.js";
2
+ import type { UpstreamExecutionPort } from "./akuma/request-serve.js";
3
+ type BodyProcessConfiguration = Readonly<{
4
+ home?: string;
5
+ gitPath?: string;
6
+ }>;
7
+ export declare function upstreamFor(launch: BodyLaunch, processConfiguration: BodyProcessConfiguration): UpstreamExecutionPort;
8
+ export {};
@@ -0,0 +1,84 @@
1
+ import { runAkumaBody } from "./akuma/body.js";
2
+ import { worldRootForAkumaPaths } from "./akuma/identity.js";
3
+ import { executeKillAkuma, executeTellAkuma, executeWaitAkuma, } from "./library/fleet.js";
4
+ import { requireBranchesToBeUpToDateFrom, worktreeHooksFrom, } from "./library/configuration.js";
5
+ import { executeForwardedDeliver, executeForwardedReview, } from "./library/contract.js";
6
+ import { Repo } from "./library/repo.js";
7
+ import { settings } from "./settings.js";
8
+ import { executeTaskMutation } from "./task/mutation.js";
9
+ export function upstreamFor(launch, processConfiguration) {
10
+ const path = worldRootForAkumaPaths(launch.paths);
11
+ return {
12
+ wait: async (input) => await executeWaitAkuma({
13
+ path,
14
+ ids: input.targets,
15
+ completion: input.completion,
16
+ ...(input.timeoutMs === undefined ? {} : { timeoutMs: input.timeoutMs }),
17
+ signal: input.signal,
18
+ }),
19
+ tell: async (input) => await executeTellAkuma({
20
+ path,
21
+ id: input.target,
22
+ body: input.body,
23
+ tellId: input.tellId,
24
+ recordedAt: input.recordedAt,
25
+ signal: input.signal,
26
+ }),
27
+ kill: async (input) => {
28
+ const result = await executeKillAkuma({ path, ids: input.targets, signal: input.signal });
29
+ return {
30
+ result,
31
+ service: result.results.map(({ id, evidence }) => ({ id, evidence })),
32
+ };
33
+ },
34
+ deliver: async (input) => {
35
+ const [repo, configuration] = await Promise.all([
36
+ Repo.at({
37
+ path: input.repoRoot,
38
+ ...(processConfiguration.gitPath === undefined ? {} : { gitPath: processConfiguration.gitPath }),
39
+ }),
40
+ settings({
41
+ root: input.repoRoot,
42
+ ...(processConfiguration.home === undefined ? {} : { home: processConfiguration.home }),
43
+ }),
44
+ ]);
45
+ return await executeForwardedDeliver({
46
+ repo,
47
+ contractId: input.contractId,
48
+ requester: input.requester,
49
+ ...(input.message === undefined ? {} : { message: input.message }),
50
+ includeDirty: input.includeDirty,
51
+ requireBranchesToBeUpToDate: requireBranchesToBeUpToDateFrom({ settings: configuration }),
52
+ hooks: worktreeHooksFrom({ settings: configuration }),
53
+ signal: input.signal,
54
+ });
55
+ },
56
+ review: async (input) => {
57
+ const [repo, configuration] = await Promise.all([
58
+ Repo.at({
59
+ path: input.repoRoot,
60
+ ...(processConfiguration.gitPath === undefined ? {} : { gitPath: processConfiguration.gitPath }),
61
+ }),
62
+ settings({
63
+ root: input.repoRoot,
64
+ ...(processConfiguration.home === undefined ? {} : { home: processConfiguration.home }),
65
+ }),
66
+ ]);
67
+ return await executeForwardedReview({
68
+ repo,
69
+ contractId: input.contractId,
70
+ requester: input.requester,
71
+ verdict: input.verdict,
72
+ ...(input.summary === undefined ? {} : { summary: input.summary }),
73
+ hooks: worktreeHooksFrom({ settings: configuration }),
74
+ });
75
+ },
76
+ task: async (input) => await executeTaskMutation({
77
+ world: input.world,
78
+ request: input.request,
79
+ requester: input.requester,
80
+ signal: input.signal,
81
+ }),
82
+ };
83
+ }
84
+ await runAkumaBody(upstreamFor);
@@ -1,15 +1,8 @@
1
- import { parseToAST } from "../markdown/parse.js";
2
- import { indexDocument, indexedHeadings, normalizeTitle, rawSlice, sectionContent, } from "../markdown/query.js";
1
+ import { sectionContent } from "../markdown/query.js";
2
+ import { decodeDocumentEnvelope } from "./envelope.js";
3
3
  function refusal(message) {
4
4
  throw new TypeError(message);
5
5
  }
6
- function nonblankRaw(document, node) {
7
- return rawSlice(document, node.span).trim().length > 0;
8
- }
9
- function topLevelSections(document) {
10
- return indexedHeadings(indexDocument(document), { level: 2 })
11
- .filter((node) => node.type === "section");
12
- }
13
6
  function requiredProse(document, sections, name) {
14
7
  const section = sections.get(name);
15
8
  if (section === undefined)
@@ -20,31 +13,13 @@ function requiredProse(document, sections, name) {
20
13
  return value;
21
14
  }
22
15
  export function decodeArcDocument(source) {
23
- const document = parseToAST(source);
24
- if (document.frontmatter !== undefined)
25
- refusal("arc document may not contain frontmatter");
26
- const titles = indexedHeadings(indexDocument(document), { level: 1 })
27
- .filter((node) => node.type === "section");
28
- if (titles.length !== 1)
29
- refusal("arc document requires exactly one H1 title");
30
- const title = titles[0];
16
+ const { document, title, sections } = decodeDocumentEnvelope(source, "arc");
31
17
  if (title.title.trim().length === 0)
32
18
  refusal("arc title must be nonblank");
33
- if (title.children.some((node) => nonblankRaw(document, node))) {
34
- refusal("arc title may not contain content before the first H2 section");
35
- }
36
- const stray = document.children.filter((node) => node.type !== "section" && nonblankRaw(document, node));
37
- if (stray.length > 0)
38
- refusal("arc document contains content outside an H1 or H2 section");
39
- const sections = new Map();
40
- for (const section of topLevelSections(document)) {
41
- const name = normalizeTitle(section.title);
42
- if (sections.has(name))
43
- refusal(`duplicate arc section '${section.title}'`);
19
+ for (const [name, section] of sections) {
44
20
  if (name !== "objective" && name !== "brief") {
45
21
  refusal(`arc document does not allow ## ${section.title}`);
46
22
  }
47
- sections.set(name, section);
48
23
  }
49
24
  return {
50
25
  title: title.title,