@astrosheep/keiyaku 2.9.3 → 2.9.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 (282) hide show
  1. package/build/.tsbuildinfo +1 -1
  2. package/build/agents/call-terms.js +14 -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 +26 -222
  6. package/build/agents/harness/index.js +3 -1
  7. package/build/agents/harness/outcome.js +68 -5
  8. package/build/agents/harness/projection.js +179 -124
  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 +82 -53
  21. package/build/agents/providers/codex-app-server/events.js +90 -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/events.js +4 -3
  36. package/build/agents/providers/pi/registration.js +145 -0
  37. package/build/agents/providers/pi/session-schema.js +6 -0
  38. package/build/agents/providers/pi/terms.js +1 -0
  39. package/build/agents/providers/registration.js +7 -0
  40. package/build/agents/providers/registry.js +76 -0
  41. package/build/agents/selector.js +2 -29
  42. package/build/agents/types.js +10 -0
  43. package/build/artifact-directories.js +22 -0
  44. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  45. package/build/cli/commands/akuma/akuma/meta.js +1 -1
  46. package/build/cli/commands/akuma/list/meta-ls.js +1 -1
  47. package/build/cli/commands/akuma/view/handler.js +5 -4
  48. package/build/cli/commands/akuma/view/meta.js +1 -1
  49. package/build/cli/commands/akuma.js +6 -0
  50. package/build/cli/commands/contract/amend/handler.js +8 -2
  51. package/build/cli/commands/contract/amend/meta.js +4 -4
  52. package/build/cli/commands/contract/arc/handler.js +8 -2
  53. package/build/cli/commands/contract/arc/meta.js +4 -4
  54. package/build/cli/commands/contract/audit/handler.js +16 -0
  55. package/build/cli/commands/contract/audit/meta.js +16 -0
  56. package/build/cli/commands/contract/bind/handler.js +4 -3
  57. package/build/cli/commands/contract/bind/meta.js +44 -8
  58. package/build/cli/commands/contract/catalog.js +2 -0
  59. package/build/cli/commands/contract/forfeit/handler.js +4 -4
  60. package/build/cli/commands/contract/forfeit/meta.js +1 -1
  61. package/build/cli/commands/contract/log/handler.js +3 -3
  62. package/build/cli/commands/contract/log/meta.js +1 -1
  63. package/build/cli/commands/contract/petition/handler.js +5 -8
  64. package/build/cli/commands/contract/petition/meta.js +6 -6
  65. package/build/cli/commands/contract/renew/handler.js +20 -6
  66. package/build/cli/commands/contract/renew/meta.js +6 -5
  67. package/build/cli/commands/metadata.js +59 -0
  68. package/build/cli/commands/projection/call/handler.js +6 -5
  69. package/build/cli/commands/projection/call/meta.js +4 -1
  70. package/build/cli/commands/projection/kill/handler.js +16 -32
  71. package/build/cli/commands/projection/kill/meta.js +1 -1
  72. package/build/cli/commands/projection/revive/meta.js +4 -1
  73. package/build/cli/commands/projection/shared.js +28 -0
  74. package/build/cli/commands/projection/status/handler.js +1 -1
  75. package/build/cli/commands/projection/status/meta.js +1 -1
  76. package/build/cli/commands/projection/tell/handler.js +14 -30
  77. package/build/cli/commands/projection/tell/meta.js +4 -1
  78. package/build/cli/commands/projection/wait/handler.js +45 -22
  79. package/build/cli/commands/projection/wait/meta.js +8 -3
  80. package/build/cli/commands/registry.js +2 -54
  81. package/build/cli/commands/shared.js +10 -7
  82. package/build/cli/commands/skills/install/handler.js +2 -5
  83. package/build/cli/commands/skills/install/meta.js +1 -1
  84. package/build/cli/commands/skills/skills/handler.js +1 -1
  85. package/build/cli/commands/skills/skills/meta.js +1 -1
  86. package/build/cli/commands/system/completion/meta.js +1 -1
  87. package/build/cli/commands/system/dump-env/meta.js +1 -1
  88. package/build/cli/commands/system/guide/meta.js +1 -1
  89. package/build/cli/commands/task/add/handler.js +7 -15
  90. package/build/cli/commands/task/add/meta.js +4 -1
  91. package/build/cli/commands/task/catalog.js +2 -2
  92. package/build/cli/commands/task/doctor/meta.js +1 -1
  93. package/build/cli/commands/task/done/handler.js +4 -8
  94. package/build/cli/commands/task/done/meta.js +1 -1
  95. package/build/cli/commands/task/drop/handler.js +3 -6
  96. package/build/cli/commands/task/drop/meta.js +1 -1
  97. package/build/cli/commands/task/ls/handler.js +12 -8
  98. package/build/cli/commands/task/ls/meta.js +3 -3
  99. package/build/cli/commands/task/shared.js +14 -4
  100. package/build/cli/commands/task/{view → show}/handler.js +7 -8
  101. package/build/cli/commands/task/show/meta.js +8 -0
  102. package/build/cli/commands/task/start/handler.js +3 -6
  103. package/build/cli/commands/task/start/meta.js +1 -1
  104. package/build/cli/commands/task/stop/handler.js +4 -8
  105. package/build/cli/commands/task/stop/meta.js +1 -1
  106. package/build/cli/commands/task/task/handler.js +1 -1
  107. package/build/cli/commands/task/task/meta.js +1 -1
  108. package/build/cli/commands/task/update/handler.js +6 -11
  109. package/build/cli/commands/task/update/meta.js +4 -1
  110. package/build/cli/completion.js +1 -1
  111. package/build/cli/flags.js +37 -6
  112. package/build/cli/help.js +7 -14
  113. package/build/cli/index.js +112 -154
  114. package/build/cli/missing-subcommand.js +7 -0
  115. package/build/cli/output-stream-error-policy.js +19 -5
  116. package/build/cli/parse-flags.js +33 -7
  117. package/build/cli/parse-metadata.js +3 -9
  118. package/build/cli/parse-selectors.js +2 -1
  119. package/build/cli/parse.js +35 -5
  120. package/build/cli/projection-address.js +12 -12
  121. package/build/cli/render/address.js +41 -37
  122. package/build/cli/render/arc.js +24 -12
  123. package/build/cli/render/audit.js +121 -0
  124. package/build/cli/render/call.js +11 -5
  125. package/build/cli/render/errors.js +36 -1
  126. package/build/cli/render/format.js +4 -0
  127. package/build/cli/render/kanshi.js +6 -12
  128. package/build/cli/render/kill.js +9 -0
  129. package/build/cli/render/misc.js +58 -13
  130. package/build/cli/render/petition.js +20 -15
  131. package/build/cli/render/projection-activity.js +15 -6
  132. package/build/cli/render/shared.js +60 -21
  133. package/build/cli/render/skills.js +10 -0
  134. package/build/cli/render/status.js +56 -23
  135. package/build/cli/render/success-response.js +78 -0
  136. package/build/cli/render/task.js +66 -57
  137. package/build/cli/render/tell.js +5 -0
  138. package/build/cli/render/terminal-failure.js +63 -0
  139. package/build/cli/render/types.js +1 -0
  140. package/build/cli/render/wait.js +68 -38
  141. package/build/cli/types.js +1 -1
  142. package/build/config/akuma-loader.js +0 -5
  143. package/build/config/env-keys.js +3 -1
  144. package/build/config/env.js +3 -0
  145. package/build/config/provider-policy-ownership.js +3 -32
  146. package/build/config/settings/disease.js +8 -1
  147. package/build/config/settings/loader.js +11 -3
  148. package/build/config/settings/provider-instance-fields.js +51 -0
  149. package/build/config/settings/schema.js +6 -176
  150. package/build/core/addressing.js +66 -16
  151. package/build/core/amend.js +61 -18
  152. package/build/core/arc.js +33 -73
  153. package/build/core/audit.js +650 -0
  154. package/build/core/bind.js +82 -35
  155. package/build/core/call/call.js +15 -9
  156. package/build/core/call/context.js +6 -51
  157. package/build/core/call/execution.js +22 -22
  158. package/build/core/call/prompt.js +14 -1
  159. package/build/core/command-markdown.js +45 -0
  160. package/build/core/context.js +13 -7
  161. package/build/core/contract-view.js +22 -6
  162. package/build/core/draft.js +139 -19
  163. package/build/core/entry.js +3 -4
  164. package/build/core/forfeit.js +5 -2
  165. package/build/core/hints.js +1 -75
  166. package/build/core/identifier-slug.js +66 -0
  167. package/build/core/ids.js +25 -11
  168. package/build/core/ledger.js +20 -68
  169. package/build/core/markdown/lex.js +2 -1
  170. package/build/core/markdown/parser.js +0 -68
  171. package/build/core/markdown/render.js +68 -0
  172. package/build/core/markdown/sections.js +2 -1
  173. package/build/core/outcome-base.js +4 -0
  174. package/build/core/projection/generation/database.js +41 -52
  175. package/build/core/projection/generation/model.js +63 -2
  176. package/build/core/{projection-generation-continuation.js → projection/generation/projection-generation-continuation.js} +3 -3
  177. package/build/core/{projection-generation-execution.js → projection/generation/projection-generation-execution.js} +36 -53
  178. package/build/core/{projection-generation-identity.js → projection/generation/projection-generation-identity.js} +2 -2
  179. package/build/core/{projection-generation-launcher.js → projection/generation/projection-generation-launcher.js} +9 -7
  180. package/build/core/{projection-generation-process.js → projection/generation/projection-generation-process.js} +2 -1
  181. package/build/core/{projection-generation-runner.js → projection/generation/projection-generation-runner.js} +19 -3
  182. package/build/core/projection/generation/store.js +3 -3
  183. package/build/core/projection/generation/transitions.js +19 -3
  184. package/build/core/projection/index.js +27 -0
  185. package/build/core/{projection-activity.js → projection/projection-activity.js} +12 -0
  186. package/build/core/{projection-alias.js → projection/projection-alias.js} +13 -13
  187. package/build/core/projection/projection-coordinate.js +61 -0
  188. package/build/core/{projection-core.js → projection/projection-core.js} +9 -9
  189. package/build/core/{projection-execution-observer.js → projection/projection-execution-observer.js} +37 -78
  190. package/build/core/{projection-identity.js → projection/projection-identity.js} +1 -1
  191. package/build/core/{projection-kill.js → projection/projection-kill.js} +1 -1
  192. package/build/core/{projection-life-observer.js → projection/projection-life-observer.js} +16 -2
  193. package/build/core/{projection-mint.js → projection/projection-mint.js} +10 -10
  194. package/build/core/{projection-status.js → projection/projection-status.js} +163 -45
  195. package/build/core/projection/projection-terminal-failure.js +106 -0
  196. package/build/core/projection/projection-wait.js +275 -0
  197. package/build/core/projection/projection-wake.js +227 -0
  198. package/build/core/provider-loader-ports.js +4 -0
  199. package/build/core/ref-log.js +65 -40
  200. package/build/core/registry.js +63 -10
  201. package/build/core/renew-session.js +128 -0
  202. package/build/core/renew.js +448 -102
  203. package/build/core/scope.js +272 -68
  204. package/build/core/seal.js +165 -40
  205. package/build/core/{claim-delivery.js → settlement/claim-delivery.js} +8 -8
  206. package/build/core/{claim.js → settlement/claim.js} +32 -8
  207. package/build/core/settlement/index.js +19 -0
  208. package/build/core/{petition-claim-gates.js → settlement/petition-claim-gates.js} +24 -49
  209. package/build/core/{petition-forfeit.js → settlement/petition-forfeit.js} +2 -2
  210. package/build/core/{petition-head-guard.js → settlement/petition-head-guard.js} +2 -2
  211. package/build/core/settlement/petition-io.js +1 -0
  212. package/build/core/{petition-preview.js → settlement/petition-preview.js} +2 -2
  213. package/build/core/{petition.js → settlement/petition.js} +21 -12
  214. package/build/core/settlement/queue-read-model.js +143 -0
  215. package/build/core/settlement/queue-seat-allocation.js +156 -0
  216. package/build/core/settlement/queue.js +6 -0
  217. package/build/core/settlement/settlement.js +288 -0
  218. package/build/core/{verdict.js → settlement/verdict.js} +9 -37
  219. package/build/core/settlement/verification.js +59 -0
  220. package/build/core/status/board.js +62 -41
  221. package/build/core/status/ledger-batch.js +158 -0
  222. package/build/core/status/reconciliation.js +10 -4
  223. package/build/core/stored-agent-event.js +6 -2
  224. package/build/core/task/board.js +41 -4
  225. package/build/core/task/commands.js +45 -28
  226. package/build/core/task/document.js +5 -0
  227. package/build/core/task/index.js +12 -0
  228. package/build/core/task/model.js +8 -1
  229. package/build/core/task/query.js +172 -0
  230. package/build/core/task/settlement-git.js +265 -74
  231. package/build/core/task/settlement-policy.js +67 -29
  232. package/build/core/task/source-board.js +1 -0
  233. package/build/core/task/task-contract.js +444 -0
  234. package/build/core/{task-doctor.js → task/task-doctor.js} +1 -1
  235. package/build/core/{task-git-actor.js → task/task-git-actor.js} +5 -10
  236. package/build/core/{task-git-runtime.js → task/task-git-runtime.js} +3 -3
  237. package/build/core/{task-git-store.js → task/task-git-store.js} +90 -19
  238. package/build/core/task/task-repository.js +1 -0
  239. package/build/core/{task-store-repository.js → task/task-store-repository.js} +6 -4
  240. package/build/core/task/task-store.js +1 -0
  241. package/build/core/{task-worktree.js → task/task-worktree.js} +2 -1
  242. package/build/core/task/task.js +7 -0
  243. package/build/core/task/tree.js +37 -0
  244. package/build/core/task/validation.js +5 -2
  245. package/build/core/transcripts.js +15 -3
  246. package/build/core/worktree-path.js +43 -28
  247. package/build/flow-error.js +83 -39
  248. package/build/fs/path-coordinate.js +16 -0
  249. package/build/generated/version.js +3 -2
  250. package/build/git/branches.js +2 -1
  251. package/build/git/core.js +106 -2
  252. package/build/git/diff/parsers.js +23 -13
  253. package/build/git/diff/structured.js +127 -0
  254. package/build/git/path-coordinate.js +17 -0
  255. package/build/git/refs.js +57 -0
  256. package/build/git/streaming-batch.js +301 -0
  257. package/build/git/worktree.js +65 -0
  258. package/build/index.js +9 -9
  259. package/build/keiyaku.js +2 -36
  260. package/build/telemetry/logger.js +4 -2
  261. package/package.json +4 -10
  262. package/skills/keiyaku/SKILL.md +5 -4
  263. package/skills/keiyaku-akuma/SKILL.md +25 -11
  264. package/skills/keiyaku-task/SKILL.md +17 -8
  265. package/skills/keiyaku-workflow/SKILL.md +58 -9
  266. package/build/agents/providers/codex-sdk.js +0 -329
  267. package/build/cli/commands/task/view/meta.js +0 -8
  268. package/build/core/command-io.js +0 -174
  269. package/build/core/path-coordinate.js +0 -27
  270. package/build/core/petition-claim.js +0 -117
  271. package/build/core/petition-run.js +0 -83
  272. package/build/core/projection-coordinate.js +0 -63
  273. package/build/core/projection-wait.js +0 -165
  274. package/build/core/projection-wake.js +0 -136
  275. package/build/core/queue.js +0 -343
  276. package/build/core/task-contract.js +0 -237
  277. package/build/core/task.js +0 -4
  278. /package/build/{core/task-repository.js → agents/launch-snapshot/types.js} +0 -0
  279. /package/build/{core/task-store.js → agents/providers/claude-agent-sdk/terms.js} +0 -0
  280. /package/build/core/{projection-generation-runtime.js → projection/generation/projection-generation-runtime.js} +0 -0
  281. /package/build/core/{projection-life-protocol.js → projection/projection-life-protocol.js} +0 -0
  282. /package/build/core/{projection-runner-lock.js → projection/projection-runner-lock.js} +0 -0
