@astrosheep/keiyaku 4.0.2 → 4.0.4

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 (145) hide show
  1. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  2. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +1 -1
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +1 -1
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +5 -0
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +3 -1
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +9 -8
  9. package/build/src/akuma/akuma.d.ts +34 -11
  10. package/build/src/akuma/akuma.js +121 -61
  11. package/build/src/akuma/allowed.d.ts +7 -0
  12. package/build/src/akuma/allowed.js +40 -0
  13. package/build/src/akuma/archetype.d.ts +7 -4
  14. package/build/src/akuma/archetype.js +15 -16
  15. package/build/src/akuma/body.d.ts +3 -1
  16. package/build/src/akuma/body.js +19 -8
  17. package/build/src/akuma/heart/facts.d.ts +49 -7
  18. package/build/src/akuma/heart/index.d.ts +4 -8
  19. package/build/src/akuma/heart/index.js +76 -15
  20. package/build/src/akuma/heart/rows.d.ts +7 -5
  21. package/build/src/akuma/heart/rows.js +98 -11
  22. package/build/src/akuma/heart/schema.d.ts +1 -1
  23. package/build/src/akuma/heart/schema.js +15 -10
  24. package/build/src/akuma/heart/soul.js +3 -1
  25. package/build/src/akuma/heart/storage.d.ts +2 -0
  26. package/build/src/akuma/heart/storage.js +20 -0
  27. package/build/src/akuma/heart/timeline.d.ts +1 -5
  28. package/build/src/akuma/heart/timeline.js +8 -17
  29. package/build/src/akuma/index.d.ts +4 -1
  30. package/build/src/akuma/index.js +1 -0
  31. package/build/src/akuma/projection.d.ts +17 -3
  32. package/build/src/akuma/projection.js +40 -3
  33. package/build/src/akuma/provider.d.ts +1 -1
  34. package/build/src/akuma/provider.js +2 -1
  35. package/build/src/akuma/providers/acp/core.d.ts +3 -1
  36. package/build/src/akuma/providers/acp/core.js +4 -4
  37. package/build/src/akuma/providers/acp/events.d.ts +11 -3
  38. package/build/src/akuma/providers/acp/events.js +78 -4
  39. package/build/src/akuma/providers/claude/events.js +62 -3
  40. package/build/src/akuma/providers/codex-app-server/events.js +1 -13
  41. package/build/src/akuma/providers/grok-build/index.d.ts +2 -1
  42. package/build/src/akuma/providers/grok-build/index.js +72 -2
  43. package/build/src/akuma/providers/opencode-sdk/events.js +71 -6
  44. package/build/src/akuma/providers/pi/events.js +17 -3
  45. package/build/src/akuma/providers/unified-patch.d.ts +4 -0
  46. package/build/src/akuma/providers/unified-patch.js +14 -0
  47. package/build/src/akuma/publication.d.ts +1 -1
  48. package/build/src/akuma/publication.js +36 -7
  49. package/build/src/akuma/requests.d.ts +70 -4
  50. package/build/src/akuma/requests.js +288 -77
  51. package/build/src/akuma-body.d.ts +1 -0
  52. package/build/src/akuma-body.js +31 -0
  53. package/build/src/cli/accepted.d.ts +4 -1
  54. package/build/src/cli/accepted.js +26 -4
  55. package/build/src/cli/commands/akuma-invoke.d.ts +8 -7
  56. package/build/src/cli/commands/akuma-invoke.js +18 -10
  57. package/build/src/cli/commands/akuma.d.ts +11 -2
  58. package/build/src/cli/commands/akuma.js +67 -28
  59. package/build/src/cli/commands/amend.d.ts +1 -1
  60. package/build/src/cli/commands/amend.js +1 -1
  61. package/build/src/cli/commands/contract.d.ts +3 -3
  62. package/build/src/cli/commands/contract.js +1 -1
  63. package/build/src/cli/commands/install.js +4 -4
  64. package/build/src/cli/commands/task-query.d.ts +1 -1
  65. package/build/src/cli/commands/task-query.js +11 -5
  66. package/build/src/cli/commands/task.js +4 -4
  67. package/build/src/cli/coordinates.d.ts +1 -0
  68. package/build/src/cli/coordinates.js +5 -1
  69. package/build/src/cli/invoke.d.ts +1 -0
  70. package/build/src/cli/invoke.js +90 -73
  71. package/build/src/cli/main.d.ts +2 -0
  72. package/build/src/cli/main.js +29 -4
  73. package/build/src/cli/parse.d.ts +1 -0
  74. package/build/src/cli/parse.js +8 -4
  75. package/build/src/cli/render/akuma.d.ts +2 -2
  76. package/build/src/cli/render/akuma.js +84 -13
  77. package/build/src/cli/render/audit.js +16 -2
  78. package/build/src/cli/render/contract.d.ts +2 -0
  79. package/build/src/cli/render/contract.js +203 -36
  80. package/build/src/cli/render/kanshi.js +64 -29
  81. package/build/src/cli/render/receipt.d.ts +2 -1
  82. package/build/src/cli/render/receipt.js +9 -1
  83. package/build/src/cli/render/text.js +3 -1
  84. package/build/src/cli/result.d.ts +38 -3
  85. package/build/src/cli/selectors.d.ts +1 -0
  86. package/build/src/cli/selectors.js +9 -6
  87. package/build/src/cli/usage.d.ts +2 -0
  88. package/build/src/cli/usage.js +4 -0
  89. package/build/src/contract-worktree.js +3 -0
  90. package/build/src/dispatch/index.js +50 -39
  91. package/build/src/git/hooks.d.ts +0 -4
  92. package/build/src/git/hooks.js +0 -36
  93. package/build/src/git/integration.d.ts +2 -6
  94. package/build/src/git/integration.js +8 -8
  95. package/build/src/git/read-observation.js +1 -0
  96. package/build/src/git/reconcile.d.ts +6 -1
  97. package/build/src/git/reconcile.js +55 -9
  98. package/build/src/git/tender.d.ts +19 -4
  99. package/build/src/git/tender.js +44 -13
  100. package/build/src/git/terminal-seal.d.ts +14 -0
  101. package/build/src/git/terminal-seal.js +15 -2
  102. package/build/src/git/workspace.d.ts +1 -0
  103. package/build/src/git/workspace.js +12 -4
  104. package/build/src/index.d.ts +1 -1
  105. package/build/src/kanshi/index.d.ts +1 -1
  106. package/build/src/kanshi/index.js +1 -1
  107. package/build/src/kanshi/read.d.ts +9 -1
  108. package/build/src/kanshi/read.js +91 -37
  109. package/build/src/kanshi/report.d.ts +1 -0
  110. package/build/src/library/address.d.ts +5 -14
  111. package/build/src/library/address.js +20 -22
  112. package/build/src/library/akuma-creation.d.ts +4 -2
  113. package/build/src/library/akuma-creation.js +43 -26
  114. package/build/src/library/bind.js +3 -3
  115. package/build/src/library/catalog.d.ts +1 -2
  116. package/build/src/library/catalog.js +11 -6
  117. package/build/src/library/configuration.d.ts +4 -0
  118. package/build/src/library/configuration.js +19 -0
  119. package/build/src/library/contract.d.ts +15 -1
  120. package/build/src/library/contract.js +51 -4
  121. package/build/src/library/fleet.d.ts +47 -9
  122. package/build/src/library/fleet.js +184 -44
  123. package/build/src/library/input.js +1 -0
  124. package/build/src/library/keiyaku.d.ts +4 -4
  125. package/build/src/library/keiyaku.js +1 -2
  126. package/build/src/protocol/bind.d.ts +1 -2
  127. package/build/src/protocol/bind.js +2 -16
  128. package/build/src/protocol/operations.d.ts +4 -1
  129. package/build/src/protocol/operations.js +33 -27
  130. package/build/src/protocol/read/status.d.ts +3 -1
  131. package/build/src/protocol/read/status.js +24 -14
  132. package/build/src/runtime/proc/run.d.ts +1 -0
  133. package/build/src/runtime/proc/run.js +9 -2
  134. package/build/src/settlement/settle.d.ts +1 -0
  135. package/build/src/settlement/settle.js +4 -1
  136. package/build/src/task/board.d.ts +18 -3
  137. package/build/src/task/board.js +73 -26
  138. package/build/src/task/index.d.ts +3 -3
  139. package/build/src/task/index.js +7 -5
  140. package/build/src/task/operations.d.ts +2 -19
  141. package/build/src/task/operations.js +29 -7
  142. package/build/src/task/query.d.ts +9 -2
  143. package/build/src/task/query.js +32 -64
  144. package/build/src/verification/execution.js +2 -1
  145. package/package.json +3 -1
