chainlesschain 0.162.162 → 0.162.164

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 (237) hide show
  1. package/package.json +3 -1
  2. package/src/assets/web-panel/assets/{AIOps-ClI2ALgv.js → AIOps-BqRkaHGb.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-BDjRcJaK.js → ActionButton-DWO7OZLC.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DikSRsaT.js → Analytics-C5oaVezM.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-9mLhMFmz.js → AppLayout-CsUESDoZ.js} +3 -3
  6. package/src/assets/web-panel/assets/{Artifacts-BSr88BrH.js → Artifacts-Dragqu5k.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-CdvyimOF.js → Audit-CQsaHctY.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-D3_hCedh.js → BackgroundAgents-DdG1jNSM.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-Cyvv9RHf.js → Backup-DN6sjK4z.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-BZM14kt8.js → BaseInput-Cxm_ZTCl.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-DrQSokVz.js → Chat-DVXGmA56.js} +6 -6
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-HwJR-C6B.js → ChatBubbleRenderer-BdaCL_iE.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-bFLYeWMV.js → Checkbox-DEc4vD_A.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-7mN7xJXT.js → Codegen-CKiTcv36.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-DAB5ZWm7.js → Col-Br9UUNN0.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-B_1TymaK.js → Community-NeUH_70T.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-Bq1uSWLJ.js → Compact-BqR-z2Df.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-CyiE1jEe.js → Compliance-DcWB_SgZ.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-Cpk0D7vm.js → Cowork-BfhgcudF.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-d_Lv3rdB.js → Cron-LaU29LgD.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-DMqhHjMR.js → Crosschain-B2ZB9uwW.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-Br-oPuBw.js → DID-BuHl38A2.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-CfLW_RaP.js → Dashboard-m627yqvZ.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-DZnTOExV.js → Dropdown-C7bxF8F5.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-DHIUHDv1.js → EmailListRenderer-DPLPQzLp.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-Bw3t3myu.js → FamilyGuardDashboard-cOiH6ike.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-D9cpQPAY.js → Federation-Bvkzg88z.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-Jdczr9Mr.js → FormItemContext-BTsiA7h_.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-BT8ZMpC5.js → GenericCardRenderer-BGnVE5qu.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-DjARip2b.js → Git-DS_j6r25.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-BgWbSRcj.js → Governance-CKRG3Rsl.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-CfouJciM.js → Inference-DW8INoNQ.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-50l7mrAr.js → KnowledgeGraph-BhEYeFhC.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-rq50H84B.js → Logs-wqi0uxuR.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-DUPIEdQH.js → MarkdownRenderer-DS8dE8ty.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-BYgPDmHU.js → Marketplace-CMlTuPLT.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-5DVgcFdx.js → McpTools-CSx4m5JJ.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-B70LPk7f.js → Memory-vnQCUy-_.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-De8ssA5Y.js → MobileBridge-BV2NA742.js} +1 -1
  40. package/src/assets/web-panel/assets/MobileProjects-Bw0k8e1B.js +1 -0
  41. package/src/assets/web-panel/assets/{Mtc-RJsOTsIA.js → Mtc-CzK_FPB1.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-Crh-6BEK.js → MtcAudit-Cb7G9oL3.js} +5 -5
  43. package/src/assets/web-panel/assets/{Multisig-wuoR5l3x.js → Multisig-qJxlUyzr.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-U2Mji4fZ.js → NLProgramming-DqaBJ7Te.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-CdTZAyDH.js → Notes-CwhTJkRi.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-C-FucWE7.js → NotificationSettings-YDs4Fal4.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-DCYFTeaI.js → OrderTableRenderer-CvEALpq7.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-an3OF85L.js → Organization-CEmQm9n8.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-C-1vmYFe.js → Overflow-zby_P_LA.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-Cwh47xKg.js → P2P-DsmDNFNT.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-Dm38GT0k.js → PdhVaultBrowser-FWwFlZGF.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-DS2Y8awD.js → Permissions-DuzQ6me5.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-BQNziA0I.js → PersonalDataHub-DZbeN613.js} +3 -3
  54. package/src/assets/web-panel/assets/{Pipeline-DF85L1ue.js → Pipeline-CoNSVAUS.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-BuLornF-.js → Privacy-OYJEAaMC.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-C3Y31IwX.js → ProjectInit-CWIapV--.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-BCPiI8aD.js → ProjectSettings-CX8xMxZn.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-BZ6s4k6g.js → Projects-BLCN8YyY.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-D3Wp022X.js → Providers-BV_sxp75.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-CUJfQkH4.js → QrScannerModal-BTnRrHRp.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-UAc0EO8l.js → QuickAsk-D1KZg018.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-BaxKupcs.js → Recommend-COm5cuNg.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-C75NNnXZ.js → RemoteSession-0saWEkYI.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-BVXynZAk.js → Reputation-Dr7iKLS2.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-C5MRZ0bi.js → Row-C1vpPBcr.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-DAKkEYka.js → RssFeed-CxlWrtNj.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-DvWv9YOA.js → Search-BT2V8dPN.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-B5peafW1.js → Security-bm9V3c5k.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-UQmvPEaG.js → Services-CocH1yyX.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-C3bhWpuF.js → Skeleton-DHItlMdd.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-DfkKf9_H.js → Skills-C59sLc4w.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-DqMSjlBM.js → Sla-Crj4BMc8.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-C-WbxWUX.js → SpeechSettings-DX4KFceY.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-CYuHZ4N2.js → SyncSettings-BD_iwQLr.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-CyeAyB9d.js → Tasks-BmCTzYsH.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-BTCeQnpq.js → Templates-BGUaxZYl.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-BifQuA9Y.js → Tenant-BS0tJx2O.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-Bx_9Bocf.js → Terminal--tHSAsld.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-BX0RFCOm.js → TimelineRenderer-D9-MFxS4.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-CGHrCmUS.js → Tokens-CiCB485f.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-DlqaY4SO.js → Trigger-SmCFuW_l.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-DfOmj-ay.js → Trust-DkK3Rqe0.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-B72jH5fE.js → UkeySign-CDM4LGdB.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-BT-63fqv.js → VideoEditing-IDB7DJXt.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-Dsul3sHr.js → Wallet-Bikj5cmt.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-Izkwwdgy.js → WebAuthn-C9abWlAw.js} +2 -2
  87. package/src/assets/web-panel/assets/{WorkflowEditor-D3FHSiH1.js → WorkflowEditor-DrbRKVky.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-CmVz5z3U.js → chat-CccCSDQf.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-CuLgyJBp.js → colors-CXObXLhb.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-Ml1YkXpw.js → compact-item-B5kfYAxC.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-BvjJ48bs.js → createContext-DmiPBfip.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-CvKrADNa.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-DVC0157C.js → hasIn-Q6lYteAc.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-C229ABfy.js → index-25Wlqps4.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-Ck_52dz_.js → index-33U29UNV.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-QXzlT9KD.js → index-4oXBDczR.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-4SBta3d7.js → index-BGYXvMc5.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-B0S8NzO6.js → index-BJCCa0s_.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DJyN28Et.js → index-BJJ5DhKI.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DC_OI9H2.js → index-BLOzViJ6.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-C_XuRgGn.js → index-BRrbUMoG.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-wvb_FtaV.js → index-BUFpT17Q.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-KMqhcF7l.js → index-BkRDiqGc.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-vG4XSJ1d.js → index-BsfvW6my.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DYXHFr4r.js → index-By-kfPED.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DuGszaWY.js → index-BzFTcQsc.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CXkJH-lK.js → index-C2FXdboq.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Dt5KMjwJ.js → index-C5XOlTHN.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-4wcgvPoF.js → index-CLhBDS2w.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-C_H_S-DY.js → index-CMq7UeoT.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BydjdZnl.js → index-CVMTWNKl.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BjHBpRYZ.js → index-Caghi-yL.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Bzr0rB5T.js → index-CdB6FuSH.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BbPtzB8M.js → index-CoK8-kWQ.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BUt8kTIB.js → index-CsO3GUAY.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-C_vRgTl9.js → index-CwZBa20Z.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DukDVXCv.js → index-D-kASucw.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BbepULSc.js → index-D8eBw7vW.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BfuKP4qe.js → index-D9wgifk4.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BiC6dQZG.js → index-DAYCTc07.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CmFNvNLZ.js → index-DF6Q8l2h.js} +1 -1
  122. package/src/assets/web-panel/assets/index-DTdFUWc_.js +1 -0
  123. package/src/assets/web-panel/assets/{index-KjgKdZe-.js → index-DgTLApS9.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BpFJHiTv.js → index-DgxVJ8pg.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Cw0Rjy87.js → index-DlJvIqoR.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CFYVQMvu.js → index-Dw4eHfsG.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CudmwNBV.js → index-Rh-ytwow.js} +3 -3
  128. package/src/assets/web-panel/assets/{index-DDGaOWBV.js → index-StAEt2pq.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-D3lD5qap.js → index-bzfw951k.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BzG-x9p4.js → index-fSUMbMvv.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-B_EvG_GW.js → index-tFHy5Wol.js} +1 -1
  132. package/src/assets/web-panel/assets/index-zQ0M_kz-.js +1 -0
  133. package/src/assets/web-panel/assets/{initDefaultProps-BDgd2YZ8.js → initDefaultProps-BNq7X72N.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-ErtrXdYe.js → motion-DOf9viTc.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-_GPPNCgs.js → move-CRHTcW4x.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-DRLL-8V2.js → mtc-parser-Cyoozgm2.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-p86sl023.js → omit-6-TjFNru.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-BTu7cMLH.js → pickAttrs-DlUmO1zi.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-BEKz6Isr.js → placementArrow-CFOL8mIR.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-Ck42c9MP.js → responsiveObserve-BtaFK5zd.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-1CEvuPsB.js → slide-BIbc2bur.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-BDfdx1GJ.js → statusUtils-BXX9d6-w.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-B4K46m5w.js → styleChecker-BGv10Bpn.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-DoIVpJNq.js → useFlexGapSupport-bKnKT8tw.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DdAWi-ln.js → useFs-FY4IL4jn.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-iUu6k8dx.js → usePersonalDataHub-Bwl9TEgu.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-DZz4R6Tw.js → vnode-BM3OHxAT.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-DXlf9sA3.js → zoom-D1fbFiHB.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/agenda.js +320 -32
  151. package/src/commands/agent.js +105 -11
  152. package/src/commands/context.js +125 -14
  153. package/src/commands/doctor.js +85 -1
  154. package/src/commands/hook.js +181 -7
  155. package/src/commands/plugin.js +456 -1
  156. package/src/commands/review.js +363 -2
  157. package/src/commands/session.js +191 -0
  158. package/src/commands/team.js +19 -1
  159. package/src/data/changelog.json +10 -4
  160. package/src/gateways/ws/remote-session-protocol.js +39 -0
  161. package/src/gateways/ws/session-protocol.js +55 -2
  162. package/src/gateways/ws/ws-agent-handler.js +50 -0
  163. package/src/harness/jsonl-session-store.js +60 -0
  164. package/src/harness/worktree-isolator.js +140 -5
  165. package/src/lib/agent-schedule-store.js +318 -10
  166. package/src/lib/agent-team/team-worktree.js +37 -2
  167. package/src/lib/agent-tool-admission.js +125 -0
  168. package/src/lib/app-preview.js +360 -0
  169. package/src/lib/async-hook-supervisor.cjs +54 -0
  170. package/src/lib/capability-manifest.js +319 -0
  171. package/src/lib/capability-negotiation.js +23 -12
  172. package/src/lib/channels/channel-manager.js +54 -2
  173. package/src/lib/cli-context-engineering.js +9 -2
  174. package/src/lib/context-breakdown.js +116 -0
  175. package/src/lib/diagnostic-bundle.js +230 -0
  176. package/src/lib/doctor-checkup.js +275 -4
  177. package/src/lib/execution-location.js +286 -0
  178. package/src/lib/headless-manifest.js +13 -40
  179. package/src/lib/hook-event-log.cjs +193 -0
  180. package/src/lib/hook-stats-store.cjs +197 -0
  181. package/src/lib/idempotency.js +109 -0
  182. package/src/lib/interaction-adapter.js +34 -4
  183. package/src/lib/json-schema-output.js +46 -12
  184. package/src/lib/json-schema-validate.js +143 -3
  185. package/src/lib/jsonl-session-store.js +1 -0
  186. package/src/lib/lsp/__tests__/lsp-manager.test.js +58 -0
  187. package/src/lib/lsp/lsp-manager.js +56 -0
  188. package/src/lib/monitor-event.js +290 -0
  189. package/src/lib/operation-fingerprint.js +217 -0
  190. package/src/lib/plugin-runtime/bin.js +37 -0
  191. package/src/lib/plugin-runtime/capabilities.js +19 -0
  192. package/src/lib/plugin-runtime/capability-consent.js +203 -0
  193. package/src/lib/plugin-runtime/hooks.js +37 -0
  194. package/src/lib/plugin-runtime/lsp.js +38 -0
  195. package/src/lib/plugin-runtime/mcp.js +40 -0
  196. package/src/lib/plugin-runtime/monitors.js +37 -0
  197. package/src/lib/plugin-runtime/plugin-options.js +151 -0
  198. package/src/lib/plugin-runtime/policy.js +62 -0
  199. package/src/lib/plugin-runtime/scopes.js +20 -4
  200. package/src/lib/pr-automation-policy.js +224 -0
  201. package/src/lib/project-instructions.js +128 -0
  202. package/src/lib/remote-approval-bridge.js +66 -13
  203. package/src/lib/repl-goal.js +132 -0
  204. package/src/lib/repl-rewind.js +153 -0
  205. package/src/lib/review-comment-anchor.js +241 -0
  206. package/src/lib/review-pipeline.js +46 -7
  207. package/src/lib/runtime-checkup.js +221 -0
  208. package/src/lib/schedule-planner.js +17 -1
  209. package/src/lib/secret-scan.js +409 -0
  210. package/src/lib/session-branch.js +0 -0
  211. package/src/lib/settings-hook-events.cjs +13 -0
  212. package/src/lib/settings-loader.cjs +42 -0
  213. package/src/lib/side-effect-ledger-store.js +88 -0
  214. package/src/lib/side-effect-ledger.js +401 -0
  215. package/src/lib/skill-loader.js +32 -0
  216. package/src/lib/skill-path-scope.js +63 -0
  217. package/src/lib/sub-agent-context.js +110 -9
  218. package/src/lib/sub-agent-registry.js +19 -0
  219. package/src/lib/subagent-inheritance.js +112 -0
  220. package/src/lib/telemetry/span-recorder.js +17 -4
  221. package/src/lib/terminal-context.js +195 -0
  222. package/src/lib/turn-binding-store.js +119 -0
  223. package/src/lib/unattended-action-policy.js +182 -0
  224. package/src/lib/worktree-cleanup-safety.js +138 -0
  225. package/src/lib/worktree-isolator.js +8 -0
  226. package/src/lib/worktree-sparse.js +131 -0
  227. package/src/repl/agent-repl.js +280 -30
  228. package/src/repl/permission-tier.js +28 -0
  229. package/src/runtime/agent-core.js +565 -21
  230. package/src/runtime/coding-agent-contract-shared.cjs +60 -1
  231. package/src/runtime/diagnostics.js +60 -0
  232. package/src/runtime/headless-runner.js +393 -1
  233. package/src/runtime/headless-stream.js +60 -1
  234. package/src/assets/web-panel/assets/MobileProjects-D10VISps.js +0 -1
  235. package/src/assets/web-panel/assets/devWarning-DhiGrbsh.js +0 -1
  236. package/src/assets/web-panel/assets/index-B7a1TrU6.js +0 -1
  237. package/src/assets/web-panel/assets/index-C2B1KRja.js +0 -1
@@ -614,7 +614,7 @@ const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
614
614
  type: "string",
615
615
  enum: ["worktree"],
616
616
  description:
617
- "Optional isolation mode: \"worktree\" runs the sub-agent in a fresh git worktree so its file edits land on an isolated branch (diff/merge summary comes back with the result).",
617
+ 'Optional isolation mode: "worktree" runs the sub-agent in a fresh git worktree so its file edits land on an isolated branch (diff/merge summary comes back with the result).',
618
618
  },
