@astrosheep/keiyaku 4.0.0 → 4.0.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 (241) hide show
  1. package/README.md +2 -9
  2. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +2 -2
  3. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +4 -4
  4. package/build/integrations/marketplace/plugins/keiyaku/opencode.js +2 -2
  5. package/build/integrations/marketplace/plugins/keiyaku/package.json +2 -2
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +11 -3
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +9 -6
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +94 -0
  9. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/agents/openai.yaml +4 -0
  10. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +41 -6
  11. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +162 -45
  12. package/build/src/akuma/akuma.d.ts +33 -25
  13. package/build/src/akuma/akuma.js +93 -61
  14. package/build/src/akuma/archetype.d.ts +8 -0
  15. package/build/src/akuma/archetype.js +28 -9
  16. package/build/src/akuma/body.js +196 -84
  17. package/build/src/akuma/coordinate.d.ts +9 -0
  18. package/build/src/akuma/coordinate.js +22 -0
  19. package/build/src/akuma/heart/facts.d.ts +67 -15
  20. package/build/src/akuma/heart/facts.js +3 -3
  21. package/build/src/akuma/heart/index.d.ts +41 -29
  22. package/build/src/akuma/heart/index.js +72 -193
  23. package/build/src/akuma/heart/rows.d.ts +30 -53
  24. package/build/src/akuma/heart/rows.js +91 -111
  25. package/build/src/akuma/heart/schema.d.ts +1 -1
  26. package/build/src/akuma/heart/schema.js +55 -8
  27. package/build/src/akuma/heart/storage.d.ts +33 -0
  28. package/build/src/akuma/heart/storage.js +164 -0
  29. package/build/src/akuma/heart/tells.d.ts +12 -0
  30. package/build/src/akuma/heart/tells.js +79 -0
  31. package/build/src/akuma/heart/timeline.d.ts +15 -0
  32. package/build/src/akuma/heart/timeline.js +86 -0
  33. package/build/src/akuma/index.d.ts +4 -2
  34. package/build/src/akuma/index.js +1 -1
  35. package/build/src/akuma/projection.d.ts +97 -0
  36. package/build/src/akuma/projection.js +145 -0
  37. package/build/src/akuma/provider.d.ts +57 -11
  38. package/build/src/akuma/provider.js +57 -37
  39. package/build/src/akuma/providers/{claude.d.ts → claude/events.d.ts} +9 -41
  40. package/build/src/akuma/providers/claude/events.js +185 -0
  41. package/build/src/akuma/providers/claude/index.d.ts +46 -0
  42. package/build/src/akuma/providers/claude/index.js +265 -0
  43. package/build/src/akuma/providers/claude/input.d.ts +13 -0
  44. package/build/src/akuma/providers/claude/input.js +109 -0
  45. package/build/src/akuma/providers/codex-app-server/events.d.ts +1 -1
  46. package/build/src/akuma/providers/codex-app-server/events.js +12 -12
  47. package/build/src/akuma/providers/codex-app-server/index.d.ts +13 -11
  48. package/build/src/akuma/providers/codex-app-server/index.js +73 -22
  49. package/build/src/akuma/providers/index.js +22 -8
  50. package/build/src/akuma/providers/opencode-sdk/events.d.ts +48 -0
  51. package/build/src/akuma/providers/opencode-sdk/events.js +116 -0
  52. package/build/src/akuma/providers/opencode-sdk/index.d.ts +32 -0
  53. package/build/src/akuma/providers/opencode-sdk/index.js +203 -0
  54. package/build/src/akuma/providers/opencode-sdk/session.d.ts +41 -0
  55. package/build/src/akuma/providers/opencode-sdk/session.js +62 -0
  56. package/build/src/akuma/providers/pi/events.d.ts +33 -0
  57. package/build/src/akuma/providers/pi/events.js +128 -0
  58. package/build/src/akuma/providers/pi/index.d.ts +11 -0
  59. package/build/src/akuma/providers/pi/index.js +162 -0
  60. package/build/src/akuma/requests.d.ts +1 -0
  61. package/build/src/akuma/requests.js +5 -2
  62. package/build/src/alias/index.d.ts +4 -3
  63. package/build/src/alias/index.js +2 -3
  64. package/build/src/body/render.js +2 -1
  65. package/build/src/body/shape.js +7 -1
  66. package/build/src/body/verification.js +19 -4
  67. package/build/src/cli/accepted.d.ts +2 -0
  68. package/build/src/cli/accepted.js +2 -1
  69. package/build/src/cli/commands/akuma-invoke.d.ts +42 -18
  70. package/build/src/cli/commands/akuma-invoke.js +94 -50
  71. package/build/src/cli/commands/akuma.d.ts +15 -7
  72. package/build/src/cli/commands/akuma.js +94 -56
  73. package/build/src/cli/commands/contract.d.ts +142 -0
  74. package/build/src/cli/commands/contract.js +14 -0
  75. package/build/src/cli/commands/install.js +2 -0
  76. package/build/src/cli/commands/task-invoke.d.ts +3 -1
  77. package/build/src/cli/commands/task-invoke.js +27 -2
  78. package/build/src/cli/commands/task.js +2 -2
  79. package/build/src/cli/invoke.d.ts +6 -1
  80. package/build/src/cli/invoke.js +241 -43
  81. package/build/src/cli/main.js +20 -1
  82. package/build/src/cli/parse.d.ts +13 -116
  83. package/build/src/cli/parse.js +51 -78
  84. package/build/src/cli/render/akuma-tool.d.ts +2 -0
  85. package/build/src/cli/render/akuma-tool.js +6 -2
  86. package/build/src/cli/render/akuma.d.ts +2 -1
  87. package/build/src/cli/render/akuma.js +157 -217
  88. package/build/src/cli/render/catalog.d.ts +2 -0
  89. package/build/src/cli/render/catalog.js +16 -0
  90. package/build/src/cli/render/contract.d.ts +1 -1
  91. package/build/src/cli/render/contract.js +38 -5
  92. package/build/src/cli/render/kanshi.js +53 -5
  93. package/build/src/cli/render/refusal.js +17 -0
  94. package/build/src/cli/render/terminal.d.ts +13 -0
  95. package/build/src/cli/render/terminal.js +75 -0
  96. package/build/src/cli/render/text.js +5 -0
  97. package/build/src/cli/result.d.ts +15 -3
  98. package/build/src/contract-worktree.d.ts +43 -0
  99. package/build/src/contract-worktree.js +213 -0
  100. package/build/src/coordination/durable-file.d.ts +3 -0
  101. package/build/src/coordination/durable-file.js +44 -1
  102. package/build/src/core/facts/codec.js +23 -4
  103. package/build/src/core/facts/eligibility.d.ts +1 -10
  104. package/build/src/core/facts/eligibility.js +0 -49
  105. package/build/src/core/facts/observation.d.ts +3 -0
  106. package/build/src/core/facts/observation.js +12 -0
  107. package/build/src/core/facts/types.d.ts +10 -3
  108. package/build/src/core/subject.js +2 -2
  109. package/build/src/core/verbs/amend.js +12 -16
  110. package/build/src/core/verbs/bind.js +2 -16
  111. package/build/src/core/verbs/deliver.js +20 -4
  112. package/build/src/core/verbs/placement.js +3 -8
  113. package/build/src/dispatch/index.d.ts +3 -1
  114. package/build/src/dispatch/index.js +14 -5
  115. package/build/src/duration.d.ts +10 -0
  116. package/build/src/duration.js +22 -0
  117. package/build/src/git/admission.d.ts +2 -2
  118. package/build/src/git/admission.js +26 -16
  119. package/build/src/git/hooks.d.ts +15 -0
  120. package/build/src/git/hooks.js +57 -5
  121. package/build/src/git/identity.d.ts +5 -1
  122. package/build/src/git/identity.js +23 -4
  123. package/build/src/git/integration.d.ts +49 -0
  124. package/build/src/git/integration.js +214 -0
  125. package/build/src/git/observe.d.ts +25 -13
  126. package/build/src/git/observe.js +175 -72
  127. package/build/src/git/read-observation.d.ts +41 -0
  128. package/build/src/git/read-observation.js +361 -0
  129. package/build/src/git/reconcile.d.ts +15 -5
  130. package/build/src/git/reconcile.js +238 -85
  131. package/build/src/git/repository.d.ts +19 -4
  132. package/build/src/git/repository.js +77 -43
  133. package/build/src/git/scratch.d.ts +13 -0
  134. package/build/src/git/scratch.js +66 -0
  135. package/build/src/git/target-placement.d.ts +56 -0
  136. package/build/src/git/target-placement.js +255 -0
  137. package/build/src/git/tender.d.ts +48 -0
  138. package/build/src/git/tender.js +82 -0
  139. package/build/src/git/terminal-seal.d.ts +24 -0
  140. package/build/src/git/terminal-seal.js +87 -0
  141. package/build/src/git/tree.d.ts +2 -2
  142. package/build/src/git/tree.js +4 -0
  143. package/build/src/git/workspace.d.ts +21 -0
  144. package/build/src/git/workspace.js +104 -0
  145. package/build/src/identity/selector.d.ts +6 -0
  146. package/build/src/identity/selector.js +19 -0
  147. package/build/src/index.d.ts +4 -2
  148. package/build/src/index.js +2 -1
  149. package/build/src/kanshi/index.d.ts +1 -1
  150. package/build/src/kanshi/read.d.ts +5 -2
  151. package/build/src/kanshi/read.js +154 -45
  152. package/build/src/kanshi/report.d.ts +31 -6
  153. package/build/src/kanshi/select.js +1 -1
  154. package/build/src/library/address.d.ts +48 -0
  155. package/build/src/library/address.js +134 -0
  156. package/build/src/library/akuma-creation.d.ts +10 -6
  157. package/build/src/library/akuma-creation.js +12 -11
  158. package/build/src/library/bind.d.ts +21 -0
  159. package/build/src/library/bind.js +56 -0
  160. package/build/src/library/catalog.d.ts +58 -0
  161. package/build/src/library/catalog.js +72 -0
  162. package/build/src/library/configuration.d.ts +6 -15
  163. package/build/src/library/configuration.js +14 -17
  164. package/build/src/library/contract.d.ts +21 -46
  165. package/build/src/library/contract.js +193 -183
  166. package/build/src/library/delivery.d.ts +23 -0
  167. package/build/src/library/delivery.js +21 -0
  168. package/build/src/library/fleet.d.ts +65 -0
  169. package/build/src/library/fleet.js +189 -0
  170. package/build/src/library/input.d.ts +3 -0
  171. package/build/src/library/input.js +19 -0
  172. package/build/src/library/keiyaku.d.ts +16 -2
  173. package/build/src/library/keiyaku.js +11 -1
  174. package/build/src/library/mutation.d.ts +38 -0
  175. package/build/src/library/mutation.js +39 -0
  176. package/build/src/library/refusal.d.ts +21 -0
  177. package/build/src/library/refusal.js +29 -0
  178. package/build/src/library/region.d.ts +2 -1
  179. package/build/src/library/region.js +3 -3
  180. package/build/src/library/repo.d.ts +4 -1
  181. package/build/src/library/repo.js +31 -16
  182. package/build/src/protocol/attempt.d.ts +16 -3
  183. package/build/src/protocol/attempt.js +39 -8
  184. package/build/src/protocol/bind.d.ts +9 -2
  185. package/build/src/protocol/bind.js +47 -39
  186. package/build/src/protocol/intent.d.ts +42 -18
  187. package/build/src/protocol/intent.js +36 -75
  188. package/build/src/protocol/operations.d.ts +93 -35
  189. package/build/src/protocol/operations.js +235 -123
  190. package/build/src/protocol/outcome.d.ts +7 -2
  191. package/build/src/protocol/outcome.js +9 -3
  192. package/build/src/protocol/placement.d.ts +30 -0
  193. package/build/src/protocol/placement.js +114 -0
  194. package/build/src/protocol/read/audit.d.ts +9 -3
  195. package/build/src/protocol/read/audit.js +25 -9
  196. package/build/src/protocol/read/documents.d.ts +2 -2
  197. package/build/src/protocol/read/documents.js +4 -4
  198. package/build/src/protocol/read/status.d.ts +11 -5
  199. package/build/src/protocol/read/status.js +28 -15
  200. package/build/src/protocol/run.d.ts +49 -7
  201. package/build/src/protocol/run.js +53 -25
  202. package/build/src/runtime/proc/line-rpc.d.ts +1 -0
  203. package/build/src/runtime/proc/line-rpc.js +21 -0
  204. package/build/src/runtime/proc/run.d.ts +18 -2
  205. package/build/src/runtime/proc/run.js +34 -8
  206. package/build/src/settings.d.ts +6 -0
  207. package/build/src/settings.js +29 -14
  208. package/build/src/settlement/fence.d.ts +9 -0
  209. package/build/src/settlement/fence.js +23 -0
  210. package/build/src/settlement/holder.d.ts +25 -4
  211. package/build/src/settlement/holder.js +102 -28
  212. package/build/src/settlement/settle.d.ts +8 -0
  213. package/build/src/settlement/settle.js +102 -28
  214. package/build/src/task/board.d.ts +20 -2
  215. package/build/src/task/board.js +11 -1
  216. package/build/src/task/compose.d.ts +2 -2
  217. package/build/src/task/compose.js +2 -2
  218. package/build/src/task/context.js +2 -7
  219. package/build/src/task/index.d.ts +9 -9
  220. package/build/src/task/index.js +15 -12
  221. package/build/src/task/operations.d.ts +31 -12
  222. package/build/src/task/operations.js +8 -4
  223. package/build/src/task/store.d.ts +4 -6
  224. package/build/src/task/store.js +3 -3
  225. package/build/src/verification/declaration.d.ts +1 -0
  226. package/build/src/verification/execution.d.ts +49 -0
  227. package/build/src/verification/execution.js +104 -0
  228. package/build/src/world.d.ts +13 -0
  229. package/build/src/world.js +103 -0
  230. package/package.json +7 -4
  231. package/build/src/akuma/activity.d.ts +0 -66
  232. package/build/src/akuma/activity.js +0 -139
  233. package/build/src/akuma/providers/claude.js +0 -305
  234. package/build/src/context-root.d.ts +0 -4
  235. package/build/src/context-root.js +0 -15
  236. package/build/src/git/delivery.d.ts +0 -20
  237. package/build/src/git/delivery.js +0 -109
  238. package/build/src/git/verification.d.ts +0 -14
  239. package/build/src/git/verification.js +0 -56
  240. package/build/src/verification/producer.d.ts +0 -23
  241. package/build/src/verification/producer.js +0 -54
