@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,10 +1,10 @@
1
- import { answeredTurnFact, endBodyFact, finishBodyFact, insertActivityFact, insertTurnEndFact, insertTurnStartFact, insertPauseControl, insertRequestFact, insertSessionFact, insertStopControl, killFactForBody, latestBodyFact, latestKillFact, latestSessionFact, lastAnsweredTurnFact, nonterminalRequestFacts, pauseFact, requestFact, sessionFactForCoordinate, stopFact, updateRequestRefused, updateRequestReserved, updateRequestServed, updateRequestVoided, updateUpstreamRequestServed, } from "./rows.js";
1
+ import { answeredTurnFact, endBodyFact, finishBodyFact, insertActivityFact, insertTurnEndFact, insertTurnStartFact, insertPauseControl, insertSessionFact, insertStopControl, killFactForBody, latestBodyFact, latestKillFact, latestSessionFact, lastAnsweredTurnFact, pauseFact, sessionFactForCoordinate, stopFact, } from "./rows.js";
2
2
  import { insertTellDeliveryFact, insertTellFact, insertTellReceiptFact, pendingTellFacts, tellFact, tellIdsForFence, } from "./tells.js";
3
- import { activityFactSlice, pruneActivityFacts, } from "./timeline.js";
3
+ import { activityFactSlice, lastActivityAt as readLastActivityAt, pruneActivityFacts, } from "./timeline.js";
4
4
  import { isHeartAbsent, readSealFromLeash, readTransaction, transaction, withHeart } from "./storage.js";
5
5
  import { soulFact } from "./soul.js";
6
- import { clipAllowedActions } from "../allowed.js";
7
6
  export { HeartAbsentError, HeldAkumaLeash, initializeHeart, isHeartAbsent, probeLeash } from "./storage.js";
7
+ export { admitRequest, readNonterminalRequests, readRequest, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, voidRequest, } from "./request-authority.js";
8
8
  export { life, lifeAt } from "./facts.js";
9
9
  const ACTIVITY_LIMIT = 5_000;
10
10
  export async function readSoul(paths) {
@@ -89,137 +89,6 @@ export async function recordTellReceipt(paths, input) {
89
89
  pruneActivityFacts(heart, ACTIVITY_LIMIT);
90
90
  }));
91
91
  }