619
619
  },
620
620
  required: ["task"],
@@ -959,6 +959,65 @@ const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
959
959
  type: "string",
960
960
  description: "Optional human label for list/cancel",
961
961
  },
962
+ expires: {
963
+ type: "string",
964
+ description:
965
+ "For wakeup/cron/monitor: retire the entry after this long if it hasn't fired (e.g. 2h, 7d); it then never fires",
966
+ },
967
+ jitter: {
968
+ type: "string",
969
+ description:
970
+ "For wakeup/cron/monitor: a random-but-stable spread (e.g. 30s) so entries sharing a schedule fan out instead of firing all at once",
971
+ },
972
+ permission_mode: {
973
+ type: "string",
974
+ enum: [
975
+ "plan",
976
+ "manual",
977
+ "default",
978
+ "acceptEdits",
979
+ "auto",
980
+ "dontAsk",
981
+ "bypassPermissions",
982
+ ],
983
+ description:
984
+ "For wakeup/cron: run the fired `cc agent` with this permission mode instead of the ambient one (e.g. `plan` for a read-only report, `manual` to gate every action)",
985
+ },
986
+ worktree: {
987
+ type: "boolean",
988
+ description:
989
+ "For wakeup/cron: run the fired `cc agent` in a fresh git worktree so its changes stay isolated",
990
+ },
991
+ max_turns: {
992
+ type: "number",
993
+ description:
994
+ "For wakeup/cron: cap the fired agent's loop iterations (a per-task budget)",
995
+ },
996
+ goal_condition: {
997
+ type: "string",
998
+ description:
999
+ "For wakeup/cron: run the fired agent to a completion condition, re-driving outer turns until met. Prefix exit-zero:/file-exists:/contains:/regex: (deterministic) or model:<desc> (model-judged). Invalid specs are dropped.",
1000
+ },
1001
+ max_outer_turns: {
1002
+ type: "number",
1003
+ description:
1004
+ "For wakeup/cron with goal_condition: max outer re-drive turns (default 10, hard cap 100)",
1005
+ },
1006
+ goal_max_tokens: {
1007
+ type: "number",
1008
+ description:
1009
+ "For wakeup/cron with goal_condition: token budget across outer turns",
1010
+ },
1011
+ goal_max_cost: {
1012
+ type: "number",
1013
+ description:
1014
+ "For wakeup/cron with goal_condition: cost budget (USD) across outer turns",
1015
+ },
1016
+ goal_max_time: {
1017
+ type: "number",
1018
+ description:
1019
+ "For wakeup/cron with goal_condition: wall-clock budget (ms) across outer turns",
1020
+ },
962
1021
  id: {
963
1022
  type: "string",
964
1023
  description: "For cancel: the schedule entry id to remove",
@@ -288,6 +288,66 @@ export async function collectDoctorReport() {
288
288
  };
289
289
  }