@@ -8,23 +8,27 @@ allowed-tools: Bash(keiyaku *)
8
8
 
9
9
  Planning notes that live in the repo. Executing them is `keiyaku-workflow` (`bind --task`).
10
10
 
11
- ## Mind model
11
+ ## Core ideas
12
12
 
13
- A task is a Markdown file in `.keiyaku/tasks/` — id `k-<12hex>`, priority 0–3 (0 highest, default 2), optional body. It has no branch and runs nothing; it's the queue you pull from. States:
13
+ A task is a tracked planning fact, not a worker or execution. Its immutable ID
14
+ is a normalized title slug with a numeric collision suffix when needed. It has
15
+ priority 0–3 (0 highest), optional body, and no branch of its own.
14
16
 
15
17
  ```
16
18
  open ⇄ in_progress → done | drop
17
19
  ```
18
20
 
19
- **Ready** = open + every `needs` dependency done. Ready is computed for you — `status` and `task ls` surface it.
21
+ **Ready** = open + every `needs` dependency satisfied. `needs` only orders
22
+ tasks; binding is what turns ready planning facts into one executable contract.
20
23
 
21
24
  ## Common usage
22
25
 
23
26
  ```bash
24
- keiyaku task add "fix the flaky pump test" # body: pipe stdin for details
27
+ keiyaku task add - < task.md # literal - reads the task body from stdin
25
28
  keiyaku task add "title" --pri 1 --needs k-aaa # priority + dependency at birth
26
- keiyaku task ls # all open, priority order
27
- keiyaku task view k-xxxx # full detail incl. needs/blocks
29
+ keiyaku task ls # all nonterminal tasks
30
+ keiyaku task ls --filter state=ready --sort priority,modified
31
+ keiyaku task show k-xxxx # full detail and parent-children tree
28
32
  keiyaku task start k-xxxx # mark in_progress (start ≠ execute)
29
33
  keiyaku task stop k-xxxx # back to open
30
34
  keiyaku task done k-xxxx
@@ -41,10 +45,15 @@ keiyaku task doctor # check board integrity
41
45
  ## Promoting to a contract
42
46
 
43
47
  ```bash
