@astrosheep/keiyaku 4.0.4 → 4.0.6

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 (233) 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 +15 -7
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +5 -3
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +24 -4
  8. package/build/src/akuma/akuma.d.ts +2 -1
  9. package/build/src/akuma/akuma.js +27 -9
  10. package/build/src/akuma/allowed.d.ts +1 -1
  11. package/build/src/akuma/allowed.js +3 -10
  12. package/build/src/akuma/archetype.d.ts +3 -1
  13. package/build/src/akuma/archetype.js +11 -6
  14. package/build/src/akuma/body-turn.d.ts +64 -0
  15. package/build/src/akuma/body-turn.js +449 -0
  16. package/build/src/akuma/body.d.ts +4 -5
  17. package/build/src/akuma/body.js +9 -414
  18. package/build/src/akuma/heart/facts.d.ts +37 -9
  19. package/build/src/akuma/heart/facts.js +3 -1
  20. package/build/src/akuma/heart/index.d.ts +4 -13
  21. package/build/src/akuma/heart/index.js +14 -135
  22. package/build/src/akuma/heart/request-authority.d.ts +12 -0
  23. package/build/src/akuma/heart/request-authority.js +119 -0
  24. package/build/src/akuma/heart/request-rows.d.ts +16 -0
  25. package/build/src/akuma/heart/request-rows.js +88 -0
  26. package/build/src/akuma/heart/rows.d.ts +1 -28
  27. package/build/src/akuma/heart/rows.js +4 -138
  28. package/build/src/akuma/heart/schema.d.ts +1 -1
  29. package/build/src/akuma/heart/schema.js +11 -3
  30. package/build/src/akuma/heart/soul.js +0 -36
  31. package/build/src/akuma/heart/timeline.d.ts +1 -0
  32. package/build/src/akuma/heart/timeline.js +19 -0
  33. package/build/src/akuma/index.d.ts +1 -1
  34. package/build/src/akuma/nuke.d.ts +2 -0
  35. package/build/src/akuma/nuke.js +152 -0
  36. package/build/src/akuma/provider-recipe.js +1 -11
  37. package/build/src/akuma/provider.d.ts +4 -7
  38. package/build/src/akuma/providers/acp/config.d.ts +8 -0
  39. package/build/src/akuma/providers/acp/config.js +37 -0
  40. package/build/src/akuma/providers/acp/core.js +12 -2
  41. package/build/src/akuma/providers/acp/index.d.ts +1 -8
  42. package/build/src/akuma/providers/acp/index.js +2 -38
  43. package/build/src/akuma/providers/claude/index.d.ts +0 -4
  44. package/build/src/akuma/providers/claude/index.js +12 -3
  45. package/build/src/akuma/providers/codex-app-server/index.d.ts +2 -27
  46. package/build/src/akuma/providers/codex-app-server/index.js +5 -2
  47. package/build/src/akuma/providers/grok-build/index.js +1 -2
  48. package/build/src/akuma/providers/index.d.ts +3 -2
  49. package/build/src/akuma/providers/index.js +36 -36
  50. package/build/src/akuma/providers/opencode-sdk/index.d.ts +2 -28
  51. package/build/src/akuma/providers/opencode-sdk/index.js +14 -4
  52. package/build/src/akuma/providers/opencode-sdk/session.d.ts +2 -2
  53. package/build/src/akuma/providers/opencode-sdk/session.js +2 -2
  54. package/build/src/akuma/providers/pi/index.d.ts +1 -1
  55. package/build/src/akuma/providers/pi/index.js +29 -6
  56. package/build/src/akuma/request-serve.d.ts +88 -0
  57. package/build/src/akuma/request-serve.js +484 -0
  58. package/build/src/akuma/request-wire.d.ts +125 -0
  59. package/build/src/akuma/request-wire.js +383 -0
  60. package/build/src/akuma/requests.d.ts +28 -94
  61. package/build/src/akuma/requests.js +52 -562
  62. package/build/src/akuma-body.d.ts +7 -0
  63. package/build/src/akuma-body.js +69 -2
  64. package/build/src/alias/index.d.ts +1 -0
  65. package/build/src/alias/index.js +34 -1
  66. package/build/src/body/arc.js +4 -29
  67. package/build/src/body/decode.js +5 -39
  68. package/build/src/body/envelope.d.ts +7 -0
  69. package/build/src/body/envelope.js +37 -0
  70. package/build/src/cli/accepted.js +21 -9
  71. package/build/src/cli/commands/akuma-invoke.js +1 -0
  72. package/build/src/cli/commands/akuma.d.ts +1 -0
  73. package/build/src/cli/commands/akuma.js +13 -10
  74. package/build/src/cli/commands/contract.d.ts +28 -17
  75. package/build/src/cli/commands/contract.js +23 -17
  76. package/build/src/cli/commands/install.d.ts +2 -2
  77. package/build/src/cli/commands/install.js +6 -6
  78. package/build/src/cli/commands/nuke.d.ts +10 -0
  79. package/build/src/cli/commands/nuke.js +20 -0
  80. package/build/src/cli/commands/task-invoke.d.ts +15 -0
  81. package/build/src/cli/commands/task-invoke.js +144 -24
  82. package/build/src/cli/commands/task-query.d.ts +2 -2
  83. package/build/src/cli/commands/task-query.js +1 -2
  84. package/build/src/cli/commands/task.js +22 -22
  85. package/build/src/cli/coordinates.d.ts +6 -2
  86. package/build/src/cli/coordinates.js +12 -56
  87. package/build/src/cli/invoke.d.ts +5 -4
  88. package/build/src/cli/invoke.js +112 -58
  89. package/build/src/cli/main.d.ts +0 -2
  90. package/build/src/cli/main.js +4 -140
  91. package/build/src/cli/parse.d.ts +17 -4
  92. package/build/src/cli/parse.js +93 -15
  93. package/build/src/cli/render/akuma.js +111 -53
  94. package/build/src/cli/render/audit.js +2 -6
  95. package/build/src/cli/render/contract.js +61 -20
  96. package/build/src/cli/render/kanshi.js +15 -5
  97. package/build/src/cli/render/nuke.d.ts +3 -0
  98. package/build/src/cli/render/nuke.js +8 -0
  99. package/build/src/cli/render/receipt.js +28 -1
  100. package/build/src/cli/render/refusal.d.ts +2 -1
  101. package/build/src/cli/render/refusal.js +31 -0
  102. package/build/src/cli/render/terminal.d.ts +0 -2
  103. package/build/src/cli/render/terminal.js +0 -29
  104. package/build/src/cli/render/text.js +6 -1
  105. package/build/src/cli/result.d.ts +36 -9
  106. package/build/src/cli/runtime.d.ts +2 -0
  107. package/build/src/cli/runtime.js +140 -0
  108. package/build/src/cli/usage.d.ts +0 -2
  109. package/build/src/cli/usage.js +0 -4
  110. package/build/src/contract-worktree.d.ts +29 -14
  111. package/build/src/contract-worktree.js +131 -70
  112. package/build/src/core/facts/codec.js +9 -0
  113. package/build/src/core/facts/fold.js +13 -1
  114. package/build/src/core/facts/types.d.ts +8 -1
  115. package/build/src/core/subject.js +4 -3
  116. package/build/src/core/verbs/placement.d.ts +9 -1
  117. package/build/src/core/verbs/placement.js +24 -6
  118. package/build/src/core/verbs/reintegrate.d.ts +13 -0
  119. package/build/src/core/verbs/reintegrate.js +24 -0
  120. package/build/src/dispatch/index.d.ts +1 -1
  121. package/build/src/git/admission.d.ts +2 -1
  122. package/build/src/git/hooks.d.ts +2 -0
  123. package/build/src/git/hooks.js +34 -1
  124. package/build/src/git/integration.d.ts +9 -2
  125. package/build/src/git/integration.js +92 -1
  126. package/build/src/git/nuke.d.ts +2 -0
  127. package/build/src/git/nuke.js +97 -0
  128. package/build/src/git/observe.d.ts +2 -1
  129. package/build/src/git/observe.js +6 -5
  130. package/build/src/git/process.d.ts +28 -0
  131. package/build/src/git/process.js +94 -0
  132. package/build/src/git/read-observation.d.ts +2 -1
  133. package/build/src/git/read-observation.js +3 -2
  134. package/build/src/git/reconcile.d.ts +4 -1
  135. package/build/src/git/reconcile.js +42 -8
  136. package/build/src/git/repository.d.ts +2 -29
  137. package/build/src/git/repository.js +7 -118
  138. package/build/src/git/scratch.d.ts +1 -1
  139. package/build/src/git/scratch.js +1 -1
  140. package/build/src/git/target-placement.d.ts +3 -2
  141. package/build/src/git/target-placement.js +15 -14
  142. package/build/src/git/tender.d.ts +2 -1
  143. package/build/src/git/tender.js +4 -3
  144. package/build/src/git/terminal-seal.d.ts +2 -2
  145. package/build/src/git/terminal-seal.js +3 -6
  146. package/build/src/git/workspace.d.ts +4 -1
  147. package/build/src/git/workspace.js +1 -1
  148. package/build/src/index.d.ts +1 -2
  149. package/build/src/index.js +1 -1
  150. package/build/src/kanshi/read.js +5 -1
  151. package/build/src/library/address.js +7 -1
  152. package/build/src/library/akuma-creation.d.ts +1 -0
  153. package/build/src/library/akuma-creation.js +8 -1
  154. package/build/src/library/audit.d.ts +3 -1
  155. package/build/src/library/audit.js +2 -1
  156. package/build/src/library/bind.d.ts +2 -1
  157. package/build/src/library/bind.js +1 -1
  158. package/build/src/library/configuration.d.ts +1 -1
  159. package/build/src/library/configuration.js +52 -18
  160. package/build/src/library/contract.d.ts +59 -4
  161. package/build/src/library/contract.js +195 -34
  162. package/build/src/library/delivery.d.ts +4 -2
  163. package/build/src/library/fleet.d.ts +27 -7
  164. package/build/src/library/fleet.js +79 -39
  165. package/build/src/library/input.d.ts +0 -1
  166. package/build/src/library/input.js +0 -5
  167. package/build/src/library/keiyaku.d.ts +6 -2
  168. package/build/src/library/keiyaku.js +2 -0
  169. package/build/src/library/mutation.d.ts +3 -1
  170. package/build/src/library/mutation.js +3 -0
  171. package/build/src/library/nuke.d.ts +14 -0
  172. package/build/src/library/nuke.js +54 -0
  173. package/build/src/library/reconcile.d.ts +2 -1
  174. package/build/src/library/reconcile.js +8 -3
  175. package/build/src/library/refusal.d.ts +10 -1
  176. package/build/src/library/region.d.ts +1 -1
  177. package/build/src/library/repo.d.ts +4 -1
  178. package/build/src/library/repo.js +10 -4
  179. package/build/src/protocol/abandon.d.ts +9 -0
  180. package/build/src/protocol/abandon.js +15 -0
  181. package/build/src/protocol/amend.d.ts +16 -0
  182. package/build/src/protocol/amend.js +77 -0
  183. package/build/src/protocol/arc.d.ts +6 -0
  184. package/build/src/protocol/arc.js +12 -0
  185. package/build/src/protocol/attempt.d.ts +2 -1
  186. package/build/src/protocol/audit.d.ts +53 -0
  187. package/build/src/protocol/audit.js +147 -0
  188. package/build/src/protocol/bind.d.ts +1 -1
  189. package/build/src/protocol/completion.d.ts +42 -0
  190. package/build/src/protocol/completion.js +183 -0
  191. package/build/src/protocol/deliver.d.ts +49 -0
  192. package/build/src/protocol/deliver.js +261 -0
  193. package/build/src/protocol/intent.d.ts +2 -1
  194. package/build/src/protocol/intent.js +8 -2
  195. package/build/src/protocol/operations.d.ts +93 -202
  196. package/build/src/protocol/operations.js +23 -642
  197. package/build/src/protocol/placement.d.ts +12 -13
  198. package/build/src/protocol/placement.js +8 -19
  199. package/build/src/protocol/read/status.d.ts +17 -5
  200. package/build/src/protocol/read/status.js +57 -13
  201. package/build/src/protocol/reconcile.d.ts +35 -0
  202. package/build/src/protocol/reconcile.js +41 -0
  203. package/build/src/protocol/reintegrate.d.ts +31 -0
  204. package/build/src/protocol/reintegrate.js +74 -0
  205. package/build/src/protocol/review.d.ts +34 -0
  206. package/build/src/protocol/review.js +156 -0
  207. package/build/src/protocol/run.d.ts +1 -1
  208. package/build/src/runtime/proc/run.js +9 -5
  209. package/build/src/settlement/fence.d.ts +3 -1
  210. package/build/src/settlement/fence.js +24 -0
  211. package/build/src/settlement/holder.d.ts +1 -1
  212. package/build/src/settlement/settle.d.ts +1 -1
  213. package/build/src/settlement/settle.js +1 -2
  214. package/build/src/task/board.d.ts +0 -1
  215. package/build/src/task/board.js +0 -3
  216. package/build/src/task/compose.d.ts +2 -2
  217. package/build/src/task/compose.js +13 -11
  218. package/build/src/task/context.d.ts +14 -2
  219. package/build/src/task/context.js +30 -7
  220. package/build/src/task/index.d.ts +7 -6
  221. package/build/src/task/index.js +17 -145
  222. package/build/src/task/input.d.ts +18 -0
  223. package/build/src/task/input.js +189 -0
  224. package/build/src/task/mutation.d.ts +44 -0
  225. package/build/src/task/mutation.js +93 -0
  226. package/build/src/task/operations.d.ts +14 -6
  227. package/build/src/task/operations.js +57 -57
  228. package/build/src/task/store.d.ts +1 -0
  229. package/build/src/task/store.js +91 -3
  230. package/build/src/verification/execution.d.ts +1 -1
  231. package/build/src/workspace-place.d.ts +3 -4
  232. package/build/src/workspace-place.js +26 -15
  233. package/package.json +3 -3