@@ -2,9 +2,9 @@ import { randomUUID } from "node:crypto";
2
2
  import { readdirSync } from "node:fs";
3
3
  import { resolve } from "node:path";
4
4
  import { spawnAkumaBody } from "./body.js";
5
- import { HeldAkumaLeash, activitySlice, life, probeLeash, readCurrentTurn, readHeart, readLastAnsweredTurn, readTurns, readForkPoint, readSeal, readSoul, recordDeath, recordTell, requestPause, requestStop, } from "./heart/index.js";
5
+ import { HeldAkumaLeash, activitySlice, life, 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 { projectActivityHistory, selectActivitySnapshot, } from "./activity.js";
7
+ import { projectActivityHistory, selectActivitySnapshot, } from "./projection.js";
8
8
  import { listArchetypes as readArchetypes, loadArchetype } from "./archetype.js";
9
9
  import { publishAkuma } from "./publication.js";
10
10
  import { providerNamed } from "./providers/index.js";
@@ -35,23 +35,21 @@ async function takeLeashUntil(paths, deadline) {
35
35
  await wait(POLL_MS);
36
36
  }
37
37
  }
38
- function recordTellBody(paths, body) {
38
+ function recordTellBody(paths, akuma, body) {
39
39
  const id = randomUUID();
40
- const admitted = recordTell(paths, { id, body, recordedAt: new Date().toISOString() });
41
- return admitted === "dead" ? { kind: "dead" } : { kind: "recorded", id };
40
+ const admitted = recordTell(paths, { kind: "tell", id, body, recordedAt: new Date().toISOString() });
41
+ if (admitted.kind === "not-born")
42
+ throw new AkumaNotBornError(akuma);
43
+ return { kind: "recorded", tellId: admitted.tell.id };
42
44
  }
43
- async function wakeTell(paths, akuma, id) {
44
- const soul = readSoul(paths);
45
- if (soul === null)
46
- throw new Error(`Akuma ${akuma} has no soul`);
45
+ async function wakeTell(paths, tellId) {
47
46
  try {
48
47
  await spawnAkumaBody({ paths });
49
- return { id, state: "recorded", wake: "spawned" };
48
+ return { admission: { tellId, fact: "recorded" }, wake: "spawned" };
50
49
  }
51
50
  catch (error) {
52
51
  return {
53
- id,
54
- state: "recorded",
52
+ admission: { tellId, fact: "recorded" },
55
53
  wake: { kind: "failed", diagnostic: error instanceof Error ? error.message : String(error) },
56
54
  };
57
55
  }
@@ -74,35 +72,39 @@ function bornListRow(paths, expected, snapshot = readHeart(paths)) {
74
72
  id: snapshot.soul.id,
75
73
  archetype: snapshot.soul.archetype,
76
74
  ...(snapshot.soul.description === undefined ? {} : { description: snapshot.soul.description }),
77
- life: life(probeLeash(paths), collar, snapshot.death),
75
+ life: life(probeLeash(paths), collar, snapshot.latestBody, snapshot.latestKill),
78
76
  collar,
79
77
  confinement: snapshot.soul.confinement,
80
78
  pending: snapshot.pending.map((tell) => tell.id),
81
79
  };
82
80
  }
83
- function bornStatus(paths, expected) {
81
+ function bornStatus(paths, expected, profile = "status") {
84
82
  const snapshot = readHeart(paths);
85
83
  if (snapshot.soul === null)
86
84
  throw new AkumaNotBornError(expected);
87
85
  const current = bornListRow(paths, expected, snapshot);
88
- const latest = readCurrentTurn(paths);
86
+ const resumeUnsupported = current.life === "stranded"
87
+ && snapshot.latestSession?.provider === snapshot.soul.provider.name
88
+ && providerNamed(snapshot.soul.provider).resume === undefined;
89
89
  return {
90
- ...current,
91
- ...(latest === null ? {} : { outcomeAt: latest.completedAt }),
92
- ...(latest?.outcome.kind === "answered"
93
- ? { answer: latest.outcome.answer, answerHistoryId: latest.outcome.historyId }
94
- : {}),
95
- ...(latest?.outcome.kind === "failed" ? { failure: latest.outcome.diagnostic } : {}),
96
- activity: (() => {
97
- const slice = activitySlice(paths, { limit: 5_000 });
90
+ id: current.id,
91
+ life: current.life,
92
+ collar: current.collar,
93
+ ...(resumeUnsupported ? { strandedReason: "resume-unsupported" } : {}),
94
+ timeline: (() => {
95
+ const slice = activitySlice(paths, { limit: Number.MAX_SAFE_INTEGER });
98
96
  return selectActivitySnapshot(slice.rows, {
99
- pending: snapshot.pending,
100
97
  lowestRetained: slice.lowestRetained,
101
98
  highest: slice.highest,
99
+ profile,
102
100
  });
103
101
  })(),
104
102
  };
105
103
  }
104
+ /** Package-internal compact observation for action feedback. */
105
+ export function readActionFeedbackStatus(worldPath, id) {
106
+ return bornStatus(pathsForAkuId(worldPath, id), id, "feedback");
107
+ }
106
108
  function diagnostic(error) {
107
109
  return error instanceof Error ? error.message : String(error);
108
110
  }
@@ -137,8 +139,9 @@ export class AkumaHandle {
137
139
  ...(input.since === undefined ? {} : { since: input.since }),
138
140
  limit: 5_000,
139
141
  });
140
- return projectActivityHistory(slice, readTurns(this.paths), {
141
- since: input.since !== undefined,
142
+ return projectActivityHistory(slice, {
143
+ ...(input.before === undefined ? {} : { before: input.before }),
144
+ ...(input.since === undefined ? {} : { since: input.since }),
142
145
  limit,
143
146
  });
144
147
  }
@@ -156,14 +159,13 @@ export class AkumaHandle {
156
159
  }
157
160
  }
158
161
  async tell(body) {
159
- const recorded = recordTellBody(this.paths, body);
160
- if (recorded.kind === "dead")
161
- throw new Error(`Akuma ${this.id} is dead`);
162
- return await wakeTell(this.paths, this.id, recorded.id);
162
+ const recorded = recordTellBody(this.paths, this.id, body);
163
+ return await wakeTell(this.paths, recorded.tellId);
163
164
  }
164
165
  async interrupt(body) {
165
- if (requestPause(this.paths, new Date().toISOString()) === "dead")
166
- return { kind: "dead" };
166
+ if (requestPause(this.paths, new Date().toISOString()).kind === "not-born") {
167
+ throw new AkumaNotBornError(this.id);
168
+ }
167
169
  let putDown = "was-idle";
168
170
  let leash = HeldAkumaLeash.try(this.paths);
169
171
  if (leash === null) {
@@ -188,16 +190,21 @@ export class AkumaHandle {
188
190
  }
189
191
  let recorded;
190
192
  try {
191
- leash.clearPause(this.paths);
192
- recorded = recordTellBody(this.paths, body);
193
+ const id = randomUUID();
194
+ const admitted = leash.recordInterruptTell(this.paths, {
195
+ kind: "tell",
196
+ id,
197
+ body,
198
+ recordedAt: new Date().toISOString(),
199
+ });
200
+ if (admitted.kind === "not-born")
201
+ throw new AkumaNotBornError(this.id);
202
+ recorded = { kind: "recorded", tellId: admitted.tell.id };
193
203
  }
194
204
  finally {
195
205
  leash.release();
196
206
  }
197
- if (recorded.kind === "dead") {
198
- return { kind: "interrupted", putDown, tell: { kind: "refused-dead" } };
199
- }
200
- return { kind: "interrupted", putDown, tell: await wakeTell(this.paths, this.id, recorded.id) };
207
+ return { kind: "interrupted", putDown, tell: await wakeTell(this.paths, recorded.tellId) };
201
208
  }
202
209
  async fork(input) {
203
210
  const source = readSoul(this.paths);
@@ -256,51 +263,67 @@ export class AkumaHandle {
256
263
  }
257
264
  async kill() {
258
265
  const at = new Date().toISOString();
259
- if (requestStop(this.paths, at) === "dead")
260
- return "already-dead";
266
+ const request = requestStop(this.paths, at);
267
+ if (request.kind === "already-killed")
268
+ return "already-killed";
269
+ const target = request.body;
261
270
  const graceDeadline = performance.now() + KILL_GRACE_MS;
262
271
  while (probeLeash(this.paths) === "held" && performance.now() < graceDeadline)
263
272
  await wait(POLL_MS);
264
- if (probeLeash(this.paths) === "held") {
265
- const current = readHeart(this.paths).latestBody;
266
- if (current === null)
267
- return "unavailable";
268
- const evidence = await putDownProcessTree(current.collar);
273
+ if (readKill(this.paths, target.sequence) !== null)
274
+ return "killed";
275
+ const targetProbe = probeProcessTree(target.collar);
276
+ if (targetProbe.kind === "unverifiable")
277
+ return "unavailable";
278
+ if (targetProbe.kind === "alive") {
279
+ const evidence = await putDownProcessTree(target.collar);
269
280
  if (evidence === "unavailable" || evidence === "alive-after-sigkill")
270
281
  return evidence;
271
282
  }
272
- const releaseDeadline = performance.now() + KILL_GRACE_MS;
273
- while (probeLeash(this.paths) === "held" && performance.now() < releaseDeadline)
274
- await wait(POLL_MS);
275
- if (probeLeash(this.paths) === "held")
276
- return "unavailable";
277
- return recordDeath(this.paths, { evidence: "killed", at }) === "already-dead" ? "already-dead" : "killed";
283
+ const leash = await takeLeashUntil(this.paths, performance.now() + KILL_GRACE_MS);
284
+ if (leash === null)
285
+ return readKill(this.paths, target.sequence) === null ? "unavailable" : "killed";
286
+ try {
287
+ const settled = leash.settleStop(this.paths, target.sequence);
288
+ if (settled === null)
289
+ return "unavailable";
290
+ return "killed";
291
+ }
292
+ finally {
293
+ leash.release();
294
+ }
278
295
  }
279
296
  lastAnswer() {
280
297
  const turn = readLastAnsweredTurn(this.paths);
281
- return turn?.outcome.kind === "answered" ? turn.outcome.answer : "";
298
+ return turn?.end?.outcome.kind === "answered"
299
+ ? { kind: "answer", answer: turn.end.outcome.answer }
300
+ : { kind: "no-answer" };
282
301
  }
283
302
  }
284
303
  export class Akuma {
285
304
  path;
286
- settings;
287
- constructor(path, settings) {
305
+ configuredSettings;
306
+ constructor(path, configuredSettings) {
288
307
  this.path = path;
289
- this.settings = settings;
308
+ this.configuredSettings = configuredSettings;
290
309
  }
291
- static at(input) {
292
- const path = resolve(input.path);
293
- return new Akuma(path, input.settings ?? readSettings({ root: path }));
310
+ static of(root, settings) {
311
+ if (typeof root !== "string")
312
+ throw new TypeError("Akuma.of root must be a WorldRoot");
313
+ return new Akuma(root, settings);
314
+ }
315
+ settings() {
316
+ return this.configuredSettings ?? readSettings({ root: this.path });
294
317
  }
295
318
  of(input) {
296
319
  return new AkumaHandle(parseAkuId(input.id).id, this.path);
297
320
  }
298
321
  listArchetypes() {
299
- return readArchetypes({ settings: this.settings });
322
+ return readArchetypes({ settings: this.settings() });
300
323
  }
301
324
  async call(input) {
302
325
  const name = archetypeName(input.archetype);
303
- const archetype = loadArchetype({ name, settings: this.settings });
326
+ const archetype = loadArchetype({ name, settings: this.settings() });
304
327
  const provider = archetype.adapter;
305
328
  const cwd = resolve(input.cwd ?? this.path);
306
329
  const recipe = Object.freeze({
@@ -342,7 +365,14 @@ export class Akuma {
342
365
  });
343
366
  return new AkumaHandle(published.id, this.path);
344
367
  }
345
- list() {
368
+ list(input = {}) {
369
+ if (typeof input !== "object" || input === null || Array.isArray(input)) {
370
+ throw new TypeError("Akuma list input must be an object");
371
+ }
372
+ const unknown = Object.keys(input).find((key) => key !== "archetype");
373
+ if (unknown !== undefined)
374
+ throw new TypeError(`Akuma list input has unknown field: ${unknown}`);
375
+ const selected = input.archetype === undefined ? undefined : archetypeName(input.archetype);
346
376
  const runRoot = akumaRunRoot(this.path);
347
377
  let names;
348
378
  try {
@@ -359,6 +389,8 @@ export class Akuma {
359
389
  const rows = names.flatMap((name) => {
360
390
  try {
361
391
  const physical = akuIdFromDirectoryName(name);
392
+ if (selected !== undefined && physical.archetype !== selected)
393
+ return [];
362
394
  const paths = akumaPaths({ runRoot, archetype: physical.archetype, suffix: physical.suffix });
363
395
  const snapshot = readHeart(paths);
364
396
  if (snapshot.soul !== null)
@@ -8,6 +8,11 @@ type DecodedArchetype = Readonly<{
8
8
  description?: string;
9
9
  options: ProviderOptions;
10
10
  }>;
11
+ export type ArchetypeCatalogRow = Readonly<{
12
+ name: string;
13
+ model?: string;
14
+ description?: string;
15
+ }>;
11
16
  type ArchetypeDefinition = DecodedArchetype & Readonly<{
12
17
  adapter: ProviderAdapter;
13
18
  }>;
@@ -24,6 +29,9 @@ export declare class AkumaArchetypeError extends Error {
24
29
  export declare function listArchetypes(input: Readonly<{
25
30
  settings: Settings;
26
31
  }>): readonly string[];
32
+ export declare function listArchetypeDefinitions(input: Readonly<{
33
+ settings: Settings;
34
+ }>): readonly ArchetypeCatalogRow[];
27
35
  export declare function loadArchetype(input: Readonly<{
28
36
  name: string;
29
37
  settings: Settings;
@@ -21,27 +21,24 @@ function archetypeDirectory(settings) {
21
21
  const userPath = settings.scopes.user.path;
22
22
  return userPath === undefined ? null : join(dirname(userPath), "akuma");
23
23
  }
24
- export function listArchetypes(input) {
25
- const directory = archetypeDirectory(input.settings);
24
+ function archetypePaths(settings) {
25
+ const directory = archetypeDirectory(settings);
26
26
  if (directory === null)
27
27
  return [];
28
28
  try {
29
29
  return readdirSync(directory, { withFileTypes: true })
30
30
  .flatMap((entry) => {
31
- if (!entry.isFile())
32
- return [];
33
- const filename = entry.name;
34
- if (!filename.endsWith(".md"))
31
+ if (!entry.isFile() || !entry.name.endsWith(".md"))
35
32
  return [];
36
- const name = filename.slice(0, -3);
33
+ const name = entry.name.slice(0, -3);
37
34
  try {
38
- return [archetypeName(name)];
35
+ return [{ name: archetypeName(name), path: join(directory, entry.name) }];
39
36
  }
40
37
  catch {
41
38
  return [];
42
39
  }
43
40
  })
44
- .sort((left, right) => Buffer.compare(Buffer.from(left), Buffer.from(right)));
41
+ .sort((left, right) => Buffer.compare(Buffer.from(left.name), Buffer.from(right.name)));
45
42
  }
46
43
  catch (error) {
47
44
  if (error.code === "ENOENT")
@@ -49,6 +46,9 @@ export function listArchetypes(input) {
49
46
  throw error;
50
47
  }
51
48
  }
49
+ export function listArchetypes(input) {
50
+ return archetypePaths(input.settings).map(({ name }) => name);
51
+ }
52
52
  function archetypeField(values, key, required = false) {
53
53
  const value = values[key];
54
54
  if (value === undefined && !required)
@@ -102,6 +102,21 @@ function decodeArchetype(name, path, markdown) {
102
102
  }),
103
103
  });
104
104
  }
105
+ export function listArchetypeDefinitions(input) {
106
+ return archetypePaths(input.settings).map(({ name, path }) => {
107
+ try {
108
+ const definition = decodeArchetype(name, path, readFileSync(path, "utf8"));
109
+ return Object.freeze({
110
+ name: definition.name,
111
+ ...(definition.options.model === undefined ? {} : { model: definition.options.model }),
112
+ ...(definition.description === undefined ? {} : { description: definition.description }),
113
+ });
114
+ }
115
+ catch (error) {
116
+ throw new AkumaArchetypeError(name, [path], `is invalid: ${error instanceof Error ? error.message : String(error)}`);
117
+ }
118
+ });
119
+ }
105
120
  function record(value) {
106
121
  return value !== null && typeof value === "object" && !Array.isArray(value)
107
122
  ? value : null;
@@ -142,6 +157,10 @@ function providerExecution(settings, name) {
142
157
  return Object.freeze({ name, kind: "claude-agent-sdk" });
143
158
  if (name === "codex-app-server")
144
159
  return Object.freeze({ name, kind: "codex-app-server" });
160
+ if (name === "opencode-sdk")
161
+ return Object.freeze({ name, kind: "opencode-sdk" });
162
+ if (name === "pi")
163
+ return Object.freeze({ name, kind: "pi" });
145
164
  throw new TypeError(`unknown provider ${name}`);
146
165
  }
147
166
  function admitArchetype(archetype, settings) {