@astrosheep/keiyaku 4.0.4 → 4.0.5

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 (173) hide show
  1. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  2. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -3
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +14 -0
  6. package/build/src/akuma/akuma.d.ts +0 -1
  7. package/build/src/akuma/akuma.js +1 -6
  8. package/build/src/akuma/allowed.d.ts +1 -1
  9. package/build/src/akuma/allowed.js +3 -10
  10. package/build/src/akuma/body.d.ts +5 -2
  11. package/build/src/akuma/body.js +29 -23
  12. package/build/src/akuma/heart/facts.d.ts +35 -9
  13. package/build/src/akuma/heart/index.d.ts +4 -13
  14. package/build/src/akuma/heart/index.js +2 -133
  15. package/build/src/akuma/heart/request-authority.d.ts +12 -0
  16. package/build/src/akuma/heart/request-authority.js +119 -0
  17. package/build/src/akuma/heart/request-rows.d.ts +16 -0
  18. package/build/src/akuma/heart/request-rows.js +88 -0
  19. package/build/src/akuma/heart/rows.d.ts +1 -28
  20. package/build/src/akuma/heart/rows.js +0 -137
  21. package/build/src/akuma/heart/schema.d.ts +1 -1
  22. package/build/src/akuma/heart/schema.js +9 -2
  23. package/build/src/akuma/heart/soul.js +0 -36
  24. package/build/src/akuma/index.d.ts +1 -1
  25. package/build/src/akuma/provider-recipe.js +1 -11
  26. package/build/src/akuma/provider.d.ts +1 -6
  27. package/build/src/akuma/providers/acp/core.js +6 -2
  28. package/build/src/akuma/providers/acp/index.js +0 -1
  29. package/build/src/akuma/providers/claude/index.d.ts +0 -4
  30. package/build/src/akuma/providers/claude/index.js +7 -3
  31. package/build/src/akuma/providers/codex-app-server/index.d.ts +2 -27
  32. package/build/src/akuma/providers/codex-app-server/index.js +0 -2
  33. package/build/src/akuma/providers/grok-build/index.js +0 -1
  34. package/build/src/akuma/providers/opencode-sdk/index.d.ts +2 -28
  35. package/build/src/akuma/providers/opencode-sdk/index.js +8 -4
  36. package/build/src/akuma/providers/opencode-sdk/session.d.ts +1 -1
  37. package/build/src/akuma/providers/opencode-sdk/session.js +1 -1
  38. package/build/src/akuma/providers/pi/index.js +11 -4
  39. package/build/src/akuma/request-serve.d.ts +87 -0
  40. package/build/src/akuma/request-serve.js +484 -0
  41. package/build/src/akuma/request-wire.d.ts +124 -0
  42. package/build/src/akuma/request-wire.js +380 -0
  43. package/build/src/akuma/requests.d.ts +28 -94
  44. package/build/src/akuma/requests.js +52 -562
  45. package/build/src/akuma-body.d.ts +7 -0
  46. package/build/src/akuma-body.js +54 -1
  47. package/build/src/body/arc.js +4 -29
  48. package/build/src/body/decode.js +5 -39
  49. package/build/src/body/envelope.d.ts +7 -0
  50. package/build/src/body/envelope.js +37 -0
  51. package/build/src/cli/accepted.js +17 -9
  52. package/build/src/cli/commands/akuma.js +2 -2
  53. package/build/src/cli/commands/contract.d.ts +6 -6
  54. package/build/src/cli/commands/contract.js +3 -3
  55. package/build/src/cli/commands/task-invoke.js +98 -9
  56. package/build/src/cli/coordinates.d.ts +1 -0
  57. package/build/src/cli/coordinates.js +7 -4
  58. package/build/src/cli/invoke.js +28 -4
  59. package/build/src/cli/parse.d.ts +2 -2
  60. package/build/src/cli/parse.js +21 -8
  61. package/build/src/cli/render/akuma.js +56 -13
  62. package/build/src/cli/render/audit.js +2 -6
  63. package/build/src/cli/render/contract.js +40 -0
  64. package/build/src/cli/render/receipt.js +28 -1
  65. package/build/src/cli/render/terminal.d.ts +0 -2
  66. package/build/src/cli/render/terminal.js +0 -29
  67. package/build/src/cli/result.d.ts +15 -7
  68. package/build/src/contract-worktree.d.ts +1 -2
  69. package/build/src/contract-worktree.js +4 -9
  70. package/build/src/core/facts/codec.js +9 -0
  71. package/build/src/core/facts/fold.js +13 -1
  72. package/build/src/core/facts/types.d.ts +8 -1
  73. package/build/src/core/subject.js +4 -3
  74. package/build/src/core/verbs/placement.d.ts +9 -1
  75. package/build/src/core/verbs/placement.js +24 -6
  76. package/build/src/core/verbs/reintegrate.d.ts +13 -0
  77. package/build/src/core/verbs/reintegrate.js +24 -0
  78. package/build/src/dispatch/index.d.ts +1 -1
  79. package/build/src/git/admission.d.ts +2 -1
  80. package/build/src/git/integration.d.ts +5 -2
  81. package/build/src/git/integration.js +54 -1
  82. package/build/src/git/observe.d.ts +2 -1
  83. package/build/src/git/observe.js +6 -5
  84. package/build/src/git/process.d.ts +26 -0
  85. package/build/src/git/process.js +94 -0
  86. package/build/src/git/read-observation.d.ts +2 -1
  87. package/build/src/git/read-observation.js +3 -2
  88. package/build/src/git/reconcile.d.ts +2 -1
  89. package/build/src/git/reconcile.js +9 -6
  90. package/build/src/git/repository.d.ts +2 -29
  91. package/build/src/git/repository.js +4 -118
  92. package/build/src/git/scratch.d.ts +1 -1
  93. package/build/src/git/scratch.js +1 -1
  94. package/build/src/git/target-placement.d.ts +1 -1
  95. package/build/src/git/target-placement.js +9 -7
  96. package/build/src/git/tender.d.ts +1 -1
  97. package/build/src/git/tender.js +2 -1
  98. package/build/src/git/terminal-seal.d.ts +2 -2
  99. package/build/src/git/terminal-seal.js +3 -6
  100. package/build/src/git/workspace.d.ts +1 -1
  101. package/build/src/git/workspace.js +1 -1
  102. package/build/src/index.d.ts +1 -2
  103. package/build/src/index.js +1 -1
  104. package/build/src/library/address.js +7 -1
  105. package/build/src/library/audit.d.ts +2 -1
  106. package/build/src/library/audit.js +1 -1
  107. package/build/src/library/bind.d.ts +2 -1
  108. package/build/src/library/bind.js +1 -1
  109. package/build/src/library/configuration.d.ts +1 -1
  110. package/build/src/library/configuration.js +52 -18
  111. package/build/src/library/contract.d.ts +55 -2
  112. package/build/src/library/contract.js +162 -23
  113. package/build/src/library/delivery.d.ts +1 -1
  114. package/build/src/library/fleet.d.ts +27 -7
  115. package/build/src/library/fleet.js +79 -39
  116. package/build/src/library/input.d.ts +0 -1
  117. package/build/src/library/input.js +0 -5
  118. package/build/src/library/keiyaku.d.ts +1 -1
  119. package/build/src/library/mutation.d.ts +2 -1
  120. package/build/src/library/reconcile.d.ts +2 -1
  121. package/build/src/library/reconcile.js +3 -1
  122. package/build/src/library/region.d.ts +1 -1
  123. package/build/src/library/repo.d.ts +3 -1
  124. package/build/src/library/repo.js +8 -4
  125. package/build/src/protocol/abandon.d.ts +9 -0
  126. package/build/src/protocol/abandon.js +15 -0
  127. package/build/src/protocol/amend.d.ts +16 -0
  128. package/build/src/protocol/amend.js +77 -0
  129. package/build/src/protocol/arc.d.ts +6 -0
  130. package/build/src/protocol/arc.js +12 -0
  131. package/build/src/protocol/attempt.d.ts +2 -1
  132. package/build/src/protocol/audit.d.ts +53 -0
  133. package/build/src/protocol/audit.js +140 -0
  134. package/build/src/protocol/bind.d.ts +1 -1
  135. package/build/src/protocol/completion.d.ts +32 -0
  136. package/build/src/protocol/completion.js +130 -0
  137. package/build/src/protocol/deliver.d.ts +37 -0
  138. package/build/src/protocol/deliver.js +160 -0
  139. package/build/src/protocol/intent.d.ts +1 -1
  140. package/build/src/protocol/intent.js +1 -1
  141. package/build/src/protocol/operations.d.ts +64 -200
  142. package/build/src/protocol/operations.js +21 -640
  143. package/build/src/protocol/placement.d.ts +4 -13
  144. package/build/src/protocol/placement.js +3 -15
  145. package/build/src/protocol/read/status.d.ts +1 -1
  146. package/build/src/protocol/read/status.js +3 -1
  147. package/build/src/protocol/reconcile.d.ts +35 -0
  148. package/build/src/protocol/reconcile.js +41 -0
  149. package/build/src/protocol/reintegrate.d.ts +31 -0
  150. package/build/src/protocol/reintegrate.js +74 -0
  151. package/build/src/protocol/review.d.ts +33 -0
  152. package/build/src/protocol/review.js +148 -0
  153. package/build/src/protocol/run.d.ts +1 -1
  154. package/build/src/settlement/fence.d.ts +1 -1
  155. package/build/src/settlement/holder.d.ts +1 -1
  156. package/build/src/settlement/settle.d.ts +1 -1
  157. package/build/src/task/board.d.ts +0 -1
  158. package/build/src/task/board.js +0 -3
  159. package/build/src/task/compose.d.ts +1 -1
  160. package/build/src/task/compose.js +11 -4
  161. package/build/src/task/index.d.ts +1 -1
  162. package/build/src/task/index.js +2 -127
  163. package/build/src/task/input.d.ts +18 -0
  164. package/build/src/task/input.js +189 -0
  165. package/build/src/task/mutation.d.ts +39 -0
  166. package/build/src/task/mutation.js +85 -0
  167. package/build/src/task/operations.d.ts +2 -0
  168. package/build/src/task/operations.js +35 -11
  169. package/build/src/task/store.js +0 -2
  170. package/build/src/verification/execution.d.ts +1 -1
  171. package/build/src/workspace-place.d.ts +1 -4
  172. package/build/src/workspace-place.js +0 -15
  173. package/package.json +3 -3
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keiyaku",
3
- "version": "0.1.0+codex.20260818034655",
3
+ "version": "0.1.0+codex.20260819092206",
4
4
  "description": "Use the Keiyaku contract, task, and Akuma CLI.",
