@astrosheep/keiyaku 4.5.7 → 4.5.9

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 (244) 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 +12 -11
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +33 -12
  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 +392 -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/archetype.js +1 -1
  23. package/build/src/akuma/body.d.ts +8 -5
  24. package/build/src/akuma/body.js +12 -5
  25. package/build/src/akuma/call-request.d.ts +80 -0
  26. package/build/src/akuma/call-request.js +211 -0
  27. package/build/src/akuma/heart/facts.d.ts +16 -78
  28. package/build/src/akuma/heart/facts.js +11 -0
  29. package/build/src/akuma/heart/index.d.ts +3 -2
  30. package/build/src/akuma/heart/index.js +4 -1
  31. package/build/src/akuma/heart/request-authority.d.ts +5 -4
  32. package/build/src/akuma/heart/request-authority.js +20 -34
  33. package/build/src/akuma/heart/request-rows.js +12 -23
  34. package/build/src/akuma/heart/schema.d.ts +1 -1
  35. package/build/src/akuma/heart/schema.js +5 -13
  36. package/build/src/akuma/index.d.ts +1 -1
  37. package/build/src/akuma/index.js +1 -1
  38. package/build/src/akuma/projection-read.d.ts +1 -0
  39. package/build/src/akuma/projection-read.js +8 -1
  40. package/build/src/akuma/projection.d.ts +907 -137
  41. package/build/src/akuma/projection.js +193 -9
  42. package/build/src/akuma/provider.d.ts +30 -3
  43. package/build/src/akuma/provider.js +111 -0
  44. package/build/src/akuma/providers/acp/core.d.ts +2 -2
  45. package/build/src/akuma/providers/acp/core.js +13 -6
  46. package/build/src/akuma/providers/acp/index.js +5 -4
  47. package/build/src/akuma/providers/claude/events.d.ts +2 -2
  48. package/build/src/akuma/providers/claude/events.js +16 -3
  49. package/build/src/akuma/providers/claude/index.d.ts +3 -3
  50. package/build/src/akuma/providers/claude/index.js +16 -5
  51. package/build/src/akuma/providers/codex-app-server/events.d.ts +2 -2
  52. package/build/src/akuma/providers/codex-app-server/events.js +25 -4
  53. package/build/src/akuma/providers/codex-app-server/index.js +18 -6
  54. package/build/src/akuma/providers/grok-build/index.js +5 -4
  55. package/build/src/akuma/providers/opencode-sdk/index.js +66 -25
  56. package/build/src/akuma/providers/opencode-sdk/session.d.ts +3 -2
  57. package/build/src/akuma/providers/opencode-sdk/session.js +28 -11
  58. package/build/src/akuma/providers/pi/index.js +72 -58
  59. package/build/src/akuma/request-lifecycle.d.ts +4 -10
  60. package/build/src/akuma/request-lifecycle.js +7 -5
  61. package/build/src/akuma/request-rendezvous.d.ts +26 -0
  62. package/build/src/akuma/request-rendezvous.js +100 -0
  63. package/build/src/akuma/request-serve.d.ts +6 -66
  64. package/build/src/akuma/request-serve.js +83 -210
  65. package/build/src/akuma/request-wire.d.ts +109 -115
  66. package/build/src/akuma/request-wire.js +63 -318
  67. package/build/src/akuma/requests.d.ts +19 -47
  68. package/build/src/akuma/requests.js +51 -107
  69. package/build/src/akuma/turn-drive.d.ts +5 -3
  70. package/build/src/akuma/turn-drive.js +39 -11
  71. package/build/src/akuma-body.d.ts +7 -2
  72. package/build/src/akuma-body.js +68 -46
  73. package/build/src/body/amend.js +18 -19
  74. package/build/src/cli/accepted.d.ts +1 -1
  75. package/build/src/cli/accepted.js +2 -2
  76. package/build/src/cli/commands/akuma-invoke.d.ts +2 -0
  77. package/build/src/cli/commands/akuma-invoke.js +17 -9
  78. package/build/src/cli/commands/akuma.js +8 -8
  79. package/build/src/cli/commands/contract-invoke.d.ts +3 -2
  80. package/build/src/cli/commands/contract-invoke.js +42 -30
  81. package/build/src/cli/commands/contract.d.ts +16 -20
  82. package/build/src/cli/commands/contract.js +58 -25
  83. package/build/src/cli/commands/install.d.ts +2 -1
  84. package/build/src/cli/commands/install.js +3 -2
  85. package/build/src/cli/commands/task-invoke.d.ts +3 -0
  86. package/build/src/cli/commands/task-invoke.js +23 -140
  87. package/build/src/cli/commands/task.js +54 -20
  88. package/build/src/cli/invoke.js +39 -25
  89. package/build/src/cli/main.js +2 -1
  90. package/build/src/cli/parse.d.ts +2 -2
  91. package/build/src/cli/parse.js +56 -27
  92. package/build/src/cli/render/akuma-activity.d.ts +3 -0
  93. package/build/src/cli/render/akuma-activity.js +45 -29
  94. package/build/src/cli/render/akuma-tool.d.ts +5 -1
  95. package/build/src/cli/render/akuma.js +4 -8
  96. package/build/src/cli/render/audit.js +1 -1
  97. package/build/src/cli/render/catalog.js +80 -21
  98. package/build/src/cli/render/contract-observation.d.ts +2 -0
  99. package/build/src/cli/render/contract-observation.js +11 -0
  100. package/build/src/cli/render/contract.js +35 -62
  101. package/build/src/cli/render/kanshi-akuma.d.ts +8 -0
  102. package/build/src/cli/render/kanshi-akuma.js +132 -0
  103. package/build/src/cli/render/kanshi.js +131 -213
  104. package/build/src/cli/render/receipt.d.ts +1 -2
  105. package/build/src/cli/render/receipt.js +76 -26
  106. package/build/src/cli/render/refusal.js +2 -10
  107. package/build/src/cli/render/region.js +5 -2
  108. package/build/src/cli/render/settings.d.ts +1 -1
  109. package/build/src/cli/render/settings.js +27 -8
  110. package/build/src/cli/render/task.js +22 -5
  111. package/build/src/cli/render/terminal.d.ts +32 -1
  112. package/build/src/cli/render/terminal.js +77 -1
  113. package/build/src/cli/result.d.ts +3 -3
  114. package/build/src/cli/runtime.js +4 -2
  115. package/build/src/cli/selectors.d.ts +2 -1
  116. package/build/src/cli/selectors.js +4 -3
  117. package/build/src/cli/square-edge.js +120 -36
  118. package/build/src/cli/usage.js +2 -1
  119. package/build/src/contract-guidance.d.ts +5 -0
  120. package/build/src/contract-guidance.js +121 -0
  121. package/build/src/contract-worktree.d.ts +0 -1
  122. package/build/src/contract-worktree.js +32 -83
  123. package/build/src/coordination/sqlite-transaction-lock.d.ts +1 -0
  124. package/build/src/coordination/sqlite-transaction-lock.js +5 -0
  125. package/build/src/core/facts/codec.d.ts +2 -0
  126. package/build/src/core/facts/codec.js +4 -0
  127. package/build/src/core/verbs/placement.d.ts +6 -1
  128. package/build/src/core/verbs/placement.js +5 -3
  129. package/build/src/dispatch/index.d.ts +0 -2
  130. package/build/src/dispatch/index.js +54 -55
  131. package/build/src/git/admission.js +2 -2
  132. package/build/src/git/hooks.d.ts +9 -15
  133. package/build/src/git/hooks.js +23 -317
  134. package/build/src/git/nuke.d.ts +2 -1
  135. package/build/src/git/nuke.js +39 -21
  136. package/build/src/git/private-state-seat.d.ts +12 -0
  137. package/build/src/git/private-state-seat.js +39 -0
  138. package/build/src/git/process.d.ts +6 -0
  139. package/build/src/git/process.js +26 -11
  140. package/build/src/git/read-observation.js +29 -20
  141. package/build/src/git/reconcile.d.ts +13 -1
  142. package/build/src/git/reconcile.js +70 -11
  143. package/build/src/git/ref-migration.d.ts +22 -0
  144. package/build/src/git/ref-migration.js +55 -0
  145. package/build/src/git/repository.d.ts +10 -6
  146. package/build/src/git/repository.js +16 -8
  147. package/build/src/git/target-placement.d.ts +1 -0
  148. package/build/src/git/target-placement.js +5 -0
  149. package/build/src/git/tender.d.ts +1 -0
  150. package/build/src/git/terminal-reconcile.d.ts +1 -1
  151. package/build/src/git/terminal-reconcile.js +22 -20
  152. package/build/src/git/workspace.d.ts +14 -0
  153. package/build/src/git/workspace.js +44 -0
  154. package/build/src/kanshi/index.d.ts +1 -1
  155. package/build/src/kanshi/read.js +16 -16
  156. package/build/src/kanshi/report.d.ts +2 -2
  157. package/build/src/library/address.d.ts +9 -3
  158. package/build/src/library/address.js +3 -2
  159. package/build/src/library/akuma-creation.d.ts +4 -3
  160. package/build/src/library/akuma-creation.js +19 -13
  161. package/build/src/library/audit.d.ts +14 -6
  162. package/build/src/library/audit.js +13 -23
  163. package/build/src/library/catalog.d.ts +3 -1
  164. package/build/src/library/catalog.js +20 -6
  165. package/build/src/library/composition.d.ts +385 -0
  166. package/build/src/library/composition.js +28 -0
  167. package/build/src/library/configuration.js +8 -2
  168. package/build/src/library/continuation.js +66 -30
  169. package/build/src/library/contract-bind.js +10 -2
  170. package/build/src/library/contract-forwarding-reconciliation-result.d.ts +102 -0
  171. package/build/src/library/contract-forwarding-reconciliation-result.js +92 -0
  172. package/build/src/library/contract-forwarding-result.d.ts +4464 -0
  173. package/build/src/library/contract-forwarding-result.js +521 -0
  174. package/build/src/library/contract-forwarding.d.ts +71 -0
  175. package/build/src/library/contract-forwarding.js +114 -0
  176. package/build/src/library/contract-handle.d.ts +38 -0
  177. package/build/src/library/contract-handle.js +314 -0
  178. package/build/src/library/contract-operations.d.ts +214 -69
  179. package/build/src/library/contract-operations.js +189 -118
  180. package/build/src/library/contract-types.d.ts +7 -3
  181. package/build/src/library/contract.d.ts +21 -34
  182. package/build/src/library/contract.js +29 -287
  183. package/build/src/library/fleet-result.d.ts +1580 -0
  184. package/build/src/library/fleet-result.js +98 -0
  185. package/build/src/library/fleet.d.ts +112 -54
  186. package/build/src/library/fleet.js +211 -71
  187. package/build/src/library/input.d.ts +3 -1
  188. package/build/src/library/input.js +11 -1
  189. package/build/src/library/keiyaku.d.ts +388 -20
  190. package/build/src/library/keiyaku.js +13 -21
  191. package/build/src/library/mutation.d.ts +3 -3
  192. package/build/src/library/mutation.js +5 -9
  193. package/build/src/library/nuke.d.ts +1 -1
  194. package/build/src/library/nuke.js +4 -3
  195. package/build/src/library/reconcile.d.ts +4 -0
  196. package/build/src/library/reconcile.js +2 -0
  197. package/build/src/library/refusal.d.ts +0 -9
  198. package/build/src/library/refusal.js +0 -7
  199. package/build/src/protocol/amend.js +56 -50
  200. package/build/src/protocol/attempt.d.ts +2 -0
  201. package/build/src/protocol/attempt.js +8 -1
  202. package/build/src/protocol/completion.js +7 -0
  203. package/build/src/protocol/deliver.js +6 -2
  204. package/build/src/protocol/operations.d.ts +4 -1
  205. package/build/src/protocol/operations.js +5 -2
  206. package/build/src/protocol/placement.d.ts +1 -0
  207. package/build/src/protocol/placement.js +52 -38
  208. package/build/src/protocol/read/observation.d.ts +1 -4
  209. package/build/src/protocol/read/observation.js +2 -15
  210. package/build/src/protocol/reintegrate.js +62 -58
  211. package/build/src/protocol/review.js +5 -0
  212. package/build/src/protocol/run.js +17 -13
  213. package/build/src/runtime/proc/run.d.ts +9 -0
  214. package/build/src/runtime/proc/run.js +51 -4
  215. package/build/src/runtime/proc/termination.js +26 -22
  216. package/build/src/runtime/proc/windows-launch.exe +0 -0
  217. package/build/src/settlement/holder.d.ts +2 -1
  218. package/build/src/settlement/holder.js +15 -4
  219. package/build/src/settlement/settle.js +99 -33
  220. package/build/src/task/board.d.ts +68 -27
  221. package/build/src/task/board.js +31 -1
  222. package/build/src/task/catalog.d.ts +4 -0
  223. package/build/src/task/catalog.js +23 -0
  224. package/build/src/task/compose-language.js +33 -7
  225. package/build/src/task/compose-parser.d.ts +1 -1
  226. package/build/src/task/compose-parser.js +1 -1
  227. package/build/src/task/compose.js +2 -0
  228. package/build/src/task/index.d.ts +12 -4
  229. package/build/src/task/index.js +97 -16
  230. package/build/src/task/mutation-result.d.ts +923 -0
  231. package/build/src/task/mutation-result.js +140 -0
  232. package/build/src/task/mutation.d.ts +552 -28
  233. package/build/src/task/mutation.js +304 -97
  234. package/build/src/task/operations.d.ts +1 -4
  235. package/build/src/task/operations.js +0 -6
  236. package/build/src/task/public.d.ts +2 -0
  237. package/build/src/task/public.js +1 -0
  238. package/build/src/workspace-place.d.ts +7 -2
  239. package/build/src/workspace-place.js +34 -24
  240. package/build/src/world.d.ts +1 -0
  241. package/build/src/world.js +11 -0
  242. package/package.json +8 -6
  243. package/build/src/akuma/request-execution.d.ts +0 -8
  244. package/build/src/akuma/request-execution.js +0 -98
