@astrosheep/keiyaku 4.0.6 → 4.1.1

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 (251) hide show
  1. package/README.md +1 -1
  2. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -0
  3. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +3 -0
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -6
  5. package/build/src/akuma/akuma.d.ts +17 -14
  6. package/build/src/akuma/akuma.js +122 -114
  7. package/build/src/akuma/allowed.d.ts +1 -0
  8. package/build/src/akuma/allowed.js +6 -0
  9. package/build/src/akuma/archetype.js +12 -2
  10. package/build/src/akuma/body-turn.js +27 -15
  11. package/build/src/akuma/body.d.ts +44 -3
  12. package/build/src/akuma/body.js +142 -33
  13. package/build/src/akuma/coordinate.js +17 -7
  14. package/build/src/akuma/heart/facts.js +1 -1
  15. package/build/src/akuma/heart/index.d.ts +1 -1
  16. package/build/src/akuma/heart/index.js +2 -4
  17. package/build/src/akuma/heart/request-authority.js +4 -4
  18. package/build/src/akuma/heart/request-rows.js +27 -16
  19. package/build/src/akuma/heart/rows.js +79 -41
  20. package/build/src/akuma/heart/soul.js +1 -1
  21. package/build/src/akuma/heart/storage.d.ts +1 -0
  22. package/build/src/akuma/heart/storage.js +76 -11
  23. package/build/src/akuma/heart/tells.js +35 -18
  24. package/build/src/akuma/heart/timeline.js +19 -11
  25. package/build/src/akuma/identity.d.ts +1 -0
  26. package/build/src/akuma/identity.js +1 -0
  27. package/build/src/akuma/index.d.ts +3 -3
  28. package/build/src/akuma/index.js +1 -1
  29. package/build/src/akuma/nuke.js +106 -14
  30. package/build/src/akuma/projection.d.ts +11 -9
  31. package/build/src/akuma/projection.js +110 -53
  32. package/build/src/akuma/provider-recipe.d.ts +2 -0
  33. package/build/src/akuma/provider-recipe.js +21 -5
  34. package/build/src/akuma/provider.js +105 -62
  35. package/build/src/akuma/providers/acp/config.d.ts +2 -0
  36. package/build/src/akuma/providers/acp/config.js +13 -6
  37. package/build/src/akuma/providers/acp/core.d.ts +5 -2
  38. package/build/src/akuma/providers/acp/core.js +45 -25
  39. package/build/src/akuma/providers/acp/events.js +38 -29
  40. package/build/src/akuma/providers/acp/index.js +18 -8
  41. package/build/src/akuma/providers/claude/events.js +8 -12
  42. package/build/src/akuma/providers/claude/index.d.ts +2 -1
  43. package/build/src/akuma/providers/claude/index.js +59 -26
  44. package/build/src/akuma/providers/claude/input.js +6 -2
  45. package/build/src/akuma/providers/codex-app-server/events.js +61 -36
  46. package/build/src/akuma/providers/codex-app-server/index.js +29 -14
  47. package/build/src/akuma/providers/grok-build/index.js +28 -17
  48. package/build/src/akuma/providers/index.js +3 -19
  49. package/build/src/akuma/providers/opencode-sdk/events.js +32 -18
  50. package/build/src/akuma/providers/opencode-sdk/index.js +29 -17
  51. package/build/src/akuma/providers/opencode-sdk/session.js +21 -5
  52. package/build/src/akuma/providers/pi/events.js +26 -19
  53. package/build/src/akuma/providers/pi/index.js +58 -38
  54. package/build/src/akuma/publication.js +9 -9
  55. package/build/src/akuma/request-serve.js +37 -40
  56. package/build/src/akuma/request-wire.d.ts +2 -2
  57. package/build/src/akuma/request-wire.js +75 -102
  58. package/build/src/akuma/requests.js +2 -2
  59. package/build/src/akuma-body.js +6 -5
  60. package/build/src/body/amend.d.ts +4 -1
  61. package/build/src/body/amend.js +14 -15
  62. package/build/src/body/decode.js +2 -4
  63. package/build/src/body/envelope.js +2 -4
  64. package/build/src/body/region.d.ts +1 -2
  65. package/build/src/body/region.js +4 -72
  66. package/build/src/body/render.js +17 -12
  67. package/build/src/body/shape.js +1 -7
  68. package/build/src/cli/accepted.d.ts +1 -1
  69. package/build/src/cli/accepted.js +10 -1
  70. package/build/src/cli/commands/akuma-invoke.js +18 -6
  71. package/build/src/cli/commands/akuma.js +10 -13
  72. package/build/src/cli/commands/amend.d.ts +2 -2
  73. package/build/src/cli/commands/amend.js +2 -4
  74. package/build/src/cli/commands/bind.d.ts +4 -4
  75. package/build/src/cli/commands/bind.js +16 -6
  76. package/build/src/cli/commands/contract.d.ts +99 -10
  77. package/build/src/cli/commands/contract.js +128 -24
  78. package/build/src/cli/commands/install.d.ts +1 -1
  79. package/build/src/cli/commands/install.js +23 -15
  80. package/build/src/cli/commands/task-invoke.d.ts +4 -1
  81. package/build/src/cli/commands/task-invoke.js +191 -68
  82. package/build/src/cli/commands/task-query.js +48 -24
  83. package/build/src/cli/commands/task.js +150 -30
  84. package/build/src/cli/coordinates.js +2 -4
  85. package/build/src/cli/invoke.js +88 -44
  86. package/build/src/cli/main.js +4 -2
  87. package/build/src/cli/parse.d.ts +2 -89
  88. package/build/src/cli/parse.js +87 -79
  89. package/build/src/cli/render/akuma-tool-command.js +6 -7
  90. package/build/src/cli/render/akuma-tool.js +10 -8
  91. package/build/src/cli/render/akuma.js +149 -53
  92. package/build/src/cli/render/audit.js +5 -15
  93. package/build/src/cli/render/board.d.ts +1 -0
  94. package/build/src/cli/render/board.js +17 -0
  95. package/build/src/cli/render/catalog.js +84 -7
  96. package/build/src/cli/render/contract-observation.d.ts +10 -0
  97. package/build/src/cli/render/contract-observation.js +58 -0
  98. package/build/src/cli/render/contract.js +57 -54
  99. package/build/src/cli/render/kanshi.js +281 -209
  100. package/build/src/cli/render/receipt.d.ts +1 -0
  101. package/build/src/cli/render/receipt.js +20 -23
  102. package/build/src/cli/render/refusal.js +16 -15
  103. package/build/src/cli/render/region.js +22 -6
  104. package/build/src/cli/render/settings.js +4 -4
  105. package/build/src/cli/render/task.js +93 -21
  106. package/build/src/cli/render/terminal.js +16 -5
  107. package/build/src/cli/result.d.ts +11 -4
  108. package/build/src/cli/runtime.d.ts +6 -1
  109. package/build/src/cli/runtime.js +84 -63
  110. package/build/src/cli/selectors.js +2 -4
  111. package/build/src/cli/usage.js +6 -5
  112. package/build/src/contract-worktree.d.ts +1 -25
  113. package/build/src/contract-worktree.js +87 -174
  114. package/build/src/coordination/durable-file.js +7 -3
  115. package/build/src/coordination/sqlite-transaction-lock.js +29 -12
  116. package/build/src/core/facts/codec.js +7 -3
  117. package/build/src/core/facts/types.d.ts +1 -1
  118. package/build/src/core/subject.js +16 -9
  119. package/build/src/core/verbs/abandon.d.ts +1 -1
  120. package/build/src/core/verbs/abandon.js +1 -1
  121. package/build/src/core/verbs/amend.d.ts +1 -1
  122. package/build/src/core/verbs/amend.js +7 -7
  123. package/build/src/core/verbs/attestation.js +4 -1
  124. package/build/src/core/verbs/bind.d.ts +1 -1
  125. package/build/src/core/verbs/bind.js +1 -1
  126. package/build/src/core/verbs/placement.d.ts +1 -1
  127. package/build/src/core/verbs/placement.js +10 -8
  128. package/build/src/dispatch/index.js +8 -6
  129. package/build/src/duration.js +5 -1
  130. package/build/src/git/admission.js +5 -3
  131. package/build/src/git/hooks.d.ts +8 -0
  132. package/build/src/git/hooks.js +63 -14
  133. package/build/src/git/identity.js +1 -4
  134. package/build/src/git/integration.d.ts +2 -3
  135. package/build/src/git/integration.js +32 -39
  136. package/build/src/git/nuke.d.ts +1 -1
  137. package/build/src/git/nuke.js +110 -62
  138. package/build/src/git/observe.d.ts +13 -1
  139. package/build/src/git/observe.js +51 -44
  140. package/build/src/git/process.js +4 -2
  141. package/build/src/git/read-observation.js +10 -8
  142. package/build/src/git/reconcile.d.ts +14 -3
  143. package/build/src/git/reconcile.js +123 -102
  144. package/build/src/git/repository.d.ts +2 -0
  145. package/build/src/git/repository.js +13 -22
  146. package/build/src/git/target-placement.d.ts +9 -10
  147. package/build/src/git/target-placement.js +59 -62
  148. package/build/src/git/tender.d.ts +13 -2
  149. package/build/src/git/tender.js +28 -22
  150. package/build/src/git/terminal-seal.js +15 -12
  151. package/build/src/git/tree.js +5 -2
  152. package/build/src/git/workspace.d.ts +24 -2
  153. package/build/src/git/workspace.js +120 -16
  154. package/build/src/identity/coordinates.js +3 -1
  155. package/build/src/identity/selector.js +6 -3
  156. package/build/src/kanshi/fleet.d.ts +6 -0
  157. package/build/src/kanshi/fleet.js +27 -0
  158. package/build/src/kanshi/index.d.ts +1 -1
  159. package/build/src/kanshi/read.js +77 -45
  160. package/build/src/kanshi/report.d.ts +13 -24
  161. package/build/src/kanshi/select.js +25 -24
  162. package/build/src/library/address.d.ts +2 -1
  163. package/build/src/library/address.js +7 -8
  164. package/build/src/library/akuma-creation.js +20 -12
  165. package/build/src/library/audit.d.ts +0 -1
  166. package/build/src/library/audit.js +1 -2
  167. package/build/src/library/bind.d.ts +53 -5
  168. package/build/src/library/bind.js +98 -22
  169. package/build/src/library/catalog.d.ts +1 -0
  170. package/build/src/library/catalog.js +6 -3
  171. package/build/src/library/configuration.js +3 -1
  172. package/build/src/library/continuation.d.ts +37 -0
  173. package/build/src/library/continuation.js +86 -0
  174. package/build/src/library/contract.d.ts +39 -45
  175. package/build/src/library/contract.js +141 -118
  176. package/build/src/library/delivery.d.ts +18 -13
  177. package/build/src/library/fleet.d.ts +1 -1
  178. package/build/src/library/fleet.js +19 -11
  179. package/build/src/library/input.d.ts +2 -0
  180. package/build/src/library/input.js +14 -0
  181. package/build/src/library/keiyaku.d.ts +8 -7
  182. package/build/src/library/keiyaku.js +3 -3
  183. package/build/src/library/mutation.js +11 -4
  184. package/build/src/library/nuke.js +14 -5
  185. package/build/src/library/reconcile.d.ts +9 -4
  186. package/build/src/library/reconcile.js +30 -24
  187. package/build/src/library/refusal.d.ts +13 -5
  188. package/build/src/library/refusal.js +7 -0
  189. package/build/src/library/region.d.ts +8 -7
  190. package/build/src/library/region.js +31 -11
  191. package/build/src/library/repo.js +2 -2
  192. package/build/src/markdown/lex.js +9 -2
  193. package/build/src/markdown/parse.js +11 -7
  194. package/build/src/markdown/query.js +4 -2
  195. package/build/src/protocol/amend.js +3 -1
  196. package/build/src/protocol/attempt.d.ts +7 -3
  197. package/build/src/protocol/attempt.js +9 -5
  198. package/build/src/protocol/audit.d.ts +1 -1
  199. package/build/src/protocol/audit.js +4 -14
  200. package/build/src/protocol/bind.d.ts +19 -6
  201. package/build/src/protocol/bind.js +28 -13
  202. package/build/src/protocol/completion.d.ts +0 -1
  203. package/build/src/protocol/completion.js +1 -3
  204. package/build/src/protocol/deliver.d.ts +12 -4
  205. package/build/src/protocol/deliver.js +60 -51
  206. package/build/src/protocol/intent.d.ts +1 -0
  207. package/build/src/protocol/intent.js +9 -7
  208. package/build/src/protocol/operations.d.ts +10 -9
  209. package/build/src/protocol/operations.js +5 -3
  210. package/build/src/protocol/placement.d.ts +0 -1
  211. package/build/src/protocol/placement.js +6 -6
  212. package/build/src/protocol/read/observation.d.ts +11 -0
  213. package/build/src/protocol/read/observation.js +37 -0
  214. package/build/src/protocol/read/status.d.ts +23 -12
  215. package/build/src/protocol/read/status.js +90 -72
  216. package/build/src/protocol/reconcile.d.ts +1 -0
  217. package/build/src/protocol/reconcile.js +2 -3
  218. package/build/src/protocol/reintegrate.js +1 -1
  219. package/build/src/protocol/review.d.ts +0 -1
  220. package/build/src/protocol/review.js +15 -22
  221. package/build/src/protocol/run.d.ts +1 -0
  222. package/build/src/protocol/run.js +4 -5
  223. package/build/src/runtime/proc/launch.d.ts +33 -0
  224. package/build/src/runtime/proc/launch.js +98 -0
  225. package/build/src/runtime/proc/line-rpc.js +9 -3
  226. package/build/src/runtime/proc/run.d.ts +12 -6
  227. package/build/src/runtime/proc/run.js +78 -21
  228. package/build/src/runtime/proc/stdio.js +7 -10
  229. package/build/src/settings.js +6 -2
  230. package/build/src/settlement/fence.d.ts +0 -2
  231. package/build/src/settlement/fence.js +0 -24
  232. package/build/src/settlement/holder.js +13 -10
  233. package/build/src/settlement/settle.js +39 -6
  234. package/build/src/task/board.d.ts +21 -2
  235. package/build/src/task/board.js +35 -16
  236. package/build/src/task/compose.js +85 -22
  237. package/build/src/task/context.js +21 -8
  238. package/build/src/task/document.js +46 -10
  239. package/build/src/task/identity.js +19 -6
  240. package/build/src/task/index.js +118 -27
  241. package/build/src/task/input.js +29 -7
  242. package/build/src/task/mutation.js +61 -19
  243. package/build/src/task/operations.d.ts +8 -2
  244. package/build/src/task/operations.js +97 -32
  245. package/build/src/task/query.js +57 -28
  246. package/build/src/task/store.d.ts +5 -1
  247. package/build/src/task/store.js +88 -95
  248. package/build/src/verification/execution.js +14 -11
  249. package/build/src/workspace-place.js +19 -7
  250. package/build/src/world.js +16 -6
  251. package/package.json +8 -3
