@astrosheep/keiyaku 4.5.7 → 4.5.8

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 (234) hide show
  1. package/README.md +10 -0
  2. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/opencode.js +3 -2
  5. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +10 -9
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +22 -1
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-babysit/SKILL.md +35 -96
  9. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +28 -49
  10. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +21 -15
  11. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +148 -89
  12. package/build/src/akuma/abort.d.ts +2 -1
  13. package/build/src/akuma/abort.js +6 -23
  14. package/build/src/akuma/akuma-handle.js +4 -2
  15. package/build/src/akuma/akuma-observe.d.ts +2 -0
  16. package/build/src/akuma/akuma-observe.js +1 -0
  17. package/build/src/akuma/akuma-product.js +9 -7
  18. package/build/src/akuma/akuma.d.ts +372 -9
  19. package/build/src/akuma/akuma.js +31 -1
  20. package/build/src/akuma/allowed.d.ts +1 -1
  21. package/build/src/akuma/allowed.js +1 -0
  22. package/build/src/akuma/body.d.ts +6 -4
  23. package/build/src/akuma/body.js +5 -3
  24. package/build/src/akuma/call-request.d.ts +80 -0
  25. package/build/src/akuma/call-request.js +211 -0
  26. package/build/src/akuma/heart/facts.d.ts +6 -78
  27. package/build/src/akuma/heart/index.d.ts +1 -1
  28. package/build/src/akuma/heart/request-authority.d.ts +5 -4
  29. package/build/src/akuma/heart/request-authority.js +20 -34
  30. package/build/src/akuma/heart/request-rows.js +12 -23
  31. package/build/src/akuma/heart/schema.d.ts +1 -1
  32. package/build/src/akuma/heart/schema.js +5 -13
  33. package/build/src/akuma/index.d.ts +1 -1
  34. package/build/src/akuma/index.js +1 -1
  35. package/build/src/akuma/projection-read.d.ts +1 -0
  36. package/build/src/akuma/projection-read.js +8 -1
  37. package/build/src/akuma/projection.d.ts +856 -137
  38. package/build/src/akuma/projection.js +190 -0
  39. package/build/src/akuma/provider.d.ts +30 -3
  40. package/build/src/akuma/provider.js +104 -0
  41. package/build/src/akuma/providers/acp/core.d.ts +2 -2
  42. package/build/src/akuma/providers/acp/core.js +13 -6
  43. package/build/src/akuma/providers/acp/index.js +5 -4
  44. package/build/src/akuma/providers/claude/index.d.ts +3 -3
  45. package/build/src/akuma/providers/claude/index.js +16 -5
  46. package/build/src/akuma/providers/codex-app-server/events.js +1 -1
  47. package/build/src/akuma/providers/codex-app-server/index.js +18 -6
  48. package/build/src/akuma/providers/grok-build/index.js +5 -4
  49. package/build/src/akuma/providers/opencode-sdk/index.js +66 -25
  50. package/build/src/akuma/providers/opencode-sdk/session.d.ts +3 -2
  51. package/build/src/akuma/providers/opencode-sdk/session.js +28 -11
  52. package/build/src/akuma/providers/pi/index.js +42 -14
  53. package/build/src/akuma/request-lifecycle.d.ts +3 -9
  54. package/build/src/akuma/request-lifecycle.js +4 -4
  55. package/build/src/akuma/request-rendezvous.d.ts +26 -0
  56. package/build/src/akuma/request-rendezvous.js +100 -0
  57. package/build/src/akuma/request-serve.d.ts +5 -65
  58. package/build/src/akuma/request-serve.js +81 -208
  59. package/build/src/akuma/request-wire.d.ts +109 -115
  60. package/build/src/akuma/request-wire.js +63 -318
  61. package/build/src/akuma/requests.d.ts +19 -47
  62. package/build/src/akuma/requests.js +51 -107
  63. package/build/src/akuma/turn-drive.d.ts +5 -3
  64. package/build/src/akuma/turn-drive.js +32 -10
  65. package/build/src/akuma-body.d.ts +7 -2
  66. package/build/src/akuma-body.js +68 -46
  67. package/build/src/cli/accepted.d.ts +1 -1
  68. package/build/src/cli/accepted.js +2 -2
  69. package/build/src/cli/commands/akuma-invoke.d.ts +2 -0
  70. package/build/src/cli/commands/akuma-invoke.js +17 -9
  71. package/build/src/cli/commands/akuma.js +7 -7
  72. package/build/src/cli/commands/contract-invoke.d.ts +3 -2
  73. package/build/src/cli/commands/contract-invoke.js +26 -19
  74. package/build/src/cli/commands/contract.d.ts +13 -13
  75. package/build/src/cli/commands/contract.js +32 -17
  76. package/build/src/cli/commands/install.d.ts +1 -1
  77. package/build/src/cli/commands/install.js +1 -1
  78. package/build/src/cli/commands/task-invoke.d.ts +3 -0
  79. package/build/src/cli/commands/task-invoke.js +23 -140
  80. package/build/src/cli/commands/task.js +54 -20
  81. package/build/src/cli/invoke.js +39 -25
  82. package/build/src/cli/main.js +2 -1
  83. package/build/src/cli/parse.d.ts +2 -2
  84. package/build/src/cli/parse.js +37 -22
  85. package/build/src/cli/render/akuma-activity.d.ts +4 -1
  86. package/build/src/cli/render/akuma-activity.js +45 -30
  87. package/build/src/cli/render/akuma-tool.d.ts +5 -1
  88. package/build/src/cli/render/akuma.js +5 -9
  89. package/build/src/cli/render/audit.js +1 -1
  90. package/build/src/cli/render/catalog.js +80 -21
  91. package/build/src/cli/render/contract-observation.d.ts +2 -0
  92. package/build/src/cli/render/contract-observation.js +11 -0
  93. package/build/src/cli/render/contract.js +25 -65
  94. package/build/src/cli/render/kanshi-akuma.d.ts +8 -0
  95. package/build/src/cli/render/kanshi-akuma.js +111 -0
  96. package/build/src/cli/render/kanshi.js +108 -213
  97. package/build/src/cli/render/receipt.d.ts +1 -2
  98. package/build/src/cli/render/receipt.js +76 -26
  99. package/build/src/cli/render/refusal.js +2 -10
  100. package/build/src/cli/render/region.js +5 -2
  101. package/build/src/cli/render/settings.d.ts +1 -1
  102. package/build/src/cli/render/settings.js +27 -8
  103. package/build/src/cli/render/task.js +22 -5
  104. package/build/src/cli/render/terminal.d.ts +28 -0
  105. package/build/src/cli/render/terminal.js +65 -0
  106. package/build/src/cli/result.d.ts +3 -3
  107. package/build/src/cli/runtime.js +3 -1
  108. package/build/src/cli/selectors.d.ts +2 -1
  109. package/build/src/cli/selectors.js +4 -3
  110. package/build/src/cli/square-edge.js +120 -36
  111. package/build/src/cli/usage.js +2 -1
  112. package/build/src/contract-guidance.d.ts +5 -0
  113. package/build/src/contract-guidance.js +121 -0
  114. package/build/src/contract-worktree.d.ts +0 -1
  115. package/build/src/contract-worktree.js +32 -83
  116. package/build/src/coordination/sqlite-transaction-lock.d.ts +1 -0
  117. package/build/src/coordination/sqlite-transaction-lock.js +5 -0
  118. package/build/src/core/facts/codec.d.ts +2 -0
  119. package/build/src/core/facts/codec.js +4 -0
  120. package/build/src/core/verbs/placement.d.ts +6 -1
  121. package/build/src/core/verbs/placement.js +5 -3
  122. package/build/src/dispatch/index.d.ts +0 -2
  123. package/build/src/dispatch/index.js +54 -55
  124. package/build/src/git/admission.js +2 -2
  125. package/build/src/git/hooks.d.ts +9 -15
  126. package/build/src/git/hooks.js +23 -317
  127. package/build/src/git/nuke.d.ts +2 -1
  128. package/build/src/git/nuke.js +36 -18
  129. package/build/src/git/private-state-seat.d.ts +12 -0
  130. package/build/src/git/private-state-seat.js +39 -0
  131. package/build/src/git/process.d.ts +6 -0
  132. package/build/src/git/process.js +19 -7
  133. package/build/src/git/read-observation.js +29 -20
  134. package/build/src/git/reconcile.d.ts +11 -0
  135. package/build/src/git/reconcile.js +64 -11
  136. package/build/src/git/repository.d.ts +6 -2
  137. package/build/src/git/repository.js +8 -0
  138. package/build/src/git/target-placement.d.ts +1 -0
  139. package/build/src/git/target-placement.js +5 -0
  140. package/build/src/git/tender.d.ts +1 -0
  141. package/build/src/git/terminal-reconcile.d.ts +1 -1
  142. package/build/src/git/terminal-reconcile.js +22 -20
  143. package/build/src/git/workspace.d.ts +14 -0
  144. package/build/src/git/workspace.js +44 -0
  145. package/build/src/kanshi/index.d.ts +1 -1
  146. package/build/src/kanshi/read.js +16 -16
  147. package/build/src/kanshi/report.d.ts +2 -2
  148. package/build/src/library/address.d.ts +9 -3
  149. package/build/src/library/address.js +3 -2
  150. package/build/src/library/akuma-creation.d.ts +4 -3
  151. package/build/src/library/akuma-creation.js +19 -13
  152. package/build/src/library/audit.d.ts +1 -1
  153. package/build/src/library/audit.js +5 -3
  154. package/build/src/library/catalog.d.ts +3 -1
  155. package/build/src/library/catalog.js +20 -6
  156. package/build/src/library/composition.d.ts +371 -0
  157. package/build/src/library/composition.js +28 -0
  158. package/build/src/library/configuration.js +8 -2
  159. package/build/src/library/continuation.js +66 -30
  160. package/build/src/library/contract-bind.js +10 -2
  161. package/build/src/library/contract-forwarding-reconciliation-result.d.ts +96 -0
  162. package/build/src/library/contract-forwarding-reconciliation-result.js +83 -0
  163. package/build/src/library/contract-forwarding-result.d.ts +6723 -0
  164. package/build/src/library/contract-forwarding-result.js +521 -0
  165. package/build/src/library/contract-forwarding.d.ts +72 -0
  166. package/build/src/library/contract-forwarding.js +125 -0
  167. package/build/src/library/contract-handle.d.ts +36 -0
  168. package/build/src/library/contract-handle.js +313 -0
  169. package/build/src/library/contract-operations.d.ts +1044 -67
  170. package/build/src/library/contract-operations.js +190 -118
  171. package/build/src/library/contract.d.ts +11 -32
  172. package/build/src/library/contract.js +16 -289
  173. package/build/src/library/fleet-result.d.ts +1454 -0
  174. package/build/src/library/fleet-result.js +96 -0
  175. package/build/src/library/fleet.d.ts +112 -54
  176. package/build/src/library/fleet.js +211 -71
  177. package/build/src/library/input.d.ts +2 -0
  178. package/build/src/library/input.js +4 -0
  179. package/build/src/library/keiyaku.d.ts +371 -20
  180. package/build/src/library/keiyaku.js +8 -21
  181. package/build/src/library/mutation.d.ts +3 -3
  182. package/build/src/library/mutation.js +5 -9
  183. package/build/src/library/nuke.d.ts +1 -1
  184. package/build/src/library/nuke.js +4 -3
  185. package/build/src/library/reconcile.d.ts +4 -0
  186. package/build/src/library/reconcile.js +2 -0
  187. package/build/src/library/refusal.d.ts +0 -9
  188. package/build/src/library/refusal.js +0 -7
  189. package/build/src/protocol/amend.js +56 -50
  190. package/build/src/protocol/attempt.d.ts +2 -0
  191. package/build/src/protocol/attempt.js +8 -1
  192. package/build/src/protocol/completion.js +7 -0
  193. package/build/src/protocol/deliver.js +6 -2
  194. package/build/src/protocol/operations.d.ts +4 -1
  195. package/build/src/protocol/operations.js +5 -2
  196. package/build/src/protocol/placement.d.ts +1 -0
  197. package/build/src/protocol/placement.js +52 -38
  198. package/build/src/protocol/read/observation.d.ts +1 -4
  199. package/build/src/protocol/read/observation.js +2 -15
  200. package/build/src/protocol/reintegrate.js +62 -58
  201. package/build/src/protocol/review.js +5 -0
  202. package/build/src/protocol/run.js +17 -13
  203. package/build/src/runtime/proc/run.d.ts +8 -0
  204. package/build/src/runtime/proc/run.js +27 -0
  205. package/build/src/runtime/proc/termination.js +26 -22
  206. package/build/src/runtime/proc/windows-launch.exe +0 -0
  207. package/build/src/settlement/holder.d.ts +2 -1
  208. package/build/src/settlement/holder.js +15 -4
  209. package/build/src/settlement/settle.js +99 -33
  210. package/build/src/task/board.d.ts +68 -27
  211. package/build/src/task/board.js +31 -1
  212. package/build/src/task/catalog.d.ts +4 -0
  213. package/build/src/task/catalog.js +23 -0
  214. package/build/src/task/compose-language.js +33 -7
  215. package/build/src/task/compose-parser.d.ts +1 -1
  216. package/build/src/task/compose-parser.js +1 -1
  217. package/build/src/task/compose.js +2 -0
  218. package/build/src/task/index.d.ts +12 -4
  219. package/build/src/task/index.js +97 -16
  220. package/build/src/task/mutation-result.d.ts +923 -0
  221. package/build/src/task/mutation-result.js +140 -0
  222. package/build/src/task/mutation.d.ts +552 -28
  223. package/build/src/task/mutation.js +304 -97
  224. package/build/src/task/operations.d.ts +1 -4
  225. package/build/src/task/operations.js +0 -6
  226. package/build/src/task/public.d.ts +2 -0
  227. package/build/src/task/public.js +1 -0
  228. package/build/src/workspace-place.d.ts +7 -2
  229. package/build/src/workspace-place.js +34 -24
  230. package/build/src/world.d.ts +1 -0
  231. package/build/src/world.js +11 -0
  232. package/package.json +8 -6
  233. package/build/src/akuma/request-execution.d.ts +0 -8
  234. package/build/src/akuma/request-execution.js +0 -98
