@duckmind/dm-windows-x64 0.60.6 → 0.60.9

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 (197) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +70 -123
  3. package/extensions/dm-9router-ext/src/index.js +410 -5
  4. package/extensions/dm-caveman/extensions/caveman.js +283 -12
  5. package/extensions/dm-cliproxy/index.js +182 -2
  6. package/extensions/dm-cliproxy/scripts/check-config-migration.js +66 -8
  7. package/extensions/dm-cliproxy/src/apply.js +228 -1
  8. package/extensions/dm-cliproxy/src/cache.js +42 -1
  9. package/extensions/dm-cliproxy/src/commands.js +50 -2
  10. package/extensions/dm-cliproxy/src/compat.js +81 -1
  11. package/extensions/dm-cliproxy/src/config.js +192 -2
  12. package/extensions/dm-cliproxy/src/conflicts.js +46 -1
  13. package/extensions/dm-cliproxy/src/fetch-models.js +190 -1
  14. package/extensions/dm-cliproxy/src/fetch-usage.js +41 -1
  15. package/extensions/dm-cliproxy/src/log.js +23 -1
  16. package/extensions/dm-cliproxy/src/status-quota.js +77 -1
  17. package/extensions/dm-cliproxy/src/ui-frame.js +50 -1
  18. package/extensions/dm-cliproxy/src/ui-hub/hub.js +199 -2
  19. package/extensions/dm-cliproxy/src/ui-hub/index.js +26 -2
  20. package/extensions/dm-cliproxy/src/ui-hub/shell.js +46 -1
  21. package/extensions/dm-cliproxy/src/ui-hub/view-diagnostics.js +69 -1
  22. package/extensions/dm-cliproxy/src/ui-hub/view-models.js +379 -1
  23. package/extensions/dm-cliproxy/src/ui-hub/view-usage.js +106 -1
  24. package/extensions/dm-cliproxy/src/ui-picker/catalog.js +41 -1
  25. package/extensions/dm-cliproxy/src/ui-picker/mutate.js +115 -1
  26. package/extensions/dm-cliproxy/src/ui-picker/prompt-confirm.js +35 -1
  27. package/extensions/dm-cliproxy/src/ui-picker/prompt-name.js +62 -1
  28. package/extensions/dm-cliproxy/src/ui-picker/providers.js +45 -1
  29. package/extensions/dm-cliproxy/src/ui-picker/render-text.js +34 -1
  30. package/extensions/dm-cliproxy/src/ui-picker/rows.js +57 -1
  31. package/extensions/dm-cliproxy/src/ui-setup.js +260 -2
  32. package/extensions/dm-cliproxy/src/ui-usage.js +151 -1
  33. package/extensions/dm-cliproxy/src/usage-shared-cache.js +97 -1
  34. package/extensions/dm-context/src/context.js +144 -1
  35. package/extensions/dm-context/src/index.js +339 -7
  36. package/extensions/dm-context/src/utils.js +9 -1
  37. package/extensions/dm-cua/bin/browser-cua.mjs +73 -8
  38. package/extensions/dm-cua/index.js +75 -6
  39. package/extensions/dm-cua/src/browser-cua-lib.mjs +490 -6
  40. package/extensions/dm-cua/src/browser-install.mjs +331 -2
  41. package/extensions/dm-fff/src/index.js +688 -12
  42. package/extensions/dm-fff/src/query.js +60 -1
  43. package/extensions/dm-goal/src/goal.js +894 -23
  44. package/extensions/dm-image2/index.js +103 -9
  45. package/extensions/dm-image2/src/image-lib.mjs +1275 -8
  46. package/extensions/dm-subagents/install.mjs +62 -8
  47. package/extensions/dm-subagents/src/agents/agent-management.js +1190 -36
  48. package/extensions/dm-subagents/src/agents/agent-memory.js +216 -6
  49. package/extensions/dm-subagents/src/agents/agent-scope.js +5 -1
  50. package/extensions/dm-subagents/src/agents/agent-selection.js +20 -1
  51. package/extensions/dm-subagents/src/agents/agent-serializer.js +120 -5
  52. package/extensions/dm-subagents/src/agents/agents.js +1139 -11
  53. package/extensions/dm-subagents/src/agents/chain-serializer.js +299 -11
  54. package/extensions/dm-subagents/src/agents/frontmatter.js +65 -6
  55. package/extensions/dm-subagents/src/agents/identity.js +29 -1
  56. package/extensions/dm-subagents/src/agents/proactive-skills.js +141 -1
  57. package/extensions/dm-subagents/src/agents/skills.js +614 -6
  58. package/extensions/dm-subagents/src/extension/config.js +35 -2
  59. package/extensions/dm-subagents/src/extension/control-notices.js +69 -4
  60. package/extensions/dm-subagents/src/extension/doctor.js +172 -15
  61. package/extensions/dm-subagents/src/extension/fanout-child.js +158 -231
  62. package/extensions/dm-subagents/src/extension/index.js +521 -359
  63. package/extensions/dm-subagents/src/extension/rpc.js +266 -7
  64. package/extensions/dm-subagents/src/extension/schemas.js +275 -1
  65. package/extensions/dm-subagents/src/extension/tool-description.js +111 -6
  66. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +126 -4
  67. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +452 -5
  68. package/extensions/dm-subagents/src/intercom/result-intercom.js +319 -3
  69. package/extensions/dm-subagents/src/profiles/profiles.js +458 -3
  70. package/extensions/dm-subagents/src/runs/background/async-execution.js +834 -40
  71. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +435 -14
  72. package/extensions/dm-subagents/src/runs/background/async-resume.js +334 -8
  73. package/extensions/dm-subagents/src/runs/background/async-status.js +313 -12
  74. package/extensions/dm-subagents/src/runs/background/chain-append.js +245 -2
  75. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +136 -1
  76. package/extensions/dm-subagents/src/runs/background/completion-batcher.js +94 -1
  77. package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +54 -1
  78. package/extensions/dm-subagents/src/runs/background/control-channel.js +190 -1
  79. package/extensions/dm-subagents/src/runs/background/fleet-view.js +483 -17
  80. package/extensions/dm-subagents/src/runs/background/notify.js +129 -3
  81. package/extensions/dm-subagents/src/runs/background/parallel-groups.js +34 -1
  82. package/extensions/dm-subagents/src/runs/background/result-watcher.js +236 -6
  83. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +76 -4
  84. package/extensions/dm-subagents/src/runs/background/run-status.js +427 -23
  85. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +487 -4
  86. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +306 -9
  87. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +2849 -73
  88. package/extensions/dm-subagents/src/runs/background/top-level-async.js +5 -1
  89. package/extensions/dm-subagents/src/runs/background/wait.js +206 -11
  90. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +1013 -12
  91. package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +980 -101
  92. package/extensions/dm-subagents/src/runs/foreground/execution.js +1165 -45
  93. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +3157 -222
  94. package/extensions/dm-subagents/src/runs/shared/acceptance.js +835 -3
  95. package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +104 -1
  96. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +116 -3
  97. package/extensions/dm-subagents/src/runs/shared/dm-args.js +208 -1
  98. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +90 -1
  99. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +282 -1
  100. package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +148 -1
  101. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +305 -1
  102. package/extensions/dm-subagents/src/runs/shared/model-fallback.js +194 -1
  103. package/extensions/dm-subagents/src/runs/shared/model-scope.js +65 -1
  104. package/extensions/dm-subagents/src/runs/shared/nested-events.js +851 -8
  105. package/extensions/dm-subagents/src/runs/shared/nested-path.js +41 -1
  106. package/extensions/dm-subagents/src/runs/shared/nested-render.js +105 -1
  107. package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +81 -4
  108. package/extensions/dm-subagents/src/runs/shared/run-history.js +51 -4
  109. package/extensions/dm-subagents/src/runs/shared/single-output.js +149 -8
  110. package/extensions/dm-subagents/src/runs/shared/structured-output.js +58 -1
  111. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +166 -5
  112. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +329 -13
  113. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +73 -1
  114. package/extensions/dm-subagents/src/runs/shared/turn-budget.js +47 -4
  115. package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +196 -1
  116. package/extensions/dm-subagents/src/runs/shared/worktree.js +435 -3
  117. package/extensions/dm-subagents/src/shared/artifacts.js +92 -2
  118. package/extensions/dm-subagents/src/shared/atomic-json.js +55 -1
  119. package/extensions/dm-subagents/src/shared/child-transcript.js +167 -5
  120. package/extensions/dm-subagents/src/shared/file-coalescer.js +25 -1
  121. package/extensions/dm-subagents/src/shared/fork-context.js +147 -4
  122. package/extensions/dm-subagents/src/shared/formatters.js +98 -7
  123. package/extensions/dm-subagents/src/shared/jsonl-writer.js +56 -2
  124. package/extensions/dm-subagents/src/shared/model-info.js +62 -1
  125. package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +68 -1
  126. package/extensions/dm-subagents/src/shared/session-identity.js +6 -1
  127. package/extensions/dm-subagents/src/shared/session-tokens.js +39 -2
  128. package/extensions/dm-subagents/src/shared/settings.js +198 -11
  129. package/extensions/dm-subagents/src/shared/status-format.js +53 -1
  130. package/extensions/dm-subagents/src/shared/types.js +184 -6
  131. package/extensions/dm-subagents/src/shared/utils.js +462 -2
  132. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +288 -1
  133. package/extensions/dm-subagents/src/slash/prompt-workflows.js +297 -7
  134. package/extensions/dm-subagents/src/slash/slash-bridge.js +118 -1
  135. package/extensions/dm-subagents/src/slash/slash-commands.js +1287 -31
  136. package/extensions/dm-subagents/src/slash/slash-live-state.js +240 -4
  137. package/extensions/dm-subagents/src/tui/render-helpers.js +64 -1
  138. package/extensions/dm-subagents/src/tui/render.js +1542 -4
  139. package/extensions/dm-usage/index.js +1294 -9
  140. package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +40 -9
  141. package/extensions/greedysearch-dm/bin/cdp-headless.mjs +5 -2
  142. package/extensions/greedysearch-dm/bin/cdp-visible.mjs +5 -2
  143. package/extensions/greedysearch-dm/bin/cdp.mjs +896 -30
  144. package/extensions/greedysearch-dm/bin/gschrome.mjs +30 -2
  145. package/extensions/greedysearch-dm/bin/kill-visible.mjs +7 -2
  146. package/extensions/greedysearch-dm/bin/launch-visible.mjs +13 -2
  147. package/extensions/greedysearch-dm/bin/launch.mjs +282 -10
  148. package/extensions/greedysearch-dm/bin/mcp.mjs +386 -361
  149. package/extensions/greedysearch-dm/bin/search.mjs +620 -540
  150. package/extensions/greedysearch-dm/bin/visible.mjs +22 -2
  151. package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +329 -579
  152. package/extensions/greedysearch-dm/extractors/chatgpt.mjs +301 -583
  153. package/extensions/greedysearch-dm/extractors/common.mjs +408 -32
  154. package/extensions/greedysearch-dm/extractors/consensus.mjs +376 -365
  155. package/extensions/greedysearch-dm/extractors/consent.mjs +303 -14
  156. package/extensions/greedysearch-dm/extractors/gemini.mjs +228 -592
  157. package/extensions/greedysearch-dm/extractors/google-ai.mjs +78 -499
  158. package/extensions/greedysearch-dm/extractors/logically.mjs +270 -347
  159. package/extensions/greedysearch-dm/extractors/perplexity.mjs +243 -581
  160. package/extensions/greedysearch-dm/extractors/selectors.mjs +32 -1
  161. package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +130 -317
  162. package/extensions/greedysearch-dm/index.js +123 -23
  163. package/extensions/greedysearch-dm/src/fetcher.mjs +576 -2
  164. package/extensions/greedysearch-dm/src/formatters/results.js +95 -10
  165. package/extensions/greedysearch-dm/src/formatters/sources.js +57 -1
  166. package/extensions/greedysearch-dm/src/formatters/synthesis.js +49 -1
  167. package/extensions/greedysearch-dm/src/github.mjs +222 -7
  168. package/extensions/greedysearch-dm/src/reddit.mjs +145 -14
  169. package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +340 -9
  170. package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +112 -4
  171. package/extensions/greedysearch-dm/src/search/chrome.mjs +486 -285
  172. package/extensions/greedysearch-dm/src/search/constants.mjs +109 -8
  173. package/extensions/greedysearch-dm/src/search/defaults.mjs +10 -1
  174. package/extensions/greedysearch-dm/src/search/engines.mjs +79 -9
  175. package/extensions/greedysearch-dm/src/search/fetch-source.mjs +441 -349
  176. package/extensions/greedysearch-dm/src/search/file-sources.mjs +29 -7
  177. package/extensions/greedysearch-dm/src/search/minimize.mjs +86 -1
  178. package/extensions/greedysearch-dm/src/search/output.mjs +51 -5
  179. package/extensions/greedysearch-dm/src/search/paths.mjs +48 -1
  180. package/extensions/greedysearch-dm/src/search/pdf.mjs +63 -2
  181. package/extensions/greedysearch-dm/src/search/port-pid.mjs +69 -1
  182. package/extensions/greedysearch-dm/src/search/progress.mjs +109 -2
  183. package/extensions/greedysearch-dm/src/search/query.mjs +21 -1
  184. package/extensions/greedysearch-dm/src/search/recovery.mjs +49 -1
  185. package/extensions/greedysearch-dm/src/search/research.mjs +2227 -458
  186. package/extensions/greedysearch-dm/src/search/scale-aware.mjs +61 -11
  187. package/extensions/greedysearch-dm/src/search/simple-research.mjs +396 -805
  188. package/extensions/greedysearch-dm/src/search/sources.mjs +412 -1
  189. package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +127 -12
  190. package/extensions/greedysearch-dm/src/search/synthesis.mjs +202 -12
  191. package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +209 -23
  192. package/extensions/greedysearch-dm/src/tools/shared.js +226 -10
  193. package/extensions/greedysearch-dm/src/utils/content.mjs +35 -4
  194. package/extensions/greedysearch-dm/src/utils/helpers.js +22 -1
  195. package/extensions/greedysearch-dm/src/utils/node-runtime.mjs +10 -1
  196. package/extensions/greedysearch-dm/src/utils/system-cmds.mjs +61 -1
  197. package/package.json +1 -1
