@astrosheep/keiyaku 4.0.0 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/README.md +133 -8
  2. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +2 -2
  3. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +4 -4
  4. package/build/integrations/marketplace/plugins/keiyaku/opencode.js +2 -2
  5. package/build/integrations/marketplace/plugins/keiyaku/package.json +2 -2
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +13 -5
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +91 -32
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +134 -0
  9. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/agents/openai.yaml +4 -0
  10. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +41 -6
  11. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +262 -46
  12. package/build/src/akuma/abort.d.ts +2 -0
  13. package/build/src/akuma/abort.js +50 -0
  14. package/build/src/akuma/akuma.d.ts +50 -33
  15. package/build/src/akuma/akuma.js +175 -127
  16. package/build/src/akuma/archetype.d.ts +14 -5
  17. package/build/src/akuma/archetype.js +85 -39
  18. package/build/src/akuma/body.d.ts +3 -5
  19. package/build/src/akuma/body.js +490 -183
  20. package/build/src/akuma/coordinate.d.ts +9 -0
  21. package/build/src/akuma/coordinate.js +22 -0
  22. package/build/src/akuma/heart/facts.d.ts +88 -45
  23. package/build/src/akuma/heart/facts.js +19 -8
  24. package/build/src/akuma/heart/index.d.ts +64 -55
  25. package/build/src/akuma/heart/index.js +172 -252
  26. package/build/src/akuma/heart/rows.d.ts +38 -83
  27. package/build/src/akuma/heart/rows.js +117 -155
  28. package/build/src/akuma/heart/schema.d.ts +1 -1
  29. package/build/src/akuma/heart/schema.js +61 -22
  30. package/build/src/akuma/heart/soul.d.ts +8 -0
  31. package/build/src/akuma/heart/soul.js +136 -0
  32. package/build/src/akuma/heart/storage.d.ts +47 -0
  33. package/build/src/akuma/heart/storage.js +224 -0
  34. package/build/src/akuma/heart/tells.d.ts +12 -0
  35. package/build/src/akuma/heart/tells.js +79 -0
  36. package/build/src/akuma/heart/timeline.d.ts +15 -0
  37. package/build/src/akuma/heart/timeline.js +86 -0
  38. package/build/src/akuma/identity.d.ts +2 -2
  39. package/build/src/akuma/identity.js +9 -9
  40. package/build/src/akuma/index.d.ts +10 -3
  41. package/build/src/akuma/index.js +7 -1
  42. package/build/src/akuma/projection.d.ts +176 -0
  43. package/build/src/akuma/projection.js +256 -0
  44. package/build/src/akuma/provider-recipe.d.ts +24 -0
  45. package/build/src/akuma/provider-recipe.js +100 -0
  46. package/build/src/akuma/provider.d.ts +67 -13
  47. package/build/src/akuma/provider.js +172 -105
  48. package/build/src/akuma/providers/acp/core.d.ts +19 -0
  49. package/build/src/akuma/providers/acp/core.js +159 -0
  50. package/build/src/akuma/providers/acp/events.d.ts +20 -0
  51. package/build/src/akuma/providers/acp/events.js +73 -0
  52. package/build/src/akuma/providers/acp/index.d.ts +12 -0
  53. package/build/src/akuma/providers/acp/index.js +94 -0
  54. package/build/src/akuma/providers/{claude.d.ts → claude/events.d.ts} +9 -41
  55. package/build/src/akuma/providers/claude/events.js +238 -0
  56. package/build/src/akuma/providers/claude/index.d.ts +47 -0
  57. package/build/src/akuma/providers/claude/index.js +279 -0
  58. package/build/src/akuma/providers/claude/input.d.ts +13 -0
  59. package/build/src/akuma/providers/claude/input.js +109 -0
  60. package/build/src/akuma/providers/codex-app-server/events.d.ts +1 -1
  61. package/build/src/akuma/providers/codex-app-server/events.js +15 -12
  62. package/build/src/akuma/providers/codex-app-server/index.d.ts +15 -13
  63. package/build/src/akuma/providers/codex-app-server/index.js +77 -33
  64. package/build/src/akuma/providers/grok-build/index.d.ts +4 -0
  65. package/build/src/akuma/providers/grok-build/index.js +70 -0
  66. package/build/src/akuma/providers/index.d.ts +5 -3
  67. package/build/src/akuma/providers/index.js +35 -56
  68. package/build/src/akuma/providers/opencode-sdk/events.d.ts +54 -0
  69. package/build/src/akuma/providers/opencode-sdk/events.js +235 -0
  70. package/build/src/akuma/providers/opencode-sdk/index.d.ts +33 -0
  71. package/build/src/akuma/providers/opencode-sdk/index.js +308 -0
  72. package/build/src/akuma/providers/opencode-sdk/session.d.ts +26 -0
  73. package/build/src/akuma/providers/opencode-sdk/session.js +63 -0
  74. package/build/src/akuma/providers/pi/events.d.ts +33 -0
  75. package/build/src/akuma/providers/pi/events.js +166 -0
  76. package/build/src/akuma/providers/pi/index.d.ts +11 -0
  77. package/build/src/akuma/providers/pi/index.js +191 -0
  78. package/build/src/akuma/publication.d.ts +3 -3
  79. package/build/src/akuma/publication.js +28 -40
  80. package/build/src/akuma/requests.d.ts +9 -6
  81. package/build/src/akuma/requests.js +95 -65
  82. package/build/src/alias/index.d.ts +4 -3
  83. package/build/src/alias/index.js +12 -13
  84. package/build/src/body/decode.js +7 -1
  85. package/build/src/body/region.d.ts +2 -0
  86. package/build/src/body/region.js +77 -7
  87. package/build/src/body/render.js +2 -1
  88. package/build/src/body/shape.js +7 -1
  89. package/build/src/body/verification.js +19 -4
  90. package/build/src/cli/accepted.d.ts +18 -12
  91. package/build/src/cli/accepted.js +73 -34
  92. package/build/src/cli/actor.js +3 -3
  93. package/build/src/cli/commands/akuma-invoke.d.ts +44 -19
  94. package/build/src/cli/commands/akuma-invoke.js +101 -52
  95. package/build/src/cli/commands/akuma.d.ts +27 -9
  96. package/build/src/cli/commands/akuma.js +151 -80
  97. package/build/src/cli/commands/contract.d.ts +155 -0
  98. package/build/src/cli/commands/contract.js +20 -0
  99. package/build/src/cli/commands/install.js +2 -0
  100. package/build/src/cli/commands/task-invoke.d.ts +19 -4
  101. package/build/src/cli/commands/task-invoke.js +65 -17
  102. package/build/src/cli/commands/task-query.d.ts +5 -0
  103. package/build/src/cli/commands/task-query.js +247 -0
  104. package/build/src/cli/commands/task.d.ts +3 -1
  105. package/build/src/cli/commands/task.js +52 -14
  106. package/build/src/cli/coordinates.d.ts +18 -0
  107. package/build/src/cli/coordinates.js +120 -0
  108. package/build/src/cli/draft.d.ts +8 -0
  109. package/build/src/cli/draft.js +96 -0
  110. package/build/src/cli/invoke.d.ts +9 -3
  111. package/build/src/cli/invoke.js +287 -108
  112. package/build/src/cli/main.js +54 -7
  113. package/build/src/cli/parse.d.ts +21 -117
  114. package/build/src/cli/parse.js +79 -84
  115. package/build/src/cli/render/akuma-tool-command.d.ts +6 -0
  116. package/build/src/cli/render/akuma-tool-command.js +144 -0
  117. package/build/src/cli/render/akuma-tool.d.ts +4 -2
  118. package/build/src/cli/render/akuma-tool.js +35 -5
  119. package/build/src/cli/render/akuma.d.ts +3 -1
  120. package/build/src/cli/render/akuma.js +247 -212
  121. package/build/src/cli/render/audit.d.ts +3 -0
  122. package/build/src/cli/render/audit.js +104 -0
  123. package/build/src/cli/render/catalog.d.ts +2 -0
  124. package/build/src/cli/render/catalog.js +16 -0
  125. package/build/src/cli/render/contract.d.ts +3 -3
  126. package/build/src/cli/render/contract.js +200 -54
  127. package/build/src/cli/render/kanshi.js +272 -129
  128. package/build/src/cli/render/receipt.d.ts +19 -0
  129. package/build/src/cli/render/receipt.js +106 -0
  130. package/build/src/cli/render/refusal.d.ts +16 -2
  131. package/build/src/cli/render/refusal.js +88 -3
  132. package/build/src/cli/render/region.d.ts +2 -0
  133. package/build/src/cli/render/region.js +17 -0
  134. package/build/src/cli/render/task.d.ts +2 -1
  135. package/build/src/cli/render/task.js +223 -60
  136. package/build/src/cli/render/terminal.d.ts +20 -0
  137. package/build/src/cli/render/terminal.js +121 -0
  138. package/build/src/cli/render/text.js +11 -3
  139. package/build/src/cli/result.d.ts +126 -19
  140. package/build/src/cli/usage.d.ts +1 -0
  141. package/build/src/cli/usage.js +3 -0
  142. package/build/src/contract-worktree.d.ts +44 -0
  143. package/build/src/contract-worktree.js +251 -0
  144. package/build/src/coordination/durable-file.d.ts +5 -1
  145. package/build/src/coordination/durable-file.js +57 -10
  146. package/build/src/coordination/sqlite-transaction-lock.d.ts +4 -0
  147. package/build/src/coordination/sqlite-transaction-lock.js +22 -5
  148. package/build/src/core/facts/codec.js +23 -4
  149. package/build/src/core/facts/eligibility.d.ts +1 -10
  150. package/build/src/core/facts/eligibility.js +0 -49
  151. package/build/src/core/facts/fold.js +1 -6
  152. package/build/src/core/facts/gate.d.ts +1 -0
  153. package/build/src/core/facts/gate.js +1 -0
  154. package/build/src/core/facts/observation.d.ts +3 -0
  155. package/build/src/core/facts/observation.js +12 -0
  156. package/build/src/core/facts/types.d.ts +10 -3
  157. package/build/src/core/subject.js +2 -2
  158. package/build/src/core/verbs/amend.d.ts +1 -1
  159. package/build/src/core/verbs/amend.js +3 -18
  160. package/build/src/core/verbs/bind.js +2 -16
  161. package/build/src/core/verbs/deliver.d.ts +1 -1
  162. package/build/src/core/verbs/deliver.js +14 -4
  163. package/build/src/core/verbs/placement.d.ts +1 -1
  164. package/build/src/core/verbs/placement.js +7 -9
  165. package/build/src/dispatch/index.d.ts +5 -3
  166. package/build/src/dispatch/index.js +30 -21
  167. package/build/src/duration.d.ts +10 -0
  168. package/build/src/duration.js +22 -0
  169. package/build/src/git/admission.d.ts +2 -2
  170. package/build/src/git/admission.js +33 -23
  171. package/build/src/git/hooks.d.ts +15 -0
  172. package/build/src/git/hooks.js +70 -18
  173. package/build/src/git/identity.d.ts +5 -1
  174. package/build/src/git/identity.js +23 -4
  175. package/build/src/git/integration.d.ts +48 -0
  176. package/build/src/git/integration.js +250 -0
  177. package/build/src/git/observe.d.ts +29 -15
  178. package/build/src/git/observe.js +185 -76
  179. package/build/src/git/read-observation.d.ts +41 -0
  180. package/build/src/git/read-observation.js +361 -0
  181. package/build/src/git/reconcile.d.ts +17 -5
  182. package/build/src/git/reconcile.js +305 -109
  183. package/build/src/git/repository.d.ts +41 -18
  184. package/build/src/git/repository.js +220 -114
  185. package/build/src/git/scratch.d.ts +17 -0
  186. package/build/src/git/scratch.js +73 -0
  187. package/build/src/git/target-placement.d.ts +92 -0
  188. package/build/src/git/target-placement.js +375 -0
  189. package/build/src/git/tender.d.ts +49 -0
  190. package/build/src/git/tender.js +63 -0
  191. package/build/src/git/terminal-seal.d.ts +24 -0
  192. package/build/src/git/terminal-seal.js +89 -0
  193. package/build/src/git/tree.d.ts +2 -2
  194. package/build/src/git/tree.js +4 -0
  195. package/build/src/git/workspace.d.ts +53 -0
  196. package/build/src/git/workspace.js +139 -0
  197. package/build/src/identity/selector.d.ts +6 -0
  198. package/build/src/identity/selector.js +19 -0
  199. package/build/src/index.d.ts +4 -2
  200. package/build/src/index.js +2 -1
  201. package/build/src/kanshi/index.d.ts +2 -2
  202. package/build/src/kanshi/index.js +1 -1
  203. package/build/src/kanshi/read.d.ts +7 -3
  204. package/build/src/kanshi/read.js +245 -45
  205. package/build/src/kanshi/report.d.ts +81 -6
  206. package/build/src/kanshi/select.d.ts +5 -0
  207. package/build/src/kanshi/select.js +33 -1
  208. package/build/src/library/address.d.ts +57 -0
  209. package/build/src/library/address.js +165 -0
  210. package/build/src/library/akuma-creation.d.ts +16 -7
  211. package/build/src/library/akuma-creation.js +96 -22
  212. package/build/src/library/audit.d.ts +18 -0
  213. package/build/src/library/audit.js +44 -0
  214. package/build/src/library/bind.d.ts +21 -0
  215. package/build/src/library/bind.js +56 -0
  216. package/build/src/library/catalog.d.ts +58 -0
  217. package/build/src/library/catalog.js +69 -0
  218. package/build/src/library/configuration.d.ts +6 -15
  219. package/build/src/library/configuration.js +14 -17
  220. package/build/src/library/contract.d.ts +23 -49
  221. package/build/src/library/contract.js +171 -185
  222. package/build/src/library/delivery.d.ts +24 -0
  223. package/build/src/library/delivery.js +21 -0
  224. package/build/src/library/fleet.d.ts +65 -0
  225. package/build/src/library/fleet.js +153 -0
  226. package/build/src/library/input.d.ts +3 -0
  227. package/build/src/library/input.js +19 -0
  228. package/build/src/library/keiyaku.d.ts +18 -3
  229. package/build/src/library/keiyaku.js +12 -1
  230. package/build/src/library/mutation.d.ts +39 -0
  231. package/build/src/library/mutation.js +37 -0
  232. package/build/src/library/reconcile.d.ts +29 -0
  233. package/build/src/library/reconcile.js +179 -0
  234. package/build/src/library/refusal.d.ts +21 -0
  235. package/build/src/library/refusal.js +29 -0
  236. package/build/src/library/region.d.ts +14 -1
  237. package/build/src/library/region.js +18 -4
  238. package/build/src/library/repo.d.ts +4 -13
  239. package/build/src/library/repo.js +14 -24
  240. package/build/src/protocol/attempt.d.ts +16 -3
  241. package/build/src/protocol/attempt.js +41 -10
  242. package/build/src/protocol/bind.d.ts +9 -2
  243. package/build/src/protocol/bind.js +48 -40
  244. package/build/src/protocol/intent.d.ts +57 -20
  245. package/build/src/protocol/intent.js +55 -79
  246. package/build/src/protocol/operations.d.ts +152 -43
  247. package/build/src/protocol/operations.js +482 -166
  248. package/build/src/protocol/outcome.d.ts +14 -3
  249. package/build/src/protocol/outcome.js +11 -3
  250. package/build/src/protocol/placement.d.ts +41 -0
  251. package/build/src/protocol/placement.js +147 -0
  252. package/build/src/protocol/read/documents.d.ts +2 -2
  253. package/build/src/protocol/read/documents.js +4 -4
  254. package/build/src/protocol/read/status.d.ts +18 -5
  255. package/build/src/protocol/read/status.js +73 -13
  256. package/build/src/protocol/run.d.ts +49 -7
  257. package/build/src/protocol/run.js +53 -25
  258. package/build/src/runtime/proc/line-rpc.d.ts +4 -8
  259. package/build/src/runtime/proc/line-rpc.js +22 -28
  260. package/build/src/runtime/proc/run.d.ts +23 -21
  261. package/build/src/runtime/proc/run.js +123 -89
  262. package/build/src/runtime/proc/stdio.d.ts +18 -0
  263. package/build/src/runtime/proc/stdio.js +58 -0
  264. package/build/src/settings.d.ts +7 -1
  265. package/build/src/settings.js +32 -17
  266. package/build/src/settlement/fence.d.ts +5 -0
  267. package/build/src/settlement/fence.js +14 -0
  268. package/build/src/settlement/holder.d.ts +35 -4
  269. package/build/src/settlement/holder.js +127 -28
  270. package/build/src/settlement/settle.d.ts +9 -1
  271. package/build/src/settlement/settle.js +74 -37
  272. package/build/src/task/board.d.ts +23 -5
  273. package/build/src/task/board.js +17 -10
  274. package/build/src/task/compose.d.ts +2 -2
  275. package/build/src/task/compose.js +18 -10
  276. package/build/src/task/context.d.ts +3 -3
  277. package/build/src/task/context.js +15 -20
  278. package/build/src/task/document.d.ts +2 -1
  279. package/build/src/task/document.js +15 -5
  280. package/build/src/task/index.d.ts +27 -15
  281. package/build/src/task/index.js +57 -35
  282. package/build/src/task/operations.d.ts +38 -13
  283. package/build/src/task/operations.js +80 -46
  284. package/build/src/task/query.d.ts +67 -0
  285. package/build/src/task/query.js +279 -0
  286. package/build/src/task/store.d.ts +5 -7
  287. package/build/src/task/store.js +43 -28
  288. package/build/src/verification/declaration.d.ts +1 -0
  289. package/build/src/verification/execution.d.ts +51 -0
  290. package/build/src/verification/execution.js +108 -0
  291. package/build/src/workspace-place.d.ts +41 -0
  292. package/build/src/workspace-place.js +386 -0
  293. package/build/src/world.d.ts +24 -0
  294. package/build/src/world.js +136 -0
  295. package/package.json +8 -4
  296. package/build/src/akuma/activity.d.ts +0 -66
  297. package/build/src/akuma/activity.js +0 -139
  298. package/build/src/akuma/providers/claude.js +0 -305
  299. package/build/src/context-root.d.ts +0 -4
  300. package/build/src/context-root.js +0 -15
  301. package/build/src/git/delivery.d.ts +0 -20
  302. package/build/src/git/delivery.js +0 -109
  303. package/build/src/git/verification.d.ts +0 -14
  304. package/build/src/git/verification.js +0 -56
  305. package/build/src/protocol/read/audit.d.ts +0 -25
  306. package/build/src/protocol/read/audit.js +0 -39
  307. package/build/src/verification/producer.d.ts +0 -23
  308. package/build/src/verification/producer.js +0 -54