92
- function sameRequestInput(fact, input) {
93
- if (fact.id !== input.id || fact.action !== input.action)
94
- return false;
95
- if (fact.action === "akuma.call" && input.action === "akuma.call") {
96
- return fact.archetype === input.archetype
97
- && fact.body === input.body
98
- && fact.cwd === input.cwd
99
- && fact.world === input.world
100
- && JSON.stringify(fact.recipe) === JSON.stringify(input.recipe);
101
- }
102
- if (fact.action === "akuma.wait" && input.action === "akuma.wait") {
103
- return fact.completion === input.completion
104
- && fact.timeoutMs === input.timeoutMs
105
- && JSON.stringify(fact.targets) === JSON.stringify(input.targets);
106
- }
107
- if (fact.action === "akuma.tell" && input.action === "akuma.tell") {
108
- return fact.target === input.target && fact.body === input.body;
109
- }
110
- return fact.action === "akuma.kill" && input.action === "akuma.kill"
111
- && JSON.stringify(fact.targets) === JSON.stringify(input.targets);
112
- }
113
- export async function admitRequest(paths, input) {
114
- return await withHeart(paths, (heart) => transaction(heart, () => {
115
- const soul = soulFact(heart);
116
- if (soul === null)
117
- throw new Error("Akuma request admission requires a born Soul");
118
- const action = input.action;
119
- const parentAllowed = soul.allowed;
120
- const permitted = action === "akuma.wait" || parentAllowed.includes(action);
121
- const normalized = input.action === "akuma.call"
122
- ? {
123
- ...input,
124
- recipe: {
125
- ...input.recipe,
126
- allowed: permitted ? clipAllowedActions(input.recipe.allowed, parentAllowed) : input.recipe.allowed,
127
- },
128
- }
129
- : input;
130
- insertRequestFact(heart, {
131
- ...normalized,
132
- requester: soul.id,
133
- admittedAt: input.admittedAt,
134
- ...(permitted ? {} : { refusal: `not-allowed: ${action}` }),
135
- });
136
- const fact = requestFact(heart, input.id);
137
- if (fact === null)
138
- throw new Error(`Akuma request ${input.id} was not admitted`);
139
- if (fact.requester !== soul.id || !sameRequestInput(fact, normalized)) {
140
- throw new Error(`Akuma request ${input.id} reused different input`);
141
- }
142
- return fact;
143
- }));
144
- }
145
- export async function reserveRequest(paths, id, child) {
146
- return await withHeart(paths, (heart) => transaction(heart, () => {
147
- const before = requestFact(heart, id);
148
- if (before === null)
149
- throw new Error(`unknown Akuma request ${id}`);
150
- if (before.action !== "akuma.call")
151
- throw new Error(`Akuma request ${id} cannot reserve a child`);
152
- if (before.state === "admitted")
153
- updateRequestReserved(heart, id, child);
154
- else if ((before.state !== "reserved" && before.state !== "served") || before.child !== child) {
155
- throw new Error(`Akuma request ${id} cannot reserve ${child}`);
156
- }
157
- return requestFact(heart, id);
158
- }));
159
- }
160
- export async function serveRequest(paths, id, child) {
161
- return await withHeart(paths, (heart) => transaction(heart, () => {
162
- const before = requestFact(heart, id);
163
- if (before === null)
164
- throw new Error(`unknown Akuma request ${id}`);
165
- if (before.action !== "akuma.call")
166
- throw new Error(`Akuma request ${id} cannot serve a child`);
167
- if (before.state === "reserved" && before.child === child)
168
- updateRequestServed(heart, id, child);
169
- else if (before.state !== "served" || before.child !== child) {
170
- throw new Error(`Akuma request ${id} cannot serve ${child}`);
171
- }
172
- return requestFact(heart, id);
173
- }));
174
- }
175
- export async function serveUpstreamRequest(paths, id, service) {
176
- return await withHeart(paths, (heart) => transaction(heart, () => {
177
- const before = requestFact(heart, id);
178
- if (before === null)
179
- throw new Error(`unknown Akuma request ${id}`);
180
- if (before.action === "akuma.call" || service.action !== before.action) {
181
- throw new Error(`Akuma request ${id} has a mismatched service reference`);
182
- }
183
- if (before.state === "admitted")
184
- updateUpstreamRequestServed(heart, id, service);
185
- else if (before.state !== "served" || JSON.stringify(before.service) !== JSON.stringify(service)) {
186
- throw new Error(`Akuma request ${id} cannot be served as ${service.action}`);
187
- }
188
- return requestFact(heart, id);
189
- }));
190
- }
191
- export async function refuseRequest(paths, id, diagnostic) {
192
- return await withHeart(paths, (heart) => transaction(heart, () => {
193
- const before = requestFact(heart, id);
194
- if (before === null)
195
- throw new Error(`unknown Akuma request ${id}`);
196
- if (before.state === "admitted")
197
- updateRequestRefused(heart, id, diagnostic);
198
- else if (before.state !== "refused" || before.diagnostic !== diagnostic) {
199
- throw new Error(`Akuma request ${id} cannot be refused`);
200
- }
201
- return requestFact(heart, id);
202
- }));
203
- }
204
- export async function voidRequest(paths, id, evidence) {
205
- return await withHeart(paths, (heart) => transaction(heart, () => {
206
- const before = requestFact(heart, id);
207
- if (before === null)
208
- throw new Error(`unknown Akuma request ${id}`);
209
- if (before.state === "admitted" || before.state === "reserved")
210
- updateRequestVoided(heart, id, evidence);
211
- else if (before.state !== "voided" || before.evidence !== evidence) {
212
- throw new Error(`Akuma request ${id} cannot be voided`);
213
- }
214
- return requestFact(heart, id);
215
- }));
216
- }
217
- export async function readRequest(paths, id) {
218
- return await withHeart(paths, (heart) => requestFact(heart, id));
219
- }
220
- export async function readNonterminalRequests(paths) {
221
- return await withHeart(paths, nonterminalRequestFacts);
222
- }
223
92
  export async function readKill(paths, bodySequence) {
224
93
  return await withHeart(paths, (heart) => killFactForBody(heart, bodySequence));
225
94
  }
