@astrosheep/keiyaku 2.8.5 → 2.9.0

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 (228) hide show
  1. package/README.md +35 -15
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/{call-terms_v2.js → call-terms.js} +12 -8
  4. package/build/agents/harness/control-types.js +1 -0
  5. package/build/agents/harness/execution-handle.js +23 -3
  6. package/build/agents/harness/index.js +6 -607
  7. package/build/agents/harness/outcome.js +141 -0
  8. package/build/agents/harness/projection.js +310 -0
  9. package/build/agents/harness/router.js +201 -0
  10. package/build/agents/harness/runtime.js +2 -1
  11. package/build/agents/{launch-snapshot_v2.js → launch-snapshot/model.js} +69 -74
  12. package/build/agents/launch-snapshot/resolve.js +72 -0
  13. package/build/agents/opencode-sdk.js +24 -3
  14. package/build/agents/providers/{claude-agent-sdk.js → claude-agent-sdk/adapter.js} +46 -368
  15. package/build/agents/providers/claude-agent-sdk/events.js +218 -0
  16. package/build/agents/providers/claude-agent-sdk/session.js +158 -0
  17. package/build/agents/providers/codex-app-server/adapter.js +347 -0
  18. package/build/agents/providers/codex-app-server/events.js +352 -0
  19. package/build/agents/providers/codex-app-server/session.js +266 -0
  20. package/build/agents/providers/codex-sdk.js +23 -5
  21. package/build/agents/providers/opencode-sdk/adapter.js +344 -0
  22. package/build/agents/providers/opencode-sdk/events.js +354 -0
  23. package/build/agents/providers/opencode-sdk/session.js +180 -0
  24. package/build/agents/providers/pi/adapter.js +269 -0
  25. package/build/agents/providers/pi/checkpoint.js +41 -0
  26. package/build/agents/providers/pi/events.js +282 -0
  27. package/build/agents/{selector_v2.js → selector.js} +8 -6
  28. package/build/cli/commands/akuma/akuma/handler.js +4 -0
  29. package/build/cli/commands/akuma/akuma/meta.js +16 -0
  30. package/build/cli/commands/akuma/catalog.js +8 -0
  31. package/build/cli/commands/akuma/list/handler.js +15 -0
  32. package/build/cli/commands/akuma/list/meta-ls.js +9 -0
  33. package/build/cli/commands/akuma/view/handler.js +34 -0
  34. package/build/cli/commands/akuma/view/meta.js +8 -0
  35. package/build/cli/commands/akuma.js +1 -1
  36. package/build/cli/commands/contract/amend/handler.js +10 -0
  37. package/build/cli/commands/contract/amend/meta.js +12 -0
  38. package/build/cli/commands/contract/arc/handler.js +9 -0
  39. package/build/cli/commands/contract/arc/meta.js +26 -0
  40. package/build/cli/commands/contract/bind/handler.js +23 -0
  41. package/build/cli/commands/contract/bind/meta.js +30 -0
  42. package/build/cli/commands/contract/catalog.js +16 -0
  43. package/build/cli/commands/contract/forfeit/handler.js +13 -0
  44. package/build/cli/commands/contract/forfeit/meta.js +12 -0
  45. package/build/cli/commands/contract/log/handler.js +12 -0
  46. package/build/cli/commands/contract/log/meta.js +12 -0
  47. package/build/cli/commands/contract/petition/handler.js +26 -0
  48. package/build/cli/commands/contract/petition/meta.js +23 -0
  49. package/build/cli/commands/contract/renew/handler.js +10 -0
  50. package/build/cli/commands/contract/renew/meta.js +17 -0
  51. package/build/cli/commands/projection/call/handler.js +39 -0
  52. package/build/cli/commands/projection/call/meta.js +9 -0
  53. package/build/cli/commands/projection/catalog.js +14 -0
  54. package/build/cli/commands/projection/kill/handler.js +35 -0
  55. package/build/cli/commands/projection/kill/meta.js +11 -0
  56. package/build/cli/commands/projection/revive/handler.js +53 -0
  57. package/build/cli/commands/projection/revive/meta.js +9 -0
  58. package/build/cli/commands/projection/status/handler.js +23 -0
  59. package/build/cli/commands/projection/status/meta.js +9 -0
  60. package/build/cli/commands/projection/tell/handler.js +34 -0
  61. package/build/cli/commands/projection/tell/meta.js +9 -0
  62. package/build/cli/commands/projection/wait/handler.js +21 -0
  63. package/build/cli/commands/projection/wait/meta.js +11 -0
  64. package/build/cli/commands/registry.js +100 -0
  65. package/build/cli/commands/shared.js +71 -0
  66. package/build/cli/commands/skills/catalog.js +6 -0
  67. package/build/cli/commands/skills/install/handler.js +10 -0
  68. package/build/cli/commands/skills/install/meta.js +9 -0
  69. package/build/cli/commands/skills/skills/handler.js +4 -0
  70. package/build/cli/commands/skills/skills/meta.js +10 -0
  71. package/build/cli/commands/system/catalog.js +8 -0
  72. package/build/cli/commands/system/completion/handler.js +10 -0
  73. package/build/cli/commands/system/completion/meta.js +9 -0
  74. package/build/cli/commands/system/dump-env/handler.js +3 -0
  75. package/build/cli/commands/system/dump-env/meta.js +10 -0
  76. package/build/cli/commands/system/guide/handler.js +4 -0
  77. package/build/cli/commands/system/guide/meta.js +9 -0
  78. package/build/cli/commands/task/add/handler.js +21 -0
  79. package/build/cli/commands/task/add/meta.js +8 -0
  80. package/build/cli/commands/task/catalog.js +22 -0
  81. package/build/cli/commands/task/doctor/handler.js +4 -0
  82. package/build/cli/commands/task/doctor/meta.js +8 -0
  83. package/build/cli/commands/task/done/handler.js +17 -0
  84. package/build/cli/commands/task/done/meta.js +8 -0
  85. package/build/cli/commands/task/drop/handler.js +6 -0
  86. package/build/cli/commands/task/drop/meta.js +8 -0
  87. package/build/cli/commands/task/ls/handler.js +13 -0
  88. package/build/cli/commands/task/ls/meta.js +8 -0
  89. package/build/cli/commands/task/shared.js +131 -0
  90. package/build/cli/commands/task/start/handler.js +6 -0
  91. package/build/cli/commands/task/start/meta.js +8 -0
  92. package/build/cli/commands/task/stop/handler.js +17 -0
  93. package/build/cli/commands/task/stop/meta.js +8 -0
  94. package/build/cli/commands/task/task/handler.js +5 -0
  95. package/build/cli/commands/task/task/meta.js +11 -0
  96. package/build/cli/commands/task/update/handler.js +18 -0
  97. package/build/cli/commands/task/update/meta.js +8 -0
  98. package/build/cli/commands/task/view/handler.js +14 -0
  99. package/build/cli/commands/task/view/meta.js +8 -0
  100. package/build/cli/completion.js +26 -27
  101. package/build/cli/flags.js +79 -2
  102. package/build/cli/help.js +27 -49
  103. package/build/cli/index.js +121 -374
  104. package/build/cli/output-stream-error-policy.js +17 -0
  105. package/build/cli/parse.js +159 -72
  106. package/build/cli/projection-address.js +5 -0
  107. package/build/cli/render/arc.js +1 -1
  108. package/build/cli/render/errors.js +4 -3
  109. package/build/cli/render/format.js +14 -0
  110. package/build/cli/render/kanshi.js +106 -0
  111. package/build/cli/render/petition.js +24 -1
  112. package/build/cli/render/projection-activity.js +5 -1
  113. package/build/cli/render/shared.js +2 -2
  114. package/build/cli/render/status.js +79 -13
  115. package/build/cli/render/task.js +113 -0
  116. package/build/cli/render/wait.js +23 -3
  117. package/build/cli/types.js +1 -1
  118. package/build/config/{akuma-loader_v2.js → akuma-loader.js} +2 -2
  119. package/build/config/env.js +9 -4
  120. package/build/config/settings/disease.js +26 -0
  121. package/build/config/settings/knobs.js +8 -0
  122. package/build/config/settings/loader.js +244 -0
  123. package/build/config/settings/schema.js +276 -0
  124. package/build/core/addressing.js +1 -1
  125. package/build/core/amend.js +3 -3
  126. package/build/core/arc.js +5 -4
  127. package/build/core/bind-workspace.js +123 -0
  128. package/build/core/bind.js +75 -155
  129. package/build/core/call/call.js +224 -0
  130. package/build/core/call/context.js +238 -0
  131. package/build/core/call/execution.js +217 -0
  132. package/build/core/call/prompt.js +42 -0
  133. package/build/core/call/reference.js +24 -0
  134. package/build/core/call-persist.js +1 -1
  135. package/build/core/claim-delivery.js +247 -0
  136. package/build/core/claim.js +39 -277
  137. package/build/core/context.js +2 -1
  138. package/build/core/{render.js → contract-view.js} +12 -140
  139. package/build/core/draft.js +107 -9
  140. package/build/core/entry.js +9 -1
  141. package/build/core/execution-coordinate.js +4 -20
  142. package/build/core/execution-pact.js +16 -31
  143. package/build/core/forfeit.js +35 -22
  144. package/build/core/hints.js +2 -2
  145. package/build/core/log.js +2 -2
  146. package/build/core/petition-claim-gates.js +8 -6
  147. package/build/core/petition-preview.js +97 -0
  148. package/build/core/petition-run.js +3 -2
  149. package/build/core/petition.js +39 -26
  150. package/build/core/projection/generation/database.js +306 -0
  151. package/build/core/projection/generation/identity.js +84 -0
  152. package/build/core/projection/generation/model.js +201 -0
  153. package/build/core/projection/generation/store.js +73 -0
  154. package/build/core/projection/generation/transitions.js +258 -0
  155. package/build/core/projection/heart.js +0 -307
  156. package/build/core/projection/tell/database.js +109 -0
  157. package/build/core/projection/tell/model.js +97 -0
  158. package/build/core/{projection-tells.js → projection/tell/store.js} +3 -158
  159. package/build/core/projection-activity.js +23 -2
  160. package/build/core/projection-core.js +2 -1
  161. package/build/core/projection-execution-observer.js +259 -0
  162. package/build/core/projection-generation-continuation.js +1 -1
  163. package/build/core/projection-generation-execution.js +90 -20
  164. package/build/core/projection-generation-launcher.js +1 -1
  165. package/build/core/projection-generation-runner.js +198 -4
  166. package/build/core/projection-identity.js +1 -1
  167. package/build/core/projection-kill.js +50 -17
  168. package/build/core/projection-life-observer.js +1 -1
  169. package/build/core/projection-life-protocol.js +10 -0
  170. package/build/core/projection-mint.js +1 -1
  171. package/build/core/projection-status.js +45 -32
  172. package/build/core/projection-wait.js +12 -2
  173. package/build/core/projection-wake.js +5 -2
  174. package/build/core/{queue_v2.js → queue.js} +2 -1
  175. package/build/core/registry.js +1 -1
  176. package/build/core/renew.js +7 -7
  177. package/build/core/response-artifact-catalog-sqlite.js +112 -0
  178. package/build/core/response-artifact-catalog.js +7 -0
  179. package/build/core/seal.js +44 -16
  180. package/build/core/status/board.js +286 -0
  181. package/build/core/status/drift.js +99 -0
  182. package/build/core/status/lifecycle.js +84 -0
  183. package/build/core/status/reconciliation.js +131 -0
  184. package/build/core/stored-agent-event.js +28 -4
  185. package/build/core/target-ref.js +10 -0
  186. package/build/core/task/board.js +175 -0
  187. package/build/core/task/commands.js +211 -0
  188. package/build/core/task/document.js +189 -0
  189. package/build/core/task/model.js +1 -0
  190. package/build/core/task/settlement-git.js +207 -0
  191. package/build/core/task/settlement-policy.js +82 -0
  192. package/build/core/task/source-board.js +84 -0
  193. package/build/core/task/source-model.js +1 -0
  194. package/build/core/task/validation.js +44 -0
  195. package/build/core/task-contract.js +224 -0
  196. package/build/core/task-doctor.js +14 -0
  197. package/build/core/task-git-actor.js +38 -0
  198. package/build/core/task-git-runtime.js +34 -0
  199. package/build/core/task-git-store.js +375 -0
  200. package/build/core/task-repository.js +1 -0
  201. package/build/core/task-store-repository.js +86 -0
  202. package/build/core/task-store.js +1 -0
  203. package/build/core/task-worktree.js +51 -0
  204. package/build/core/task.js +4 -0
  205. package/build/core/transcripts.js +138 -56
  206. package/build/core/validation.js +17 -0
  207. package/build/core/verdict.js +2 -2
  208. package/build/core/worktree-path.js +24 -6
  209. package/build/flow-error.js +3 -2
  210. package/build/git/commits.js +3 -0
  211. package/build/git/diff/parsers.js +0 -94
  212. package/build/index.js +2 -0
  213. package/build/keiyaku-constants.js +2 -0
  214. package/build/keiyaku.js +4 -3
  215. package/package.json +3 -2
  216. package/skills/keiyaku/SKILL.md +1 -1
  217. package/skills/keiyaku-akuma/SKILL.md +11 -6
  218. package/build/agents/harness/pump.js +0 -158
  219. package/build/agents/providers/codex-app-server.js +0 -864
  220. package/build/agents/providers/opencode-sdk.js +0 -363
  221. package/build/agents/providers/pi.js +0 -557
  222. package/build/config/settings.js +0 -533
  223. package/build/core/call.js +0 -686
  224. package/build/core/projection/generation.js +0 -412
  225. package/build/core/projection/identity.js +0 -81
  226. package/build/core/projection/mint.js +0 -174
  227. package/build/core/projection-generation-store.js +0 -707
  228. package/build/core/status.js +0 -539