44
- keiyaku bind --task k-xxxx
48
+ keiyaku bind --task fix-parser
49
+ keiyaku bind --task fix-parser --task fix-parser-help - < contract.md
45
50
  ```
46
51
 
47
- Only a **ready** task binds. in_progress → `task stop` first; blocked → finish its needs first. Binding links the task to the contract; the contract's intent you still write yourself — the title doesn't auto-expand into a spec.
52
+ Only ready tasks bind. Multi-bind only when the tasks describe one delivery
53
+ intent, share a coherent write surface, and should settle together; otherwise
54
+ keep separate contracts and express order with task `needs` or contract
55
+ `--after`. The first task supplies name/objective defaults; provide a partial
56
+ bind document for any missing `Scope` or `Checks`.
48
57
 
49
58
  ## Gotcha
50
59
 
@@ -8,9 +8,15 @@ allowed-tools: Bash(keiyaku *)
8
8
 
9
9
  How a contract lives. Running akuma is the `keiyaku-akuma` skill; task planning is `keiyaku-task`.
10
10
 
11
- ## Mind model
11
+ ## Core ideas
12
12
 
13
- A contract = one branch + one worktree + a ledger (its record file in the repo). You work inside the worktree however you like; keiyaku only judges the result at petition. States:
13
+ - A contract is one delivery intent with one ledger, branch, and linked worktree.
14
+ - Workers edit and test; the host reviews their dirty tree and commits accepted bytes.
15
+ - `audit` reads one pinned candidate. `petition` is the settlement action.
16
+ - Scope is an audit boundary, not a write lock. Amend changed intent before delivery.
17
+ - An arc is a named story chapter for one coherent stretch inside the contract.
18
+
19
+ Lifecycle:
14
20
 
15
21
  ```
