@astrosheep/keiyaku 2.9.2 → 2.9.4

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 (238) hide show
  1. package/build/.tsbuildinfo +1 -1
  2. package/build/agents/call-terms.js +13 -94
  3. package/build/agents/harness/activity-values.js +16 -0
  4. package/build/agents/harness/event-persistence.js +218 -0
  5. package/build/agents/harness/execution-handle.js +25 -222
  6. package/build/agents/harness/index.js +3 -1
  7. package/build/agents/harness/outcome.js +47 -4
  8. package/build/agents/harness/projection.js +42 -59
  9. package/build/agents/harness/provider-adapter.js +58 -0
  10. package/build/agents/harness/router.js +11 -187
  11. package/build/agents/harness/runtime.js +17 -4
  12. package/build/agents/harness/session.js +15 -0
  13. package/build/agents/launch-snapshot/model.js +15 -236
  14. package/build/agents/launch-snapshot/parse-helpers.js +114 -0
  15. package/build/agents/launch-snapshot/resolve.js +17 -43
  16. package/build/agents/opencode-sdk.js +45 -12
  17. package/build/agents/providers/claude-agent-sdk/adapter.js +6 -17
  18. package/build/agents/providers/claude-agent-sdk/registration.js +214 -0
  19. package/build/agents/providers/claude-agent-sdk/session-schema.js +2 -0
  20. package/build/agents/providers/codex-app-server/adapter.js +50 -53
  21. package/build/agents/providers/codex-app-server/events.js +67 -11
  22. package/build/agents/providers/codex-app-server/identity.js +1 -0
  23. package/build/agents/providers/codex-app-server/policy.js +29 -0
  24. package/build/agents/providers/codex-app-server/registration.js +248 -0
  25. package/build/agents/providers/codex-app-server/session-schema.js +6 -0
  26. package/build/agents/providers/codex-app-server/session.js +23 -17
  27. package/build/agents/providers/codex-app-server/terms.js +1 -0
  28. package/build/agents/providers/opencode-sdk/adapter.js +94 -50
  29. package/build/agents/providers/opencode-sdk/events.js +8 -4
  30. package/build/agents/providers/opencode-sdk/registration.js +135 -0
  31. package/build/agents/providers/opencode-sdk/session-schema.js +2 -0
  32. package/build/agents/providers/opencode-sdk/session.js +21 -14
  33. package/build/agents/providers/opencode-sdk/terms.js +1 -0
  34. package/build/agents/providers/pi/adapter.js +36 -30
  35. package/build/agents/providers/pi/registration.js +145 -0
  36. package/build/agents/providers/pi/session-schema.js +6 -0
  37. package/build/agents/providers/pi/terms.js +1 -0
  38. package/build/agents/providers/registration.js +7 -0
  39. package/build/agents/providers/registry.js +76 -0
  40. package/build/agents/selector.js +2 -29
  41. package/build/agents/types.js +8 -0
  42. package/build/artifact-directories.js +22 -0
  43. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  44. package/build/cli/commands/akuma/view/handler.js +5 -4
  45. package/build/cli/commands/akuma.js +6 -0
  46. package/build/cli/commands/contract/amend/handler.js +8 -2
  47. package/build/cli/commands/contract/arc/handler.js +7 -1
  48. package/build/cli/commands/contract/bind/handler.js +4 -3
  49. package/build/cli/commands/contract/bind/meta.js +34 -7
  50. package/build/cli/commands/contract/forfeit/handler.js +4 -4
  51. package/build/cli/commands/contract/log/handler.js +3 -3
  52. package/build/cli/commands/contract/petition/handler.js +4 -6
  53. package/build/cli/commands/contract/renew/handler.js +20 -6
  54. package/build/cli/commands/contract/renew/meta.js +5 -4
  55. package/build/cli/commands/metadata.js +58 -0
  56. package/build/cli/commands/projection/call/handler.js +6 -5
  57. package/build/cli/commands/projection/kill/handler.js +16 -32
  58. package/build/cli/commands/projection/shared.js +28 -0
  59. package/build/cli/commands/projection/status/handler.js +1 -1
  60. package/build/cli/commands/projection/tell/handler.js +14 -30
  61. package/build/cli/commands/projection/wait/handler.js +45 -22
  62. package/build/cli/commands/projection/wait/meta.js +7 -2
  63. package/build/cli/commands/registry.js +2 -54
  64. package/build/cli/commands/shared.js +10 -7
  65. package/build/cli/commands/skills/install/handler.js +2 -5
  66. package/build/cli/commands/skills/skills/handler.js +1 -1
  67. package/build/cli/commands/task/add/handler.js +5 -13
  68. package/build/cli/commands/task/catalog.js +2 -2
  69. package/build/cli/commands/task/done/handler.js +4 -8
  70. package/build/cli/commands/task/drop/handler.js +3 -6
  71. package/build/cli/commands/task/ls/handler.js +3 -5
  72. package/build/cli/commands/task/shared.js +14 -4
  73. package/build/cli/commands/task/{view → show}/handler.js +7 -8
  74. package/build/cli/commands/task/show/meta.js +8 -0
  75. package/build/cli/commands/task/start/handler.js +3 -6
  76. package/build/cli/commands/task/stop/handler.js +4 -8
  77. package/build/cli/commands/task/task/handler.js +1 -1
  78. package/build/cli/commands/task/update/handler.js +4 -8
  79. package/build/cli/completion.js +1 -1
  80. package/build/cli/flags.js +19 -3
  81. package/build/cli/help.js +4 -11
  82. package/build/cli/index.js +57 -83
  83. package/build/cli/missing-subcommand.js +7 -0
  84. package/build/cli/parse-flags.js +14 -5
  85. package/build/cli/parse-metadata.js +3 -3
  86. package/build/cli/parse-selectors.js +1 -1
  87. package/build/cli/parse.js +21 -0
  88. package/build/cli/projection-address.js +7 -12
  89. package/build/cli/render/address.js +41 -37
  90. package/build/cli/render/arc.js +24 -12
  91. package/build/cli/render/call.js +11 -5
  92. package/build/cli/render/errors.js +36 -1
  93. package/build/cli/render/format.js +4 -0
  94. package/build/cli/render/kanshi.js +6 -12
  95. package/build/cli/render/kill.js +9 -0
  96. package/build/cli/render/misc.js +55 -12
  97. package/build/cli/render/petition.js +20 -15
  98. package/build/cli/render/projection-activity.js +3 -0
  99. package/build/cli/render/shared.js +60 -21
  100. package/build/cli/render/skills.js +10 -0
  101. package/build/cli/render/status.js +37 -14
  102. package/build/cli/render/success-response.js +77 -0
  103. package/build/cli/render/task.js +54 -14
  104. package/build/cli/render/tell.js +5 -0
  105. package/build/cli/render/terminal-failure.js +58 -0
  106. package/build/cli/render/types.js +1 -0
  107. package/build/cli/render/wait.js +38 -36
  108. package/build/cli/types.js +1 -1
  109. package/build/config/akuma-loader.js +0 -5
  110. package/build/config/env-keys.js +1 -1
  111. package/build/config/provider-policy-ownership.js +3 -32
  112. package/build/config/settings/disease.js +8 -1
  113. package/build/config/settings/loader.js +11 -3
  114. package/build/config/settings/provider-instance-fields.js +51 -0
  115. package/build/config/settings/schema.js +6 -176
  116. package/build/core/addressing.js +66 -16
  117. package/build/core/amend.js +135 -18
  118. package/build/core/arc.js +19 -67
  119. package/build/core/bind.js +82 -35
  120. package/build/core/call/call.js +15 -9
  121. package/build/core/call/context.js +6 -51
  122. package/build/core/call/execution.js +21 -22
  123. package/build/core/call/prompt.js +14 -1
  124. package/build/core/command-markdown.js +45 -0
  125. package/build/core/context.js +13 -7
  126. package/build/core/contract-view.js +20 -3
  127. package/build/core/draft.js +123 -16
  128. package/build/core/forfeit.js +5 -2
  129. package/build/core/hints.js +1 -75
  130. package/build/core/identifier-slug.js +66 -0
  131. package/build/core/ids.js +25 -11
  132. package/build/core/ledger.js +20 -68
  133. package/build/core/markdown/parser.js +0 -68
  134. package/build/core/markdown/render.js +68 -0
  135. package/build/core/markdown/sections.js +2 -1
  136. package/build/core/outcome-base.js +4 -0
  137. package/build/core/projection/generation/database.js +2 -38
  138. package/build/core/projection/generation/model.js +42 -1
  139. package/build/core/{projection-generation-continuation.js → projection/generation/projection-generation-continuation.js} +3 -3
  140. package/build/core/{projection-generation-execution.js → projection/generation/projection-generation-execution.js} +19 -49
  141. package/build/core/{projection-generation-identity.js → projection/generation/projection-generation-identity.js} +2 -2
  142. package/build/core/{projection-generation-launcher.js → projection/generation/projection-generation-launcher.js} +5 -3
  143. package/build/core/{projection-generation-process.js → projection/generation/projection-generation-process.js} +2 -1
  144. package/build/core/{projection-generation-runner.js → projection/generation/projection-generation-runner.js} +18 -3
  145. package/build/core/projection/generation/store.js +2 -2
  146. package/build/core/projection/generation/transitions.js +1 -1
  147. package/build/core/projection/index.js +27 -0
  148. package/build/core/{projection-activity.js → projection/projection-activity.js} +12 -0
  149. package/build/core/{projection-alias.js → projection/projection-alias.js} +14 -19
  150. package/build/core/projection/projection-coordinate.js +61 -0
  151. package/build/core/{projection-core.js → projection/projection-core.js} +9 -9
  152. package/build/core/{projection-execution-observer.js → projection/projection-execution-observer.js} +2 -57
  153. package/build/core/{projection-identity.js → projection/projection-identity.js} +1 -1
  154. package/build/core/{projection-kill.js → projection/projection-kill.js} +1 -1
  155. package/build/core/{projection-life-observer.js → projection/projection-life-observer.js} +1 -1
  156. package/build/core/{projection-mint.js → projection/projection-mint.js} +10 -10
  157. package/build/core/{projection-status.js → projection/projection-status.js} +98 -25
  158. package/build/core/projection/projection-terminal-failure.js +104 -0
  159. package/build/core/projection/projection-wait.js +278 -0
  160. package/build/core/projection/projection-wake.js +203 -0
  161. package/build/core/provider-loader-ports.js +4 -0
  162. package/build/core/ref-log.js +65 -40
  163. package/build/core/registry.js +63 -10
  164. package/build/core/renew-session.js +128 -0
  165. package/build/core/renew.js +426 -99
  166. package/build/core/scope.js +143 -66
  167. package/build/core/seal.js +139 -35
  168. package/build/core/{claim-delivery.js → settlement/claim-delivery.js} +9 -14
  169. package/build/core/{claim.js → settlement/claim.js} +32 -8
  170. package/build/core/settlement/index.js +19 -0
  171. package/build/core/{petition-claim-gates.js → settlement/petition-claim-gates.js} +22 -18
  172. package/build/core/{petition-forfeit.js → settlement/petition-forfeit.js} +2 -2
  173. package/build/core/{petition-head-guard.js → settlement/petition-head-guard.js} +2 -2
  174. package/build/core/settlement/petition-io.js +1 -0
  175. package/build/core/{petition-preview.js → settlement/petition-preview.js} +1 -1
  176. package/build/core/{petition.js → settlement/petition.js} +12 -10
  177. package/build/core/settlement/queue-read-model.js +143 -0
  178. package/build/core/settlement/queue-seat-allocation.js +156 -0
  179. package/build/core/settlement/queue.js +6 -0
  180. package/build/core/settlement/settlement.js +295 -0
  181. package/build/core/{verdict.js → settlement/verdict.js} +9 -37
  182. package/build/core/status/board.js +24 -23
  183. package/build/core/status/ledger-batch.js +158 -0
  184. package/build/core/status/reconciliation.js +10 -4
  185. package/build/core/stored-agent-event.js +3 -0
  186. package/build/core/task/board.js +40 -4
  187. package/build/core/task/commands.js +30 -21
  188. package/build/core/task/index.js +12 -0
  189. package/build/core/task/settlement-git.js +157 -65
  190. package/build/core/task/settlement-policy.js +54 -24
  191. package/build/core/task/task-contract.js +370 -0
  192. package/build/core/{task-doctor.js → task/task-doctor.js} +1 -1
  193. package/build/core/{task-git-actor.js → task/task-git-actor.js} +5 -10
  194. package/build/core/{task-git-runtime.js → task/task-git-runtime.js} +3 -3
  195. package/build/core/{task-git-store.js → task/task-git-store.js} +92 -29
  196. package/build/core/task/task-repository.js +1 -0
  197. package/build/core/{task-store-repository.js → task/task-store-repository.js} +5 -4
  198. package/build/core/task/task-store.js +1 -0
  199. package/build/core/{task-worktree.js → task/task-worktree.js} +2 -1
  200. package/build/core/task/task.js +5 -0
  201. package/build/core/task/tree.js +37 -0
  202. package/build/core/task/validation.js +5 -2
  203. package/build/core/transcripts.js +15 -3
  204. package/build/core/worktree-path.js +43 -28
  205. package/build/flow-error.js +85 -39
  206. package/build/fs/durability.js +27 -0
  207. package/build/fs/path-coordinate.js +16 -0
  208. package/build/generated/version.js +3 -2
  209. package/build/git/branches.js +2 -1
  210. package/build/git/core.js +106 -2
  211. package/build/git/path-coordinate.js +17 -0
  212. package/build/git/refs.js +57 -0
  213. package/build/git/streaming-batch.js +301 -0
  214. package/build/git/worktree.js +65 -0
  215. package/build/index.js +3 -3
  216. package/build/keiyaku.js +2 -36
  217. package/package.json +4 -10
  218. package/skills/keiyaku/SKILL.md +3 -2
  219. package/skills/keiyaku-akuma/SKILL.md +10 -6
  220. package/skills/keiyaku-task/SKILL.md +1 -1
  221. package/skills/keiyaku-workflow/SKILL.md +25 -4
  222. package/build/agents/providers/codex-sdk.js +0 -329
  223. package/build/cli/commands/task/view/meta.js +0 -8
  224. package/build/core/command-io.js +0 -174
  225. package/build/core/path-coordinate.js +0 -27
  226. package/build/core/petition-claim.js +0 -117
  227. package/build/core/petition-run.js +0 -83
  228. package/build/core/projection-coordinate.js +0 -63
  229. package/build/core/projection-wait.js +0 -165
  230. package/build/core/projection-wake.js +0 -136
  231. package/build/core/queue.js +0 -343
  232. package/build/core/task-contract.js +0 -237
  233. package/build/core/task.js +0 -4
  234. /package/build/{core/task-repository.js → agents/launch-snapshot/types.js} +0 -0
  235. /package/build/{core/task-store.js → agents/providers/claude-agent-sdk/terms.js} +0 -0
  236. /package/build/core/{projection-generation-runtime.js → projection/generation/projection-generation-runtime.js} +0 -0
  237. /package/build/core/{projection-life-protocol.js → projection/projection-life-protocol.js} +0 -0
  238. /package/build/core/{projection-runner-lock.js → projection/projection-runner-lock.js} +0 -0