@@ -1,5 +1,5 @@
1
1
  import { LineRpcProcess } from "../../../runtime/proc/line-rpc.js";
2
- import { AgentEventChannel, AKUMA_REQUESTS_ENV, } from "../../provider.js";
2
+ import { AgentEventChannel, AKUMA_REQUESTS_ENV, createProviderAttempt, } from "../../provider.js";
3
3
  import { codexNotificationResult, codexObject, codexText } from "./events.js";
4
4
  export { CODEX_ITEM_DISPOSITIONS, CODEX_NOTIFICATION_DISPOSITIONS } from "./events.js";
5
5
  function diagnostic(error) {
@@ -97,7 +97,7 @@ async function admitTurn(server, input, state, events, config) {
97
97
  sandboxPolicy: sandbox(input.cwd, input.options, input.requests),
98
98
  }));
99
99
  }
100
- async function forkCodex(execution, input) {
100
+ async function forkCodex(execution, input, custody) {
101
101
  if (!("sessionId" in input.session))
102
102
  throw new Error("Codex app-server fork requires sessionId");
103
103
  const server = new LineRpcProcess({
@@ -105,6 +105,12 @@ async function forkCodex(execution, input) {
105
105
  cwd: input.cwd,
106
106
  ...(execution.env === undefined ? {} : { env: { ...process.env, ...execution.env } }),
107
107
  });
108
+ const closed = new Promise((resolve) => server.onExit(() => resolve()));
109
+ custody.own({
110
+ closed,
111
+ abort: async () => await server.close(true),
112
+ forceDispose: async () => await server.close(true),
113
+ });
108
114
  try {
109
115
  await initialize(server);
110
116
  const child = threadId(await server.request("thread/fork", {
@@ -139,12 +145,18 @@ function codexServer(execution, input) {
139
145
  }),
140
146
  });
141
147
  }
142
- async function startCodex(execution, input) {
148
+ async function startCodex(execution, input, custody) {
143
149
  const signal = input.signal ?? new AbortController().signal;
144
150
  if (input.session.kind === "resume" && !("sessionId" in input.session.coordinate))
145
151
  throw new Error("Codex app-server resume requires sessionId");
146
152
  const events = new AgentEventChannel();
147
153
  const server = codexServer(execution, input);
154
+ const closed = new Promise((resolve) => server.onExit(() => resolve()));
155
+ custody.own({
156
+ closed,
157
+ abort: async () => await server.close(true),
158
+ forceDispose: async () => await server.close(true),
159
+ });
148
160
  const state = { settled: false, tools: new Map() };
149
161
  let settle;
150
162
  const completion = new Promise((resolve) => {
@@ -220,8 +232,8 @@ export function createCodexAppServerProvider(input = "codex") {
220
232
  ...(options.readonly === undefined ? {} : { readonly: { enforcement: "native" } }),
221
233
  };
222
234
  },
223
- fork: (input) => forkCodex(execution, input),
224
- start: (input) => startCodex(execution, input),
225
- resume: (input) => startCodex(execution, input),
235
+ fork: (input) => createProviderAttempt(new AbortController().signal, async (custody) => await forkCodex(execution, input, custody)),
236
+ start: (input) => createProviderAttempt(input.signal, async (custody) => await startCodex(execution, { ...input, signal: custody.signal }, custody)),
237
+ resume: (input) => createProviderAttempt(input.signal, async (custody) => await startCodex(execution, { ...input, signal: custody.signal }, custody)),
226
238
  };
227
239
  }
@@ -1,3 +1,4 @@
1
+ import { createProviderAttempt } from "../../provider.js";
1
2
  import { startAcpSession, } from "../acp/core.js";
2
3
  const INTERJECT_METHOD = "x.ai/interject";
3
4
  function nonblank(value) {
@@ -131,7 +132,7 @@ function withInterject(live) {
131
132
  export function createGrokBuildProvider(execution, dependencies = {}) {
132
133
  if (execution.executable === undefined)
133
134
  throw new TypeError("Grok Build provider execution requires executable");
134
- const drive = async (input) => {
135
+ const drive = async (input, custody) => {
135
136
  const launch = {
136
137
  argv: argv(execution, input.options),
137
138
  ...(execution.env === undefined ? {} : { env: execution.env }),
@@ -140,11 +141,11 @@ export function createGrokBuildProvider(execution, dependencies = {}) {
140
141
  ...dependencies,
141
142
  interpretTool: interpretGrokTool,
142
143
  ...grokSessionMeta(execution.config, input.options),
143
- }));
144
+ }, custody));
144
145
  };
145
146
  return {
146
147
  admitOptions: optionAdmission,
147
- start: async (input) => await drive(input),
148
- resume: async (input) => await drive(input),
148
+ start: (input) => createProviderAttempt(input.signal, async (custody) => await drive({ ...input, signal: custody.signal }, custody)),
149
+ resume: (input) => createProviderAttempt(input.signal, async (custody) => await drive({ ...input, signal: custody.signal }, custody)),
149
150
  };
150
151
  }
@@ -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;
@@ -68,20 +68,14 @@ async function piCreateOptions(sdk, input) {
68
68
  ...(customTools === undefined ? {} : { customTools }),
69
69
  };
70
70
  }
71
- async function createPiSession(sdk, execution, input, signal) {
71
+ async function createPiSession(sdk, execution, input) {
72
72
  const setup = piCreateOptions(sdk, input).then(async (options) => {
73
73
  if (execution.config !== undefined) {
74
74
  throw new TypeError("Pi provider config cannot be consumed by native CreateAgentSessionOptions");
75
75
  }
76
76
  return await sdk.createAgentSession(options);
77
77
  });
78
- return await abortable(setup, signal, (created) => created.session.dispose());
79
- }
80
- function forceDisposePi(dispose, settle, setAborting) {
81
- setAborting();
82
- dispose();
83
- settle({ kind: "failed", diagnostic: "Pi session force-disposed" });
84
- return Promise.resolve();
78
+ return await setup;
85
79
  }
86
80
  async function runPiPrompt(native, input, events, state, settle) {
87
81
  try {
@@ -109,60 +103,81 @@ async function runPiPrompt(native, input, events, state, settle) {
109
103
  : { kind: "failed", diagnostic: diagnostic(error) });
110
104
  }
111
105
  }
112
- async function drivePi(sdk, execution, input, signal) {
113
- const created = await createPiSession(sdk, execution, input, signal);
106
+ async function drivePi(sdk, execution, input, signal, custody) {
107
+ const created = await createPiSession(sdk, execution, input);
114
108
  const native = created.session;
115
- if (signal.aborted) {
116
- native.dispose();
117
- signal.throwIfAborted();
118
- }
119
- if (native.sessionFile === undefined || native.sessionFile.trim().length === 0) {
120
- native.dispose();
121
- throw new Error("Pi session admitted without sessionFile");
122
- }
109
+ let settleRetired;
110
+ let failRetired;
111
+ const closed = new Promise((resolve, reject) => {
112
+ settleRetired = resolve;
113
+ failRetired = reject;
114
+ });
123
115
  const events = new AgentEventChannel();
124
116
  const eventState = { answer: "", assistantSeen: false, tools: new Map() };
125
117
  const state = {
126
118
  terminalFailure: null,
127
- disposed: false,
128
119
  aborting: false,
129
120
  settled: false,
130
121
  };
131
- const dispose = () => {
132
- if (state.disposed)
133
- return;
134
- state.disposed = true;
122
+ let settleCompletion;
123
+ const completion = new Promise((resolve) => {
124
+ settleCompletion = resolve;
125
+ });
126
+ const unsubscribe = native.subscribe((event) => {
127
+ if (event.type === "agent_end" && !event.willRetry)
128
+ state.terminalFailure = piTerminalFailure(event.messages);
129
+ for (const translated of translatePiEvent(event, eventState))
130
+ events.emit(translated);
131
+ });
132
+ const settle = (result) => {
133
+ if (state.settled)
134
+ return closed;
135
+ state.settled = true;
136
+ let failure;
135
137
  try {
136
138
  unsubscribe();
137
139
  }
140
+ catch (error) {
141
+ failure = error;
142
+ }
138
143
  finally {
139
144
  try {
140
- native.dispose();
145
+ events.end();
141
146
  }
142
147
  finally {
143
- events.end();
148
+ settleCompletion(result);
149
+ try {
150
+ native.dispose();
151
+ }
152
+ catch (error) {
153
+ failure ??= error;
154
+ }
144
155
  }
145
156
  }
157
+ if (failure === undefined)
158
+ settleRetired();
159
+ else
160
+ failRetired(failure);
161
+ return closed;
146
162
  };
147
- const unsubscribe = native.subscribe((event) => {
148
- if (event.type === "agent_end" && !event.willRetry)
149
- state.terminalFailure = piTerminalFailure(event.messages);
150
- for (const translated of translatePiEvent(event, eventState))
151
- events.emit(translated);
152
- });
163
+ const forceDispose = async () => {
164
+ if (!state.settled)
165
+ state.aborting = true;
166
+ await settle({ kind: "failed", diagnostic: "Pi session force-disposed" });
167
+ };
168
+ custody?.own({ closed, abort: forceDispose, forceDispose });
169
+ if (signal.aborted) {
170
+ await settle({ kind: "failed", diagnostic: "Pi session aborted" });
171
+ signal.throwIfAborted();
172
+ }
173
+ if (native.sessionFile === undefined || native.sessionFile.trim().length === 0) {
174
+ await settle({ kind: "failed", diagnostic: "Pi session admitted without sessionFile" });
175
+ throw new Error("Pi session admitted without sessionFile");
176
+ }
153
177
  events.emit({ type: "session", coordinate: { sessionFile: native.sessionFile, sessionId: native.sessionId } });
154
- let settleCompletion;
155
- const completion = new Promise((resolve) => {
156
- settleCompletion = resolve;
178
+ void runPiPrompt(native, input, eventState, state, (result) => {
179
+ void settle(result);
157
180
  });
158
- const settle = (result) => {
159
- if (state.settled)
160
- return;
161
- state.settled = true;
162
- dispose();
163
- settleCompletion(result);
164
- };
165
- void runPiPrompt(native, input, eventState, state, settle);
166
181
  return {
167
182
  admission: { fence: native.sessionId },
168
183
  events,
@@ -178,13 +193,11 @@ async function drivePi(sdk, execution, input, signal) {
178
193
  catch {
179
194
  /* prompt settlement still proves local cleanup */
180
195
  }
181
- settle({ kind: "failed", diagnostic: "Pi session aborted" });
196
+ await settle({ kind: "failed", diagnostic: "Pi session aborted" });
182
197
  })();
183
198
  return state.abortRequest;
184
199
  },
185
- forceDispose: () => forceDisposePi(dispose, settle, () => {
186
- state.aborting = true;
187
- }),
200
+ forceDispose,
188
201
  };
189
202
  }
190
203
  function piSessionFile(coordinate) {
@@ -216,21 +229,22 @@ export function createPiProvider(execution = { name: "pi", kind: "pi" }, load =
216
229
  if (execution.env !== undefined && Object.keys(execution.env).length > 0) {
217
230
  throw new TypeError("env injection not supported for provider pi");
218
231
  }
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);
232
+ const drive = async (input, custody) => {
233
+ custody.signal.throwIfAborted();
234
+ const sdk = await abortable(load(), custody.signal);
235
+ custody.signal.throwIfAborted();
236
+ return await drivePi(sdk, execution, { ...input, signal: custody.signal }, custody.signal, custody);
223
237
  };
224
238
  return {
225
239
  admitOptions: admitPiOptions,
226
- start: drive,
227
- resume: async (input) => {
240
+ start: (input) => createProviderAttempt(input.signal, async (custody) => await drive(input, custody)),
241
+ resume: (input) => createProviderAttempt(input.signal, async (custody) => {
228
242
  piSessionFile(input.session.coordinate);
229
- return drive(input);
230
- },
231
- fork: async (input) => {
243
+ return await drive(input, custody);
244
+ }),
245
+ fork: (input) => createProviderAttempt(new AbortController().signal, async () => {
232
246
  piSessionFile(input.session);
233
- return forkPi(await load(), input);
234
- },
247
+ return await forkPi(await load(), input);
248
+ }),
235
249
  };
236
250
  }
@@ -1,28 +1,22 @@
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
- }>) => Promise<void>;
19
+ }>) => Promise<boolean>;
26
20
  export declare class BodyRequestPump {
27
21
  private readonly input;
28
22
  private readonly serveClaim;
@@ -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,14 +45,14 @@ 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);
52
52
  continue;
53
53
  }
54
54
  if (!this.admissionClosed) {
55
- await this.serveClaim({
55
+ const served = await this.serveClaim({
56
56
  directory: this.directory,
57
57
  transportId,
58
58
  claim,
@@ -60,6 +60,8 @@ export class BodyRequestPump {
60
60
  signal: this.executionSignal.signal,
61
61
  admissionOpen: () => !this.admissionClosed,
62
62
  });
63
+ if (!served)
64
+ await rm(path, { force: true });
63
65
  }
64
66
  this.handled.add(transportId);
65
67
  }
@@ -111,7 +113,7 @@ async function settleReservedSoul(paths, parent, request, soul) {
111
113
  : voidRequest(paths, request.id, "reserved child origin does not match the request"));
112
114
  }
113
115
  async function settleReserved(paths, parent, request, now, signal) {
114
- const childPaths = pathsForAkuId(request.world, request.child);
116
+ const childPaths = pathsForAkuId(worldRootForAkumaPaths(paths), request.child);
115
117
  const deadline = performance.now() + BIRTH_TIMEOUT_MS;
116
118
  for (;;) {
117
119
  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
+ }>>;