@@ -1,13 +1,14 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { access, mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
3
3
  import { isAbsolute, join, resolve } from "node:path";
4
- import { HeldAkumaLeash, admitRequest, readNonterminalRequests, readRequest, readSoul, refuseRequest, reserveRequest, serveRequest, voidRequest, } from "./heart/index.js";
4
+ import { HeldAkumaLeash, admitRequest, readNonterminalRequests, readRequest, readSoul, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, voidRequest, } from "./heart/index.js";
5
5
  import { parseAkuId, pathsForAkuId, archetypeName, worldRootForAkumaPaths, } from "./identity.js";
6
6
  import { BIRTH_TIMEOUT_MS, publishAkuma } from "./publication.js";
7
7
  import { abortableDelay } from "./abort.js";
8
8
  import { AKUMA_REQUESTS_ENV } from "./provider.js";
9
9
  import { decodeProviderOptions, decodeReadonlyRestraint } from "./provider-recipe.js";
10
10
  import { resolveProviderExecution } from "./providers/index.js";
11
+ import { decodeAllowedActions } from "./allowed.js";
11
12
  const POLL_MS = 25;
12
13
  const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
13
14
  export class AkumaBodyRequestError extends Error {
@@ -36,24 +37,13 @@ function exactKeys(value, expected) {
36
37
  function absolute(value) {
37
38
  return typeof value === "string" && isAbsolute(value) && resolve(value) === value;
38
39
  }
39
- function matchingConfinement(value, expected) {
40
- const confinement = object(value);
41
- if (confinement === null || confinement.kind !== expected.kind)
42
- return false;
43
- if (expected.kind === "unconfined")
44
- return exactKeys(confinement, ["kind"]);
45
- if (!exactKeys(confinement, ["kind", "writableRoots"]) || !Array.isArray(confinement.writableRoots))
46
- return false;
47
- return confinement.writableRoots.length === expected.writableRoots.length
48
- && confinement.writableRoots.every((root, index) => root === expected.writableRoots[index]);
49
- }
50
40
  function decodeRecipe(value, cwd) {
51
41
  const recipe = object(value);
52
42
  if (recipe === null)
53
43
  return null;
54
44
  const expectedKeys = [
55
- "confinement",
56
45
  ...(recipe.description === undefined ? [] : ["description"]),
46
+ "allowed",
57
47
  "options",
58
48
  "provider",
59
49
  ...(recipe.readonly === undefined ? [] : ["readonly"]),
@@ -72,10 +62,9 @@ function decodeRecipe(value, cwd) {
72
62
  if ((decodedOptions.readonly === true) !== (readonly !== undefined))
73
63
  return null;
74
64
  const confinement = adapter.confinement({ cwd, options: decodedOptions });
75
- if (!matchingConfinement(recipe.confinement, confinement))
76
- return null;
77
65
  return Object.freeze({
78
66
  ...(recipe.description === undefined ? {} : { description: recipe.description }),
67
+ allowed: decodeAllowedActions(recipe.allowed),
79
68
  provider,
80
69
  options: decodedOptions,
81
70
  ...(readonly === undefined ? {} : { readonly }),
@@ -86,52 +75,98 @@ function decodeRecipe(value, cwd) {
86
75
  return null;
87
76
  }
88
77
  }
89
- function decodeClaim(bytes, fileId) {
90
- let decoded;
78
+ function canonicalAkuId(value) {
79
+ if (typeof value !== "string")
80
+ return null;
91
81
  try {
92
- decoded = JSON.parse(bytes);
82
+ const id = parseAkuId(value).id;
83
+ return id === value ? id : null;
93
84
  }
94
85
  catch {
95
86
  return null;
96
87
  }
97
- const value = object(decoded);
98
- if (value === null)
88
+ }
89
+ function canonicalTargets(value) {
90
+ if (!Array.isArray(value) || value.length === 0)
99
91
  return null;
92
+ const ids = [];
93
+ for (const item of value) {
94
+ const id = canonicalAkuId(item);
95
+ if (id === null)
96
+ return null;
97
+ if (ids.length > 0 && Buffer.compare(Buffer.from(ids.at(-1)), Buffer.from(id)) >= 0)
98
+ return null;
99
+ ids.push(id);
100
+ }
101
+ return ids;
102
+ }
103
+ function decodeCallClaim(id, payload) {
100
104
  const expected = [
101
105
  "archetype",
102
106
  "body",
103
- ...(value.cwd === undefined ? [] : ["cwd"]),
104
- "id",
107
+ ...(payload.cwd === undefined ? [] : ["cwd"]),
105
108
  "recipe",
106
109
  "world",
107
110
  ];
108
- if (!exactKeys(value, expected))
111
+ if (!exactKeys(payload, expected)
112
+ || typeof payload.body !== "string"
113
+ || !absolute(payload.world))
109
114
  return null;
110
- if (value.id !== fileId || typeof value.id !== "string" || !UUID.test(value.id))
111
- return null;
112
- if (typeof value.body !== "string" || !absolute(value.world))
113
- return null;
114
- if (value.cwd !== undefined && !absolute(value.cwd))
115
+ if (payload.cwd !== undefined && !absolute(payload.cwd))
115
116
  return null;
116
117
  try {
117
- archetypeName(value.archetype);
118
+ archetypeName(payload.archetype);
118
119
  }
119
120
  catch {
120
121
  return null;
121
122
  }
122
- const recipe = decodeRecipe(value.recipe, (value.cwd ?? value.world));
123
- if (recipe === null)
123
+ return {
124
+ id,
125
+ action: "akuma.call",
126
+ world: payload.world,
127
+ archetype: payload.archetype,
128
+ body: payload.body,
129
+ recipe: payload.recipe,
130
+ ...(payload.cwd === undefined ? {} : { cwd: payload.cwd }),
131
+ };
132
+ }
133
+ function decodeWaitClaim(id, payload) {
134
+ const expected = [
135
+ "completion",
136
+ "targets",
137
+ ...(payload.timeoutMs === undefined ? [] : ["timeoutMs"]),
138
+ ];
139
+ const targets = canonicalTargets(payload.targets);
140
+ if (!exactKeys(payload, expected)
141
+ || targets === null
142
+ || (payload.completion !== "any" && payload.completion !== "all"))
143
+ return null;
144
+ if (payload.timeoutMs !== undefined
145
+ && (!Number.isSafeInteger(payload.timeoutMs) || payload.timeoutMs < 0))
124
146
  return null;
125
147
  return {
126
- id: value.id,
127
- world: value.world,
128
- archetype: value.archetype,
129
- body: value.body,
130
- recipe,
131
- ...(value.cwd === undefined ? {} : { cwd: value.cwd }),
148
+ id,
149
+ action: "akuma.wait",
150
+ targets,
151
+ completion: payload.completion,
152
+ ...(payload.timeoutMs === undefined ? {} : { timeoutMs: payload.timeoutMs }),
132
153
  };
133
154
  }
134
- function decodeReceipt(bytes, requestId) {
155
+ function decodeTellClaim(id, payload) {
156
+ const target = canonicalAkuId(payload.target);
157
+ if (!exactKeys(payload, ["body", "target"])
158
+ || target === null
159
+ || typeof payload.body !== "string")
160
+ return null;
161
+ return { id, action: "akuma.tell", target, body: payload.body };
162
+ }
163
+ function decodeKillClaim(id, payload) {
164
+ const targets = canonicalTargets(payload.targets);
165
+ return exactKeys(payload, ["targets"]) && targets !== null
166
+ ? { id, action: "akuma.kill", targets }
167
+ : null;
168
+ }
169
+ function decodeClaim(bytes, fileId) {
135
170
  let decoded;
136
171
  try {
137
172
  decoded = JSON.parse(bytes);
@@ -140,23 +175,82 @@ function decodeReceipt(bytes, requestId) {
140
175
  return null;
141
176
  }
142
177
  const value = object(decoded);
143
- if (value === null || value.id !== requestId)
178
+ if (value === null
179
+ || !exactKeys(value, ["action", "id", "payload"])
180
+ || value.id !== fileId
181
+ || typeof value.id !== "string"
182
+ || !UUID.test(value.id))
144
183
  return null;
145
- if (value.state === "served" && exactKeys(value, ["child", "id", "state"])) {
146
- try {
147
- return { id: requestId, state: "served", child: parseAkuId(value.child).id };
148
- }
149
- catch {
150
- return null;
151
- }
184
+ const payload = object(value.payload);
185
+ if (payload === null)
186
+ return null;
187
+ if (value.action === "akuma.call")
188
+ return decodeCallClaim(value.id, payload);
189
+ if (value.action === "akuma.wait")
190
+ return decodeWaitClaim(value.id, payload);
191
+ if (value.action === "akuma.tell")
192
+ return decodeTellClaim(value.id, payload);
193
+ if (value.action === "akuma.kill")
194
+ return decodeKillClaim(value.id, payload);
195
+ return null;
196
+ }
197
+ function decodeUpstreamFailure(value) {
198
+ const failure = object(value);
199
+ if (failure === null)
200
+ return null;
201
+ if (failure.kind === "akuma-not-born" && exactKeys(failure, ["id", "kind"])) {
202
+ const id = canonicalAkuId(failure.id);
203
+ return id === null ? null : { kind: failure.kind, id };
204
+ }
205
+ if (failure.kind === "failed"
206
+ && exactKeys(failure, ["diagnostic", "kind"])
207
+ && typeof failure.diagnostic === "string") {
208
+ return { kind: failure.kind, diagnostic: failure.diagnostic };
209
+ }
210
+ return null;
211
+ }
212
+ function decodeUpstreamOutcome(value) {
213
+ const outcome = object(value);
214
+ if (outcome?.kind === "returned" && exactKeys(outcome, ["kind", "result"])) {
215
+ return { kind: outcome.kind, result: outcome.result };
216
+ }
217
+ if (outcome?.kind !== "failed" || !exactKeys(outcome, ["failure", "kind"]))
218
+ return null;
219
+ const failure = decodeUpstreamFailure(outcome.failure);
220
+ return failure === null ? null : { kind: outcome.kind, failure };
221
+ }
222
+ function decodeReceipt(bytes, requestId, action) {
223
+ let decoded;
224
+ try {
225
+ decoded = JSON.parse(bytes);
226
+ }
227
+ catch {
228
+ return null;
229
+ }
230
+ const value = object(decoded);
231
+ if (value === null || value.id !== requestId || value.action !== action)
232
+ return null;
233
+ if (action === "akuma.call"
234
+ && value.state === "served"
235
+ && exactKeys(value, ["action", "child", "id", "state"])) {
236
+ const child = canonicalAkuId(value.child);
237
+ return child === null ? null : { id: requestId, action, state: value.state, child };
238
+ }
239
+ if (action !== "akuma.call"
240
+ && value.state === "served"
241
+ && exactKeys(value, ["action", "id", "outcome", "state"])) {
242
+ const outcome = decodeUpstreamOutcome(value.outcome);
243
+ return outcome === null ? null : { id: requestId, action, state: value.state, outcome };
152
244
  }
153
- if (value.state === "refused" && exactKeys(value, ["diagnostic", "id", "state"])
245
+ if (value.state === "refused"
246
+ && exactKeys(value, ["action", "diagnostic", "id", "state"])
154
247
  && typeof value.diagnostic === "string") {
155
- return { id: requestId, state: "refused", diagnostic: value.diagnostic };
248
+ return { id: requestId, action, state: value.state, diagnostic: value.diagnostic };
156
249
  }
157
- if (value.state === "voided" && exactKeys(value, ["evidence", "id", "state"])
250
+ if (value.state === "voided"
251
+ && exactKeys(value, ["action", "evidence", "id", "state"])
158
252
  && typeof value.evidence === "string") {
159
- return { id: requestId, state: "voided", evidence: value.evidence };
253
+ return { id: requestId, action, state: value.state, evidence: value.evidence };
160
254
  }
161
255
  return null;
162
256
  }
@@ -171,16 +265,22 @@ async function atomicJson(path, value) {
171
265
  }
172
266
  }
173
267
  function receiptFor(fact) {
174
- if (fact.state === "served")
175
- return { id: fact.id, state: fact.state, child: fact.child };
176
- if (fact.state === "refused")
177
- return { id: fact.id, state: fact.state, diagnostic: fact.diagnostic };
268
+ if (fact.action === "akuma.call" && fact.state === "served") {
269
+ return { id: fact.id, action: fact.action, state: fact.state, child: fact.child };
270
+ }
271
+ if (fact.state === "refused") {
272
+ return { id: fact.id, action: fact.action, state: fact.state, diagnostic: fact.diagnostic };
273
+ }
178
274
  if (fact.state === "voided")
179
- return { id: fact.id, state: fact.state, evidence: fact.evidence };
275
+ return { id: fact.id, action: fact.action, state: fact.state, evidence: fact.evidence };
180
276
  return null;
181
277
  }
182
- async function projectReceipt(directory, fact) {
183
- const receipt = receiptFor(fact);
278
+ async function projectReceipt(directory, fact, outcome) {
279
+ const receipt = outcome === undefined
280
+ ? receiptFor(fact)
281
+ : fact.action === "akuma.call" || fact.state !== "served"
282
+ ? null
283
+ : { id: fact.id, action: fact.action, state: fact.state, outcome };
184
284
  if (receipt !== null)
185
285
  await atomicJson(join(directory, `${fact.id}.receipt.json`), receipt);
186
286
  }
@@ -192,45 +292,74 @@ export function injectedBodyRequests() {
192
292
  throw new Error(`${AKUMA_REQUESTS_ENV} must be an absolute normalized path`);
193
293
  return directory;
194
294
  }
195
- export async function requestBodyCall(input) {
196
- await atomicJson(join(input.directory, `${input.id}.request.json`), {
197
- id: input.id,
198
- world: input.world,
199
- archetype: input.archetype,
200
- body: input.body,
201
- recipe: input.recipe,
202
- ...(input.cwd === undefined ? {} : { cwd: input.cwd }),
203
- });
204
- const receiptPath = join(input.directory, `${input.id}.receipt.json`);
295
+ async function requestBody(input) {
296
+ const { id, action, ...payload } = input.claim;
297
+ await atomicJson(join(input.directory, `${id}.request.json`), { id, action, payload });
298
+ const receiptPath = join(input.directory, `${id}.receipt.json`);
205
299
  for (;;) {
206
300
  try {
207
- const receipt = decodeReceipt(await readFile(receiptPath, "utf8"), input.id);
301
+ const receipt = decodeReceipt(await readFile(receiptPath, "utf8"), id, action);
208
302
  if (receipt === null)
209
- throw new Error(`Akuma body request ${input.id} has an invalid receipt`);
210
- if (receipt.state === "served")
211
- return receipt.child;
303
+ throw new Error(`Akuma body request ${id} has an invalid receipt`);
212
304
  if (receipt.state === "refused")
213
305
  throw new AkumaBodyRequestError("refused", receipt.diagnostic);
214
- throw new AkumaBodyRequestError("voided", receipt.evidence);
306
+ if (receipt.state === "voided")
307
+ throw new AkumaBodyRequestError("voided", receipt.evidence);
308
+ return receipt;
215
309
  }
216
310
  catch (error) {
217
311
  if (error.code !== "ENOENT")
218
312
  throw error;
219
313
  }
314
+ if (!await access(input.directory).then(() => true, () => false)) {
315
+ throw new AkumaBodyRequestError("voided", "parent request channel closed before a receipt");
316
+ }
220
317
  await abortableDelay(POLL_MS);
221
318
  }
222
319
  }
223
- async function serveClaim(input) {
320
+ export async function requestBodyCall(input) {
321
+ const { directory, ...claim } = input;
322
+ const receipt = await requestBody({ directory, claim: { ...claim, action: "akuma.call" } });
323
+ if (receipt.action !== "akuma.call" || receipt.state !== "served") {
324
+ throw new Error(`Akuma body request ${input.id} returned the wrong action`);
325
+ }
326
+ return receipt.child;
327
+ }
328
+ async function requestUpstream(directory, claim) {
329
+ const receipt = await requestBody({ directory, claim });
330
+ if (receipt.action === "akuma.call" || receipt.state !== "served") {
331
+ throw new Error(`Akuma body request ${claim.id} returned the wrong action`);
332
+ }
333
+ return receipt.outcome;
334
+ }
335
+ export async function requestBodyWait(input) {
336
+ const { directory, id = randomUUID(), ...claim } = input;
337
+ return await requestUpstream(directory, { ...claim, id, action: "akuma.wait" });
338
+ }
339
+ export async function requestBodyTell(input) {
340
+ const { directory, id = randomUUID(), ...claim } = input;
341
+ return await requestUpstream(directory, { ...claim, id, action: "akuma.tell" });
342
+ }
343
+ export async function requestBodyKill(input) {
344
+ const { directory, id = randomUUID(), ...claim } = input;
345
+ return await requestUpstream(directory, { ...claim, id, action: "akuma.kill" });
346
+ }
347
+ async function serveCallClaim(input) {
224
348
  input.signal.throwIfAborted();
225
- let fact = await admitRequest(input.paths, { ...input.claim, admittedAt: input.now() });
349
+ const cwd = input.claim.cwd ?? input.parent.cwd;
350
+ const recipe = decodeRecipe(input.claim.recipe, cwd);
351
+ if (recipe === null)
352
+ return;
353
+ let fact = await admitRequest(input.paths, { ...input.claim, recipe, admittedAt: input.now() });
226
354
  const existing = receiptFor(fact);
227
355
  if (existing !== null) {
228
356
  if (!input.signal.aborted)
229
357
  await projectReceipt(input.directory, fact);
230
358
  return;
231
359
  }
232
- if (fact.state !== "admitted")
360
+ if (fact.action !== "akuma.call" || fact.state !== "admitted") {
233
361
  throw new Error(`Akuma request ${fact.id} cannot be replayed from ${fact.state}`);
362
+ }
234
363
  const request = fact;
235
364
  const servingWorld = worldRootForAkumaPaths(input.paths);
236
365
  if (request.world !== servingWorld) {
@@ -238,7 +367,6 @@ async function serveClaim(input) {
238
367
  await projectReceipt(input.directory, fact);
239
368
  return;
240
369
  }
241
- const cwd = resolve(request.cwd ?? servingWorld);
242
370
  let child;
243
371
  try {
244
372
  input.signal.throwIfAborted();
@@ -256,6 +384,7 @@ async function serveClaim(input) {
256
384
  provider: request.recipe.provider,
257
385
  options: request.recipe.options,
258
386
  ...(request.recipe.readonly === undefined ? {} : { readonly: request.recipe.readonly }),
387
+ allowed: request.recipe.allowed,
259
388
  cwd,
260
389
  origin: { kind: "request", parent: input.parent.id, requestId: request.id },
261
390
  confinement: request.recipe.confinement,
@@ -280,6 +409,87 @@ async function serveClaim(input) {
280
409
  fact = await serveRequest(input.paths, request.id, child);
281
410
  await projectReceipt(input.directory, fact);
282
411
  }
412
+ function upstreamFailure(error) {
413
+ const value = object(error);
414
+ if (value?.kind === "akuma-not-born") {
415
+ const id = canonicalAkuId(value.id);
416
+ if (id !== null)
417
+ return { kind: "akuma-not-born", id };
418
+ }
419
+ return { kind: "failed", diagnostic: diagnostic(error) };
420
+ }
421
+ async function serveUpstreamClaim(input) {
422
+ input.signal.throwIfAborted();
423
+ let fact = await admitRequest(input.paths, { ...input.claim, admittedAt: input.now() });
424
+ const existing = receiptFor(fact);
425
+ if (existing !== null) {
426
+ if (!input.signal.aborted)
427
+ await projectReceipt(input.directory, fact);
428
+ return;
429
+ }
430
+ if (fact.state === "served") {
431
+ await projectReceipt(input.directory, fact, {
432
+ kind: "failed",
433
+ failure: { kind: "failed", diagnostic: "served request receipt is no longer available" },
434
+ });
435
+ return;
436
+ }
437
+ if (fact.state !== "admitted" || fact.action === "akuma.call") {
438
+ throw new Error(`Akuma request ${fact.id} cannot be served from ${fact.state}`);
439
+ }
440
+ const request = fact;
441
+ let outcome;
442
+ let killService = [];
443
+ try {
444
+ if (input.upstream === undefined)
445
+ throw new Error("upstream execution port is unavailable");
446
+ let result;
447
+ if (request.action === "akuma.wait") {
448
+ const waitRequest = request;
449
+ result = await input.upstream.wait({
450
+ targets: waitRequest.targets,
451
+ completion: waitRequest.completion,
452
+ ...(waitRequest.timeoutMs === undefined ? {} : { timeoutMs: waitRequest.timeoutMs }),
453
+ signal: input.signal,
454
+ });
455
+ }
456
+ else if (request.action === "akuma.tell") {
457
+ const tellRequest = request;
458
+ result = await input.upstream.tell({
459
+ target: tellRequest.target,
460
+ body: tellRequest.body,
461
+ tellId: tellRequest.id,
462
+ recordedAt: tellRequest.admittedAt,
463
+ signal: input.signal,
464
+ });
465
+ }
466
+ else {
467
+ const killRequest = request;
468
+ const served = await input.upstream.kill({ targets: killRequest.targets, signal: input.signal });
469
+ killService = served.service;
470
+ result = served.result;
471
+ }
472
+ outcome = { kind: "returned", result };
473
+ }
474
+ catch (error) {
475
+ if (input.signal.aborted)
476
+ return;
477
+ outcome = { kind: "failed", failure: upstreamFailure(error) };
478
+ }
479
+ input.signal.throwIfAborted();
480
+ const service = request.action === "akuma.wait"
481
+ ? { action: request.action }
482
+ : request.action === "akuma.tell"
483
+ ? { action: request.action, target: request.target, tellId: request.id }
484
+ : { action: request.action, results: killService };
485
+ fact = await serveUpstreamRequest(input.paths, request.id, service);
486
+ await projectReceipt(input.directory, fact, outcome);
487
+ }
488
+ async function serveClaim(input) {
489
+ return input.claim.action === "akuma.call"
490
+ ? await serveCallClaim({ ...input, claim: input.claim })
491
+ : await serveUpstreamClaim({ ...input, claim: input.claim });
492
+ }
283
493
  async function requestFiles(directory) {
284
494
  try {
285
495
  return (await readdir(directory))
@@ -411,7 +621,8 @@ export async function settleBodyRequests(paths, parent, now, signal) {
411
621
  if (request.state === "admitted") {
412
622
  await voidRequest(paths, request.id, "body died before serving the request");
413
623
  }
414
- else if (request.state === "reserved" && !await settleReserved(paths, parent, request, now, signal))
624
+ else if (request.state === "reserved"
625
+ && !await settleReserved(paths, parent, request, now, signal))
415
626
  pending = true;
416
627
  }
417
628
  return pending ? "pending" : "settled";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ import { runAkumaBody } from "./akuma/body.js";
2
+ import { worldRootForAkumaPaths } from "./akuma/identity.js";
3
+ import { executeKillAkuma, executeTellAkuma, executeWaitAkuma, } from "./library/fleet.js";
4
+ function upstreamFor(launch) {
5
+ const path = worldRootForAkumaPaths(launch.paths);
6
+ return {
7
+ wait: async (input) => await executeWaitAkuma({
8
+ path,
9
+ ids: input.targets,
10
+ completion: input.completion,
11
+ ...(input.timeoutMs === undefined ? {} : { timeoutMs: input.timeoutMs }),
12
+ signal: input.signal,
13
+ }),
14
+ tell: async (input) => await executeTellAkuma({
15
+ path,
16
+ id: input.target,
17
+ body: input.body,
18
+ tellId: input.tellId,
19
+ recordedAt: input.recordedAt,
20
+ signal: input.signal,
21
+ }),
22
+ kill: async (input) => {
23
+ const result = await executeKillAkuma({ path, ids: input.targets, signal: input.signal });
24
+ return {
25
+ result,
26
+ service: result.results.map(({ id, evidence }) => ({ id, evidence })),
27
+ };
28
+ },
29
+ };
30
+ }
31
+ await runAkumaBody(upstreamFor);
@@ -5,7 +5,10 @@ type MutationCallOptions = Readonly<{
5
5
  coordinate?: ContractId;
6
6
  projectRefusal?: (refusal: unknown) => unknown;
7
7
  }>;
8
- export declare function acceptedBind(result: BindResult, target: string | null): AcceptedBindResult;
8
+ export declare function acceptedBind(result: BindResult, coordinates: Readonly<{
9
+ workspace: "worktree" | "here";
10
+ target?: string;
11
+ }>): AcceptedBindResult;
9
12
  export declare function acceptedAmend(result: AmendResult, coordinate: ContractId): AcceptedAmendResult;
10
13
  export declare function acceptedDeliver(result: MutationResult<Delivery>, coordinate: ContractId): AcceptedDeliverResult;
11
14
  export declare function acceptedReview(result: MutationResult<Review>, coordinate: ContractId): AcceptedReviewResult;
@@ -26,11 +26,12 @@ function acceptedRegion(result) {
26
26
  return { overlapFailure: result.overlapFailure };
27
27
  return { overlaps: result.overlaps };
28
28
  }
29
- export function acceptedBind(result, target) {
29
+ export function acceptedBind(result, coordinates) {
30
30
  return {
31
31
  ...acceptedEnvelope(result, undefined),
32
32
  verb: "bind",
33
- target,
33
+ workspace: coordinates.workspace,
34
+ target: coordinates.target ?? null,
34
35
  ...acceptedRegion(result),
35
36
  };
36
37
  }
@@ -44,9 +45,12 @@ export function acceptedAmend(result, coordinate) {
44
45
  }
45
46
  export function acceptedDeliver(result, coordinate) {
46
47
  const value = result.value;
48
+ const attestation = result.facts.find((fact) => fact.kind === "attestation");
49
+ const verificationVerdict = attestation?.data.verdict ?? value.verificationReuse?.verdict;
47
50
  return {
48
51
  ...acceptedEnvelope(result, coordinate),
49
52
  verb: "deliver",
53
+ ...(verificationVerdict === undefined ? {} : { verificationVerdict }),
50
54
  ...(value.verification === undefined ? {} : { verification: value.verification }),
51
55
  ...(value.verificationReuse === undefined ? {} : { verificationReuse: value.verificationReuse }),
52
56
  ...(value.placement === undefined ? {} : { placement: value.placement }),
@@ -56,18 +60,36 @@ export function acceptedDeliver(result, coordinate) {
56
60
  }
57
61
  export function acceptedReview(result, coordinate) {
58
62
  const value = result.value;
63
+ const attestation = result.facts.find((fact) => fact.kind === "attestation");
64
+ if (attestation === undefined)
65
+ throw new Error("accepted review is missing its attestation fact");
59
66
  return {
60
67
  ...acceptedEnvelope(result, coordinate),
61
68
  verb: "review",
69
+ verdict: attestation.data.verdict,
62
70
  ...(value.placement === undefined ? {} : { placement: value.placement }),
63
71
  ...(value.workspace === undefined ? {} : { workspace: value.workspace }),
64
72
  };
65
73
  }
66
74
  export function acceptedArc(result, coordinate) {
67
- return { ...acceptedEnvelope(result, coordinate), verb: "arc" };
75
+ const arc = result.facts.find((fact) => fact.kind === "arc");
76
+ if (arc === undefined)
77
+ throw new Error("accepted arc is missing its arc fact");
78
+ return {
79
+ ...acceptedEnvelope(result, coordinate),
80
+ verb: "arc",
81
+ chapter: { seq: arc.data.seq, title: arc.data.title },
82
+ };
68
83
  }
69
84
  export function acceptedAbandon(result, coordinate) {
70
- return { ...acceptedEnvelope(result, coordinate), verb: "abandon" };
85
+ const abandoned = result.facts.find((fact) => fact.kind === "abandoned");
86
+ if (abandoned === undefined)
87
+ throw new Error("accepted abandon is missing its abandoned fact");
88
+ return {
89
+ ...acceptedEnvelope(result, coordinate),
90
+ verb: "abandon",
91
+ ...(abandoned.data.note === undefined ? {} : { note: abandoned.data.note }),
92
+ };
71
93
  }
72
94
  export function acceptedAudit(result, coordinate) {
73
95
  return {
@@ -1,8 +1,8 @@
1
1
  import { type AkuId, type ActivityHistory } from "../../akuma/index.js";
2
- import { Keiyaku, type AkumaKillResult, type AkumaHistoryResult, type AkumaStatusView, type AkumaTellResult, type AkumaWaitResult, type CallResult, type ForkResult, type Keiyaku as KeiyakuContract, type Repo } from "../../index.js";
2
+ import { Keiyaku, type AkumaKillResult, type AkumaHistoryResult, type AkumaObservation, type AkumaTellResult, type AkumaWaitResult, type CallResult, type ForkResult, type Keiyaku as KeiyakuContract, type Repo } from "../../index.js";
3
3
  import type { Settings } from "../../settings.js";
4
4
  import type { WorldRoot } from "../../world.js";
5
- import type { ParsedAkumaCommand } from "./akuma.js";
5
+ import type { InvokedAkumaCommand } from "./akuma.js";
6
6
  export type AkumaInvocationResult = Readonly<{
7
7
  kind: "akuma";
8
8
  action: "call";
@@ -11,7 +11,7 @@ export type AkumaInvocationResult = Readonly<{
11
11
  }> | Readonly<{
12
12
  kind: "akuma";
13
13
  action: "status";
14
- status: AkumaStatusView;
14
+ status: AkumaObservation;
15
15
  alias?: string;
16
16
  }> | Readonly<{
17
17
  kind: "akuma";
@@ -67,12 +67,13 @@ export type AkumaInvocationResult = Readonly<{
67
67
  }>;
68
68
  type InvokeInput = Readonly<{
69
69
  path: WorldRoot;
70
- executionCwd?: string;
71
- settings: Settings;
70
+ statedCwd?: string;
71
+ home?: string;
72
+ settings?: Settings;
72
73
  contract?: KeiyakuContract;
73
74
  repo?: Repo;
74
75
  readStdin(): Promise<string>;
75
76
  }>;
76
- export declare function invokeAkuma(command: ParsedAkumaCommand, input: InvokeInput): Promise<AkumaInvocationResult>;
77
- export declare function invokeAkumaStatus(path: WorldRoot, akuma: string, settings: Settings, alias?: string, repo?: Repo): Promise<AkumaInvocationResult>;
77
+ export declare function invokeAkuma(command: InvokedAkumaCommand, input: InvokeInput): Promise<AkumaInvocationResult>;
78
+ export declare function invokeAkumaStatus(path: WorldRoot, akuma: string, alias?: string, repo?: Repo): Promise<AkumaInvocationResult>;
78
79
  export {};