@astrosheep/keiyaku 4.5.18 → 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 (115) 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-handle.js +10 -10
  5. package/build/src/akuma/akuma-observe.d.ts +2 -0
  6. package/build/src/akuma/akuma-observe.js +35 -25
  7. package/build/src/akuma/akuma-product.d.ts +1 -0
  8. package/build/src/akuma/akuma-product.js +3 -0
  9. package/build/src/akuma/akuma.d.ts +20 -3
  10. package/build/src/akuma/akuma.js +3 -5
  11. package/build/src/akuma/allowed.d.ts +20 -0
  12. package/build/src/akuma/allowed.js +11 -0
  13. package/build/src/akuma/archetype.js +3 -0
  14. package/build/src/akuma/body.js +11 -36
  15. package/build/src/akuma/call-request.d.ts +3 -0
  16. package/build/src/akuma/call-request.js +12 -4
  17. package/build/src/akuma/fleet-execution.js +35 -11
  18. package/build/src/akuma/fleet-observation.d.ts +54 -0
  19. package/build/src/akuma/fleet-request.js +2 -1
  20. package/build/src/akuma/heart/index.d.ts +11 -17
  21. package/build/src/akuma/heart/index.js +91 -81
  22. package/build/src/akuma/heart/request-authority.js +19 -19
  23. package/build/src/akuma/heart/schema.d.ts +1 -1
  24. package/build/src/akuma/heart/schema.js +19 -6
  25. package/build/src/akuma/heart/storage.d.ts +2 -4
  26. package/build/src/akuma/heart/storage.js +63 -63
  27. package/build/src/akuma/heart/tells.d.ts +9 -19
  28. package/build/src/akuma/heart/tells.js +76 -76
  29. package/build/src/akuma/heart/timeline.d.ts +7 -1
  30. package/build/src/akuma/heart/timeline.js +62 -53
  31. package/build/src/akuma/provider-recipe.d.ts +1 -0
  32. package/build/src/akuma/provider-recipe.js +5 -0
  33. package/build/src/akuma/providers/acp/index.js +3 -0
  34. package/build/src/akuma/providers/claude/index.js +3 -0
  35. package/build/src/akuma/providers/codex-app-server/index.js +8 -0
  36. package/build/src/akuma/providers/grok-build/index.js +3 -0
  37. package/build/src/akuma/providers/opencode-sdk/index.js +2 -0
  38. package/build/src/akuma/providers/pi/index.js +2 -0
  39. package/build/src/akuma/request-observation.d.ts +45 -0
  40. package/build/src/akuma/request-observation.js +162 -0
  41. package/build/src/akuma/request-rendezvous.d.ts +6 -3
  42. package/build/src/akuma/request-rendezvous.js +75 -14
  43. package/build/src/akuma/request-serve.js +27 -4
  44. package/build/src/akuma/request-wire.d.ts +5 -0
  45. package/build/src/akuma/request-wire.js +2 -0
  46. package/build/src/akuma-body.js +3 -0
  47. package/build/src/cli/commands/akuma-invoke.d.ts +1 -1
  48. package/build/src/cli/commands/akuma-invoke.js +1 -1
  49. package/build/src/cli/commands/akuma.js +13 -8
  50. package/build/src/cli/commands/contract-help.d.ts +15 -15
  51. package/build/src/cli/commands/contract-help.js +28 -15
  52. package/build/src/cli/commands/contract-invoke.d.ts +3 -0
  53. package/build/src/cli/commands/contract-invoke.js +25 -11
  54. package/build/src/cli/commands/install.d.ts +1 -1
  55. package/build/src/cli/commands/install.js +1 -1
  56. package/build/src/cli/commands/task.js +18 -18
  57. package/build/src/cli/invoke.d.ts +3 -0
  58. package/build/src/cli/invoke.js +58 -23
  59. package/build/src/cli/parse.d.ts +1 -0
  60. package/build/src/cli/parse.js +21 -4
  61. package/build/src/cli/render/akuma-activity.d.ts +1 -0
  62. package/build/src/cli/render/akuma-activity.js +1 -0
  63. package/build/src/cli/render/akuma.js +4 -1
  64. package/build/src/cli/render/execution-progress.d.ts +4 -0
  65. package/build/src/cli/render/execution-progress.js +55 -0
  66. package/build/src/cli/render/receipt.js +1 -1
  67. package/build/src/cli/runtime.js +27 -1
  68. package/build/src/git/hooks.d.ts +13 -1
  69. package/build/src/git/hooks.js +2 -1
  70. package/build/src/git/integration.js +4 -4
  71. package/build/src/git/process.d.ts +2 -0
  72. package/build/src/git/process.js +106 -0
  73. package/build/src/git/read-observation.js +79 -56
  74. package/build/src/git/target-placement.d.ts +13 -2
  75. package/build/src/git/target-placement.js +119 -68
  76. package/build/src/git/verification-environment.d.ts +10 -0
  77. package/build/src/git/verification-environment.js +281 -0
  78. package/build/src/library/akuma-creation.js +5 -3
  79. package/build/src/library/audit.d.ts +2 -0
  80. package/build/src/library/audit.js +1 -0
  81. package/build/src/library/composition.d.ts +1 -0
  82. package/build/src/library/contract-execution.d.ts +2 -0
  83. package/build/src/library/contract-execution.js +15 -1
  84. package/build/src/library/contract-forwarding.d.ts +4 -0
  85. package/build/src/library/contract-forwarding.js +3 -0
  86. package/build/src/library/contract-handle.d.ts +7 -0
  87. package/build/src/library/contract-handle.js +25 -0
  88. package/build/src/library/contract-operations.d.ts +5 -0
  89. package/build/src/library/contract-operations.js +20 -0
  90. package/build/src/library/execution-result.d.ts +2 -2
  91. package/build/src/library/execution.d.ts +8 -0
  92. package/build/src/library/execution.js +102 -0
  93. package/build/src/library/keiyaku.d.ts +2 -0
  94. package/build/src/library/mutation.js +15 -7
  95. package/build/src/plugin/akuma-signals.js +1 -1
  96. package/build/src/plugin/public.d.ts +2 -2
  97. package/build/src/plugin/runtime.d.ts +1 -3
  98. package/build/src/plugin/runtime.js +91 -88
  99. package/build/src/protocol/completion.js +28 -10
  100. package/build/src/protocol/execution-observation.d.ts +185 -0
  101. package/build/src/protocol/execution-observation.js +45 -0
  102. package/build/src/protocol/intent.d.ts +3 -2
  103. package/build/src/protocol/intent.js +44 -34
  104. package/build/src/protocol/placement.js +47 -29
  105. package/build/src/protocol/progress.d.ts +5 -1
  106. package/build/src/protocol/progress.js +10 -1
  107. package/build/src/protocol/result-codec.js +18 -4
  108. package/build/src/runtime/proc/run.d.ts +16 -4
  109. package/build/src/runtime/proc/run.js +64 -22
  110. package/build/src/runtime/proc/windows-launch.exe +0 -0
  111. package/build/src/verification/execution.d.ts +11 -2
  112. package/build/src/verification/execution.js +167 -42
  113. package/build/src/verification/observation.d.ts +45 -0
  114. package/build/src/verification/observation.js +30 -0
  115. package/package.json +7 -5