@@ -303,11 +172,21 @@ export async function readHeart(paths) {
303
172
  latestKill: latestKillFact(heart),
304
173
  stop: stopFact(heart),
305
174
  pause: pauseFact(heart),
175
+ lastActivityAt: readLastActivityAt(heart),
306
176
  })));
307
177
  }
308
178
  catch (error) {
309
179
  if (isHeartAbsent(error)) {
310
- return { soul: null, latestBody: null, latestSession: null, pending: [], latestKill: null, stop: null, pause: null };
180
+ return {
181
+ soul: null,
182
+ latestBody: null,
183
+ latestSession: null,
184
+ pending: [],
185
+ latestKill: null,
186
+ stop: null,
187
+ pause: null,
188
+ lastActivityAt: null,
189
+ };
311
190
  }
312
191
  throw error;
313
192
  }
@@ -0,0 +1,12 @@
1
+ import type { AkuId, AkumaPaths } from "../identity.js";
2
+ import type { RequestFact, RequestInput, UpstreamRequestService } from "./facts.js";
3
+ export declare function admitRequest(paths: AkumaPaths, input: RequestInput & Readonly<{
4
+ admittedAt: string;
5
+ }>): Promise<RequestFact>;
6
+ export declare function reserveRequest(paths: AkumaPaths, id: string, child: AkuId): Promise<RequestFact>;
7
+ export declare function serveRequest(paths: AkumaPaths, id: string, child: AkuId): Promise<RequestFact>;
8
+ export declare function serveUpstreamRequest(paths: AkumaPaths, id: string, service: UpstreamRequestService): Promise<RequestFact>;
9
+ export declare function refuseRequest(paths: AkumaPaths, id: string, diagnostic: string): Promise<RequestFact>;
10
+ export declare function voidRequest(paths: AkumaPaths, id: string, evidence: string): Promise<RequestFact>;
11
+ export declare function readRequest(paths: AkumaPaths, id: string): Promise<RequestFact | null>;
12
+ export declare function readNonterminalRequests(paths: AkumaPaths): Promise<readonly RequestFact[]>;
@@ -0,0 +1,119 @@
1
+ import { insertRequestFact, nonterminalRequestFacts, requestFact, updateRequestRefused, updateRequestReserved, updateRequestServed, updateRequestVoided, updateUpstreamRequestServed, } from "./request-rows.js";
2
+ import { transaction, withHeart } from "./storage.js";
3
+ import { soulFact } from "./soul.js";
4
+ import { clipAllowedActions } from "../allowed.js";
5
+ import { requestPayloadJson } from "./request-rows.js";
6
+ export async function admitRequest(paths, input) {
7
+ return await withHeart(paths, (heart) => transaction(heart, () => {
8
+ const soul = soulFact(heart);
9
+ if (soul === null)
10
+ throw new Error("Akuma request admission requires a born Soul");
11
+ const action = input.action;
12
+ const parentAllowed = soul.allowed;
13
+ const permitted = action === "akuma.wait" || parentAllowed.includes(action);
14
+ const normalized = input.action === "akuma.call"
15
+ ? {
16
+ ...input,
17
+ recipe: {
18
+ ...input.recipe,
19
+ allowed: permitted ? clipAllowedActions(input.recipe.allowed, parentAllowed) : input.recipe.allowed,
20
+ },
21
+ }
22
+ : input;
23
+ insertRequestFact(heart, {
24
+ ...normalized,
25
+ requester: soul.id,
26
+ admittedAt: input.admittedAt,
27
+ ...(permitted ? {} : { refusal: `not-allowed: ${action}` }),
28
+ });
29
+ const fact = requestFact(heart, input.id);
30
+ if (fact === null)
31
+ throw new Error(`Akuma request ${input.id} was not admitted`);
32
+ if (fact.requester !== soul.id
33
+ || fact.id !== normalized.id
34
+ || fact.action !== normalized.action
35
+ || requestPayloadJson(fact) !== requestPayloadJson(normalized)) {
36
+ throw new Error(`Akuma request ${input.id} reused different input`);
37
+ }
38
+ return fact;
39
+ }));
40
+ }
41
+ export async function reserveRequest(paths, id, child) {
42
+ return await withHeart(paths, (heart) => transaction(heart, () => {
43
+ const before = requestFact(heart, id);
44
+ if (before === null)
45
+ throw new Error(`unknown Akuma request ${id}`);
46
+ if (before.action !== "akuma.call")
47
+ throw new Error(`Akuma request ${id} cannot reserve a child`);
48
+ if (before.state === "admitted")
49
+ updateRequestReserved(heart, id, child);
50
+ else if ((before.state !== "reserved" && before.state !== "served") || before.child !== child) {
51
+ throw new Error(`Akuma request ${id} cannot reserve ${child}`);
52
+ }
53
+ return requestFact(heart, id);
54
+ }));
55
+ }
56
+ export async function serveRequest(paths, id, child) {
57
+ return await withHeart(paths, (heart) => transaction(heart, () => {
58
+ const before = requestFact(heart, id);
59
+ if (before === null)
60
+ throw new Error(`unknown Akuma request ${id}`);
61
+ if (before.action !== "akuma.call")
62
+ throw new Error(`Akuma request ${id} cannot serve a child`);
63
+ if (before.state === "reserved" && before.child === child)
64
+ updateRequestServed(heart, id, child);
65
+ else if (before.state !== "served" || before.child !== child) {
66
+ throw new Error(`Akuma request ${id} cannot serve ${child}`);
67
+ }
68
+ return requestFact(heart, id);
69
+ }));
70
+ }
71
+ export async function serveUpstreamRequest(paths, id, service) {
72
+ return await withHeart(paths, (heart) => transaction(heart, () => {
73
+ const before = requestFact(heart, id);
74
+ if (before === null)
75
+ throw new Error(`unknown Akuma request ${id}`);
76
+ if (before.action === "akuma.call" || service.action !== before.action) {
77
+ throw new Error(`Akuma request ${id} has a mismatched service reference`);
78
+ }
79
+ if (before.state === "admitted")
80
+ updateUpstreamRequestServed(heart, id, service);
81
+ else if (before.state !== "served") {
82
+ throw new Error(`Akuma request ${id} cannot be served as ${service.action}`);
83
+ }
84
+ return requestFact(heart, id);
85
+ }));
86
+ }
87
+ export async function refuseRequest(paths, id, diagnostic) {
88
+ return await withHeart(paths, (heart) => transaction(heart, () => {
89
+ const before = requestFact(heart, id);
90
+ if (before === null)
91
+ throw new Error(`unknown Akuma request ${id}`);
92
+ if (before.state === "admitted")
93
+ updateRequestRefused(heart, id, diagnostic);
94
+ else if (before.state !== "refused" || before.diagnostic !== diagnostic) {
95
+ throw new Error(`Akuma request ${id} cannot be refused`);
96
+ }
97
+ return requestFact(heart, id);
98
+ }));
99
+ }
100
+ export async function voidRequest(paths, id, evidence) {
101
+ return await withHeart(paths, (heart) => transaction(heart, () => {
102
+ const before = requestFact(heart, id);
103
+ if (before === null)
104
+ throw new Error(`unknown Akuma request ${id}`);
105
+ if (before.state === "admitted" || before.state === "reserved") {
106
+ updateRequestVoided(heart, id, evidence);
107
+ }
108
+ else if (before.state !== "voided" || before.evidence !== evidence) {
109
+ throw new Error(`Akuma request ${id} cannot be voided`);
110
+ }
111
+ return requestFact(heart, id);
112
+ }));
113
+ }
114
+ export async function readRequest(paths, id) {
115
+ return await withHeart(paths, (heart) => requestFact(heart, id));
116
+ }
117
+ export async function readNonterminalRequests(paths) {
118
+ return await withHeart(paths, nonterminalRequestFacts);
119
+ }
@@ -0,0 +1,16 @@
1
+ import type { DatabaseSync } from "node:sqlite";
2
+ import type { AkuId } from "../identity.js";
3
+ import type { RequestFact, RequestInput } from "./facts.js";
4
+ export declare function requestPayloadJson(input: RequestInput): string;
5
+ export declare function insertRequestFact(database: DatabaseSync, input: RequestInput & Readonly<{
6
+ requester: AkuId;
7
+ admittedAt: string;
8
+ refusal?: string;
9
+ }>): void;
10
+ export declare function requestFact(database: DatabaseSync, id: string): RequestFact | null;
11
+ export declare function nonterminalRequestFacts(database: DatabaseSync): readonly RequestFact[];
12
+ export declare function updateRequestReserved(database: DatabaseSync, id: string, child: AkuId): void;
13
+ export declare function updateRequestServed(database: DatabaseSync, id: string, child: AkuId): void;
14
+ export declare function updateUpstreamRequestServed(database: DatabaseSync, id: string, service: unknown): void;
15
+ export declare function updateRequestRefused(database: DatabaseSync, id: string, diagnostic: string): void;
16
+ export declare function updateRequestVoided(database: DatabaseSync, id: string, evidence: string): void;
@@ -0,0 +1,88 @@
1
+ import { isTaskMutationAction } from "../../task/mutation.js";
2
+ function json(value) {
3
+ return JSON.stringify(value);
4
+ }
5
+ function parsed(value) {
6
+ if (typeof value !== "string")
7
+ throw new Error("Akuma authority contains non-text JSON");
8
+ return JSON.parse(value);
9
+ }
10
+ export function requestPayloadJson(input) {
11
+ const { id: _id, action: _action, requester: _requester, admittedAt: _admittedAt, state: _state, child: _child, service: _service, diagnostic: _diagnostic, evidence: _evidence, refusal: _refusal, ...payload } = input;
12
+ return json(payload);
13
+ }
14
+ function decodeRequestRow(row) {
15
+ const knownAction = [
16
+ "akuma.call", "akuma.wait", "akuma.tell", "akuma.kill", "contract.deliver", "contract.review",
17
+ ].includes(row.action) || isTaskMutationAction(row.action);
18
+ if (!knownAction)
19
+ throw new Error(`Akuma authority contains an unknown request action: ${row.action}`);
20
+ const payload = parsed(row.payload_json);
21
+ const input = {
22
+ id: row.id,
23
+ requester: row.requester,
24
+ action: row.action,
25
+ ...payload,
26
+ admittedAt: row.admitted_at,
27
+ };
28
+ if (row.state === "reserved") {
29
+ if (input.action !== "akuma.call" || row.child === null) {
30
+ throw new Error("Akuma authority contains an invalid reserved request");
31
+ }
32
+ return { ...input, state: row.state, child: row.child };
33
+ }
34
+ if (row.state === "served") {
35
+ if (input.action === "akuma.call") {
36
+ if (row.child === null)
37
+ throw new Error("Akuma authority contains a served call without a child");
38
+ return { ...input, state: row.state, child: row.child };
39
+ }
40
+ if (row.service_json === null) {
41
+ throw new Error("Akuma authority contains a served request without a service reference");
42
+ }
43
+ const service = parsed(row.service_json);
44
+ return { ...input, state: row.state, service };
45
+ }
46
+ if (row.state === "refused")
47
+ return { ...input, state: row.state, diagnostic: row.diagnostic };
48
+ if (row.state === "voided")
49
+ return { ...input, state: row.state, evidence: row.evidence };
50
+ return { ...input, state: "admitted" };
51
+ }
52
+ const REQUEST_COLUMNS = `sequence, id, requester, action, payload_json, admitted_at,
53
+ state, child, service_json, diagnostic, evidence`;
54
+ export function insertRequestFact(database, input) {
55
+ const { id, action, requester, admittedAt, refusal } = input;
56
+ database.prepare(`INSERT OR IGNORE INTO requests(
57
+ id, requester, action, payload_json, admitted_at, state, diagnostic
58
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)`).run(id, requester, action, requestPayloadJson(input), admittedAt, refusal === undefined ? "admitted" : "refused", refusal ?? null);
59
+ }
60
+ export function requestFact(database, id) {
61
+ const row = database.prepare(`SELECT ${REQUEST_COLUMNS} FROM requests WHERE id = ?`).get(id);
62
+ return row === undefined ? null : decodeRequestRow(row);
63
+ }
64
+ export function nonterminalRequestFacts(database) {
65
+ const rows = database.prepare(`SELECT ${REQUEST_COLUMNS} FROM requests
66
+ WHERE state IN ('admitted', 'reserved') ORDER BY sequence`).all();
67
+ return rows.map(decodeRequestRow);
68
+ }
69
+ export function updateRequestReserved(database, id, child) {
70
+ database.prepare("UPDATE requests SET state = 'reserved', child = ? WHERE id = ? AND state = 'admitted'")
71
+ .run(child, id);
72
+ }
73
+ export function updateRequestServed(database, id, child) {
74
+ database.prepare(`UPDATE requests SET state = 'served', child = ?
75
+ WHERE id = ? AND state = 'reserved'`).run(child, id);
76
+ }
77
+ export function updateUpstreamRequestServed(database, id, service) {
78
+ database.prepare(`UPDATE requests SET state = 'served', service_json = ?
79
+ WHERE id = ? AND state = 'admitted' AND action != 'akuma.call'`).run(json(service), id);
80
+ }
81
+ export function updateRequestRefused(database, id, diagnostic) {
82
+ database.prepare(`UPDATE requests SET state = 'refused', diagnostic = ?
83
+ WHERE id = ? AND state = 'admitted'`).run(diagnostic, id);
84
+ }
85
+ export function updateRequestVoided(database, id, evidence) {
86
+ database.prepare(`UPDATE requests SET state = 'voided', child = NULL, service_json = NULL, evidence = ?
87
+ WHERE id = ? AND state IN ('admitted', 'reserved')`).run(evidence, id);
88
+ }
@@ -1,6 +1,5 @@
1
1
  import type { DatabaseSync } from "node:sqlite";