@@ -1,18 +1,151 @@
1
1
  import { fileURLToPath } from "node:url";
2
- import { HeldAkumaLeash, advanceTell, appendActivity, breakBody, clearAbandonedControl, finishBodyIfIdle, heartExists, pauseRequested, readHeart, recordBody, recordSession, recordTurn, stopRequested, } from "./heart/index.js";
3
- import { encodeAgentEvent } from "./provider.js";
4
- import { providerNamed } from "./providers/index.js";
5
- import { BodyRequestPump, settleBodyRequests, } from "./requests.js";
6
- import { currentProcessCollar, probeProcessTree, putDownProcessTree, spawnDetachedProcess, } from "../runtime/proc/run.js";
2
+ import { abortableDelay } from "./abort.js";
3
+ import { HeldAkumaLeash, appendActivity, beginTurn, breakBody, endTurn, finishBodyIfIdle, heartExists, isHeartAbsent, readHeart, recordSession, recordTellDeliveries, recordTellReceipt, } from "./heart/index.js";
4
+ import { encodeAgentEvent, } from "./provider.js";
5
+ import { resolveProviderExecution } from "./providers/index.js";
6
+ import { BodyRequestPump, clearBodyRequestTransport, settleBodyRequests, } from "./requests.js";
7
+ import { spawnDetachedProcess, } from "../runtime/proc/run.js";
7
8
  const LEASH_RETRY_MS = 25;
