@astrosheep/keiyaku 4.5.19 → 4.5.20

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 (94) hide show
  1. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +11 -4
  2. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +33 -14
  3. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +0 -1
  4. package/build/src/akuma/akuma-observe.js +1 -0
  5. package/build/src/akuma/akuma-product.d.ts +1 -0
  6. package/build/src/akuma/akuma-product.js +3 -0
  7. package/build/src/akuma/akuma.d.ts +19 -1
  8. package/build/src/akuma/akuma.js +2 -0
  9. package/build/src/akuma/allowed.d.ts +20 -0
  10. package/build/src/akuma/allowed.js +11 -0
  11. package/build/src/akuma/archetype.js +3 -0
  12. package/build/src/akuma/body.js +5 -23
  13. package/build/src/akuma/call-request.d.ts +3 -0
  14. package/build/src/akuma/call-request.js +12 -4
  15. package/build/src/akuma/fleet-observation.d.ts +54 -0
  16. package/build/src/akuma/fleet-request.js +2 -1
  17. package/build/src/akuma/provider-recipe.d.ts +1 -0
  18. package/build/src/akuma/provider-recipe.js +5 -0
  19. package/build/src/akuma/providers/acp/index.js +3 -0
  20. package/build/src/akuma/providers/claude/index.js +3 -0
  21. package/build/src/akuma/providers/codex-app-server/index.js +8 -0
  22. package/build/src/akuma/providers/grok-build/index.js +3 -0
  23. package/build/src/akuma/providers/opencode-sdk/index.js +2 -0
  24. package/build/src/akuma/providers/pi/index.js +2 -0
  25. package/build/src/akuma/request-observation.d.ts +45 -0
  26. package/build/src/akuma/request-observation.js +162 -0
  27. package/build/src/akuma/request-rendezvous.d.ts +6 -3
  28. package/build/src/akuma/request-rendezvous.js +75 -14
  29. package/build/src/akuma/request-serve.js +27 -4
  30. package/build/src/akuma/request-wire.d.ts +5 -0
  31. package/build/src/akuma/request-wire.js +2 -0
  32. package/build/src/akuma-body.js +3 -0
  33. package/build/src/cli/commands/akuma-invoke.d.ts +1 -1
  34. package/build/src/cli/commands/akuma-invoke.js +1 -1
  35. package/build/src/cli/commands/akuma.js +13 -8
  36. package/build/src/cli/commands/contract-help.d.ts +15 -15
  37. package/build/src/cli/commands/contract-help.js +28 -15
  38. package/build/src/cli/commands/contract-invoke.d.ts +3 -0
  39. package/build/src/cli/commands/contract-invoke.js +25 -11
  40. package/build/src/cli/commands/install.d.ts +1 -1
  41. package/build/src/cli/commands/install.js +1 -1
  42. package/build/src/cli/commands/task.js +18 -18
  43. package/build/src/cli/invoke.d.ts +3 -0
  44. package/build/src/cli/invoke.js +58 -23
  45. package/build/src/cli/parse.d.ts +1 -0
  46. package/build/src/cli/parse.js +21 -4
  47. package/build/src/cli/render/akuma-activity.d.ts +1 -0
  48. package/build/src/cli/render/akuma-activity.js +1 -0
  49. package/build/src/cli/render/akuma.js +4 -1
  50. package/build/src/cli/render/execution-progress.d.ts +4 -0
  51. package/build/src/cli/render/execution-progress.js +55 -0
  52. package/build/src/cli/render/receipt.js +1 -1
  53. package/build/src/cli/runtime.js +27 -1
  54. package/build/src/git/hooks.d.ts +13 -1
  55. package/build/src/git/hooks.js +2 -1
  56. package/build/src/git/verification-environment.d.ts +10 -0
  57. package/build/src/git/verification-environment.js +281 -0
  58. package/build/src/library/akuma-creation.js +1 -1
  59. package/build/src/library/audit.d.ts +2 -0
  60. package/build/src/library/audit.js +1 -0
  61. package/build/src/library/composition.d.ts +1 -0
  62. package/build/src/library/contract-execution.d.ts +2 -0
  63. package/build/src/library/contract-execution.js +15 -1
  64. package/build/src/library/contract-forwarding.d.ts +4 -0
  65. package/build/src/library/contract-forwarding.js +3 -0
  66. package/build/src/library/contract-handle.d.ts +7 -0
  67. package/build/src/library/contract-handle.js +25 -0
  68. package/build/src/library/contract-operations.d.ts +5 -0
  69. package/build/src/library/contract-operations.js +20 -0
  70. package/build/src/library/execution-result.d.ts +2 -2
  71. package/build/src/library/execution.d.ts +8 -0
  72. package/build/src/library/execution.js +102 -0
  73. package/build/src/library/keiyaku.d.ts +2 -0
  74. package/build/src/library/mutation.js +15 -7
  75. package/build/src/plugin/akuma-signals.js +1 -1
  76. package/build/src/plugin/public.d.ts +2 -2
  77. package/build/src/plugin/runtime.d.ts +1 -3
  78. package/build/src/plugin/runtime.js +91 -102
  79. package/build/src/protocol/completion.js +28 -10
  80. package/build/src/protocol/execution-observation.d.ts +185 -0
  81. package/build/src/protocol/execution-observation.js +45 -0
  82. package/build/src/protocol/intent.d.ts +3 -2
  83. package/build/src/protocol/intent.js +44 -34
  84. package/build/src/protocol/progress.d.ts +5 -1
  85. package/build/src/protocol/progress.js +10 -1
  86. package/build/src/protocol/result-codec.js +18 -4
  87. package/build/src/runtime/proc/run.d.ts +16 -4
  88. package/build/src/runtime/proc/run.js +64 -22
  89. package/build/src/runtime/proc/windows-launch.exe +0 -0
  90. package/build/src/verification/execution.d.ts +11 -2
  91. package/build/src/verification/execution.js +167 -42
  92. package/build/src/verification/observation.d.ts +45 -0
  93. package/build/src/verification/observation.js +30 -0
  94. package/package.json +1 -1