@@ -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
@@ -8,17 +8,13 @@ import { resolveProviderExecution } from "./providers/index.js";
8
8
  import { publishAkuma } from "./publication.js";
9
9
  import { spawnAkumaBody } from "./body.js";
10
10
  import { AkumaNotBornError } from "./akuma-errors.js";
11
- import { bornStatus } from "./akuma-observe.js";
11
+ import { bornStatus, defaultWaitComplete, readWaitComplete } from "./akuma-observe.js";
12
12
  const CALL_EXECUTION = Symbol("akuma-call-execution");
13
13
  const POLL_MS = 100;
14
14
  const wait = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
15
15
  function diagnostic(error) {
16
16
  return error instanceof Error ? error.message : String(error);
17
17
  }
18
- function defaultWaitComplete(status) {
19
- return (status.life !== "running" &&
20
- !status.timeline.entries.some((entry) => entry.kind === "row" && entry.row.kind === "tell" && entry.row.state === "pending"));
21
- }
22
18
  async function takeLeashUntilSignal(paths, bodySequence, signal, unbounded = false) {
23
19
  const leash = await acquireLeash(paths, {
24
20
  bodySequence,
@@ -35,7 +31,7 @@ async function takeLeashUntilSignal(paths, bodySequence, signal, unbounded = fal
35
31
  return { kind: "unavailable", evidence: "unavailable" };
36
32
  }
37
33
  export async function settleAkumaKill(paths, signal, retainLeash = false) {
38
- const request = await requestStop(paths, new Date().toISOString());
34
+ const request = await requestStop(paths, new Date().toISOString(), signal);
39
35
  if (request.kind !== "requested") {
40
36
  if (!retainLeash)
41
37
  return { evidence: request.kind };
@@ -141,9 +137,13 @@ export class AkumaHandle {
141
137
  }
142
138
  const deadline = options.timeoutMs === undefined ? undefined : performance.now() + options.timeoutMs;
143
139
  for (;;) {
144
- const status = await this.status();
145
- if (predicate(status) || (deadline !== undefined && performance.now() >= deadline))
146
- return status;
140
+ if (predicate !== defaultWaitComplete ||
141
+ (deadline !== undefined && performance.now() >= deadline) ||
142
+ (await readWaitComplete(this.worldPath, this.id))) {
143
+ const status = await this.status();
144
+ if (predicate(status) || (deadline !== undefined && performance.now() >= deadline))
145
+ return status;
146
+ }
147
147
  await wait(deadline === undefined ? POLL_MS : Math.min(POLL_MS, Math.max(0, deadline - performance.now())));
148
148
  }
149
149
  }
@@ -160,7 +160,7 @@ export class AkumaHandle {
160
160
  return await wakeRecordedTell(this.paths, admitted.tell.id, runtime);
161
161
  }
162
162
  async interrupt(body, options = {}) {
163
- const request = await requestPause(this.paths, new Date().toISOString());
163
+ const request = await requestPause(this.paths, new Date().toISOString(), options.signal);
164
164
  if (request.kind === "not-born") {
165
165
  throw new AkumaNotBornError(this.id);
166
166
  }
@@ -17,5 +17,7 @@ export declare function readBudgetedStatus(worldPath: WorldRoot, id: AkuId, inpu
17
17
  ordinaryBudget?: number;
18
18
  admittedTellId?: string;
19
19
  }>): Promise<BudgetedStatusObservation>;
20
+ export declare function defaultWaitComplete(status: AkumaStatus): boolean;
21
+ export declare function readWaitComplete(worldPath: WorldRoot, id: AkuId): Promise<boolean>;
20
22
  export declare function readAkumaBirthCwd(worldPath: WorldRoot, id: AkuId): Promise<string>;
21
23
  export { selectHistory, type ActivityHistory, type ActivitySnapshot };
@@ -1,12 +1,22 @@
1
- import { activitySlice, HeldAkumaLeash, isHeartAbsent, life, lifeAt, probeLeash, readHeart, readSeal, readSoul, } from "./heart/index.js";
1
+ import { readStatusFacts, readLifeSnapshot, HeldAkumaLeash, isHeartAbsent, life, lifeAt, probeLeash, readHeart, readSeal, readSoul, } from "./heart/index.js";
2
2
  import { pathsForAkuId } from "./identity.js";
3
3
  import { ordinarySnapshotBudget, projectTurns, selectHistory, selectSnapshot, } from "./projection.js";
4
4
  import { resolveProviderExecution } from "./providers/index.js";
5
5
  import { AkumaNotBornError } from "./akuma-errors.js";
6
6
  export async function fleetListRow(paths, expected) {
7
7
  const snapshot = await readHeart(paths);
8
- if (snapshot.soul !== null)
9
- return (await bornObservation(paths, expected, snapshot)).row;
8
+ if (snapshot.soul !== null) {
9
+ const observed = await bornObservation(paths, expected, () => readHeart(paths), snapshot);
10
+ return {
11
+ id: observed.soul.id,
12
+ archetype: observed.soul.archetype,
13
+ ...(observed.soul.description === undefined ? {} : { description: observed.soul.description }),
14
+ life: observed.currentLife,
15
+ lifeAt: lifeAt(observed.currentLife, observed.snapshot.latestBody, observed.snapshot.latestKill, observed.soul.createdAt),
16
+ lastActivityAt: observed.snapshot.lastActivityAt,
17
+ pending: observed.snapshot.pending.map((tell) => tell.id),
18
+ };
19
+ }
10
20
  try {
11
21
  if ((await probeLeash(paths)) === "held")
12
22
  return { id: expected, life: "unborn" };
@@ -19,8 +29,8 @@ export async function fleetListRow(paths, expected) {
19
29
  throw error;
20
30
  }
21
31
  }
22
- async function bornObservation(paths, expected, snapshot) {
23
- snapshot ??= await readHeart(paths);
32
+ async function bornObservation(paths, expected, read, snapshot) {
33
+ snapshot ??= await read();
24
34
  if (snapshot.soul === null)
25
35
  throw new AkumaNotBornError(expected);
26
36
  const claim = await HeldAkumaLeash.try(paths);
@@ -28,7 +38,7 @@ async function bornObservation(paths, expected, snapshot) {
28
38
  // A Body may finish after the first read. Refresh under the free seat so
29
39
  // neither its release nor a successor can manufacture an untidy observation.
30
40
  if (claim !== null)
31
- snapshot = await readHeart(paths);
41
+ snapshot = await read();
32
42
  const soul = snapshot.soul;
33
43
  if (soul === null)
34
44
  throw new AkumaNotBornError(expected);
@@ -39,19 +49,7 @@ async function bornObservation(paths, expected, snapshot) {
39
49
  body: snapshot.latestBody,
40
50
  kill: snapshot.latestKill,
41
51
  });
42
- return {
43
- snapshot,
44
- soul,
45
- row: {
46
- id: soul.id,
47
- archetype: soul.archetype,
48
- ...(soul.description === undefined ? {} : { description: soul.description }),
49
- life: currentLife,
50
- lifeAt: lifeAt(currentLife, snapshot.latestBody, snapshot.latestKill, soul.createdAt),
51
- lastActivityAt: snapshot.lastActivityAt,
52
- pending: snapshot.pending.map((tell) => tell.id),
53
- },
54
- };
52
+ return { snapshot, soul, currentLife };
55
53
  }
56
54
  finally {
57
55
  claim?.release();
@@ -60,20 +58,21 @@ async function bornObservation(paths, expected, snapshot) {
60
58
  export async function bornStatus(paths, expected, input) {
61
59
  if (input.ordinaryBudget !== undefined && (!Number.isSafeInteger(input.ordinaryBudget) || input.ordinaryBudget < 0))
62
60
  throw new TypeError("ordinary budget must be a nonnegative safe integer");
63
- const { snapshot, soul, row: current } = await bornObservation(paths, expected);
64
- const resumeUnsupported = current.life === "stranded" &&
61
+ const { snapshot, soul, currentLife } = await bornObservation(paths, expected, () => readLifeSnapshot(paths));
62
+ const resumeUnsupported = currentLife === "stranded" &&
65
63
  snapshot.latestSession?.provider === soul.provider.name &&
66
64
  (await resolveProviderExecution(soul.provider)).adapter.resume === undefined;
67
- const slice = await activitySlice(paths);
68
- const selected = selectSnapshot(projectTurns(slice.rows), {
65
+ const facts = await readStatusFacts(paths, input);
66
+ const selected = selectSnapshot(projectTurns(facts), {
69
67
  aperture: input.aperture,
70
68
  budget: ordinarySnapshotBudget(input.ordinaryBudget),
71
69
  ...(input.admittedTellId === undefined ? {} : { admittedTellId: input.admittedTellId }),
72
70
  });
73
71
  return {
74
72
  status: {
75
- id: current.id,
76
- life: current.life,
73
+ id: soul.id,
74
+ life: currentLife,
75
+ allowed: soul.allowed,
77
76
  ...(soul.readonly === undefined ? {} : { readonly: soul.readonly }),
78
77
  ...(resumeUnsupported ? { strandedReason: "resume-unsupported" } : {}),
79
78
  timeline: selected.snapshot,
@@ -84,6 +83,17 @@ export async function bornStatus(paths, expected, input) {
84
83
  export async function readBudgetedStatus(worldPath, id, input) {
85
84
  return await bornStatus(pathsForAkuId(worldPath, id), id, input);
86
85
  }
86
+ function complete(life, pending) {
87
+ return life !== "running" && !pending;
88
+ }
89
+ export function defaultWaitComplete(status) {
90
+ return complete(status.life, status.timeline.entries.some((entry) => entry.kind === "row" && entry.row.kind === "tell" && entry.row.state === "pending"));
91
+ }
92
+ export async function readWaitComplete(worldPath, id) {
93
+ const paths = pathsForAkuId(worldPath, id);
94
+ const observed = await bornObservation(paths, id, () => readLifeSnapshot(paths));
95
+ return complete(observed.currentLife, observed.snapshot.hasPendingTell);
96
+ }
87
97
  export async function readAkumaBirthCwd(worldPath, id) {
88
98
  const soul = await readSoul(pathsForAkuId(worldPath, id));
89
99
  if (soul === null)
@@ -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<{
@@ -408,8 +426,7 @@ export declare const akumaStatusSchema: z.ZodObject<{
408
426
  }, z.core.$strict>;
409
427
  export type AkumaStatus = z.infer<typeof akumaStatusSchema>;
410
428
  export declare function parseAkumaStatus(value: unknown): AkumaStatus;
411
- /** The default completion judgment over one complete status snapshot. */
412
- export declare function defaultWaitComplete(status: AkumaStatus): boolean;
429
+ export { defaultWaitComplete } from "./akuma-observe.js";
413
430
  export type { ReadonlyRestraint } from "./provider-recipe.js";
414
431
  export type * from "./projection.js";
415
432
  export type UnbornAkumaListRow = Readonly<{
@@ -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(),
@@ -33,11 +35,7 @@ export const akumaStatusSchema = z
33
35
  export function parseAkumaStatus(value) {
34
36
  return akumaStatusSchema.parse(value);
35
37
  }
36
- /** The default completion judgment over one complete status snapshot. */
37
- export function defaultWaitComplete(status) {
38
- return (status.life !== "running" &&
39
- !status.timeline.entries.some((entry) => entry.kind === "row" && entry.row.kind === "tell" && entry.row.state === "pending"));
40
- }
38
+ export { defaultWaitComplete } from "./akuma-observe.js";
41
39
  export { AkumaNotBornError } from "./akuma-errors.js";
42
40
  export function wait(milliseconds) {
43
41
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
@@ -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
  : {
@@ -3,7 +3,7 @@ import { fileURLToPath } from "node:url";
3
3
  import { abortableDelay } from "./abort.js";
4
4
  import { BodySupervisor } from "./body-supervisor.js";
5
5
  import { driveTurn, turnRecipe } from "./turn-drive.js";
6
- import { HeldAkumaLeash, breakBody, decidePendingTellDisposition, drainPendingTells, endTurn, failOpenBoundTurns, finishBodyIfIdle, heartExists, isHeartAbsent, probeLeash, projectTell, provePendingTellDispositionCustody, readHeart, readOpenBoundTurns, readOpenPendingTellDisposition, readTell, readTurn, readNonterminalRequests, recordUndeliveredPendingTells, resolvePendingTellDisposition, } from "./heart/index.js";
6
+ import { HeldAkumaLeash, breakBody, decidePendingTellDisposition, drainPendingTells, endTurn, failOpenBoundTurns, finishBodyIfIdle, heartExists, isHeartAbsent, probeLeash, projectTell, readHeart, readOpenBoundTurns, readOpenPendingTellDisposition, readTell, readTurn, readNonterminalRequests, resolvePendingTellDisposition, } from "./heart/index.js";
7
7
  import { worldRootForAkumaPaths } from "./identity.js";
8
8
  import { pluginRuntime } from "../plugin/runtime.js";
9
9
  import { World } from "../world.js";
@@ -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() });
@@ -369,15 +357,10 @@ async function settleUndeliveredDisposition(paths, disposition, error) {
369
357
  if (!(await heartExists(paths)))
370
358
  return;
371
359
  const at = new Date().toISOString();
372
- await recordUndeliveredPendingTells(paths, at, disposition.tellIds);
373
- await resolvePendingTellDisposition(paths, disposition.bodySequence, at);
360
+ await resolvePendingTellDisposition(paths, disposition.bodySequence, at, "undelivered");
374
361
  }
375
362
  async function consumeProvenDisposition(paths, disposition) {
376
- const proof = await provePendingTellDispositionCustody(paths, disposition);
377
- if (proof.kind !== "proven")
378
- return false;
379
- await resolvePendingTellDisposition(paths, disposition.bodySequence, new Date().toISOString());
380
- return true;
363
+ return await resolvePendingTellDisposition(paths, disposition.bodySequence, new Date().toISOString());
381
364
  }
382
365
  /**
383
366
  * Wait for Heart custody proof of the frozen Tell-id snapshot, or for the
@@ -386,7 +369,7 @@ async function consumeProvenDisposition(paths, disposition) {
386
369
  */
387
370
  async function awaitDispositionCustody(paths, disposition, child, schedule) {
388
371
  for (;;) {
389
- if ((await provePendingTellDispositionCustody(paths, disposition)).kind === "proven") {
372
+ if (await consumeProvenDisposition(paths, disposition)) {
390
373
  child.release();
391
374
  return { kind: "proven" };
392
375
  }
@@ -405,7 +388,7 @@ async function awaitDispositionCustody(paths, disposition, child, schedule) {
405
388
  continue;
406
389
  timerController.abort();
407
390
  await timer.catch(() => undefined);
408
- if ((await provePendingTellDispositionCustody(paths, disposition)).kind === "proven") {
391
+ if (await consumeProvenDisposition(paths, disposition)) {
409
392
  child.release();
410
393
  return { kind: "proven" };
411
394
  }
@@ -417,10 +400,8 @@ async function resolveDecidedPendingTellDisposition(paths, disposition, spawn) {
417
400
  if (await consumeProvenDisposition(paths, disposition))
418
401
  return;
419
402
  const wake = await awaitDispositionCustody(paths, disposition, await spawn({ paths, refuseIfHeld: true }), abortableDelay);
420
- if (wake.kind === "proven") {
421
- await resolvePendingTellDisposition(paths, disposition.bodySequence, new Date().toISOString());
403
+ if (wake.kind === "proven")
422
404
  return;
423
- }
424
405
  // Predecessor still holds the leash: leave the Heart disposition open.
425
406
  if (wake.kind === "held")
426
407
  return;
@@ -515,19 +496,13 @@ export async function driveAkumaBody(launch, adapter, runtimeInput = {}) {
515
496
  await resolveDecidedPendingTellDisposition(launch.paths, finished.decided, runtime.spawnBody ?? spawnAkumaBody);
516
497
  if (finished.notification !== undefined)
517
498
  await finished.notification;
518
- void emitTurnOutcome?.drain();
519
499
  }
520
500
  }
521
501
  export async function spawnAkumaBody(launch) {
522
502
  return await spawnDetachedProcess(await bodyProcessInput(launch));
523
503
  }
524
504
  export async function runAkumaBody(launch, world, externalCommands) {
525
- try {
526
- return await driveAkumaBody(launch, undefined, { world, externalCommands });
527
- }
528
- finally {
529
- await (await pluginRuntime({ world })).drain();
530
- }
505
+ return await driveAkumaBody(launch, undefined, { world, externalCommands });
531
506
  }
532
507
  const DIRECT_TELL_WAKE = {
533
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>>;