@@ -1,3 +1,835 @@
1
- import{spawn as g}from"node:child_process";import{spawnSync as L}from"node:child_process";import*as E from"node:path";var _={none:0,attested:1,checked:2,verified:3,reviewed:4},F=new Set(["auto","none","attested","checked","verified","reviewed"]),S=new Set(["changed-files","tests-added","commands-run","validation-output","residual-risks","no-staged-files","diff-summary","review-findings","manual-notes"]),b=new Set(["level","criteria","evidence","verify","review","stopRules","reason"]),v=new Set(["id","must","evidence","severity"]),h=new Set(["id","command","timeoutMs","cwd","env","allowFailure"]),m=new Set(["agent","focus","required"]);function s(D){return D??"auto"}function w(D){return[...new Set(D)]}function R(D){switch(D){case"none":return[];case"attested":return["manual-notes","residual-risks"];case"checked":return["changed-files","tests-added","commands-run","residual-risks","no-staged-files"];case"verified":case"reviewed":return["changed-files","tests-added","commands-run","validation-output","residual-risks","no-staged-files"]}}function d(D){let H=D.agentName.toLowerCase(),J=D.task?.toLowerCase()??"",M=[],Q=/\b(?:reviewer|scout|context-builder|researcher|analyst)\b/.test(H),X=/\b(?:read[- ]only|review[- ]only|do not edit|don't edit|no edits|without edits|inspect|summari[sz]e)\b/.test(J),U=/\b(?:fix|implement|update|write|edit|modify|migrate|release|security|delete|remove|refactor|commit)\b/.test(J)||/\bworker\b/.test(H);if(Boolean(D.async&&U)||Boolean(D.dynamic)||Boolean(D.dynamicGroup)||/\b(?:release|migration|migrate|security|data[- ]loss|destructive|post-review|fix pass)\b/.test(J)){if(M.push(D.async?"async write-capable or risky run":"risky write-capable run"),D.dynamic||D.dynamicGroup)M.push("dynamic fanout context");return{level:"reviewed",reasons:M,criteria:["Implement the requested change without widening scope","Return evidence sufficient for an independent acceptance review"],evidence:R("reviewed"),review:{agent:"reviewer",required:!0}}}if(U&&!X)return M.push("write-capable worker/task"),{level:"checked",reasons:M,criteria:["Implement the requested change without widening scope"],evidence:R("checked")};if(Q||X)return M.push(Q?"read-only/reviewer-style agent":"read-only task wording"),{level:"attested",reasons:M,criteria:["Return concrete findings with file paths and severity when applicable"],evidence:["review-findings","residual-risks"]};return M.push("default lightweight attestation"),{level:"attested",reasons:M,criteria:["Return a concise result and residual risks when applicable"],evidence:["manual-notes","residual-risks"]}}function u(D){if(D===void 0||D==="auto")return{level:"auto"};if(D===!1)return{level:"none",reason:"disabled by deprecated false shorthand"};if(typeof D==="string")return{level:D};return{...D}}function l(D){return D.level==="none"&&typeof D.reason==="string"&&D.reason.trim().length>0}function UD(D,H="acceptance"){let J=[];if(D===void 0)return J;if(D===!1)return J;if(typeof D==="string"){if(!F.has(D))J.push(`${H} has invalid level '${D}'.`);return J}if(!D||typeof D!=="object"||Array.isArray(D))return J.push(`${H} must be a string level, false, or an object.`),J;let M=D;for(let Q of Object.keys(M))if(!b.has(Q))J.push(`${H}.${Q} is not supported.`);if(M.level!==void 0&&(typeof M.level!=="string"||!F.has(M.level)))J.push(`${H}.level must be one of auto, none, attested, checked, verified, reviewed.`);if(M.level==="none"&&(typeof M.reason!=="string"||!M.reason.trim()))J.push(`${H}.reason is required when level is none.`);if(M.reason!==void 0&&typeof M.reason!=="string")J.push(`${H}.reason must be a string.`);if(M.criteria!==void 0&&!Array.isArray(M.criteria))J.push(`${H}.criteria must be an array.`);if(Array.isArray(M.criteria))for(let[Q,X]of M.criteria.entries()){if(typeof X==="string")continue;let U=`${H}.criteria[${Q}]`;if(!X||typeof X!=="object"||Array.isArray(X)){J.push(`${U} must be a string or an object.`);continue}let Z=X;for(let $ of Object.keys(Z))if(!v.has($))J.push(`${U}.${$} is not supported.`);if(typeof Z.id!=="string"||!Z.id.trim())J.push(`${U}.id is required.`);if(typeof Z.must!=="string"||!Z.must.trim())J.push(`${U}.must is required.`);if(Z.evidence!==void 0&&!Array.isArray(Z.evidence))J.push(`${U}.evidence must be an array.`);if(Array.isArray(Z.evidence)){for(let[$,z]of Z.evidence.entries())if(typeof z!=="string"||!S.has(z))J.push(`${U}.evidence[${$}] is not a supported evidence kind.`)}if(Z.severity!==void 0&&Z.severity!=="required"&&Z.severity!=="recommended")J.push(`${U}.severity must be required or recommended.`)}if(Array.isArray(M.evidence)){for(let[Q,X]of M.evidence.entries())if(typeof X!=="string"||!S.has(X))J.push(`${H}.evidence[${Q}] is not a supported evidence kind.`)}else if(M.evidence!==void 0)J.push(`${H}.evidence must be an array.`);if(M.verify!==void 0&&!Array.isArray(M.verify))J.push(`${H}.verify must be an array.`);if(Array.isArray(M.verify))for(let[Q,X]of M.verify.entries()){if(!X||typeof X!=="object"||Array.isArray(X)){J.push(`${H}.verify[${Q}] must be an object.`);continue}let U=X;for(let Z of Object.keys(U))if(!h.has(Z))J.push(`${H}.verify[${Q}].${Z} is not supported.`);if(typeof U.id!=="string"||!U.id.trim())J.push(`${H}.verify[${Q}].id is required.`);if(typeof U.command!=="string"||!U.command.trim())J.push(`${H}.verify[${Q}].command is required.`);if(U.timeoutMs!==void 0&&(typeof U.timeoutMs!=="number"||!Number.isInteger(U.timeoutMs)||U.timeoutMs<1))J.push(`${H}.verify[${Q}].timeoutMs must be an integer >= 1.`);if(U.cwd!==void 0&&typeof U.cwd!=="string")J.push(`${H}.verify[${Q}].cwd must be a string.`);if(U.env!==void 0){if(!U.env||typeof U.env!=="object"||Array.isArray(U.env))J.push(`${H}.verify[${Q}].env must be an object.`);else for(let[Z,$]of Object.entries(U.env))if(typeof $!=="string")J.push(`${H}.verify[${Q}].env.${Z} must be a string.`)}if(U.allowFailure!==void 0&&typeof U.allowFailure!=="boolean")J.push(`${H}.verify[${Q}].allowFailure must be a boolean.`)}if(M.review!==void 0&&M.review!==!1)if(!M.review||typeof M.review!=="object"||Array.isArray(M.review))J.push(`${H}.review must be false or an object.`);else{let Q=M.review;for(let X of Object.keys(Q))if(!m.has(X))J.push(`${H}.review.${X} is not supported.`);if(Q.agent!==void 0&&typeof Q.agent!=="string")J.push(`${H}.review.agent must be a string.`);if(Q.focus!==void 0&&typeof Q.focus!=="string")J.push(`${H}.review.focus must be a string.`);if(Q.required!==void 0&&typeof Q.required!=="boolean")J.push(`${H}.review.required must be a boolean.`)}if(M.stopRules!==void 0&&!Array.isArray(M.stopRules))J.push(`${H}.stopRules must be an array.`);if(Array.isArray(M.stopRules)){for(let[Q,X]of M.stopRules.entries())if(typeof X!=="string")J.push(`${H}.stopRules[${Q}] must be a string.`)}return J}function n(D,H){return(D??[]).map((J,M)=>{if(typeof J==="string")return{id:`criterion-${M+1}`,must:J,evidence:H,severity:"required"};return{id:J.id?.trim()||`criterion-${M+1}`,must:J.must??"",evidence:J.evidence?.filter((Q)=>S.has(Q))??H,severity:J.severity??"required"}}).filter((J)=>J.must.trim())}function XD(D){let H=u(D.explicit),J=d(D),M=s(H.level),Q=l(H)?"none":M==="auto"?J.level:_[M]>=_[J.level]?M:J.level,X=w([...Q===J.level?J.evidence:R(Q),...H.evidence??[]]),U=n(H.criteria?.length?H.criteria:J.criteria,X),Z=H.review!==void 0?H.review:J.review;if(Q==="reviewed"&&M!=="auto"&&M!=="reviewed"&&H.review===void 0&&Z&&Z!==!1)Z={...Z,required:!1};return{level:Q,explicit:D.explicit!==void 0,inferredReason:J.reasons,criteria:U,evidence:X,verify:H.verify??[],review:Z,stopRules:H.stopRules??[],reason:H.reason}}function ZD(D){if(D.level==="none")return"";let H=["","## Acceptance Contract",`Acceptance level: ${D.level}`,"Completion is not accepted from prose alone. End with a structured acceptance report.","","Criteria:",...D.criteria.length?D.criteria.map((J)=>`- ${J.id}: ${J.must}`):["- Return the requested result."],"",`Required evidence: ${D.evidence.join(", ")||"none"}`];if(D.verify.length>0){H.push("","Runtime verification commands configured by parent:");for(let J of D.verify)H.push(`- ${J.id}: ${J.command}`)}if(D.review&&D.review!==!1){if(H.push("",`Review gate: ${D.review.required===!1?"optional":"required"}${D.review.agent?` by ${D.review.agent}`:""}.`),D.review.focus)H.push(`Review focus: ${D.review.focus}`)}if(D.stopRules.length>0)H.push("","Stop rules:",...D.stopRules.map((J)=>`- ${J}`));return H.push("","Finish with a fenced JSON block tagged `acceptance-report` in this shape:","Use empty arrays when no items apply; array fields contain strings unless object entries are shown.","```acceptance-report",JSON.stringify({criteriaSatisfied:[{id:"criterion-1",status:"satisfied",evidence:"specific proof"}],changedFiles:["src/file.ts"],testsAddedOrUpdated:["test/file.test.ts"],commandsRun:[{command:"command",result:"passed",summary:"short result"}],validationOutput:["validation output or concise summary"],residualRisks:["none"],noStagedFiles:!0,diffSummary:"short description of the diff",reviewFindings:["blocker: file.ts:12 - issue found, or no blockers"],manualNotes:"anything else the parent should know"},null,2),"```"),H.join(`
2
- `)}function f(D,H){let J=0,M=!1,Q=!1;for(let X=H;X<D.length;X++){let U=D[X];if(M){if(Q)Q=!1;else if(U==="\\")Q=!0;else if(U==='"')M=!1;continue}if(U==='"'){M=!0;continue}if(U==="{")J++;if(U==="}"){if(J--,J===0)return D.slice(H,X+1)}}return}function O(D){if(!D||typeof D!=="object"||Array.isArray(D))return D;let H=D;if("acceptance"in H)return H.acceptance;if("acceptance-report"in H)return H["acceptance-report"];return D}function c(D){return Array.isArray(D)&&D.every((H)=>{if(!H||typeof H!=="object"||Array.isArray(H))return!1;let J=H;return typeof J.command==="string"&&(J.result==="passed"||J.result==="failed"||J.result==="not-run")&&typeof J.summary==="string"})}function o(D){if(!D||typeof D!=="object"||Array.isArray(D))return!1;let H=D;return"criteriaSatisfied"in H&&(Y(H.changedFiles)||Y(H.testsAddedOrUpdated)||c(H.commandsRun)||Y(H.validationOutput)||Y(H.residualRisks)||typeof H.noStagedFiles==="boolean"||typeof H.diffSummary==="string"||Y(H.reviewFindings)||typeof H.manualNotes==="string")}function k(D){let H=D.trim();try{return JSON.parse(H)}catch(J){let M=H.indexOf("{");if(M>0){let Q=f(H,M);if(Q)return JSON.parse(Q)}throw J}}function T(D,H){return[...D.matchAll(new RegExp(`\`\`\`${H}\\s*\\n([\\s\\S]*?)\`\`\``,"gi"))].map((J)=>J[1]?.trim()).filter((J)=>Boolean(J))}function y(D){if(!D||typeof D!=="object"||Array.isArray(D))return"";let H=D;if("acceptance"in H)return"acceptance";if("acceptance-report"in H)return"acceptance-report";return""}function p(D){let H=k(D),J=O(H);return C(J,y(H))}function x(D){let H=k(D),J=O(H),M=C(J);if(!M.report)return;return o(M.report)?M.report:void 0}function i(D){let H=T(D,"acceptance-report"),J=[];for(let Q of H)try{let X=p(Q);if(X.report)return{report:X.report};J.push(`Invalid acceptance-report: ${X.errors.join("; ")}`)}catch(X){J.push(X instanceof Error?X.message:String(X))}if(J.length>0)return{error:`Failed to parse acceptance-report: ${J.join("; ")}`};for(let Q of T(D,"(?:json|jsonc|json5)"))try{let X=x(Q);if(X)return{report:X}}catch{}let M=D.search(/ACCEPTANCE_REPORT\s*:/i);if(M!==-1){let Q=D.indexOf("{",M);if(Q!==-1){let X=f(D,Q);if(X)try{let U=JSON.parse(X),Z=O(U),$=C(Z,y(U));if($.report)return{report:$.report};return{error:`Failed to parse acceptance-report: Invalid acceptance-report: ${$.errors.join("; ")}`}}catch(U){return{error:U instanceof Error?U.message:String(U)}}}}return{error:"Structured acceptance report not found."}}function $D(D){let H=/\n?```(acceptance-report|json|jsonc|json5)\s*\n([\s\S]*?)```\s*/gi,J;for(let M of D.matchAll(H)){let Q=(M.index??0)+M[0].length;if(D.slice(Q).trim().length===0&&M[1]&&M[2])J={index:M.index??0,tag:M[1].toLowerCase(),body:M[2]}}if(J){if(J.tag==="acceptance-report")return D.slice(0,J.index).trimEnd();try{if(x(J.body))return D.slice(0,J.index).trimEnd()}catch{}}return D.replace(/\n?```acceptance-report\s*\n[\s\S]*?```\s*$/i,"").replace(/\n?ACCEPTANCE_REPORT\s*:\s*\{[\s\S]*\}\s*$/i,"").trimEnd()}function Y(D){return Array.isArray(D)&&D.every((H)=>typeof H==="string")}function N(D,H){return D?`${D}.${H}`:H}function a(D){if(D===void 0)return"missing";if(D===null)return"null";if(Array.isArray(D))return"array";if(typeof D==="object")return"object";if(typeof D==="string"){let H=D.length>80?`${D.slice(0,77)}...`:D;return JSON.stringify(H)}return`${typeof D} ${String(D)}`}function G(D,H,J,M){D.push(`${H}: expected ${J}; got ${a(M)}`)}function I(D,H,J){if(!Array.isArray(H)){G(D,J,"string[]",H);return}for(let[M,Q]of H.entries())if(typeof Q!=="string")G(D,`${J}[${M}]`,"string",Q)}function C(D,H=""){let J=[];if(!D||typeof D!=="object"||Array.isArray(D))return G(J,H||"acceptance-report","object",D),{errors:J};let M=D;if(M.criteriaSatisfied!==void 0)if(!Array.isArray(M.criteriaSatisfied))G(J,N(H,"criteriaSatisfied"),"array",M.criteriaSatisfied);else for(let[X,U]of M.criteriaSatisfied.entries()){let Z=`${N(H,"criteriaSatisfied")}[${X}]`;if(!U||typeof U!=="object"||Array.isArray(U)){G(J,Z,"object",U);continue}let $=U;if($.id!==void 0&&typeof $.id!=="string")G(J,`${Z}.id`,"string",$.id);if($.status!=="satisfied"&&$.status!=="not-satisfied"&&$.status!=="not-applicable")G(J,`${Z}.status`,'one of "satisfied", "not-satisfied", "not-applicable"',$.status);if(typeof $.evidence!=="string"||!$.evidence.trim())G(J,`${Z}.evidence`,"non-empty string",$.evidence)}if(M.changedFiles!==void 0)I(J,M.changedFiles,N(H,"changedFiles"));if(M.testsAddedOrUpdated!==void 0)I(J,M.testsAddedOrUpdated,N(H,"testsAddedOrUpdated"));if(M.commandsRun!==void 0)if(!Array.isArray(M.commandsRun))G(J,N(H,"commandsRun"),"array",M.commandsRun);else for(let[X,U]of M.commandsRun.entries()){let Z=`${N(H,"commandsRun")}[${X}]`;if(!U||typeof U!=="object"||Array.isArray(U)){G(J,Z,"object",U);continue}let $=U;if(typeof $.command!=="string"||!$.command.trim())G(J,`${Z}.command`,"non-empty string",$.command);if($.result!=="passed"&&$.result!=="failed"&&$.result!=="not-run")G(J,`${Z}.result`,'one of "passed", "failed", "not-run"',$.result);if(typeof $.summary!=="string")G(J,`${Z}.summary`,"string",$.summary)}if(M.validationOutput!==void 0)I(J,M.validationOutput,N(H,"validationOutput"));if(M.residualRisks!==void 0)I(J,M.residualRisks,N(H,"residualRisks"));if(M.noStagedFiles!==void 0&&typeof M.noStagedFiles!=="boolean")G(J,N(H,"noStagedFiles"),"boolean",M.noStagedFiles);if(M.diffSummary!==void 0&&typeof M.diffSummary!=="string")G(J,N(H,"diffSummary"),"string",M.diffSummary);if(M.reviewFindings!==void 0)I(J,M.reviewFindings,N(H,"reviewFindings"));if(M.manualNotes!==void 0&&typeof M.manualNotes!=="string")G(J,N(H,"manualNotes"),"string",M.manualNotes);if(M.notes!==void 0&&typeof M.notes!=="string")G(J,N(H,"notes"),"string",M.notes);if(J.length>0)return{errors:J};return M.criteriaSatisfied!==void 0||M.changedFiles!==void 0||M.testsAddedOrUpdated!==void 0||M.commandsRun!==void 0||M.validationOutput!==void 0||M.residualRisks!==void 0||M.noStagedFiles!==void 0||M.diffSummary!==void 0||M.manualNotes!==void 0||M.notes!==void 0||M.reviewFindings!==void 0?{report:M,errors:J}:{errors:[`${H||"acceptance-report"}: expected at least one acceptance report field`]}}function t(D,H){let J=new Map((H.criteriaSatisfied??[]).filter((M)=>M.id).map((M)=>[M.id,M]));return D.filter((M)=>M.severity!=="recommended").map((M)=>{let Q=J.get(M.id);if(!Q)return{id:`criterion:${M.id}`,status:"failed",message:`Required criterion '${M.id}' was not reported.`};if(Q.status!=="satisfied")return{id:`criterion:${M.id}`,status:"failed",message:`Required criterion '${M.id}' was reported as ${Q.status}.`};return{id:`criterion:${M.id}`,status:"passed",message:`Required criterion '${M.id}' satisfied.`}})}function r(D,H){switch(H){case"changed-files":return Y(D.changedFiles)&&D.changedFiles.length>0;case"tests-added":return Y(D.testsAddedOrUpdated)&&D.testsAddedOrUpdated.length>0;case"commands-run":return Array.isArray(D.commandsRun)&&D.commandsRun.length>0;case"validation-output":return Y(D.validationOutput)&&D.validationOutput.length>0;case"residual-risks":return Y(D.residualRisks);case"no-staged-files":return D.noStagedFiles===!0;case"diff-summary":return typeof D.diffSummary==="string"&&D.diffSummary.trim().length>0;case"review-findings":return Y(D.reviewFindings);case"manual-notes":return Boolean((D.manualNotes??D.notes)?.trim())}}function e(D){let H=L("git",["status","--short"],{cwd:D,encoding:"utf-8"});if(H.status!==0)return{id:"no-staged-files",status:"not-applicable",message:"git status unavailable; no staged-files check skipped"};let J=H.stdout.split(/\r?\n/).filter((M)=>M.length>=2&&M[0]!==" "&&M[0]!=="?");return J.length===0?{id:"no-staged-files",status:"passed",message:"No staged files detected."}:{id:"no-staged-files",status:"failed",message:`Staged files present: ${J.join(", ")}`}}function DD(D,H,J){let M=[];for(let Q of D.evidence){let X=r(H,Q);M.push({id:`evidence:${Q}`,status:X?"passed":"failed",message:X?`${Q} evidence present.`:`${Q} evidence missing from child report.`})}if(D.evidence.includes("no-staged-files"))M.push(e(J));return M}function K(D){let H=D.trim();if(!H)return;return H.length>12000?`${H.slice(0,12000)}
3
- ...[truncated]`:H}function q(D){return w(D.map((H)=>H?.trim()).filter((H)=>Boolean(H)))}function GD(D){let H=D.results.map((Q)=>Q.acceptance?.childReport).filter((Q)=>Boolean(Q)),J=D.results.filter((Q)=>Q.exitCode!==0||Q.acceptance?.status==="rejected"),M=D.results.length>0&&J.length===0;return{criteriaSatisfied:[{id:"criterion-1",status:M?"satisfied":"not-satisfied",evidence:M?`All ${D.results.length} dynamic child run(s) completed without child or acceptance blockers.`:"Dynamic fanout produced no accepted child evidence."},{id:"criterion-2",status:M?"satisfied":"not-satisfied",evidence:M?"Collected child acceptance evidence for aggregate review.":"Dynamic fanout produced no aggregate review evidence."},...D.results.map((Q,X)=>({id:`child-${X+1}`,status:Q.exitCode===0&&Q.acceptance?.status!=="rejected"?"satisfied":"not-satisfied",evidence:`${Q.agent}: acceptance ${Q.acceptance?.status??"unreported"}${Q.error?` (${Q.error})`:""}`}))],changedFiles:q(H.flatMap((Q)=>Q.changedFiles??[])),testsAddedOrUpdated:q(H.flatMap((Q)=>Q.testsAddedOrUpdated??[])),commandsRun:H.flatMap((Q)=>Q.commandsRun??[]),validationOutput:q(H.flatMap((Q)=>Q.validationOutput??[])),residualRisks:q([...H.flatMap((Q)=>Q.residualRisks??[]),...J.map((Q)=>`${Q.agent}: ${Q.error??"child or acceptance gate failed"}`)]),noStagedFiles:H.length>0&&H.every((Q)=>Q.noStagedFiles===!0),reviewFindings:q(H.flatMap((Q)=>Q.reviewFindings??[])),manualNotes:D.notes??`Aggregated acceptance evidence from ${D.results.length} dynamic fanout child run(s).`,notes:D.notes}}function HD(D,H,J={}){return new Promise((M)=>{let Q=Date.now(),X=D.cwd?E.resolve(H,D.cwd):H,U="",Z="",$=!1,z=!1,j,B=g(D.command,{cwd:X,env:{...process.env,...D.env??{}},shell:!0,stdio:["ignore","pipe","pipe"],windowsHide:!0}),V=(W)=>{if(z)return;if(z=!0,clearTimeout(P),j)clearTimeout(j);J.signal?.removeEventListener("abort",A),M({id:D.id,command:D.command,cwd:X,durationMs:Date.now()-Q,...W})},A=()=>{if(z||$)return;$=!0,B.kill("SIGTERM"),j=setTimeout(()=>{B.kill("SIGKILL"),V({exitCode:null,status:"timed-out",stdout:K(U),stderr:K(Z||J.abortMessage||"Acceptance verification timed out.")})},1000),j.unref?.()},P=setTimeout(A,D.timeoutMs??120000);if(P.unref?.(),J.signal?.aborted)A();else J.signal?.addEventListener("abort",A,{once:!0});B.stdout.on("data",(W)=>{U+=W.toString()}),B.stderr.on("data",(W)=>{Z+=W.toString()}),B.on("close",(W)=>{V({exitCode:W,status:$?"timed-out":W===0&&!$?"passed":D.allowFailure?"allowed-failure":"failed",stdout:K(U),stderr:K(Z||($?J.abortMessage??"":""))})}),B.on("error",(W)=>{V({exitCode:$?null:1,status:$?"timed-out":D.allowFailure?"allowed-failure":"failed",stderr:$?K(Z||J.abortMessage||"Acceptance verification timed out."):W instanceof Error?W.message:String(W)})})})}async function ND(D){let H=D.acceptance,J={status:H.level==="none"?"not-required":"claimed",explicit:H.explicit,effectiveAcceptance:H,inferredReason:H.inferredReason,criteria:H.criteria,runtimeChecks:[],verifyRuns:[]};if(H.level==="none")return J;let M=D.report?{report:D.report}:i(D.output);if(M.report)J.childReport=M.report,J.status="attested";else return J.childReportParseError=M.error,J.runtimeChecks.push({id:"attestation",status:"failed",message:M.error??"Structured acceptance report missing."}),J.status="rejected",J;if(_[H.level]>=_.checked){if(J.runtimeChecks=[...t(H.criteria,M.report),...DD(H,M.report,D.cwd)],J.runtimeChecks.some((Q)=>Q.status==="failed"))return J.status="rejected",J;J.status="checked"}if(_[H.level]>=_.verified&&(H.level==="verified"||H.verify.length>0)){if(H.level==="verified"&&H.verify.length===0)return J.runtimeChecks.push({id:"verification-config",status:"failed",message:"verified acceptance requires runtime verify commands."}),J.status="rejected",J;J.verifyRuns=[];for(let Q of H.verify)if(J.verifyRuns.push(await HD(Q,D.cwd,{signal:D.signal,abortMessage:D.abortMessage})),D.signal?.aborted)break;if(J.verifyRuns.some((Q)=>Q.status==="failed"||Q.status==="timed-out"))return J.status="rejected",J;J.status="verified"}if(H.level==="reviewed")if(D.reviewResult)J.reviewResult=D.reviewResult,J.status=D.reviewResult.status==="no-blockers"?"reviewed":"rejected";else{let Q=H.review&&H.review!==!1&&H.review.required===!1;if(J.reviewResult={status:"needs-parent-decision",findings:[{severity:H.explicit&&!Q?"blocker":"non-blocking",issue:"Reviewed acceptance requires an independent reviewer result.",rationale:"The run cannot be marked reviewed from child evidence alone."}]},H.review===!1||H.explicit&&!Q)J.status="rejected"}return J}function WD(D){if(D.status!=="rejected")return;let H=D.runtimeChecks.find((M)=>M.status==="failed");if(H)return`Acceptance rejected: ${H.message}`;let J=D.verifyRuns.find((M)=>M.status==="failed"||M.status==="timed-out");if(J)return`Acceptance verification '${J.id}' ${J.status}.`;if(D.reviewResult?.status==="needs-parent-decision")return"Acceptance review required but no automatic reviewer result is available.";if(D.reviewResult?.status==="blockers")return"Acceptance review found blockers.";return"Acceptance rejected."}export{UD as validateAcceptanceInput,$D as stripAcceptanceReport,XD as resolveEffectiveAcceptance,i as parseAcceptanceReport,u as normalizeAcceptanceInput,ZD as formatAcceptancePrompt,ND as evaluateAcceptance,GD as aggregateAcceptanceReport,WD as acceptanceFailureMessage};
1
+ import { spawn } from "node:child_process";
2
+ import { spawnSync } from "node:child_process";
3
+ import * as path from "node:path";
4
+ const LEVEL_RANK = {
5
+ none: 0,
6
+ attested: 1,
7
+ checked: 2,
8
+ verified: 3,
9
+ reviewed: 4
10
+ };
11
+ const VALID_LEVELS = new Set(["auto", "none", "attested", "checked", "verified", "reviewed"]);
12
+ const VALID_EVIDENCE = new Set([
13
+ "changed-files",
14
+ "tests-added",
15
+ "commands-run",
16
+ "validation-output",
17
+ "residual-risks",
18
+ "no-staged-files",
19
+ "diff-summary",
20
+ "review-findings",
21
+ "manual-notes"
22
+ ]);
23
+ const ACCEPTANCE_CONFIG_KEYS = new Set(["level", "criteria", "evidence", "verify", "review", "stopRules", "reason"]);
24
+ const ACCEPTANCE_GATE_KEYS = new Set(["id", "must", "evidence", "severity"]);
25
+ const ACCEPTANCE_VERIFY_KEYS = new Set(["id", "command", "timeoutMs", "cwd", "env", "allowFailure"]);
26
+ const ACCEPTANCE_REVIEW_KEYS = new Set(["agent", "focus", "required"]);
27
+ function normalizeLevel(level) {
28
+ return level ?? "auto";
29
+ }
30
+ function unique(items) {
31
+ return [...new Set(items)];
32
+ }
33
+ function requiredEvidenceForLevel(level) {
34
+ switch (level) {
35
+ case "none":
36
+ return [];
37
+ case "attested":
38
+ return ["manual-notes", "residual-risks"];
39
+ case "checked":
40
+ return ["changed-files", "tests-added", "commands-run", "residual-risks", "no-staged-files"];
41
+ case "verified":
42
+ case "reviewed":
43
+ return ["changed-files", "tests-added", "commands-run", "validation-output", "residual-risks", "no-staged-files"];
44
+ }
45
+ }
46
+ function inferLevel(input) {
47
+ const agent = input.agentName.toLowerCase();
48
+ const task = input.task?.toLowerCase() ?? "";
49
+ const reasons = [];
50
+ const readOnlyAgent = /\b(?:reviewer|scout|context-builder|researcher|analyst)\b/.test(agent);
51
+ const readOnlyTask = /\b(?:read[- ]only|review[- ]only|do not edit|don't edit|no edits|without edits|inspect|summari[sz]e)\b/.test(task);
52
+ const writeTask = /\b(?:fix|implement|update|write|edit|modify|migrate|release|security|delete|remove|refactor|commit)\b/.test(task) || /\bworker\b/.test(agent);
53
+ const risky = Boolean(input.async && writeTask) || Boolean(input.dynamic) || Boolean(input.dynamicGroup) || /\b(?:release|migration|migrate|security|data[- ]loss|destructive|post-review|fix pass)\b/.test(task);
54
+ if (risky) {
55
+ reasons.push(input.async ? "async write-capable or risky run" : "risky write-capable run");
56
+ if (input.dynamic || input.dynamicGroup)
57
+ reasons.push("dynamic fanout context");
58
+ return {
59
+ level: "reviewed",
60
+ reasons,
61
+ criteria: ["Implement the requested change without widening scope", "Return evidence sufficient for an independent acceptance review"],
62
+ evidence: requiredEvidenceForLevel("reviewed"),
63
+ review: { agent: "reviewer", required: true }
64
+ };
65
+ }
66
+ if (writeTask && !readOnlyTask) {
67
+ reasons.push("write-capable worker/task");
68
+ return {
69
+ level: "checked",
70
+ reasons,
71
+ criteria: ["Implement the requested change without widening scope"],
72
+ evidence: requiredEvidenceForLevel("checked")
73
+ };
74
+ }
75
+ if (readOnlyAgent || readOnlyTask) {
76
+ reasons.push(readOnlyAgent ? "read-only/reviewer-style agent" : "read-only task wording");
77
+ return {
78
+ level: "attested",
79
+ reasons,
80
+ criteria: ["Return concrete findings with file paths and severity when applicable"],
81
+ evidence: ["review-findings", "residual-risks"]
82
+ };
83
+ }
84
+ reasons.push("default lightweight attestation");
85
+ return {
86
+ level: "attested",
87
+ reasons,
88
+ criteria: ["Return a concise result and residual risks when applicable"],
89
+ evidence: ["manual-notes", "residual-risks"]
90
+ };
91
+ }
92
+ export function normalizeAcceptanceInput(input) {
93
+ if (input === undefined || input === "auto")
94
+ return { level: "auto" };
95
+ if (input === false)
96
+ return { level: "none", reason: "disabled by deprecated false shorthand" };
97
+ if (typeof input === "string")
98
+ return { level: input };
99
+ return { ...input };
100
+ }
101
+ function explicitAcceptanceCanDisable(explicit) {
102
+ return explicit.level === "none" && typeof explicit.reason === "string" && explicit.reason.trim().length > 0;
103
+ }
104
+ export function validateAcceptanceInput(input, pathLabel = "acceptance") {
105
+ const errors = [];
106
+ if (input === undefined)
107
+ return errors;
108
+ if (input === false)
109
+ return errors;
110
+ if (typeof input === "string") {
111
+ if (!VALID_LEVELS.has(input))
112
+ errors.push(`${pathLabel} has invalid level '${input}'.`);
113
+ return errors;
114
+ }
115
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
116
+ errors.push(`${pathLabel} must be a string level, false, or an object.`);
117
+ return errors;
118
+ }
119
+ const value = input;
120
+ for (const key of Object.keys(value)) {
121
+ if (!ACCEPTANCE_CONFIG_KEYS.has(key))
122
+ errors.push(`${pathLabel}.${key} is not supported.`);
123
+ }
124
+ if (value.level !== undefined && (typeof value.level !== "string" || !VALID_LEVELS.has(value.level))) {
125
+ errors.push(`${pathLabel}.level must be one of auto, none, attested, checked, verified, reviewed.`);
126
+ }
127
+ if (value.level === "none" && (typeof value.reason !== "string" || !value.reason.trim())) {
128
+ errors.push(`${pathLabel}.reason is required when level is none.`);
129
+ }
130
+ if (value.reason !== undefined && typeof value.reason !== "string")
131
+ errors.push(`${pathLabel}.reason must be a string.`);
132
+ if (value.criteria !== undefined && !Array.isArray(value.criteria))
133
+ errors.push(`${pathLabel}.criteria must be an array.`);
134
+ if (Array.isArray(value.criteria)) {
135
+ for (const [index, criterion] of value.criteria.entries()) {
136
+ if (typeof criterion === "string")
137
+ continue;
138
+ const criterionPath = `${pathLabel}.criteria[${index}]`;
139
+ if (!criterion || typeof criterion !== "object" || Array.isArray(criterion)) {
140
+ errors.push(`${criterionPath} must be a string or an object.`);
141
+ continue;
142
+ }
143
+ const gate = criterion;
144
+ for (const key of Object.keys(gate)) {
145
+ if (!ACCEPTANCE_GATE_KEYS.has(key))
146
+ errors.push(`${criterionPath}.${key} is not supported.`);
147
+ }
148
+ if (typeof gate.id !== "string" || !gate.id.trim())
149
+ errors.push(`${criterionPath}.id is required.`);
150
+ if (typeof gate.must !== "string" || !gate.must.trim())
151
+ errors.push(`${criterionPath}.must is required.`);
152
+ if (gate.evidence !== undefined && !Array.isArray(gate.evidence))
153
+ errors.push(`${criterionPath}.evidence must be an array.`);
154
+ if (Array.isArray(gate.evidence)) {
155
+ for (const [evidenceIndex, item] of gate.evidence.entries()) {
156
+ if (typeof item !== "string" || !VALID_EVIDENCE.has(item)) {
157
+ errors.push(`${criterionPath}.evidence[${evidenceIndex}] is not a supported evidence kind.`);
158
+ }
159
+ }
160
+ }
161
+ if (gate.severity !== undefined && gate.severity !== "required" && gate.severity !== "recommended") {
162
+ errors.push(`${criterionPath}.severity must be required or recommended.`);
163
+ }
164
+ }
165
+ }
166
+ if (Array.isArray(value.evidence)) {
167
+ for (const [index, item] of value.evidence.entries()) {
168
+ if (typeof item !== "string" || !VALID_EVIDENCE.has(item)) {
169
+ errors.push(`${pathLabel}.evidence[${index}] is not a supported evidence kind.`);
170
+ }
171
+ }
172
+ } else if (value.evidence !== undefined) {
173
+ errors.push(`${pathLabel}.evidence must be an array.`);
174
+ }
175
+ if (value.verify !== undefined && !Array.isArray(value.verify))
176
+ errors.push(`${pathLabel}.verify must be an array.`);
177
+ if (Array.isArray(value.verify)) {
178
+ for (const [index, command] of value.verify.entries()) {
179
+ if (!command || typeof command !== "object" || Array.isArray(command)) {
180
+ errors.push(`${pathLabel}.verify[${index}] must be an object.`);
181
+ continue;
182
+ }
183
+ const cmd = command;
184
+ for (const key of Object.keys(cmd)) {
185
+ if (!ACCEPTANCE_VERIFY_KEYS.has(key))
186
+ errors.push(`${pathLabel}.verify[${index}].${key} is not supported.`);
187
+ }
188
+ if (typeof cmd.id !== "string" || !cmd.id.trim())
189
+ errors.push(`${pathLabel}.verify[${index}].id is required.`);
190
+ if (typeof cmd.command !== "string" || !cmd.command.trim())
191
+ errors.push(`${pathLabel}.verify[${index}].command is required.`);
192
+ if (cmd.timeoutMs !== undefined && (typeof cmd.timeoutMs !== "number" || !Number.isInteger(cmd.timeoutMs) || cmd.timeoutMs < 1)) {
193
+ errors.push(`${pathLabel}.verify[${index}].timeoutMs must be an integer >= 1.`);
194
+ }
195
+ if (cmd.cwd !== undefined && typeof cmd.cwd !== "string")
196
+ errors.push(`${pathLabel}.verify[${index}].cwd must be a string.`);
197
+ if (cmd.env !== undefined) {
198
+ if (!cmd.env || typeof cmd.env !== "object" || Array.isArray(cmd.env)) {
199
+ errors.push(`${pathLabel}.verify[${index}].env must be an object.`);
200
+ } else {
201
+ for (const [envKey, envValue] of Object.entries(cmd.env)) {
202
+ if (typeof envValue !== "string")
203
+ errors.push(`${pathLabel}.verify[${index}].env.${envKey} must be a string.`);
204
+ }
205
+ }
206
+ }
207
+ if (cmd.allowFailure !== undefined && typeof cmd.allowFailure !== "boolean") {
208
+ errors.push(`${pathLabel}.verify[${index}].allowFailure must be a boolean.`);
209
+ }
210
+ }
211
+ }
212
+ if (value.review !== undefined && value.review !== false) {
213
+ if (!value.review || typeof value.review !== "object" || Array.isArray(value.review)) {
214
+ errors.push(`${pathLabel}.review must be false or an object.`);
215
+ } else {
216
+ const review = value.review;
217
+ for (const key of Object.keys(review)) {
218
+ if (!ACCEPTANCE_REVIEW_KEYS.has(key))
219
+ errors.push(`${pathLabel}.review.${key} is not supported.`);
220
+ }
221
+ if (review.agent !== undefined && typeof review.agent !== "string")
222
+ errors.push(`${pathLabel}.review.agent must be a string.`);
223
+ if (review.focus !== undefined && typeof review.focus !== "string")
224
+ errors.push(`${pathLabel}.review.focus must be a string.`);
225
+ if (review.required !== undefined && typeof review.required !== "boolean")
226
+ errors.push(`${pathLabel}.review.required must be a boolean.`);
227
+ }
228
+ }
229
+ if (value.stopRules !== undefined && !Array.isArray(value.stopRules))
230
+ errors.push(`${pathLabel}.stopRules must be an array.`);
231
+ if (Array.isArray(value.stopRules)) {
232
+ for (const [index, item] of value.stopRules.entries()) {
233
+ if (typeof item !== "string")
234
+ errors.push(`${pathLabel}.stopRules[${index}] must be a string.`);
235
+ }
236
+ }
237
+ return errors;
238
+ }
239
+ function normalizeCriteria(criteria, evidence) {
240
+ return (criteria ?? []).map((criterion, index) => {
241
+ if (typeof criterion === "string") {
242
+ return { id: `criterion-${index + 1}`, must: criterion, evidence, severity: "required" };
243
+ }
244
+ return {
245
+ id: criterion.id?.trim() || `criterion-${index + 1}`,
246
+ must: criterion.must ?? "",
247
+ evidence: criterion.evidence?.filter((item) => VALID_EVIDENCE.has(item)) ?? evidence,
248
+ severity: criterion.severity ?? "required"
249
+ };
250
+ }).filter((criterion) => criterion.must.trim());
251
+ }
252
+ export function resolveEffectiveAcceptance(input) {
253
+ const explicit = normalizeAcceptanceInput(input.explicit);
254
+ const inferred = inferLevel(input);
255
+ const explicitLevel = normalizeLevel(explicit.level);
256
+ const level = explicitAcceptanceCanDisable(explicit) ? "none" : explicitLevel === "auto" ? inferred.level : LEVEL_RANK[explicitLevel] >= LEVEL_RANK[inferred.level] ? explicitLevel : inferred.level;
257
+ const evidence = unique([...level === inferred.level ? inferred.evidence : requiredEvidenceForLevel(level), ...explicit.evidence ?? []]);
258
+ const criteria = normalizeCriteria(explicit.criteria?.length ? explicit.criteria : inferred.criteria, evidence);
259
+ let review = explicit.review !== undefined ? explicit.review : inferred.review;
260
+ if (level === "reviewed" && explicitLevel !== "auto" && explicitLevel !== "reviewed" && explicit.review === undefined && review && review !== false) {
261
+ review = { ...review, required: false };
262
+ }
263
+ return {
264
+ level,
265
+ explicit: input.explicit !== undefined,
266
+ inferredReason: inferred.reasons,
267
+ criteria,
268
+ evidence,
269
+ verify: explicit.verify ?? [],
270
+ review,
271
+ stopRules: explicit.stopRules ?? [],
272
+ reason: explicit.reason
273
+ };
274
+ }
275
+ export function formatAcceptancePrompt(acceptance) {
276
+ if (acceptance.level === "none")
277
+ return "";
278
+ const lines = [
279
+ "",
280
+ "## Acceptance Contract",
281
+ `Acceptance level: ${acceptance.level}`,
282
+ "Completion is not accepted from prose alone. End with a structured acceptance report.",
283
+ "",
284
+ "Criteria:",
285
+ ...acceptance.criteria.length ? acceptance.criteria.map((criterion) => `- ${criterion.id}: ${criterion.must}`) : ["- Return the requested result."],
286
+ "",
287
+ `Required evidence: ${acceptance.evidence.join(", ") || "none"}`
288
+ ];
289
+ if (acceptance.verify.length > 0) {
290
+ lines.push("", "Runtime verification commands configured by parent:");
291
+ for (const command of acceptance.verify)
292
+ lines.push(`- ${command.id}: ${command.command}`);
293
+ }
294
+ if (acceptance.review && acceptance.review !== false) {
295
+ lines.push("", `Review gate: ${acceptance.review.required === false ? "optional" : "required"}${acceptance.review.agent ? ` by ${acceptance.review.agent}` : ""}.`);
296
+ if (acceptance.review.focus)
297
+ lines.push(`Review focus: ${acceptance.review.focus}`);
298
+ }
299
+ if (acceptance.stopRules.length > 0) {
300
+ lines.push("", "Stop rules:", ...acceptance.stopRules.map((rule) => `- ${rule}`));
301
+ }
302
+ lines.push("", "Finish with a fenced JSON block tagged `acceptance-report` in this shape:", "Use empty arrays when no items apply; array fields contain strings unless object entries are shown.", "```acceptance-report", JSON.stringify({
303
+ criteriaSatisfied: [{ id: "criterion-1", status: "satisfied", evidence: "specific proof" }],
304
+ changedFiles: ["src/file.ts"],
305
+ testsAddedOrUpdated: ["test/file.test.ts"],
306
+ commandsRun: [{ command: "command", result: "passed", summary: "short result" }],
307
+ validationOutput: ["validation output or concise summary"],
308
+ residualRisks: ["none"],
309
+ noStagedFiles: true,
310
+ diffSummary: "short description of the diff",
311
+ reviewFindings: ["blocker: file.ts:12 - issue found, or no blockers"],
312
+ manualNotes: "anything else the parent should know"
313
+ }, null, 2), "```");
314
+ return lines.join(`
315
+ `);
316
+ }
317
+ function extractBalancedJson(text, start) {
318
+ let depth = 0;
319
+ let inString = false;
320
+ let escaped = false;
321
+ for (let i = start;i < text.length; i++) {
322
+ const char = text[i];
323
+ if (inString) {
324
+ if (escaped)
325
+ escaped = false;
326
+ else if (char === "\\")
327
+ escaped = true;
328
+ else if (char === '"')
329
+ inString = false;
330
+ continue;
331
+ }
332
+ if (char === '"') {
333
+ inString = true;
334
+ continue;
335
+ }
336
+ if (char === "{")
337
+ depth++;
338
+ if (char === "}") {
339
+ depth--;
340
+ if (depth === 0)
341
+ return text.slice(start, i + 1);
342
+ }
343
+ }
344
+ return;
345
+ }
346
+ function unwrapAcceptanceReport(value) {
347
+ if (!value || typeof value !== "object" || Array.isArray(value))
348
+ return value;
349
+ const record = value;
350
+ if ("acceptance" in record)
351
+ return record.acceptance;
352
+ if ("acceptance-report" in record)
353
+ return record["acceptance-report"];
354
+ return value;
355
+ }
356
+ function isCommandsRunArray(value) {
357
+ return Array.isArray(value) && value.every((item) => {
358
+ if (!item || typeof item !== "object" || Array.isArray(item))
359
+ return false;
360
+ const command = item;
361
+ return typeof command.command === "string" && (command.result === "passed" || command.result === "failed" || command.result === "not-run") && typeof command.summary === "string";
362
+ });
363
+ }
364
+ function hasGenericAcceptanceReportSignal(value) {
365
+ if (!value || typeof value !== "object" || Array.isArray(value))
366
+ return false;
367
+ const record = value;
368
+ return "criteriaSatisfied" in record && (isStringArray(record.changedFiles) || isStringArray(record.testsAddedOrUpdated) || isCommandsRunArray(record.commandsRun) || isStringArray(record.validationOutput) || isStringArray(record.residualRisks) || typeof record.noStagedFiles === "boolean" || typeof record.diffSummary === "string" || isStringArray(record.reviewFindings) || typeof record.manualNotes === "string");
369
+ }
370
+ function parseReportJson(body) {
371
+ const trimmed = body.trim();
372
+ try {
373
+ return JSON.parse(trimmed);
374
+ } catch (error) {
375
+ const jsonStart = trimmed.indexOf("{");
376
+ if (jsonStart > 0) {
377
+ const json = extractBalancedJson(trimmed, jsonStart);
378
+ if (json)
379
+ return JSON.parse(json);
380
+ }
381
+ throw error;
382
+ }
383
+ }
384
+ function fencedBlocks(output, tag) {
385
+ return [...output.matchAll(new RegExp(`\`\`\`${tag}\\s*\\n([\\s\\S]*?)\`\`\``, "gi"))].map((match) => match[1]?.trim()).filter((value) => Boolean(value));
386
+ }
387
+ function validationPathLabelForWrapper(value) {
388
+ if (!value || typeof value !== "object" || Array.isArray(value))
389
+ return "";
390
+ const record = value;
391
+ if ("acceptance" in record)
392
+ return "acceptance";
393
+ if ("acceptance-report" in record)
394
+ return "acceptance-report";
395
+ return "";
396
+ }
397
+ function parseAcceptanceReportBody(body) {
398
+ const parsed = parseReportJson(body);
399
+ const report = unwrapAcceptanceReport(parsed);
400
+ return validateAcceptanceReport(report, validationPathLabelForWrapper(parsed));
401
+ }
402
+ function parseGenericJsonAcceptanceReportBody(body) {
403
+ const parsed = parseReportJson(body);
404
+ const report = unwrapAcceptanceReport(parsed);
405
+ const validation = validateAcceptanceReport(report);
406
+ if (!validation.report)
407
+ return;
408
+ return hasGenericAcceptanceReportSignal(validation.report) ? validation.report : undefined;
409
+ }
410
+ export function parseAcceptanceReport(output) {
411
+ const fenced = fencedBlocks(output, "acceptance-report");
412
+ const parseErrors = [];
413
+ for (const body of fenced) {
414
+ try {
415
+ const validation = parseAcceptanceReportBody(body);
416
+ if (validation.report)
417
+ return { report: validation.report };
418
+ parseErrors.push(`Invalid acceptance-report: ${validation.errors.join("; ")}`);
419
+ } catch (error) {
420
+ parseErrors.push(error instanceof Error ? error.message : String(error));
421
+ }
422
+ }
423
+ if (parseErrors.length > 0)
424
+ return { error: `Failed to parse acceptance-report: ${parseErrors.join("; ")}` };
425
+ for (const body of fencedBlocks(output, "(?:json|jsonc|json5)")) {
426
+ try {
427
+ const report = parseGenericJsonAcceptanceReportBody(body);
428
+ if (report)
429
+ return { report };
430
+ } catch {}
431
+ }
432
+ const markerIndex = output.search(/ACCEPTANCE_REPORT\s*:/i);
433
+ if (markerIndex !== -1) {
434
+ const jsonStart = output.indexOf("{", markerIndex);
435
+ if (jsonStart !== -1) {
436
+ const json = extractBalancedJson(output, jsonStart);
437
+ if (json) {
438
+ try {
439
+ const parsed = JSON.parse(json);
440
+ const report = unwrapAcceptanceReport(parsed);
441
+ const validation = validateAcceptanceReport(report, validationPathLabelForWrapper(parsed));
442
+ if (validation.report)
443
+ return { report: validation.report };
444
+ return { error: `Failed to parse acceptance-report: Invalid acceptance-report: ${validation.errors.join("; ")}` };
445
+ } catch (error) {
446
+ return { error: error instanceof Error ? error.message : String(error) };
447
+ }
448
+ }
449
+ }
450
+ }
451
+ return { error: "Structured acceptance report not found." };
452
+ }
453
+ export function stripAcceptanceReport(output) {
454
+ const trailingFencePattern = /\n?```(acceptance-report|json|jsonc|json5)\s*\n([\s\S]*?)```\s*/gi;
455
+ let trailingFence;
456
+ for (const match of output.matchAll(trailingFencePattern)) {
457
+ const end = (match.index ?? 0) + match[0].length;
458
+ if (output.slice(end).trim().length === 0 && match[1] && match[2]) {
459
+ trailingFence = { index: match.index ?? 0, tag: match[1].toLowerCase(), body: match[2] };
460
+ }
461
+ }
462
+ if (trailingFence) {
463
+ if (trailingFence.tag === "acceptance-report")
464
+ return output.slice(0, trailingFence.index).trimEnd();
465
+ try {
466
+ if (parseGenericJsonAcceptanceReportBody(trailingFence.body))
467
+ return output.slice(0, trailingFence.index).trimEnd();
468
+ } catch {}
469
+ }
470
+ return output.replace(/\n?```acceptance-report\s*\n[\s\S]*?```\s*$/i, "").replace(/\n?ACCEPTANCE_REPORT\s*:\s*\{[\s\S]*\}\s*$/i, "").trimEnd();
471
+ }
472
+ function isStringArray(value) {
473
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
474
+ }
475
+ function pathFor(base, segment) {
476
+ return base ? `${base}.${segment}` : segment;
477
+ }
478
+ function describeValidationValue(value) {
479
+ if (value === undefined)
480
+ return "missing";
481
+ if (value === null)
482
+ return "null";
483
+ if (Array.isArray(value))
484
+ return "array";
485
+ if (typeof value === "object")
486
+ return "object";
487
+ if (typeof value === "string") {
488
+ const short = value.length > 80 ? `${value.slice(0, 77)}...` : value;
489
+ return JSON.stringify(short);
490
+ }
491
+ return `${typeof value} ${String(value)}`;
492
+ }
493
+ function pushTypeError(errors, pathLabel, expected, value) {
494
+ errors.push(`${pathLabel}: expected ${expected}; got ${describeValidationValue(value)}`);
495
+ }
496
+ function validateStringArrayField(errors, value, pathLabel) {
497
+ if (!Array.isArray(value)) {
498
+ pushTypeError(errors, pathLabel, "string[]", value);
499
+ return;
500
+ }
501
+ for (const [index, item] of value.entries()) {
502
+ if (typeof item !== "string")
503
+ pushTypeError(errors, `${pathLabel}[${index}]`, "string", item);
504
+ }
505
+ }
506
+ function validateAcceptanceReport(value, pathLabel = "") {
507
+ const errors = [];
508
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
509
+ pushTypeError(errors, pathLabel || "acceptance-report", "object", value);
510
+ return { errors };
511
+ }
512
+ const report = value;
513
+ if (report.criteriaSatisfied !== undefined) {
514
+ if (!Array.isArray(report.criteriaSatisfied)) {
515
+ pushTypeError(errors, pathFor(pathLabel, "criteriaSatisfied"), "array", report.criteriaSatisfied);
516
+ } else {
517
+ for (const [index, item] of report.criteriaSatisfied.entries()) {
518
+ const itemPath = `${pathFor(pathLabel, "criteriaSatisfied")}[${index}]`;
519
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
520
+ pushTypeError(errors, itemPath, "object", item);
521
+ continue;
522
+ }
523
+ const criterion = item;
524
+ if (criterion.id !== undefined && typeof criterion.id !== "string")
525
+ pushTypeError(errors, `${itemPath}.id`, "string", criterion.id);
526
+ if (criterion.status !== "satisfied" && criterion.status !== "not-satisfied" && criterion.status !== "not-applicable") {
527
+ pushTypeError(errors, `${itemPath}.status`, 'one of "satisfied", "not-satisfied", "not-applicable"', criterion.status);
528
+ }
529
+ if (typeof criterion.evidence !== "string" || !criterion.evidence.trim())
530
+ pushTypeError(errors, `${itemPath}.evidence`, "non-empty string", criterion.evidence);
531
+ }
532
+ }
533
+ }
534
+ if (report.changedFiles !== undefined)
535
+ validateStringArrayField(errors, report.changedFiles, pathFor(pathLabel, "changedFiles"));
536
+ if (report.testsAddedOrUpdated !== undefined)
537
+ validateStringArrayField(errors, report.testsAddedOrUpdated, pathFor(pathLabel, "testsAddedOrUpdated"));
538
+ if (report.commandsRun !== undefined) {
539
+ if (!Array.isArray(report.commandsRun)) {
540
+ pushTypeError(errors, pathFor(pathLabel, "commandsRun"), "array", report.commandsRun);
541
+ } else {
542
+ for (const [index, item] of report.commandsRun.entries()) {
543
+ const itemPath = `${pathFor(pathLabel, "commandsRun")}[${index}]`;
544
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
545
+ pushTypeError(errors, itemPath, "object", item);
546
+ continue;
547
+ }
548
+ const command = item;
549
+ if (typeof command.command !== "string" || !command.command.trim())
550
+ pushTypeError(errors, `${itemPath}.command`, "non-empty string", command.command);
551
+ if (command.result !== "passed" && command.result !== "failed" && command.result !== "not-run") {
552
+ pushTypeError(errors, `${itemPath}.result`, 'one of "passed", "failed", "not-run"', command.result);
553
+ }
554
+ if (typeof command.summary !== "string")
555
+ pushTypeError(errors, `${itemPath}.summary`, "string", command.summary);
556
+ }
557
+ }
558
+ }
559
+ if (report.validationOutput !== undefined)
560
+ validateStringArrayField(errors, report.validationOutput, pathFor(pathLabel, "validationOutput"));
561
+ if (report.residualRisks !== undefined)
562
+ validateStringArrayField(errors, report.residualRisks, pathFor(pathLabel, "residualRisks"));
563
+ if (report.noStagedFiles !== undefined && typeof report.noStagedFiles !== "boolean")
564
+ pushTypeError(errors, pathFor(pathLabel, "noStagedFiles"), "boolean", report.noStagedFiles);
565
+ if (report.diffSummary !== undefined && typeof report.diffSummary !== "string")
566
+ pushTypeError(errors, pathFor(pathLabel, "diffSummary"), "string", report.diffSummary);
567
+ if (report.reviewFindings !== undefined)
568
+ validateStringArrayField(errors, report.reviewFindings, pathFor(pathLabel, "reviewFindings"));
569
+ if (report.manualNotes !== undefined && typeof report.manualNotes !== "string")
570
+ pushTypeError(errors, pathFor(pathLabel, "manualNotes"), "string", report.manualNotes);
571
+ if (report.notes !== undefined && typeof report.notes !== "string")
572
+ pushTypeError(errors, pathFor(pathLabel, "notes"), "string", report.notes);
573
+ if (errors.length > 0)
574
+ return { errors };
575
+ const hasReportField = report.criteriaSatisfied !== undefined || report.changedFiles !== undefined || report.testsAddedOrUpdated !== undefined || report.commandsRun !== undefined || report.validationOutput !== undefined || report.residualRisks !== undefined || report.noStagedFiles !== undefined || report.diffSummary !== undefined || report.manualNotes !== undefined || report.notes !== undefined || report.reviewFindings !== undefined;
576
+ return hasReportField ? { report, errors } : { errors: [`${pathLabel || "acceptance-report"}: expected at least one acceptance report field`] };
577
+ }
578
+ function checkCriteriaSatisfied(criteria, report) {
579
+ const reports = new Map((report.criteriaSatisfied ?? []).filter((item) => item.id).map((item) => [item.id, item]));
580
+ return criteria.filter((criterion) => criterion.severity !== "recommended").map((criterion) => {
581
+ const item = reports.get(criterion.id);
582
+ if (!item)
583
+ return { id: `criterion:${criterion.id}`, status: "failed", message: `Required criterion '${criterion.id}' was not reported.` };
584
+ if (item.status !== "satisfied")
585
+ return { id: `criterion:${criterion.id}`, status: "failed", message: `Required criterion '${criterion.id}' was reported as ${item.status}.` };
586
+ return { id: `criterion:${criterion.id}`, status: "passed", message: `Required criterion '${criterion.id}' satisfied.` };
587
+ });
588
+ }
589
+ function reportEvidencePresent(report, kind) {
590
+ switch (kind) {
591
+ case "changed-files":
592
+ return isStringArray(report.changedFiles) && report.changedFiles.length > 0;
593
+ case "tests-added":
594
+ return isStringArray(report.testsAddedOrUpdated) && report.testsAddedOrUpdated.length > 0;
595
+ case "commands-run":
596
+ return Array.isArray(report.commandsRun) && report.commandsRun.length > 0;
597
+ case "validation-output":
598
+ return isStringArray(report.validationOutput) && report.validationOutput.length > 0;
599
+ case "residual-risks":
600
+ return isStringArray(report.residualRisks);
601
+ case "no-staged-files":
602
+ return report.noStagedFiles === true;
603
+ case "diff-summary":
604
+ return typeof report.diffSummary === "string" && report.diffSummary.trim().length > 0;
605
+ case "review-findings":
606
+ return isStringArray(report.reviewFindings);
607
+ case "manual-notes":
608
+ return Boolean((report.manualNotes ?? report.notes)?.trim());
609
+ }
610
+ }
611
+ function checkNoStagedFiles(cwd) {
612
+ const result = spawnSync("git", ["status", "--short"], { cwd, encoding: "utf-8" });
613
+ if (result.status !== 0) {
614
+ return { id: "no-staged-files", status: "not-applicable", message: "git status unavailable; no staged-files check skipped" };
615
+ }
616
+ const staged = result.stdout.split(/\r?\n/).filter((line) => line.length >= 2 && line[0] !== " " && line[0] !== "?");
617
+ return staged.length === 0 ? { id: "no-staged-files", status: "passed", message: "No staged files detected." } : { id: "no-staged-files", status: "failed", message: `Staged files present: ${staged.join(", ")}` };
618
+ }
619
+ function runStructuralChecks(acceptance, report, cwd) {
620
+ const checks = [];
621
+ for (const kind of acceptance.evidence) {
622
+ const present = reportEvidencePresent(report, kind);
623
+ checks.push({
624
+ id: `evidence:${kind}`,
625
+ status: present ? "passed" : "failed",
626
+ message: present ? `${kind} evidence present.` : `${kind} evidence missing from child report.`
627
+ });
628
+ }
629
+ if (acceptance.evidence.includes("no-staged-files"))
630
+ checks.push(checkNoStagedFiles(cwd));
631
+ return checks;
632
+ }
633
+ function trimOutput(value) {
634
+ const trimmed = value.trim();
635
+ if (!trimmed)
636
+ return;
637
+ return trimmed.length > 12000 ? `${trimmed.slice(0, 12000)}
638
+ ...[truncated]` : trimmed;
639
+ }
640
+ function uniqueStrings(items) {
641
+ return unique(items.map((item) => item?.trim()).filter((item) => Boolean(item)));
642
+ }
643
+ export function aggregateAcceptanceReport(input) {
644
+ const childReports = input.results.map((result) => result.acceptance?.childReport).filter((report) => Boolean(report));
645
+ const blockers = input.results.filter((result) => result.exitCode !== 0 || result.acceptance?.status === "rejected");
646
+ const successfulChildren = input.results.length > 0 && blockers.length === 0;
647
+ return {
648
+ criteriaSatisfied: [
649
+ { id: "criterion-1", status: successfulChildren ? "satisfied" : "not-satisfied", evidence: successfulChildren ? `All ${input.results.length} dynamic child run(s) completed without child or acceptance blockers.` : "Dynamic fanout produced no accepted child evidence." },
650
+ { id: "criterion-2", status: successfulChildren ? "satisfied" : "not-satisfied", evidence: successfulChildren ? "Collected child acceptance evidence for aggregate review." : "Dynamic fanout produced no aggregate review evidence." },
651
+ ...input.results.map((result, index) => ({
652
+ id: `child-${index + 1}`,
653
+ status: result.exitCode === 0 && result.acceptance?.status !== "rejected" ? "satisfied" : "not-satisfied",
654
+ evidence: `${result.agent}: acceptance ${result.acceptance?.status ?? "unreported"}${result.error ? ` (${result.error})` : ""}`
655
+ }))
656
+ ],
657
+ changedFiles: uniqueStrings(childReports.flatMap((report) => report.changedFiles ?? [])),
658
+ testsAddedOrUpdated: uniqueStrings(childReports.flatMap((report) => report.testsAddedOrUpdated ?? [])),
659
+ commandsRun: childReports.flatMap((report) => report.commandsRun ?? []),
660
+ validationOutput: uniqueStrings(childReports.flatMap((report) => report.validationOutput ?? [])),
661
+ residualRisks: uniqueStrings([
662
+ ...childReports.flatMap((report) => report.residualRisks ?? []),
663
+ ...blockers.map((result) => `${result.agent}: ${result.error ?? "child or acceptance gate failed"}`)
664
+ ]),
665
+ noStagedFiles: childReports.length > 0 && childReports.every((report) => report.noStagedFiles === true),
666
+ reviewFindings: uniqueStrings(childReports.flatMap((report) => report.reviewFindings ?? [])),
667
+ manualNotes: input.notes ?? `Aggregated acceptance evidence from ${input.results.length} dynamic fanout child run(s).`,
668
+ notes: input.notes
669
+ };
670
+ }
671
+ function runVerifyCommand(command, defaultCwd, options = {}) {
672
+ return new Promise((resolve) => {
673
+ const startedAt = Date.now();
674
+ const cwd = command.cwd ? path.resolve(defaultCwd, command.cwd) : defaultCwd;
675
+ let stdout = "";
676
+ let stderr = "";
677
+ let timedOut = false;
678
+ let settled = false;
679
+ let hardKill;
680
+ const child = spawn(command.command, {
681
+ cwd,
682
+ env: { ...process.env, ...command.env ?? {} },
683
+ shell: true,
684
+ stdio: ["ignore", "pipe", "pipe"],
685
+ windowsHide: true
686
+ });
687
+ const finish = (result) => {
688
+ if (settled)
689
+ return;
690
+ settled = true;
691
+ clearTimeout(timeout);
692
+ if (hardKill)
693
+ clearTimeout(hardKill);
694
+ options.signal?.removeEventListener("abort", abortVerification);
695
+ resolve({
696
+ id: command.id,
697
+ command: command.command,
698
+ cwd,
699
+ durationMs: Date.now() - startedAt,
700
+ ...result
701
+ });
702
+ };
703
+ const abortVerification = () => {
704
+ if (settled || timedOut)
705
+ return;
706
+ timedOut = true;
707
+ child.kill("SIGTERM");
708
+ hardKill = setTimeout(() => {
709
+ child.kill("SIGKILL");
710
+ finish({
711
+ exitCode: null,
712
+ status: "timed-out",
713
+ stdout: trimOutput(stdout),
714
+ stderr: trimOutput(stderr || options.abortMessage || "Acceptance verification timed out.")
715
+ });
716
+ }, 1000);
717
+ hardKill.unref?.();
718
+ };
719
+ const timeout = setTimeout(abortVerification, command.timeoutMs ?? 120000);
720
+ timeout.unref?.();
721
+ if (options.signal?.aborted)
722
+ abortVerification();
723
+ else
724
+ options.signal?.addEventListener("abort", abortVerification, { once: true });
725
+ child.stdout.on("data", (chunk) => {
726
+ stdout += chunk.toString();
727
+ });
728
+ child.stderr.on("data", (chunk) => {
729
+ stderr += chunk.toString();
730
+ });
731
+ child.on("close", (exitCode) => {
732
+ const passed = exitCode === 0 && !timedOut;
733
+ finish({
734
+ exitCode,
735
+ status: timedOut ? "timed-out" : passed ? "passed" : command.allowFailure ? "allowed-failure" : "failed",
736
+ stdout: trimOutput(stdout),
737
+ stderr: trimOutput(stderr || (timedOut ? options.abortMessage ?? "" : ""))
738
+ });
739
+ });
740
+ child.on("error", (error) => {
741
+ finish({
742
+ exitCode: timedOut ? null : 1,
743
+ status: timedOut ? "timed-out" : command.allowFailure ? "allowed-failure" : "failed",
744
+ stderr: timedOut ? trimOutput(stderr || options.abortMessage || "Acceptance verification timed out.") : error instanceof Error ? error.message : String(error)
745
+ });
746
+ });
747
+ });
748
+ }
749
+ export async function evaluateAcceptance(input) {
750
+ const acceptance = input.acceptance;
751
+ const ledger = {
752
+ status: acceptance.level === "none" ? "not-required" : "claimed",
753
+ explicit: acceptance.explicit,
754
+ effectiveAcceptance: acceptance,
755
+ inferredReason: acceptance.inferredReason,
756
+ criteria: acceptance.criteria,
757
+ runtimeChecks: [],
758
+ verifyRuns: []
759
+ };
760
+ if (acceptance.level === "none")
761
+ return ledger;
762
+ const parsed = input.report ? { report: input.report } : parseAcceptanceReport(input.output);
763
+ if (parsed.report) {
764
+ ledger.childReport = parsed.report;
765
+ ledger.status = "attested";
766
+ } else {
767
+ ledger.childReportParseError = parsed.error;
768
+ ledger.runtimeChecks.push({ id: "attestation", status: "failed", message: parsed.error ?? "Structured acceptance report missing." });
769
+ ledger.status = "rejected";
770
+ return ledger;
771
+ }
772
+ if (LEVEL_RANK[acceptance.level] >= LEVEL_RANK.checked) {
773
+ ledger.runtimeChecks = [
774
+ ...checkCriteriaSatisfied(acceptance.criteria, parsed.report),
775
+ ...runStructuralChecks(acceptance, parsed.report, input.cwd)
776
+ ];
777
+ if (ledger.runtimeChecks.some((check) => check.status === "failed")) {
778
+ ledger.status = "rejected";
779
+ return ledger;
780
+ }
781
+ ledger.status = "checked";
782
+ }
783
+ if (LEVEL_RANK[acceptance.level] >= LEVEL_RANK.verified && (acceptance.level === "verified" || acceptance.verify.length > 0)) {
784
+ if (acceptance.level === "verified" && acceptance.verify.length === 0) {
785
+ ledger.runtimeChecks.push({ id: "verification-config", status: "failed", message: "verified acceptance requires runtime verify commands." });
786
+ ledger.status = "rejected";
787
+ return ledger;
788
+ }
789
+ ledger.verifyRuns = [];
790
+ for (const command of acceptance.verify) {
791
+ ledger.verifyRuns.push(await runVerifyCommand(command, input.cwd, { signal: input.signal, abortMessage: input.abortMessage }));
792
+ if (input.signal?.aborted)
793
+ break;
794
+ }
795
+ if (ledger.verifyRuns.some((run) => run.status === "failed" || run.status === "timed-out")) {
796
+ ledger.status = "rejected";
797
+ return ledger;
798
+ }
799
+ ledger.status = "verified";
800
+ }
801
+ if (acceptance.level === "reviewed") {
802
+ if (input.reviewResult) {
803
+ ledger.reviewResult = input.reviewResult;
804
+ ledger.status = input.reviewResult.status === "no-blockers" ? "reviewed" : "rejected";
805
+ } else {
806
+ const optionalReview = acceptance.review && acceptance.review !== false && acceptance.review.required === false;
807
+ ledger.reviewResult = {
808
+ status: "needs-parent-decision",
809
+ findings: [{
810
+ severity: acceptance.explicit && !optionalReview ? "blocker" : "non-blocking",
811
+ issue: "Reviewed acceptance requires an independent reviewer result.",
812
+ rationale: "The run cannot be marked reviewed from child evidence alone."
813
+ }]
814
+ };
815
+ if (acceptance.review === false || acceptance.explicit && !optionalReview)
816
+ ledger.status = "rejected";
817
+ }
818
+ }
819
+ return ledger;
820
+ }
821
+ export function acceptanceFailureMessage(ledger) {
822
+ if (ledger.status !== "rejected")
823
+ return;
824
+ const failedCheck = ledger.runtimeChecks.find((check) => check.status === "failed");
825
+ if (failedCheck)
826
+ return `Acceptance rejected: ${failedCheck.message}`;
827
+ const failedVerify = ledger.verifyRuns.find((run) => run.status === "failed" || run.status === "timed-out");
828
+ if (failedVerify)
829
+ return `Acceptance verification '${failedVerify.id}' ${failedVerify.status}.`;
830
+ if (ledger.reviewResult?.status === "needs-parent-decision")
831
+ return "Acceptance review required but no automatic reviewer result is available.";
832
+ if (ledger.reviewResult?.status === "blockers")
833
+ return "Acceptance review found blockers.";
834
+ return "Acceptance rejected.";
835
+ }