package/build/core/arc.js CHANGED
@@ -2,63 +2,18 @@ import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
3
  import { requireText, FlowError } from "../flow-error.js";
4
4
  import { getCurrentBranch, requireGitRepository } from "../git/branches.js";
5
- import { DIRTY_FILE_CATEGORY, getDirtyFiles, renderDirtyFileStatusLine } from "../git/worktree.js";
5
+ import { assertCleanTrackedWorkingTree } from "../git/worktree.js";
6
6
  import { KEIYAKU_FILE, ACTOR_IDENTITY } from "../keiyaku.js";
7
- import { buildDirtyWorktreeArtifactHint } from "./hints.js";
8
7
  import { normalizeInlineTitle } from "./markdown/titles.js";
9
- import { parseCommandMarkdown, requireMarkdownTextSection, requireMarkdownTitle, } from "./command-io.js";
8
+ import { parseCommandMarkdown, requireMarkdownTextSection, requireMarkdownTitle, } from "./command-markdown.js";
10
9
  import { resolveContractAddress, withResolvedAddress } from "./addressing.js";
11
10
  import { activeLedgerRef, appendWithRetry, readLedger } from "./ledger.js";
12
- import { renderContractView } from "./contract-view.js";
11
+ import { refreshContractView } from "./contract-view.js";
13
12
  import { findOpenArc, getMaxArc, sealCurrentArc } from "./seal.js";