@@ -1,100 +1,27 @@
1
1
  import * as crypto from "node:crypto";
2
- import * as fs from "node:fs/promises";
3
- import * as path from "node:path";
4
- import { assertSettingsKnobUsable, loadKeiyakuSettings } from "../config/settings.js";
2
+ import { assertSettingsKnobUsable } from "../config/settings/disease.js";
3
+ import { loadKeiyakuSettings } from "../config/settings/loader.js";
5
4
  import { FlowError } from "../flow-error.js";
6
- import { KEIYAKU_FILE } from "../keiyaku.js";
7
- import { createGit, wrapGitError } from "../git/core.js";
8
- import { activeLedgerRef, appendEntry, appendWithRetry, listContractIds, readLedger } from "./ledger.js";
5
+ import { activeLedgerRef, appendEntry, appendWithRetry, readLedger } from "./ledger.js";
9
6
  import { parseBindDraft } from "./draft.js";
10
7
  import { assertValidContractId, buildContractId } from "./ids.js";
11
- import { CONTRACT_PLACES } from "./places.js";
12
- import { renderContractView } from "./render.js";
13
8
  import { registerScope, releaseScope } from "./registry.js";
14
- import { deriveContractState, isTerminalState, normalizeTargetRef, resolveDefaultBranch } from "./status.js";
9
+ import { resolveDefaultBranch } from "./status/drift.js";
10
+ import { normalizeTargetRef } from "./target-ref.js";
11
+ import { reconcileTaskForfeit, settleTaskBoardForBind } from "./task-contract.js";
15
12
  import { bootstrapEntryDataFromObservation, isBootstrapSuccess, runWorktreeBootstrap, } from "./worktree-bootstrap.js";