package/README.md CHANGED
@@ -94,7 +94,7 @@ did not land until the gates had current evidence.
94
94
  kanshi ─ 7 keiyaku · 18 akuma · 286 task ─ /Users/astrosheep/Developer/keiyaku-v4 main 9cfdca6017633e51827b9b2eba3c76a7fe08e05f
95
95
 
96
96
  keiyaku 7
97
- ⧗ kei/add-acp-provider-and-grok-build-profile pending-delivery
97
+ ⧗ kei/add-acp-provider-and-grok-build-profile tendered
98
98
  worktree · integration c5cafef6 · -> refs/heads/main
99
99
  ! reviewed
100
100
  ⧗ kei/align-task-cli-truth-promises waiting
@@ -39,4 +39,7 @@ keiyaku -C <repo> tell <aku/...|@alias> (<prompt> | -)
39
39
  `-C` is the invocation and Akuma execution cwd. Add `--repo <path>` only when
40
40
  a Contract or Dispatch reader must use a different Git repository.
41
41
 
42
+ Repeated `--allowed` values add actions to the Archetype defaults. Restriction
43
+ comes from the Archetype base and, for nested calls, the direct parent Soul.
44
+
42
45
  Use text by default; add `--json` when a script needs structured output.
@@ -29,6 +29,9 @@ exclusive. Decide up front whether you will stay:
29
29
  Alias already points elsewhere, it moves to the born Akuma. The worker's