14
13
  import { deriveContractState, isTerminalState } from "./status/lifecycle.js";
15
14
  import { resolveDefaultBranch } from "./status/drift.js";
16
15
  import { persistResponseHistory } from "./transcripts.js";
17
- const DIRTY_WORKTREE_LIST_LIMIT = 10;
18
- const KEIYAKU_ARTIFACT_DIR_PREFIX = ".keiyaku/";
19
- function normalizePathForDirtyMatch(cwd, filePath) {
20
- const normalizedPath = filePath.trim();
21
- if (!normalizedPath)
22
- return "";
23
- const repoRelativePath = path.isAbsolute(normalizedPath) ? path.relative(cwd, normalizedPath) : normalizedPath;
24
- const platformNormalizedPath = path.normalize(repoRelativePath.replace(/[\\/]+/g, path.sep));
25
- return platformNormalizedPath.split(path.sep).join("/").replace(/^\.\/+/, "").replace(/^\/+/, "");
26
- }
27
- function pathSegments(normalizedPath) {
28
- return normalizedPath.split("/").filter((segment) => segment.length > 0 && segment !== ".");
29
- }
30
- function matchesIgnorePattern(filePath, ignorePattern) {
31
- const patternSegments = pathSegments(ignorePattern);
32
- const fileSegments = pathSegments(filePath);
33
- return patternSegments.length > 0
34
- && fileSegments.length >= patternSegments.length
35
- && patternSegments.every((segment, index) => fileSegments[index] === segment);
36
- }
37
- export function filterDirtyFilesByAllowlist(cwd, dirtyFiles, ignorePatterns) {
38
- const ignoredPaths = Array.from(new Set((ignorePatterns ?? []).map((pattern) => normalizePathForDirtyMatch(cwd, pattern)).filter(Boolean)));
39
- return {
40
- dirtyFiles: dirtyFiles.filter((file) => {
41
- const normalizedFilePath = normalizePathForDirtyMatch(cwd, file.path);
42
- return !ignoredPaths.some((ignoredPath) => matchesIgnorePattern(normalizedFilePath, ignoredPath));
43
- }),
44
- ignoredPaths,
45
- };
46
- }
47
- export function filterBlockingDirtyFiles(dirtyFiles) {
48
- return dirtyFiles.filter((file) => file.category !== DIRTY_FILE_CATEGORY.untracked);
49
- }
50
- export async function assertCleanWorkingTree(cwd, ignorePatterns) {
51
- const blockingDirtyFiles = filterBlockingDirtyFiles(await getDirtyFiles(cwd));
52
- const { dirtyFiles } = filterDirtyFilesByAllowlist(cwd, blockingDirtyFiles, ignorePatterns);
53
- if (dirtyFiles.length === 0)
54
- return;
55
- const hasDirtyKeiyakuArtifacts = dirtyFiles.some((file) => normalizePathForDirtyMatch(cwd, file.path).startsWith(KEIYAKU_ARTIFACT_DIR_PREFIX));
56
- const shown = dirtyFiles.slice(0, DIRTY_WORKTREE_LIST_LIMIT);
57
- const list = shown.map((file) => ` ${renderDirtyFileStatusLine(file)}`).join("\n");
58
- const overflowCount = dirtyFiles.length - shown.length;
59
- const overflowLine = overflowCount > 0 ? `\n ... (+${overflowCount} more)` : "";
60
- throw new FlowError("DIRTY_WORKTREE", `Uncommitted changes detected in working tree:\n${list}${overflowLine}\n\nBlocking changes must be committed or stashed before proceeding.`, hasDirtyKeiyakuArtifacts ? { hints: [buildDirtyWorktreeArtifactHint()] } : undefined);
61
- }
16
+ import { resolveContractDeliveryCwd } from "./worktree-path.js";
62
17
  export function buildArcViews(entries) {
63
18
  const arcs = new Map();
64
19
  for (const entry of entries) {
@@ -96,25 +51,20 @@ function assertArcOpenable(contractId, entries) {
96
51
  throw new FlowError("INTERNAL_STATE", `contract ${contractId} cannot open arc from state ${state.state}`);
97
52
  }
98
53
  }
99
- async function renderUpdatedContract(cwd, contractId) {
100
- const ledger = await readLedger(cwd, contractId);
101
- if (!ledger) {
102
- throw new FlowError("INTERNAL_STATE", `contract ${contractId} active ledger disappeared before render`);
103
- }
104
- await fs.writeFile(path.join(cwd, KEIYAKU_FILE), renderContractView({ id: contractId, entries: ledger.entries }), "utf8");
105
- }
106
54
  export async function openArc(input) {
107
55
  const title = requireText("title", input.title);
108
56
  const objective = requireText("objective", input.objective);
109
57
  const brief = requireText("brief", input.brief);
58
+ const renderCwd = input.renderCwd ?? input.cwd;
110
59
  const ledger = await readLedger(input.cwd, input.contractId);
111
60
  if (!ledger) {
112
61
  throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} not found or not active`);
113
62
  }
114
63
  assertArcOpenable(input.contractId, ledger.entries);
64
+ // Seal capture needs delivery HEAD; ledger append may use hub or delivery (shared refs).
115
65
  const sealed = await sealCurrentArc({
116
- cwd: input.cwd,
117
- renderCwd: input.cwd,
66
+ cwd: renderCwd,
67
+ renderCwd,
118
68
  contractId: input.contractId,
119
69
  sealedBy: "arc",
120
70
  nowMs: input.nowMs,
@@ -140,7 +90,7 @@ export async function openArc(input) {
140
90
  if (!result.ok || arcNumber === 0) {
141
91
  throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} active ledger disappeared during arc`);
142
92
  }
