@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,205 +1,67 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { rename, rm, writeFile } from "node:fs/promises";
3
- import { isAbsolute, join, resolve } from "node:path";
4
- import { decodeAllowedActions } from "./allowed.js";
5
- import { archetypeName, parseAkuId } from "./identity.js";
6
- import { decodeProviderOptions, decodeReadonlyRestraint } from "./provider-recipe.js";
7
- import { decodeProviderExecution } from "./providers/index.js";
8
- import { decodeTaskMutationRequest, isTaskMutationAction, } from "../task/mutation.js";
3
+ import { join } from "node:path";
4
+ import { z } from "zod";
9
5
  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;
10
- function object(value) {
11
- return value !== null && typeof value === "object" && !Array.isArray(value)
12
- ? value
13
- : null;
14
- }
15
- function exactKeys(value, expected) {
16
- const actual = Object.keys(value).sort();
17
- const sorted = [...expected].sort();
18
- return actual.length === sorted.length && actual.every((key, index) => key === sorted[index]);
19
- }
20
- export function absolute(value) {
21
- return typeof value === "string" && isAbsolute(value) && resolve(value) === value;
22
- }
23
- export function canonicalAkuId(value) {
24
- if (typeof value !== "string")
25
- return null;
26
- try {
27
- const id = parseAkuId(value).id;
28
- return id === value ? id : null;
29
- }
30
- catch {
31
- return null;
32
- }
33
- }
34
- function canonicalTargets(value) {
35
- if (!Array.isArray(value) || value.length === 0)
36
- return null;
37
- const ids = [];
38
- for (const item of value) {
39
- const id = canonicalAkuId(item);
40
- if (id === null || (ids.length > 0 && Buffer.compare(Buffer.from(ids.at(-1)), Buffer.from(id)) >= 0))
41
- return null;
42
- ids.push(id);
43
- }
44
- return ids;
45
- }
46
- export function decodeRecipe(value) {
47
- const recipe = object(value);
48
- if (recipe === null)
49
- return null;
50
- const expected = [
51
- "allowed",
52
- "options",
53
- "provider",
54
- ...(recipe.description === undefined ? [] : ["description"]),
55
- ...(recipe.readonly === undefined ? [] : ["readonly"]),
56
- ];
57
- if (!exactKeys(recipe, expected) ||
58
- (recipe.description !== undefined &&
59
- (typeof recipe.description !== "string" || recipe.description.trim().length === 0)))
60
- return null;
61
- try {
62
- const options = decodeProviderOptions(recipe.options);
63
- const readonly = recipe.readonly === undefined ? undefined : decodeReadonlyRestraint(recipe.readonly);
64
- if ((options.readonly === true) !== (readonly !== undefined))
65
- return null;
66
- return Object.freeze({
67
- ...(recipe.description === undefined ? {} : { description: recipe.description }),
68
- allowed: decodeAllowedActions(recipe.allowed),
69
- provider: decodeProviderExecution(recipe.provider),
70
- options,
71
- ...(readonly === undefined ? {} : { readonly }),
72
- });
73
- }
74
- catch {
75
- return null;
76
- }
77
- }
78
- function contractId(value) {
79
- return typeof value === "string" && /^kei\/[^/\s]+$/u.test(value) ? value : null;
80
- }
81
- function decodeCall(id, payload) {
82
- const expected = ["archetype", "body", "recipe", "world", ...(payload.cwd === undefined ? [] : ["cwd"])];
83
- if (!exactKeys(payload, expected) ||
84
- typeof payload.body !== "string" ||
85
- !absolute(payload.world) ||
86
- (payload.cwd !== undefined && !absolute(payload.cwd)))
87
- return null;
88
- try {
89
- archetypeName(payload.archetype);
90
- }
91
- catch {
92
- return null;
93
- }
94
- return {
95
- id,
96
- action: "akuma.call",
97
- world: payload.world,
98
- archetype: payload.archetype,
99
- body: payload.body,
100
- recipe: payload.recipe,
101
- ...(payload.cwd === undefined ? {} : { cwd: payload.cwd }),
102
- };
103
- }
104
- function decodeWait(id, payload) {
105
- const targets = canonicalTargets(payload.targets);
106
- const expected = ["completion", "targets", ...(payload.timeoutMs === undefined ? [] : ["timeoutMs"])];
107
- if (!exactKeys(payload, expected) ||
108
- targets === null ||
109
- (payload.completion !== "any" && payload.completion !== "all") ||
110
- (payload.timeoutMs !== undefined && (!Number.isSafeInteger(payload.timeoutMs) || payload.timeoutMs < 0)))
111
- return null;
6
+ const requestIdSchema = z.string().regex(UUID);
7
+ const actionSchema = z.string().refine((value) => value.trim() !== "");
8
+ const requestEnvelopeSchema = z.object({ id: requestIdSchema, action: actionSchema, payload: z.unknown() }).strict();
9
+ const upstreamRequestFailureSchema = z.union([
10
+ z.object({ kind: z.literal("akuma-not-born"), id: z.string() }).strict(),
11
+ z.object({ kind: z.literal("failed"), diagnostic: z.string() }).strict(),
12
+ ]);
13
+ const returnedEnvelopeSchema = z.union([
14
+ z.object({ kind: z.literal("returned"), result: z.unknown() }).strict(),
15
+ z.object({ kind: z.literal("failed"), failure: upstreamRequestFailureSchema }).strict(),
16
+ ]);
17
+ const receiptEnvelopeSchema = z.union([
18
+ z
19
+ .object({ id: requestIdSchema, action: actionSchema, state: z.literal("served"), outcome: returnedEnvelopeSchema })
20
+ .strict(),
21
+ z.object({ id: requestIdSchema, action: actionSchema, state: z.literal("served"), reference: z.unknown() }).strict(),
22
+ z.object({ id: requestIdSchema, action: actionSchema, state: z.literal("refused"), diagnostic: z.string() }).strict(),
23
+ z
24
+ .object({
25
+ id: requestIdSchema,
26
+ action: actionSchema,
27
+ state: z.literal("voided"),
28
+ evidence: z.string(),
29
+ failure: z.unknown().optional(),
30
+ })
31
+ .strict(),
32
+ ]);
33
+ /** Erases only static TypeScript parameters; each operation codec remains captured by its owner descriptor. */
34
+ export function eraseRequestCommand(command) {
112
35
  return {
113
- id,
114
- action: "akuma.wait",
115
- targets,
116
- completion: payload.completion,
117
- ...(payload.timeoutMs === undefined ? {} : { timeoutMs: payload.timeoutMs }),
36
+ action: command.action,
37
+ resolve: (payload) => {
38
+ const input = command.decodeRequest(payload);
39
+ if (input === null)
40
+ return null;
41
+ return {
42
+ payloadJson: JSON.stringify(command.encodeRequest(input)),
43
+ isPermitted: (allowed) => command.isPermitted(allowed),
44
+ execute: async (context) => {
45
+ const served = await command.execute(input, command.decodeExecutionContext(context));
46
+ return "child" in served
47
+ ? { result: command.encodeResult(served.result), child: served.child }
48
+ : {
49
+ result: command.encodeResult(served.result),
50
+ serviceJson: JSON.stringify(command.encodeService(served.service)),
51
+ };
52
+ },
53
+ encodeFailure: (error) => command.encodeFailure?.(error) ?? null,
54
+ projectServiceJson: (serviceJson) => command.projectService(command.decodeService(JSON.parse(serviceJson))),
55
+ projectChild: (child) => {
56
+ if (command.projectChild === undefined)
57
+ throw new Error(`request ${command.action} has no child reference codec`);
58
+ return command.projectChild(child);
59
+ },
60
+ };
61
+ },
118
62
  };
119
63
  }
120
- function decodeTell(id, payload) {
121
- const target = canonicalAkuId(payload.target);
122
- return exactKeys(payload, ["body", "target"]) && target !== null && typeof payload.body === "string"
123
- ? { id, action: "akuma.tell", target, body: payload.body }
124
- : null;
125
- }
126
- function decodeKill(id, payload) {
127
- const targets = canonicalTargets(payload.targets);
128
- return exactKeys(payload, ["targets"]) && targets !== null ? { id, action: "akuma.kill", targets } : null;
129
- }
130
- function decodeDeliver(id, payload) {
131
- const valid = exactKeys(payload, [
132
- "contractId",
133
- "includeDirty",
134
- "materializeConflict",
135
- "repoRoot",
136
- ...(payload.message === undefined ? [] : ["message"]),
137
- ]);
138
- const selected = contractId(payload.contractId);
139
- return valid &&
140
- selected !== null &&
141
- absolute(payload.repoRoot) &&
142
- typeof payload.includeDirty === "boolean" &&
143
- typeof payload.materializeConflict === "boolean" &&
144
- (payload.message === undefined || (typeof payload.message === "string" && payload.message.trim().length > 0))
145
- ? {
146
- id,
147
- action: "contract.deliver",
148
- repoRoot: payload.repoRoot,
149
- contractId: selected,
150
- includeDirty: payload.includeDirty,
151
- materializeConflict: payload.materializeConflict,
152
- ...(payload.message === undefined ? {} : { message: payload.message }),
153
- }
154
- : null;
155
- }
156
- function decodeReview(id, payload) {
157
- const valid = exactKeys(payload, [
158
- "contractId",
159
- "repoRoot",
160
- "verdict",
161
- ...(payload.summary === undefined ? [] : ["summary"]),
162
- ]);
163
- const selected = contractId(payload.contractId);
164
- return valid &&
165
- selected !== null &&
166
- absolute(payload.repoRoot) &&
167
- (payload.verdict === "satisfied" || payload.verdict === "unsatisfied") &&
168
- (payload.summary === undefined || (typeof payload.summary === "string" && payload.summary.trim().length > 0))
169
- ? {
170
- id,
171
- action: "contract.review",
172
- repoRoot: payload.repoRoot,
173
- contractId: selected,
174
- verdict: payload.verdict,
175
- ...(payload.summary === undefined ? {} : { summary: payload.summary }),
176
- }
177
- : null;
178
- }
179
- function decodeTask(id, action, payload) {
180
- if (!isTaskMutationAction(action) || !exactKeys(payload, ["request", "world"]) || !absolute(payload.world))
181
- return null;
182
- try {
183
- return {
184
- id,
185
- action,
186
- world: payload.world,
187
- request: decodeTaskMutationRequest(action, payload.request),
188
- };
189
- }
190
- catch {
191
- return null;
192
- }
193
- }
194
- const CLAIM_DECODERS = {
195
- "akuma.call": decodeCall,
196
- "akuma.wait": decodeWait,
197
- "akuma.tell": decodeTell,
198
- "akuma.kill": decodeKill,
199
- "contract.deliver": decodeDeliver,
200
- "contract.review": decodeReview,
201
- };
202
- export function decodeClaim(bytes, fileId) {
64
+ export function decodeEnvelope(bytes, fileId) {
203
65
  let decoded;
204
66
  try {
205
67
  decoded = JSON.parse(bytes);
@@ -207,107 +69,10 @@ export function decodeClaim(bytes, fileId) {
207
69
  catch {
208
70
  return null;
209
71
  }
210
- const value = object(decoded);
211
- if (value === null ||
212
- !exactKeys(value, ["action", "id", "payload"]) ||
213
- !UUID.test(fileId) ||
214
- typeof value.id !== "string" ||
215
- !UUID.test(value.id))
216
- return null;
217
- const payload = object(value.payload);
218
- if (payload === null || typeof value.action !== "string")
219
- return null;
220
- const decoder = CLAIM_DECODERS[value.action];
221
- return decoder === undefined ? decodeTask(value.id, value.action, payload) : decoder(value.id, payload);
222
- }
223
- function outcome(value) {
224
- const input = object(value);
225
- if (input?.kind === "returned" && exactKeys(input, ["kind", "result"])) {
226
- return { kind: input.kind, result: input.result };
227
- }
228
- const failure = object(input?.failure);
229
- if (input?.kind !== "failed" || !exactKeys(input, ["failure", "kind"]) || failure === null)
230
- return null;
231
- if (failure.kind === "akuma-not-born" && exactKeys(failure, ["id", "kind"])) {
232
- const id = canonicalAkuId(failure.id);
233
- return id === null ? null : { kind: input.kind, failure: { kind: failure.kind, id } };
234
- }
235
- return failure.kind === "failed" &&
236
- exactKeys(failure, ["diagnostic", "kind"]) &&
237
- typeof failure.diagnostic === "string"
238
- ? { kind: input.kind, failure: { kind: failure.kind, diagnostic: failure.diagnostic } }
239
- : null;
72
+ const parsed = requestEnvelopeSchema.safeParse(decoded);
73
+ return UUID.test(fileId) && parsed.success ? parsed.data : null;
240
74
  }
241
- function envelope(value, id, action) {
242
- if (value.state === "refused" &&
243
- exactKeys(value, ["action", "diagnostic", "id", "state"]) &&
244
- typeof value.diagnostic === "string") {
245
- return { id, action, state: value.state, diagnostic: value.diagnostic };
246
- }
247
- if (value.state === "voided" &&
248
- exactKeys(value, ["action", "evidence", "id", "state"]) &&
249
- typeof value.evidence === "string") {
250
- return { id, action, state: value.state, evidence: value.evidence };
251
- }
252
- if (action !== "akuma.call" && value.state === "served" && exactKeys(value, ["action", "id", "outcome", "state"])) {
253
- const decoded = outcome(value.outcome);
254
- return decoded === null ? null : { id, action, state: value.state, outcome: decoded };
255
- }
256
- return null;
257
- }
258
- function reference(value, action) {
259
- const input = object(value);
260
- const field = action === "contract.deliver" ? "deliveryFactId" : "reviewFactId";
261
- if (input === null ||
262
- !exactKeys(input, ["contractId", field, "kind", "repoRoot"]) ||
263
- input.kind !== "accepted-reference" ||
264
- !absolute(input.repoRoot) ||
265
- contractId(input.contractId) === null ||
266
- typeof input[field] !== "string" ||
267
- input[field].trim().length === 0)
268
- return null;
269
- return action === "contract.deliver"
270
- ? {
271
- kind: input.kind,
272
- repoRoot: input.repoRoot,
273
- contractId: input.contractId,
274
- deliveryFactId: input.deliveryFactId,
275
- }
276
- : {
277
- kind: input.kind,
278
- repoRoot: input.repoRoot,
279
- contractId: input.contractId,
280
- reviewFactId: input.reviewFactId,
281
- };
282
- }
283
- function decodeCallReceipt(value, id) {
284
- if (value.state !== "served" || !exactKeys(value, ["action", "child", "id", "state"]))
285
- return null;
286
- const child = canonicalAkuId(value.child);
287
- return child === null ? null : { id, action: "akuma.call", state: value.state, child };
288
- }
289
- function decodeContractReceipt(value, id, action) {
290
- if (value.state !== "served" || !exactKeys(value, ["action", "id", "reference", "state"]))
291
- return null;
292
- const selected = reference(value.reference, action);
293
- if (selected === null)
294
- return null;
295
- return action === "contract.deliver"
296
- ? { id, action, state: value.state, reference: selected }
297
- : { id, action, state: value.state, reference: selected };
298
- }
299
- function decodeTaskReceipt(value, id, action) {
300
- if (value.state !== "served" || !exactKeys(value, ["action", "id", "reference", "state"]))
301
- return null;
302
- const selected = object(value.reference);
303
- if (selected === null ||
304
- !exactKeys(selected, ["action", "kind"]) ||
305
- selected.kind !== "served-reference" ||
306
- selected.action !== action)
307
- return null;
308
- return { id, action, state: value.state, reference: { kind: selected.kind, action } };
309
- }
310
- export function decodeReceipt(bytes, id, action) {
75
+ export function decodeReceiptEnvelope(bytes, id, action) {
311
76
  let decoded;
312
77
  try {
313
78
  decoded = JSON.parse(bytes);
@@ -315,28 +80,8 @@ export function decodeReceipt(bytes, id, action) {
315
80
  catch {
316
81
  return null;
317
82
  }
318
- const value = object(decoded);
319
- if (value === null || value.id !== id || value.action !== action)
320
- return null;
321
- const shared = envelope(value, id, action);
322
- if (shared !== null)
323
- return shared;
324
- if (action === "akuma.call")
325
- return decodeCallReceipt(value, id);
326
- if (action === "contract.deliver" || action === "contract.review")
327
- return decodeContractReceipt(value, id, action);
328
- if (isTaskMutationAction(action))
329
- return decodeTaskReceipt(value, id, action);
330
- return null;
331
- }
332
- export function requestPayload(claim) {
333
- if (isTaskMutationAction(claim.action)) {
334
- const task = claim;
335
- const { action: _action, ...request } = task.request;
336
- return { world: task.world, request };
337
- }
338
- const { id: _id, action: _action, ...payload } = claim;
339
- return payload;
83
+ const parsed = receiptEnvelopeSchema.safeParse(decoded);
84
+ return parsed.success && parsed.data.id === id && parsed.data.action === action ? parsed.data : null;
340
85
  }
341
86
  export function requestPath(directory, id) {
342
87
  return join(directory, `${id}.request.json`);
@@ -1,49 +1,21 @@
1
- import { type CallRequestClaim, type DeliverRequestClaim, type ForwardedDeliveryReference, type ForwardedReviewReference, type KillRequestClaim, type ReviewRequestClaim, type TellRequestClaim, type UpstreamRequestOutcome, type WaitRequestClaim } from "./request-wire.js";
2
- import { type TaskMutationRequest } from "../task/mutation.js";
3
- export type { UpstreamRequestOutcome } from "./request-wire.js";
4
- export declare class AkumaBodyRequestError extends Error {
5
- readonly outcome: "refused" | "voided";
6
- readonly diagnostic: string;
7
- readonly kind = "akuma-body-request";
8
- constructor(outcome: "refused" | "voided", diagnostic: string);
9
- }
10
- export declare function injectedBodyRequests(): string | null;
11
- export declare function requestBodyCall(input: Omit<CallRequestClaim, "action"> & Readonly<{
1
+ export { AkumaBodyRequestError, requestBodyCommand } from "./request-rendezvous.js";
2
+ export type ExecutionChannel = Readonly<{
3
+ kind: "local";
4
+ }> | Readonly<{
5
+ kind: "body-request";
12
6
  directory: string;
13
- }>): Promise<import("./identity.js").AkuId>;
14
- export declare function requestBodyTask(input: Readonly<{
15
- directory: string;
16
- world: string;
17
- request: TaskMutationRequest;
18
- id?: string;
19
- }>): Promise<unknown>;
20
- export declare function requestBodyWait(input: Omit<WaitRequestClaim, "action" | "id"> & Readonly<{
21
- directory: string;
22
- id?: string;
23
- }>): Promise<UpstreamRequestOutcome>;
24
- export declare function requestBodyTell(input: Omit<TellRequestClaim, "action" | "id"> & Readonly<{
25
- directory: string;
26
- id?: string;
27
- }>): Promise<UpstreamRequestOutcome>;
28
- export declare function requestBodyKill(input: Omit<KillRequestClaim, "action" | "id"> & Readonly<{
29
- directory: string;
30
- id?: string;
31
- }>): Promise<UpstreamRequestOutcome>;
32
- type ContractRequestInput<T extends DeliverRequestClaim | ReviewRequestClaim> = Omit<T, "action" | "id"> & Readonly<{
33
- directory: string;
34
- signal?: AbortSignal;
35
7
  }>;
36
- type BodyDeliverInput = ContractRequestInput<DeliverRequestClaim>;
37
- export declare function requestBodyDeliver(input: BodyDeliverInput & Readonly<{
38
- id?: never;
39
- }>): Promise<UpstreamRequestOutcome>;
40
- export declare function requestBodyDeliver(input: BodyDeliverInput & Readonly<{
41
- id: string;
42
- }>): Promise<UpstreamRequestOutcome | ForwardedDeliveryReference>;
43
- type BodyReviewInput = ContractRequestInput<ReviewRequestClaim>;
44
- export declare function requestBodyReview(input: BodyReviewInput & Readonly<{
45
- id?: never;
46
- }>): Promise<UpstreamRequestOutcome>;
47
- export declare function requestBodyReview(input: BodyReviewInput & Readonly<{
48
- id: string;
49
- }>): Promise<UpstreamRequestOutcome | ForwardedReviewReference>;
8
+ export type ExecutionContext = Readonly<{
9
+ channel: ExecutionChannel;
10
+ }>;
11
+ export type LibraryExecution = ExecutionContext;
12
+ export declare function localExecutionContext(): ExecutionContext;
13
+ export declare function bodyRequestExecutionContext(directory: unknown): ExecutionContext;
14
+ /** Constructs the one direct-parent route a caller may explicitly compose. */
15
+ export declare function bodyRequestExecution(input: Readonly<{
16
+ directory: string;
17
+ }>): LibraryExecution;
18
+ /** Normalizes the public carrier before a composition captures its immutable channel. */
19
+ export declare function libraryExecution(value: unknown): LibraryExecution;
20
+ export declare function libraryExecutionInput(input: unknown): LibraryExecution;
21
+ export declare function executionChannel(context?: ExecutionContext): ExecutionChannel;
@@ -1,120 +1,64 @@
1
- import { randomUUID } from "node:crypto";
2
- import { access, readFile } from "node:fs/promises";
3
- import { abortableDelay } from "./abort.js";
4
- import { AKUMA_REQUESTS_ENV } from "./provider.js";
5
- import { absolute, atomicJson, decodeReceipt, receiptPath, requestPath, requestPayload, } from "./request-wire.js";
6
- import { isTaskMutationAction } from "../task/mutation.js";
7
- const POLL_MS = 100;
8
- export class AkumaBodyRequestError extends Error {
9
- outcome;
10
- diagnostic;
11
- kind = "akuma-body-request";
12
- constructor(outcome, diagnostic) {
13
- super(`Akuma body request ${outcome}: ${diagnostic}`);
14
- this.outcome = outcome;
15
- this.diagnostic = diagnostic;
16
- this.name = "AkumaBodyRequestError";
17
- }
18
- }
19
- export function injectedBodyRequests() {
20
- const directory = process.env[AKUMA_REQUESTS_ENV];
21
- if (directory === undefined)
22
- return null;
23
- if (!absolute(directory))
24
- throw new Error(`${AKUMA_REQUESTS_ENV} must be an absolute normalized path`);
25
- return directory;
1
+ import { isAbsolute, resolve } from "node:path";
2
+ export { AkumaBodyRequestError, requestBodyCommand } from "./request-rendezvous.js";
3
+ const LOCAL_CHANNEL = Object.freeze({ kind: "local" });
4
+ export function localExecutionContext() {
5
+ return Object.freeze({ channel: LOCAL_CHANNEL });
26
6
  }
27
- async function requestBody(input) {
28
- input.signal?.throwIfAborted();
29
- const transportId = randomUUID();
30
- await atomicJson(requestPath(input.directory, transportId), {
31
- id: input.claim.id,
32
- action: input.claim.action,
33
- payload: requestPayload(input.claim),
34
- });
35
- const path = receiptPath(input.directory, transportId);
36
- for (;;) {
37
- try {
38
- const receipt = decodeReceipt(await readFile(path, "utf8"), input.claim.id, input.claim.action);
39
- if (receipt === null)
40
- throw new Error(`Akuma body request ${input.claim.id} has an invalid receipt`);
41
- if (receipt.state === "refused")
42
- throw new AkumaBodyRequestError("refused", receipt.diagnostic);
43
- if (receipt.state === "voided")
44
- throw new AkumaBodyRequestError("voided", receipt.evidence);
45
- return receipt;
46
- }
47
- catch (error) {
48
- if (error.code !== "ENOENT")
49
- throw error;
50
- }
51
- if (!(await access(input.directory).then(() => true, () => false))) {
52
- throw new AkumaBodyRequestError("voided", "parent request channel closed before a receipt");
53
- }
54
- await abortableDelay(POLL_MS, input.signal);
7
+ export function bodyRequestExecutionContext(directory) {
8
+ if (typeof directory !== "string" || !isAbsolute(directory) || resolve(directory) !== directory) {
9
+ throw new Error("AKUMA_REQUESTS must be an absolute normalized path");
55
10
  }
11
+ return Object.freeze({ channel: Object.freeze({ kind: "body-request", directory }) });
56
12
  }
57
- export async function requestBodyCall(input) {
58
- const { directory, ...claim } = input;
59
- const receipt = await requestBody({ directory, claim: { ...claim, action: "akuma.call" } });
60
- if (receipt.action !== "akuma.call" || receipt.state !== "served") {
61
- throw new Error(`Akuma body request ${input.id} returned the wrong action`);
13
+ function valueRecord(value, label) {
14
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
15
+ throw new TypeError(`${label} must be an object`);
62
16
  }
63
- return receipt.child;
17
+ return value;
64
18
  }
65
- async function requestUpstream(directory, claim, signal) {
66
- const receipt = await requestBody({ directory, claim, ...(signal === undefined ? {} : { signal }) });
67
- if (receipt.action === "akuma.call" || receipt.state !== "served" || !("outcome" in receipt)) {
68
- throw new Error(`Akuma body request ${claim.id} returned the wrong action`);
19
+ function closed(value, keys, label) {
20
+ for (const key of Object.keys(value)) {
21
+ if (!keys.includes(key))
22
+ throw new TypeError(`${label} has unknown field: ${key}`);
69
23
  }
70
- return receipt.outcome;
71
24
  }
72
- export async function requestBodyTask(input) {
73
- const id = input.id ?? randomUUID();
74
- const receipt = await requestBody({
75
- directory: input.directory,
76
- claim: { id, action: input.request.action, world: input.world, request: input.request },
77
- });
78
- if (!isTaskMutationAction(receipt.action) || receipt.action !== input.request.action || receipt.state !== "served") {
79
- throw new Error(`Akuma body request ${id} returned the wrong action`);
25
+ /** Constructs the one direct-parent route a caller may explicitly compose. */
26
+ export function bodyRequestExecution(input) {
27
+ const value = valueRecord(input, "body request execution input");
28
+ closed(value, ["directory"], "body request execution input");
29
+ if (typeof value.directory !== "string")
30
+ throw new TypeError("body request execution directory must be a string");
31
+ try {
32
+ return bodyRequestExecutionContext(value.directory);
33
+ }
34
+ catch (error) {
35
+ throw new TypeError(error instanceof Error ? error.message : String(error));
80
36
  }
81
- if ("reference" in receipt)
82
- return receipt.reference;
83
- if (receipt.outcome.kind === "returned")
84
- return receipt.outcome.result;
85
- throw new Error(receipt.outcome.failure.kind === "failed"
86
- ? receipt.outcome.failure.diagnostic
87
- : "Akuma body request parent is not born");
88
- }
89
- export async function requestBodyWait(input) {
90
- const { directory, id = randomUUID(), ...claim } = input;
91
- return await requestUpstream(directory, { ...claim, id, action: "akuma.wait" });
92
- }
93
- export async function requestBodyTell(input) {
94
- const { directory, id = randomUUID(), ...claim } = input;
95
- return await requestUpstream(directory, { ...claim, id, action: "akuma.tell" });
96
- }
97
- export async function requestBodyKill(input) {
98
- const { directory, id = randomUUID(), ...claim } = input;
99
- return await requestUpstream(directory, { ...claim, id, action: "akuma.kill" });
100
37
  }
101
- async function requestContract(input, action) {
102
- const { directory, id = randomUUID(), signal, ...claim } = input;
103
- const receipt = await requestBody({
104
- directory,
105
- claim: { ...claim, id, action },
106
- ...(signal === undefined ? {} : { signal }),
107
- });
108
- if (receipt.action !== action || receipt.state !== "served") {
109
- throw new Error(`Akuma body request ${id} returned the wrong action`);
38
+ /** Normalizes the public carrier before a composition captures its immutable channel. */
39
+ export function libraryExecution(value) {
40
+ const context = valueRecord(value, "execution");
41
+ closed(context, ["channel"], "execution");
42
+ const channel = valueRecord(context.channel, "execution channel");
43
+ if (channel.kind === "local") {
44
+ closed(channel, ["kind"], "execution channel");
45
+ return localExecutionContext();
46
+ }
47
+ if (channel.kind === "body-request") {
48
+ closed(channel, ["directory", "kind"], "execution channel");
49
+ if (typeof channel.directory !== "string")
50
+ throw new TypeError("execution channel directory must be a string");
51
+ return bodyRequestExecution({ directory: channel.directory });
110
52
  }
111
- return "reference" in receipt
112
- ? receipt.reference
113
- : receipt.outcome;
53
+ throw new TypeError("execution channel must be local or body-request");
114
54
  }
115
- export async function requestBodyDeliver(input) {
116
- return await requestContract(input, "contract.deliver");
55
+ export function libraryExecutionInput(input) {
56
+ const value = valueRecord(input, "Keiyaku.withExecution input");
57
+ closed(value, ["execution"], "Keiyaku.withExecution input");
58
+ if (value.execution === undefined)
59
+ throw new TypeError("Keiyaku.withExecution input requires execution");
60
+ return libraryExecution(value.execution);
117
61
  }
118
- export async function requestBodyReview(input) {
119
- return await requestContract(input, "contract.review");
62
+ export function executionChannel(context) {
63
+ return context?.channel ?? LOCAL_CHANNEL;
120
64
  }