8
- function wait(milliseconds) {
9
- return new Promise((resolve) => setTimeout(resolve, milliseconds));
9
+ const BODY_CONTROL_OBSERVATION_MS = 100;
10
+ export const CONTROL_RESPONSE_MS = 1_000;
11
+ class BodySupervisor {
12
+ paths;
13
+ bodySequence;
14
+ leash;
15
+ signal;
16
+ controller = new AbortController();
17
+ finished = new AbortController();
18
+ observer;
19
+ waiter;
20
+ observation;
21
+ stopping;
22
+ constructor(paths, bodySequence, leash, observation) {
23
+ this.paths = paths;
24
+ this.bodySequence = bodySequence;
25
+ this.leash = leash;
26
+ this.signal = this.controller.signal;
27
+ this.observation = observation;
28
+ this.observer = this.observe();
29
+ }
30
+ static async open(paths, bodySequence, leash) {
31
+ return new BodySupervisor(paths, bodySequence, leash, await readHeart(paths));
32
+ }
33
+ get reason() { return this.stopping; }
34
+ current() { return this.observation; }
35
+ async recordHung(diagnostic, at) {
36
+ await this.leash.recordBodyHung(this.paths, { sequence: this.bodySequence, diagnostic, at });
37
+ }
38
+ async refresh() {
39
+ this.publish(await readHeart(this.paths));
40
+ return this.observation;
41
+ }
42
+ next(after) {
43
+ if (this.observation !== after)
44
+ return Promise.resolve(this.observation);
45
+ return new Promise((resolve) => { this.waiter = resolve; });
46
+ }
47
+ cancel(reason) {
48
+ if (this.stopping !== undefined)
49
+ return;
50
+ this.stopping = reason;
51
+ this.controller.abort(new Error(reason === "control"
52
+ ? "Akuma Body interrupted by durable control"
53
+ : "Akuma Heart disappeared"));
54
+ this.waiter?.(this.observation);
55
+ this.waiter = undefined;
56
+ }
57
+ async close() {
58
+ this.finished.abort();
59
+ await this.observer;
60
+ this.waiter = undefined;
61
+ }
62
+ publish(snapshot) {
63
+ this.observation = snapshot;
64
+ this.waiter?.(snapshot);
65
+ this.waiter = undefined;
66
+ }
67
+ async observe() {
68
+ for (;;) {
69
+ const snapshot = this.observation;
70
+ if (!await heartExists(this.paths)) {
71
+ this.cancel("heart-gone");
72
+ return;
73
+ }
74
+ if (bodyControlRequested(snapshot, this.bodySequence)) {
75
+ this.cancel("control");
76
+ return;
77
+ }
78
+ try {
79
+ await abortableDelay(BODY_CONTROL_OBSERVATION_MS, this.finished.signal);
80
+ }
81
+ catch {
82
+ return;
83
+ }
84
+ try {
85
+ await this.refresh();
86
+ }
87
+ catch (error) {
88
+ if (await heartExists(this.paths))
89
+ throw error;
90
+ this.cancel("heart-gone");
91
+ return;
92
+ }
93
+ }
94
+ }
95
+ }
96
+ function serializeEffects() {
97
+ let tail = Promise.resolve();
98
+ let failed = false;
99
+ let failure;
100
+ const serialize = async (effect) => {
101
+ const result = tail.then(effect);
102
+ tail = result.then(() => undefined, (error) => {
103
+ if (!failed)
104
+ failure = error;
105
+ failed = true;
106
+ });
107
+ return await result;
108
+ };
109
+ serialize.drain = async () => {
110
+ await tail;
111
+ if (failed)
112
+ throw failure;
113
+ };
114
+ return serialize;
10
115
  }
11
116
  function missing(error) {
12
- return error.code === "ENOENT";
117
+ return isHeartAbsent(error) || error.code === "ENOENT";
13
118
  }
14
- function turnRecipe(paths, soul) {
15
- const latest = readHeart(paths).latestSession;
119
+ async function writeProviderEvent(input, active, event) {
120
+ if (input.supervisor.signal.aborted)
121
+ return;
122
+ const at = input.now();
123
+ if (event.type === "session") {
124
+ await recordSession(input.paths, {
125
+ provider: input.soul.provider.name,
126
+ coordinate: event.coordinate,
127
+ cwd: active.cwd,
128
+ options: active.options,
129
+ admittedAt: at,
130
+ });
131
+ }
132
+ await appendActivity(input.paths, {
133
+ turnSequence: active.turnSequence,
134
+ event: encodeAgentEvent(event),
135
+ at,
136
+ });
137
+ }
138
+ function bodyControlRequested(snapshot, bodySequence) {
139
+ return snapshot.stop?.bodySequence === bodySequence
140
+ || snapshot.pause?.bodySequence === bodySequence;
141
+ }
142
+ async function putDownIfControlled(paths, supervisor, bodySequence, now) {
143
+ if (supervisor.reason === "control" && await heartExists(paths)) {
144
+ await breakBody(paths, { sequence: bodySequence, end: "put-down", at: now() });
145
+ }
146
+ }
147
+ async function turnRecipe(paths, soul) {
148
+ const latest = (await readHeart(paths)).latestSession;
16
149
  const admitted = latest?.provider === soul.provider.name ? latest : undefined;
17
150
  const session = admitted?.coordinate;
18
151
  return {
@@ -24,7 +157,7 @@ function turnRecipe(paths, soul) {
24
157
  async function takeLeash(paths) {
25
158
  for (;;) {
26
159
  try {
27
- const leash = HeldAkumaLeash.try(paths);
160
+ const leash = await HeldAkumaLeash.try(paths);
28
161
  if (leash !== null)
29
162
  return leash;
30
163
  }
@@ -33,168 +166,305 @@ async function takeLeash(paths) {
33
166
  return null;
34
167
  throw error;
35
168
  }
36
- await wait(LEASH_RETRY_MS);
169
+ await abortableDelay(LEASH_RETRY_MS);
37
170
  }
38
171
  }
39
- async function settlePredecessor(paths) {
40
- const predecessor = readHeart(paths).latestBody;
41
- if (predecessor === null)
42
- return true;
43
- const probe = probeProcessTree(predecessor.collar);
44
- if (probe.kind === "gone")
45
- return true;
46
- if (probe.kind === "unverifiable")
47
- return false;
48
- const evidence = await putDownProcessTree(predecessor.collar);
49
- return evidence !== "unavailable" && evidence !== "alive-after-sigkill";
50
- }
51
172
  async function startTurnDrive(input) {
52
- const { cwd, options, session } = turnRecipe(input.paths, input.soul);
173
+ const { cwd, options, session } = await turnRecipe(input.paths, input.soul);
174
+ if (session !== undefined && input.adapter.resume === undefined)
175
+ return { kind: "resume-unsupported" };
176
+ const turn = await beginTurn(input.paths, {
177
+ bodySequence: input.bodySequence,
178
+ startedAt: input.now(),
179
+ ...(input.call === undefined ? {} : { call: input.call }),
180
+ });
53
181
  const requests = input.soul.confinement.kind === "declared"
54
- ? new BodyRequestPump({
182
+ ? await BodyRequestPump.open({
55
183
  paths: input.paths,
56
184
  parent: input.soul,
57
185
  bodySequence: input.bodySequence,
58
186
  now: input.now,
59
187
  spawn: input.runtimeSpawn,
188
+ signal: input.supervisor.signal,
60
189
  })
61
190
  : null;
191
+ const driveInput = {
192
+ body: input.body,
193
+ launchTells: input.launchTells.map((tell) => ({ id: tell.id, text: tell.body })),
194
+ cwd,
195
+ options,
196
+ signal: input.supervisor.signal,
197
+ ...(requests === null ? {} : { requests: { dir: requests.directory } }),
198
+ };
199
+ const setup = session === undefined
200
+ ? input.adapter.start({ ...driveInput, session: { kind: "fresh" } })
201
+ : input.adapter.resume({ ...driveInput, session: { kind: "resume", coordinate: session } });
62
202
  try {
63
- const drive = await input.adapter.start({
64
- prompt: input.prompt,
65
- cwd,
66
- options,
67
- ...(session === undefined ? {} : { session }),
68
- ...(requests === null ? {} : { requests: { dir: requests.directory } }),
69
- });
203
+ const selected = await setup;
204
+ if (input.supervisor.signal.aborted) {
205
+ await retireProviderCustody(input, turn.sequence, selected);
206
+ await requests?.close();
207
+ return { kind: "stopped" };
208
+ }
209
+ if (input.launchTells.length > 0) {
210
+ await recordTellDeliveries(input.paths, input.launchTells.map((tell) => ({
211
+ tellId: tell.id,
212
+ route: "launch",
213
+ turnSequence: turn.sequence,
214
+ fence: selected.admission.fence,
215
+ deliveredAt: input.now(),
216
+ })));
217
+ }
70
218
  if (requests !== null)
71
- void drive.completion.then(() => requests.stopAdmission());
72
- return { drive, requests, cwd, options, ...(session === undefined ? {} : { session }) };
219
+ void selected.completion.then(() => requests.stopAdmission());
220
+ return { turnSequence: turn.sequence, drive: selected, requests, cwd, options,
221
+ ...(session === undefined ? {} : { resume: session }) };
73
222
  }
74
223
  catch (error) {
75
- if (requests !== null)
76
- await requests.close();
77
- throw error;
224
+ await requests?.close();
225
+ if (input.supervisor.signal.aborted) {
226
+ if (input.supervisor.reason === "heart-gone")
227
+ throw error;
228
+ return { kind: "stopped" };
229
+ }
230
+ return {
231
+ kind: "failed",
232
+ turnSequence: turn.sequence,
233
+ diagnostic: error instanceof Error ? error.message : String(error),
234
+ };
78
235
  }
79
236
  }
237
+ async function retireProviderCustody(input, turnSequence, drive) {
238
+ const outcome = await Promise.race([
239
+ drive.abort().then(() => ({ kind: "retired" }), (error) => ({ kind: "held", error })),
240
+ abortableDelay(CONTROL_RESPONSE_MS).then(() => ({
241
+ kind: "held",
242
+ error: new Error(`provider custody remained live after ${CONTROL_RESPONSE_MS}ms`),
243
+ })),
244
+ ]);
245
+ if (outcome.kind === "retired")
246
+ return;
247
+ if (await heartExists(input.paths)) {
248
+ const diagnostic = outcome.error instanceof Error ? outcome.error.message : String(outcome.error);
249
+ const at = input.now();
250
+ try {
251
+ await input.supervisor.recordHung(diagnostic, at);
252
+ await appendActivity(input.paths, {
253
+ turnSequence,
254
+ event: encodeAgentEvent({ type: "note", text: `Provider custody was not retired: ${diagnostic}` }),
255
+ at,
256
+ });
257
+ }
258
+ catch { /* undisposed provider custody still owns the leash */ }
259
+ }
260
+ for (;;)
261
+ await abortableDelay(60_000);
262
+ }
263
+ function pumpReceipts(writers) {
264
+ const { input, turnSequence, drive, writeWitness, mayWrite } = writers;
265
+ return (async () => {
266
+ if (drive.receipts === undefined)
267
+ return;
268
+ for await (const receipt of drive.receipts) {
269
+ if (!mayWrite())
270
+ return;
271
+ await writeWitness(async () => {
272
+ if (!mayWrite())
273
+ return;
274
+ await recordTellReceipt(input.paths, receipt.evidence === "exact"
275
+ ? { ...receipt, receivedAt: input.now() }
276
+ : { ...receipt, turnSequence, receivedAt: input.now() });
277
+ });
278
+ }
279
+ })();
280
+ }
281
+ function observeReceiptFailure(receiptPump) {
282
+ return receiptPump.then(() => new Promise(() => { }), (error) => Promise.reject(error));
283
+ }
284
+ async function submitPendingLiveTells(writers, pending, attempted, tellLive) {
285
+ const { input, turnSequence, drive, writeWitness, mayWrite } = writers;
286
+ for (const tell of pending) {
287
+ if (attempted.has(tell.id))
288
+ continue;
289
+ attempted.add(tell.id);
290
+ const outcome = await writeWitness(async () => {
291
+ const submission = await tellLive({ id: tell.id, text: tell.body });
292
+ if (!mayWrite())
293
+ return "turn-ended";
294
+ if (submission.kind === "turn-ended")
295
+ return "turn-ended";
296
+ await recordTellDeliveries(input.paths, [{
297
+ tellId: tell.id,
298
+ route: "live",
299
+ turnSequence,
300
+ fence: submission.fence,
301
+ receipt: drive.receipts === undefined ? "unavailable" : "required",
302
+ deliveredAt: input.now(),
303
+ }]);
304
+ return "accepted";
305
+ });
306
+ if (outcome === "turn-ended")
307
+ return "turn-ended";
308
+ }
309
+ return "live";
310
+ }
311
+ async function stopActiveDrive(input, active) {
312
+ await retireProviderCustody(input, active.turnSequence, active.drive);
313
+ await active.requests?.close();
314
+ }
315
+ function hasUnattemptedTell(liveTells, tellPump, pending, attempted) {
316
+ return liveTells && tellPump === null && pending.some((tell) => !attempted.has(tell.id));
317
+ }
318
+ function persistProviderEvent(_input, _active, event) {
319
+ return event.type === "session" ? event.coordinate : undefined;
320
+ }
321
+ async function settleCompletion(result, session, requests) {
322
+ await requests?.close();
323
+ return result.kind === "answered" ? { ...result, ...(session === undefined ? {} : { session }) } : result;
324
+ }
80
325
  async function consumeTurnDrive(input, active) {
81
- const { drive, requests, cwd, options, session } = active;
82
- let turnSession = session;
83
- let entered = false;
326
+ const { turnSequence, drive, requests, resume } = active;
327
+ let heart = input.supervisor.current();
328
+ let turnSession = resume;
329
+ const attempted = new Set(input.launchTells.map((tell) => tell.id));
330
+ const writeWitness = serializeEffects();
331
+ let writesOpen = true;
332
+ const mayWrite = () => writesOpen && !input.supervisor.signal.aborted;
333
+ const writers = { input, turnSequence, drive, writeWitness, mayWrite };
334
+ const receiptPump = pumpReceipts(writers);
335
+ const receiptFailure = observeReceiptFailure(receiptPump);
84
336
  const iterator = drive.events[Symbol.asyncIterator]();
85
337
  let pending = iterator.next();
86
- for (;;) {
87
- if (stopRequested(input.paths) || pauseRequested(input.paths)) {
88
- const draining = requests?.close();
89
- try {
90
- await drive.abort();
338
+ let liveTells = true;
339
+ let tellPump = null;
340
+ let tellObservation = null;
341
+ try {
342
+ for (;;) {
343
+ if (input.supervisor.signal.aborted) {
344
+ writesOpen = false;
345
+ await stopActiveDrive(input, active);
346
+ return { kind: "stopped" };
91
347
  }
92
- finally {
93
- await draining;
348
+ if (hasUnattemptedTell(liveTells, tellPump, heart.pending, attempted)) {
349
+ if (drive.tell === undefined) {
350
+ writesOpen = false;
351
+ await stopActiveDrive(input, active);
352
+ return { kind: "handoff" };
353
+ }
354
+ tellPump = submitPendingLiveTells(writers, heart.pending, attempted, drive.tell);
355
+ tellObservation = tellPump.then((result) => ({ kind: "tell", result }));
94
356
  }
95
- return { kind: "stopped" };
357
+ const next = await Promise.race([
358
+ pending.then((event) => ({ kind: "event", event })),
359
+ input.supervisor.next(heart).then((observation) => ({ kind: "heart", observation })),
360
+ ...(tellObservation === null ? [] : [tellObservation]),
361
+ receiptFailure,
362
+ ...(requests === null ? [] : [requests.failure]),
363
+ ]);
364
+ if (next.kind === "event" && !next.event.done) {
365
+ await writeProviderEvent(input, active, next.event.value);
366
+ }
367
+ if (next.kind === "heart") {
368
+ heart = next.observation;
369
+ continue;
370
+ }
371
+ if (next.kind === "tell") {
372
+ tellPump = null;
373
+ tellObservation = null;
374
+ liveTells = next.result === "live";
375
+ continue;
376
+ }
377
+ if (next.event.done)
378
+ break;
379
+ turnSession = persistProviderEvent(input, active, next.event.value) ?? turnSession;
380
+ pending = iterator.next();
96
381
  }
97
- const next = await Promise.race([
98
- pending,
99
- wait(LEASH_RETRY_MS).then(() => null),
100
- ...(requests === null ? [] : [requests.failure]),
101
- ]);
102
- if (next === null)
103
- continue;
104
- if (next.done)
105
- break;
106
- const event = next.value;
107
- const at = input.now();
108
- if (event.type === "session") {
109
- turnSession = event.coordinate;
110
- recordSession(input.paths, {
111
- provider: input.soul.provider.name,
112
- coordinate: event.coordinate,
113
- cwd,
114
- options,
115
- admittedAt: at,
116
- });
382
+ const result = await drive.completion;
383
+ if (tellPump !== null)
384
+ await tellPump;
385
+ await writeWitness.drain();
386
+ writesOpen = false;
387
+ return await settleCompletion(result, turnSession, requests);
388
+ }
389
+ catch (error) {
390
+ writesOpen = false;
391
+ if (input.supervisor.signal.aborted) {
392
+ await stopActiveDrive(input, active);
393
+ return { kind: "stopped" };
117
394
  }
118
- appendActivity(input.paths, {
119
- bodySequence: input.bodySequence,
120
- event: encodeAgentEvent(event),
121
- at,
122
- });
123
- if (!entered && input.tellId !== undefined) {
124
- entered = true;
125
- advanceTell(input.paths, input.tellId, "seen");
126
- advanceTell(input.paths, input.tellId, "consumed");
395
+ await retireProviderCustody(input, turnSequence, drive);
396
+ try {
397
+ await requests?.close();
127
398
  }
128
- pending = iterator.next();
399
+ catch { /* preserve the first pump failure */ }
400
+ return { kind: "failed", diagnostic: error instanceof Error ? error.message : String(error) };
129
401
  }
130
- const result = await drive.completion;
131
- if (requests !== null)
132
- await requests.close();
133
- if (!entered && result.kind === "answered" && input.tellId !== undefined) {
134
- advanceTell(input.paths, input.tellId, "seen");
135
- advanceTell(input.paths, input.tellId, "consumed");
136
- }
137
- return result.kind === "answered"
138
- ? { ...result, ...(turnSession === undefined ? {} : { session: turnSession }) }
139
- : result;
140
402
  }
141
403
  async function driveTurn(input) {
142
- const active = await startTurnDrive(input);
404
+ let active;
143
405
  try {
144
- return await consumeTurnDrive(input, active);
406
+ const started = await startTurnDrive(input);
407
+ if ("kind" in started)
408
+ return started;
409
+ active = started;
145
410
  }
146
411
  catch (error) {
147
- const draining = active.requests?.close();
148
- if (heartExists(input.paths)) {
149
- await draining;
150
- throw error;
151
- }
152
- try {
153
- await active.drive.abort();
412
+ if (!await heartExists(input.paths)) {
413
+ input.supervisor.cancel("heart-gone");
414
+ return { kind: "stopped" };
154
415
  }
155
- catch { /* the heart loss still owns shutdown */ }
156
- try {
157
- await draining;
416
+ throw error;
417
+ }
418
+ try {
419
+ const result = await consumeTurnDrive(input, active);
420
+ return result.kind === "stopped" || result.kind === "handoff"
421
+ ? result
422
+ : { ...result, turnSequence: active.turnSequence };
423
+ }
424
+ catch (error) {
425
+ if (await heartExists(input.paths)) {
426
+ await active.requests?.close();
427
+ return { kind: "failed", diagnostic: error instanceof Error ? error.message : String(error),
428
+ turnSequence: active.turnSequence };
158
429
  }
159
- catch { /* the vanished heart owns the request loss */ }
160
- return { kind: "heart-gone" };
430
+ input.supervisor.cancel("heart-gone");
431
+ await stopActiveDrive(input, active);
432
+ return { kind: "stopped" };
161
433
  }
162
434
  }
163
- function bornSoul(launch, leash, now) {
164
- const before = readHeart(launch.paths);
165
- if (before.death !== null)
166
- return null;
167
- if (before.soul !== null) {
168
- clearAbandonedControl(launch.paths);
435
+ async function bornSoul(launch, leash, now) {
436
+ const before = await readHeart(launch.paths);
437
+ if (before.soul !== null)
169
438
  return before.soul;
170
- }
171
439
  if (launch.seed === undefined)
172
440
  throw new Error("Akuma wake has no born soul");
173
441
  const candidate = { ...launch.seed, createdAt: now };
174
- if (leash.birth(launch.paths, candidate, launch.birthSession) === "sealed")
442
+ if (await leash.birth(launch.paths, candidate, launch.birthSession) === "sealed")
175
443
  return null;
176
- const born = readHeart(launch.paths).soul;
444
+ const born = (await readHeart(launch.paths)).soul;
177
445
  if (born === null || born.id !== candidate.id) {
178
446
  throw new Error("Akuma birth identity does not match its directory");
179
447
  }
180
448
  return born;
181
449
  }
182
- function launchCwd(launch) {
450
+ async function launchCwd(launch) {
183
451
  if (launch.seed !== undefined)
184
452
  return launch.seed.cwd;
185
- const snapshot = readHeart(launch.paths);
186
- const soul = snapshot.soul;
453
+ const soul = (await readHeart(launch.paths)).soul;
187
454
  if (soul === null)
188
455
  throw new Error("Akuma wake has no born soul");
189
- return snapshot.latestSession?.provider === soul.provider.name
190
- ? snapshot.latestSession.cwd
191
- : soul.cwd;
456
+ return (await turnRecipe(launch.paths, soul)).cwd;
192
457
  }
193
- function persistTurn(paths, bodySequence, result, completedAt) {
194
- recordTurn(paths, {
195
- bodySequence,
458
+ async function persistTurn(paths, turnSequence, result, completedAt) {
459
+ await endTurn(paths, {
460
+ turnSequence,
196
461
  outcome: result.kind === "answered" && result.session !== undefined
197
- ? { kind: "answered", historyId: result.historyId, session: result.session, answer: result.answer }
462
+ ? {
463
+ kind: "answered",
464
+ session: result.session,
465
+ answer: result.answer,
466
+ ...(result.historyId === undefined ? {} : { historyId: result.historyId }),
467
+ }
198
468
  : result.kind === "answered"
199
469
  ? { kind: "failed", diagnostic: "Provider answered without a resumable session" }
200
470
  : { kind: "failed", diagnostic: result.diagnostic },
@@ -204,82 +474,114 @@ function persistTurn(paths, bodySequence, result, completedAt) {
204
474
  }
205
475
  function defaultRuntime() {
206
476
  return {
207
- collar: currentProcessCollar(),
208
477
  now: () => new Date().toISOString(),
209
- putDownOwnTree: async (collar) => { await putDownProcessTree(collar); },
210
- spawnChild: async (launch) => await spawnAkumaBody(launch),
478
+ spawnChild: spawnAkumaBody,
211
479
  };
212
480
  }
213
- function childSpawner(runtime) {
214
- return runtime.spawnChild ?? spawnAkumaBody;
481
+ async function prepareBodyStart(paths, leash) {
482
+ const before = await readHeart(paths);
483
+ if (before.pause !== null)
484
+ return false;
485
+ if (before.stop === null)
486
+ return true;
487
+ if (before.latestBody?.sequence === before.stop.bodySequence && before.latestBody.end === "put-down") {
488
+ await leash.settleStop(paths);
489
+ }
490
+ else {
491
+ await leash.clearStop(paths);
492
+ }
493
+ return true;
215
494
  }
216
- async function beginBody(paths, soul, runtime) {
217
- if (!await settlePredecessor(paths))
218
- return null;
219
- const body = recordBody(paths, { collar: runtime.collar, leashTakenAt: runtime.now() });
220
- if (await settleBodyRequests(paths, soul, runtime.now) === "settled")
221
- return body;
222
- breakBody(paths, { sequence: body.sequence, end: "broke-off", at: runtime.now() });
223
- return null;
495
+ async function recoverBodyRequests(input) {
496
+ const { launch, soul, bodySequence, supervisor, runtime } = input;
497
+ try {
498
+ const result = await settleBodyRequests(launch.paths, soul, runtime.now, supervisor.signal);
499
+ await clearBodyRequestTransport(launch.paths);
500
+ if (result === "settled")
501
+ return true;
502
+ await breakBody(launch.paths, { sequence: bodySequence, end: "broke-off", at: runtime.now() });
503
+ }
504
+ catch (error) {
505
+ if (!supervisor.signal.aborted)
506
+ throw error;
507
+ await clearBodyRequestTransport(launch.paths);
508
+ await putDownIfControlled(launch.paths, supervisor, bodySequence, runtime.now);
509
+ }
510
+ return false;
511
+ }
512
+ async function runBodyTurns(input) {
513
+ const { launch, soul, adapter, bodySequence, supervisor, runtime } = input;
514
+ let initial = launch.initialBody;
515
+ for (;;) {
516
+ const launchTells = supervisor.current().pending;
517
+ if (supervisor.signal.aborted) {
518
+ await putDownIfControlled(launch.paths, supervisor, bodySequence, runtime.now);
519
+ return;
520
+ }
521
+ if (initial === undefined && launchTells.length === 0) {
522
+ const finished = await finishBodyIfIdle(launch.paths, { sequence: bodySequence, at: runtime.now() });
523
+ if (finished.kind === "finished")
524
+ return;
525
+ if (finished.kind === "controlled") {
526
+ supervisor.cancel("control");
527
+ return;
528
+ }
529
+ continue;
530
+ }
531
+ const result = await driveTurn({
532
+ paths: launch.paths,
533
+ soul,
534
+ adapter,
535
+ bodySequence,
536
+ supervisor,
537
+ runtimeSpawn: runtime.spawnChild ?? spawnAkumaBody,
538
+ body: initial ?? "",
539
+ ...(initial === undefined ? {} : { call: initial }),
540
+ launchTells,
541
+ now: runtime.now,
542
+ });
543
+ if (result.kind === "stopped") {
544
+ await putDownIfControlled(launch.paths, supervisor, bodySequence, runtime.now);
545
+ return;
546
+ }
547
+ if (result.kind === "handoff") {
548
+ await breakBody(launch.paths, { sequence: bodySequence, end: "put-down", at: runtime.now() });
549
+ return;
550
+ }
551
+ if (result.kind === "resume-unsupported"
552
+ || await persistTurn(launch.paths, result.turnSequence, result, runtime.now()) === "failed") {
553
+ await breakBody(launch.paths, { sequence: bodySequence, end: "broke-off", at: runtime.now() });
554
+ return;
555
+ }
556
+ await supervisor.refresh();
557
+ initial = undefined;
558
+ }
224
559
  }
225
560
  export async function driveAkumaBody(launch, adapter, runtime = defaultRuntime()) {
226
561
  const leash = await takeLeash(launch.paths);
227
562
  if (leash === null)
228
563
  return;
229
564
  try {
230
- const soul = bornSoul(launch, leash, runtime.now());
565
+ const soul = await bornSoul(launch, leash, runtime.now());
231
566
  if (soul === null)
232
567
  return;
233
- const selected = adapter ?? providerNamed(soul.provider);
234
- const body = await beginBody(launch.paths, soul, runtime);
235
- if (body === null)
568
+ const selected = adapter ?? resolveProviderExecution(soul.provider).adapter;
569
+ if (!await prepareBodyStart(launch.paths, leash))
236
570
  return;
237
- let initial = launch.initialBody;
238
- for (;;) {
239
- const snapshot = readHeart(launch.paths);
240
- if (snapshot.death !== null) {
241
- breakBody(launch.paths, { sequence: body.sequence, end: "put-down", at: runtime.now() });
242
- return;
243
- }
244
- const tell = initial === undefined ? snapshot.pending[0] : undefined;
245
- const prompt = initial ?? tell?.body;
246
- if (prompt === undefined) {
247
- const finished = finishBodyIfIdle(launch.paths, { sequence: body.sequence, at: runtime.now() });
248
- if (finished.kind === "finished")
249
- return;
250
- continue;
251
- }
252
- if (tell !== undefined)
253
- advanceTell(launch.paths, tell.id, "delivered");
254
- const result = await driveTurn({
255
- paths: launch.paths,
256
- soul,
257
- adapter: selected,
258
- bodySequence: body.sequence,
259
- runtimeSpawn: childSpawner(runtime),
260
- prompt,
261
- ...(tell === undefined ? {} : { tellId: tell.id }),
262
- now: runtime.now,
263
- });
264
- if (result.kind === "heart-gone") {
265
- await runtime.putDownOwnTree(runtime.collar);
266
- return;
267
- }
268
- if (result.kind === "stopped") {
269
- breakBody(launch.paths, { sequence: body.sequence, end: "put-down", at: runtime.now() });
270
- return;
271
- }
272
- if (persistTurn(launch.paths, body.sequence, result, runtime.now()) === "failed") {
273
- breakBody(launch.paths, { sequence: body.sequence, end: "broke-off", at: runtime.now() });
274
- return;
275
- }
276
- initial = undefined;
571
+ const body = await leash.recordBody(launch.paths, { leashTakenAt: runtime.now() });
572
+ const supervisor = await BodySupervisor.open(launch.paths, body.sequence, leash);
573
+ const execution = { launch, soul, adapter: selected, bodySequence: body.sequence, supervisor, runtime };
574
+ try {
575
+ if (await recoverBodyRequests(execution))
576
+ await runBodyTurns(execution);
577
+ }
578
+ finally {
579
+ await supervisor.close();
277
580
  }
278
581
  }
279
582
  catch (error) {
280
- if (heartExists(launch.paths))
583
+ if (await heartExists(launch.paths))
281
584
  throw error;
282
- await runtime.putDownOwnTree(runtime.collar);
283
585
  }
284
586
  finally {
285
587
  leash.release();
@@ -287,11 +589,16 @@ export async function driveAkumaBody(launch, adapter, runtime = defaultRuntime()
287
589
  }
288
590
  export async function spawnAkumaBody(launch) {
289
591
  const encoded = Buffer.from(JSON.stringify(launch), "utf8").toString("base64url");
290
- return await spawnDetachedProcess({
592
+ const actorId = launch.seed?.id ?? (await readHeart(launch.paths)).soul?.id;
593
+ if (actorId === undefined)
594
+ throw new Error("Akuma wake has no born soul");
595
+ const owned = await spawnDetachedProcess({
291
596
  argv: [process.execPath, ...process.execArgv, fileURLToPath(import.meta.url), encoded],
292
- cwd: launchCwd(launch),
597
+ cwd: await launchCwd(launch),
598
+ env: { ...process.env, KEIYAKU_ACTOR_ID: actorId },
293
599
  log: launch.paths.log,
294
600
  });
601
+ owned.release();
295
602
  }
296
603
  async function main() {
297
604
  const encoded = process.argv[2];