@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,4 +1,5 @@
1
1
  import { noteEvent, unknownEvent, } from "../../provider.js";
2
+ import { diffstatFromUnifiedPatch } from "../unified-patch.js";
2
3
  export const CODEX_NOTIFICATION_DISPOSITIONS = {
3
4
  "account/login/completed": "drop",
4
5
  "account/rateLimits/updated": "drop",
@@ -135,19 +136,6 @@ function fileChangeOp(value) {
135
136
  ? record.type
136
137
  : undefined;
137
138
  }
138
- function diffstatFromUnifiedPatch(patch) {
139
- if (!patch.split("\n").some((line) => /^@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@/u.test(line)))
140
- return undefined;
141
- let added = 0;
142
- let removed = 0;
143
- for (const line of patch.split("\n")) {
144
- if (line.startsWith("+") && !line.startsWith("+++"))
145
- added += 1;
146
- if (line.startsWith("-") && !line.startsWith("---"))
147
- removed += 1;
148
- }
149
- return { added, removed };
150
- }
151
139
  function itemChanges(item) {
152
140
  if (!Array.isArray(item.changes))
153
141
  return [];
@@ -1,4 +1,5 @@
1
1
  import type { ProviderAdapter } from "../../provider.js";
2
2
  import type { ProviderExecution } from "../../provider-recipe.js";
3
- import { type AcpDependencies } from "../acp/core.js";
3
+ import { type AcpDependencies, type AcpToolInterpreter } from "../acp/core.js";
4
+ export declare const interpretGrokTool: AcpToolInterpreter;
4
5
  export declare function createGrokBuildProvider(execution: ProviderExecution, dependencies?: AcpDependencies): ProviderAdapter;
@@ -1,5 +1,72 @@
1
- import { startAcpSession } from "../acp/core.js";
1
+ import { startAcpSession, } from "../acp/core.js";
2
2
  const INTERJECT_METHOD = "x.ai/interject";
3
+ function nonblank(value) {
4
+ return typeof value === "string" && value.trim().length > 0 ? value : undefined;
5
+ }
6
+ function object(value) {
7
+ return value !== null && typeof value === "object" && !Array.isArray(value)
8
+ ? value
9
+ : undefined;
10
+ }
11
+ function nativeName(update) {
12
+ const wire = update;
13
+ return nonblank(update.name) ?? nonblank(wire.toolName);
14
+ }
15
+ function readCall(name, input) {
16
+ if (name !== "read_file" && name !== "hashline_read")
17
+ return undefined;
18
+ const sourcePath = nonblank(input.target_file);
19
+ const capturedPath = name === "read_file" ? nonblank(input.path) : undefined;
20
+ const path = sourcePath ?? capturedPath;
21
+ return path === undefined ? undefined : { kind: "read", path };
22
+ }
23
+ function contentSearchCall(name, input) {
24
+ if (name !== "grep" && name !== "hashline_grep")
25
+ return undefined;
26
+ const query = nonblank(input.pattern);
27
+ if (query === undefined)
28
+ return undefined;
29
+ const path = nonblank(input.path);
30
+ const glob = nonblank(input.glob);
31
+ return {
32
+ kind: "search",
33
+ query,
34
+ scope: "content",
35
+ ...(path === undefined ? {} : { path }),
36
+ ...(glob === undefined ? {} : { glob }),
37
+ };
38
+ }
39
+ function runCall(name, input) {
40
+ if (name !== "run_terminal_cmd")
41
+ return undefined;
42
+ const command = nonblank(input.command);
43
+ return command === undefined ? undefined : { kind: "run", command };
44
+ }
45
+ function webSearchCall(name, input) {
46
+ if (name !== "web_search")
47
+ return undefined;
48
+ const query = nonblank(input.query);
49
+ return query === undefined ? undefined : { kind: "search", query, scope: "web" };
50
+ }
51
+ function fileChangeCall(name, input) {
52
+ if (name !== "search_replace")
53
+ return undefined;
54
+ const path = nonblank(input.file_path);
55
+ return path === undefined
56
+ ? undefined
57
+ : { kind: "fileChange", changes: [{ op: "unspecified", path }] };
58
+ }
59
+ export const interpretGrokTool = (update) => {
60
+ const name = nativeName(update);
61
+ const input = object(update.rawInput);
62
+ if (name === undefined || input === undefined)
63
+ return undefined;
64
+ return readCall(name, input)
65
+ ?? contentSearchCall(name, input)
66
+ ?? runCall(name, input)
67
+ ?? webSearchCall(name, input)
68
+ ?? fileChangeCall(name, input);
69
+ };
3
70
  function optionAdmission(options) {
4
71
  if (options.network !== undefined) {
5
72
  return { kind: "refused", diagnostic: "Grok Build does not support the network option" };
@@ -59,7 +126,10 @@ export function createGrokBuildProvider(execution, dependencies = {}) {
59
126
  argv: argv(execution, input.options),
60
127
  ...(execution.env === undefined ? {} : { env: execution.env }),
61
128
  };
62
- return withInterject(await startAcpSession(launch, input, dependencies));
129
+ return withInterject(await startAcpSession(launch, input, {
130
+ ...dependencies,
131
+ interpretTool: interpretGrokTool,
132
+ }));
63
133
  };
64
134
  return {
65
135
  confinement: () => ({ kind: "unconfined" }),
@@ -59,6 +59,61 @@ function diagnostic(value) {
59
59
  function positiveLine(value) {
60
60
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 1 ? value : undefined;
61
61
  }
62
+ function nonnegativeCount(value) {
63
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : undefined;
64
+ }
65
+ function diffstat(value) {
66
+ if (value === undefined)
67
+ return undefined;
68
+ const added = nonnegativeCount(value.additions);
69
+ const removed = nonnegativeCount(value.deletions);
70
+ return added === undefined || removed === undefined ? undefined : { added, removed };
71
+ }
72
+ function fileChange(changes) {
73
+ return changes.length === 0 ? undefined : { kind: "fileChange", changes };
74
+ }
75
+ function change(op, path, stats) {
76
+ return stats === undefined ? { op, path } : { op, path, diffstat: stats };
77
+ }
78
+ function editCall(input, metadata) {
79
+ const path = text(input?.filePath);
80
+ if (path === undefined)
81
+ return undefined;
82
+ return fileChange([change("update", path, diffstat(object(metadata?.filediff)))]);
83
+ }
84
+ function writeCall(input, metadata) {
85
+ const path = text(metadata?.filepath) ?? text(input?.filePath);
86
+ const op = metadata?.exists === false ? "add"
87
+ : metadata?.exists === true ? "update" : undefined;
88
+ return path === undefined || op === undefined ? undefined : fileChange([change(op, path, undefined)]);
89
+ }
90
+ function applyPatchFile(value) {
91
+ const file = object(value);
92
+ if (file === undefined)
93
+ return undefined;
94
+ const type = file.type;
95
+ const op = type === "add" ? "add"
96
+ : type === "delete" ? "delete"
97
+ : type === "update" || type === "modify" || type === "move"
98
+ ? "update" : undefined;
99
+ const path = type === "move" ? text(file.movePath) : text(file.filePath);
100
+ return op === undefined || path === undefined ? undefined : change(op, path, diffstat(file));
101
+ }
102
+ function applyPatchCall(metadata) {
103
+ if (!Array.isArray(metadata?.files))
104
+ return undefined;
105
+ return fileChange(metadata.files.flatMap((value) => {
106
+ const next = applyPatchFile(value);
107
+ return next === undefined ? [] : [next];
108
+ }));
109
+ }
110
+ function strongerCall(previous, next) {
111
+ if (next === undefined)
112
+ return previous;
113
+ if (previous?.kind === "fileChange" && next.kind !== "fileChange")
114
+ return previous;
115
+ return next;
116
+ }
62
117
  function runCall(name, value) {
63
118
  return name === "bash" || name === "shell"
64
119
  ? { kind: "run", command: text(value?.command) ?? name }
@@ -96,8 +151,9 @@ function searchCall(name, value) {
96
151
  ...(scope === "content" && glob !== undefined ? { glob } : {}),
97
152
  };
98
153
  }
99
- function callFor(name, input) {
154
+ function callFor(name, input, metadata) {
100
155
  const value = object(input);
156
+ const meta = object(metadata);
101
157
  const lower = name.toLowerCase();
102
158
  if (lower === "bash" || lower === "shell")
103
159
  return runCall(lower, value);
@@ -105,6 +161,12 @@ function callFor(name, input) {
105
161
  return readCall(lower, value);
106
162
  if (lower === "grep" || lower === "glob" || lower === "search")
107
163
  return searchCall(lower, value);
164
+ if (lower === "edit")
165
+ return editCall(value, meta) ?? { kind: "other", display: name };
166
+ if (lower === "write")
167
+ return writeCall(value, meta) ?? { kind: "other", display: name };
168
+ if (lower === "apply_patch")
169
+ return applyPatchCall(meta) ?? { kind: "other", display: name };
108
170
  return { kind: "other", display: name };
109
171
  }
110
172
  function belongs(part, state) {
@@ -139,12 +201,12 @@ function mapToolPart(part, events, state) {
139
201
  const toolState = object(part.state);
140
202
  if (id === undefined || name === undefined || toolState === undefined)
141
203
  return;
204
+ const next = callFor(name, toolState.input, toolState.metadata);
142
205
  if ((toolState.status === "pending" || toolState.status === "running")
143
206
  && !state.tools.has(id) && !state.completedTools.has(id)) {
144
- const call = callFor(name, toolState.input);
145
- if (call === undefined)
207
+ if (next === undefined)
146
208
  return;
147
- const observed = { name, call };
209
+ const observed = { name, call: next };
148
210
  state.tools.set(id, observed);
149
211
  events.emit({ type: "tool", phase: "started", id, ...observed });
150
212
  return;
@@ -152,14 +214,17 @@ function mapToolPart(part, events, state) {
152
214
  if ((toolState.status !== "completed" && toolState.status !== "error") || state.completedTools.has(id))
153
215
  return;
154
216
  const started = state.tools.get(id);
155
- const call = started?.call ?? callFor(name, toolState.input);
217
+ const call = strongerCall(started?.call, next);
156
218
  if (call === undefined)
157
219
  return;
158
- const observed = started ?? { name, call };
220
+ const observed = { name: started?.name ?? name, call };
159
221
  if (!state.tools.has(id)) {
160
222
  state.tools.set(id, observed);
161
223
  events.emit({ type: "tool", phase: "started", id, ...observed });
162
224
  }
225
+ else {
226
+ state.tools.set(id, observed);
227
+ }
163
228
  state.completedTools.add(id);
164
229
  events.emit({
165
230
  type: "tool",
@@ -1,4 +1,5 @@
1
1
  import { noteEvent, unknownEvent } from "../../provider.js";
2
+ import { diffstatFromUnifiedPatch } from "../unified-patch.js";
2
3
  export const PI_EVENT_DISPOSITIONS = {
3
4
  agent_end: "drop",
4
5
  agent_settled: "drop",
@@ -82,9 +83,21 @@ function searchCall(name, value) {
82
83
  };
83
84
  }
84
85
  function fileChangeCall(name, value) {
85
- if ((name !== "edit" && name !== "write") || typeof value.path !== "string")
86
+ if (name !== "edit" || typeof value.path !== "string")
86
87
  return undefined;
87
- return { kind: "fileChange", changes: [{ op: name === "write" ? "add" : "update", path: value.path }] };
88
+ return { kind: "fileChange", changes: [{ op: "update", path: value.path }] };
89
+ }
90
+ function refineEditCall(call, result) {
91
+ if (call.kind !== "fileChange")
92
+ return call;
93
+ const details = record(record(result)?.details);
94
+ if (typeof details?.patch !== "string")
95
+ return call;
96
+ const diffstat = diffstatFromUnifiedPatch(details.patch);
97
+ const change = call.changes[0];
98
+ if (diffstat === undefined || change === undefined)
99
+ return call;
100
+ return { kind: "fileChange", changes: [{ ...change, diffstat }] };
88
101
  }
89
102
  function toolCall(name, args) {
90
103
  const value = record(args) ?? {};
@@ -108,9 +121,10 @@ function translateToolEnd(event, state) {
108
121
  const started = state.tools.get(event.toolCallId);
109
122
  state.tools.delete(event.toolCallId);
110
123
  const name = started?.name ?? event.toolName;
124
+ const call = started?.call ?? { kind: "other", display: name };
111
125
  return [{
112
126
  type: "tool", phase: "completed", id: event.toolCallId, name,
113
- call: started?.call ?? { kind: "other", display: name },
127
+ call: event.isError ? call : refineEditCall(call, event.result),
114
128
  result: { status: event.isError ? "error" : "ok" },
115
129
  }];
116
130
  }
@@ -0,0 +1,4 @@
1
+ export declare function diffstatFromUnifiedPatch(patch: string): Readonly<{
2
+ added: number;
3
+ removed: number;
4
+ }> | undefined;
@@ -0,0 +1,14 @@
1
+ export function diffstatFromUnifiedPatch(patch) {
2
+ const lines = patch.split("\n");
3
+ if (!lines.some((line) => /^@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@/u.test(line)))
4
+ return undefined;
5
+ let added = 0;
6
+ let removed = 0;
7
+ for (const line of lines) {
8
+ if (line.startsWith("+") && !line.startsWith("+++"))
9
+ added += 1;
10
+ if (line.startsWith("-") && !line.startsWith("---"))
11
+ removed += 1;
12
+ }
13
+ return { added, removed };
14
+ }
@@ -1,5 +1,5 @@
1
1
  import { type AllocatedAkuma } from "./identity.js";
2
- export declare const BIRTH_TIMEOUT_MS = 5000;
2
+ export declare const BIRTH_TIMEOUT_MS = 30000;
3
3
  export declare function publishAkuma(input: Readonly<{
4
4
  worldPath: string;
5
5
  archetype: string;
@@ -1,8 +1,8 @@
1
- import { HeldAkumaLeash, initializeHeart, readHeart, readSoul, } from "./heart/index.js";
1
+ import { HeldAkumaLeash, initializeHeart, readHeart, readSeal, readSoul, } from "./heart/index.js";
2
2
  import { allocateAkumaDirectory, } from "./identity.js";
3
3
  import { abortableDelay } from "./abort.js";
4
4
  const POLL_MS = 25;
5
- export const BIRTH_TIMEOUT_MS = 5_000;
5
+ export const BIRTH_TIMEOUT_MS = 30_000;
6
6
  function diagnostic(error) {
7
7
  return error instanceof Error ? error.message : String(error);
8
8
  }
@@ -10,15 +10,38 @@ async function settleTimedOutBirth(paths) {
10
10
  const leash = await HeldAkumaLeash.try(paths);
11
11
  if (leash === null)
12
12
  return null;
13
- const result = await leash.sealIfUnborn(paths, { evidence: "call-timeout", at: new Date().toISOString() });
14
- if (result === "sealed")
15
- throw new Error("Akuma body failed before birth");
16
- leash.release();
13
+ let result;
14
+ try {
15
+ result = await leash.sealIfUnborn(paths, {
16
+ evidence: "call-timeout",
17
+ at: new Date().toISOString(),
18
+ });
19
+ }
20
+ finally {
21
+ leash.release();
22
+ }
23
+ if (result === "sealed") {
24
+ const seal = await readSeal(paths);
25
+ throw new Error(seal?.evidence === "call-timeout" || seal === null
26
+ ? "Akuma body failed before birth"
27
+ : seal.evidence);
28
+ }
17
29
  const soul = await readSoul(paths);
18
30
  if (soul === null)
19
31
  throw new Error("Akuma birth settled without a soul");
20
32
  return soul;
21
33
  }
34
+ async function observedBirthFailure(paths) {
35
+ const leash = await HeldAkumaLeash.try(paths);
36
+ if (leash === null)
37
+ return null;
38
+ try {
39
+ return leash.readSeal()?.evidence ?? null;
40
+ }
41
+ finally {
42
+ leash.release();
43
+ }
44
+ }
22
45
  async function awaitBirth(paths, signal) {
23
46
  const deadline = performance.now() + BIRTH_TIMEOUT_MS;
24
47
  for (;;) {
@@ -26,6 +49,9 @@ async function awaitBirth(paths, signal) {
26
49
  const soul = await readSoul(paths);
27
50
  if (soul !== null)
28
51
  return soul;
52
+ const failure = await observedBirthFailure(paths);
53
+ if (failure !== null)
54
+ throw new Error(failure);
29
55
  if (performance.now() >= deadline) {
30
56
  const settled = await settleTimedOutBirth(paths);
31
57
  if (settled !== null)
@@ -63,7 +89,10 @@ async function sealLocalFailure(allocated, error) {
63
89
  if (leash === null)
64
90
  return;
65
91
  try {
66
- await leash.sealIfUnborn(allocated.paths, { evidence: diagnostic(error), at: new Date().toISOString() });
92
+ await leash.sealIfUnborn(allocated.paths, {
93
+ evidence: diagnostic(error),
94
+ at: new Date().toISOString(),
95
+ });
67
96
  }
68
97
  finally {
69
98
  leash.release();
@@ -1,12 +1,65 @@
1
- import { type RequestRecipe, type Soul } from "./heart/index.js";
1
+ import { type KillEvidence, type RequestRecipe, type Soul } from "./heart/index.js";
2
2
  import { type AkuId, type AkumaPaths } from "./identity.js";
3
- type RequestClaim = Readonly<{
3
+ type RequestClaimRecipe = Omit<RequestRecipe, "confinement">;
4
+ type CallRequestClaim = Readonly<{
4
5
  id: string;
6
+ action: "akuma.call";
5
7
  world: string;
6
8
  archetype: string;
7
9
  body: string;
8
10
  cwd?: string;
9
- recipe: RequestRecipe;
11
+ recipe: RequestClaimRecipe;
12
+ }>;
13
+ type WaitRequestClaim = Readonly<{
14
+ id: string;
15
+ action: "akuma.wait";
16
+ targets: readonly AkuId[];
17
+ completion: "any" | "all";
18
+ timeoutMs?: number;
19
+ }>;
20
+ type TellRequestClaim = Readonly<{
21
+ id: string;
22
+ action: "akuma.tell";
23
+ target: AkuId;
24
+ body: string;
25
+ }>;
26
+ type KillRequestClaim = Readonly<{
27
+ id: string;
28
+ action: "akuma.kill";
29
+ targets: readonly AkuId[];
30
+ }>;
31
+ type UpstreamRequestFailure = Readonly<{
32
+ kind: "akuma-not-born";
33
+ id: AkuId;
34
+ }> | Readonly<{
35
+ kind: "failed";
36
+ diagnostic: string;
37
+ }>;
38
+ export type UpstreamRequestOutcome = Readonly<{
39
+ kind: "returned";
40
+ result: unknown;
41
+ }> | Readonly<{
42
+ kind: "failed";
43
+ failure: UpstreamRequestFailure;
44
+ }>;
45
+ export type UpstreamExecutionPort = Readonly<{
46
+ wait(input: Omit<WaitRequestClaim, "id" | "action"> & Readonly<{
47
+ signal: AbortSignal;
48
+ }>): Promise<unknown>;
49
+ tell(input: Omit<TellRequestClaim, "id" | "action"> & Readonly<{
50
+ tellId: string;
51
+ recordedAt: string;
52
+ signal: AbortSignal;
53
+ }>): Promise<unknown>;
54
+ kill(input: Omit<KillRequestClaim, "id" | "action"> & Readonly<{
55
+ signal: AbortSignal;
56
+ }>): Promise<Readonly<{
57
+ result: unknown;
58
+ service: readonly Readonly<{
59
+ id: AkuId;
60
+ evidence: KillEvidence;
61
+ }>[];
62
+ }>>;
10
63
  }>;
11
64
  export type RequestChildLaunch = Readonly<{
12
65
  paths: AkumaPaths;
@@ -20,9 +73,21 @@ export declare class AkumaBodyRequestError extends Error {
20
73
  constructor(outcome: "refused" | "voided", diagnostic: string);
21
74
  }
22
75
  export declare function injectedBodyRequests(): string | null;
23
- export declare function requestBodyCall(input: RequestClaim & Readonly<{
76
+ export declare function requestBodyCall(input: Omit<CallRequestClaim, "action"> & Readonly<{
24
77
  directory: string;
25
78
  }>): Promise<AkuId>;
79
+ export declare function requestBodyWait(input: Omit<WaitRequestClaim, "action" | "id"> & Readonly<{
80
+ directory: string;
81
+ id?: string;
82
+ }>): Promise<UpstreamRequestOutcome>;
83
+ export declare function requestBodyTell(input: Omit<TellRequestClaim, "action" | "id"> & Readonly<{
84
+ directory: string;
85
+ id?: string;
86
+ }>): Promise<UpstreamRequestOutcome>;
87
+ export declare function requestBodyKill(input: Omit<KillRequestClaim, "action" | "id"> & Readonly<{
88
+ directory: string;
89
+ id?: string;
90
+ }>): Promise<UpstreamRequestOutcome>;
26
91
  export declare class BodyRequestPump {
27
92
  private readonly input;
28
93
  readonly directory: string;
@@ -38,6 +103,7 @@ export declare class BodyRequestPump {
38
103
  bodySequence: number;
39
104
  now(): string;
40
105
  spawn(launch: RequestChildLaunch): Promise<void>;
106
+ upstream?: UpstreamExecutionPort;
41
107
  signal: AbortSignal;
42
108
  }>): Promise<BodyRequestPump>;
43
109
  private run;