@tea-agent/loop-agent 0.43.0-next.17 → 0.43.0-next.19

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 (236) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +68 -2309
  3. package/README.md +7 -0
  4. package/dist/application/dag/args.js +8 -0
  5. package/dist/application/task-lifecycle/advance.js +38 -7
  6. package/dist/build-stamp.json +3 -3
  7. package/dist/cli/command-definitions.js +2 -0
  8. package/dist/cli/program.js +3 -1
  9. package/dist/cli/update/init-surface-notifier.js +2 -0
  10. package/dist/cli/update/policy.js +2 -0
  11. package/dist/commands/cursor-prompt.js +2 -0
  12. package/dist/commands/dag-approve.js +2 -0
  13. package/dist/commands/dag-final-verification.js +2 -0
  14. package/dist/commands/dag-init-hybrid.js +2 -0
  15. package/dist/commands/dag-reconcile-tasks.js +2 -0
  16. package/dist/commands/dag-reject.js +2 -0
  17. package/dist/commands/dag-report.js +2 -0
  18. package/dist/commands/dag-rerun.js +2 -0
  19. package/dist/commands/dag-resume.js +2 -0
  20. package/dist/commands/dag-workflow-compile.js +2 -0
  21. package/dist/commands/dag-workflow-plan.js +2 -0
  22. package/dist/commands/dag-workflow-validate.js +2 -0
  23. package/dist/commands/delegate.js +2 -0
  24. package/dist/commands/examples.js +2 -0
  25. package/dist/commands/import-prd.js +2 -0
  26. package/dist/commands/init-model-catalog.js +2 -0
  27. package/dist/commands/init.js +19 -13
  28. package/dist/commands/instructions.js +2 -0
  29. package/dist/commands/knowledge.js +2 -0
  30. package/dist/commands/loop-benchmark.js +2 -0
  31. package/dist/commands/pi-prompt.js +2 -0
  32. package/dist/commands/pi-reuse-benchmark.js +2 -0
  33. package/dist/commands/promote-run.js +2 -0
  34. package/dist/commands/stats.js +1 -1
  35. package/dist/commands/study-init.js +2 -0
  36. package/dist/commands/task-advance.js +34 -0
  37. package/dist/commands/task-source-prepare.js +2 -0
  38. package/dist/commands/worktree-create.js +2 -0
  39. package/dist/commands/worktree-remove.js +2 -0
  40. package/dist/executors/dag-pi-executor.js +1640 -194
  41. package/dist/executors/pi-executor.js +85 -3
  42. package/dist/executors/pi-sdk-executor.js +18 -0
  43. package/dist/executors/shell-executor.js +92 -7
  44. package/dist/executors/shell-verification.js +3 -0
  45. package/dist/governance/exec-plans.js +6 -3
  46. package/dist/infrastructure/console/app-data.js +6 -0
  47. package/dist/shared/artifacts-core.js +2 -0
  48. package/dist/shared/backend-dogfood-preflight.js +47 -0
  49. package/dist/shared/dag-failure-category.js +3 -0
  50. package/dist/shared/git-progress.js +2 -0
  51. package/dist/shared/one-shot-prompt-args.js +2 -0
  52. package/dist/shared/operator/capabilities.js +180 -0
  53. package/dist/shared/package-metadata.js +6 -4
  54. package/dist/shared/pi-context-pressure/sift-bridge.js +1 -1
  55. package/dist/shared/reference-context.js +2 -0
  56. package/dist/task/config-types.js +29 -0
  57. package/dist/task/contract/project.js +9 -0
  58. package/dist/task/contract/recover.js +6 -0
  59. package/dist/task/contract/schema.js +17 -0
  60. package/dist/task/source-prepare/artifact-meta.js +9 -2
  61. package/dist/task/source-prepare/build-draft.js +60 -0
  62. package/dist/task/source-prepare/fragment-inventory.js +4 -1
  63. package/dist/task/source-prepare/parse-intent.js +36 -11
  64. package/dist/task/task-demand-routing.js +5 -2
  65. package/dist/worker/console/chat/browser-automation.js +468 -308
  66. package/dist/worker/console/chat/browser-routes.js +175 -57
  67. package/dist/worker/console/chat/explore-tools.js +5 -2
  68. package/dist/worker/console/chat/pi-mode-loop-isolation.js +155 -0
  69. package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +115 -20
  70. package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +24 -16
  71. package/dist/worker/console/chat/pi-runtime/custom-tools/scheduled-goal-tools.js +22 -0
  72. package/dist/worker/console/chat/pi-runtime/progressive-tools.js +195 -0
  73. package/dist/worker/console/chat/pi-runtime.js +95 -28
  74. package/dist/worker/console/chat/routes.js +20 -1
  75. package/dist/worker/console/chat/scheduled-goal-booking.js +59 -0
  76. package/dist/worker/console/chat/scheduled-goal-delivery.js +27 -0
  77. package/dist/worker/console/chat/scheduled-goal-request.js +190 -0
  78. package/dist/worker/console/chat/sdd-data-alignment.js +1 -1
  79. package/dist/worker/console/chat/session-mode.js +8 -4
  80. package/dist/worker/console/chat/session-store.js +42 -9
  81. package/dist/worker/console/chat/tool-preview.js +115 -0
  82. package/dist/worker/console/chat/tools.js +7 -1
  83. package/dist/worker/console/chat/turn-order.js +13 -0
  84. package/dist/worker/console/chat/turn-process.js +32 -30
  85. package/dist/worker/console/operator-actions.js +50 -0
  86. package/dist/worker/console/prd-intake-bridge.js +54 -1
  87. package/dist/worker/console/scheduled-goal-host.js +98 -0
  88. package/dist/worker/console/scheduled-goal-operation-adapter.js +127 -0
  89. package/dist/worker/console/server.js +15 -0
  90. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4NXyedy.js → abnfDiagram-N423BO3Z-8-j6y-sd.js} +1 -1
  91. package/dist/worker/console/static/assets/{arc-NZ3qXvBI.js → arc-eoQiMvuk.js} +1 -1
  92. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-COZSRvml.js → architectureDiagram-T3A2C74G-C4A3uMcI.js} +1 -1
  93. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Ck0Gxi9C.js → blockDiagram-VBNYF7ZC-D4zD2F-Q.js} +1 -1
  94. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BX5HQ4Ox.js → c4Diagram-5PPSVZJV-j1RkJziL.js} +1 -1
  95. package/dist/worker/console/static/assets/channel-ChE7y-cx.js +1 -0
  96. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-CoviemSL.js → chunk-2GRJ4B5K-YBHmhik1.js} +1 -1
  97. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DbOdOkbZ.js → chunk-2Q5K7J3B-COfWyo9P.js} +1 -1
  98. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BsG2pmdQ.js → chunk-5RXB4S5H-I99OUkHY.js} +1 -1
  99. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-DGdvrNm8.js → chunk-5VM5RSS4-XKxoJNJ4.js} +1 -1
  100. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BAy7Yd6y.js → chunk-6Q2QTUOP-DLT_cYx1.js} +1 -1
  101. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-D9UA7hUb.js → chunk-GF5L2VYU-CxcKZ9mV.js} +1 -1
  102. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-DPz7SZ8V.js → chunk-JWPE2WC7-V1EqXdjY.js} +1 -1
  103. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BmuEOIh7.js → chunk-KBJHAD2P-DebTtdFp.js} +1 -1
  104. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CIhBWnd8.js → chunk-RYQCIY6F-B-ivNRDf.js} +1 -1
  105. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CScR6mDD.js → chunk-XXDRQBXY-DC_Ds11b.js} +1 -1
  106. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +1 -0
  107. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +1 -0
  108. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BjpfWaUr.js → cose-bilkent-JH36ORCC-sWEqKwIb.js} +1 -1
  109. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BFQohJgT.js → cynefin-VYW2F7L2-BXa_dcu4.js} +1 -1
  110. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-D3gzRJnS.js → cynefinDiagram-MW4NZA55-C-Mle84F.js} +1 -1
  111. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-5eojkhoA.js → dagre-VZM6K2ZE-CXPDBITe.js} +1 -1
  112. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CpD8LWu_.js → diagram-7IWD3JNH-CC-WJQfa.js} +1 -1
  113. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B8_4qJ2a.js → diagram-B4RE2ZJO-CDAV5Vs4.js} +1 -1
  114. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-HOsXf0eu.js → diagram-LBJQPF4R-CmFiAcNz.js} +1 -1
  115. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-BqwRwiWP.js → diagram-Q27KOJAE-DPBZHuyn.js} +1 -1
  116. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BRVCNZe1.js → diagram-UB23O5K3-jUlm_Ds3.js} +1 -1
  117. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Cvb1TgGh.js → ebnfDiagram-BXEA7PRR-DWhQ3mfY.js} +1 -1
  118. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DS0O0qG9.js → erDiagram-JOGREHBK-Tr2gMqet.js} +1 -1
  119. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-BHIRvRr3.js → flowDiagram-UKHOOZJN-DJjVQHPA.js} +1 -1
  120. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DcCJ1JWx.js → ganttDiagram-PKOTCBZU-D74dQ4u0.js} +1 -1
  121. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-B9ia5ony.js → gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js} +1 -1
  122. package/dist/worker/console/static/assets/index-BWkIfcrK.css +1 -0
  123. package/dist/worker/console/static/assets/{index-wXlQohNs.js → index-Cdkvw_H6.js} +97 -97
  124. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-3ogpgegn.js → infoDiagram-6WML65LV-ILCbxJyb.js} +1 -1
  125. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-4tgpQ5SL.js → ishikawaDiagram-WSZJBQD7-DeuWBQ89.js} +1 -1
  126. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DNfgHqFx.js → journeyDiagram-NVQOT4AX-CF5ih8Fk.js} +1 -1
  127. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Dz7ngC8z.js → kanban-definition-27J2QSJJ-C7yOSuRO.js} +1 -1
  128. package/dist/worker/console/static/assets/{linear-DI7Jv62E.js → linear-BDZ9riWi.js} +1 -1
  129. package/dist/worker/console/static/assets/{mermaid.core-CQA4Nzmo.js → mermaid.core-7pKqYtpZ.js} +5 -5
  130. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CtsMc8Mf.js → mindmap-definition-FAOFIHXS-LeJDybSU.js} +1 -1
  131. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-ByppE9yH.js → pegDiagram-VL7TDLO6-BJvT3pMD.js} +1 -1
  132. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-B5PtgFgt.js → pieDiagram-7S7Q4E2Y-_rGqpMan.js} +1 -1
  133. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Cq4p0k2K.js → quadrantDiagram-CIZ2JOQS-DPcSv5aA.js} +1 -1
  134. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-DjfkyljN.js → railroadDiagram-AXF67PYL-Drxx4hkJ.js} +1 -1
  135. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DzynNRSl.js → requirementDiagram-LRYGKXZP-BCTUdU4z.js} +1 -1
  136. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Cj7TCpTJ.js → sankeyDiagram-W5VNT64P-B6wzbZmB.js} +1 -1
  137. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-C73C6ykA.js → sequenceDiagram-SI44F4Z6-BGt8d3QQ.js} +1 -1
  138. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-BiNLgN2x.js → sizeCapture-X5ZJPWSS-7nlhJKo7.js} +1 -1
  139. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-UwvjS64-.js → stateDiagram-OKZ733FA-Cv2stqMA.js} +1 -1
  140. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +1 -0
  141. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CC7DH98r.js → swimlanes-SLNWSIFB-qDAo4Yc1.js} +2 -2
  142. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +8 -0
  143. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-LSBXTR70.js → timeline-definition-Z64GVDOM-CKbDNKTr.js} +1 -1
  144. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BAuxWwGP.js → vennDiagram-T6HMQDX7-DI-9EHic.js} +1 -1
  145. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y--E5_gjZx.js → wardleyDiagram-T6FBY63Y-BrzzRDpX.js} +1 -1
  146. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-dJWNmyzF.js → xychartDiagram-ELKLHX3M-BclH5hGh.js} +1 -1
  147. package/dist/worker/console/static/index.html +2 -2
  148. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +41 -11
  149. package/dist/worker/console/static-src/operator-chat/compaction-message.js +3 -17
  150. package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +15 -5
  151. package/dist/worker/console/static-src/operator-chat/timeline-merge.js +29 -0
  152. package/dist/worker/console/static-src/operator-chat/useChatThread.js +2 -5
  153. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +2 -0
  154. package/dist/worker/console/static-src/prd-file-import.js +3 -1
  155. package/dist/worker/console/workspace-context.js +17 -1
  156. package/dist/worker/delivery/git-transaction.js +2 -0
  157. package/dist/worker/feature/fullstack-validate.js +2 -0
  158. package/dist/worker/loop-agent/loop-agent-client.js +8 -2
  159. package/dist/worker/materialize/harness-task-materializer.js +4 -1
  160. package/dist/worker/metrics/projector.js +1 -1
  161. package/dist/worker/observability/event-store.js +4 -1
  162. package/dist/worker/observability/read-model.js +4 -1
  163. package/dist/worker/observe/node-transparency.js +23 -1
  164. package/dist/worker/observe/routes.js +4 -0
  165. package/dist/worker/observe/static/operator-chrome.js +3 -1
  166. package/dist/worker/outcomes/declared-artifacts.js +2 -0
  167. package/dist/worker/preflight.js +3 -0
  168. package/dist/worker/scheduler/scheduled-goal-dispatch.js +117 -0
  169. package/dist/worker/scheduler/scheduled-goal-evidence.js +167 -0
  170. package/dist/worker/scheduler/scheduled-goal-recovery.js +62 -0
  171. package/dist/worker/scheduler/scheduled-goal-store.js +699 -0
  172. package/dist/worker/scheduler/scheduled-goal-supervisor.js +132 -0
  173. package/dist/worker/scheduler/scheduled-goal-time.js +102 -0
  174. package/dist/worker/scheduler/scheduled-goal-types.js +95 -0
  175. package/dist/worker/task-spec/validate.js +4 -1
  176. package/dist/workflows/dag/backend-test-markdown-workflow.js +6 -24
  177. package/dist/workflows/dag/backend-test-plan-protocol.js +82 -5
  178. package/dist/workflows/dag/dag-retry-schema.js +11 -0
  179. package/dist/workflows/dag/decision-envelope.js +2 -0
  180. package/dist/workflows/dag/dynamic-runtime/shared.js +2 -2
  181. package/dist/workflows/dag/frontend-closeout.js +4 -2
  182. package/dist/workflows/dag/frontend-committed-facts.js +461 -0
  183. package/dist/workflows/dag/frontend-durable-tools.js +15 -3
  184. package/dist/workflows/dag/frontend-implementation-contract.js +365 -8
  185. package/dist/workflows/dag/frontend-plan-canary.js +53 -0
  186. package/dist/workflows/dag/frontend-plan-decision-contract.js +803 -0
  187. package/dist/workflows/dag/frontend-plan-render.js +0 -2
  188. package/dist/workflows/dag/frontend-prewrite-gate.js +3 -3
  189. package/dist/workflows/dag/frontend-provider-capability-matrix.js +8 -61
  190. package/dist/workflows/dag/frontend-recovery-run.js +57 -0
  191. package/dist/workflows/dag/frontend-review-context.js +43 -70
  192. package/dist/workflows/dag/frontend-risk.js +92 -10
  193. package/dist/workflows/dag/frontend-session-budget.js +117 -3
  194. package/dist/workflows/dag/frontend-shape.js +11 -55
  195. package/dist/workflows/dag/frontend-test-execution-evidence.js +35 -10
  196. package/dist/workflows/dag/frontend-typed-event-store.js +32 -25
  197. package/dist/workflows/dag/frontend-verification-trace.js +25 -12
  198. package/dist/workflows/dag/frontend-writer-admission.js +3 -47
  199. package/dist/workflows/dag/frontend-writer-status.js +0 -23
  200. package/dist/workflows/dag/init-hybrid.js +202 -111
  201. package/dist/workflows/dag/interrupt-request.js +2 -0
  202. package/dist/workflows/dag/lifecycle.js +11 -2
  203. package/dist/workflows/dag/node-execution.js +50 -13
  204. package/dist/workflows/dag/reconcile-run.js +2 -0
  205. package/dist/workflows/dag/repair-artifact.js +3 -1
  206. package/dist/workflows/dag/report.js +2 -0
  207. package/dist/workflows/dag/rerun-plan.js +10 -0
  208. package/dist/workflows/dag/rerun-task.js +79 -1
  209. package/dist/workflows/dag/retry-policy.js +18 -0
  210. package/dist/workflows/dag/scheduler.js +2 -4
  211. package/dist/workflows/dag/types.js +44 -6
  212. package/dist/workflows/dag/validate.js +4 -0
  213. package/docs/README.md +1 -0
  214. package/docs/governance/README.md +1 -0
  215. package/docs/init-surface.manifest.json +1 -0
  216. package/docs/operations/README.md +2 -0
  217. package/docs/templates/README.md +1 -1
  218. package/docs/templates/agent-dag.schema.json +2 -2
  219. package/docs/templates/backend-test-dag.json +14 -10
  220. package/docs/templates/frontend-implementation-contract.schema.json +0 -7
  221. package/docs/templates/frontend-implementation-dag.json +8 -9
  222. package/package.json +8 -3
  223. package/skills/frontend-bounded-implement/SKILL.md +3 -4
  224. package/skills/frontend-design-review/SKILL.md +7 -12
  225. package/skills/frontend-design-review/references/review-checklist.md +7 -7
  226. package/skills/frontend-plan/SKILL.md +8 -18
  227. package/skills/frontend-plan/references/decision-contract.md +19 -26
  228. package/skills/frontend-review/SKILL.md +21 -25
  229. package/skills/frontend-review/references/review-findings.md +48 -24
  230. package/dist/worker/console/static/assets/channel-BxdTlb_5.js +0 -1
  231. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-_ByA1Nf8.js +0 -1
  232. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-_ByA1Nf8.js +0 -1
  233. package/dist/worker/console/static/assets/index-B9goN3S5.css +0 -1
  234. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-nLgMZ0D3.js +0 -1
  235. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BcYqTady.js +0 -8
  236. package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -975