16
- import { contractWorktreePathFromBind, findBindEntry } from "./worktree-path.js";
17
- function randomBytesDefault() {
18
- return [...crypto.randomBytes(10)];
19
- }
20
- async function getHead(cwd) {
21
- const git = createGit(cwd);
22
- try {
23
- return (await git.raw(["rev-parse", "HEAD"])).trim();
24
- }
25
- catch (error) {
26
- throw wrapGitError("rev-parse HEAD", error, cwd);
27
- }
28
- }
29
- async function createBranch(cwd, branch, base) {
30
- const git = createGit(cwd);
31
- try {
32
- await git.raw(["branch", branch, base]);
33
- }
34
- catch (error) {
35
- throw wrapGitError(`branch ${branch} ${base}`, error, cwd);
36
- }
37
- }
38
- async function defaultCreateWorktree(input) {
39
- const git = createGit(input.cwd);
40
- try {
41
- await git.raw(["worktree", "add", input.worktreePath, input.branch]);
42
- }
43
- catch (error) {
44
- throw wrapGitError(`worktree add ${input.worktreePath} ${input.branch}`, error, input.cwd);
45
- }
46
- }
47
- async function resolveTreeRoot(cwd, workspace, contractId, bind) {
48
- const repoRoot = await fs.realpath(cwd);
49
- if (workspace === "here")
50
- return repoRoot;
51
- return await contractWorktreePathFromBind(repoRoot, contractId, bind);
52
- }
53
- async function removeWorktree(cwd, worktreePath) {
54
- const git = createGit(cwd);
55
- try {
56
- await git.raw(["worktree", "remove", "--force", worktreePath]);
57
- }
58
- catch (error) {
59
- throw wrapGitError(`worktree remove ${worktreePath}`, error, cwd);
13
+ import { contractWorktreePathFromBind, stableRepoRoot } from "./worktree-path.js";
14
+ import { allocateWorktreePlace, cleanupCreatedBindResources, createContractBranch, defaultCreateWorktree, materializeContractView, resolveTreeRoot, } from "./bind-workspace.js";
15
+ class BindLedgerCollisionError extends Error {
16
+ contractId;
17
+ constructor(contractId) {
18
+ super(`contract ledger already exists for ${contractId}`);
19
+ this.contractId = contractId;
20
+ this.name = "BindLedgerCollisionError";
60
21
  }
61
22
  }
62
- async function deleteBranch(cwd, branch) {
63
- const git = createGit(cwd);
64
- try {
65
- await git.raw(["branch", "-D", branch]);
66
- }
67
- catch (error) {
68
- throw wrapGitError(`branch -D ${branch}`, error, cwd);
69
- }
70
- }
71
- async function cleanupCreatedResources(cwd, resources) {
72
- const failures = [];
73
- if (resources.keiyakuPath) {
74
- try {
75
- await fs.rm(resources.keiyakuPath, { force: true });
76
- }
77
- catch (error) {
78
- failures.push(`delete ${resources.keiyakuPath}: ${error instanceof Error ? error.message : String(error)}`);
79
- }
80
- }
81
- if (resources.worktreePath) {
82
- try {
83
- await removeWorktree(cwd, resources.worktreePath);
84
- }
85
- catch (error) {
86
- failures.push(`remove worktree: ${error instanceof Error ? error.message : String(error)}`);
87
- }
88
- }
89
- if (resources.branch) {
90
- try {
91
- await deleteBranch(cwd, resources.branch);
92
- }
93
- catch (error) {
94
- failures.push(`delete branch: ${error instanceof Error ? error.message : String(error)}`);
95
- }
96
- }
97
- return failures;
23
+ function randomBytesDefault() {
24
+ return [...crypto.randomBytes(10)];
98
25
  }
99
26
  async function appendBindFailedForfeit(cwd, id, actor, at) {
100
27
  const forfeit = {
@@ -108,11 +35,18 @@ async function appendBindFailedForfeit(cwd, id, actor, at) {
108
35
  by: actor,
109
36
  },
110
37
  };
111
- const ref = activeLedgerRef(id);
112
- const result = await appendWithRetry(cwd, ref, () => forfeit, { maxRetries: 1 });
113
- if (!result.ok) {
114
- throw new FlowError("INTERNAL_STATE", `BIND_FAILURE_FORFEIT_FAILED: failed to append bind-failed forfeit for ${id}: ${result.reason}`);
115
- }
38
+ const stableRoot = await stableRepoRoot(cwd);
39
+ await reconcileTaskForfeit({
40
+ cwd: stableRoot,
41
+ contractId: id,
42
+ publishLedger: async () => {
43
+ const result = await appendWithRetry(stableRoot, activeLedgerRef(id), () => forfeit, { maxRetries: 1 });
44
+ if (!result.ok) {
45
+ throw new FlowError("INTERNAL_STATE", `BIND_FAILURE_FORFEIT_FAILED: failed to append bind-failed forfeit for ${id}: ${result.reason}`);
46
+ }
47
+ return result;
48
+ },
49
+ });
116
50
  }
117
51
  async function appendBootstrapEntry(cwd, id, actor, at, data) {
118
52
  const entry = {
@@ -151,41 +85,6 @@ function resolvedBootstrapPlan(settings) {
151
85
  function bootstrapProgressLine(command) {
152
86
  return `bootstrap » ${command.join(" ")}`;
153
87
  }
154
- async function occupiedWorktreePlaces(cwd) {
155
- const occupied = new Set();
156
- for (const id of await listContractIds(cwd)) {
157
- const ledger = await readLedger(cwd, id);
158
- if (!ledger || isTerminalState(deriveContractState(ledger.entries)))
159
- continue;
160
- const place = findBindEntry(ledger.entries)?.data.place;
161
- if (place)
162
- occupied.add(place);
163
- }
164
- return occupied;
165
- }
166
- function randomIndex(randomBytes, size) {
167
- let value = 0;
168
- for (const byte of randomBytes) {
169
- value = (value * 256 + byte) % size;
170
- }
171
- return value;
172
- }
173
- function nextGenerationPlace(base, occupied) {
174
- for (let generation = 2;; generation += 1) {
175
- const candidate = `${base}${generation}`;
176
- if (!occupied.has(candidate))
177
- return candidate;
178
- }
179
- }
180
- async function allocateWorktreePlace(cwd, randomBytes) {
181
- const occupied = await occupiedWorktreePlaces(cwd);
182
- const candidates = CONTRACT_PLACES.filter((place) => !occupied.has(place));
183
- if (candidates.length > 0) {
184
- return candidates[randomIndex(randomBytes, candidates.length)];
185
- }
186
- const base = CONTRACT_PLACES[randomIndex(randomBytes, CONTRACT_PLACES.length)];
187
- return nextGenerationPlace(base, occupied);
188
- }
189
88
  function makeBindEntry(input) {
190
89
  return {
191
90
  v: 1,
@@ -197,6 +96,8 @@ function makeBindEntry(input) {
197
96
  name: input.draft.name,
198
97
  objective: input.draft.objective,
199
98
  scope: input.draft.scope,
99
+ checks: input.draft.checks,
100
+ extensions: input.draft.extensions,
200
101
  base: input.base,
201
102
  target: input.target,
202
103
  workspace: input.workspace,
@@ -210,7 +111,6 @@ export async function bindContract(input) {
210
111
  const draft = parseBindDraft(input.draft);
211
112
  const workspace = input.workspace ?? "here";
212
113
  const at = new Date(input.nowMs).toISOString();
213
- const base = await getHead(input.cwd);
214
114
  const defaultBranch = await resolveDefaultBranch(input.cwd);
215
115
  if (!defaultBranch) {
216
116
  throw new FlowError("INTERNAL_STATE", "cannot bind: no default branch");
@@ -232,30 +132,49 @@ export async function bindContract(input) {
232
132
  randomBytes,
233
133
  });
234
134
  await assertValidContractId(input.cwd, id);
235
- const entry = makeBindEntry({
236
- id,
237
- at,
238
- actor: input.actor,
239
- draft,
240
- base,
241
- target,
242
- after: input.after ?? [],
243
- workspace,
244
- place,
245
- bootstrap: bootstrapPlan,
246
- });
247
135
  const ref = activeLedgerRef(id);
248
- const appended = await appendEntry(input.cwd, ref, entry, null);
249
- if (!appended.ok) {
250
- collidedIds.push(id);
251
- continue;
136
+ let settlement;
137
+ try {
138
+ settlement = await settleTaskBoardForBind({
139
+ cwd: input.cwd,
140
+ ...(input.taskId === undefined ? {} : { taskId: input.taskId }),
141
+ contractId: id,
142
+ publishLedger: async (base) => {
143
+ const entry = makeBindEntry({
144
+ id,
145
+ at,
146
+ actor: input.actor,
147
+ draft,
148
+ base,
149
+ target,
150
+ after: input.after ?? [],
151
+ workspace,
152
+ place,
153
+ bootstrap: bootstrapPlan,
154
+ });
155
+ const appended = await appendEntry(input.cwd, ref, entry, null);
156
+ if (!appended.ok)
157
+ throw new BindLedgerCollisionError(id);
158
+ return { entry, appended };
159
+ },
160
+ });
252
161
  }
162
+ catch (error) {
163
+ if (error instanceof BindLedgerCollisionError) {
164
+ collidedIds.push(id);
165
+ continue;
166
+ }
167
+ throw error;
168
+ }
169
+ const { entry, appended } = settlement.ledger;
170
+ const base = settlement.boardCommit;
253
171
  const resources = { id };
254
172
  const branch = `keiyaku/${id}`;
255
173
  const worktreePath = await contractWorktreePathFromBind(input.cwd, id, entry);
256
174
  const targetRoot = workspace === "here" ? input.cwd : worktreePath;
257
175
  let ledgerHead = appended.head;
258
176
  let bootstrapRecorded = false;
177
+ let registryRegistered = false;
259
178
  try {
260
179
  const tree = await resolveTreeRoot(input.cwd, workspace, id, entry);
261
180
  const registration = await registerScope(input.cwd, {
@@ -266,9 +185,9 @@ export async function bindContract(input) {
266
185
  at,
267
186
  exclusive: input.exclusive,
268
187
  });
269
- resources.registry = true;
188
+ registryRegistered = true;
270
189
  if (workspace === "worktree") {
271
- await createBranch(input.cwd, branch, base);
190
+ await createContractBranch(input.cwd, branch, base);
272
191
  resources.branch = branch;
273
192
  await (input.createWorktree ?? defaultCreateWorktree)({
274
193
  cwd: input.cwd,
@@ -292,10 +211,11 @@ export async function bindContract(input) {
292
211
  const ledger = await readLedger(input.cwd, id);
293
212
  if (!ledger)
294
213
  throw new FlowError("INTERNAL_STATE", `LEDGER_MISSING: ledger missing immediately after bind for ${id}`);
295
- const keiyakuPath = path.join(targetRoot, KEIYAKU_FILE);
296
- await fs.mkdir(path.dirname(keiyakuPath), { recursive: true });
297
- await fs.writeFile(keiyakuPath, renderContractView({ id, entries: ledger.entries }), "utf8");
298
- resources.keiyakuPath = keiyakuPath;
214
+ resources.keiyakuPath = await materializeContractView({
215
+ targetRoot,
216
+ id,
217
+ entries: ledger.entries,
218
+ });
299
219
  return {
300
220
  id,
301
221
  ref,
@@ -314,7 +234,7 @@ export async function bindContract(input) {
314
234
  // Non-bootstrap failure: cleanup first remains compatible with prior path,
315
235
  // then forfeit. When bootstrap already recorded its failed fact, forfeit
316
236
  // must precede cleanup.
317
- cleanupFailures.push(...(await cleanupCreatedResources(input.cwd, resources)));
237
+ cleanupFailures.push(...(await cleanupCreatedBindResources(input.cwd, resources)));
318
238
  try {
319
239
  await appendBindFailedForfeit(input.cwd, id, input.actor, at);
320
240
  }
@@ -329,9 +249,9 @@ export async function bindContract(input) {
329
249
  catch (forfeitError) {
330
250
  cleanupFailures.push(forfeitError instanceof Error ? forfeitError.message : String(forfeitError));
331
251
  }
332
- cleanupFailures.push(...(await cleanupCreatedResources(input.cwd, resources)));
252
+ cleanupFailures.push(...(await cleanupCreatedBindResources(input.cwd, resources)));
333
253
  }
334
- if (resources.registry) {
254
+ if (registryRegistered) {
335
255
  try {
336
256
  await releaseScope(input.cwd, { contractId: id, actor: input.actor, at });
337
257
  }
@@ -0,0 +1,224 @@
1
+ import * as fs from "fs/promises";
2
+ import { selectSubagent } from "../../agents/selector.js";
3
+ import { applySnapshotEffort } from "../../agents/launch-snapshot/model.js";
4
+ import { resolveAkumaLaunchSnapshot } from "../../agents/launch-snapshot/resolve.js";
5
+ import { FlowError, requireText } from "../../flow-error.js";
6
+ import { isGitRepo } from "../../git/branches.js";
7
+ import { ACTOR_IDENTITY } from "../../keiyaku.js";
8
+ import { appendDebugBlock } from "../../telemetry/debug-log.js";
9
+ import { logInfo } from "../../telemetry/logger.js";
10
+ import { withResolvedAddress } from "../addressing.js";
11
+ import { assertProjectionAlias } from "../projection-alias.js";
12
+ import { PROJECTION_ADOPTION_TIMEOUT_MS } from "../projection-generation-launcher.js";
13
+ import { observeProjectionLife } from "../projection-life-observer.js";
14
+ import { waitForProjection } from "../projection-wait.js";
15
+ import { requireResumableArtifact } from "../transcripts.js";
16
+ import { stableRepoRoot } from "../worktree-path.js";
17
+ import { readCallKeiyakuContext } from "./context.js";
18
+ import { executeSubagent, launchSubagentGeneration, } from "./execution.js";
19
+ import { buildCallPrompt } from "./prompt.js";
20
+ import { collectCallReferenceFiles } from "./reference.js";
21
+ function renderSubagentWarning(warning) {
22
+ if (warning.reason === "retry") {
23
+ const status = warning.errorStatus === null ? "no response" : String(warning.errorStatus);
24
+ return `Provider warning: retry. attempt=${warning.attempt}, maxRetries=${warning.maxRetries}, retryDelayMs=${warning.retryDelayMs}, errorStatus=${status}`;
25
+ }
26
+ const modelTransition = warning.originalModel && warning.fallbackModel
27
+ ? `${warning.originalModel} -> ${warning.fallbackModel}`
28
+ : warning.originalModel ?? warning.fallbackModel;
29
+ const details = [
30
+ modelTransition,
31
+ warning.direction ? `direction=${warning.direction}` : undefined,
32
+ warning.requestId !== undefined ? `requestId=${warning.requestId ?? "null"}` : undefined,
33
+ warning.signerModel ? `signerModel=${warning.signerModel}` : undefined,
34
+ ].filter((value) => Boolean(value));
35
+ const summary = warning.reason === "refusal" ? "Provider warning: model fallback." : "Provider warning: overload fallback.";
36
+ return details.length > 0 ? `${summary} ${details.join(", ")}` : summary;
37
+ }
38
+ export async function execSubagent(agentName, prompt, cwd, options) {
39
+ const { outcome } = await executeSubagent(agentName, prompt, cwd, options);
40
+ return outcome;
41
+ }
42
+ export async function runCall(input) {
43
+ const { signal, agent } = input;
44
+ const reviveArtifact = input.reviveArtifact ? requireResumableArtifact(input.reviveArtifact) : undefined;
45
+ let rawCwd = input.cwd;
46
+ let rawRepo = input.repo;
47
+ if (reviveArtifact?.provenance.authority.kind === "user") {
48
+ if (!("workdir" in reviveArtifact.provenance.evidence)) {
49
+ throw new FlowError("INVALID_RESPONSE_PATH", `Invalid response artifact '${reviveArtifact.artifactId}': field 'provenance.evidence.workdir' is missing.`);
50
+ }
51
+ rawCwd = reviveArtifact.provenance.evidence.workdir;
52
+ rawRepo = undefined;
53
+ }
54
+ else if (reviveArtifact?.provenance.authority.kind === "repository") {
55
+ const artifactHome = await fs.realpath(reviveArtifact.storageRoot);
56
+ const addressed = input.repo ?? input.cwd;
57
+ if (await isGitRepo(addressed)) {
58
+ const addressedRoot = await stableRepoRoot(addressed);
59
+ if (addressedRoot !== artifactHome) {
60
+ throw new FlowError("INVALID_RESPONSE_PATH", `Response artifact '${reviveArtifact.artifactId}' belongs to ${artifactHome}, but the addressed repository is ${addressedRoot}.`);
61
+ }
62
+ }
63
+ rawCwd = artifactHome;
64
+ rawRepo = artifactHome;
65
+ }
66
+ const cwd = await fs.realpath(rawCwd);
67
+ const repoCwd = rawRepo ? await fs.realpath(rawRepo) : undefined;
68
+ const title = requireText("title", input.title);
69
+ const task = requireText("task", input.task);
70
+ if (input.incognito && reviveArtifact) {
71
+ throw new FlowError("EMPTY_PARAM", "Parameter 'incognito' cannot be used with 'reviveArtifact' — incognito calls cannot be revived or persist conversation history.");
72
+ }
73
+ const keiyakuContext = await readCallKeiyakuContext(cwd, {
74
+ bare: input.bare,
75
+ contractId: input.contractId,
76
+ contractAddressSource: input.contractAddressSource,
77
+ mode: input.mode ?? "call",
78
+ repoCwd,
79
+ reviveBinding: reviveArtifact?.provenance.binding,
80
+ });
81
+ return await withResolvedAddress(keiyakuContext.address, async () => {
82
+ const executionCwd = keiyakuContext.effectiveCwd;
83
+ const referenceFiles = await collectCallReferenceFiles(executionCwd, keiyakuContext);
84
+ const prompt = buildCallPrompt({
85
+ task,
86
+ context: input.context,
87
+ referenceFiles,
88
+ currentIntent: keiyakuContext.currentIntent,
89
+ });
90
+ const launchSnapshot = reviveArtifact
91
+ ? input.effort !== undefined
92
+ ? applySnapshotEffort(reviveArtifact.launchSnapshot, input.effort)
93
+ : reviveArtifact.launchSnapshot
94
+ : resolveAkumaLaunchSnapshot(await selectSubagent(agent, keiyakuContext.profileCwd), {
95
+ executionCwd,
96
+ projectRoot: keiyakuContext.profileCwd,
97
+ ...(input.model !== undefined ? { model: input.model } : {}),
98
+ ...(input.effort !== undefined ? { effort: input.effort } : {}),
99
+ });
100
+ const continuation = reviveArtifact
101
+ ? {
102
+ session: reviveArtifact.session,
103
+ resumePath: reviveArtifact.absolutePath,
104
+ sourceArtifactId: reviveArtifact.artifactId,
105
+ }
106
+ : undefined;
107
+ logInfo(`[${ACTOR_IDENTITY}] Running execution for '${launchSnapshot.akuma.name}' in ${executionCwd}`, {
108
+ cwd: executionCwd,
109
+ section: "script",
110
+ progressOnly: true,
111
+ });
112
+ // Storage coordinate: addressed repository stable root / caller cwd.
113
+ // Evidence coordinate: commission worktree for bound runs; execution cwd otherwise.
114
+ const historyStorageCwd = keiyakuContext.historyCwd;
115
+ const evidenceCwd = executionCwd;
116
+ const executionOptions = {
117
+ signal,
118
+ continuation,
119
+ task,
120
+ launchSnapshot,
121
+ persistProjectionResponse: !input.incognito,
122
+ historyStorageCwd,
123
+ evidenceCwd,
124
+ projectionCwd: keiyakuContext.projectionCwd,
125
+ authority: keiyakuContext.address.repo.kind === "repository"
126
+ ? { kind: "repository" }
127
+ : { kind: "user" },
128
+ binding: keiyakuContext.address.binding,
129
+ title,
130
+ context: input.context,
131
+ ...(input.mode === "call" && input.alias !== undefined ? { alias: assertProjectionAlias(input.alias) } : {}),
132
+ };
133
+ if ((input.mode ?? "call") === "call") {
134
+ const launched = await launchSubagentGeneration(launchSnapshot.akuma.name, prompt, executionCwd, executionOptions);
135
+ const adoptedProjection = {
136
+ id: launched.projection.id,
137
+ phase: observeProjectionLife(launched.projection.dir, {
138
+ nowMs: Date.now(),
139
+ startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
140
+ }).phase,
141
+ ...(launched.projection.alias ? { alias: launched.projection.alias } : {}),
142
+ ...(launched.projection.previousAliasTarget ? { previousAliasTarget: launched.projection.previousAliasTarget } : {}),
143
+ };
144
+ input.onLaunch?.({ address: keiyakuContext.address, projection: adoptedProjection });
145
+ if (input.detach) {
146
+ return {
147
+ state: "launched",
148
+ summary: "",
149
+ agent: launchSnapshot.akuma.name,
150
+ launchSnapshot,
151
+ projectionOwnsResponsePersistence: true,
152
+ projection: adoptedProjection,
153
+ address: keiyakuContext.address,
154
+ };
155
+ }
156
+ const joined = await waitForProjection({
157
+ projectionDirectory: launched.projection.dir,
158
+ signal,
159
+ });
160
+ if (joined.state !== "terminal") {
161
+ throw new FlowError("INTERNAL_STATE", "foreground call wait ended without a terminal projection verdict");
162
+ }
163
+ return {
164
+ state: "completed",
165
+ summary: joined.outcomeText || `${ACTOR_IDENTITY} completed successfully.`,
166
+ agent: launchSnapshot.akuma.name,
167
+ launchSnapshot,
168
+ ...(joined.snapshot.responsePath ? { responsePath: joined.snapshot.responsePath } : {}),
169
+ ...(joined.snapshot.artifactId ? { artifactId: joined.snapshot.artifactId } : {}),
170
+ projectionOwnsResponsePersistence: true,
171
+ meta: { effectivePolicy: launchSnapshot.akuma.policy },
172
+ projection: { id: launched.projection.id, phase: joined.snapshot.phase },
173
+ foregroundWait: joined,
174
+ address: keiyakuContext.address,
175
+ };
176
+ }
177
+ const execution = await executeSubagent(launchSnapshot.akuma.name, prompt, executionCwd, executionOptions);
178
+ const subagentOutcome = execution.outcome;
179
+ if (subagentOutcome.diagnostics.trim()) {
180
+ appendDebugBlock("subagent diagnostics", subagentOutcome.diagnostics, { cwd: executionCwd, section: "subagent-stderr" });
181
+ }
182
+ let projection;
183
+ let responsePath;
184
+ let artifactId;
185
+ if (execution.projection) {
186
+ responsePath = typeof execution.completionFacts?.responsePath === "string"
187
+ ? execution.completionFacts.responsePath
188
+ : undefined;
189
+ artifactId = typeof execution.completionFacts?.artifactId === "string"
190
+ ? execution.completionFacts.artifactId
191
+ : undefined;
192
+ try {
193
+ projection = {
194
+ id: execution.projection.id,
195
+ phase: observeProjectionLife(execution.projection.dir, {
196
+ nowMs: Date.now(),
197
+ startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS,
198
+ }).phase,
199
+ };
200
+ }
201
+ catch {
202
+ // Projection observations are best-effort and must not reverse success.
203
+ }
204
+ }
205
+ return {
206
+ state: "completed",
207
+ summary: subagentOutcome.finalMessage || `${ACTOR_IDENTITY} completed successfully.`,
208
+ agent: launchSnapshot.akuma.name,
209
+ session: subagentOutcome.session,
210
+ launchSnapshot: subagentOutcome.launchSnapshot,
211
+ ...(responsePath ? { responsePath } : {}),
212
+ ...(artifactId ? { artifactId } : {}),
213
+ ...(reviveArtifact ? { revivedArtifactId: reviveArtifact.artifactId } : {}),
214
+ projectionOwnsResponsePersistence: execution.projection !== undefined,
215
+ meta: {
216
+ ...(subagentOutcome.warnings.length > 0 ? { warnings: subagentOutcome.warnings.map(renderSubagentWarning) } : {}),
217
+ effectivePolicy: subagentOutcome.launchSnapshot.akuma.policy,
218
+ },
219
+ ...(projection ? { projection } : {}),
220
+ address: keiyakuContext.address,
221
+ ...(input.incognito ? {} : { evidenceCwd }),
222
+ };
223
+ });
224
+ }