290
290
 
291
+ /**
292
+ * Parse `git worktree list --porcelain` output into `{ path, branch }` entries.
293
+ *
294
+ * Pure (no I/O) so it can be unit-tested against fixed fixtures. A detached
295
+ * worktree has no `branch` line and yields `branch: null`.
296
+ */
297
+ export function parseGitWorktrees(porcelain) {
298
+ if (!porcelain || typeof porcelain !== "string") return [];
299
+ const entries = [];
300
+ let cur = null;
301
+ for (const line of porcelain.split("\n")) {
302
+ const trimmed = line.trimEnd();
303
+ if (trimmed.startsWith("worktree ")) {
304
+ if (cur) entries.push(cur);
305
+ cur = { path: trimmed.slice("worktree ".length).trim(), branch: null };
306
+ } else if (trimmed.startsWith("branch ") && cur) {
307
+ cur.branch = trimmed
308
+ .slice("branch ".length)
309
+ .replace("refs/heads/", "")
310
+ .trim();
311
+ }
312
+ }
313
+ if (cur) entries.push(cur);
314
+ return entries;
315
+ }
316
+
317
+ /** Best-effort live git worktree list for the current repo. */
318
+ export function collectWorktrees() {
319
+ return parseGitWorktrees(safeExec("git worktree list --porcelain"));
320
+ }
321
+
322
+ /**
323
+ * Map a doctor report + ambient runtime signals into the input contract of
324
+ * `buildDiagnosticBundle` (src/lib/diagnostic-bundle.js).
325
+ *
326
+ * Pure & deterministic aside from reading this process's own platform tags:
327
+ * the caller gathers live signals (env, worktrees, lockfiles) and passes them
328
+ * in, so the mapping itself is unit-testable. Session-runtime fields
329
+ * (connectionState, reconnectHistory, trace, redactionEvents) are absent from a
330
+ * cold `cc doctor` invocation and left to the bundle builder's safe defaults.
331
+ */
332
+ export function buildDoctorDiagnosticInput(report, signals = {}) {
333
+ const r = report || {};
334
+ return {
335
+ version: r.version || null,
336
+ platform: {
337
+ os: process.platform,
338
+ arch: process.arch,
339
+ node: process.versions?.node || null,
340
+ },
341
+ ports: (Array.isArray(r.ports) ? r.ports : [])
342
+ .filter((p) => p && p.open)
343
+ .map((p) => ({ port: p.port, proto: p.name })),
344
+ worktrees: Array.isArray(signals.worktrees) ? signals.worktrees : [],
345
+ lockfiles: Array.isArray(signals.lockfiles) ? signals.lockfiles : [],
346
+ env: signals.env && typeof signals.env === "object" ? signals.env : {},
347
+ notes: signals.notes != null ? signals.notes : null,
348
+ };
349
+ }
350
+
291
351
  /**
292
352
  * Collect a machine-readable status report.
293
353
  *
@@ -44,11 +44,20 @@ import {
44
44
  appendTokenUsage as jsonlAppendTokenUsage,
45
45
  appendToolCallCompact as jsonlAppendToolCallCompact,
46
46
  appendCompactEvent as jsonlAppendCompactEvent,
47
+ appendEvent as jsonlAppendEvent,
48
+ readEvents as jsonlReadEvents,
47
49
  rebuildMessages as jsonlRebuildMessages,
48
50
  sessionExists as jsonlSessionExists,
49
51
  getLastSessionId as jsonlGetLastSessionId,
50
52
  verifySession as jsonlVerifySession,
51
53
  } from "../harness/jsonl-session-store.js";
54
+ import {
55
+ SideEffectLedger,
56
+ reconcileSideEffects,
57
+ classifyToolSideEffect,
58
+ } from "../lib/side-effect-ledger.js";
59
+ import { SIDE_EFFECT_LEDGER_EVENT } from "../lib/side-effect-ledger-store.js";
60
+ import { operationIdempotencyKey } from "../lib/idempotency.js";
52
61
  import { expandFileRefsAsync } from "./file-ref-expander.js";
53
62
  import { composeSystemPrompt } from "./system-prompt.js";
54
63
  import { buildUserContent } from "../lib/image-input.js";
@@ -461,6 +470,8 @@ export async function runAgentHeadless(options = {}, deps = {}) {
461
470
  appendToolCallCompact:
462
471
  deps.appendToolCallCompact || jsonlAppendToolCallCompact,
463
472
  appendCompactEvent: deps.appendCompactEvent || jsonlAppendCompactEvent,
473
+ appendEvent: deps.appendEvent || jsonlAppendEvent,
474
+ readEvents: deps.readEvents || jsonlReadEvents,
464
475
  getLastSessionId: deps.getLastSessionId || jsonlGetLastSessionId,
465
476
  verifySession: deps.verifySession || jsonlVerifySession,
466
477
  };
@@ -684,6 +695,81 @@ export async function runAgentHeadless(options = {}, deps = {}) {
684
695
  }
685
696
  }
686
697
 
698
+ // ── P0-2: crash-safe side-effect ledger ────────────────────────────────
699
+ // Dangerous tools (file writes, opaque shell, git push, publish/schedule/
700
+ // notify/browser actions) are recorded prepare→start→commit|fail and the
701
+ // full snapshot persisted before the effect settles, so a worker killed
702
+ // mid-flight does NOT blindly replay an effect that may already have landed.
703
+ // Only active when persisting (an ephemeral/one-shot run can't be resumed);
704
+ // the byte-for-byte default path is untouched when no dangerous tool runs.
705
+ const sideEffectRunNonce = String(deps.now ? deps.now() : Date.now());
706
+ let sideEffectLedger = new SideEffectLedger({ clock: deps.now || null });
707
+ let sideEffectSeq = 0;
708
+ let currentSideEffectOpId = null;
709
+ let resumeSideEffectContext = null;
710
+ const persistSideEffectLedger = () => {
711
+ if (!persist) return;
712
+ try {
713
+ store.appendEvent(
714
+ sessionId,
715
+ SIDE_EFFECT_LEDGER_EVENT,
716
+ sideEffectLedger.toJSON(),
717
+ );
718
+ } catch {
719
+ // best-effort — never fail the run over ledger persistence
720
+ }
721
+ };
722
+ if (persist) {
723
+ try {
724
+ const events = store.readEvents(sessionId) || [];
725
+ for (let i = events.length - 1; i >= 0; i--) {
726
+ const e = events[i];
727
+ if (
728
+ e &&
729
+ e.type === SIDE_EFFECT_LEDGER_EVENT &&
730
+ e.data &&
731
+ Array.isArray(e.data.ops)
732
+ ) {
733
+ sideEffectLedger = SideEffectLedger.fromJSON(e.data, {
734
+ clock: deps.now || null,
735
+ });
736
+ break;
737
+ }
738
+ }
739
+ } catch {
740
+ // fresh ledger — best effort
741
+ }
742
+ // On resume, surface any operation that was in flight when the prior run
743
+ // died: its outcome is UNKNOWN, so the model is told to VERIFY before any
744
+ // replay rather than silently re-issue an irreversible effect.
745
+ if (resumeId) {
746
+ try {
747
+ const plan = reconcileSideEffects(sideEffectLedger);
748
+ if (plan.inspect.length > 0) {
749
+ const lines = plan.plans
750
+ .filter((p) => p.action === "inspect")
751
+ .map((p) => {
752
+ const op = sideEffectLedger.get(p.opId);
753
+ const kind = op?.kind || "unknown";
754
+ const key = op?.key ? ` (${op.key})` : "";
755
+ return ` • [${kind}]${key} — ${p.reason}`;
756
+ });
757
+ resumeSideEffectContext =
758
+ "Recovery notice — the previous run was interrupted while these " +
759
+ "irreversible operations were in flight; their outcome is UNKNOWN. " +
760
+ "Do NOT blindly re-run them. Verify whether each already took " +
761
+ "effect before repeating it, and ask the user if unsure:\n" +
762
+ lines.join("\n");
763
+ writeErr(
764
+ `⚠ ${plan.inspect.length} interrupted side-effect(s) need verification before replay (resume ${resumeId}).\n`,
765
+ );
766
+ }
767
+ } catch {
768
+ resumeSideEffectContext = null;
769
+ }
770
+ }
771
+ }
772
+
687
773
  // ── Wire the persistent ApprovalGate with our non-interactive confirmer
688
774
  // and force the session-policy tier dictated by --permission-mode. ──────
689
775
  let approvalGate = null;
@@ -744,12 +830,35 @@ export async function runAgentHeadless(options = {}, deps = {}) {
744
830
  outputStyleBody = null;
745
831
  }
746
832
 
833
+ // Large-monorepo context lever: `instructionExcludes` (settings.json or an
834
+ // explicit caller/SDK option) suppresses cc.md/CLAUDE.md/AGENTS.md, path-scoped
835
+ // rules, and @imports that resolve into legacy/vendor/generated subtrees.
836
+ // Explicit option wins; otherwise union across the layered settings files.
837
+ let instructionExcludes = Array.isArray(options.instructionExcludes)
838
+ ? options.instructionExcludes
839
+ : null;
840
+ if (!instructionExcludes) {
841
+ try {
842
+ const { readStringArraySetting } =
843
+ await import("../lib/settings-loader.cjs");
844
+ const fromSettings = readStringArraySetting("instructionExcludes", {
845
+ cwd,
846
+ settingsFile: options.settingsFile,
847
+ });
848
+ if (fromSettings && fromSettings.length)
849
+ instructionExcludes = fromSettings;
850
+ } catch {
851
+ instructionExcludes = null; // fail-open
852
+ }
853
+ }
854
+
747
855
  const systemContent = composeSystemPrompt(
748
856
  buildSystemPrompt(cwd, { additionalDirectories }),
749
857
  {
750
858
  systemPrompt: options.systemPrompt,
751
859
  appendSystemPrompt: options.appendSystemPrompt,
752
860
  outputStyle: outputStyleBody,
861
+ instructionExcludes,
753
862
  },
754
863
  );
755
864
 
@@ -836,6 +945,9 @@ export async function runAgentHeadless(options = {}, deps = {}) {
836
945
  ...(sessionStartContext
837
946
  ? [{ role: "system", content: sessionStartContext }]
838
947
  : []),
948
+ ...(resumeSideEffectContext
949
+ ? [{ role: "system", content: resumeSideEffectContext }]
950
+ : []),
839
951
  ...history,
840
952
  { role: "user", content: userMessageContent },
841
953
  ]);
@@ -1040,7 +1152,7 @@ export async function runAgentHeadless(options = {}, deps = {}) {
1040
1152
  try {
1041
1153
  const { AsyncHookSupervisor } =
1042
1154
  await import("../lib/async-hook-supervisor.cjs");
1043
- _hookSupervisor = new AsyncHookSupervisor();
1155
+ _hookSupervisor = new AsyncHookSupervisor({ persistStats: true });
1044
1156
  } catch {
1045
1157
  _hookSupervisor = null; // async hooks are best-effort
1046
1158
  }
@@ -1068,6 +1180,10 @@ export async function runAgentHeadless(options = {}, deps = {}) {
1068
1180
  model,
1069
1181
  provider,
1070
1182
  recorder: _otlpRecorder,
1183
+ // --otlp-content: opt in to stamping prompt CONTENT onto --otlp spans. Off by
1184
+ // default → content stays redacted (agent-core omits it entirely, so default
1185
+ // OTLP output is byte-identical). Only meaningful when a recorder is attached.
1186
+ otlpIncludeContent: options.otlpContent === true,
1071
1187
  hookSupervisor: _hookSupervisor,
1072
1188
  // Extended thinking (Anthropic; opt-in via --think/--ultrathink). null/off
1073
1189
  // → chatWithTools sends no thinking field. thinkingBudget (--thinking-budget)
@@ -1091,6 +1207,13 @@ export async function runAgentHeadless(options = {}, deps = {}) {
1091
1207
  approvalGate,
1092
1208
  permissionRules,
1093
1209
  settingsHooks,
1210
+ // Seed the subagent-contract CEILING with this run's permission mode so a
1211
+ // spawned sub-agent inherits/tightens from it (tighten-only): a
1212
+ // `--permission-mode bypassPermissions` run can hand a child bypass (→ allow
1213
+ // confirmer), a `--permission-mode plan` run clamps children to read-only,
1214
+ // while a "default" run resolves children to "default" exactly as before
1215
+ // (byte-identical — the previous null ceiling also yielded "default").
1216
+ subAgentContract: { permissionMode: options.permissionMode || "default" },
1094
1217
  classifyAllShell,
1095
1218
  enabledToolNames,
1096
1219
  disabledTools,
@@ -1278,6 +1401,138 @@ export async function runAgentHeadless(options = {}, deps = {}) {
1278
1401
  };
1279
1402
  };
1280
1403
 
1404
+ // --goal-condition: session-level completion-condition engine (P1). After each
1405
+ // outer turn an independent checker judges a completion CONDITION; if unmet and
1406
+ // budget remains, a follow-up user turn re-drives the agent — until met
1407
+ // (goal_completed) or a budget is exhausted (goal_exhausted). OPT-IN: when
1408
+ // unset, `goalEngine` is null and the outer loop runs exactly once, so one-shot
1409
+ // `cc agent -p` behavior is byte-for-byte unchanged.
1410
+ let goalEngine = null;
1411
+ let _goalHelpers = null;
1412
+ let _goalTokensSeen = 0; // per-turn usage deltas (engine totals == run total)
1413
+ let _goalCostSeen = 0;
1414
+ // Model-judged conditions reuse the run's own model as an independent
1415
+ // evaluator (mirrors --goal-assess). Overridable via deps for tests.
1416
+ const _defaultGoalJudge = async (cond, transcript) => {
1417
+ const { chatWithTools } = await import("./agent-core.js");
1418
+ const { firstBalancedJson } = await import("../lib/json-schema-output.js");
1419
+ const judgePrompt =
1420
+ `You are judging whether a coding session met a completion condition.\n` +
1421
+ `Condition: ${cond.text || cond.source}\n\n` +
1422
+ `Latest assistant output:\n${String(transcript.finalText || "").slice(0, 2000)}\n\n` +
1423
+ `Reply with STRICT JSON only: {"met": true|false, "reason": "<short>"}.`;
1424
+ const r = await chatWithTools([{ role: "user", content: judgePrompt }], {
1425
+ model,
1426
+ provider,
1427
+ baseUrl,
1428
+ apiKey,
1429
+ enabledToolNames: [],
1430
+ });
1431
+ const text = r?.message?.content || "";
1432
+ let met = false;
1433
+ let reason = "model judge returned no verdict";
1434
+ const block = firstBalancedJson(text, "{");
1435
+ if (block) {
1436
+ try {
1437
+ const parsed = JSON.parse(block);
1438
+ met = parsed.met === true;
1439
+ if (typeof parsed.reason === "string" && parsed.reason.trim())
1440
+ reason = parsed.reason.trim();
1441
+ else reason = met ? "condition met" : "condition not met";
1442
+ } catch {
1443
+ /* tolerant: a non-JSON verdict stays unmet */
1444
+ }
1445
+ }
1446
+ return {
1447
+ met,
1448
+ reason,
1449
+ evidence: { kind: "model", raw: text.slice(0, 200) },
1450
+ };
1451
+ };
1452
+ // Persist the engine snapshot as a hash-chained `goal_snapshot` session event
1453
+ // (best-effort, only when persisting). Re-read on --resume so an unfinished
1454
+ // goal continues across processes with its outerTurns/tokens/cost/startedAtMs
1455
+ // intact. rebuildMessages ignores this event type, so it never pollutes model
1456
+ // context. Defined here so the outer loop can call it after each evaluate().
1457
+ const persistGoalSnapshot = () => {
1458
+ if (!persist || !goalEngine) return;
1459
+ try {
1460
+ store.appendEvent(sessionId, "goal_snapshot", goalEngine.snapshot());
1461
+ } catch {
1462
+ // best-effort — never fail the run over persistence
1463
+ }
1464
+ };
1465
+ if (options.goalCondition) {
1466
+ try {
1467
+ const eng = await import("../lib/goal-condition-engine.js");
1468
+ const goalNow = () => (deps.now ? deps.now() : Date.now());
1469
+ // Cross-process resume: if the resumed session persisted an UNFINISHED
1470
+ // goal, continue it (fromSnapshot keeps accumulated progress + startedAtMs)
1471
+ // rather than starting fresh. A finished (done) snapshot is ignored — the
1472
+ // prior goal already concluded, so --goal-condition begins a new cycle. On
1473
+ // restore the persisted condition/budget win over freshly-passed flags, so
1474
+ // a resume faithfully continues the same goal.
1475
+ let restoredSnap = null;
1476
+ if (resumeId) {
1477
+ try {
1478
+ const events = store.readEvents(resumeId) || [];
1479
+ for (let i = events.length - 1; i >= 0; i--) {
1480
+ const ev = events[i];
1481
+ if (ev && ev.type === "goal_snapshot") {
1482
+ const snap = ev.data;
1483
+ if (snap && snap.state && snap.state.done !== true)
1484
+ restoredSnap = snap;
1485
+ break; // only the latest goal_snapshot matters
1486
+ }
1487
+ }
1488
+ } catch {
1489
+ restoredSnap = null; // unreadable transcript → start fresh
1490
+ }
1491
+ }
1492
+ if (restoredSnap) {
1493
+ goalEngine = eng.GoalConditionEngine.fromSnapshot(restoredSnap, {
1494
+ now: goalNow,
1495
+ });
1496
+ } else {
1497
+ goalEngine = new eng.GoalConditionEngine({
1498
+ condition: options.goalCondition,
1499
+ budget: {
1500
+ maxOuterTurns: options.maxOuterTurns,
1501
+ maxTokens: options.goalMaxTokens,
1502
+ maxCostUsd: options.goalMaxCostUsd,
1503
+ maxTimeMs: options.goalMaxTimeMs,
1504
+ },
1505
+ now: goalNow,
1506
+ });
1507
+ }
1508
+ _goalHelpers = {
1509
+ isDeterministicCondition: eng.isDeterministicCondition,
1510
+ runDeterministicCheck: eng.runDeterministicCheck,
1511
+ GOAL_DECISION: eng.GOAL_DECISION,
1512
+ };
1513
+ const started = goalEngine.start();
1514
+ emitStream({
1515
+ type: started.type,
1516
+ condition: started.condition,
1517
+ budget: started.budget,
1518
+ resumed: Boolean(restoredSnap),
1519
+ });
1520
+ persistGoalSnapshot(); // checkpoint the starting/restored state
1521
+ if (isText)
1522
+ writeErr(
1523
+ ` ◎ goal-condition ${restoredSnap ? "resumed" : "active"}: ` +
1524
+ `${goalEngine.condition.source} ` +
1525
+ `(outer turn ${goalEngine.state.outerTurns + 1}/` +
1526
+ `${goalEngine.budget.maxOuterTurns})\n`,
1527
+ );
1528
+ } catch (err) {
1529
+ // A bad spec should have been rejected at the command layer; fail-open so
1530
+ // a malformed condition never aborts an otherwise-valid run.
1531
+ goalEngine = null;
1532
+ if (isText) writeErr(` goal-condition ignored: ${err.message}\n`);
1533
+ }
1534
+ }
1535
+
1281
1536
  // A Ctrl-C (SIGINT) / SIGTERM terminates Node WITHOUT unwinding the `finally`