5
5
  "author": { "name": "Keiyaku" },
6
6
  "skills": "./skills/"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keiyaku",
3
- "version": "0.1.0+codex.20260818034655",
3
+ "version": "0.1.0+codex.20260819092206",
4
4
  "description": "Use the Keiyaku contract, task, and Akuma CLI.",
5
5
  "author": {
6
6
  "name": "Keiyaku"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keiyaku-harness",
3
- "version": "0.1.0+codex.20260818034655",
3
+ "version": "0.1.0+codex.20260819092206",
4
4
  "type": "module",
5
5
  "main": "./opencode.js",
6
6
  "keywords": ["pi-package"],
@@ -15,9 +15,9 @@ read `<command> --help` before using flags.
15
15
  then `keiyaku-workflow` for the remaining lifecycle.
16
16
  - An Akuma is a callable worker; see `keiyaku-akuma`.
17
17
 
18
- Settings owns named gate sets. Omitting `--gates` selects `gates.default`, or
19
- freezes an empty gate list when that entry is absent. `--gates <name>` selects
20
- one configured set; it does not add a literal gate word.
18
+ Settings owns named gate bundles. Omitting `--gates` selects `gates.default`,
19
+ or freezes `["reviewed"]` when that entry is absent. `--gates <name,...>`
20
+ selects configured bundles in order; it does not add literal gate words.
21
21
 
22
22
  ## Quick Start
23
23
 
@@ -94,6 +94,12 @@ frontmatter names `Contract`, then reads the listed owner documents and source
94
94
  files before acting. Do not substitute a generic repository tour for the files
95
95
  that actually govern the assignment.
96
96
 
97
+ Contract association, available forwarded actions, and the brief are
98
+ independent inputs. When a Reviewer should record its own verdict, include
99
+ `--allowed contract.review` and say in the brief to record `--satisfied` on
100
+ pass or `--unsatisfied` on failure. When a Deliverer should tender its completed
101
+ candidate, include `--allowed contract.deliver` and say so in the brief.
102
+
97
103
  A `Deliverer` implements and verifies the terms in `Worktree`. Commission a
98
104
  `Reviewer` after delivery. The reviewer inspects the complete current Contract
99
105
  worktree snapshot, not a worker report or named candidate commit, and does not
@@ -182,6 +188,11 @@ substitute for them.
182
188
 
183
189
  ## Deliver
184
190
 
191
+ When the brief assigns delivery to the Deliverer, include `--allowed
192
+ contract.deliver`; require it to run the command below after verification and
193
+ return the receipt. Otherwise the coordinator runs it after accepting the
194
+ candidate.
195
+
185
196
  Deliver when the worktree content is the candidate you intend to land:
186
197
 
187
198
  ```bash
@@ -217,6 +228,9 @@ keiyaku review <contract> --unsatisfied --summary "<finding>"
217
228
  ```
218
229
 
219
230
  Have an independent reviewer inspect the delivered Contract worktree snapshot.
231
+ If it should record the verdict itself, dispatch it with `--allowed
232
+ contract.review` and state both verdicts in the brief. Otherwise its answer is
233
+ review input for the coordinator to record.
220
234
  The `review` command records the verdict. `--satisfied` requests placement; if
221
235
  the other gates are current, the receipt shows `claimed`.
222
236
 
@@ -10,7 +10,6 @@ export type AkumaListRow = Readonly<{
10
10
  description?: string;
11
11
  life: AkumaLife;
12
12
  lifeAt: string | null;
13
- confinement: Soul["confinement"];
14
13
  pending: readonly string[];
15
14
  }>;
16
15
  export type AkumaStatus = Readonly<{
@@ -107,7 +107,6 @@ async function bornListRow(paths, expected, snapshot) {
107
107
  ...(snapshot.soul.description === undefined ? {} : { description: snapshot.soul.description }),
108
108
  life: currentLife,
109
109
  lifeAt: lifeAt(currentLife, snapshot.latestBody, snapshot.latestKill, snapshot.soul.createdAt),
110
- confinement: snapshot.soul.confinement,
111
110
  pending: snapshot.pending.map((tell) => tell.id),
112
111
  };
113
112
  }
@@ -294,7 +293,6 @@ export class AkumaHandle {
294
293
  allowed: source.allowed,
295
294
  cwd: source.cwd,
296
295
  origin: { kind: "fork", parent: this.id, at: input.at },
297
- confinement: source.confinement,
298
296
  },
299
297
  birthSession,
300
298
  }),