@@ -392,6 +392,147 @@ const COMMON_MUTATION_ERRORS = [
392
392
  ];
393
393
  export function buildOperatorCapabilitiesDocument() {
394
394
  const actions = [
395
+ {
396
+ action: "prepareScheduledTask", cli: "console scheduled task preparation", kind: "mutation",
397
+ inputSchemaVersion: 1, resultSchemaVersion: 1, envelopeSchemaVersion: 1, requiredErrorCodes: ["INVALID_INPUT"],
398
+ description: "Preferred entry for natural-language one-time scheduled tasks, including read-only inspection reports. Call this directly after understanding the goal, output scope, executable acceptance and time; the server uses the CURRENT project directory and creates the immutable PRD, managed Task and strict writeSet gate, binds THIS Chat automatically and returns a REAL planned goal/confirmation card. No pre-existing task, worktree or session ID is needed: never ask the user for these internal fields and do not manually create/edit them with bash/write/newTask/importPrd. Keep preparation concise: preserve the user goal without inventing extra requirements. Omit budget for ordinary tasks. Use minimal independent acceptance commands (AC-1, AC-2), preferably existing test/build scripts or test -s scheduled-report.md for a report; no compound shell/pipelines/substitution, language-specific grep checks or redundant scope assertions (the host enforces scope). Infer routine defaults and verification commands from the repository; ask only about genuinely ambiguous goal/scope/time. For read-only requests allow just an ordinary report file in the current project directory, e.g. scheduled-report.md, not .harness/. Use schedule.delayMinutes for relative time (counted after preparation finishes), or localAt + IANA timezone for an exact date/time. The current workspace must match committed HEAD; never auto-commit/stash/reset existing changes. The card exposes the exact time, bounded defaults and committed base before one human confirmation. Same request in this Chat is idempotent. Only a returned goalId with state=planned is a prepared reservation; textual drafts, Tasks or failed operations are NOT bookings. Never replace a requested schedule with immediate execution or sleep, never call runDag or self-confirm. On failure explain the returned stage, correct business inputs and retry this tool; do not mutate imported PRD/runtime artifacts. For a new distinct appointment, make its goal/time distinct.",
399
+ inputParams: [{ name: "request", type: "object", required: true, description: "Business goal, workflow, allowedPaths, acceptance and schedule; optional forbiddenPaths/budget. Runtime identity is server-owned." }],
400
+ modelCallable: "always", humanConfirmation: "none",
401
+ },
402
+ {
403
+ "action": "prepareScheduledGoal",
404
+ "cli": "console scheduled goal service",
405
+ "kind": "mutation",
406
+ "inputSchemaVersion": 1,
407
+ "resultSchemaVersion": 1,
408
+ "envelopeSchemaVersion": 1,
409
+ "requiredErrorCodes": [
410
+ "INVALID_INPUT"
411
+ ],
412
+ "description": "Advanced entry ONLY for an already prepared Task. For a new natural-language reservation use prepareScheduledTask instead. Requires the current repository or its linked worktree at frozenBase and a managed Task already strict-prepared at its writeSet gate. booking must include managedTaskId, originalGoal, chatSessionRef, workflow, worktreeRef, frozenBase, allowedPaths, forbiddenPaths, acceptance {policy, criteria [{acId,description,command}]}, budget {maxTurns,maxDagRuns,maxSupervisorCalls}, time {executeAtUtc,latestStartUtc,stopAtUtc,expiresAt,timezone,allowedHours,misfirePolicy}. Resolve natural-language dates with the user timezone; ask about ambiguous time/AC/scope. No execution or authorization occurs. Show the complete scope/time/budget for one browser confirmation. Never call runDag for this reservation.",
413
+ "inputParams": [
414
+ {
415
+ "name": "booking",
416
+ "type": "object",
417
+ "required": true,
418
+ "description": "Complete frozen booking inputs; never supply prepared, controllerRef, approval or owner tokens."
419
+ }
420
+ ],
421
+ "modelCallable": "always",
422
+ "humanConfirmation": "none"
423
+ },
424
+ {
425
+ "action": "confirmScheduledGoal",
426
+ "cli": "console scheduled goal service",
427
+ "kind": "mutation",
428
+ "inputSchemaVersion": 1,
429
+ "resultSchemaVersion": 1,
430
+ "envelopeSchemaVersion": 1,
431
+ "requiredErrorCodes": [
432
+ "INVALID_INPUT"
433
+ ],
434
+ "description": "Human browser confirmation of the exact displayed ScheduledGoal envelope.",
435
+ "inputParams": [
436
+ {
437
+ "name": "goalId",
438
+ "type": "string",
439
+ "required": true,
440
+ "description": "Prepared goal id"
441
+ },
442
+ {
443
+ "name": "stateVersion",
444
+ "type": "number",
445
+ "required": true,
446
+ "description": "Displayed state version"
447
+ },
448
+ {
449
+ "name": "envelopeHash",
450
+ "type": "string",
451
+ "required": true,
452
+ "description": "Displayed envelope hash"
453
+ }
454
+ ],
455
+ "modelCallable": "never",
456
+ "humanConfirmation": "required"
457
+ },
458
+ {
459
+ "action": "cancelScheduledGoal",
460
+ "cli": "console scheduled goal service",
461
+ "kind": "mutation",
462
+ "inputSchemaVersion": 1,
463
+ "resultSchemaVersion": 1,
464
+ "envelopeSchemaVersion": 1,
465
+ "requiredErrorCodes": [
466
+ "INVALID_INPUT"
467
+ ],
468
+ "description": "Cancel future ScheduledGoal admission; in-flight/unknown ownership remains until reconciled.",
469
+ "inputParams": [
470
+ {
471
+ "name": "goalId",
472
+ "type": "string",
473
+ "required": true,
474
+ "description": "Goal id"
475
+ },
476
+ {
477
+ "name": "stateVersion",
478
+ "type": "number",
479
+ "required": true,
480
+ "description": "Current state version"
481
+ },
482
+ {
483
+ "name": "reason",
484
+ "type": "string",
485
+ "required": true,
486
+ "description": "Human cancellation reason"
487
+ }
488
+ ],
489
+ "modelCallable": "never",
490
+ "humanConfirmation": "required"
491
+ },
492
+ {
493
+ "action": "scheduledGoalGet",
494
+ "cli": "console scheduled goal service",
495
+ "kind": "read",
496
+ "inputSchemaVersion": 1,
497
+ "resultSchemaVersion": 1,
498
+ "envelopeSchemaVersion": 1,
499
+ "requiredErrorCodes": [
500
+ "INVALID_INPUT"
501
+ ],
502
+ "description": "Read a durable ScheduledGoal booking and separate execution, acceptance, delivery and reconciliation results. Never implies authorization.",
503
+ "inputParams": [
504
+ {
505
+ "name": "goalId",
506
+ "type": "string",
507
+ "required": true,
508
+ "description": "Goal id"
509
+ }
510
+ ],
511
+ "modelCallable": "always",
512
+ "humanConfirmation": "none"
513
+ },
514
+ {
515
+ "action": "scheduledGoalList",
516
+ "cli": "console scheduled goal service",
517
+ "kind": "read",
518
+ "inputSchemaVersion": 1,
519
+ "resultSchemaVersion": 1,
520
+ "envelopeSchemaVersion": 1,
521
+ "requiredErrorCodes": [
522
+ "INVALID_INPUT"
523
+ ],
524
+ "description": "List durable ScheduledGoals, including those whose original Chat was archived or deleted.",
525
+ "inputParams": [
526
+ {
527
+ "name": "chatSessionRef",
528
+ "type": "string",
529
+ "required": false,
530
+ "description": "Optional original Chat session id"
531
+ }
532
+ ],
533
+ "modelCallable": "always",
534
+ "humanConfirmation": "none"
535
+ },
395
536
  {
396
537
  action: "operatorCapabilities",
397
538
  cli: "loop-agent operator capabilities --json",
@@ -714,6 +855,24 @@ export function buildOperatorCapabilitiesDocument() {
714
855
  required: false,
715
856
  description: "optional verify commands (label:command or shell)",
716
857
  },
858
+ {
859
+ name: "requirementOwnership",
860
+ type: "array",
861
+ required: false,
862
+ description: "optional frozen requirement→file ownership for frontend-implementation DAGs: [{requirementIds, implementationFiles?, verificationFiles?}] — machine-enforced at plan finalize",
863
+ },
864
+ {
865
+ name: "capabilityBoundary",
866
+ type: "array",
867
+ required: false,
868
+ description: "optional frozen test capability boundary {allowed?, forbidden?} composed into dependencyPolicy by the runtime",
869
+ },
870
+ {
871
+ name: "interactionIds",
872
+ type: "array",
873
+ required: false,
874
+ description: "optional frozen interaction id vocabulary — contract interaction names are machine-checked against it at plan finalize",
875
+ },
717
876
  ],
718
877
  modelCallable: "always",
719
878
  humanConfirmation: "none",
@@ -1069,6 +1228,27 @@ export function buildOperatorCapabilitiesDocument() {
1069
1228
  required: false,
1070
1229
  description: "verification entries as label:command strings",
1071
1230
  },
1231
+ {
1232
+ name: "requirementOwnership",
1233
+ type: "array",
1234
+ itemsType: "string",
1235
+ required: false,
1236
+ description: "frozen requirement→file ownership: [{requirementIds, implementationFiles?, verificationFiles?}] — machine-enforced at plan finalize",
1237
+ },
1238
+ {
1239
+ name: "capabilityBoundary",
1240
+ type: "array",
1241
+ itemsType: "string",
1242
+ required: false,
1243
+ description: "frozen test capability boundary {allowed?, forbidden?} composed into dependencyPolicy by the runtime",
1244
+ },
1245
+ {
1246
+ name: "interactionIds",
1247
+ type: "array",
1248
+ itemsType: "string",
1249
+ required: false,
1250
+ description: "frozen interaction id vocabulary — contract interaction names are machine-checked against it at plan finalize",
1251
+ },
1072
1252
  {
1073
1253
  name: "approveGate",
1074
1254
  type: "string",
@@ -134,8 +134,10 @@ export function computePackageFingerprint(packageRoot) {
134
134
  const bBuf = Buffer.from(b.relativePath, "utf-8");
135
135
  const minLen = Math.min(aBuf.length, bBuf.length);
136
136
  for (let i = 0; i < minLen; i++) {
137
- if (aBuf[i] !== bBuf[i])
138
- return aBuf[i] - bBuf[i];
137
+ const aByte = aBuf[i];
138
+ const bByte = bBuf[i];
139
+ if (aByte !== bByte)
140
+ return (aByte ?? 0) - (bByte ?? 0);
139
141
  }
140
142
  return aBuf.length - bBuf.length;
141
143
  });
@@ -183,8 +185,8 @@ export function resolveBinPaths(requested, resolvedAt, inputs) {
183
185
  // Search PATH
184
186
  const pathEnv = env.PATH ?? env.Path ?? env.path ?? "";
185
187
  const candidates = resolveFromPath(requested, pathEnv, osPlatform, env.PATHEXT ?? env.Pathext ?? env.pathext, cwd);
186
- if (candidates.length > 0) {
187
- const entry = candidates[0];
188
+ const entry = candidates[0];
189
+ if (entry !== undefined) {
188
190
  assertRegularFile(entry, requested);
189
191
  return { requested, entry, realEntry: realpathSync(entry), resolvedAt };
190
192
  }
@@ -268,7 +268,7 @@ export function sliceUtf8Range(text, offset, maxBytes) {
268
268
  }
269
269
  const buffer = Buffer.from(text, "utf8");
270
270
  let end = Math.min(totalBytes, start + limit);
271
- while (end > start && (buffer[end] & 0b1100_0000) === 0b1000_0000) {
271
+ while (end > start && ((buffer[end] ?? 0) & 0b1100_0000) === 0b1000_0000) {
272
272
  end -= 1;
273
273
  }
274
274
  const slice = buffer.subarray(start, end).toString("utf8");
@@ -186,6 +186,8 @@ function globToRegExp(glob) {
186
186
  let regex = '^';
187
187
  for (let i = 0; i < glob.length; i += 1) {
188
188
  const char = glob[i];
189
+ if (char === undefined)
190
+ continue;
189
191
  if (char === '*') {
190
192
  const next = glob[i + 1];
191
193
  if (next === '*') {
@@ -286,6 +286,35 @@ const taskConfigObjectSchema = z.object({
286
286
  verifyPreset: verifyPresetSchema.optional().default("auto"),
287
287
  /** Task-contract commands that must be included in final DAG shell verification. */
288
288
  verifyCommands: z.array(taskVerifyCommandSchema).optional().default([]),
289
+ /** Generation-frozen requirement→file ownership for frontend-implementation
290
+ * DAGs. Machine-checked at plan finalize: a requirement's implementation
291
+ * targets (and its verification targets' files) must stay inside the
292
+ * declared sets, so ownership never depends on model compliance with prose.
293
+ */
294
+ requirementOwnership: z
295
+ .array(z
296
+ .object({
297
+ requirementIds: z.array(z.string().min(1)).min(1),
298
+ implementationFiles: z.array(z.string().min(1)).optional(),
299
+ verificationFiles: z.array(z.string().min(1)).optional(),
300
+ })
301
+ .strict()
302
+ .refine((entry) => (entry.implementationFiles?.length ?? 0) > 0 ||
303
+ (entry.verificationFiles?.length ?? 0) > 0, { message: "requirementOwnership entry needs implementationFiles or verificationFiles" }))
304
+ .optional(),
305
+ /** Generation-frozen test capability boundary composed into the canonical
306
+ * dependencyPolicy by the runtime (planner never maintains it). */
307
+ capabilityBoundary: z
308
+ .object({
309
+ allowed: z.array(z.string().min(1)).optional(),
310
+ forbidden: z.array(z.string().min(1)).optional(),
311
+ })
312
+ .strict()
313
+ .refine((entry) => (entry.allowed?.length ?? 0) > 0 || (entry.forbidden?.length ?? 0) > 0, { message: "capabilityBoundary needs allowed or forbidden entries" })
314
+ .optional(),
315
+ /** Generation-frozen interaction id vocabulary. Contract interaction names
316
+ * must come from this set (plan finalize deterministic check). */
317
+ interactionIds: z.array(z.string().min(1)).optional(),
289
318
  /** Verification selection policy. Intermediate loops may use quota; final gates still run full required verification. */
290
319
  verifyQuota: verifyQuotaSchema.optional().default("full"),
291
320
  /** DAG supervised verify strategy. Final quota is fixed to full; intermediate quota may reduce cost. */
@@ -187,6 +187,15 @@ export function mergeManagedTaskConfigFields(existing, draft) {
187
187
  ...(draft.frontendOpenspec !== undefined
188
188
  ? { frontendOpenspec: draft.frontendOpenspec }
189
189
  : {}),
190
+ ...(draft.requirementOwnership !== undefined
191
+ ? { requirementOwnership: draft.requirementOwnership }
192
+ : {}),
193
+ ...(draft.capabilityBoundary !== undefined
194
+ ? { capabilityBoundary: draft.capabilityBoundary }
195
+ : {}),
196
+ ...(draft.interactionIds !== undefined
197
+ ? { interactionIds: draft.interactionIds }
198
+ : {}),
190
199
  };
191
200
  if (draft.featureId !== undefined) {
192
201
  next.featureId = draft.featureId;
@@ -70,6 +70,12 @@ export async function recoverTaskContract(input) {
70
70
  };
71
71
  }
72
72
  const txId = input.txId ?? unfinished[0];
73
+ if (txId === undefined) {
74
+ return {
75
+ action: "manual-required",
76
+ message: "no unfinished transactions to recover",
77
+ };
78
+ }
73
79
  if (!unfinished.includes(txId)) {
74
80
  return {
75
81
  action: "manual-required",
@@ -75,6 +75,23 @@ export const taskContractDraftSchema = z
75
75
  assumptions: z.array(z.string()).optional(),
76
76
  hardConstraints: z.array(z.string()).optional(),
77
77
  frontendOpenspec: frontendOpenspecConfigSchema.optional(),
78
+ requirementOwnership: z
79
+ .array(z
80
+ .object({
81
+ requirementIds: z.array(z.string().min(1)).min(1),
82
+ implementationFiles: z.array(z.string().min(1)).optional(),
83
+ verificationFiles: z.array(z.string().min(1)).optional(),
84
+ })
85
+ .strict())
86
+ .optional(),
87
+ capabilityBoundary: z
88
+ .object({
89
+ allowed: z.array(z.string().min(1)).optional(),
90
+ forbidden: z.array(z.string().min(1)).optional(),
91
+ })
92
+ .strict()
93
+ .optional(),
94
+ interactionIds: z.array(z.string().min(1)).optional(),
78
95
  interviewAssessmentRef: z
79
96
  .object({
80
97
  draftSha256: z.string().min(1),
@@ -17,7 +17,10 @@ function parseSimpleFrontmatter(block) {
17
17
  const match = /^([A-Za-z0-9_-]+)\s*:\s*(.+?)\s*$/u.exec(line);
18
18
  if (!match)
19
19
  continue;
20
- out[match[1]] = parseYamlishScalar(match[2]);
20
+ const [, key, value] = match;
21
+ if (key === undefined || value === undefined)
22
+ continue;
23
+ out[key] = parseYamlishScalar(value);
21
24
  }
22
25
  return out;
23
26
  }
@@ -46,7 +49,11 @@ export function detectProductArtifactMeta(markdown) {
46
49
  if (!match) {
47
50
  return { artifactType: "unknown", recognized: false };
48
51
  }
49
- const fields = parseSimpleFrontmatter(match[1]);
52
+ const [, frontmatter] = match;
53
+ if (frontmatter === undefined) {
54
+ return { artifactType: "unknown", recognized: false };
55
+ }
56
+ const fields = parseSimpleFrontmatter(frontmatter);
50
57
  const artifactType = normalizeArtifactType(fields.artifact_type);
51
58
  const recognized = artifactType !== "unknown" || Boolean(fields.artifact_version);
52
59
  return {
@@ -90,6 +90,42 @@ function resolveAllowedRoots(input) {
90
90
  return normalized.ok ? normalized.value : raw.trim();
91
91
  });
92
92
  }
93
+ function resolveInteractionIds(input) {
94
+ if (input.flags.interactionIds !== undefined) {
95
+ return input.flags.interactionIds;
96
+ }
97
+ if (input.baseDraft?.interactionIds?.length) {
98
+ return input.baseDraft.interactionIds;
99
+ }
100
+ if (input.existing?.interactionIds?.length) {
101
+ return input.existing.interactionIds;
102
+ }
103
+ return undefined;
104
+ }
105
+ function resolveCapabilityBoundary(input) {
106
+ if (input.flags.capabilityBoundary !== undefined) {
107
+ return input.flags.capabilityBoundary;
108
+ }
109
+ if (input.baseDraft?.capabilityBoundary) {
110
+ return input.baseDraft.capabilityBoundary;
111
+ }
112
+ if (input.existing?.capabilityBoundary) {
113
+ return input.existing.capabilityBoundary;
114
+ }
115
+ return undefined;
116
+ }
117
+ function resolveRequirementOwnership(input) {
118
+ if (input.flags.requirementOwnership !== undefined) {
119
+ return input.flags.requirementOwnership;
120
+ }
121
+ if (input.baseDraft?.requirementOwnership?.length) {
122
+ return input.baseDraft.requirementOwnership;
123
+ }
124
+ if (input.existing?.requirementOwnership?.length) {
125
+ return input.existing.requirementOwnership;
126
+ }
127
+ return undefined;
128
+ }
93
129
  function resolveVerifyCommands(input) {
94
130
  if (input.flags.verifyCommands !== undefined) {
95
131
  return input.flags.verifyCommands.map((cmd) => ({
@@ -178,6 +214,21 @@ export function buildPrepareDraft(input) {
178
214
  baseDraft,
179
215
  existing: existingTaskConfig,
180
216
  });
217
+ const interactionIds = resolveInteractionIds({
218
+ flags,
219
+ baseDraft,
220
+ existing: existingTaskConfig,
221
+ });
222
+ const capabilityBoundary = resolveCapabilityBoundary({
223
+ flags,
224
+ baseDraft,
225
+ existing: existingTaskConfig,
226
+ });
227
+ const requirementOwnership = resolveRequirementOwnership({
228
+ flags,
229
+ baseDraft,
230
+ existing: existingTaskConfig,
231
+ });
181
232
  const pathAssessment = assessPreparePaths({
182
233
  allowedPaths,
183
234
  forbiddenPaths: finalForbidden,
@@ -215,6 +266,15 @@ export function buildPrepareDraft(input) {
215
266
  })),
216
267
  },
217
268
  };
269
+ if (requirementOwnership !== undefined) {
270
+ draft.requirementOwnership = requirementOwnership;
271
+ }
272
+ if (capabilityBoundary !== undefined) {
273
+ draft.capabilityBoundary = capabilityBoundary;
274
+ }
275
+ if (interactionIds !== undefined && interactionIds.length > 0) {
276
+ draft.interactionIds = interactionIds;
277
+ }
218
278
  if (featureId)
219
279
  draft.featureId = featureId;
220
280
  const backendTest = resolveBackendTest(flags, baseDraft, existingTaskConfig);
@@ -122,7 +122,10 @@ function isAtxHeading(line) {
122
122
  const match = /^(#{1,6})\s+(.+?)\s*$/u.exec(line);
123
123
  if (!match)
124
124
  return null;
125
- return { level: match[1].length, text: match[2].trim() };
125
+ const [, hashes, text] = match;
126
+ if (hashes === undefined || text === undefined)
127
+ return null;
128
+ return { level: hashes.length, text: text.trim() };
126
129
  }
127
130
  function isListItem(line) {
128
131
  return /^\s*(?:[-*+]|\d+[.)])\s+(?:\[(?: |x|X)\]\s+)?/.test(line);
@@ -59,11 +59,21 @@ function stripFencedAndComments(text) {
59
59
  .replace(/<!--[\s\S]*?-->/g, "\n");
60
60
  }
61
61
  function normalizeAcceptanceId(raw) {
62
- return raw
62
+ return (raw
63
63
  .trim()
64
64
  .toUpperCase()
65
65
  .replace(/_/g, "-")
66
- .replace(/^(AC)(FE|BE|UI|API)(?=-|\d)/u, "$1-$2");
66
+ .replace(/^(AC)(FE|BE|UI|API)(?=-|\d)/u, "$1-$2")
67
+ // A PRD writes `AC1` as naturally as `AC-1` or `AC_1`. Only the two earlier
68
+ // rules canonicalized a separator, so a bare `AC1` escaped verbatim — and the
69
+ // generated DAG's requirement-id schema requires a hyphen after the prefix
70
+ // (`/^(?:REQ|BR|AC)-[A-Z0-9]+(?:-[A-Z0-9]+)*$/`). Other consumers already
71
+ // assume `AC-...` (e.g. the frontend-test checklist demands `AC-FE-001`).
72
+ // The result was a meaningless split: the same PRD style generated fine under
73
+ // `taskKind: standard` and failed DAG generation under
74
+ // `taskKind: frontend-implementation`. Canonicalize here, once, so every
75
+ // workflow reads the same ids.
76
+ .replace(/^AC(?=\d)/u, "AC-"));
67
77
  }
68
78
  function parseEmbeddedAcHeading(headingText) {
69
79
  const normalized = headingText
@@ -77,7 +87,10 @@ function parseEmbeddedAcHeading(headingText) {
77
87
  const match = EMBEDDED_AC_HEADING.exec(normalized);
78
88
  if (!match)
79
89
  return null;
80
- const id = normalizeAcceptanceId(match[1]);
90
+ const [, rawId] = match;
91
+ if (rawId === undefined)
92
+ return null;
93
+ const id = normalizeAcceptanceId(rawId);
81
94
  // Bold-wrapped ids leave their closing marker inside the captured text
82
95
  // ("**AC-FE-INS-001**:xxx" captures "**:xxx"); strip emphasis remnants
83
96
  // and leading separators so the bound text is clean prose.
@@ -154,13 +167,17 @@ function isAtxHeading(line) {
154
167
  const match = /^(#{1,6})\s+(.+?)\s*$/u.exec(line);
155
168
  if (!match)
156
169
  return null;
157
- return { level: match[1].length, text: match[2] };
170
+ const [, hashes, text] = match;
171
+ if (hashes === undefined || text === undefined)
172
+ return null;
173
+ return { level: hashes.length, text };
158
174
  }
159
175
  function parseListItem(line) {
160
176
  const match = /^\s*(?:[-*+]|\d+[.)])\s+(?:\[(?: |x|X)\]\s+)?(.+?)\s*$/u.exec(line);
161
177
  if (!match)
162
178
  return null;
163
- return match[1].trim();
179
+ const [, item] = match;
180
+ return item === undefined ? null : item.trim();
164
181
  }
165
182
  function parseAcceptanceLine(line, nextId) {
166
183
  const checklist = /^\s*[-*+]\s+\[(?: |x|X)\]\s+(.+?)\s*$/u.exec(line);
@@ -173,14 +190,20 @@ function parseAcceptanceLine(line, nextId) {
173
190
  return embedded;
174
191
  const withId = /^(AC[-_]?\d+)\s*[::]\s*(.+)$/iu.exec(body);
175
192
  if (withId) {
176
- return { id: normalizeAcceptanceId(withId[1]), text: withId[2].trim() };
193
+ const [, rawId, rawText] = withId;
194
+ if (rawId !== undefined && rawText !== undefined) {
195
+ return { id: normalizeAcceptanceId(rawId), text: rawText.trim() };
196
+ }
177
197
  }
178
198
  const bareAc = /^(AC[-_]?\d+)\s+(.+)$/iu.exec(body);
179
199
  if (bareAc) {
180
- return {
181
- id: normalizeAcceptanceId(bareAc[1]),
182
- text: bareAc[2].trim(),
183
- };
200
+ const [, rawId, rawText] = bareAc;
201
+ if (rawId !== undefined && rawText !== undefined) {
202
+ return {
203
+ id: normalizeAcceptanceId(rawId),
204
+ text: rawText.trim(),
205
+ };
206
+ }
184
207
  }
185
208
  return { id: nextId(), text: body };
186
209
  }
@@ -395,7 +418,9 @@ export function mergeAcceptanceCriteria(parts) {
395
418
  for (const id of order) {
396
419
  const texts = [...(byId.get(id) ?? [])];
397
420
  if (texts.length === 1) {
398
- items.push({ id, text: texts[0] });
421
+ const [onlyText] = texts;
422
+ if (onlyText !== undefined)
423
+ items.push({ id, text: onlyText });
399
424
  }
400
425
  else if (texts.length > 1) {
401
426
  conflicts.push({ id, texts });
@@ -102,11 +102,14 @@ function stripOutOfScopeSections(markdown) {
102
102
  for (const line of markdown.split(/\r?\n/)) {
103
103
  const heading = /^(#{1,6})\s+(.+?)\s*$/.exec(line.trim());
104
104
  if (heading) {
105
- const level = heading[1].length;
105
+ const [, hashes, title] = heading;
106
+ if (hashes === undefined || title === undefined)
107
+ continue;
108
+ const level = hashes.length;
106
109
  if (skippedHeadingLevel !== null && level <= skippedHeadingLevel) {
107
110
  skippedHeadingLevel = null;
108
111
  }
109
- if (OUT_OF_SCOPE_HEADING.test(heading[2].trim())) {
112
+ if (OUT_OF_SCOPE_HEADING.test(title.trim())) {
110
113
  skippedHeadingLevel = level;
111
114
  continue;
112
115
  }