30
30
  execution cwd is exactly `-C <path>`, or your own cwd when `-C` is omitted.
31
31
 
32
+ Repeated `--allowed` values add actions to the Archetype defaults. Restriction
33
+ comes from the Archetype base and, for nested calls, the direct parent Soul.
34
+
32
35
  ## Archetypes
33
36
 
34
37
  An Archetype is a Markdown file at `~/.keiyaku/akuma/<name>.md`. Its
@@ -17,7 +17,7 @@ contract document -> bind -> work -> audit -> deliver -> review gates
17
17
  \-> abandoned
18
18
  ```
19
19
 
20
- The lifecycle is `waiting -> bound -> pending-delivery -> claimed | abandoned`;
20
+ The lifecycle is `waiting -> bound -> tendered -> claimed | abandoned`;
21
21
  the last two are terminal. Reserve `--after` for true logical ordering: one
22
22
  Contract's result must ultimately build on another's settled outcome, or their
23
23
  intended work has a large or irreconcilable interaction that should be
@@ -219,7 +219,7 @@ included. Read the receipt:
219
219
  delivery is done.
220
220
  - When a gate is not current, the receipt shows the recorded candidate and the
221
221
  placement stop. This is not a failed delivery. The Contract stays
222
- `pending-delivery` while you complete the gates.
222
+ `tendered` while you complete the gates.
223
223
  - A lag row reports an accepted physical effect that has not finished. The
224
224
  delivery stands; `reconcile` completes the effect later. It never changes
225
225
  the verdict.
@@ -283,10 +283,7 @@ Placement follows the Git mental model you already have:
283
283
  The deliver or review you just ran still counts: the recorded candidate and
284
284
  any `✓ reviewed` verdict are kept, but nothing claims and nothing moves.
285
285
  The target ref, its checkout, and your bytes stay exactly where they were,
286
- and the Contract stays `pending-delivery`.
287
- - A `--here` Contract behaves like a commit with gates: it lands on the current
288
- branch and cannot deliver to a foreign checked-out target.
289
-
286
+ and the Contract stays `tendered`.
290
287
  After a refusal, handle the listed paths, then `deliver` again or record a
291
288
  satisfied review; either command requests placement again.
292
289
 
@@ -1,5 +1,6 @@
1
+ import { type TellResult, type TellWakeRuntime } from "./body.js";
1
2
  import { type AkumaLife, type KillEvidence, type ResumeCoordinate, type Soul } from "./heart/index.js";
2
- import { type AkuId } from "./identity.js";
3
+ import { type AkuId, type AkumaPaths } from "./identity.js";
3
4
  import { type ActivityHistory, type ActivitySnapshot } from "./projection.js";
4
5
  import { type Settings } from "../settings.js";
5
6
  import type { WorldRoot } from "../world.js";
@@ -20,6 +21,8 @@ export type AkumaStatus = Readonly<{
20
21
  timeline: ActivitySnapshot;
21
22
  strandedReason?: "resume-unsupported";
22
23
  }>;
24
+ /** The default completion judgment over one complete status snapshot. */
25
+ export declare function defaultWaitComplete(status: AkumaStatus): boolean;
23
26
  export type { ReadonlyRestraint } from "./provider-recipe.js";
24
27
  export type * from "./projection.js";
25
28
  export type UnbornAkumaListRow = Readonly<{
@@ -54,16 +57,7 @@ type AkumaCallContext = Readonly<{
54
57
  initiatorCwd?: string;
55
58
  cwdCanonical?: true;
56
59
  }>;
57
- export type TellResult = Readonly<{
58
- admission: Readonly<{
59
- tellId: string;
60
- fact: "recorded";
61
- }>;
62
- wake: "spawned" | Readonly<{
63
- kind: "failed";
64
- diagnostic: string;
65
- }>;
66
- }>;
60
+ export type { TellResult, TellWake } from "./body.js";
67
61
  export type InterruptReceipt = Readonly<{
68
62
  kind: "unavailable";
69
63
  evidence: "hung" | "untidy" | "unavailable";
@@ -94,14 +88,23 @@ export declare class AkumaNotBornError extends Error {
94
88
  readonly kind = "akuma-not-born";
95
89
  constructor(id: AkuId);
96
90
  }
97
- export declare function tellAkumaWithId(worldPath: WorldRoot, id: AkuId, body: string, tellId: string, recordedAt?: string): Promise<TellResult>;
91
+ export declare function tellAkumaWithId(input: Readonly<{
92
+ worldPath: WorldRoot;
93
+ id: AkuId;
94
+ body: string;
95
+ tellId: string;
96
+ recordedAt?: string;
97
+ runtime?: TellWakeRuntime;
98
+ }>): Promise<TellResult>;
99
+ export declare function killAkumaWithRecovery(paths: AkumaPaths, recover?: (paths: AkumaPaths) => Promise<void>): Promise<KillEvidence>;
98
100
  export type BudgetedStatusObservation = Readonly<{
99
101
  status: AkumaStatus;
100
102
  ordinarySelected: number;
101
103
  }>;
102
- /** Package-internal budgeted observation; Fleet allocates, Akuma still selects. */
104
+ /** Package-internal observation; Fleet chooses the aperture and ordinary budget. */
103
105
  export declare function readBudgetedStatus(worldPath: WorldRoot, id: AkuId, input: Readonly<{
104
- ordinaryBudget: number;
106
+ aperture: "monitoring" | "receipt";
107
+ ordinaryBudget?: number;
105
108
  }>): Promise<BudgetedStatusObservation>;
106
109
  /** Package-internal birth projection used by the composed Library call result. */
107
110
  export declare function readAkumaBirthCwd(worldPath: WorldRoot, id: AkuId): Promise<string>;
@@ -1,17 +1,17 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { readdir, realpath, stat } from "node:fs/promises";
3
3
  import { resolve } from "node:path";
4
- import { CONTROL_RESPONSE_MS, spawnAkumaBody } from "./body.js";
4
+ import { CONTROL_RESPONSE_MS, handoffPendingTells, spawnAkumaBody, wakeRecordedTell, } from "./body.js";
5
5
  import { HeldAkumaLeash, activitySlice, isHeartAbsent, life, lifeAt, probeLeash, readHeart, readLastAnsweredTurn, readForkPoint, readKill, readSeal, readSoul, recordTell, requestPause, requestStop, } from "./heart/index.js";
6
6
  import { akuIdFromDirectoryName, akumaPaths, akumaRunRoot, parseAkuId, pathsForAkuId, archetypeName, } from "./identity.js";
7
- import { ordinarySelectedCount, ordinarySnapshotBudget, projectTurns, selectActivitySnapshot, selectHistory, selectSnapshot, } from "./projection.js";
7
+ import { ordinarySnapshotBudget, projectTurns, selectHistory, selectSnapshot, } from "./projection.js";
8
8
  import { listArchetypes as readArchetypes, loadArchetype } from "./archetype.js";
9
9
  import { publishAkuma } from "./publication.js";
10
10
  import { resolveProviderExecution } from "./providers/index.js";
11
11
  import { injectedBodyRequests, requestBodyCall } from "./requests.js";
12
12
  import { settings as readSettings } from "../settings.js";
13
- import { decodeAllowedActions, } from "./allowed.js";
14
- const POLL_MS = 25;
13
+ import { decodeAllowedActions, unionAllowedActions } from "./allowed.js";
14
+ const POLL_MS = 100;
15
15
  async function canonicalBirthCwd(input) {
16
16
  const selected = resolve(input);
17
17
  try {
@@ -31,6 +31,11 @@ function callReadonly(value) {
31
31
  throw new TypeError("Akuma call readonly must be true");
32
32
  return { readonly: true };
33
33
  }
34
+ /** The default completion judgment over one complete status snapshot. */
35
+ export function defaultWaitComplete(status) {
36
+ return (status.life !== "running" &&
37
+ !status.timeline.entries.some((entry) => entry.kind === "row" && entry.row.kind === "tell" && entry.row.state === "pending"));
38
+ }
34
39
  const CALL_EXECUTION = Symbol("akuma-call-execution");
35
40
  const CALL_WITH_CONTEXT = Symbol("akuma-call-with-context");
36
41
  export class AkumaNotBornError extends Error {
@@ -52,7 +57,7 @@ async function takeLeashUntil(paths, deadline) {
52
57
  return leash;
53
58
  if (performance.now() >= deadline)
54
59
  return null;
55
- await wait(POLL_MS);
60
+ await wait(Math.min(POLL_MS, Math.max(0, deadline - performance.now())));
56
61
  }
57
62
  }
58
63
  async function recordTellBody(paths, akuma, body, id = randomUUID(), recordedAt = new Date().toISOString()) {
@@ -61,32 +66,56 @@ async function recordTellBody(paths, akuma, body, id = randomUUID(), recordedAt
61
66
  throw new AkumaNotBornError(akuma);
62
67
  return { kind: "recorded", tellId: admitted.tell.id };
63
68
  }
64
- async function wakeTell(paths, tellId) {
65
- try {
66
- await spawnAkumaBody({ paths });
67
- return { admission: { tellId, fact: "recorded" }, wake: "spawned" };
68
- }
69
- catch (error) {
70
- return {
71
- admission: { tellId, fact: "recorded" },
72
- wake: { kind: "failed", diagnostic: diagnostic(error) },
73
- };
74
- }
75
- }
76
- export async function tellAkumaWithId(worldPath, id, body, tellId, recordedAt = new Date().toISOString()) {
77
- const paths = pathsForAkuId(worldPath, id);
78
- const recorded = await recordTellBody(paths, id, body, tellId, recordedAt);
79
- return await wakeTell(paths, recorded.tellId);
69
+ export async function tellAkumaWithId(input) {
70
+ const paths = pathsForAkuId(input.worldPath, input.id);
71
+ const recorded = await recordTellBody(paths, input.id, input.body, input.tellId, input.recordedAt);
72
+ return await wakeRecordedTell(paths, recorded.tellId, input.runtime);
80
73
  }
81
74
  function diagnostic(error) {
82
75
  return error instanceof Error ? error.message : String(error);
83
76
  }
77
+ export async function killAkumaWithRecovery(paths, recover = handoffPendingTells) {
78
+ try {
79
+ const request = await requestStop(paths, new Date().toISOString());
80
+ if (request.kind !== "requested")
81
+ return request.kind;
82
+ const target = request.body;
83
+ const leash = await takeLeashUntil(paths, performance.now() + CONTROL_RESPONSE_MS);
84
+ if ((await readKill(paths, target.sequence)) !== null) {
85
+ leash?.release();
86
+ return "killed";
87
+ }
88
+ if (leash === null) {
89
+ if ((await readKill(paths, target.sequence)) !== null)
90
+ return "killed";
91
+ const body = (await readHeart(paths)).latestBody;
92
+ return body?.sequence === target.sequence && body.hung !== undefined ? "hung" : "unavailable";
93
+ }
94
+ try {
95
+ const settledBody = (await readHeart(paths)).latestBody;
96
+ if (settledBody?.sequence !== target.sequence)
97
+ return (await readKill(paths, target.sequence)) === null ? "unavailable" : "killed";
98
+ if (settledBody.end !== "put-down") {
99
+ await leash.clearStop(paths);
100
+ return "untidy";
101
+ }
102
+ const settled = await leash.settleStop(paths, target.sequence);
103
+ return settled === null ? "unavailable" : "killed";
104
+ }
105
+ finally {
106
+ leash.release();
107
+ }
108
+ }
109
+ finally {
110
+ void recover(paths).catch(() => undefined);
111
+ }
112
+ }
84
113
  async function fleetListRow(paths, expected) {
85
114
  const snapshot = await readHeart(paths);
86
115
  if (snapshot.soul !== null)
87
116
  return await bornListRow(paths, expected, snapshot);
88
117
  try {
89
- if (await probeLeash(paths) === "held")
118
+ if ((await probeLeash(paths)) === "held")
90
119
  return { id: expected, life: "unborn" };
91
120
  const seal = await readSeal(paths);
92
121
  return seal === null ? { id: expected, life: "unborn" } : { id: expected, life: "stillborn", seal };
@@ -118,32 +147,36 @@ async function bornListRow(paths, expected, snapshot) {
118
147
  pending: snapshot.pending.map((tell) => tell.id),
119
148
  };
120
149
  }
121
- async function bornStatus(paths, expected, budget) {
150
+ async function bornStatus(paths, expected, input) {
151
+ if (input.ordinaryBudget !== undefined && (!Number.isSafeInteger(input.ordinaryBudget) || input.ordinaryBudget < 0)) {
152
+ throw new TypeError("ordinary budget must be a nonnegative safe integer");
153
+ }
122
154
  const snapshot = await readHeart(paths);
123
155
  if (snapshot.soul === null)
124
156
  throw new AkumaNotBornError(expected);
125
157
  const current = await bornListRow(paths, expected, snapshot);
126
- const resumeUnsupported = current.life === "stranded"
127
- && snapshot.latestSession?.provider === snapshot.soul.provider.name
128
- && (await resolveProviderExecution(snapshot.soul.provider)).adapter.resume === undefined;
158
+ const resumeUnsupported = current.life === "stranded" &&
159
+ snapshot.latestSession?.provider === snapshot.soul.provider.name &&
160
+ (await resolveProviderExecution(snapshot.soul.provider)).adapter.resume === undefined;
129
161
  const slice = await activitySlice(paths);
162
+ const selected = selectSnapshot(projectTurns(slice.rows), {
163
+ aperture: input.aperture,
164
+ budget: ordinarySnapshotBudget(input.ordinaryBudget),
165
+ });
130
166
  return {
131
- id: current.id,
132
- life: current.life,
133
- ...(snapshot.soul.readonly === undefined ? {} : { readonly: snapshot.soul.readonly }),
134
- ...(resumeUnsupported ? { strandedReason: "resume-unsupported" } : {}),
135
- timeline: budget === undefined
136
- ? selectActivitySnapshot(slice.rows)
137
- : selectSnapshot(projectTurns(slice.rows), budget),
167
+ status: {
168
+ id: current.id,
169
+ life: current.life,
170
+ ...(snapshot.soul.readonly === undefined ? {} : { readonly: snapshot.soul.readonly }),
171
+ ...(resumeUnsupported ? { strandedReason: "resume-unsupported" } : {}),
172
+ timeline: selected.snapshot,
173
+ },
174
+ ordinarySelected: selected.ordinaryCount,
138
175
  };
139
176
  }
140
- /** Package-internal budgeted observation; Fleet allocates, Akuma still selects. */
177
+ /** Package-internal observation; Fleet chooses the aperture and ordinary budget. */
141
178
  export async function readBudgetedStatus(worldPath, id, input) {
142
- if (!Number.isSafeInteger(input.ordinaryBudget) || input.ordinaryBudget < 0) {
143
- throw new TypeError("ordinary budget must be a nonnegative safe integer");
144
- }
145
- const status = await bornStatus(pathsForAkuId(worldPath, id), id, ordinarySnapshotBudget(input.ordinaryBudget));
146
- return { status, ordinarySelected: ordinarySelectedCount(status.timeline) };
179
+ return await bornStatus(pathsForAkuId(worldPath, id), id, input);
147
180
  }
148
181
  /** Package-internal birth projection used by the composed Library call result. */
149
182
  export async function readAkumaBirthCwd(worldPath, id) {
@@ -166,13 +199,16 @@ export class AkumaHandle {
166
199
  return pathsForAkuId(this.worldPath, this.id);
167
200
  }
168
201
  async status() {
169
- return await bornStatus(this.paths, this.id);
202
+ return (await bornStatus(this.paths, this.id, { aperture: "monitoring" })).status;
170
203
  }
171
204
  async history(input = {}) {
172
205
  if (input.before !== undefined && input.since !== undefined) {
173
206
  throw new TypeError("Akuma history before and since are mutually exclusive");
174
207
  }
175
- for (const [name, value] of [["before", input.before], ["since", input.since]]) {
208
+ for (const [name, value] of [
209
+ ["before", input.before],
210
+ ["since", input.since],
211
+ ]) {
176
212
  if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) {
177
213
  throw new TypeError(`Akuma history ${name} must be a positive safe integer`);
178
214
  }
@@ -191,9 +227,8 @@ export class AkumaHandle {
191
227
  limit,
192
228
  });
193
229
  }
194
- async wait(predicate = (status) => status.life !== "running", options = {}) {
195
- if (options.timeoutMs !== undefined
196
- && (!Number.isFinite(options.timeoutMs) || options.timeoutMs < 0)) {
230
+ async wait(predicate = defaultWaitComplete, options = {}) {
231
+ if (options.timeoutMs !== undefined && (!Number.isFinite(options.timeoutMs) || options.timeoutMs < 0)) {
197
232
  throw new TypeError("Akuma wait timeoutMs must be a nonnegative finite millisecond duration");
198
233
  }
199
234
  const deadline = options.timeoutMs === undefined ? undefined : performance.now() + options.timeoutMs;
@@ -206,7 +241,7 @@ export class AkumaHandle {
206
241
  }
207
242
  async tell(body) {
208
243
  const recorded = await recordTellBody(this.paths, this.id, body);
209
- return await wakeTell(this.paths, recorded.tellId);
244
+ return await wakeRecordedTell(this.paths, recorded.tellId);
210
245
  }
211
246
  async interrupt(body) {
212
247
  const request = await requestPause(this.paths, new Date().toISOString());
@@ -221,9 +256,10 @@ export class AkumaHandle {
221
256
  }
222
257
  if (leash === null) {
223
258
  const body = (await readHeart(this.paths)).latestBody;
224
- return { kind: "unavailable", evidence: body?.sequence === request.body.sequence && body.hung !== undefined
225
- ? "hung"
226
- : "unavailable" };
259
+ return {
260
+ kind: "unavailable",
261
+ evidence: body?.sequence === request.body.sequence && body.hung !== undefined ? "hung" : "unavailable",
262
+ };
227
263
  }
228
264
  const settledBody = (await readHeart(this.paths)).latestBody;
229
265
  if (settledBody?.sequence === request.body.sequence && settledBody.hung !== undefined) {
@@ -262,7 +298,7 @@ export class AkumaHandle {
262
298
  finally {
263
299
  leash.release();
264
300
  }
265
- return { kind: "interrupted", putDown, tell: await wakeTell(this.paths, recorded.tellId) };
301
+ return { kind: "interrupted", putDown, tell: await wakeRecordedTell(this.paths, recorded.tellId) };
266
302
  }
267
303
  async fork(input) {
268
304
  const source = await readSoul(this.paths);
@@ -298,21 +334,23 @@ export class AkumaHandle {
298
334
  worldPath: this.worldPath,
299
335
  archetype: source.archetype,
300
336
  awaitAsleep: true,
301
- launch: async (allocated) => await spawnAkumaBody({
302
- paths: allocated.paths,
303
- seed: {
304
- id: allocated.id,
305
- archetype: source.archetype,
306
- ...(source.description === undefined ? {} : { description: source.description }),
307
- provider: source.provider,
308
- options: source.options,
309
- ...(source.readonly === undefined ? {} : { readonly: source.readonly }),
310
- allowed: source.allowed,
311
- cwd: source.cwd,
312
- origin: { kind: "fork", parent: this.id, at: input.at },
313
- },
314
- birthSession,
315
- }),
337
+ launch: async (allocated) => {
338
+ (await spawnAkumaBody({
339
+ paths: allocated.paths,
340
+ seed: {
341
+ id: allocated.id,
342
+ archetype: source.archetype,
343
+ ...(source.description === undefined ? {} : { description: source.description }),
344
+ provider: source.provider,
345
+ options: source.options,
346
+ ...(source.readonly === undefined ? {} : { readonly: source.readonly }),
347
+ allowed: source.allowed,
348
+ cwd: source.cwd,
349
+ origin: { kind: "fork", parent: this.id, at: input.at },
350
+ },
351
+ birthSession,
352
+ })).release();
353
+ },
316
354
  });
317
355
  return { kind: "forked", child: child.id };
318
356
  }
@@ -325,39 +363,7 @@ export class AkumaHandle {
325
363
  }
326
364
  }
327
365
  async kill() {
328
- const at = new Date().toISOString();
329
- const request = await requestStop(this.paths, at);
330
- if (request.kind !== "requested")
331
- return request.kind;
332
- const target = request.body;
333
- const graceDeadline = performance.now() + CONTROL_RESPONSE_MS;
334
- const leash = await takeLeashUntil(this.paths, graceDeadline);
335
- if (await readKill(this.paths, target.sequence) !== null) {
336
- leash?.release();
337
- return "killed";
338
- }
339
- if (leash === null) {
340
- if (await readKill(this.paths, target.sequence) !== null)
341
- return "killed";
342
- const body = (await readHeart(this.paths)).latestBody;
343
- return body?.sequence === target.sequence && body.hung !== undefined ? "hung" : "unavailable";
344
- }
345
- try {
346
- const settledBody = (await readHeart(this.paths)).latestBody;
347
- if (settledBody?.sequence !== target.sequence)
348
- return await readKill(this.paths, target.sequence) === null ? "unavailable" : "killed";
349
- if (settledBody.end !== "put-down") {
350
- await leash.clearStop(this.paths);
351
- return "untidy";
352
- }
353
- const settled = await leash.settleStop(this.paths, target.sequence);
354
- if (settled === null)
355
- return "unavailable";
356
- return "killed";
357
- }
358
- finally {
359
- leash.release();
360
- }
366
+ return await killAkumaWithRecovery(this.paths);
361
367
  }
362
368
  async lastAnswer() {
363
369
  const turn = await readLastAnsweredTurn(this.paths);
@@ -395,7 +401,7 @@ export class Akuma {
395
401
  const readonly = callReadonly(input.readonly);
396
402
  const name = archetypeName(input.archetype);
397
403
  const home = this.configuration.home === undefined ? {} : { home: this.configuration.home };
398
- const settings = this.configuration.settings ?? await readSettings({ root: this.path, ...home });
404
+ const settings = this.configuration.settings ?? (await readSettings({ root: this.path, ...home }));
399
405
  const archetype = await loadArchetype({
400
406
  name,
401
407
  ...home,
@@ -404,7 +410,7 @@ export class Akuma {
404
410
  });
405
411
  const allowed = input.allowed === undefined
406
412
  ? archetype.allowed
407
- : decodeAllowedActions(input.allowed, "Akuma call allowed");
413
+ : unionAllowedActions(archetype.allowed, decodeAllowedActions(input.allowed, "Akuma call allowed"));
408
414
  const requests = injectedBodyRequests();
409
415
  const requestRecipe = Object.freeze({
410
416
  ...(archetype.description === undefined ? {} : { description: archetype.description }),
@@ -416,7 +422,9 @@ export class Akuma {
416
422
  if (requests !== null) {
417
423
  const cwd = input.cwd === undefined
418
424
  ? undefined
419
- : context?.cwdCanonical === true ? input.cwd : await canonicalBirthCwd(input.cwd);
425
+ : context?.cwdCanonical === true
426
+ ? input.cwd
427
+ : await canonicalBirthCwd(input.cwd);
420
428
  const child = await requestBodyCall({
421
429
  directory: requests,
422
430
  id: randomUUID(),
@@ -434,24 +442,24 @@ export class Akuma {
434
442
  }
435
443
  const initiatorCwd = context.initiatorCwd;
436
444
  const selectedCwd = input.cwd ?? initiatorCwd ?? this.path;
437
- const cwd = input.cwd !== undefined && context?.cwdCanonical === true
438
- ? input.cwd
439
- : await canonicalBirthCwd(selectedCwd);
445
+ const cwd = input.cwd !== undefined && context?.cwdCanonical === true ? input.cwd : await canonicalBirthCwd(selectedCwd);
440
446
  const recipe = requestRecipe;
441
447
  const published = await publishAkuma({
442
448
  worldPath: this.path,
443
449
  archetype: archetype.name,
444
- launch: async (allocated) => await spawnAkumaBody({
445
- paths: allocated.paths,
446
- seed: {
447
- id: allocated.id,
448
- archetype: allocated.archetype,
449
- ...recipe,
450
- cwd,
451
- origin: { kind: "direct" },
452
- },
453
- initialBody: input.body,
454
- }),
450
+ launch: async (allocated) => {
451
+ (await spawnAkumaBody({
452
+ paths: allocated.paths,
453
+ seed: {
454
+ id: allocated.id,
455
+ archetype: allocated.archetype,
456
+ ...recipe,
457
+ cwd,
458
+ origin: { kind: "direct" },
459
+ },
460
+ initialBody: input.body,
461
+ })).release();
462
+ },
455
463
  });
456
464
  return new AkumaHandle(published.id, this.path, {
457
465
  cwd,
@@ -4,4 +4,5 @@ export type AllowedActions = readonly AllowedAction[];
4
4
  export declare function isAllowedAction(value: unknown): value is AllowedAction;
5
5
  export declare function decodeAllowedActions(value: unknown, label?: string): AllowedActions;
6
6
  export declare function effectiveAllowedActions(value: unknown): AllowedActions;
7
+ export declare function unionAllowedActions(base: AllowedActions, additions: AllowedActions): AllowedActions;
7
8
  export declare function clipAllowedActions(requested: AllowedActions, parent: AllowedActions): AllowedActions;
@@ -27,6 +27,12 @@ export function decodeAllowedActions(value, label = "allowed") {
27
27
  export function effectiveAllowedActions(value) {
28
28
  return value === undefined ? ALLOWED_ACTIONS : decodeAllowedActions(value);
29
29
  }
30
+ export function unionAllowedActions(base, additions) {
31
+ const merged = new Set(base);
32
+ for (const action of additions)
33
+ merged.add(action);
34
+ return Object.freeze([...merged].sort());
35
+ }
30
36
  export function clipAllowedActions(requested, parent) {
31
37
  const ceiling = new Set(parent);
32
38
  return Object.freeze(requested.filter((action) => ceiling.has(action)));
@@ -96,7 +96,11 @@ function decodeArchetype(name, path, markdown) {
96
96
  const network = archetypeEnum(values, "network", ["disabled", "enabled"]);
97
97
  const description = archetypeField(values, "description");
98
98
  const allowed = effectiveAllowedActions(values.allowed);
99
+ const systemPromptMode = archetypeEnum(values, "systemPromptMode", ["append", "replace"]);
99
100
  const systemPrompt = lines.slice(closing + 1).join("\n");
101
+ if (systemPromptMode !== undefined && systemPrompt.length === 0) {
102
+ throw new TypeError("Akuma systemPromptMode requires a nonempty Markdown body");
103
+ }
100
104
  return Object.freeze({
101
105
  name,
102
106
  path,
@@ -106,7 +110,12 @@ function decodeArchetype(name, path, markdown) {
106
110
  ...(model === undefined ? {} : { model }),
107
111
  ...(effort === undefined ? {} : { effort }),
108
112
  ...(network === undefined ? {} : { network }),
109
- ...(systemPrompt.length === 0 ? {} : { systemPrompt }),
113
+ ...(systemPrompt.length === 0
114
+ ? {}
115
+ : {
116
+ systemPrompt,
117
+ systemPromptMode: systemPromptMode ?? "append",
118
+ }),
110
119
  }),
111
120
  ...(readonly === undefined ? {} : { readonly }),
112
121
  allowed,
@@ -135,7 +144,8 @@ export async function listArchetypeDefinitions(input = {}) {
135
144
  }
136
145
  function record(value) {
137
146
  return value !== null && typeof value === "object" && !Array.isArray(value)
138
- ? value : null;
147
+ ? value
148
+ : null;
139
149
  }
140
150
  function optionalProviderText(value, name, field) {
141
151
  const selected = value[field];