@@ -36,15 +36,22 @@ keiyaku -C <repo> review [<contract>|@<contract>] --satisfied
36
36
  ```
37
37
 
38
38
  ```bash
39
- keiyaku -C <cwd> call <akuma-name> [--contract <kei/...>] [--alias @name] [--allowed <product.action>]... [--schema <file>] [--wait <duration> | -d | --detach] [--json] (<prompt> | -)
39
+ keiyaku -C <cwd> call <akuma-name> [--contract <kei/...>] [--workdir <path>] [--alias @name] [--allowed <product.action>]... [--schema <file>] [--wait <duration> | -d | --detach] (<prompt> | -)
40
40
  keiyaku -C <repo> wait <akuma-selector>... [--any | --all]
41
41
  keiyaku -C <repo> tell <aku/...|@alias> (<prompt> | -)
42
42
  ```
43
43
 
44
- `-C` is the invocation and Akuma execution cwd. Add `--repo <path>` only when
45
- a Contract or Dispatch reader must use a different Git repository.
44
+ `-C` selects the invocation cwd and therefore the World. `--repo <path>` only
45
+ selects the Contract repository; it never retargets that World. `call
46
+ --workdir <path>` selects execution cwd (relative to the effective invocation
47
+ cwd). Without it, a Contract call uses its appointed worktree and an
48
+ unassociated call uses the invocation cwd.
46
49
 
47
50
  Repeated `--allowed` values add actions to the selected Akuma's defaults. A
48
51
  nested call can use only actions permitted by its direct parent Soul.
49
52
 
50
- Use text by default; add `--json` when a script needs structured output.
53
+ `deliver`, `review`, and `audit` may take time after their leading admission.
54
+ Their witnessed progress is ephemeral stderr output; stdout remains one final
55
+ text or JSON result. Ctrl-C asks the owned operation to stop and waits for its
56
+ truthful final receipt, so it is not evidence that an already admitted action
57
+ was rolled back.
@@ -15,7 +15,7 @@ is accepted; the identity underneath never changes.
15
15
  ## Start One
16
16
 
17
17
  ```bash
18
- keiyaku -C <cwd> call <akuma-name> [--alias @name] [--allowed <product.action>]... [--schema <file>] [--wait <duration> | -d | --detach] [--json] (<prompt> | -)
18
+ keiyaku -C <cwd> call <akuma-name> [--workdir <path>] [--alias @name] [--allowed <product.action>]... [--schema <file>] [--wait <duration> | -d | --detach] (<prompt> | -)
19
19
  ```
20
20
 
21
21
  Give the worker's initial prompt as one argument (quote it when it contains
@@ -28,20 +28,30 @@ exclusive. Decide up front whether you will stay:
28
28
  work outlives your attention; come back with `wait`.
29
29
 
30
30
  `--alias @name` assigns that world-local selector to the born Akuma. If the
31
- Alias already points elsewhere, it moves to the born Akuma. The worker's
32
- execution cwd is exactly `-C <path>`, or your own cwd when `-C` is omitted.
31
+ Alias already points elsewhere, it moves to the born Akuma. `-C` selects the
32
+ invocation World; `--workdir <path>` selects the worker execution cwd relative
33
+ to that invocation directory. Without `--workdir`, an unassociated call uses
34
+ the invocation cwd, while a `--contract` call uses its appointed worktree.
33
35
 
34
- Repeated `--allowed` values add actions to the selected Akuma's defaults. A
35
- nested call can use only actions permitted by its direct parent Soul.
36
+ Repeated `--allowed` values add actions to the selected Akuma's defaults; they
37
+ never narrow them. An omitted Archetype default permits the complete action
38
+ vocabulary, while an explicit empty default permits none. A nested call can use
39
+ only actions permitted by its direct parent Soul. Use `status <aku/...|@alias>`
40
+ to inspect the born worker's frozen effective actions.
36
41
 
37
42
  ## Akuma Names
38
43
 
39
- An Akuma name selects a Markdown file at `~/.keiyaku/akuma/<name>.md`; pass
40
- the filename without `.md` as `<akuma-name>`. Its frontmatter selects the
41
- provider and may declare `model`, `effort`, `readonly`, `network`, and
42
- `description`; the body is an optional system prompt, and an empty body keeps
43
- the harness default. If none grants the permissions and stance the work needs,
44
- add a new Akuma name. A born Akuma keeps its selected defaults for its lifetime.
44
+ An Akuma name selects a reusable worker configuration, not an individual worker.
45
+ Calling the same name multiple times creates independent workers with distinct
46
+ AkuIds. Choose different names for different capabilities, not merely to run
47
+ work in parallel.
48
+
49
+ Each name fixes its own capability stance provider, model, permissions
50
+ and a born Akuma keeps those selected defaults for its lifetime. `keiyaku ls
51
+ aku/` lists the available names with their providers and descriptions. If none
52
+ grants the permissions and stance the work needs, add a new Akuma name;
53
+ `keiyaku settings --help` says where Akuma definitions live and what they may
54
+ declare.
45
55
 
46
56
  ## Commission And Steer
47
57
 
@@ -64,6 +74,13 @@ including nested calls, which stay under this Soul's ceiling — and state the
64
74
  loop as the question. Steering that delegation afterwards goes to the holder,
65
75
  not around it.
66
76
 
77
+ When that whole-loop worker must operate in the main repository rather than its
78
+ Contract worktree, make that execution choice explicit:
79
+
80
+ ```bash
81
+ keiyaku -C <repo> call worker --contract <kei/...> --workdir <repo> --allowed contract.deliver -d "Own this Contract loop."
82
+ ```
83
+
67
84
  ## Watch
68
85
 
69
86
  ```bash