@@ -1,6 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { abortable } from "../../abort.js";
3
- import { AKUMA_REQUESTS_ENV, AgentEventChannel, } from "../../provider.js";
2
+ import { AKUMA_REQUESTS_ENV, AgentEventChannel, createProviderAttempt, } from "../../provider.js";
4
3
  import { createEventState, mapEvent } from "./events.js";
5
4
  import { coordinate, loadOpencode, OPENCODE_SDK_PROVIDER, parseModel } from "./session.js";
6
5
  function object(value) {
@@ -201,27 +200,51 @@ async function forceDisposeOpencode(abortController, close, finish) {
201
200
  await close();
202
201
  await finish({ kind: "failed", diagnostic: "OpenCode session force-disposed" });
203
202
  }
204
- async function loadDriveRuntime(execution, input, signal, loader) {
205
- return await abortable(loadOpencode({
203
+ async function loadDriveRuntime(execution, input, signal, loader, onRuntime) {
204
+ const runtime = await loadOpencode({
206
205
  ...execution,
207
206
  env: {
208
207
  ...execution.env,
209
208
  ...(input.requests === undefined ? {} : { [AKUMA_REQUESTS_ENV]: input.requests.dir }),
210
209
  },
211
- }, input.cwd, signal, loader), signal, async (late) => await late.close());
210
+ }, input.cwd, signal, loader, onRuntime);
211
+ if (signal.aborted) {
212
+ await runtime.close();
213
+ signal.throwIfAborted();
214
+ }
215
+ return runtime;
216
+ }
217
+ function ownRuntime(custody, runtime) {
218
+ let closing;
219
+ let settleRuntimeClosed;
220
+ let rejectRuntimeClosed;
221
+ const runtimeClosed = new Promise((resolve, reject) => {
222
+ settleRuntimeClosed = resolve;
223
+ rejectRuntimeClosed = reject;
224
+ });
225
+ const close = () => {
226
+ if (closing === undefined) {
227
+ closing = runtime.close();
228
+ void closing.then(settleRuntimeClosed, rejectRuntimeClosed);
229
+ }
230
+ return closing;
231
+ };
232
+ custody?.own({ closed: runtimeClosed, abort: close, forceDispose: close });
233
+ return close;
212
234
  }
213
235
  async function openDriveSession(runtime, input, resumeSessionId, signal) {
214
236
  const session = runtime.client.session;
215
- const response = await abortable(input.session.kind === "fresh"
237
+ const response = await (input.session.kind === "fresh"
216
238
  ? session.create({ query: { directory: input.cwd }, throwOnError: true })
217
- : session.get({ path: { id: resumeSessionId }, query: { directory: input.cwd }, throwOnError: true }), signal);
239
+ : session.get({ path: { id: resumeSessionId }, query: { directory: input.cwd }, throwOnError: true }));
240
+ signal.throwIfAborted();
218
241
  const info = object(object(response)?.data) ?? object(response);
219
242
  const sessionId = text(info?.id) ?? resumeSessionId;
220
243
  if (sessionId === undefined)
221
244
  throw new Error("OpenCode did not return a session id");
222
245
  return { session, sessionId };
223
246
  }
224
- async function drive(execution, input, loader) {
247
+ async function drive(execution, input, loader, custody) {
225
248
  admit(input.options);
226
249
  const signal = input.signal ?? new AbortController().signal;
227
250
  const resumeSessionId = input.session.kind === "resume" ? opencodeSessionId(input.session.coordinate) : undefined;
@@ -229,13 +252,11 @@ async function drive(execution, input, loader) {
229
252
  const abortSetup = () => abortController.abort(signal.reason);
230
253
  signal.addEventListener("abort", abortSetup, { once: true });
231
254
  signal.throwIfAborted();
232
- const runtime = await loadDriveRuntime(execution, input, abortController.signal, loader);
233
- let closing;
255
+ let closeOnce;
256
+ const runtime = await loadDriveRuntime(execution, input, abortController.signal, loader, (ready) => {
257
+ closeOnce = ownRuntime(custody, ready);
258
+ });
234
259
  let iterator;
235
- const closeOnce = () => {
236
- closing ??= runtime.close();
237
- return closing;
238
- };
239
260
  try {
240
261
  const { session, sessionId } = await openDriveSession(runtime, input, resumeSessionId, abortController.signal);
241
262
  const events = new AgentEventChannel();
@@ -243,9 +264,11 @@ async function drive(execution, input, loader) {
243
264
  const messageID = `msg_${randomUUID().replaceAll("-", "")}`;
244
265
  const submissionState = { started: false };
245
266
  events.emit({ type: "session", coordinate: coordinate(sessionId) });
246
- const streamResult = await abortable(runtime.client.event.subscribe({ query: { directory: input.cwd } }), abortController.signal, async (late) => {
247
- await late.stream[Symbol.asyncIterator]().return?.(undefined);
248
- });
267
+ const streamResult = await runtime.client.event.subscribe({ query: { directory: input.cwd } });
268
+ if (abortController.signal.aborted) {
269
+ await streamResult.stream[Symbol.asyncIterator]().return?.(undefined);
270
+ abortController.signal.throwIfAborted();
271
+ }
249
272
  iterator = streamResult.stream[Symbol.asyncIterator]();
250
273
  const { completion, finish } = terminalSettlement(events, closeOnce);
251
274
  const observation = observeTurn({
@@ -259,12 +282,13 @@ async function drive(execution, input, loader) {
259
282
  submissionState,
260
283
  });
261
284
  submissionState.started = true;
262
- await abortable(session.promptAsync({
285
+ await session.promptAsync({
263
286
  path: { id: sessionId },
264
287
  query: { directory: input.cwd },
265
288
  body: promptBody(input, messageID),
266
289
  throwOnError: true,
267
- }), abortController.signal);
290
+ });
291
+ abortController.signal.throwIfAborted();
268
292
  void observation.then(finish);
269
293
  signal.removeEventListener("abort", abortSetup);
270
294
  return {
@@ -312,11 +336,28 @@ export function createOpencodeProvider(input = { name: OPENCODE_SDK_PROVIDER, ki
312
336
  }),
313
337
  };
314
338
  },
315
- start: (input) => drive(execution, input, loader),
316
- resume: (input) => drive(execution, input, loader),
317
- fork: async (input) => {
339
+ start: (input) => createProviderAttempt(input.signal, async (custody) => await drive(execution, { ...input, signal: custody.signal }, loader, custody)),
340
+ resume: (input) => createProviderAttempt(input.signal, async (custody) => await drive(execution, { ...input, signal: custody.signal }, loader, custody)),
341
+ fork: (input) => createProviderAttempt(new AbortController().signal, async (custody) => {
318
342
  const sessionId = opencodeSessionId(input.session);
319
- const runtime = await loadOpencode(execution, input.cwd, new AbortController().signal, loader);
343
+ let close;
344
+ const runtime = await loadOpencode(execution, input.cwd, custody.signal, loader, (ready) => {
345
+ let closing;
346
+ let settleRuntimeClosed;
347
+ let rejectRuntimeClosed;
348
+ const runtimeClosed = new Promise((resolve, reject) => {
349
+ settleRuntimeClosed = resolve;
350
+ rejectRuntimeClosed = reject;
351
+ });
352
+ close = () => {
353
+ if (closing === undefined) {
354
+ closing = ready.close();
355
+ void closing.then(settleRuntimeClosed, rejectRuntimeClosed);
356
+ }
357
+ return closing;
358
+ };
359
+ custody.own({ closed: runtimeClosed, abort: close, forceDispose: close });
360
+ });
320
361
  try {
321
362
  const result = await runtime.client.session.fork({
322
363
  path: { id: sessionId },
@@ -330,8 +371,8 @@ export function createOpencodeProvider(input = { name: OPENCODE_SDK_PROVIDER, ki
330
371
  return { session: coordinate(id) };
331
372
  }
332
373
  finally {
333
- await runtime.close();
374
+ await close();
334
375
  }
335
- },
376
+ }),
336
377
  };
337
378
  }
@@ -3,12 +3,13 @@ import type { ResumeCoordinate } from "../../heart/index.js";
3
3
  import type { ProviderExecution } from "../../provider-recipe.js";
4
4
  export type OpencodeSdkSession = Pick<OpencodeClient["session"], "create" | "get" | "fork" | "abort" | "promptAsync" | "messages">;
5
5
  export type OpencodeSdkEvent = Pick<OpencodeClient["event"], "subscribe">;
6
- export type OpencodeSdkLoader = (cwd: string, execution: ProviderExecution) => Promise<Readonly<{
6
+ export type OpencodeSdkLoader = (cwd: string, execution: ProviderExecution, signal: AbortSignal) => Promise<Readonly<{
7
7
  client: {
8
8
  session: OpencodeSdkSession;
9
9
  event: OpencodeSdkEvent;
10
10
  };
11
11
  close?: () => Promise<void> | void;
12
+ ready?: Promise<void>;
12
13
  }>>;
13
14
  export declare const OPENCODE_SDK_PROVIDER: "opencode-sdk";
14
15
  export declare function parseModel(model: string): Readonly<{
@@ -22,5 +23,5 @@ export type OpencodeRuntime = Readonly<{
22
23
  };
23
24
  close: () => Promise<void>;
24
25
  }>;
25
- export declare function loadOpencode(execution: ProviderExecution, cwd: string, signal: AbortSignal, loader?: OpencodeSdkLoader): Promise<OpencodeRuntime>;
26
+ export declare function loadOpencode(execution: ProviderExecution, cwd: string, signal: AbortSignal, loader?: OpencodeSdkLoader, onRuntime?: (runtime: OpencodeRuntime) => void): Promise<OpencodeRuntime>;
26
27
  export declare function coordinate(sessionId: string): ResumeCoordinate;
@@ -37,15 +37,28 @@ async function availablePort() {
37
37
  });
38
38
  });
39
39
  }
40
- export async function loadOpencode(execution, cwd, signal, loader) {
40
+ export async function loadOpencode(execution, cwd, signal, loader, onRuntime) {
41
41
  if (loader) {
42
- const loaded = await loader(cwd, execution);
43
- return {
42
+ const loaded = await abortable(loader(cwd, execution, signal), signal);
43
+ let closing;
44
+ const runtime = {
44
45
  client: loaded.client,
45
46
  close: async () => {
46
- await loaded.close?.();
47
+ closing ??= Promise.resolve(loaded.close?.()).then(() => undefined);
48
+ await closing;
47
49
  },
48
50
  };
51
+ onRuntime?.(runtime);
52
+ try {
53
+ if (loaded.ready !== undefined)
54
+ await abortable(loaded.ready, signal);
55
+ signal.throwIfAborted();
56
+ return runtime;
57
+ }
58
+ catch (error) {
59
+ await runtime.close();
60
+ throw error;
61
+ }
49
62
  }
50
63
  const port = await availablePort();
51
64
  const owned = await spawnDetachedProcess({
@@ -60,19 +73,23 @@ export async function loadOpencode(execution, cwd, signal, loader) {
60
73
  });
61
74
  const { createOpencodeClient } = await import("@opencode-ai/sdk");
62
75
  const client = createOpencodeClient({ baseUrl: `http://127.0.0.1:${port}`, directory: cwd });
76
+ let closing;
77
+ const runtime = {
78
+ client,
79
+ close: async () => {
80
+ closing ??= owned.terminate();
81
+ await closing;
82
+ },
83
+ };
84
+ onRuntime?.(runtime);
63
85
  try {
64
86
  await waitReady(client, cwd, signal);
65
87
  }
66
88
  catch (error) {
67
- await owned.terminate();
89
+ await runtime.close();
68
90
  throw error;
69
91
  }
70
- return {
71
- client,
72
- close: async () => {
73
- await owned.terminate();
74
- },
75
- };
92
+ return runtime;
76
93
  }
77
94
  export function coordinate(sessionId) {
78
95
  return { sessionId };
@@ -1,5 +1,5 @@
1
1
  import { abortable } from "../../abort.js";
2
- import { AKUMA_REQUESTS_ENV, AgentEventChannel, } from "../../provider.js";
2
+ import { AKUMA_REQUESTS_ENV, AgentEventChannel, createProviderAttempt, } from "../../provider.js";
3
3
  import { piTerminalFailure, translatePiEvent } from "./events.js";
4
4
  const PI_THINKING_LEVELS = new Set(["minimal", "low", "medium", "high", "xhigh", "max"]);
5
5
  const MODEL_PATTERN = /^[^/\s]+\/[^\s]+$/u;
@@ -75,7 +75,12 @@ async function createPiSession(sdk, execution, input, signal) {
75
75
  }
76
76
  return await sdk.createAgentSession(options);
77
77
  });
78
- return await abortable(setup, signal, (created) => created.session.dispose());
78
+ const created = await setup;
79
+ if (signal.aborted) {
80
+ await created.session.dispose();
81
+ signal.throwIfAborted();
82
+ }
83
+ return created;
79
84
  }
80
85
  function forceDisposePi(dispose, settle, setAborting) {
81
86
  setAborting();
@@ -109,9 +114,27 @@ async function runPiPrompt(native, input, events, state, settle) {
109
114
  : { kind: "failed", diagnostic: diagnostic(error) });
110
115
  }
111
116
  }
112
- async function drivePi(sdk, execution, input, signal) {
117
+ async function drivePi(sdk, execution, input, signal, custody) {
113
118
  const created = await createPiSession(sdk, execution, input, signal);
114
119
  const native = created.session;
120
+ let retired = false;
121
+ let settleRetired;
122
+ const closed = new Promise((resolve) => {
123
+ settleRetired = resolve;
124
+ });
125
+ custody?.own({
126
+ closed,
127
+ abort: async () => {
128
+ native.dispose();
129
+ retired = true;
130
+ settleRetired();
131
+ },
132
+ forceDispose: async () => {
133
+ native.dispose();
134
+ retired = true;
135
+ settleRetired();
136
+ },
137
+ });
115
138
  if (signal.aborted) {
116
139
  native.dispose();
117
140
  signal.throwIfAborted();
@@ -138,6 +161,10 @@ async function drivePi(sdk, execution, input, signal) {
138
161
  finally {
139
162
  try {
140
163
  native.dispose();
164
+ if (!retired) {
165
+ retired = true;
166
+ settleRetired();
167
+ }
141
168
  }
142
169
  finally {
143
170
  events.end();
@@ -216,21 +243,22 @@ export function createPiProvider(execution = { name: "pi", kind: "pi" }, load =
216
243
  if (execution.env !== undefined && Object.keys(execution.env).length > 0) {
217
244
  throw new TypeError("env injection not supported for provider pi");
218
245
  }
219
- const drive = async (input) => {
220
- const signal = input.signal ?? new AbortController().signal;
221
- signal.throwIfAborted();
222
- return drivePi(await abortable(load(), signal), execution, input, signal);
246
+ const drive = async (input, custody) => {
247
+ custody.signal.throwIfAborted();
248
+ const sdk = await abortable(load(), custody.signal);
249
+ custody.signal.throwIfAborted();
250
+ return await drivePi(sdk, execution, { ...input, signal: custody.signal }, custody.signal, custody);
223
251
  };
224
252
  return {
225
253
  admitOptions: admitPiOptions,
226
- start: drive,
227
- resume: async (input) => {
254
+ start: (input) => createProviderAttempt(input.signal, async (custody) => await drive(input, custody)),
255
+ resume: (input) => createProviderAttempt(input.signal, async (custody) => {
228
256
  piSessionFile(input.session.coordinate);
229
- return drive(input);
230
- },
231
- fork: async (input) => {
257
+ return await drive(input, custody);
258
+ }),
259
+ fork: (input) => createProviderAttempt(new AbortController().signal, async () => {
232
260
  piSessionFile(input.session);
233
- return forkPi(await load(), input);
234
- },
261
+ return await forkPi(await load(), input);
262
+ }),
235
263
  };
236
264
  }
@@ -1,25 +1,19 @@
1
1
  import { type Soul } from "./heart/index.js";
2
2
  import { type AkumaPaths } from "./identity.js";
3
- import { type StructuralRequestClaim } from "./request-wire.js";
4
- import type { OwnedProcess } from "../runtime/proc/run.js";
5
- export type RequestChildLaunch = Readonly<{
6
- paths: AkumaPaths;
7
- seed: Omit<Soul, "createdAt">;
8
- initialBody: string;
9
- }>;
3
+ import { type RequestEnvelope } from "./request-wire.js";
10
4
  export type PumpInput = Readonly<{
11
5
  paths: AkumaPaths;
12
6
  parent: Soul;
13
7
  bodySequence: number;
14
8
  now(): string;
15
- spawn(launch: RequestChildLaunch): Promise<OwnedProcess | void>;
9
+ spawn(launch: unknown): Promise<unknown>;
16
10
  upstream?: unknown;
17
11
  signal: AbortSignal;
18
12
  }>;
19
13
  type ServeClaim = (input: PumpInput & Readonly<{
20
14
  directory: string;
21
15
  transportId: string;
22
- claim: StructuralRequestClaim;
16
+ claim: RequestEnvelope;
23
17
  signal: AbortSignal;
24
18
  admissionOpen(): boolean;
25
19
  }>) => Promise<void>;
@@ -2,9 +2,9 @@ import { access, mkdir, readFile, readdir, rm } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { abortableDelay } from "./abort.js";
4
4
  import { HeldAkumaLeash, readNonterminalRequests, readSoul, serveRequest, voidRequest, } from "./heart/index.js";
5
- import { pathsForAkuId } from "./identity.js";
5
+ import { pathsForAkuId, worldRootForAkumaPaths } from "./identity.js";
6
6
  import { BIRTH_TIMEOUT_MS } from "./publication.js";
7
- import { decodeClaim } from "./request-wire.js";
7
+ import { decodeEnvelope } from "./request-wire.js";
8
8
  const POLL_MS = 100;
9
9
  export class BodyRequestPump {
10
10
  input;
@@ -45,7 +45,7 @@ export class BodyRequestPump {
45
45
  if (this.handled.has(transportId))
46
46
  continue;
47
47
  const path = join(this.directory, name);
48
- const claim = decodeClaim(await readFile(path, "utf8"), transportId);
48
+ const claim = decodeEnvelope(await readFile(path, "utf8"), transportId);
49
49
  if (claim === null) {
50
50
  await rm(path, { force: true });
51
51
  this.handled.add(transportId);
@@ -111,7 +111,7 @@ async function settleReservedSoul(paths, parent, request, soul) {
111
111
  : voidRequest(paths, request.id, "reserved child origin does not match the request"));
112
112
  }
113
113
  async function settleReserved(paths, parent, request, now, signal) {
114
- const childPaths = pathsForAkuId(request.world, request.child);
114
+ const childPaths = pathsForAkuId(worldRootForAkumaPaths(paths), request.child);
115
115
  const deadline = performance.now() + BIRTH_TIMEOUT_MS;
116
116
  for (;;) {
117
117
  signal?.throwIfAborted();
@@ -0,0 +1,26 @@
1
+ import { type RequestCommand } from "./request-wire.js";
2
+ export declare class AkumaBodyRequestError extends Error {
3
+ readonly action: string;
4
+ readonly outcome: "refused" | "voided";
5
+ readonly diagnostic: string;
6
+ readonly kind = "akuma-body-request";
7
+ constructor(action: string, outcome: "refused" | "voided", diagnostic: string);
8
+ }
9
+ /** Generic rendezvous only: operation owners supply their own request and result codecs. */
10
+ export declare function requestBodyCommand<Input, Output, Service, Reference = Service>(input: Readonly<{
11
+ directory: string;
12
+ id?: string;
13
+ command: RequestCommand<Input, Output, Service, Reference>;
14
+ value: Input;
15
+ signal?: AbortSignal;
16
+ }>): Promise<Readonly<{
17
+ kind: "returned";
18
+ result: Output;
19
+ requestId: string;
20
+ action: string;
21
+ }> | Readonly<{
22
+ kind: "reference";
23
+ reference: Reference;
24
+ requestId: string;
25
+ action: string;
26
+ }>>;
@@ -0,0 +1,100 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { access, readFile } from "node:fs/promises";
3
+ import { abortableDelay } from "./abort.js";
4
+ import { atomicJson, decodeReceiptEnvelope, receiptPath, requestPath } from "./request-wire.js";
5
+ const POLL_MS = 100;
6
+ export class AkumaBodyRequestError extends Error {
7
+ action;
8
+ outcome;
9
+ diagnostic;
10
+ kind = "akuma-body-request";
11
+ constructor(action, outcome, diagnostic) {
12
+ super(`${action} ${outcome === "voided" ? "failed" : "refused"}: ${diagnostic}`);
13
+ this.action = action;
14
+ this.outcome = outcome;
15
+ this.diagnostic = diagnostic;
16
+ this.name = "AkumaBodyRequestError";
17
+ }
18
+ }
19
+ function throwVoidedRequestFailure(action, evidence, failure, decodeFailure) {
20
+ const ownerFailure = decodeVoidedOwnerFailure(failure, decodeFailure);
21
+ if (ownerFailure !== null)
22
+ throw ownerFailure;
23
+ throw new AkumaBodyRequestError(action, "voided", evidence);
24
+ }
25
+ function decodeVoidedOwnerFailure(failure, decodeFailure) {
26
+ if (failure === undefined || decodeFailure === undefined)
27
+ return null;
28
+ try {
29
+ return decodeFailure(failure);
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ function withRequestMetadata(response, id, action) {
36
+ Object.defineProperties(response, {
37
+ requestId: { value: id, enumerable: false },
38
+ action: { value: action, enumerable: false },
39
+ });
40
+ return response;
41
+ }
42
+ /** Generic rendezvous only: operation owners supply their own request and result codecs. */
43
+ export async function requestBodyCommand(input) {
44
+ const id = input.id ?? randomUUID();
45
+ input.signal?.throwIfAborted();
46
+ const transportId = randomUUID();
47
+ await atomicJson(requestPath(input.directory, transportId), {
48
+ id,
49
+ action: input.command.action,
50
+ payload: input.command.encodeRequest(input.value),
51
+ });
52
+ const path = receiptPath(input.directory, transportId);
53
+ for (;;) {
54
+ try {
55
+ const receipt = decodeReceiptEnvelope(await readFile(path, "utf8"), id, input.command.action);
56
+ if (receipt === null)
57
+ throw new Error(`Akuma body request ${id} has an invalid receipt`);
58
+ if (receipt.state === "refused")
59
+ throw new AkumaBodyRequestError(receipt.action, "refused", receipt.diagnostic);
60
+ if (receipt.state === "voided") {
61
+ throwVoidedRequestFailure(receipt.action, receipt.evidence, receipt.failure, input.command.decodeFailure);
62
+ }
63
+ if ("reference" in receipt) {
64
+ let reference;
65
+ try {
66
+ reference = input.command.decodeReference(receipt.reference);
67
+ }
68
+ catch (error) {
69
+ const diagnostic = error instanceof Error ? error.message : String(error);
70
+ throw new Error(`transport integrity: request ${id} action ${input.command.action} returned an invalid durable reference: ${diagnostic}`);
71
+ }
72
+ return withRequestMetadata({
73
+ kind: "reference",
74
+ reference,
75
+ }, id, input.command.action);
76
+ }
77
+ if (receipt.outcome.kind === "failed") {
78
+ const failure = receipt.outcome.failure;
79
+ throw new Error(failure.kind === "failed" ? failure.diagnostic : `Akuma ${failure.id} was not born`);
80
+ }
81
+ let result;
82
+ try {
83
+ result = input.command.decodeResult(receipt.outcome.result);
84
+ }
85
+ catch (error) {
86
+ const diagnostic = error instanceof Error ? error.message : String(error);
87
+ throw new Error(`transport integrity: request ${id} action ${input.command.action} returned an invalid live result: ${diagnostic}`);
88
+ }
89
+ return withRequestMetadata({ kind: "returned", result }, id, input.command.action);
90
+ }
91
+ catch (error) {
92
+ if (error.code !== "ENOENT")
93
+ throw error;
94
+ }
95
+ if (!(await access(input.directory).then(() => true, () => false))) {
96
+ throw new AkumaBodyRequestError(input.command.action, "voided", "parent request channel closed before a receipt");
97
+ }
98
+ await abortableDelay(POLL_MS, input.signal);
99
+ }
100
+ }
@@ -1,76 +1,16 @@
1
- import { type KillEvidence, type RequestFact } from "./heart/index.js";
2
- import { type AkuId } from "./identity.js";
3
- import { type StructuralRequestClaim } from "./request-wire.js";
4
- import { type TaskMutationRequest } from "../task/mutation.js";
5
- import { BodyRequestPump as LifecycleBodyRequestPump, type RequestChildLaunch as LifecycleRequestChildLaunch, type PumpInput as LifecyclePumpInput } from "./request-lifecycle.js";
1
+ import { type ErasedRequestCommand, type RequestEnvelope } from "./request-wire.js";
2
+ import { BodyRequestPump as LifecycleBodyRequestPump, type PumpInput as LifecyclePumpInput } from "./request-lifecycle.js";
6
3
  export { clearBodyRequestTransport, settleBodyRequests } from "./request-lifecycle.js";
7
- export type RequestChildLaunch = LifecycleRequestChildLaunch;
8
- type UpstreamCall = Readonly<{
9
- signal: AbortSignal;
10
- }>;
11
- type RequesterCall = UpstreamCall & Readonly<{
12
- requester: AkuId;
13
- }>;
14
- type ContractCall = RequesterCall & Readonly<{
15
- repoRoot: string;
16
- contractId: string;
17
- }>;
18
- export type UpstreamExecutionPort = Readonly<{
19
- wait(input: UpstreamCall & Readonly<{
20
- targets: readonly AkuId[];
21
- completion: "any" | "all";
22
- timeoutMs?: number;
23
- }>): Promise<unknown>;
24
- tell(input: UpstreamCall & Readonly<{
25
- target: AkuId;
26
- body: string;
27
- tellId: string;
28
- recordedAt: string;
29
- }>): Promise<unknown>;
30
- kill(input: Readonly<{
31
- targets: readonly AkuId[];
32
- signal: AbortSignal;
33
- }>): Promise<Readonly<{
34
- result: unknown;
35
- service: readonly Readonly<{
36
- id: AkuId;
37
- evidence: KillEvidence;
38
- }>[];
39
- }>>;
40
- deliver(input: ContractCall & Readonly<{
41
- message?: string;
42
- includeDirty: boolean;
43
- materializeConflict: boolean;
44
- }>): Promise<Readonly<{
45
- result: unknown;
46
- deliveryFactId?: string;
47
- }>>;
48
- review(input: ContractCall & Readonly<{
49
- verdict: "satisfied" | "unsatisfied";
50
- summary?: string;
51
- }>): Promise<Readonly<{
52
- result: unknown;
53
- reviewFactId?: string;
54
- }>>;
55
- task(input: RequesterCall & Readonly<{
56
- world: string;
57
- request: TaskMutationRequest;
58
- }>): Promise<unknown>;
59
- }>;
60
4
  type PumpInput = Omit<LifecyclePumpInput, "upstream"> & Readonly<{
61
- upstream?: UpstreamExecutionPort;
5
+ upstream?: unknown;
6
+ commands: Readonly<Record<string, ErasedRequestCommand>>;
62
7
  }>;
63
8
  export type ServeInput = Omit<PumpInput, "bodySequence"> & Readonly<{
64
9
  directory: string;
65
10
  transportId: string;
66
- claim: StructuralRequestClaim;
11
+ claim: RequestEnvelope;
67
12
  admissionOpen(): boolean;
68
13
  }>;
69
- export type UpstreamFact = Exclude<Extract<RequestFact, {
70
- state: "admitted";
71
- }>, Extract<RequestFact, {
72
- action: "akuma.call";
73
- }>>;
74
14
  export declare function serveRequest(input: ServeInput): Promise<void>;
75
15
  export declare class BodyRequestPump extends LifecycleBodyRequestPump {
76
16
  static open(input: PumpInput): Promise<BodyRequestPump>;