2
- import type { AkuId } from "../identity.js";
3
- import type { BodyEnd, BodyFact, CallFact, KillFact, PauseFact, RequestFact, RequestInput, ResumeCoordinate, SessionFact, StopFact, TurnFact, TurnStartFact, TurnEndFact } from "./facts.js";
2
+ import type { BodyEnd, BodyFact, CallFact, KillFact, PauseFact, ResumeCoordinate, SessionFact, StopFact, TurnFact, TurnStartFact, TurnEndFact } from "./facts.js";
4
3
  export type SealRow = Readonly<{
5
4
  evidence: string;
6
5
  at: string;
@@ -39,19 +38,6 @@ export type CallRow = Readonly<{
39
38
  body: string;
40
39
  at: string;
41
40
  }>;
42
- export type RequestRow = Readonly<{
43
- sequence: number;
44
- id: string;
45
- requester: string;
46
- action: string;
47
- payload_json: string;
48
- admitted_at: string;
49
- state: RequestFact["state"];
50
- child: string | null;
51
- service_json: string | null;
52
- diagnostic: string | null;
53
- evidence: string | null;
54
- }>;
55
41
  export type ActivityRow = Readonly<{
56
42
  sequence: number;
57
43
  turn_sequence: number;
@@ -77,7 +63,6 @@ export declare function decodeSessionRow(row: SessionRow): SessionFact;
77
63
  export declare function decodeBodyRow(row: BodyRow): BodyFact;
78
64
  export declare function decodeTurnRow(row: TurnRow): TurnFact;
79
65
  export declare function decodeCallRow(row: CallRow): CallFact;
80
- export declare function decodeRequestRow(row: RequestRow): RequestFact;
81
66
  export declare function encodeActivityEvent(event: unknown): string;
82
67
  export declare function decodeActivityRow(row: ActivityRow): ActivityFact;
83
68
  export declare function sealExists(database: DatabaseSync): boolean;
@@ -100,18 +85,6 @@ export declare function insertActivityFact(database: DatabaseSync, input: Readon
100
85
  event: unknown;
101
86
  at: string;
102
87
  }>): number;
103
- export declare function insertRequestFact(database: DatabaseSync, input: RequestInput & Readonly<{
104
- requester: AkuId;
105
- admittedAt: string;
106
- refusal?: string;
107
- }>): void;
108
- export declare function requestFact(database: DatabaseSync, id: string): RequestFact | null;
109
- export declare function nonterminalRequestFacts(database: DatabaseSync): readonly RequestFact[];
110
- export declare function updateRequestReserved(database: DatabaseSync, id: string, child: AkuId): void;
111
- export declare function updateRequestServed(database: DatabaseSync, id: string, child: AkuId): void;
112
- export declare function updateUpstreamRequestServed(database: DatabaseSync, id: string, service: unknown): void;
113
- export declare function updateRequestRefused(database: DatabaseSync, id: string, diagnostic: string): void;
114
- export declare function updateRequestVoided(database: DatabaseSync, id: string, evidence: string): void;
115
88
  export declare function insertStopControl(database: DatabaseSync, bodySequence: number, at: string): void;
116
89
  export declare function insertPauseControl(database: DatabaseSync, at: string): void;
117
90
  export declare function stopFact(database: DatabaseSync): StopFact | null;
@@ -7,66 +7,6 @@ function parsed(value) {
7
7
  throw new Error("Akuma authority contains non-text JSON");
8
8
  return JSON.parse(value);
9
9
  }
10
- function object(value) {
11
- return value !== null && typeof value === "object" && !Array.isArray(value)
12
- ? value
13
- : null;
14
- }
15
- function exactKeys(value, expected) {
16
- const actual = Object.keys(value).sort();
17
- return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
18
- }
19
- const KILL_EVIDENCE = [
20
- "already-killed",
21
- "already-stopped",
22
- "hung",
23
- "killed",
24
- "untidy",
25
- "unavailable",
26
- ];
27
- function decodeRequestService(value, input) {
28
- const service = object(value);
29
- if (service === null || service.action !== input.action) {
30
- throw new Error("Akuma authority contains a mismatched request service reference");
31
- }
32
- if (input.action === "akuma.wait") {
33
- if (!exactKeys(service, ["action"])) {
34
- throw new Error("Akuma authority contains an invalid wait service reference");
35
- }
36
- return { action: input.action };
37
- }
38
- if (input.action === "akuma.tell") {
39
- if (!exactKeys(service, ["action", "target", "tellId"])
40
- || service.target !== input.target
41
- || service.tellId !== input.id) {
42
- throw new Error("Akuma authority contains an invalid tell service reference");
43
- }
44
- return { action: input.action, target: input.target, tellId: input.id };
45
- }
46
- if (!exactKeys(service, ["action", "results"]) || !Array.isArray(service.results)) {
47
- throw new Error("Akuma authority contains an invalid kill service reference");
48
- }
49
- let targetIndex = -1;
50
- const results = service.results.map((value) => {
51
- const result = object(value);
52
- if (result === null
53
- || !exactKeys(result, ["evidence", "id"])
54
- || typeof result.id !== "string"
55
- || !KILL_EVIDENCE.includes(result.evidence)) {
56
- throw new Error("Akuma authority contains an invalid kill service reference");
57
- }
58
- const index = input.targets.indexOf(result.id);
59
- if (index <= targetIndex) {
60
- throw new Error("Akuma authority contains an invalid kill service reference");
61
- }
62
- targetIndex = index;
63
- return {
64
- id: result.id,
65
- evidence: result.evidence,
66
- };
67
- });
68
- return { action: input.action, results };
69
- }
70
10
  function resumeCoordinate(value) {
71
11
  const coordinate = decodeResumeCoordinate(value);
72
12
  if (coordinate === null)
@@ -126,46 +66,6 @@ export function decodeTurnRow(row) {
126
66
  export function decodeCallRow(row) {
127
67
  return { kind: "call", sequence: row.sequence, turnSequence: row.turn_sequence, body: row.body, at: row.at };
128
68
  }
129
- export function decodeRequestRow(row) {
130
- if (!["akuma.call", "akuma.wait", "akuma.tell", "akuma.kill"].includes(row.action)) {
131
- throw new Error(`Akuma authority contains an unknown request action: ${row.action}`);
132
- }
133
- const payload = parsed(row.payload_json);
134
- const input = {
135
- id: row.id,
136
- requester: row.requester,
137
- action: row.action,
138
- ...payload,
139
- admittedAt: row.admitted_at,
140
- };
141
- if (row.state === "reserved") {
142
- if (input.action !== "akuma.call" || row.child === null) {
143
- throw new Error("Akuma authority contains an invalid reserved request");
144
- }
145
- return { ...input, state: row.state, child: row.child };
146
- }
147
- if (row.state === "served") {
148
- if (input.action === "akuma.call") {
149
- if (row.child === null)
150
- throw new Error("Akuma authority contains a served call without a child");
151
- return { ...input, state: row.state, child: row.child };
152
- }
153
- if (row.service_json === null) {
154
- throw new Error("Akuma authority contains a served request without a service reference");
155
- }
156
- const service = decodeRequestService(parsed(row.service_json), input);
157
- return {
158
- ...input,
159
- state: row.state,
160
- service,
161
- };
162
- }
163
- if (row.state === "refused")
164
- return { ...input, state: row.state, diagnostic: row.diagnostic };
165
- if (row.state === "voided")
166
- return { ...input, state: row.state, evidence: row.evidence };
167
- return { ...input, state: "admitted" };
168
- }
169
69
  export function encodeActivityEvent(event) {
170
70
  return json(event);
171
71
  }
@@ -196,6 +96,9 @@ export function sealFact(database) {
196
96
  return row === undefined ? null : { evidence: row.evidence, at: row.at };
197
97
  }
198
98
  export function insertBodyFact(database, input) {
99
+ if (latestBodyFact(database)?.hung !== undefined) {
100
+ throw new Error("Akuma successor is permanently gated by hung custody");
101
+ }
199
102
  const result = database.prepare("INSERT INTO bodies(leash_taken_at) VALUES (?)").run(input.leashTakenAt);
200
103
  return Number(result.lastInsertRowid);
201
104
  }
@@ -210,43 +113,6 @@ export function insertActivityFact(database, input) {
210
113
  .run(sequence, input.turnSequence, encodeActivityEvent(input.event), input.at);
211
114
  return sequence;
212
115
  }
213
- const REQUEST_COLUMNS = `sequence, id, requester, action, payload_json, admitted_at,
214
- state, child, service_json, diagnostic, evidence`;
215
- export function insertRequestFact(database, input) {
216
- const { id, action, requester, admittedAt, refusal, ...payload } = input;
217
- database.prepare(`INSERT OR IGNORE INTO requests(
218
- id, requester, action, payload_json, admitted_at, state, diagnostic
219
- ) VALUES (?, ?, ?, ?, ?, ?, ?)`).run(id, requester, action, json(payload), admittedAt, refusal === undefined ? "admitted" : "refused", refusal ?? null);
220
- }
221
- export function requestFact(database, id) {
222
- const row = database.prepare(`SELECT ${REQUEST_COLUMNS} FROM requests WHERE id = ?`).get(id);
223
- return row === undefined ? null : decodeRequestRow(row);
224
- }
225
- export function nonterminalRequestFacts(database) {
226
- const rows = database.prepare(`SELECT ${REQUEST_COLUMNS} FROM requests
227
- WHERE state IN ('admitted', 'reserved') ORDER BY sequence`).all();
228
- return rows.map(decodeRequestRow);
229
- }
230
- export function updateRequestReserved(database, id, child) {
231
- database.prepare("UPDATE requests SET state = 'reserved', child = ? WHERE id = ? AND state = 'admitted'")
232
- .run(child, id);
233
- }
234
- export function updateRequestServed(database, id, child) {
235
- database.prepare(`UPDATE requests SET state = 'served', child = ?
236
- WHERE id = ? AND state = 'reserved'`).run(child, id);
237
- }
238
- export function updateUpstreamRequestServed(database, id, service) {
239
- database.prepare(`UPDATE requests SET state = 'served', service_json = ?
240
- WHERE id = ? AND state = 'admitted' AND action != 'akuma.call'`).run(json(service), id);
241
- }
242
- export function updateRequestRefused(database, id, diagnostic) {
243
- database.prepare(`UPDATE requests SET state = 'refused', diagnostic = ?
244
- WHERE id = ? AND state = 'admitted'`).run(diagnostic, id);
245
- }
246
- export function updateRequestVoided(database, id, evidence) {
247
- database.prepare(`UPDATE requests SET state = 'voided', child = NULL, service_json = NULL, evidence = ?
248
- WHERE id = ? AND state IN ('admitted', 'reserved')`).run(evidence, id);
249
- }
250
116
  export function insertStopControl(database, bodySequence, at) {
251
117
  database.prepare("INSERT OR IGNORE INTO control(kind, value_json, at) VALUES ('stop', ?, ?)")
252
118
  .run(json({ bodySequence }), at);
@@ -288,7 +154,7 @@ export function endBodyFact(database, input) {
288
154
  }
289
155
  export function markBodyHung(database, input) {
290
156
  const result = database.prepare(`UPDATE bodies SET hung_diagnostic = ?, hung_at = ?
291
- WHERE sequence = ? AND end IS NULL AND hung_diagnostic IS NULL`)
157
+ WHERE sequence = ? AND hung_diagnostic IS NULL`)
292
158
  .run(input.diagnostic, input.at, input.sequence);
293
159
  if (result.changes !== 1)
294
160
  throw new Error(`Akuma Body ${input.sequence} cannot record hung custody`);