@@ -78,9 +95,11 @@ a mode. When observing multiple Akuma, prefer one plural wait over separate
78
95
  waits and choose exactly one mode:
79
96
 
80
97
  ```bash
81
- keiyaku -C <cwd> wait @worker-a --timeout 5m
82
- keiyaku -C <cwd> wait @worker-a @worker-b --all --timeout 5m
83
- keiyaku -C <cwd> wait @worker-a @worker-b --any --timeout 5m
98
+ keiyaku -C <cwd> call worker --alias @projection -d "Inspect the projection."
99
+ keiyaku -C <cwd> call worker --alias @host-boundary -d "Inspect the host boundary."
100
+ keiyaku -C <cwd> wait @projection --timeout 5m
101
+ keiyaku -C <cwd> wait @projection @host-boundary --all --timeout 5m
102
+ keiyaku -C <cwd> wait @projection @host-boundary --any --timeout 5m
84
103
  ```
85
104
 
86
105
  `--all` waits until every selected Akuma stops running. `--any` returns when the
@@ -62,7 +62,6 @@ keiyaku task update <task-id> --title <text>
62
62
  keiyaku task context [<namespace>]
63
63
  ```
64
64
 
65
- Use `--json` on any command when a script must inspect the typed result.
66
65
  Relations are explicit facts: `needs` orders work, `parent` groups it,
67
66
  `supersedes` navigates replacement, and `relates` does not affect readiness.
68
67
  `task start` accepts one or more complete TaskIds. A single ID keeps the
@@ -72,6 +72,7 @@ export async function bornStatus(paths, expected, input) {
72
72
  status: {
73
73
  id: soul.id,
74
74
  life: currentLife,
75
+ allowed: soul.allowed,
75
76
  ...(soul.readonly === undefined ? {} : { readonly: soul.readonly }),
76
77
  ...(resumeUnsupported ? { strandedReason: "resume-unsupported" } : {}),
77
78
  timeline: selected.snapshot,
@@ -22,6 +22,7 @@ export type BornAkumaCall = Readonly<{
22
22
  }>;
23
23
  initialBody?: string;
24
24
  initialSchemaJson?: string;
25
+ awaitAsleep?: true;
25
26
  execution: BornExecution;
26
27
  }>;
27
28
  export type RequestedAkumaCall = Readonly<{
@@ -26,6 +26,7 @@ async function admitBodyRequest(input) {
26
26
  world: input.path,
27
27
  archetype: input.name,
28
28
  ...(input.call.body === undefined ? {} : { body: input.call.body }),
29
+ ...(input.call.schema === undefined ? {} : { awaitAsleep: true }),
29
30
  ...(cwd === undefined ? {} : { cwd }),
30
31
  recipe: input.recipe,
31
32
  });
@@ -56,6 +57,7 @@ async function admitDirect(input) {
56
57
  },
57
58
  ...(input.call.body === undefined ? {} : { initialBody: input.call.body }),
58
59
  ...(input.call.schema === undefined ? {} : { initialSchemaJson: schemaJsonText(input.call.schema) }),
60
+ ...(input.call.schema === undefined ? {} : { awaitAsleep: true }),
59
61
  execution: {
60
62
  cwd,
61
63
  source: input.call.cwd !== undefined ? "input" : initiatorCwd === undefined ? "world" : "process",
@@ -206,6 +208,7 @@ class AkumaProduct {
206
208
  }
207
209
  const published = await launchAkuma({
208
210
  allocated: born.allocated,
211
+ ...(born.awaitAsleep === undefined ? {} : { awaitAsleep: born.awaitAsleep }),
209
212
  launch: async (allocated) => await spawnAkumaBody({
210
213
  paths: allocated.paths,
211
214
  seed: born.seed,
@@ -3,7 +3,7 @@ export type { KillEvidence };
3
3
  import { type AkuId } from "./identity.js";
4
4
  import type { Settings } from "../settings.js";
5
5
  import type { WorldRoot } from "../world.js";
6
- import type { AllowedAction } from "./allowed.js";
6
+ import { type AllowedAction } from "./allowed.js";
7
7
  import type { ExecutionContext } from "./requests.js";
8
8
  import { z } from "zod";
9
9
  import type { BoundedList } from "../bounded-list.js";
@@ -32,6 +32,24 @@ export declare const akumaStatusSchema: z.ZodObject<{
32
32
  asleep: "asleep";
33
33
  stranded: "stranded";
34
34
  }>;
35
+ allowed: z.ZodPipe<z.ZodArray<z.ZodEnum<{
36
+ "task.add": "task.add";
37
+ "task.addDocument": "task.addDocument";
38
+ "task.compose": "task.compose";
39
+ "task.done": "task.done";
40
+ "task.drop": "task.drop";
41
+ "task.hold": "task.hold";
42
+ "task.resume": "task.resume";
43
+ "task.start": "task.start";
44
+ "task.stop": "task.stop";
45
+ "task.update": "task.update";
46
+ "akuma.call": "akuma.call";
47
+ "akuma.kill": "akuma.kill";
48
+ "akuma.tell": "akuma.tell";
49
+ "contract.audit": "contract.audit";
50
+ "contract.deliver": "contract.deliver";
51
+ "contract.review": "contract.review";
52
+ }>>, z.ZodTransform<import("./allowed.js").AllowedActions, ("task.add" | "task.addDocument" | "task.compose" | "task.done" | "task.drop" | "task.hold" | "task.resume" | "task.start" | "task.stop" | "task.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.audit" | "contract.deliver" | "contract.review")[]>>;
35
53
  readonly: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
36
54
  enforcement: z.ZodLiteral<"native">;
37
55
  }, z.core.$strict>, z.ZodObject<{
@@ -1,5 +1,6 @@
1
1
  import { parseAkuId } from "./identity.js";
2
2
  import { activitySnapshotSchema } from "./projection.js";
3
+ import { allowedActionsSchema } from "./allowed.js";
3
4
  import { z } from "zod";
4
5
  import { createAkumaProduct } from "./akuma-product.js";
5
6
  import { killAkumaWithRecovery } from "./akuma-handle.js";
@@ -25,6 +26,7 @@ export const akumaStatusSchema = z
25
26
  .object({
26
27
  id: akumaIdSchema,
27
28
  life: z.enum(["running", "asleep", "stranded", "hung", "untidy", "killed"]),
29
+ allowed: allowedActionsSchema,
28
30
  readonly: readonlySchema.optional(),
29
31
  timeline: activitySnapshotSchema,
30
32
  strandedReason: z.literal("resume-unsupported").optional(),
@@ -1,8 +1,28 @@
1
+ import { z } from "zod";
1
2
  export declare const ALLOWED_ACTIONS: readonly ["akuma.call", "akuma.kill", "akuma.tell", "contract.audit", "contract.deliver", "contract.review", "task.add", "task.addDocument", "task.compose", "task.done", "task.drop", "task.hold", "task.resume", "task.start", "task.stop", "task.update"];
2
3
  export type AllowedAction = (typeof ALLOWED_ACTIONS)[number];
3
4
  export type AllowedActions = readonly AllowedAction[];
4
5
  export declare function isAllowedAction(value: unknown): value is AllowedAction;
5
6
  export declare function decodeAllowedActions(value: unknown, label?: string): AllowedActions;
7
+ /** Public codecs retain the Soul's canonical, duplicate-free action membership. */
8
+ export declare const allowedActionsSchema: z.ZodPipe<z.ZodArray<z.ZodEnum<{
9
+ "task.add": "task.add";
10
+ "task.addDocument": "task.addDocument";
11
+ "task.compose": "task.compose";
12
+ "task.done": "task.done";
13
+ "task.drop": "task.drop";
14
+ "task.hold": "task.hold";
15
+ "task.resume": "task.resume";
16
+ "task.start": "task.start";
17
+ "task.stop": "task.stop";
18
+ "task.update": "task.update";
19
+ "akuma.call": "akuma.call";
20
+ "akuma.kill": "akuma.kill";
21
+ "akuma.tell": "akuma.tell";
22
+ "contract.audit": "contract.audit";
23
+ "contract.deliver": "contract.deliver";
24
+ "contract.review": "contract.review";
25
+ }>>, z.ZodTransform<AllowedActions, ("task.add" | "task.addDocument" | "task.compose" | "task.done" | "task.drop" | "task.hold" | "task.resume" | "task.start" | "task.stop" | "task.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.audit" | "contract.deliver" | "contract.review")[]>>;
6
26
  export declare function effectiveAllowedActions(value: unknown): AllowedActions;
7
27
  export declare function unionAllowedActions(base: AllowedActions, additions: AllowedActions): AllowedActions;
8
28
  export declare function clipAllowedActions(requested: AllowedActions, parent: AllowedActions): AllowedActions;
@@ -1,4 +1,5 @@
1
1
  import { TASK_MUTATION_ACTIONS } from "../task/mutation.js";
2
+ import { z } from "zod";
2
3
  export const ALLOWED_ACTIONS = Object.freeze([
3
4
  "akuma.call",
4
5
  "akuma.kill",
@@ -25,6 +26,16 @@ export function decodeAllowedActions(value, label = "allowed") {
25
26
  }
26
27
  return Object.freeze([...seen].sort());
27
28
  }
29
+ /** Public codecs retain the Soul's canonical, duplicate-free action membership. */
30
+ export const allowedActionsSchema = z.array(z.enum(ALLOWED_ACTIONS)).transform((value, context) => {
31
+ try {
32
+ return decodeAllowedActions(value);
33
+ }
34
+ catch (error) {
35
+ context.addIssue({ code: "custom", message: error instanceof Error ? error.message : String(error) });
36
+ return z.NEVER;
37
+ }
38
+ });
28
39
  export function effectiveAllowedActions(value) {
29
40
  return value === undefined ? ALLOWED_ACTIONS : decodeAllowedActions(value);
30
41
  }
@@ -132,6 +132,7 @@ function decodeArchetypeFields(values) {
132
132
  const effort = archetypeField(values, "effort");
133
133
  const readonly = archetypeReadonly(values);
134
134
  const network = archetypeEnum(values, "network", ["disabled", "enabled"]);
135
+ const sandbox = archetypeEnum(values, "sandbox", ["full-access"]);
135
136
  const description = archetypeField(values, "description");
136
137
  const allowedPresent = "allowed" in values;
137
138
  const allowed = allowedPresent ? effectiveAllowedActions(values.allowed) : undefined;
@@ -143,6 +144,7 @@ function decodeArchetypeFields(values) {
143
144
  ...(effort === undefined ? {} : { effort }),
144
145
  ...(readonly === undefined ? {} : { readonly }),
145
146
  ...(network === undefined ? {} : { network }),
147
+ ...(sandbox === undefined ? {} : { sandbox }),
146
148
  ...(description === undefined ? {} : { description }),
147
149
  ...(allowed === undefined ? {} : { allowed }),
148
150
  allowedPresent,
@@ -166,6 +168,7 @@ function decodeArchetype(name, path, markdown) {
166
168
  ...(fields.model === undefined ? {} : { model: fields.model }),
167
169
  ...(fields.effort === undefined ? {} : { effort: fields.effort }),
168
170
  ...(fields.network === undefined ? {} : { network: fields.network }),
171
+ ...(fields.sandbox === undefined ? {} : { sandbox: fields.sandbox }),
169
172
  ...(systemPrompt.length === 0
170
173
  ? {}
171
174
  : {
@@ -231,13 +231,11 @@ async function recoverBodyRequests(input) {
231
231
  }
232
232
  function turnOutcomeEmitter(launch, soul) {
233
233
  let plugins;
234
- const pending = new Set();
235
234
  const reportDiagnostic = (message) => {
236
235
  void recordPluginDiagnostic(launch.paths, "turn outcome", message);
237
236
  };
238
- const emit = (turnSequence, outcome) => {
239
- let delivery;
240
- delivery = (async () => {
237
+ return {
238
+ async emit(turnSequence, outcome) {
241
239
  try {
242
240
  plugins ??= pluginRuntime({
243
241
  world: await World.at(worldRootForAkumaPaths(launch.paths)),
@@ -256,17 +254,8 @@ function turnOutcomeEmitter(launch, soul) {
256
254
  catch (error) {
257
255
  await recordPluginDiagnostic(launch.paths, "turn outcome", error);
258
256
  }
259
- })().finally(() => pending.delete(delivery));
260
- pending.add(delivery);
261
- return delivery;
262
- };
263
- const drain = async () => {
264
- while (pending.size > 0)
265
- await Promise.all([...pending]);
266
- if (plugins !== undefined)
267
- await (await plugins).drain("akuma.turn-outcome");
257
+ },
268
258
  };
269
- return { emit, drain };
270
259
  }
271
260
  function bodyEndEmitter(launch, soul) {
272
261
  let plugins;
@@ -288,7 +277,6 @@ function bodyEndEmitter(launch, soul) {
288
277
  ...(diagnostic === undefined ? {} : { diagnostic }),
289
278
  ...(launch.completion?.contractId === undefined ? {} : { contractId: launch.completion.contractId }),
290
279
  }, reportDiagnostic);
291
- await (await plugins).drain("akuma.body-ended");
292
280
  }
293
281
  catch (error) {
294
282
  await recordPluginDiagnostic(launch.paths, "body end", error);
@@ -346,7 +334,7 @@ async function runBodyTurns(input) {
346
334
  return;
347
335
  }
348
336
  const outcome = await persistTurn(launch.paths, result.turnSequence, result, runtime.now());
349
- void turnOutcome.emit(result.turnSequence, outcome);
337
+ await turnOutcome.emit(result.turnSequence, outcome);
350
338
  if (outcome.outcome === "failed") {
351
339
  await failOpenBoundTurnsIfPresent(launch.paths, bodySequence, outcome.diagnostic, runtime.now());
352
340
  await breakBody(launch.paths, { sequence: bodySequence, end: "broke-off", at: runtime.now() });
@@ -508,19 +496,13 @@ export async function driveAkumaBody(launch, adapter, runtimeInput = {}) {
508
496
  await resolveDecidedPendingTellDisposition(launch.paths, finished.decided, runtime.spawnBody ?? spawnAkumaBody);
509
497
  if (finished.notification !== undefined)
510
498
  await finished.notification;
511
- void emitTurnOutcome?.drain();
512
499
  }
513
500
  }
514
501
  export async function spawnAkumaBody(launch) {
515
502
  return await spawnDetachedProcess(await bodyProcessInput(launch));
516
503
  }
517
504
  export async function runAkumaBody(launch, world, externalCommands) {
518
- try {
519
- return await driveAkumaBody(launch, undefined, { world, externalCommands });
520
- }
521
- finally {
522
- await (await pluginRuntime({ world })).drain();
523
- }
505
+ return await driveAkumaBody(launch, undefined, { world, externalCommands });
524
506
  }
525
507
  const DIRECT_TELL_WAKE = {
526
508
  spawn: async (paths) => await spawnAkumaBody({ paths, refuseIfHeld: true }),
@@ -19,6 +19,7 @@ declare const akumaCallRecipeSchema: z.ZodObject<{
19
19
  effort?: string;
20
20
  readonly?: true;
21
21
  network?: "disabled" | "enabled";
22
+ sandbox?: "full-access";
22
23
  systemPrompt?: string;
23
24
  systemPromptMode?: import("./provider-recipe.js").SystemPromptMode;
24
25
  }>, unknown>>;
@@ -28,6 +29,7 @@ declare const akumaCallPayloadSchema: z.ZodObject<{
28
29
  world: z.ZodString;
29
30
  archetype: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
30
31
  body: z.ZodOptional<z.ZodString>;
32
+ awaitAsleep: z.ZodOptional<z.ZodLiteral<true>>;
31
33
  cwd: z.ZodOptional<z.ZodString>;
32
34
  recipe: z.ZodObject<{
33
35
  description: z.ZodOptional<z.ZodString>;
@@ -44,6 +46,7 @@ declare const akumaCallPayloadSchema: z.ZodObject<{
44
46
  effort?: string;
45
47
  readonly?: true;
46
48
  network?: "disabled" | "enabled";
49
+ sandbox?: "full-access";
47
50
  systemPrompt?: string;
48
51
  systemPromptMode?: import("./provider-recipe.js").SystemPromptMode;
49
52
  }>, unknown>>;
@@ -4,7 +4,7 @@ import { refuseRequest, reserveRequest } from "./heart/index.js";
4
4
  import { archetypeName, parseAkuId } from "./identity.js";
5
5
  import { publishAkuma } from "./publication.js";
6
6
  import { decodeProviderOptions, decodeReadonlyRestraint } from "./provider-recipe.js";
7
- import { decodeProviderExecution } from "./providers/index.js";
7
+ import { decodeProviderExecution, resolveProviderExecution } from "./providers/index.js";
8
8
  import { requestBodyCommand } from "./request-rendezvous.js";
9
9
  import { eraseRequestCommand, } from "./request-wire.js";
10
10
  import { z } from "zod";
@@ -67,6 +67,7 @@ const akumaCallPayloadSchema = z
67
67
  world: absolutePathSchema,
68
68
  archetype: archetypeSchema,
69
69
  body: z.string().optional(),
70
+ awaitAsleep: z.literal(true).optional(),
70
71
  cwd: absolutePathSchema.optional(),
71
72
  recipe: akumaCallRecipeSchema,
72
73
  })
@@ -86,17 +87,24 @@ async function executeAkumaCall(request, facts, capabilities) {
86
87
  await refuseRequest(paths, facts.id, `request world ${requestWorld} does not match ${world}`);
87
88
  throw new Error(`request world ${requestWorld} does not match ${world}`);
88
89
  }
90
+ const selected = await resolveProviderExecution(request.recipe.provider);
91
+ const admission = selected.adapter.admitOptions(request.recipe.options);
92
+ if (admission.kind === "refused") {
93
+ await refuseRequest(paths, facts.id, admission.diagnostic);
94
+ throw new Error(admission.diagnostic);
95
+ }
89
96
  const recipe = {
90
97
  ...(request.recipe.description === undefined ? {} : { description: request.recipe.description }),
91
98
  allowed: clipAllowedActions(request.recipe.allowed, parent.allowed),
92
- provider: request.recipe.provider,
93
- options: request.recipe.options,
94
- ...(request.recipe.readonly === undefined ? {} : { readonly: request.recipe.readonly }),
99
+ provider: selected.execution,
100
+ options: admission.options,
101
+ ...(admission.readonly === undefined ? {} : { readonly: admission.readonly }),
95
102
  };
96
103
  const published = await publishAkuma({
97
104
  worldPath: world,
98
105
  archetype: request.archetype,
99
106
  signal: facts.signal,
107
+ ...(request.awaitAsleep === undefined ? {} : { awaitAsleep: request.awaitAsleep }),
100
108
  launch: async (allocated) => {
101
109
  if (!facts.admissionOpen())
102
110
  throw new Error("body closed request admission");
@@ -10,6 +10,24 @@ declare const akumaObservationSchema: z.ZodObject<{
10
10
  asleep: "asleep";
11
11
  stranded: "stranded";
12
12
  }>;
13
+ allowed: z.ZodPipe<z.ZodArray<z.ZodEnum<{
14
+ "task.add": "task.add";
15
+ "task.addDocument": "task.addDocument";
16
+ "task.compose": "task.compose";
17
+ "task.done": "task.done";
18
+ "task.drop": "task.drop";
19
+ "task.hold": "task.hold";
20
+ "task.resume": "task.resume";
21
+ "task.start": "task.start";
22
+ "task.stop": "task.stop";
23
+ "task.update": "task.update";
24
+ "akuma.call": "akuma.call";
25
+ "akuma.kill": "akuma.kill";
26
+ "akuma.tell": "akuma.tell";
27
+ "contract.audit": "contract.audit";
28
+ "contract.deliver": "contract.deliver";
29
+ "contract.review": "contract.review";
30
+ }>>, z.ZodTransform<import("./allowed.js").AllowedActions, ("task.add" | "task.addDocument" | "task.compose" | "task.done" | "task.drop" | "task.hold" | "task.resume" | "task.start" | "task.stop" | "task.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.audit" | "contract.deliver" | "contract.review")[]>>;
13
31
  readonly: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
14
32
  enforcement: z.ZodLiteral<"native">;
15
33
  }, z.core.$strict>, z.ZodObject<{
@@ -446,6 +464,24 @@ declare const akumaWaitResultSchema: z.ZodObject<{
446
464
  asleep: "asleep";
447
465
  stranded: "stranded";
448
466
  }>;
467
+ allowed: z.ZodPipe<z.ZodArray<z.ZodEnum<{
468
+ "task.add": "task.add";
469
+ "task.addDocument": "task.addDocument";
470
+ "task.compose": "task.compose";
471
+ "task.done": "task.done";
472
+ "task.drop": "task.drop";
473
+ "task.hold": "task.hold";
474
+ "task.resume": "task.resume";
475
+ "task.start": "task.start";
476
+ "task.stop": "task.stop";
477
+ "task.update": "task.update";
478
+ "akuma.call": "akuma.call";
479
+ "akuma.kill": "akuma.kill";
480
+ "akuma.tell": "akuma.tell";
481
+ "contract.audit": "contract.audit";
482
+ "contract.deliver": "contract.deliver";
483
+ "contract.review": "contract.review";
484
+ }>>, z.ZodTransform<import("./allowed.js").AllowedActions, ("task.add" | "task.addDocument" | "task.compose" | "task.done" | "task.drop" | "task.hold" | "task.resume" | "task.start" | "task.stop" | "task.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.audit" | "contract.deliver" | "contract.review")[]>>;
449
485
  readonly: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
450
486
  enforcement: z.ZodLiteral<"native">;
451
487
  }, z.core.$strict>, z.ZodObject<{
@@ -1001,6 +1037,24 @@ export declare const fleetResultSchemas: {
1001
1037
  asleep: "asleep";
1002
1038
  stranded: "stranded";
1003
1039
  }>;
1040
+ allowed: z.ZodPipe<z.ZodArray<z.ZodEnum<{
1041
+ "task.add": "task.add";
1042
+ "task.addDocument": "task.addDocument";
1043
+ "task.compose": "task.compose";
1044
+ "task.done": "task.done";
1045
+ "task.drop": "task.drop";
1046
+ "task.hold": "task.hold";
1047
+ "task.resume": "task.resume";
1048
+ "task.start": "task.start";
1049
+ "task.stop": "task.stop";
1050
+ "task.update": "task.update";
1051
+ "akuma.call": "akuma.call";
1052
+ "akuma.kill": "akuma.kill";
1053
+ "akuma.tell": "akuma.tell";
1054
+ "contract.audit": "contract.audit";
1055
+ "contract.deliver": "contract.deliver";
1056
+ "contract.review": "contract.review";
1057
+ }>>, z.ZodTransform<import("./allowed.js").AllowedActions, ("task.add" | "task.addDocument" | "task.compose" | "task.done" | "task.drop" | "task.hold" | "task.resume" | "task.start" | "task.stop" | "task.update" | "akuma.call" | "akuma.kill" | "akuma.tell" | "contract.audit" | "contract.deliver" | "contract.review")[]>>;
1004
1058
  readonly: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
1005
1059
  enforcement: z.ZodLiteral<"native">;
1006
1060
  }, z.core.$strict>, z.ZodObject<{
@@ -100,7 +100,8 @@ export function fleetRequestProtocol(action) {
100
100
  decodeResult: (result) => decodedFleetResult(action, result),
101
101
  decodeReference: (reference) => decodeFleetService(action, reference),
102
102
  isPermitted: (allowed) => action === "akuma.wait" ||
103
- ((action === "akuma.tell" || action === "akuma.tell-answer") && allowed.includes("akuma.tell")),
103
+ ((action === "akuma.tell" || action === "akuma.tell-answer") && allowed.includes("akuma.tell")) ||
104
+ (action === "akuma.kill" && allowed.includes("akuma.kill")),
104
105
  };
105
106
  }
106
107
  export function fleetRequestCommand(action, port) {
@@ -4,6 +4,7 @@ export type ProviderOptions = Readonly<{
4
4
  effort?: string;
5
5
  readonly?: true;
6
6
  network?: "disabled" | "enabled";
7
+ sandbox?: "full-access";
7
8
  systemPrompt?: string;
8
9
  systemPromptMode?: SystemPromptMode;
9
10
  }>;
@@ -33,6 +33,10 @@ export function decodeProviderOptions(value) {
33
33
  if (network !== undefined && network !== "disabled" && network !== "enabled") {
34
34
  throw new TypeError("provider option network must be disabled, enabled");
35
35
  }
36
+ const sandbox = options.sandbox;
37
+ if (sandbox !== undefined && sandbox !== "full-access") {
38
+ throw new TypeError("provider option sandbox must be full-access");
39
+ }
36
40
  const systemPrompt = optionText(options, "systemPrompt", "allow");
37
41
  const systemPromptMode = options.systemPromptMode;
38
42
  if (systemPromptMode !== undefined) {
@@ -48,6 +52,7 @@ export function decodeProviderOptions(value) {
48
52
  ...(effort === undefined ? {} : { effort }),
49
53
  ...(options.readonly === undefined ? {} : { readonly: true }),
50
54
  ...(network === undefined ? {} : { network }),
55
+ ...(sandbox === undefined ? {} : { sandbox }),
51
56
  ...(systemPrompt === undefined ? {} : { systemPrompt }),
52
57
  ...(systemPromptMode === undefined ? {} : { systemPromptMode }),
53
58
  });
@@ -3,6 +3,9 @@ import { decodeAcpConfig } from "./config.js";
3
3
  import { startAcpSession } from "./core.js";
4
4
  export { decodeAcpConfig } from "./config.js";
5
5
  function optionAdmission(options, config) {
6
+ if (options.sandbox !== undefined) {
7
+ return { kind: "refused", diagnostic: "ACP provider does not support the sandbox option" };
8
+ }
6
9
  if (options.network !== undefined) {
7
10
  return { kind: "refused", diagnostic: "ACP provider does not support the network option" };
8
11
  }
@@ -38,6 +38,9 @@ class ReceiptChannel {
38
38
  }
39
39
  }
40
40
  function admitClaudeOptions(options) {
41
+ if (options.sandbox !== undefined) {
42
+ return { kind: "refused", diagnostic: "Claude provider does not support the sandbox option" };
43
+ }
41
44
  if (options.network !== undefined) {
42
45
  return { kind: "refused", diagnostic: "Claude provider does not support the network option" };
43
46
  }
@@ -48,6 +48,8 @@ async function steerTurn(server, state, tell) {
48
48
  return { kind: "accepted", fence: `${accepted}:${tell.id}` };
49
49
  }
50
50
  function sandbox(cwd, options, requests) {
51
+ if (options.sandbox === "full-access")
52
+ return { type: "dangerFullAccess" };
51
53
  if (options.readonly === true) {
52
54
  return { type: "readOnly", networkAccess: options.network === "enabled" };
53
55
  }
@@ -228,6 +230,12 @@ export function createCodexAppServerProvider(input = "codex") {
228
230
  const execution = typeof input === "string" ? { name: "codex-app-server", kind: "codex-app-server", executable: input } : input;
229
231
  return {
230
232
  admitOptions(options) {
233
+ if (options.sandbox === "full-access" && options.readonly === true) {
234
+ return { kind: "refused", diagnostic: "Codex full-access sandbox cannot combine with readonly" };
235
+ }
236
+ if (options.sandbox === "full-access" && options.network === "disabled") {
237
+ return { kind: "refused", diagnostic: "Codex full-access sandbox cannot combine with disabled network" };
238
+ }
231
239
  return {
232
240
  kind: "admitted",
233
241
  options: Object.freeze({ ...options }),
@@ -67,6 +67,9 @@ export const interpretGrokTool = (update) => {
67
67
  fileChangeCall(name, input));
68
68
  };
69
69
  function optionAdmission(options) {
70
+ if (options.sandbox !== undefined) {
71
+ return { kind: "refused", diagnostic: "Grok Build does not support the sandbox option" };
72
+ }
70
73
  if (options.network !== undefined) {
71
74
  return { kind: "refused", diagnostic: "Grok Build does not support the network option" };
72
75
  }
@@ -22,6 +22,8 @@ function opencodeSessionId(coordinateValue) {
22
22
  return coordinateValue.sessionId;
23
23
  }
24
24
  function admit(options) {
25
+ if (options.sandbox !== undefined)
26
+ throw new Error("OpenCode does not support the sandbox option");
25
27
  if (options.network !== undefined)
26
28
  throw new Error("OpenCode does not support explicit network");
27
29
  if (options.systemPromptMode === "replace") {
@@ -7,6 +7,8 @@ function diagnostic(error) {
7
7
  return error instanceof Error ? error.message : String(error);
8
8
  }
9
9
  function admitPiOptions(options) {
10
+ if (options.sandbox !== undefined)
11
+ return { kind: "refused", diagnostic: "Pi provider does not support the sandbox option" };
10
12
  if (options.network !== undefined)
11
13
  return { kind: "refused", diagnostic: "Pi provider does not support the network option" };
12
14
  if (options.model !== undefined && !MODEL_PATTERN.test(options.model)) {