1282
1537
  // below, so its background-task reaper is bypassed — a backgrounded run_shell
1283
1538
  // task (e.g. a dev server) this run spawned would be orphaned. Install a
@@ -1322,10 +1577,50 @@ export async function runAgentHeadless(options = {}, deps = {}) {
1322
1577
  args: event.args,
1323
1578
  });
1324
1579
  toolCalls.push({ tool: event.tool, args: event.args });
1580
+ // P0-2: record an irreversible effect as STARTED (persisted before
1581
+ // it settles) so a crash before the matching tool-result leaves a
1582
+ // reconcilable "in flight" marker instead of a silent replay.
1583
+ currentSideEffectOpId = null;
1584
+ if (persist) {
1585
+ const se = classifyToolSideEffect(event.tool, event.args);
1586
+ if (se) {
1587
+ const opId = `${sideEffectRunNonce}:${sideEffectSeq++}`;
1588
+ currentSideEffectOpId = opId;
1589
+ sideEffectLedger
1590
+ .prepare(opId, {
1591
+ kind: se.kind,
1592
+ key: se.key,
1593
+ // Content-addressed key: a resumed replay of the SAME effect
1594
+ // derives the SAME key, so an external provider can de-dupe
1595
+ // and countDuplicateCommittedEffects can measure `0` repeats.
1596
+ meta: {
1597
+ tool: event.tool,
1598
+ idempotencyKey: operationIdempotencyKey({
1599
+ tool: event.tool,
1600
+ args: event.args,
1601
+ }),
1602
+ },
1603
+ })
1604
+ .start(opId);
1605
+ persistSideEffectLedger();
1606
+ }
1607
+ }
1325
1608
  break;