@@ -1,16 +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, serveUpstreamRequest, 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";
11
- import { decodeAllowedActions } from "./allowed.js";
5
+ import { absolute, atomicJson, decodeReceipt, receiptPath, requestPath, requestPayload, } from "./request-wire.js";
6
+ import { isTaskMutationAction } from "../task/mutation.js";
12
7
  const POLL_MS = 25;
13
- 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;
14
8
  export class AkumaBodyRequestError extends Error {
15
9
  outcome;
16
10
  diagnostic;
@@ -22,268 +16,6 @@ export class AkumaBodyRequestError extends Error {
22
16
  this.name = "AkumaBodyRequestError";
23
17
  }
24
18
  }
25
- function diagnostic(error) {
26
- return error instanceof Error ? error.message : String(error);
27
- }
28
- function object(value) {
29
- return value !== null && typeof value === "object" && !Array.isArray(value)
30
- ? value
31
- : null;
32
- }
33
- function exactKeys(value, expected) {
34
- const actual = Object.keys(value).sort();
35
- return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
36
- }
37
- function absolute(value) {
38
- return typeof value === "string" && isAbsolute(value) && resolve(value) === value;
39
- }
40
- function decodeRecipe(value, cwd) {
41
- const recipe = object(value);
42
- if (recipe === null)
43
- return null;
44
- const expectedKeys = [
45
- ...(recipe.description === undefined ? [] : ["description"]),
46
- "allowed",
47
- "options",
48
- "provider",
49
- ...(recipe.readonly === undefined ? [] : ["readonly"]),
50
- ];
51
- if (!exactKeys(recipe, expectedKeys))
52
- return null;
53
- if (recipe.description !== undefined
54
- && (typeof recipe.description !== "string" || recipe.description.trim().length === 0))
55
- return null;
56
- try {
57
- const selected = resolveProviderExecution(recipe.provider);
58
- const provider = selected.execution;
59
- const adapter = selected.adapter;
60
- const decodedOptions = decodeProviderOptions(recipe.options);
61
- const readonly = recipe.readonly === undefined ? undefined : decodeReadonlyRestraint(recipe.readonly);
62
- if ((decodedOptions.readonly === true) !== (readonly !== undefined))
63
- return null;
64
- const confinement = adapter.confinement({ cwd, options: decodedOptions });
65
- return Object.freeze({
66
- ...(recipe.description === undefined ? {} : { description: recipe.description }),
67
- allowed: decodeAllowedActions(recipe.allowed),
68
- provider,
69
- options: decodedOptions,
70
- ...(readonly === undefined ? {} : { readonly }),
71
- confinement,
72
- });
73
- }
74
- catch {
75
- return null;
76
- }
77
- }
78
- function canonicalAkuId(value) {
79
- if (typeof value !== "string")
80
- return null;
81
- try {
82
- const id = parseAkuId(value).id;
83
- return id === value ? id : null;
84
- }
85
- catch {
86
- return null;
87
- }
88
- }
89
- function canonicalTargets(value) {
90
- if (!Array.isArray(value) || value.length === 0)
91
- return null;
92
- const ids = [];
93
- for (const item of value) {
94
- const id = canonicalAkuId(item);
95
- if (id === null)
96
- return null;
97
- if (ids.length > 0 && Buffer.compare(Buffer.from(ids.at(-1)), Buffer.from(id)) >= 0)
98
- return null;
99
- ids.push(id);
100
- }
101
- return ids;
102
- }
103
- function decodeCallClaim(id, payload) {
104
- const expected = [
105
- "archetype",
106
- "body",
107
- ...(payload.cwd === undefined ? [] : ["cwd"]),
108
- "recipe",
109
- "world",
110
- ];
111
- if (!exactKeys(payload, expected)
112
- || typeof payload.body !== "string"
113
- || !absolute(payload.world))
114
- return null;
115
- if (payload.cwd !== undefined && !absolute(payload.cwd))
116
- return null;
117
- try {
118
- archetypeName(payload.archetype);
119
- }
120
- catch {
121
- return null;
122
- }
123
- return {
124
- id,
125
- action: "akuma.call",
126
- world: payload.world,
127
- archetype: payload.archetype,
128
- body: payload.body,
129
- recipe: payload.recipe,
130
- ...(payload.cwd === undefined ? {} : { cwd: payload.cwd }),
131
- };
132
- }
133
- function decodeWaitClaim(id, payload) {
134
- const expected = [
135
- "completion",
136
- "targets",
137
- ...(payload.timeoutMs === undefined ? [] : ["timeoutMs"]),
138
- ];
139
- const targets = canonicalTargets(payload.targets);
140
- if (!exactKeys(payload, expected)
141
- || targets === null
142
- || (payload.completion !== "any" && payload.completion !== "all"))
143
- return null;
144
- if (payload.timeoutMs !== undefined
145
- && (!Number.isSafeInteger(payload.timeoutMs) || payload.timeoutMs < 0))
146
- return null;
147
- return {
148
- id,
149
- action: "akuma.wait",
150
- targets,
151
- completion: payload.completion,
152
- ...(payload.timeoutMs === undefined ? {} : { timeoutMs: payload.timeoutMs }),
153
- };
154
- }
155
- function decodeTellClaim(id, payload) {
156
- const target = canonicalAkuId(payload.target);
157
- if (!exactKeys(payload, ["body", "target"])
158
- || target === null
159
- || typeof payload.body !== "string")
160
- return null;
161
- return { id, action: "akuma.tell", target, body: payload.body };
162
- }
163
- function decodeKillClaim(id, payload) {
164
- const targets = canonicalTargets(payload.targets);
165
- return exactKeys(payload, ["targets"]) && targets !== null
166
- ? { id, action: "akuma.kill", targets }
167
- : null;
168
- }
169
- function decodeClaim(bytes, fileId) {
170
- let decoded;
171
- try {
172
- decoded = JSON.parse(bytes);
173
- }
174
- catch {
175
- return null;
176
- }
177
- const value = object(decoded);
178
- if (value === null
179
- || !exactKeys(value, ["action", "id", "payload"])
180
- || value.id !== fileId
181
- || typeof value.id !== "string"
182
- || !UUID.test(value.id))
183
- return null;
184
- const payload = object(value.payload);
185
- if (payload === null)
186
- return null;
187
- if (value.action === "akuma.call")
188
- return decodeCallClaim(value.id, payload);
189
- if (value.action === "akuma.wait")
190
- return decodeWaitClaim(value.id, payload);
191
- if (value.action === "akuma.tell")
192
- return decodeTellClaim(value.id, payload);
193
- if (value.action === "akuma.kill")
194
- return decodeKillClaim(value.id, payload);
195
- return null;
196
- }
197
- function decodeUpstreamFailure(value) {
198
- const failure = object(value);
199
- if (failure === null)
200
- return null;
201
- if (failure.kind === "akuma-not-born" && exactKeys(failure, ["id", "kind"])) {
202
- const id = canonicalAkuId(failure.id);
203
- return id === null ? null : { kind: failure.kind, id };
204
- }
205
- if (failure.kind === "failed"
206
- && exactKeys(failure, ["diagnostic", "kind"])
207
- && typeof failure.diagnostic === "string") {
208
- return { kind: failure.kind, diagnostic: failure.diagnostic };
209
- }
210
- return null;
211
- }
212
- function decodeUpstreamOutcome(value) {
213
- const outcome = object(value);
214
- if (outcome?.kind === "returned" && exactKeys(outcome, ["kind", "result"])) {
215
- return { kind: outcome.kind, result: outcome.result };
216
- }
217
- if (outcome?.kind !== "failed" || !exactKeys(outcome, ["failure", "kind"]))
218
- return null;
219
- const failure = decodeUpstreamFailure(outcome.failure);
220
- return failure === null ? null : { kind: outcome.kind, failure };
221
- }
222
- function decodeReceipt(bytes, requestId, action) {
223
- let decoded;
224
- try {
225
- decoded = JSON.parse(bytes);
226
- }
227
- catch {
228
- return null;
229
- }
230
- const value = object(decoded);
231
- if (value === null || value.id !== requestId || value.action !== action)
232
- return null;
233
- if (action === "akuma.call"
234
- && value.state === "served"
235
- && exactKeys(value, ["action", "child", "id", "state"])) {
236
- const child = canonicalAkuId(value.child);
237
- return child === null ? null : { id: requestId, action, state: value.state, child };
238
- }
239
- if (action !== "akuma.call"
240
- && value.state === "served"
241
- && exactKeys(value, ["action", "id", "outcome", "state"])) {
242
- const outcome = decodeUpstreamOutcome(value.outcome);
243
- return outcome === null ? null : { id: requestId, action, state: value.state, outcome };
244
- }
245
- if (value.state === "refused"
246
- && exactKeys(value, ["action", "diagnostic", "id", "state"])
247
- && typeof value.diagnostic === "string") {
248
- return { id: requestId, action, state: value.state, diagnostic: value.diagnostic };
249
- }
250
- if (value.state === "voided"
251
- && exactKeys(value, ["action", "evidence", "id", "state"])
252
- && typeof value.evidence === "string") {
253
- return { id: requestId, action, state: value.state, evidence: value.evidence };
254
- }
255
- return null;
256
- }
257
- async function atomicJson(path, value) {
258
- const temporary = `${path}.${randomUUID()}.tmp`;
259
- try {
260
- await writeFile(temporary, `${JSON.stringify(value)}\n`, { flag: "wx" });
261
- await rename(temporary, path);
262
- }
263
- finally {
264
- await rm(temporary, { force: true });
265
- }
266
- }
267
- function receiptFor(fact) {
268
- if (fact.action === "akuma.call" && fact.state === "served") {
269
- return { id: fact.id, action: fact.action, state: fact.state, child: fact.child };
270
- }
271
- if (fact.state === "refused") {
272
- return { id: fact.id, action: fact.action, state: fact.state, diagnostic: fact.diagnostic };
273
- }
274
- if (fact.state === "voided")
275
- return { id: fact.id, action: fact.action, state: fact.state, evidence: fact.evidence };
276
- return null;
277
- }
278
- async function projectReceipt(directory, fact, outcome) {
279
- const receipt = outcome === undefined
280
- ? receiptFor(fact)
281
- : fact.action === "akuma.call" || fact.state !== "served"
282
- ? null
283
- : { id: fact.id, action: fact.action, state: fact.state, outcome };
284
- if (receipt !== null)
285
- await atomicJson(join(directory, `${fact.id}.receipt.json`), receipt);
286
- }
287
19
  export function injectedBodyRequests() {
288
20
  const directory = process.env[AKUMA_REQUESTS_ENV];
289
21
  if (directory === undefined)
@@ -293,14 +25,18 @@ export function injectedBodyRequests() {
293
25
  return directory;
294
26
  }
295
27
  async function requestBody(input) {
296
- const { id, action, ...payload } = input.claim;
297
- await atomicJson(join(input.directory, `${id}.request.json`), { id, action, payload });
298
- const receiptPath = join(input.directory, `${id}.receipt.json`);
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),
33
+ });
34
+ const path = receiptPath(input.directory, input.claim.id);
299
35
  for (;;) {
300
36
  try {
301
- const receipt = decodeReceipt(await readFile(receiptPath, "utf8"), id, action);
37
+ const receipt = decodeReceipt(await readFile(path, "utf8"), input.claim.id, input.claim.action);
302
38
  if (receipt === null)
303
- throw new Error(`Akuma body request ${id} has an invalid receipt`);
39
+ throw new Error(`Akuma body request ${input.claim.id} has an invalid receipt`);
304
40
  if (receipt.state === "refused")
305
41
  throw new AkumaBodyRequestError("refused", receipt.diagnostic);
306
42
  if (receipt.state === "voided")
@@ -314,7 +50,7 @@ async function requestBody(input) {
314
50
  if (!await access(input.directory).then(() => true, () => false)) {
315
51
  throw new AkumaBodyRequestError("voided", "parent request channel closed before a receipt");
316
52
  }
317
- await abortableDelay(POLL_MS);
53
+ await abortableDelay(POLL_MS, input.signal);
318
54
  }
319
55
  }
320
56
  export async function requestBodyCall(input) {
@@ -325,13 +61,30 @@ export async function requestBodyCall(input) {
325
61
  }
326
62
  return receipt.child;
327
63
  }
328
- async function requestUpstream(directory, claim) {
329
- const receipt = await requestBody({ directory, claim });
330
- if (receipt.action === "akuma.call" || receipt.state !== "served") {
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)) {
331
67
  throw new Error(`Akuma body request ${claim.id} returned the wrong action`);
332
68
  }
333
69
  return receipt.outcome;
334
70
  }
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
+ }
335
88
  export async function requestBodyWait(input) {
336
89
  const { directory, id = randomUUID(), ...claim } = input;
337
90
  return await requestUpstream(directory, { ...claim, id, action: "akuma.wait" });
@@ -344,286 +97,23 @@ export async function requestBodyKill(input) {
344
97
  const { directory, id = randomUUID(), ...claim } = input;
345
98
  return await requestUpstream(directory, { ...claim, id, action: "akuma.kill" });
346
99
  }
347
- async function serveCallClaim(input) {
348
- input.signal.throwIfAborted();
349
- const cwd = input.claim.cwd ?? input.parent.cwd;
350
- const recipe = decodeRecipe(input.claim.recipe, cwd);
351
- if (recipe === null)
352
- return;
353
- let fact = await admitRequest(input.paths, { ...input.claim, recipe, admittedAt: input.now() });
354
- const existing = receiptFor(fact);
355
- if (existing !== null) {
356
- if (!input.signal.aborted)
357
- await projectReceipt(input.directory, fact);
358
- return;
359
- }
360
- if (fact.action !== "akuma.call" || fact.state !== "admitted") {
361
- throw new Error(`Akuma request ${fact.id} cannot be replayed from ${fact.state}`);
362
- }
363
- const request = fact;
364
- const servingWorld = worldRootForAkumaPaths(input.paths);
365
- if (request.world !== servingWorld) {
366
- fact = await refuseRequest(input.paths, request.id, `request world ${request.world} does not match ${servingWorld}`);
367
- await projectReceipt(input.directory, fact);
368
- return;
369
- }
370
- let child;
371
- try {
372
- input.signal.throwIfAborted();
373
- const published = await publishAkuma({
374
- worldPath: servingWorld,
375
- archetype: request.archetype,
376
- reserve: async (allocated) => { fact = await reserveRequest(input.paths, request.id, allocated.id); },
377
- signal: input.signal,
378
- launch: async (allocated) => await input.spawn({
379
- paths: allocated.paths,
380
- seed: {
381
- id: allocated.id,
382
- archetype: allocated.archetype,
383
- ...(request.recipe.description === undefined ? {} : { description: request.recipe.description }),
384
- provider: request.recipe.provider,
385
- options: request.recipe.options,
386
- ...(request.recipe.readonly === undefined ? {} : { readonly: request.recipe.readonly }),
387
- allowed: request.recipe.allowed,
388
- cwd,
389
- origin: { kind: "request", parent: input.parent.id, requestId: request.id },
390
- confinement: request.recipe.confinement,
391
- },
392
- initialBody: request.body,
393
- }),
394
- });
395
- child = published.id;
396
- }
397
- catch (error) {
398
- if (input.signal.aborted)
399
- return;
400
- const current = await readRequest(input.paths, request.id);
401
- if (current === null)
402
- throw error;
403
- fact = current.state === "admitted" || current.state === "reserved"
404
- ? await voidRequest(input.paths, request.id, diagnostic(error))
405
- : current;
406
- await projectReceipt(input.directory, fact);
407
- return;
408
- }
409
- fact = await serveRequest(input.paths, request.id, child);
410
- await projectReceipt(input.directory, fact);
411
- }
412
- function upstreamFailure(error) {
413
- const value = object(error);
414
- if (value?.kind === "akuma-not-born") {
415
- const id = canonicalAkuId(value.id);
416
- if (id !== null)
417
- return { kind: "akuma-not-born", id };
418
- }
419
- return { kind: "failed", diagnostic: diagnostic(error) };
420
- }
421
- async function serveUpstreamClaim(input) {
422
- input.signal.throwIfAborted();
423
- let fact = await admitRequest(input.paths, { ...input.claim, admittedAt: input.now() });
424
- const existing = receiptFor(fact);
425
- if (existing !== null) {
426
- if (!input.signal.aborted)
427
- await projectReceipt(input.directory, fact);
428
- return;
429
- }
430
- if (fact.state === "served") {
431
- await projectReceipt(input.directory, fact, {
432
- kind: "failed",
433
- failure: { kind: "failed", diagnostic: "served request receipt is no longer available" },
434
- });
435
- return;
436
- }
437
- if (fact.state !== "admitted" || fact.action === "akuma.call") {
438
- throw new Error(`Akuma request ${fact.id} cannot be served from ${fact.state}`);
439
- }
440
- const request = fact;
441
- let outcome;
442
- let killService = [];
443
- try {
444
- if (input.upstream === undefined)
445
- throw new Error("upstream execution port is unavailable");
446
- let result;
447
- if (request.action === "akuma.wait") {
448
- const waitRequest = request;
449
- result = await input.upstream.wait({
450
- targets: waitRequest.targets,
451
- completion: waitRequest.completion,
452
- ...(waitRequest.timeoutMs === undefined ? {} : { timeoutMs: waitRequest.timeoutMs }),
453
- signal: input.signal,
454
- });
455
- }
456
- else if (request.action === "akuma.tell") {
457
- const tellRequest = request;
458
- result = await input.upstream.tell({
459
- target: tellRequest.target,
460
- body: tellRequest.body,
461
- tellId: tellRequest.id,
462
- recordedAt: tellRequest.admittedAt,
463
- signal: input.signal,
464
- });
465
- }
466
- else {
467
- const killRequest = request;
468
- const served = await input.upstream.kill({ targets: killRequest.targets, signal: input.signal });
469
- killService = served.service;
470
- result = served.result;
471
- }
472
- outcome = { kind: "returned", result };
473
- }
474
- catch (error) {
475
- if (input.signal.aborted)
476
- return;
477
- outcome = { kind: "failed", failure: upstreamFailure(error) };
478
- }
479
- input.signal.throwIfAborted();
480
- const service = request.action === "akuma.wait"
481
- ? { action: request.action }
482
- : request.action === "akuma.tell"
483
- ? { action: request.action, target: request.target, tellId: request.id }
484
- : { action: request.action, results: killService };
485
- fact = await serveUpstreamRequest(input.paths, request.id, service);
486
- await projectReceipt(input.directory, fact, outcome);
487
- }
488
- async function serveClaim(input) {
489
- return input.claim.action === "akuma.call"
490
- ? await serveCallClaim({ ...input, claim: input.claim })
491
- : await serveUpstreamClaim({ ...input, claim: input.claim });
492
- }
493
- async function requestFiles(directory) {
494
- try {
495
- return (await readdir(directory))
496
- .filter((name) => name.endsWith(".request.json"))
497
- .sort();
498
- }
499
- catch (error) {
500
- if (error.code === "ENOENT")
501
- return [];
502
- throw error;
503
- }
504
- }
505
- export class BodyRequestPump {
506
- input;
507
- directory;
508
- failure;
509
- closeSignal = new AbortController();
510
- handled = new Set();
511
- running;
512
- cancelAdmission;
513
- constructor(input) {
514
- this.input = input;
515
- this.directory = join(input.paths.directory, "requests", String(input.bodySequence));
516
- this.cancelAdmission = () => this.stopAdmission();
517
- input.signal.addEventListener("abort", this.cancelAdmission, { once: true });
518
- if (input.signal.aborted)
519
- this.cancelAdmission();
520
- this.running = this.run();
521
- this.failure = this.running.then(() => new Promise(() => { }), (error) => Promise.reject(error));
522
- }
523
- static async open(input) {
524
- const directory = join(input.paths.directory, "requests", String(input.bodySequence));
525
- await mkdir(directory, { recursive: true });
526
- return new BodyRequestPump(input);
527
- }
528
- async run() {
529
- while (!this.closeSignal.signal.aborted) {
530
- for (const name of await requestFiles(this.directory)) {
531
- if (this.closeSignal.signal.aborted)
532
- return;
533
- const id = name.slice(0, -".request.json".length);
534
- if (this.handled.has(id))
535
- continue;
536
- const claim = decodeClaim(await readFile(join(this.directory, name), "utf8"), id);
537
- if (claim === null) {
538
- this.handled.add(id);
539
- continue;
540
- }
541
- await serveClaim({ directory: this.directory, claim, ...this.input, signal: this.closeSignal.signal });
542
- this.handled.add(id);
543
- }
544
- try {
545
- await abortableDelay(POLL_MS, this.closeSignal.signal);
546
- }
547
- catch {
548
- return;
549
- }
550
- }
551
- }
552
- async close() {
553
- this.stopAdmission();
554
- try {
555
- await this.running;
556
- }
557
- finally {
558
- this.input.signal.removeEventListener("abort", this.cancelAdmission);
559
- await rm(this.directory, { recursive: true, force: true });
560
- }
561
- }
562
- stopAdmission() {
563
- if (!this.closeSignal.signal.aborted)
564
- this.closeSignal.abort(new Error("Body request pump closed"));
565
- }
566
- }
567
- export async function clearBodyRequestTransport(paths) {
568
- await rm(join(paths.directory, "requests"), { recursive: true, force: true });
569
- }
570
- function matchingRequestOrigin(soul, parent, requestId) {
571
- return soul.origin.kind === "request"
572
- && soul.origin.parent === parent
573
- && soul.origin.requestId === requestId;
574
- }
575
- async function settleObservedSoul(paths, parent, request, soul) {
576
- if (matchingRequestOrigin(soul, parent.id, request.id))
577
- await serveRequest(paths, request.id, request.child);
578
- else
579
- await voidRequest(paths, request.id, "reserved child origin does not match the request");
580
- }
581
- async function settleReserved(paths, parent, request, now, signal) {
582
- const childPaths = pathsForAkuId(request.world, request.child);
583
- const deadline = performance.now() + BIRTH_TIMEOUT_MS;
584
- for (;;) {
585
- signal?.throwIfAborted();
586
- if (!await access(childPaths.directory).then(() => true, () => false)) {
587
- await voidRequest(paths, request.id, "reserved child directory is absent");
588
- return true;
589
- }
590
- const childSoul = await readSoul(childPaths);
591
- if (childSoul !== null) {
592
- await settleObservedSoul(paths, parent, request, childSoul);
593
- return true;
594
- }
595
- const leash = await HeldAkumaLeash.try(childPaths);
596
- if (leash !== null) {
597
- try {
598
- const settledSoul = await readSoul(childPaths);
599
- if (settledSoul !== null) {
600
- await settleObservedSoul(paths, parent, request, settledSoul);
601
- }
602
- else {
603
- await leash.sealIfUnborn(childPaths, { evidence: "request settlement", at: now() });
604
- await voidRequest(paths, request.id, "reserved child was sealed unborn");
605
- }
606
- }
607
- finally {
608
- leash.release();
609
- }
610
- return true;
611
- }
612
- if (performance.now() >= deadline)
613
- return false;
614
- await abortableDelay(POLL_MS, signal);
615
- }
616
- }
617
- export async function settleBodyRequests(paths, parent, now, signal) {
618
- let pending = false;
619
- for (const request of await readNonterminalRequests(paths)) {
620
- signal?.throwIfAborted();
621
- if (request.state === "admitted") {
622
- await voidRequest(paths, request.id, "body died before serving the request");
623
- }
624
- else if (request.state === "reserved"
625
- && !await settleReserved(paths, parent, request, now, signal))
626
- pending = true;
627
- }
628
- return pending ? "pending" : "settled";
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`);
109
+ }
110
+ return "reference" in receipt
111
+ ? receipt.reference
112
+ : receipt.outcome;
113
+ }
114
+ export async function requestBodyDeliver(input) {
115
+ return await requestContract(input, "contract.deliver");
116
+ }
117
+ export async function requestBodyReview(input) {
118
+ return await requestContract(input, "contract.review");
629
119
  }
@@ -1 +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;
1
8
  export {};