143
- await renderUpdatedContract(input.cwd, input.contractId);
93
+ await refreshContractView({ ledgerCwd: input.cwd, renderCwd, contractId: input.contractId });
144
94
  return { contractId: input.contractId, arc: arcNumber, ledgerHead: result.head, sealed };
145
95
  }
146
96
  export function buildArcInput(content, cwd, waivePaths = [], contractId, contractAddressSource) {
@@ -162,11 +112,13 @@ async function readBranchLabels(cwd) {
162
112
  }
163
113
  export async function arcKeiyaku(input) {
164
114
  await requireGitRepository(input.cwd, "arc");
165
- const address = await resolveContractAddress(input.cwd, input.contractId, input.contractAddressSource);
115
+ const address = input.address ?? await resolveContractAddress(input.cwd, input.contractId, input.contractAddressSource);
166
116
  return await withResolvedAddress(address, async () => {
167
- await assertCleanWorkingTree(input.cwd, []);
117
+ const contractId = address.binding.commissionId;
118
+ const renderCwd = await resolveContractDeliveryCwd(input.cwd, contractId);
119
+ await assertCleanTrackedWorkingTree(renderCwd);
168
120
  try {
169
- await fs.access(path.join(input.cwd, KEIYAKU_FILE));
121
+ await fs.access(path.join(renderCwd, KEIYAKU_FILE));
170
122
  }
171
123
  catch {
172
124
  throw new FlowError("MISSING_PROTOCOL_FILES", `missing protocol file for current keiyaku (${KEIYAKU_FILE})`);
@@ -174,9 +126,9 @@ export async function arcKeiyaku(input) {
174
126
  const title = normalizeInlineTitle(requireText("title", input.title));
175
127
  const objective = requireText("objective", input.objective);
176
128
  const brief = requireText("brief", input.brief);
177
- const contractId = address.binding.commissionId;
178
129
  const opened = await openArc({
179
130
  cwd: input.cwd,
131
+ renderCwd,
180
132
  contractId,
181
133
  actor: ACTOR_IDENTITY,
182
134
  nowMs: Date.now(),
@@ -185,11 +137,10 @@ export async function arcKeiyaku(input) {
185
137
  brief,
186
138
  waivePaths: input.waivePaths,
187
139
  });
188
- const { currentBranch, baseBranch } = await readBranchLabels(input.cwd);
140
+ const { currentBranch, baseBranch } = await readBranchLabels(renderCwd);
189
141
  const summary = opened.sealed
190
142
  ? `a${opened.sealed.arc} sealed; a${opened.arc} open: ${title}.`
191
143
  : `a${opened.arc} open: ${title}.`;
192
- const diff = opened.sealed?.incrementalDiff ?? "working tree untouched";
193
144
  let responsePath;
194
145
  let artifactId;
195
146
  try {
@@ -202,7 +153,7 @@ export async function arcKeiyaku(input) {
202
153
  brief,
203
154
  ledgerCommit: opened.ledgerHead,
204
155
  summary,
205
- diff,
156
+ emptyDiffLabel: "working tree untouched",
206
157
  ...(opened.sealed ? { sealed: opened.sealed } : {}),
207
158
  });
208
159
  responsePath = persisted.responsePath;
@@ -217,7 +168,8 @@ export async function arcKeiyaku(input) {
217
168
  currentBranch,
218
169
  baseBranch,
219
170
  commit: opened.ledgerHead,
220
- diff,
171
+ // CLI/render synthesizes public face bytes from sealed typed facts.
172
+ diff: "",
221
173
  summary,
222
174
  title,
223
175
  objective,
@@ -3,12 +3,12 @@ import { assertSettingsKnobUsable } from "../config/settings/disease.js";
3
3
  import { loadKeiyakuSettings } from "../config/settings/loader.js";
4
4
  import { FlowError } from "../flow-error.js";
5
5
  import { activeLedgerRef, appendEntry, appendWithRetry, readLedger } from "./ledger.js";
6
- import { parseBindDraft } from "./draft.js";
6
+ import { describeMissingBindSections, materializeBindDraft, mergeBindDraft, missingBindDraftSections, parseBindDraft, parsePartialBindDraft, renderMissingBindSectionsTemplate, } from "./draft.js";
7
7
  import { assertValidContractId, buildContractId } from "./ids.js";
8
8
  import { registerScope, releaseScope } from "./registry.js";
9
9
  import { resolveDefaultBranch } from "./status/drift.js";
10
10
  import { normalizeTargetRef } from "./target-ref.js";
11
- import { reconcileTaskForfeit, settleTaskBoardForBind } from "./task-contract.js";
11
+ import { reconcileTaskForfeit, settleTaskBoardForBind } from "./task/index.js";
12
12
  import { bootstrapEntryDataFromObservation, isBootstrapSuccess, runWorktreeBootstrap, } from "./worktree-bootstrap.js";
13
13
  import { contractWorktreePathFromBind, stableRepoRoot } from "./worktree-path.js";
14
14
  import { allocateWorktreePlace, cleanupCreatedBindResources, createContractBranch, defaultCreateWorktree, materializeContractView, resolveTreeRoot, } from "./bind-workspace.js";
@@ -107,8 +107,40 @@ function makeBindEntry(input) {
107
107
  },
108
108
  };
109
109
  }
110
+ function taskBindDefaults(task) {
111
+ const defaults = { name: task.title };
112
+ const objective = task.body.trim();
113
+ if (objective.length > 0)
114
+ defaults.objective = objective;
115
+ return defaults;
116
+ }
117
+ function incompleteTaskBindError(taskId, missing) {
118
+ const coordinate = taskId;
119
+ const template = renderMissingBindSectionsTemplate(missing);
120
+ const message = [
121
+ `task ${coordinate} carries no ${describeMissingBindSections(missing)}`,
122
+ `» keiyaku bind --task ${coordinate} <<'KEIYAKU'`,
123
+ template,
124
+ "KEIYAKU",
125
+ ].join("\n");
126
+ return new FlowError("BIND_TASK_INCOMPLETE", message);
127
+ }
128
+ /**
129
+ * Section-level merge for `bind --task`: task title/body are name/objective
130
+ * defaults; stdin partial markdown sections override. Missing required sections
131
+ * fail with a paste-ready partial heredoc template.
132
+ */
133
+ export function composeTaskBindDraft(input) {
134
+ const merged = mergeBindDraft(taskBindDefaults(input.task), parsePartialBindDraft(input.draft));
135
+ const missing = missingBindDraftSections(merged);
136
+ if (missing.length > 0) {
137
+ throw incompleteTaskBindError(input.task.id, missing);
138
+ }
139
+ return materializeBindDraft(merged);
140
+ }
110
141
  export async function bindContract(input) {
111
- const draft = parseBindDraft(input.draft);
142
+ const isTaskBound = input.taskIds !== undefined && input.taskIds.length > 0;
143
+ const ordinaryDraft = !isTaskBound ? parseBindDraft(input.draft) : undefined;
112
144
  const workspace = input.workspace ?? "here";
113
145
  const at = new Date(input.nowMs).toISOString();
114
146
  const defaultBranch = await resolveDefaultBranch(input.cwd);
@@ -126,47 +158,60 @@ export async function bindContract(input) {
126
158
  for (let attempt = 0; attempt < 2; attempt += 1) {
127
159
  const randomBytes = input.randomBytes?.(attempt) ?? randomBytesDefault();
128
160
  const place = workspace === "worktree" ? await allocateWorktreePlace(input.cwd, randomBytes) : undefined;
129
- const id = buildContractId({
130
- name: draft.name,
131
- nowMs: input.nowMs,
132
- randomBytes,
133
- });
134
- await assertValidContractId(input.cwd, id);
135
- const ref = activeLedgerRef(id);
136
161
  let settlement;
137
162
  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,
163
+ const prepare = async (draft) => {
164
+ const id = buildContractId({ name: draft.name, nowMs: input.nowMs, randomBytes });
165
+ await assertValidContractId(input.cwd, id);
166
+ const ref = activeLedgerRef(id);
167
+ return {
168
+ contractId: id,
169
+ publishLedger: async (base) => {
170
+ const entry = makeBindEntry({
171
+ id,
172
+ at,
173
+ actor: input.actor,
174
+ draft,
175
+ base,
176
+ target,
177
+ after: input.after ?? [],
178
+ workspace,
179
+ place,
180
+ bootstrap: bootstrapPlan,
181
+ });
182
+ const appended = await appendEntry(input.cwd, ref, entry, null);
183
+ if (!appended.ok)
184
+ throw new BindLedgerCollisionError(id);
185
+ return { entry, appended };
186
+ },
187
+ };
188
+ };
189
+ settlement = !isTaskBound
190
+ ? await (async () => {
191
+ const prepared = await prepare(ordinaryDraft);
192
+ return settleTaskBoardForBind({ cwd: input.cwd, signal: input.signal, ...prepared });
193
+ })()
194
+ : await (async () => {
195
+ return settleTaskBoardForBind({
196
+ cwd: input.cwd,
197
+ taskIds: input.taskIds,
198
+ signal: input.signal,
199
+ prepare: async (tasks) => prepare(composeTaskBindDraft({ task: tasks[0], draft: input.draft })),
154
200
  });
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
- });
201
+ })();
161
202
  }
162
203
  catch (error) {
163
204
  if (error instanceof BindLedgerCollisionError) {
164
- collidedIds.push(id);
205
+ // The selected task's locked snapshot chose this attempted identity.
206
+ // Record the concrete id only when preparation reached publication.
207
+ collidedIds.push(error.contractId);
165
208
  continue;
166
209
  }
167
210
  throw error;
168
211
  }
169
212
  const { entry, appended } = settlement.ledger;
213
+ const id = settlement.contractId;
214
+ const ref = activeLedgerRef(id);
170
215
  const base = settlement.boardCommit;
171
216
  const resources = { id };
172
217
  const branch = `keiyaku/${id}`;
@@ -179,7 +224,7 @@ export async function bindContract(input) {
179
224
  const tree = await resolveTreeRoot(input.cwd, workspace, id, entry);
180
225
  const registration = await registerScope(input.cwd, {
181
226
  contractId: id,
182
- scope: draft.scope,
227
+ scope: entry.data.scope,
183
228
  tree,
184
229
  actor: input.actor,
185
230
  at,
@@ -219,11 +264,13 @@ export async function bindContract(input) {
219
264
  return {
220
265
  id,
221
266
  ref,
267
+ target,
268
+ place: workspace === "worktree" ? entry.data.place ?? null : null,
222
269
  branch: workspace === "worktree" ? branch : undefined,
223
270
  worktreePath: targetRoot,
224
271
  workspace,
225
272
  ledgerHead,
226
- warnings: registration.warnings,
273
+ scopeOverlaps: registration.overlaps,
227
274
  };
228
275
  }
229
276
  catch (error) {
@@ -8,10 +8,8 @@ import { ACTOR_IDENTITY } from "../../keiyaku.js";
8
8
  import { appendDebugBlock } from "../../telemetry/debug-log.js";
9
9
  import { logInfo } from "../../telemetry/logger.js";
10
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";
11
+ import { assertProjectionAlias, PROJECTION_ADOPTION_TIMEOUT_MS, observeProjectionLife, waitForProjection, } from "../projection/index.js";
12
+ import { normalizeFilesystemCoordinate } from "../../fs/path-coordinate.js";
15
13
  import { requireResumableArtifact } from "../transcripts.js";
16
14
  import { stableRepoRoot } from "../worktree-path.js";
17
15
  import { readCallKeiyakuContext } from "./context.js";
@@ -23,6 +21,9 @@ function renderSubagentWarning(warning) {
23
21
  const status = warning.errorStatus === null ? "no response" : String(warning.errorStatus);
24
22
  return `Provider warning: retry. attempt=${warning.attempt}, maxRetries=${warning.maxRetries}, retryDelayMs=${warning.retryDelayMs}, errorStatus=${status}`;
25
23
  }
24
+ if (warning.reason === "rate-limit") {
25
+ return "Provider warning: rate limit exhausted.";
26
+ }
26
27
  const modelTransition = warning.originalModel && warning.fallbackModel
27
28
  ? `${warning.originalModel} -> ${warning.fallbackModel}`
28
29
  : warning.originalModel ?? warning.fallbackModel;
@@ -42,18 +43,18 @@ export async function execSubagent(agentName, prompt, cwd, options) {
42
43
  export async function runCall(input) {
43
44
  const { signal, agent } = input;
44
45
  const reviveArtifact = input.reviveArtifact ? requireResumableArtifact(input.reviveArtifact) : undefined;
45
- let rawCwd = input.cwd;
46
- let rawRepo = input.repo;
46
+ let rawCwd = normalizeFilesystemCoordinate(input.cwd);
47
+ let rawRepo = input.repo === undefined ? undefined : normalizeFilesystemCoordinate(input.repo);
47
48
  if (reviveArtifact?.provenance.authority.kind === "user") {
48
49
  if (!("workdir" in reviveArtifact.provenance.evidence)) {
49
50
  throw new FlowError("INVALID_RESPONSE_PATH", `Invalid response artifact '${reviveArtifact.artifactId}': field 'provenance.evidence.workdir' is missing.`);
50
51
  }
51
- rawCwd = reviveArtifact.provenance.evidence.workdir;
52
+ rawCwd = normalizeFilesystemCoordinate(reviveArtifact.provenance.evidence.workdir);
52
53
  rawRepo = undefined;
53
54
  }
54
55
  else if (reviveArtifact?.provenance.authority.kind === "repository") {
55
56
  const artifactHome = await fs.realpath(reviveArtifact.storageRoot);
56
- const addressed = input.repo ?? input.cwd;
57
+ const addressed = normalizeFilesystemCoordinate(input.repo ?? input.cwd);
57
58
  const observation = await observeGitRepository(addressed);
58
59
  const coordinate = assertGitObservation(observation, "revive");
59
60
  if (coordinate === "repository") {
@@ -91,6 +92,7 @@ export async function runCall(input) {
91
92
  context: input.context,
92
93
  referenceFiles,
93
94
  currentIntent: keiyakuContext.currentIntent,
95
+ commissioned: keiyakuContext.address.binding.kind === "commission",
94
96
  });
95
97
  const launchSnapshot = reviveArtifact
96
98
  ? input.effort !== undefined
@@ -146,7 +148,11 @@ export async function runCall(input) {
146
148
  ...(launched.projection.alias ? { alias: launched.projection.alias } : {}),
147
149
  ...(launched.projection.previousAliasTarget ? { previousAliasTarget: launched.projection.previousAliasTarget } : {}),
148
150
  };
149
- input.onLaunch?.({ address: keiyakuContext.address, projection: adoptedProjection });
151
+ input.onLaunch?.({
152
+ address: keiyakuContext.address,
153
+ projection: adoptedProjection,
154
+ akumaName: launchSnapshot.akuma.name,
155
+ });
150
156
  if (input.detach) {
151
157
  return {
152
158
  state: "launched",
@@ -3,10 +3,11 @@ import { FlowError } from "../../flow-error.js";
3
3
  import { assertGitObservation, observeGitRepository } from "../../git/branches.js";
4
4
  import { createGit, wrapGitError } from "../../git/core.js";
5
5
  import { AddressResolutionError, buildResolutionAttempt, buildUncommissionedAddress, buildUncommissionedRepositoryAddress, resolveContractAddress, withResolvedAddress, } from "../addressing.js";
6
+ import { normalizeFilesystemCoordinate } from "../../fs/path-coordinate.js";
6
7
  import { findOpenArcView } from "../arc.js";
7
8
  import { readLedger } from "../ledger.js";
8
9
  import { deriveContractState, isTerminalState } from "../status/lifecycle.js";
9
- import { contractPresentedByWorktree, contractWorktreePathFromBind, stableRepoRoot } from "../worktree-path.js";
10
+ import { contractWorktreePathFromBind, stableRepoRoot } from "../worktree-path.js";
10
11
  async function findWorktreePathForContract(cwd, contractId) {
11
12
  const git = createGit(cwd);
12
13
  let output;
@@ -47,6 +48,8 @@ async function resolveWorkspaceCwd(cwd, contractId, bind) {
47
48
  return worktreePath;
48
49
  }
49
50
  export async function readCallKeiyakuContext(executionCwd, input) {
51
+ executionCwd = normalizeFilesystemCoordinate(executionCwd);
52
+ input.repoCwd = input.repoCwd === undefined ? undefined : normalizeFilesystemCoordinate(input.repoCwd);
50
53
  const ledgerCandidate = input.repoCwd ?? executionCwd;
51
54
  const ledgerObservation = await observeGitRepository(ledgerCandidate);
52
55
  if (ledgerObservation.capability === "unknown")
@@ -136,7 +139,7 @@ export async function readCallKeiyakuContext(executionCwd, input) {
136
139
  },
137
140
  }));
138
141
  }
139
- if (input.bare) {
142
+ if (input.bare || !input.contractId) {
140
143
  return {
141
144
  activeKeiyaku: false,
142
145
  effectiveCwd: executionCwd,
@@ -150,55 +153,7 @@ export async function readCallKeiyakuContext(executionCwd, input) {
150
153
  }),
151
154
  };
152
155
  }
153
- let address;
154
- if (input.contractId) {
155
- address = await resolveContractAddress(ledgerCwd, input.contractId, input.contractAddressSource);
156
- }
157
- else {
158
- const executionObservation = await observeGitRepository(executionCwd);
159
- if (executionObservation.capability === "unknown")
160
- throw executionObservation.error;
161
- if (executionObservation.capability === "available" && executionObservation.coordinate === "repository") {
162
- const executionRepo = await stableRepoRoot(executionCwd);
163
- if (input.repoCwd && executionRepo !== ledgerCwd) {
164
- const presented = await contractPresentedByWorktree(executionCwd);
165
- if (presented) {
166
- throw new FlowError("EMPTY_PARAM", `cross-repository contract-worktree addressing is pending: -C presents @${presented} from ${executionRepo}, but --repo selects ${ledgerCwd}`);
167
- }
168
- }
169
- else {
170
- const presented = await contractPresentedByWorktree(executionCwd);
171
- if (presented) {
172
- address = await resolveContractAddress(ledgerCwd, presented, "worktree-presented");
173
- }
174
- }
175
- }
176
- }
177
- if (!address) {
178
- try {
179
- address = await resolveContractAddress(ledgerCwd);
180
- }
181
- catch (error) {
182
- if (error instanceof AddressResolutionError
183
- && error.code === "EMPTY_PARAM"
184
- && error.attempt.expected === "commission"
185
- && error.attempt.candidates.length === 0) {
186
- return {
187
- activeKeiyaku: false,
188
- effectiveCwd: executionCwd,
189
- historyCwd: ledgerCwd,
190
- profileCwd: ledgerCwd,
191
- projectionCwd: ledgerCwd,
192
- address: buildUncommissionedRepositoryAddress({
193
- stableRoot: ledgerCwd,
194
- workdir: executionCwd,
195
- binding: "unbound",
196
- }),
197
- };
198
- }
199
- throw error;
200
- }
201
- }
156
+ const address = await resolveContractAddress(ledgerCwd, input.contractId, input.contractAddressSource);
202
157
  return await withResolvedAddress(address, async () => {
203
158
  const contractId = address.binding.commissionId;
204
159
  const ledger = await readLedger(ledgerCwd, contractId);
@@ -3,14 +3,14 @@ import { createAbortError, restoreOutcome, SubagentFailure, } from "../../agents
3
3
  import { resolveAkumaLaunchSnapshot } from "../../agents/launch-snapshot/resolve.js";
4
4
  import { selectSubagent } from "../../agents/selector.js";
5
5
  import { FlowError } from "../../flow-error.js";
6
- import { assertProjectionAlias, moveProjectionAlias } from "../projection-alias.js";
7
- import { projectionDir } from "../projection-core.js";
8
- import { createProjectionExecutionId } from "../projection-generation-identity.js";
9
- import { PROJECTION_ADOPTION_TIMEOUT_MS, startProjectionGeneration } from "../projection-generation-launcher.js";
10
- import { openProjectionGenerationStore, openProjectionGenerationStoreReadOnly, } from "../projection/generation/store.js";
11
- import { observeProjectionLife } from "../projection-life-observer.js";
12
- import { mintProjection, mintRepositoryProjection } from "../projection-mint.js";
13
- import { waitForProjection } from "../projection-wait.js";
6
+ import { assertProjectionAlias, moveProjectionAlias } from "../projection/index.js";
7
+ import { projectionDir } from "../projection/index.js";
8
+ import { createProjectionExecutionId } from "../projection/index.js";
9
+ import { PROJECTION_ADOPTION_TIMEOUT_MS, startProjectionGeneration } from "../projection/index.js";
10
+ import { openProjectionGenerationStore, openProjectionGenerationStoreReadOnly, } from "../projection/index.js";
11
+ import { observeProjectionLife } from "../projection/index.js";
12
+ import { mintProjection, mintRepositoryProjection } from "../projection/index.js";
13
+ import { waitForProjection } from "../projection/index.js";
14
14
  export function readCurrentGenerationOrThrow(projectionDirectory) {
15
15
  const store = openProjectionGenerationStoreReadOnly(projectionDirectory);
16
16
  try {
@@ -148,7 +148,7 @@ export async function buildCallConfig(agentName, prompt, cwd, options) {
148
148
  };
149
149
  const minted = options.authority.kind === "repository"
150
150
  ? await mintRepositoryProjection({ ...mintInput, authority: { kind: "repository" } })
151
- : mintProjection({ ...mintInput, authority: { kind: "user" } });
151
+ : await mintProjection({ ...mintInput, authority: { kind: "user" } });
152
152
  const executionId = createProjectionExecutionId();
153
153
  const launchFacts = JSON.parse(JSON.stringify({
154
154
  executionId,
@@ -184,20 +184,19 @@ export async function buildCallConfig(agentName, prompt, cwd, options) {
184
184
  }
185
185
  const previousAliasTarget = options.alias === undefined
186
186
  ? undefined
187
- : moveProjectionAlias(options.projectionCwd ?? cwd, assertProjectionAlias(options.alias), minted.id);
188
- const previousAliasTargetStillRunning = previousAliasTarget === undefined
189
- ? undefined
190
- : (() => {
191
- try {
192
- const phase = observeProjectionLife(projectionDir(options.projectionCwd ?? cwd, previousAliasTarget), { nowMs: Date.now(), startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS }).phase;
193
- return phase === "minting" || phase === "active" || phase === "quiescent"
194
- ? previousAliasTarget
195
- : undefined;
196
- }
197
- catch {
198
- return undefined;
187
+ : await moveProjectionAlias(options.projectionCwd ?? cwd, assertProjectionAlias(options.alias), minted.id);
188
+ let previousAliasTargetStillRunning;
189
+ if (previousAliasTarget !== undefined) {
190
+ try {
191
+ const phase = observeProjectionLife(await projectionDir(options.projectionCwd ?? cwd, previousAliasTarget), { nowMs: Date.now(), startupTimeoutMs: PROJECTION_ADOPTION_TIMEOUT_MS }).phase;
192
+ if (phase === "minting" || phase === "active" || phase === "quiescent") {
193
+ previousAliasTargetStillRunning = previousAliasTarget;
199
194
  }
200
- })();
195
+ }
196
+ catch {
197
+ previousAliasTargetStillRunning = undefined;
198
+ }
199
+ }
201
200
  return {
202
201
  id: minted.id,
203
202
  dir: minted.dir,
@@ -1,4 +1,10 @@
1
1
  import { ACTOR_IDENTITY } from "../../keiyaku.js";
2
+ const COMMISSIONED_DELIVERY_BOUNDARY = [
3
+ "Delivery boundary: edit and test only inside this linked contract worktree.",
4
+ "Leave source changes uncommitted as a dirty working tree, then report changed files and checks run.",
5
+ "Do not run git add, git commit, or any command that writes Git metadata. A refused Git metadata write is not a failed delivery.",
6
+ "The host coordinator reviews and commits accepted bytes, then continues arc, renew, or petition settlement.",
7
+ ].join(" ");
2
8
  export function buildCallPrompt(input) {
3
9
  const references = [...new Set((input.referenceFiles ?? []).map((file) => file.trim()).filter(Boolean))];
4
10
  if (input.currentIntent?.source === "arc") {
@@ -9,6 +15,7 @@ export function buildCallPrompt(input) {
9
15
  `Title: ${arc.title}`,
10
16
  `Objective: ${arc.objective}`,
11
17
  `Brief:\n${arc.brief}`,
18
+ COMMISSIONED_DELIVERY_BOUNDARY,
12
19
  "",
13
20
  `Task:\n${input.task}`,
14
21
  `Context:\n${input.context?.trim() || "(none provided)"}`,
@@ -30,13 +37,19 @@ export function buildCallPrompt(input) {
30
37
  `Scope:\n${bind.scope.map((value) => `- ${value}`).join("\n")}`,
31
38
  `Checks:\n${bind.checks.map((value) => `- ${value}`).join("\n")}`,
32
39
  extensionsBlock,
40
+ COMMISSIONED_DELIVERY_BOUNDARY,
33
41
  `Task:\n${input.task}`,
34
42
  trimmedContext ? `Context:\n${trimmedContext}` : "",
35
43
  references.length > 0 ? `Reference files:\n${references.map((file) => `- ${file}`).join("\n")}` : "",
36
44
  "Provide the final result for the caller/main agent.",
37
45
  ].filter(Boolean).join("\n\n");
38
46
  }
39
- return [input.task, input.context?.trim() ?? "", references.length > 0 ? `Reference files:\n${references.map((file) => `- ${file}`).join("\n")}` : ""]
47
+ return [
48
+ input.task,
49
+ input.context?.trim() ?? "",
50
+ input.commissioned ? COMMISSIONED_DELIVERY_BOUNDARY : "",
51
+ references.length > 0 ? `Reference files:\n${references.map((file) => `- ${file}`).join("\n")}` : "",
52
+ ]
40
53
  .filter(Boolean)
41
54
  .join("\n\n");
42
55
  }
@@ -0,0 +1,45 @@
1
+ import { FlowError } from "../flow-error.js";
2
+ import { parseMarkdownListSection, parseMarkdownStructure } from "./markdown/sections.js";
3
+ import { KeiyakuParseError } from "./markdown/types.js";
4
+ import { normalizeSectionTitle } from "./markdown/titles.js";
5
+ export function parseCommandMarkdown(content) {
6
+ try {
7
+ const parsed = parseMarkdownStructure(content);
8
+ const sections = new Map();
9
+ for (const [key, lines] of parsed.sections.entries()) {
10
+ sections.set(key, lines.join("\n").trim());
11
+ }
12
+ return { title: parsed.title, sections };
13
+ }
14
+ catch (error) {
15
+ if (error instanceof KeiyakuParseError) {
16
+ throw new FlowError("INVALID_KEIYAKU_DRAFT", error.message, { cause: error });
17
+ }
18
+ throw error;
19
+ }
20
+ }
21
+ export function requireMarkdownTextSection(parts, name) {
22
+ const normalized = normalizeSectionTitle(name);
23
+ const value = parts.sections.get(normalized)?.trim();
24
+ if (!value) {
25
+ throw new FlowError("INVALID_KEIYAKU_DRAFT", `missing required section: ## ${name}`);
26
+ }
27
+ return value;
28
+ }
29
+ export function requireMarkdownListSection(parts, name) {
30
+ const text = requireMarkdownTextSection(parts, name);
31
+ const items = parseMarkdownListSection(text)
32
+ .map((item) => item.trim())
33
+ .filter((item) => item.length > 0);
34
+ if (items.length === 0) {
35
+ throw new FlowError("INVALID_KEIYAKU_DRAFT", `missing required section: ## ${name}`);
36
+ }
37
+ return items;
38
+ }
39
+ export function requireMarkdownTitle(parts) {
40
+ const title = parts.title?.trim();
41
+ if (!title) {
42
+ throw new FlowError("INVALID_KEIYAKU_DRAFT", "missing required title: # Title");
43
+ }
44
+ return title;
45
+ }