16
22
  bound → active → petitioned → claimed | forfeited
@@ -21,13 +27,21 @@ bound → active → petitioned → claimed | forfeited
21
27
  ```bash
22
28
  keiyaku bind --place fix-pump --objective "make the pump test pass" \
23
29
  --scope "src/pump/**" --checks "pump tests green"
24
- # or the same four fields as Markdown: keiyaku bind < contract.md
30
+ # or the same four fields as Markdown: keiyaku bind - < contract.md
25
31
  # (# <name> / ## Objective / ## Scope / ## Checks)
26
- # or promote a ready task: keiyaku bind --task k-xxxx
32
+ # or promote one or more ready tasks:
33
+ keiyaku bind --task fix-parser --task fix-parser-help - < contract.md
34
+
35
+ cd .keiyaku/wt/fix-pump # worker edits/tests here and leaves a dirty tree
27
36
 
28
- cd .keiyaku/wt/fix-pump # worktree lives at .keiyaku/wt/<place>; commit normally
37
+ # Back on the host after reviewing the worker's diff and test report:
38
+ git -C .keiyaku/wt/fix-pump add -- <accepted-paths>
39
+ git -C .keiyaku/wt/fix-pump commit -m "fix pump"
29
40
 
30
- keiyaku petition <<'EOF'
41
+ # Rehearse the exact pinned delivery before settlement:
42
+ keiyaku @fix-pump audit
43
+
44
+ keiyaku petition - <<'EOF'
31
45
  ## Oath
32
46
  I made the pump test pass; ran the suite, all green.
33
47
  EOF
@@ -37,21 +51,56 @@ EOF
37
51
  # The oath is required (OATH_MISSING otherwise) and checked for presence, not content.
38
52
  ```
39
53
 
40
- That's the whole small case: bind → commit → petition.
54
+ A task is optional planning, not a prerequisite. Bind directly for new work;
55
+ use `--task` only when promoting existing tracked planning into this contract.
56
+
57
+ That's the whole small case: bind → worker dirty-tree handoff → host review/commit → petition.
58
+
59
+ Multi-bind tasks only when they are evidence for the same delivery intent, share
60
+ one coherent write surface, and should settle together. This reduces competing
61
+ contracts/worktrees; it does not replace `needs` or contract `--after` ordering.
62
+
63
+ `audit` is read-only and never invokes an Akuma or settles the contract. Exit 0
64
+ means it constructed the report, not that the candidate is acceptable. Use
65
+ `--diff-budget BYTES` for bounded output and `--json` for complete typed facts.
66
+
67
+ Commissioned workers never write Git metadata. The host alone reviews and
68
+ commits accepted bytes before `arc`, `renew`, or `petition`.
41
69
 
42
70
  ## Mid-flight commands (use when needed, skip otherwise)
43
71
 
44
72
  ```bash
45
- keiyaku arc < arc.md # optional iteration boundary: seals the current intent, opens
73
+ keiyaku arc - < arc.md # optional iteration boundary: seals the current intent, opens
46
74
  # the next. Markdown: # <title> / ## Objective / ## Brief.
47
75
  # Only needed when one contract has multiple distinct pushes.
48
76
  keiyaku renew # main moved under you → rebase onto it. Refuses on conflict
49
77
  # instead of guessing; resolve, then renew again.
50
- keiyaku amend < amendment.md # scope/checks changed → update the paper before the code
78
+ keiyaku amend - < amendment.md # scope/checks changed → update the paper before the code
79
+ # Scope terms can be appended durably during a lifecycle operation:
80
+ keiyaku @fix-pump arc --append-scope "docs/generated/**" - < arc.md
81
+ keiyaku @fix-pump renew --append-scope "docs/generated/**"
51
82
  keiyaku log # this contract's history
52
83
  ```
53
84
 
85
+ `amend` accepts prose plus an optional ordered scope-pattern delta:
86
+
87
+ ````markdown
88
+ Clarify the owned documentation surface.
89
+
90
+ ## Scope Append
91
+ ```
92
+ docs/**
93
+ !docs/generated/**
94
+ ```
95
+ ````
96
+
97
+ `Scope Append` appends ordered raw gitignore-subset lines in one column-zero
98
+ backtick fence; `Scope Add` and Markdown bullets are rejected. A later
99
+ `!pattern` can narrow earlier scope. Use `--append-scope PATTERN` when a
100
+ lifecycle operation discovers one durable term.
101
+
54
102
  Gotchas:
103
+ - `-C DIR` selects the effective working directory; repository discovery starts there.
55
104
  - Base drift blocks petition — if main moved, `renew` first; petition tells you.
56
105
  - After an explicit `arc` seals, new loose commits need another `arc` before `renew`/`petition` accept them.
57
106
  - Not in the worktree? Address a contract with `--contract ADDR` (place, slug, or full id) or the `keiyaku @addr <verb>` prefix.
@@ -1,329 +0,0 @@
1
- import { z } from "zod";
2
- import { Codex, } from "@openai/codex-sdk";
3
- import { CODEX_MODEL_REASONING_EFFORT_CONFIG_KEY } from "../../keiyaku.js";
4
- import { appendDebugBlock, appendDebugLog } from "../../telemetry/debug-log.js";
5
- import { SUBAGENT_SESSION_VERSION } from "../session.js";
6
- import { createProjectionDriver } from "../harness/execution-handle.js";
7
- import { boundActivityExcerpt } from "../harness/activity-values.js";
8
- import { buildSubagentEnv, emitProviderEvidence, sha256Utf8 } from "../harness/runtime.js";
9
- import { coerceString } from "../harness/runtime.js";
10
- import { AGENT_PLAN_ITEM_TEXT_MAX_CHARS, AGENT_PLAN_MAX_ITEMS, AGENT_THOUGHT_TEXT_MAX_CHARS, } from "../harness/events.js";
11
- import { appendEventType, renderEventTypeSummary } from "./event-type-tail.js";
12
- /** Identity-only opacity boundary for Codex SDK ThreadOptions modelReasoningEffort. */
13
- function toCodexSdkModelReasoningEffort(effort) {
14
- return effort;
15
- }
16
- function withCodexSdkModelReasoningEffort(threadOptions) {
17
- const raw = threadOptions.modelReasoningEffort;
18
- if (typeof raw !== "string")
19
- return threadOptions;
20
- return {
21
- ...threadOptions,
22
- modelReasoningEffort: toCodexSdkModelReasoningEffort(raw),
23
- };
24
- }
25
- let codexConstructor = Codex;
26
- function getCodexConstructor(override) {
27
- if (override)
28
- return override;
29
- return codexConstructor;
30
- }
31
- /** @deprecated Use CodexSdkOptions.codexConstructor instead. */
32
- export function setCodexSdkConstructorForTests(constructor) {
33
- codexConstructor = constructor;
34
- }
35
- /** @deprecated Use CodexSdkOptions.codexConstructor instead. */
36
- export function resetCodexSdkConstructorForTests() {
37
- codexConstructor = Codex;
38
- }
39
- export const codexThreadSchema = z.object({ threadId: z.string().trim().min(1) });
40
- const CODEX_EVENT_THREAD_STARTED = "thread.started";
41
- const CODEX_EVENT_TURN_STARTED = "turn.started";
42
- const CODEX_EVENT_TURN_FAILED = "turn.failed";
43
- const CODEX_EVENT_ERROR = "error";
44
- const CODEX_EVENT_ITEM_STARTED = "item.started";
45
- const CODEX_EVENT_ITEM_COMPLETED = "item.completed";
46
- const CODEX_EVENT_ITEM_UPDATED = "item.updated";
47
- const CODEX_EVENT_TURN_COMPLETED = "turn.completed";
48
- const CODEX_SDK_PROVIDER = "codex-sdk";
49
- const CODEX_SDK_ACKNOWLEDGED_UNPERSISTED = new Set([CODEX_EVENT_THREAD_STARTED]);
50
- const CODEX_SDK_MAPPED = new Set([
51
- CODEX_EVENT_TURN_STARTED,
52
- CODEX_EVENT_TURN_COMPLETED,
53
- CODEX_EVENT_TURN_FAILED,
54
- CODEX_EVENT_ERROR,
55
- CODEX_EVENT_ITEM_STARTED,
56
- CODEX_EVENT_ITEM_UPDATED,
57
- CODEX_EVENT_ITEM_COMPLETED,
58
- ]);
59
- function codexSdkIngressDisposition(type) {
60
- if (CODEX_SDK_ACKNOWLEDGED_UNPERSISTED.has(type))
61
- return "acknowledged-unpersisted";
62
- if (CODEX_SDK_MAPPED.has(type))
63
- return "mapped";
64
- return "unknown";
65
- }
66
- function extractAgentMessage(event) {
67
- if (event.type !== CODEX_EVENT_ITEM_COMPLETED && event.type !== CODEX_EVENT_ITEM_UPDATED) {
68
- return undefined;
69
- }
70
- if (event.item.type !== "agent_message")
71
- return undefined;
72
- const text = coerceString(event.item.text);
73
- if (!text)
74
- return undefined;
75
- return { id: event.item.id, text };
76
- }
77
- function codexActivityCall(item) {
78
- switch (item.type) {
79
- case "command_execution": return { kind: "run", command: item.command };
80
- case "file_change": return {
81
- kind: "fileChange",
82
- changes: item.changes.map((change) => ({ op: change.kind, path: change.path })),
83
- };
84
- case "mcp_tool_call": return { kind: "other", display: `${item.server}.${item.tool}` };
85
- case "web_search": return { kind: "search", query: item.query };
86
- }
87
- }
88
- function codexMcpResultMessage(item) {
89
- if (item.error?.message)
90
- return boundActivityExcerpt(item.error.message);
91
- const text = item.result?.content.flatMap((block) => (block.type === "text" && typeof block.text === "string" ? [block.text] : [])).join("\n");
92
- return text ? boundActivityExcerpt(text) : undefined;
93
- }
94
- function codexActivityResult(item) {
95
- switch (item.type) {
96
- case "command_execution": return {
97
- status: item.status === "failed" || (item.exit_code !== undefined && item.exit_code !== 0) ? "error" : "ok",
98
- ...(item.aggregated_output ? { message: boundActivityExcerpt(item.aggregated_output) } : {}),
99
- ...(item.exit_code === undefined ? {} : { exitCode: item.exit_code }),
100
- };
101
- case "file_change": return { status: item.status === "failed" ? "error" : "ok" };
102
- case "mcp_tool_call": {
103
- const message = codexMcpResultMessage(item);
104
- return { status: item.status === "failed" ? "error" : "ok", ...(message ? { message } : {}) };
105
- }
106
- case "web_search": return { status: "ok" };
107
- }
108
- }
109
- function emitCodexSemanticEvent(builder, event) {
110
- if (event.type === CODEX_EVENT_TURN_STARTED) {
111
- builder.emit({ type: "turn", phase: "started" });
112
- return;
113
- }
114
- if (event.type === CODEX_EVENT_TURN_COMPLETED) {
115
- builder.emit({
116
- type: "usage",
117
- inputTokens: event.usage.input_tokens,
118
- outputTokens: event.usage.output_tokens,
119
- cachedInputTokens: event.usage.cached_input_tokens,
120
- });
121
- builder.emit({ type: "turn", phase: "completed" });
122
- return;
123
- }
124
- if (event.type !== CODEX_EVENT_ITEM_STARTED &&
125
- event.type !== CODEX_EVENT_ITEM_UPDATED &&
126
- event.type !== CODEX_EVENT_ITEM_COMPLETED) {
127
- return;
128
- }
129
- if (event.item.type === "reasoning" &&
130
- (event.type === CODEX_EVENT_ITEM_UPDATED || event.type === CODEX_EVENT_ITEM_COMPLETED)) {
131
- const truncated = event.item.text.length > AGENT_THOUGHT_TEXT_MAX_CHARS;
132
- builder.emit({
133
- type: "thought",
134
- id: event.item.id,
135
- text: event.item.text.slice(0, AGENT_THOUGHT_TEXT_MAX_CHARS),
136
- final: event.type === CODEX_EVENT_ITEM_COMPLETED,
137
- ...(truncated ? { truncated: true } : {}),
138
- });
139
- return;
140
- }
141
- if (event.item.type === "todo_list") {
142
- builder.emit({
143
- type: "plan",
144
- id: event.item.id,
145
- items: event.item.items.slice(0, AGENT_PLAN_MAX_ITEMS).map((item) => ({
146
- text: item.text.slice(0, AGENT_PLAN_ITEM_TEXT_MAX_CHARS),
147
- done: item.completed,
148
- })),
149
- });
150
- return;
151
- }
152
- if (event.item.type !== "command_execution" &&
153
- event.item.type !== "file_change" &&
154
- event.item.type !== "mcp_tool_call" &&
155
- event.item.type !== "web_search") {
156
- return;
157
- }
158
- const phase = event.type === CODEX_EVENT_ITEM_STARTED
159
- ? "started"
160
- : event.type === CODEX_EVENT_ITEM_UPDATED
161
- ? "updated"
162
- : "completed";
163
- builder.emit({
164
- type: "activity",
165
- phase,
166
- id: event.item.id,
167
- tool: event.item.type,
168
- call: codexActivityCall(event.item),
169
- ...(phase === "started" ? {} : { result: codexActivityResult(event.item) }),
170
- });
171
- }
172
- /** @deprecated Use the codex-app-server adapter. */
173
- export async function startCodexSdk(prompt, cwd, options) {
174
- const threadId = options.threadId?.trim();
175
- const recordedEventTypes = [];
176
- let totalEventCount = 0;
177
- let activeAbort;
178
- let runReturned = false;
179
- appendDebugLog(`startCodexSdk: executable=${options.executable ?? "codex"} model=${options.model ?? "default"} ${CODEX_MODEL_REASONING_EFFORT_CONFIG_KEY}=${String(options.config?.[CODEX_MODEL_REASONING_EFFORT_CONFIG_KEY] ?? "default")} mode=${threadId ? "resume" : "fresh"}`, { cwd, section: CODEX_SDK_PROVIDER });
180
- return createProjectionDriver({
181
- steerMode: "replay",
182
- initialTurn: options.initialTurn,
183
- onIngress: options.onIngress,
184
- async abort(mode) {
185
- activeAbort?.abort();
186
- if (mode === "force" && !runReturned) {
187
- throw new Error("codex-sdk cannot guarantee forced termination");
188
- }
189
- },
190
- async run(builder) {
191
- activeAbort = new AbortController();
192
- const linked = options.signal ?? builder.signal;
193
- const onOuterAbort = () => activeAbort?.abort();
194
- linked.addEventListener("abort", onOuterAbort, { once: true });
195
- if (linked.aborted)
196
- onOuterAbort();
197
- builder.signal.addEventListener("abort", onOuterAbort, { once: true });
198
- try {
199
- const env = buildSubagentEnv();
200
- const codexOptions = {
201
- codexPathOverride: options.executable,
202
- profile: options.profile,
203
- config: options.config,
204
- env,
205
- };
206
- const sdk = new (getCodexConstructor(options.codexConstructor))(codexOptions);
207
- const threadOptions = withCodexSdkModelReasoningEffort(options.threadOptions);
208
- const thread = threadId ? sdk.resumeThread(threadId, threadOptions) : sdk.startThread(threadOptions);
209
- const streamed = await thread.runStreamed(prompt, { signal: activeAbort.signal });
210
- const eventIterator = streamed.events[Symbol.asyncIterator]();
211
- // The SDK iterable is lazy: its first next() starts the native run.
212
- // Record outbound bytes immediately before that crossing, then ack only
213
- // after it settles (including a legitimate no-event completion).
214
- if (options.onEvidence) {
215
- emitProviderEvidence(options.onEvidence, {
216
- type: "outbound",
217
- provider: CODEX_SDK_PROVIDER,
218
- direction: "input",
219
- kind: "replay-prompt",
220
- ...sha256Utf8(prompt),
221
- }, (text) => builder.emit({ type: "diagnostic", text }));
222
- }
223
- const firstEvent = await eventIterator.next();
224
- if (options.onEvidence) {
225
- emitProviderEvidence(options.onEvidence, {
226
- type: "ack",
227
- provider: CODEX_SDK_PROVIDER,
228
- fence: { kind: "ordinal", ordinal: 0 },
229
- outbound: "run",
230
- }, (text) => builder.emit({ type: "diagnostic", text }));
231
- }
232
- const events = async function* () {
233
- if (!firstEvent.done)
234
- yield firstEvent.value;
235
- while (true) {
236
- const next = await eventIterator.next();
237
- if (next.done)
238
- return;
239
- yield next.value;
240
- }
241
- };
242
- let resumeToken;
243
- let lastAgentMessage;
244
- let turnFailedMessage;
245
- let streamErrorMessage;
246
- for await (const event of events()) {
247
- if (activeAbort.signal.aborted) {
248
- throw Object.assign(new Error(`${CODEX_SDK_PROVIDER} exec cancelled by client`), { name: "AbortError" });
249
- }
250
- totalEventCount += 1;
251
- appendEventType(recordedEventTypes, event.type);
252
- builder.ingress(CODEX_SDK_PROVIDER, event.type, event, codexSdkIngressDisposition(event.type), () => {
253
- if (event.type === CODEX_EVENT_THREAD_STARTED) {
254
- resumeToken = event.thread_id;
255
- if (options.onEvidence)
256
- emitProviderEvidence(options.onEvidence, { type: "session", provider: CODEX_SDK_PROVIDER, threadId: resumeToken }, (text) => builder.emit({ type: "diagnostic", text }));
257
- }
258
- else if (event.type === CODEX_EVENT_TURN_STARTED && !resumeToken) {
259
- builder.emit({
260
- type: "fail",
261
- message: `${CODEX_SDK_PROVIDER} exec emitted turn.started before thread.started with thread_id`,
262
- });
263
- throw new Error(`${CODEX_SDK_PROVIDER} exec emitted turn.started before thread.started with thread_id`);
264
- }
265
- else if (event.type === CODEX_EVENT_TURN_FAILED) {
266
- turnFailedMessage = coerceString(event.error.message) ?? `${CODEX_SDK_PROVIDER} turn failed`;
267
- }
268
- else if (event.type === CODEX_EVENT_ERROR) {
269
- streamErrorMessage = coerceString(event.message) ?? `${CODEX_SDK_PROVIDER} stream error`;
270
- builder.emit({ type: "diagnostic", text: `${streamErrorMessage}\n` });
271
- }
272
- emitCodexSemanticEvent(builder, event);
273
- const message = extractAgentMessage(event);
274
- if (message) {
275
- lastAgentMessage = message.text;
276
- builder.emit({ type: "message", id: message.id, text: message.text });
277
- }
278
- });
279
- }
280
- if (turnFailedMessage) {
281
- builder.emit({ type: "diagnostic", text: turnFailedMessage });
282
- builder.emit({ type: "fail", message: turnFailedMessage });
283
- throw new Error(turnFailedMessage);
284
- }
285
- if (streamErrorMessage && !lastAgentMessage) {
286
- builder.emit({ type: "fail", message: streamErrorMessage });
287
- throw new Error(streamErrorMessage);
288
- }
289
- if (!resumeToken) {
290
- appendDebugLog(`Missing required codex event: ${CODEX_EVENT_THREAD_STARTED} with thread_id.`, {
291
- cwd,
292
- section: CODEX_SDK_PROVIDER,
293
- });
294
- appendDebugBlock("codex-sdk stdout event types", renderEventTypeSummary(recordedEventTypes, totalEventCount), {
295
- cwd,
296
- section: CODEX_SDK_PROVIDER,
297
- });
298
- builder.emit({
299
- type: "fail",
300
- message: `${CODEX_SDK_PROVIDER} exec succeeded but did not emit a ${CODEX_EVENT_THREAD_STARTED} event with thread_id`,
301
- });
302
- throw new Error(`${CODEX_SDK_PROVIDER} exec succeeded but did not emit a ${CODEX_EVENT_THREAD_STARTED} event with thread_id`);
303
- }
304
- const finalMessage = lastAgentMessage ?? "";
305
- const session = {
306
- provider: "codex-sdk",
307
- version: SUBAGENT_SESSION_VERSION,
308
- handle: { threadId: resumeToken },
309
- };
310
- if (options.onEvidence)
311
- emitProviderEvidence(options.onEvidence, { type: "checkpoint", provider: CODEX_SDK_PROVIDER, fence: { kind: "ordinal", ordinal: 1 }, source: "response" }, (text) => builder.emit({ type: "diagnostic", text }));
312
- builder.emit({
313
- type: "done",
314
- finalMessage,
315
- session,
316
- });
317
- return { finalMessage, session };
318
- }
319
- catch (error) {
320
- appendDebugLog(`startCodexSdk error: ${error instanceof Error ? `${error.name}: ${error.message}` : String(error)}`, { cwd, section: CODEX_SDK_PROVIDER });
321
- throw error;
322
- }
323
- finally {
324
- runReturned = true;
325
- linked.removeEventListener("abort", onOuterAbort);
326
- }
327
- },
328
- });
329
- }
@@ -1,8 +0,0 @@
1
- export const meta = {
2
- command: "task view",
3
- summary: "View one task",
4
- usage: ["keiyaku task view [-C|--cwd DIR] ID [--json]"],
5
- stdin: "none",
6
- flags: ["cwd", "json"],
7
- purpose: "Read one task from the working-tree board.",
8
- };
@@ -1,174 +0,0 @@
1
- import { ACTOR_IDENTITY } from "../keiyaku.js";
2
- import { FlowError } from "../flow-error.js";
3
- import { parseMarkdownListSection, parseMarkdownStructure } from "./markdown/sections.js";
4
- import { KeiyakuParseError } from "./markdown/types.js";
5
- import { normalizeSectionTitle } from "./markdown/titles.js";
6
- import { isBlankScopePatternLine, normalizeScopePattern, ScopePatternError } from "./scope.js";
7
- function amendScopeHeading(name) {
8
- return `## Scope ${name[0].toUpperCase()}${name.slice(1)}`;
9
- }
10
- function sourceLines(markdown) {
11
- const lines = [];
12
- let start = 0;
13
- while (start < markdown.length) {
14
- const newline = markdown.indexOf("\n", start);
15
- const end = newline === -1 ? markdown.length : newline;
16
- const next = newline === -1 ? markdown.length : newline + 1;
17
- const raw = markdown.slice(start, end);
18
- lines.push({ text: raw.endsWith("\r") ? raw.slice(0, -1) : raw, start, end, next });
19
- start = next;
20
- }
21
- return lines;
22
- }
23
- function amendHeading(line) {
24
- const match = line.match(/^ {0,3}(#{1,2})(?!#)[ \t]+(.+)$/);
25
- if (!match)
26
- return null;
27
- const level = match[1].length;
28
- const title = normalizeSectionTitle(match[2]);
29
- if (level !== 2)
30
- return { level };
31
- if (title === "scope add")
32
- return { level, name: "add" };
33
- return { level };
34
- }
35
- function amendFence(line) {
36
- const match = line.match(/^ {0,3}(`{3,}|~{3,})/);
37
- if (!match)
38
- return null;
39
- const marker = match[1];
40
- return { delimiter: marker[0], length: marker.length };
41
- }
42
- function parseAmendScopeSections(markdown) {
43
- const lines = sourceLines(markdown);
44
- const sections = {};
45
- let active;
46
- let fence;
47
- let frontMatterEnd = -1;
48
- if (lines[0]?.text.trim() === "---") {
49
- for (let index = 1; index < lines.length; index += 1) {
50
- if (lines[index].text.trim() === "---") {
51
- frontMatterEnd = index;
52
- break;
53
- }
54
- }
55
- }
56
- const finishActive = (end) => {
57
- if (!active)
58
- return;
59
- sections[active.name] = { ...active, end };
60
- active = undefined;
61
- };
62
- for (let index = 0; index < lines.length; index += 1) {
63
- const line = lines[index];
64
- if (index <= frontMatterEnd)
65
- continue;
66
- const marker = amendFence(line.text);
67
- if (fence) {
68
- if (marker && marker.delimiter === fence.delimiter && marker.length >= fence.length) {
69
- fence = undefined;
70
- }
71
- continue;
72
- }
73
- if (marker) {
74
- fence = marker;
75
- continue;
76
- }
77
- const heading = amendHeading(line.text);
78
- if (!heading)
79
- continue;
80
- finishActive(line.start);
81
- if (!heading.name)
82
- continue;
83
- if (sections[heading.name]) {
84
- throw new FlowError("INVALID_KEIYAKU_DRAFT", `amend input may contain ${amendScopeHeading(heading.name)} at most once`);
85
- }
86
- active = { name: heading.name, start: line.start, contentStart: line.next };
87
- }
88
- finishActive(markdown.length);
89
- let prose = markdown;
90
- for (const section of Object.values(sections).sort((left, right) => right.start - left.start)) {
91
- prose = `${prose.slice(0, section.start)}${prose.slice(section.end)}`;
92
- }
93
- return { sections, prose };
94
- }
95
- function scopePatterns(markdown, section) {
96
- const patterns = markdown
97
- .slice(section.contentStart, section.end)
98
- .split(/\r?\n/)
99
- .filter((line) => !isBlankScopePatternLine(line))
100
- .map((pattern) => {
101
- try {
102
- // Do not trim() here: gitignore treats trailing `\ ` as a significant space.
103
- return normalizeScopePattern(pattern);
104
- }
105
- catch (error) {
106
- if (error instanceof ScopePatternError) {
107
- throw new FlowError("INVALID_KEIYAKU_DRAFT", error.message);
108
- }
109
- throw error;
110
- }
111
- });
112
- if (patterns.length === 0) {
113
- throw new FlowError("INVALID_KEIYAKU_DRAFT", `amend input has an empty ${amendScopeHeading(section.name)} section`);
114
- }
115
- return patterns;
116
- }
117
- export function buildAmendInput(markdown, cwd, contractId, contractAddressSource) {
118
- const { sections, prose } = parseAmendScopeSections(markdown);
119
- const add = sections.add ? scopePatterns(markdown, sections.add) : [];
120
- if (!prose.trim()) {
121
- throw new FlowError("EMPTY_PARAM", "amendment cannot be empty");
122
- }
123
- return {
124
- cwd,
125
- ...(contractId ? { contractId } : {}),
126
- ...(contractAddressSource ? { contractAddressSource } : {}),
127
- amendment: prose,
128
- ...(sections.add ? { scopeDelta: { add } } : {}),
129
- };
130
- }
131
- export function defaultActorClock(nowMs = Date.now()) {
132
- return { actor: ACTOR_IDENTITY, nowMs };
133
- }
134
- export function parseCommandMarkdown(content) {
135
- try {
136
- const parsed = parseMarkdownStructure(content);
137
- const sections = new Map();
138
- for (const [key, lines] of parsed.sections.entries()) {
139
- sections.set(key, lines.join("\n").trim());
140
- }
141
- return { title: parsed.title, sections };
142
- }
143
- catch (error) {
144
- if (error instanceof KeiyakuParseError) {
145
- throw new FlowError("INVALID_KEIYAKU_DRAFT", error.message, { cause: error });
146
- }
147
- throw error;
148
- }
149
- }
150
- export function requireMarkdownTextSection(parts, name) {
151
- const normalized = normalizeSectionTitle(name);
152
- const value = parts.sections.get(normalized)?.trim();
153
- if (!value) {
154
- throw new FlowError("INVALID_KEIYAKU_DRAFT", `missing required section: ## ${name}`);
155
- }
156
- return value;
157
- }
158
- export function requireMarkdownListSection(parts, name) {
159
- const text = requireMarkdownTextSection(parts, name);
160
- const items = parseMarkdownListSection(text)
161
- .map((item) => item.trim())
162
- .filter((item) => item.length > 0);
163
- if (items.length === 0) {
164
- throw new FlowError("INVALID_KEIYAKU_DRAFT", `missing required section: ## ${name}`);
165
- }
166
- return items;
167
- }
168
- export function requireMarkdownTitle(parts) {
169
- const title = parts.title?.trim();
170
- if (!title) {
171
- throw new FlowError("INVALID_KEIYAKU_DRAFT", "missing required title: # Title");
172
- }
173
- return title;
174
- }