1326
1609
  }
1327
1610
  case "tool-result": {
1328
1611
  const err = event.error || event.result?.error || null;
1612
+ // P0-2: settle the in-flight side-effect (commit on success, fail on
1613
+ // a clean error) and persist the updated ledger snapshot.
1614
+ if (persist && currentSideEffectOpId) {
1615
+ if (err)
1616
+ sideEffectLedger.fail(
1617
+ currentSideEffectOpId,
1618
+ String(err).slice(0, 200),
1619
+ );
1620
+ else sideEffectLedger.commit(currentSideEffectOpId);
1621
+ persistSideEffectLedger();
1622
+ currentSideEffectOpId = null;
1623
+ }
1329
1624
  if (isText && err) writeErr(` Error: ${err}\n`);
1330
1625
  emitStream({
1331
1626
  type: "tool_result",
@@ -1492,6 +1787,103 @@ export async function runAgentHeadless(options = {}, deps = {}) {
1492
1787
  );
1493
1788
  }
1494
1789
  }
1790
+ // ── goal-condition re-drive (opt-in via --goal-condition) ─────────────
1791
+ // After the turn settles (and any auto-rewake fix), evaluate the session
1792
+ // completion condition. Unmet + budget remaining → append a follow-up user
1793
+ // turn and re-drive; met → goal_completed; budget spent → goal_exhausted.
1794
+ // Cost / max-turns exhaustion of the INNER loop stops here regardless.
1795
+ if (
1796
+ goalEngine &&
1797
+ _goalHelpers &&
1798
+ !goalEngine.done &&
1799
+ !stopForCost &&
1800
+ endReason !== "cost-budget-exhausted" &&
1801
+ endReason !== "max_turns"
1802
+ ) {
1803
+ // Feed this turn's usage DELTA so the engine's totals equal the run's
1804
+ // cumulative usage (recordTurnUsage accumulates).
1805
+ const curTokens =
1806
+ usage.input_tokens +
1807
+ usage.output_tokens +
1808
+ usage.cache_read_input_tokens +
1809
+ usage.cache_creation_input_tokens;
1810
+ const curCost = costBudget ? Number(costBudget.spentUsd) || 0 : 0;
1811
+ goalEngine.recordTurnUsage({
1812
+ tokens: curTokens - _goalTokensSeen,
1813
+ costUsd: curCost - _goalCostSeen,
1814
+ });
1815
+ _goalTokensSeen = curTokens;
1816
+ _goalCostSeen = curCost;
1817
+
1818
+ const cond = goalEngine.condition;
1819
+ let evaluation;
1820
+ try {
1821
+ if (_goalHelpers.isDeterministicCondition(cond)) {
1822
+ const gc = deps.goalCheck || {};
1823
+ const spawnSync =
1824
+ gc.spawnSync || (await import("node:child_process")).spawnSync;
1825
+ const existsSync =
1826
+ gc.existsSync || (await import("node:fs")).existsSync;
1827
+ evaluation = _goalHelpers.runDeterministicCheck(cond, {
1828
+ spawnSync,
1829
+ existsSync,
1830
+ cwd,
1831
+ lastOutput: finalText,
1832
+ });
1833
+ } else {
1834
+ const judge = deps.goalConditionJudge || _defaultGoalJudge;
1835
+ evaluation = await judge(cond, {
1836
+ prompt: options.prompt,
1837
+ finalText,
1838
+ toolCalls,
1839
+ });
1840
+ }
1841
+ } catch (err) {
1842
+ evaluation = {
1843
+ met: false,
1844
+ reason: `goal check failed: ${err.message}`,
1845
+ evidence: { error: true },
1846
+ };
1847
+ }
1848
+
1849
+ const { decision, events } = goalEngine.evaluate(evaluation);
1850
+ for (const ev of events) {
1851
+ emitStream(ev);
1852
+ if (isText) {
1853
+ if (ev.type === "goal_completed")
1854
+ writeErr(` ✔ goal-condition met: ${ev.reason}\n`);
1855
+ else if (ev.type === "goal_exhausted")
1856
+ writeErr(
1857
+ ` ⛔ goal-condition unmet (${ev.limit}): ${ev.reason}\n`,
1858
+ );
1859
+ else if (ev.type === "goal_evaluated")
1860
+ writeErr(
1861
+ ` ◎ goal-condition ${ev.met ? "met" : "not met"}: ${ev.reason}\n`,
1862
+ );
1863
+ }
1864
+ }
1865
+ // Checkpoint the post-evaluate state (outerTurns incremented, and
1866
+ // done/outcome on a terminal decision) so a crash or Ctrl-C between here
1867
+ // and the next turn still resumes at the right point.
1868
+ persistGoalSnapshot();
1869
+ if (decision === _goalHelpers.GOAL_DECISION.CONTINUE) {
1870
+ messages.push({
1871
+ role: "user",
1872
+ content:
1873
+ `The completion condition is not yet met: ${evaluation.reason}.\n` +
1874
+ `Keep working toward: "${cond.source}". When you believe it is ` +
1875
+ `satisfied, make sure it actually passes.`,
1876
+ });
1877
+ finalText = "";
1878
+ endReason = "complete";
1879
+ if (isText)
1880
+ writeErr(
1881
+ ` ↻ goal-condition re-drive — outer turn ${goalEngine.state.outerTurns + 1}\n`,
1882
+ );
1883
+ continue; // re-drive: run another outer turn
1884
+ }
1885
+ // complete | exhausted → fall through to the break below.
1886
+ }
1495
1887
  break;
1496
1888
  }
1497
1889
  } catch (err) {