@@ -416,10 +414,7 @@ export class Akuma {
416
414
  const cwd = input.cwd !== undefined && context?.cwdCanonical === true
417
415
  ? input.cwd
418
416
  : await canonicalBirthCwd(selectedCwd);
419
- const recipe = Object.freeze({
420
- ...requestRecipe,
421
- confinement: archetype.adapter.confinement({ cwd, options: archetype.options }),
422
- });
417
+ const recipe = requestRecipe;
423
418
  const published = await publishAkuma({
424
419
  worldPath: this.path,
425
420
  archetype: archetype.name,
@@ -1,4 +1,4 @@
1
- export declare const ALLOWED_ACTIONS: readonly ["akuma.call", "akuma.kill", "akuma.tell", "contract.deliver", "task.add", "task.addDocument", "task.compose", "task.done", "task.drop", "task.hold", "task.resume", "task.start", "task.stop", "task.update"];
1
+ export declare const ALLOWED_ACTIONS: readonly ["akuma.call", "akuma.kill", "akuma.tell", "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
2
  export type AllowedAction = (typeof ALLOWED_ACTIONS)[number];
3
3
  export type AllowedActions = readonly AllowedAction[];
4
4
  export declare function isAllowedAction(value: unknown): value is AllowedAction;
@@ -1,18 +1,11 @@
1
+ import { TASK_MUTATION_ACTIONS } from "../task/mutation.js";
1
2
  export const ALLOWED_ACTIONS = Object.freeze([
2
3
  "akuma.call",
3
4
  "akuma.kill",
4
5
  "akuma.tell",
5
6
  "contract.deliver",
6
- "task.add",
7
- "task.addDocument",
8
- "task.compose",
9
- "task.done",
10
- "task.drop",
11
- "task.hold",
12
- "task.resume",
13
- "task.start",
14
- "task.stop",
15
- "task.update",
7
+ "contract.review",
8
+ ...TASK_MUTATION_ACTIONS,
16
9
  ]);
17
10
  const ALLOWED_ACTION_SET = new Set(ALLOWED_ACTIONS);
18
11
  export function isAllowedAction(value) {
@@ -1,7 +1,7 @@
1
1
  import { type SessionFact, type Soul } from "./heart/index.js";
2
2
  import type { AkumaPaths } from "./identity.js";
3
3
  import { type ProviderAdapter } from "./provider.js";
4
- import { type UpstreamExecutionPort, type RequestChildLaunch } from "./requests.js";
4
+ import { type UpstreamExecutionPort, type RequestChildLaunch } from "./request-serve.js";
5
5
  export declare const CONTROL_RESPONSE_MS = 1000;
6
6
  export type BodyLaunch = Readonly<{
7
7
  paths: AkumaPaths;
@@ -16,5 +16,8 @@ type BodyRuntime = Readonly<{
16
16
  }>;
17
17
  export declare function driveAkumaBody(launch: BodyLaunch, adapter?: ProviderAdapter, runtime?: BodyRuntime): Promise<void>;
18
18
  export declare function spawnAkumaBody(launch: BodyLaunch): Promise<void>;
19
- export declare function runAkumaBody(upstreamFor: (launch: BodyLaunch) => UpstreamExecutionPort): Promise<void>;
19
+ export declare function runAkumaBody(upstreamFor: (launch: BodyLaunch, configuration: Readonly<{
20
+ home?: string;
21
+ gitPath?: string;
22
+ }>) => UpstreamExecutionPort): Promise<void>;
20
23
  export {};
@@ -3,7 +3,7 @@ import { abortableDelay } from "./abort.js";
3
3
  import { HeldAkumaLeash, appendActivity, beginTurn, breakBody, endTurn, finishBodyIfIdle, heartExists, isHeartAbsent, readHeart, recordSession, recordTellDeliveries, recordTellReceipt, } from "./heart/index.js";
4
4
  import { encodeAgentEvent, } from "./provider.js";
5
5
  import { resolveProviderExecution } from "./providers/index.js";
6
- import { BodyRequestPump, clearBodyRequestTransport, settleBodyRequests, } from "./requests.js";
6
+ import { BodyRequestPump, clearBodyRequestTransport, settleBodyRequests, } from "./request-serve.js";
7
7
  import { spawnDetachedProcess, } from "../runtime/proc/run.js";
8
8
  const LEASH_RETRY_MS = 25;
9
9
  const BODY_CONTROL_OBSERVATION_MS = 100;
@@ -178,24 +178,22 @@ async function startTurnDrive(input) {
178
178
  startedAt: input.now(),
179
179
  ...(input.call === undefined ? {} : { call: input.call }),
180
180
  });
181
- const requests = input.soul.confinement.kind === "declared"
182
- ? await BodyRequestPump.open({
183
- paths: input.paths,
184
- parent: input.soul,
185
- bodySequence: input.bodySequence,
186
- now: input.now,
187
- spawn: input.runtimeSpawn,
188
- ...(input.upstream === undefined ? {} : { upstream: input.upstream }),
189
- signal: input.supervisor.signal,
190
- })
191
- : null;
181
+ const requests = await BodyRequestPump.open({
182
+ paths: input.paths,
183
+ parent: input.soul,
184
+ bodySequence: input.bodySequence,
185
+ now: input.now,
186
+ spawn: input.runtimeSpawn,
187
+ ...(input.upstream === undefined ? {} : { upstream: input.upstream }),
188
+ signal: input.supervisor.signal,
189
+ });
192
190
  const driveInput = {
193
191
  body: input.body,
194
192
  launchTells: input.launchTells.map((tell) => ({ id: tell.id, text: tell.body })),
195
193
  cwd,
196
194
  options,
197
195
  signal: input.supervisor.signal,
198
- ...(requests === null ? {} : { requests: { dir: requests.directory } }),
196
+ requests: { dir: requests.directory },
199
197
  };
200
198
  const setup = session === undefined
201
199
  ? input.adapter.start({ ...driveInput, session: { kind: "fresh" } })
@@ -204,7 +202,7 @@ async function startTurnDrive(input) {
204
202
  const selected = await setup;
205
203
  if (input.supervisor.signal.aborted) {
206
204
  await retireProviderCustody(input, turn.sequence, selected);
207
- await requests?.close();
205
+ await requests.close();
208
206
  return { kind: "stopped" };
209
207
  }
210
208
  if (input.launchTells.length > 0) {
@@ -216,13 +214,12 @@ async function startTurnDrive(input) {
216
214
  deliveredAt: input.now(),
217
215
  })));
218
216
  }
219
- if (requests !== null)
220
- void selected.completion.then(() => requests.stopAdmission());
217
+ void selected.completion.then(() => requests.stopAdmission());
221
218
  return { turnSequence: turn.sequence, drive: selected, requests, cwd, options,
222
219
  ...(session === undefined ? {} : { resume: session }) };
223
220
  }
224
221
  catch (error) {
225
- await requests?.close();
222
+ await requests.close();
226
223
  if (input.supervisor.signal.aborted) {
227
224
  if (input.supervisor.reason === "heart-gone")
228
225
  throw error;
@@ -311,7 +308,7 @@ async function submitPendingLiveTells(writers, pending, attempted, tellLive) {
311
308
  }
312
309
  async function stopActiveDrive(input, active) {
313
310
  await retireProviderCustody(input, active.turnSequence, active.drive);
314
- await active.requests?.close();
311
+ await active.requests.close();
315
312
  }
316
313
  function hasUnattemptedTell(liveTells, tellPump, pending, attempted) {
317
314
  return liveTells && tellPump === null && pending.some((tell) => !attempted.has(tell.id));
@@ -320,7 +317,7 @@ function persistProviderEvent(_input, _active, event) {
320
317
  return event.type === "session" ? event.coordinate : undefined;
321
318
  }
322
319
  async function settleCompletion(result, session, requests) {
323
- await requests?.close();
320
+ await requests.close();
324
321
  return result.kind === "answered" ? { ...result, ...(session === undefined ? {} : { session }) } : result;
325
322
  }
326
323
  async function consumeTurnDrive(input, active) {
@@ -360,7 +357,7 @@ async function consumeTurnDrive(input, active) {
360
357
  input.supervisor.next(heart).then((observation) => ({ kind: "heart", observation })),
361
358
  ...(tellObservation === null ? [] : [tellObservation]),
362
359
  receiptFailure,
363
- ...(requests === null ? [] : [requests.failure]),
360
+ requests.failure,
364
361
  ]);
365
362
  if (next.kind === "event" && !next.event.done) {
366
363
  await writeProviderEvent(input, active, next.event.value);
@@ -395,7 +392,7 @@ async function consumeTurnDrive(input, active) {
395
392
  }
396
393
  await retireProviderCustody(input, turnSequence, drive);
397
394
  try {
398
- await requests?.close();
395
+ await requests.close();
399
396
  }
400
397
  catch { /* preserve the first pump failure */ }
401
398
  return { kind: "failed", diagnostic: error instanceof Error ? error.message : String(error) };
@@ -424,7 +421,7 @@ async function driveTurn(input) {
424
421
  }
425
422
  catch (error) {
426
423
  if (await heartExists(input.paths)) {
427
- await active.requests?.close();
424
+ await active.requests.close();
428
425
  return { kind: "failed", diagnostic: error instanceof Error ? error.message : String(error),
429
426
  turnSequence: active.turnSequence };
430
427
  }
@@ -615,9 +612,18 @@ export async function runAkumaBody(upstreamFor) {
615
612
  if (encoded === undefined)
616
613
  throw new TypeError("Akuma body launch payload is missing");
617
614
  const launch = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
615
+ const mappedHome = process.env.KEIYAKU_HOME?.trim();
616
+ const mappedGitPath = process.env.KEIYAKU_GIT_PATH;
617
+ if (mappedGitPath !== undefined && mappedGitPath.trim().length === 0) {
618
+ throw new TypeError("KEIYAKU_GIT_PATH requires a nonblank value");
619
+ }
620
+ const configuration = {
621
+ ...(mappedHome === undefined || mappedHome.length === 0 ? {} : { home: mappedHome }),
622
+ ...(mappedGitPath === undefined ? {} : { gitPath: mappedGitPath }),
623
+ };
618
624
  await driveAkumaBody(launch, undefined, {
619
625
  now: () => new Date().toISOString(),
620
626
  spawnChild: spawnAkumaBody,
621
- upstream: upstreamFor(launch),
627
+ upstream: upstreamFor(launch, configuration),
622
628
  });
623
629
  }
@@ -2,13 +2,8 @@ import type { AkuId } from "../identity.js";
2
2
  import type { ResumeCoordinate } from "../coordinate.js";
3
3
  import type { ProviderExecution, ProviderOptions, ReadonlyRestraint } from "../provider-recipe.js";
4
4
  import type { AllowedActions } from "../allowed.js";
5
+ import type { TaskMutationAction, TaskMutationRequest } from "../../task/mutation.js";
5
6
  export type { ResumeCoordinate } from "../coordinate.js";
6
- export type Confinement = Readonly<{
7
- kind: "unconfined";
8
- }> | Readonly<{
9
- kind: "declared";
10
- writableRoots: readonly string[];
11
- }>;
12
7
  export type AkumaOrigin = Readonly<{
13
8
  kind: "direct";
14
9
  }> | Readonly<{
@@ -29,11 +24,10 @@ export type Soul = Readonly<{
29
24
  readonly?: ReadonlyRestraint;
30
25
  cwd: string;
31
26
  origin: AkumaOrigin;
32
- confinement: Confinement;
33
27
  allowed: AllowedActions;
34
28
  createdAt: string;
35
29
  }>;
36
- export type RequestRecipe = Pick<Soul, "description" | "provider" | "options" | "readonly" | "confinement" | "allowed">;
30
+ export type RequestRecipe = Pick<Soul, "description" | "provider" | "options" | "readonly" | "allowed">;
37
31
  export type BodyEnd = "exited" | "broke-off" | "put-down";
38
32
  export type BodyFact = Readonly<{
39
33
  sequence: number;
@@ -129,6 +123,12 @@ export type TellReceiptInput = Readonly<{
129
123
  turnSequence: number;
130
124
  fence: string;
131
125
  }>);
126
+ export type TaskRequestInput = Readonly<{
127
+ id: string;
128
+ action: TaskMutationAction;
129
+ world: string;
130
+ request: TaskMutationRequest;
131
+ }>;
132
132
  export type RequestInput = Readonly<{
133
133
  id: string;
134
134
  action: "akuma.call";
@@ -152,7 +152,21 @@ export type RequestInput = Readonly<{
152
152
  id: string;
153
153
  action: "akuma.kill";
154
154
  targets: readonly AkuId[];
155
- }>;
155
+ }> | Readonly<{
156
+ id: string;
157
+ action: "contract.deliver";
158
+ repoRoot: string;
159
+ contractId: string;
160
+ message?: string;
161
+ includeDirty: boolean;
162
+ }> | Readonly<{
163
+ id: string;
164
+ action: "contract.review";
165
+ repoRoot: string;
166
+ contractId: string;
167
+ verdict: "satisfied" | "unsatisfied";
168
+ summary?: string;
169
+ }> | TaskRequestInput;
156
170
  export type UpstreamRequestService = Readonly<{
157
171
  action: "akuma.wait";
158
172
  }> | Readonly<{
@@ -165,6 +179,18 @@ export type UpstreamRequestService = Readonly<{
165
179
  id: AkuId;
166
180
  evidence: KillEvidence;
167
181
  }>[];
182
+ }> | Readonly<{
183
+ action: "contract.deliver";
184
+ repoRoot: string;
185
+ contractId: string;
186
+ deliveryFactId: string;
187
+ }> | Readonly<{
188
+ action: "contract.review";
189
+ repoRoot: string;
190
+ contractId: string;
191
+ reviewFactId: string;
192
+ }> | Readonly<{
193
+ action: TaskMutationRequest["action"];
168
194
  }>;
169
195
  type AdmittedRequest = RequestInput & Readonly<{
170
196
  requester: AkuId;
@@ -1,11 +1,12 @@
1
- import type { AkuId, AkumaPaths } from "../identity.js";
2
- import type { BodyEnd, BodyFact, ForkPoint, HeartSnapshot, KillFact, RequestFact, RequestInput, UpstreamRequestService, SealFact, SessionFact, Soul, TellDeliveryInput, TellFact, TellReceiptInput, TurnEndFact, TurnFact, TurnOutcome, TurnStartFact } from "./facts.js";
1
+ import type { AkumaPaths } from "../identity.js";
2
+ import type { BodyEnd, BodyFact, ForkPoint, HeartSnapshot, KillFact, SealFact, SessionFact, Soul, TellDeliveryInput, TellFact, TellReceiptInput, TurnEndFact, TurnFact, TurnOutcome, TurnStartFact } from "./facts.js";
3
3
  export type { CallFact } from "./facts.js";
4
4
  import type { ActivityFact } from "./rows.js";
5
5
  import { type ActivityFactSlice } from "./timeline.js";
6
6
  export { HeartAbsentError, HeldAkumaLeash, initializeHeart, isHeartAbsent, probeLeash } from "./storage.js";
7
+ export { admitRequest, readNonterminalRequests, readRequest, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, voidRequest, } from "./request-authority.js";
7
8
  export { life, lifeAt } from "./facts.js";
8
- export type { AkumaLife, AkumaOrigin, BodyEnd, BodyFact, Confinement, HeartSnapshot, ForkPoint, KillEvidence, KillFact, LeashProbe, PauseFact, RequestFact, RequestInput, RequestRecipe, UpstreamRequestService, SealFact, SessionFact, Soul, StopFact, TellFact, TellDelivery, TellDeliveryInput, TellReceiptInput, TurnEndFact, TurnFact, TurnOutcome, TurnStartFact, } from "./facts.js";
9
+ export type { AkumaLife, AkumaOrigin, BodyEnd, BodyFact, HeartSnapshot, ForkPoint, KillEvidence, KillFact, LeashProbe, PauseFact, RequestFact, RequestInput, RequestRecipe, UpstreamRequestService, SealFact, SessionFact, Soul, StopFact, TellFact, TellDelivery, TellDeliveryInput, TellReceiptInput, TurnEndFact, TurnFact, TurnOutcome, TurnStartFact, } from "./facts.js";
9
10
  export type { ResumeCoordinate } from "../coordinate.js";
10
11
  export declare function readSoul(paths: AkumaPaths): Promise<Soul | null>;
11
12
  export declare function heartExists(paths: AkumaPaths): Promise<boolean>;
@@ -28,16 +29,6 @@ export declare function recordTell(paths: AkumaPaths, tell: Omit<TellFact, "sequ
28
29
  }>>;
29
30
  export declare function recordTellDeliveries(paths: AkumaPaths, inputs: readonly TellDeliveryInput[]): Promise<void>;
30
31
  export declare function recordTellReceipt(paths: AkumaPaths, input: TellReceiptInput): Promise<void>;
31
- export declare function admitRequest(paths: AkumaPaths, input: RequestInput & Readonly<{
32
- admittedAt: string;
33
- }>): Promise<RequestFact>;
34
- export declare function reserveRequest(paths: AkumaPaths, id: string, child: AkuId): Promise<RequestFact>;
35
- export declare function serveRequest(paths: AkumaPaths, id: string, child: AkuId): Promise<RequestFact>;
36
- export declare function serveUpstreamRequest(paths: AkumaPaths, id: string, service: UpstreamRequestService): Promise<RequestFact>;
37
- export declare function refuseRequest(paths: AkumaPaths, id: string, diagnostic: string): Promise<RequestFact>;
38
- export declare function voidRequest(paths: AkumaPaths, id: string, evidence: string): Promise<RequestFact>;
39
- export declare function readRequest(paths: AkumaPaths, id: string): Promise<RequestFact | null>;
40
- export declare function readNonterminalRequests(paths: AkumaPaths): Promise<readonly RequestFact[]>;
41
32
  export declare function readKill(paths: AkumaPaths, bodySequence: number): Promise<KillFact | null>;
42
33
  export declare function requestStop(paths: AkumaPaths, at: string): Promise<Readonly<{
43
34
  kind: "requested";
@@ -1,10 +1,10 @@
1
- import { answeredTurnFact, endBodyFact, finishBodyFact, insertActivityFact, insertTurnEndFact, insertTurnStartFact, insertPauseControl, insertRequestFact, insertSessionFact, insertStopControl, killFactForBody, latestBodyFact, latestKillFact, latestSessionFact, lastAnsweredTurnFact, nonterminalRequestFacts, pauseFact, requestFact, sessionFactForCoordinate, stopFact, updateRequestRefused, updateRequestReserved, updateRequestServed, updateRequestVoided, updateUpstreamRequestServed, } from "./rows.js";
1
+ import { answeredTurnFact, endBodyFact, finishBodyFact, insertActivityFact, insertTurnEndFact, insertTurnStartFact, insertPauseControl, insertSessionFact, insertStopControl, killFactForBody, latestBodyFact, latestKillFact, latestSessionFact, lastAnsweredTurnFact, pauseFact, sessionFactForCoordinate, stopFact, } from "./rows.js";
2
2
  import { insertTellDeliveryFact, insertTellFact, insertTellReceiptFact, pendingTellFacts, tellFact, tellIdsForFence, } from "./tells.js";
3
3
  import { activityFactSlice, pruneActivityFacts, } from "./timeline.js";
4
4
  import { isHeartAbsent, readSealFromLeash, readTransaction, transaction, withHeart } from "./storage.js";
5
5
  import { soulFact } from "./soul.js";
6
- import { clipAllowedActions } from "../allowed.js";
7
6
  export { HeartAbsentError, HeldAkumaLeash, initializeHeart, isHeartAbsent, probeLeash } from "./storage.js";
7
+ export { admitRequest, readNonterminalRequests, readRequest, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, voidRequest, } from "./request-authority.js";
8
8
  export { life, lifeAt } from "./facts.js";
9
9
  const ACTIVITY_LIMIT = 5_000;
10
10
  export async function readSoul(paths) {
@@ -89,137 +89,6 @@ export async function recordTellReceipt(paths, input) {
89
89
  pruneActivityFacts(heart, ACTIVITY_LIMIT);
90
90
  }));
91
91
  }
92
- function sameRequestInput(fact, input) {
93
- if (fact.id !== input.id || fact.action !== input.action)
94
- return false;
95
- if (fact.action === "akuma.call" && input.action === "akuma.call") {
96
- return fact.archetype === input.archetype
97
- && fact.body === input.body
98
- && fact.cwd === input.cwd
99
- && fact.world === input.world
100
- && JSON.stringify(fact.recipe) === JSON.stringify(input.recipe);
101
- }
102
- if (fact.action === "akuma.wait" && input.action === "akuma.wait") {
103
- return fact.completion === input.completion
104
- && fact.timeoutMs === input.timeoutMs
105
- && JSON.stringify(fact.targets) === JSON.stringify(input.targets);
106
- }
107
- if (fact.action === "akuma.tell" && input.action === "akuma.tell") {
108
- return fact.target === input.target && fact.body === input.body;
109
- }
110
- return fact.action === "akuma.kill" && input.action === "akuma.kill"
111
- && JSON.stringify(fact.targets) === JSON.stringify(input.targets);
112
- }
113
- export async function admitRequest(paths, input) {
114
- return await withHeart(paths, (heart) => transaction(heart, () => {
115
- const soul = soulFact(heart);
116
- if (soul === null)
117
- throw new Error("Akuma request admission requires a born Soul");
118
- const action = input.action;
119
- const parentAllowed = soul.allowed;
120
- const permitted = action === "akuma.wait" || parentAllowed.includes(action);
121
- const normalized = input.action === "akuma.call"
122
- ? {
123
- ...input,
124
- recipe: {
125
- ...input.recipe,
126
- allowed: permitted ? clipAllowedActions(input.recipe.allowed, parentAllowed) : input.recipe.allowed,
127
- },
128
- }
129
- : input;
130
- insertRequestFact(heart, {
131
- ...normalized,
132
- requester: soul.id,
133
- admittedAt: input.admittedAt,
134
- ...(permitted ? {} : { refusal: `not-allowed: ${action}` }),
135
- });
136
- const fact = requestFact(heart, input.id);
137
- if (fact === null)
138
- throw new Error(`Akuma request ${input.id} was not admitted`);
139
- if (fact.requester !== soul.id || !sameRequestInput(fact, normalized)) {
140
- throw new Error(`Akuma request ${input.id} reused different input`);
141
- }
142
- return fact;
143
- }));
144
- }
145
- export async function reserveRequest(paths, id, child) {
146
- return await withHeart(paths, (heart) => transaction(heart, () => {
147
- const before = requestFact(heart, id);
148
- if (before === null)
149
- throw new Error(`unknown Akuma request ${id}`);
150
- if (before.action !== "akuma.call")
151
- throw new Error(`Akuma request ${id} cannot reserve a child`);
152
- if (before.state === "admitted")
153
- updateRequestReserved(heart, id, child);
154
- else if ((before.state !== "reserved" && before.state !== "served") || before.child !== child) {
155
- throw new Error(`Akuma request ${id} cannot reserve ${child}`);
156
- }
157
- return requestFact(heart, id);
158
- }));
159
- }
160
- export async function serveRequest(paths, id, child) {
161
- return await withHeart(paths, (heart) => transaction(heart, () => {
162
- const before = requestFact(heart, id);
163
- if (before === null)
164
- throw new Error(`unknown Akuma request ${id}`);
165
- if (before.action !== "akuma.call")
166
- throw new Error(`Akuma request ${id} cannot serve a child`);
167
- if (before.state === "reserved" && before.child === child)
168
- updateRequestServed(heart, id, child);
169
- else if (before.state !== "served" || before.child !== child) {
170
- throw new Error(`Akuma request ${id} cannot serve ${child}`);
171
- }
172
- return requestFact(heart, id);
173
- }));
174
- }
175
- export async function serveUpstreamRequest(paths, id, service) {
176
- return await withHeart(paths, (heart) => transaction(heart, () => {
177
- const before = requestFact(heart, id);
178
- if (before === null)
179
- throw new Error(`unknown Akuma request ${id}`);
180
- if (before.action === "akuma.call" || service.action !== before.action) {
181
- throw new Error(`Akuma request ${id} has a mismatched service reference`);
182
- }
183
- if (before.state === "admitted")
184
- updateUpstreamRequestServed(heart, id, service);
185
- else if (before.state !== "served" || JSON.stringify(before.service) !== JSON.stringify(service)) {
186
- throw new Error(`Akuma request ${id} cannot be served as ${service.action}`);
187
- }
188
- return requestFact(heart, id);
189
- }));
190
- }
191
- export async function refuseRequest(paths, id, diagnostic) {
192
- return await withHeart(paths, (heart) => transaction(heart, () => {
193
- const before = requestFact(heart, id);
194
- if (before === null)
195
- throw new Error(`unknown Akuma request ${id}`);
196
- if (before.state === "admitted")
197
- updateRequestRefused(heart, id, diagnostic);
198
- else if (before.state !== "refused" || before.diagnostic !== diagnostic) {
199
- throw new Error(`Akuma request ${id} cannot be refused`);
200
- }
201
- return requestFact(heart, id);
202
- }));
203
- }
204
- export async function voidRequest(paths, id, evidence) {
205
- return await withHeart(paths, (heart) => transaction(heart, () => {
206
- const before = requestFact(heart, id);
207
- if (before === null)
208
- throw new Error(`unknown Akuma request ${id}`);
209
- if (before.state === "admitted" || before.state === "reserved")
210
- updateRequestVoided(heart, id, evidence);
211
- else if (before.state !== "voided" || before.evidence !== evidence) {
212
- throw new Error(`Akuma request ${id} cannot be voided`);
213
- }
214
- return requestFact(heart, id);
215
- }));
216
- }
217
- export async function readRequest(paths, id) {
218
- return await withHeart(paths, (heart) => requestFact(heart, id));
219
- }
220
- export async function readNonterminalRequests(paths) {
221
- return await withHeart(paths, nonterminalRequestFacts);
222
- }
223
92
  export async function readKill(paths, bodySequence) {
224
93
  return await withHeart(paths, (heart) => killFactForBody(heart, bodySequence));
225
94
  }
@@ -0,0 +1,12 @@
1
+ import type { AkuId, AkumaPaths } from "../identity.js";
2
+ import type { RequestFact, RequestInput, UpstreamRequestService } from "./facts.js";
3
+ export declare function admitRequest(paths: AkumaPaths, input: RequestInput & Readonly<{
4
+ admittedAt: string;
5
+ }>): Promise<RequestFact>;
6
+ export declare function reserveRequest(paths: AkumaPaths, id: string, child: AkuId): Promise<RequestFact>;
7
+ export declare function serveRequest(paths: AkumaPaths, id: string, child: AkuId): Promise<RequestFact>;
8
+ export declare function serveUpstreamRequest(paths: AkumaPaths, id: string, service: UpstreamRequestService): Promise<RequestFact>;
9
+ export declare function refuseRequest(paths: AkumaPaths, id: string, diagnostic: string): Promise<RequestFact>;
10
+ export declare function voidRequest(paths: AkumaPaths, id: string, evidence: string): Promise<RequestFact>;
11
+ export declare function readRequest(paths: AkumaPaths, id: string): Promise<RequestFact | null>;
12
+ export declare function readNonterminalRequests(paths: AkumaPaths): Promise<readonly RequestFact[]>;