chainlesschain 0.162.162 → 0.162.163

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 (192) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-ClI2ALgv.js → AIOps-BQ0kgfA1.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-BDjRcJaK.js → ActionButton-Dr1MDANe.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DikSRsaT.js → Analytics-CZ5vsaXW.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-9mLhMFmz.js → AppLayout-BIBga881.js} +3 -3
  6. package/src/assets/web-panel/assets/{Artifacts-BSr88BrH.js → Artifacts-vpXHRU3u.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-CdvyimOF.js → Audit-B8exuoub.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-D3_hCedh.js → BackgroundAgents-923evRRN.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-Cyvv9RHf.js → Backup-sr_sswlQ.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-BZM14kt8.js → BaseInput-CnW-4XKT.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-DrQSokVz.js → Chat-FZCsTZIL.js} +6 -6
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-DDPd9qT0.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-bFLYeWMV.js → Checkbox-DFrxB59H.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-7mN7xJXT.js → Codegen-_b6WEel8.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-DAB5ZWm7.js → Col-CkbnnZ-K.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-B_1TymaK.js → Community-BxM_82zW.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-Bq1uSWLJ.js → Compact-DYinP45R.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-CyiE1jEe.js → Compliance-DyXp3sSP.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-Cpk0D7vm.js → Cowork-BEr2DhW7.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-d_Lv3rdB.js → Cron-B-Vwo1hN.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-DMqhHjMR.js → Crosschain-DbLxKddd.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-Br-oPuBw.js → DID-BsQ_LFw9.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-CfLW_RaP.js → Dashboard-lVlXQHu5.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-DZnTOExV.js → Dropdown-Beo_foO7.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-DHIUHDv1.js → EmailListRenderer-DszHIXED.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-Bw3t3myu.js → FamilyGuardDashboard-DcAwoh81.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-D9cpQPAY.js → Federation-CuJLa2eb.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-Jdczr9Mr.js → FormItemContext-Cxd1lxhc.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-BT8ZMpC5.js → GenericCardRenderer-BBvq5Jsw.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-DjARip2b.js → Git-WqPKetx6.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-BgWbSRcj.js → Governance-CWYtY3ke.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-CfouJciM.js → Inference-BnX1iiNV.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-50l7mrAr.js → KnowledgeGraph-BJuM8GN5.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-rq50H84B.js → Logs-DqLMNntm.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-DUPIEdQH.js → MarkdownRenderer-BqYwEYou.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-BYgPDmHU.js → Marketplace-_vGfmhNl.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-5DVgcFdx.js → McpTools-BeJDOkDj.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-B70LPk7f.js → Memory-Da0EEZDO.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-De8ssA5Y.js → MobileBridge-DSuzcCCy.js} +1 -1
  40. package/src/assets/web-panel/assets/MobileProjects-D0RAk4e8.js +1 -0
  41. package/src/assets/web-panel/assets/{Mtc-RJsOTsIA.js → Mtc-BE9YgdA0.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-Crh-6BEK.js → MtcAudit-fC2BpO5S.js} +5 -5
  43. package/src/assets/web-panel/assets/{Multisig-wuoR5l3x.js → Multisig-D24Xxqw3.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-U2Mji4fZ.js → NLProgramming-Bs3oGvS8.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-CdTZAyDH.js → Notes-BFjmAA-3.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-C-FucWE7.js → NotificationSettings-BX_9Bhv0.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-DCYFTeaI.js → OrderTableRenderer-wjkZwJXI.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-an3OF85L.js → Organization-DBcdsVEP.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-C-1vmYFe.js → Overflow-DDyaF9vC.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-Cwh47xKg.js → P2P-BB78L4he.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-Dm38GT0k.js → PdhVaultBrowser-DqTiZFum.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-DS2Y8awD.js → Permissions-INbnWv4j.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-BQNziA0I.js → PersonalDataHub-BST-VmTq.js} +3 -3
  54. package/src/assets/web-panel/assets/{Pipeline-DF85L1ue.js → Pipeline-CE7_tS2k.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-BuLornF-.js → Privacy-hK5L8dOB.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-C3Y31IwX.js → ProjectInit-BvkkUoDn.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-BCPiI8aD.js → ProjectSettings-u7_EUtkm.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-BZ6s4k6g.js → Projects-5Fmt_8Nw.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-D3Wp022X.js → Providers-B9eanarR.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-CUJfQkH4.js → QrScannerModal-C0dX5N2f.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-UAc0EO8l.js → QuickAsk-CKTpG-PZ.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-BaxKupcs.js → Recommend-Px_w_XQM.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-C75NNnXZ.js → RemoteSession-Di0fnYkM.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-BVXynZAk.js → Reputation-vesnWjXt.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-C5MRZ0bi.js → Row-exbY6LkT.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-DAKkEYka.js → RssFeed-B3O3x70t.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-DvWv9YOA.js → Search-uRA0DhNz.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-B5peafW1.js → Security-CViqdH6K.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-UQmvPEaG.js → Services-CkXS97WN.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-C3bhWpuF.js → Skeleton-DxW5WYnE.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-DfkKf9_H.js → Skills-CLxmGFse.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-DqMSjlBM.js → Sla-Cr5fziXq.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-C-WbxWUX.js → SpeechSettings-H0JSb3cp.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-CYuHZ4N2.js → SyncSettings-DMYWMae7.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-CyeAyB9d.js → Tasks-CbYyDdF4.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-BTCeQnpq.js → Templates-Cpc8JjiK.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-BifQuA9Y.js → Tenant-CRh77Jda.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-Bx_9Bocf.js → Terminal-4D8tNBO5.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-BX0RFCOm.js → TimelineRenderer-D41jzzbG.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-CGHrCmUS.js → Tokens-C_jWEy6b.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-DlqaY4SO.js → Trigger-Dk1ZUhsy.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-DfOmj-ay.js → Trust-CLVy4l7m.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-B72jH5fE.js → UkeySign-C-FeqFDj.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-BT-63fqv.js → VideoEditing-CqVZzyMm.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-Dsul3sHr.js → Wallet-DX9BW9pu.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-Izkwwdgy.js → WebAuthn-7hQDpWw_.js} +2 -2
  87. package/src/assets/web-panel/assets/{WorkflowEditor-D3FHSiH1.js → WorkflowEditor-6XqUAO0j.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-CmVz5z3U.js → chat-5R_vkaJG.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-CuLgyJBp.js → colors-huUPZdC-.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-Ml1YkXpw.js → compact-item-CfJlgisE.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-BvjJ48bs.js → createContext-jO8heofr.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-DeIrCfH-.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-DVC0157C.js → hasIn-DY8Vxqx7.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DJyN28Et.js → index-A48PtXw9.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CudmwNBV.js → index-B300RPnT.js} +3 -3
  96. package/src/assets/web-panel/assets/{index-BbepULSc.js → index-B74OXFxC.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DYXHFr4r.js → index-BAON7TzU.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BzG-x9p4.js → index-BCqxyvqp.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-wvb_FtaV.js → index-BHE8OIls.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-QXzlT9KD.js → index-BQS-uPOK.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DukDVXCv.js → index-BRXOP09X.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CFYVQMvu.js → index-BYLBPpt8.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BydjdZnl.js → index-BfCx4pFT.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CmFNvNLZ.js → index-BliaT02i.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-KMqhcF7l.js → index-BmqGerpC.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Ck_52dz_.js → index-BsBtRWbA.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-C229ABfy.js → index-BtEXSoPL.js} +1 -1
  108. package/src/assets/web-panel/assets/index-BxUKMRpP.js +1 -0
  109. package/src/assets/web-panel/assets/{index-C_H_S-DY.js → index-Bxax9vQ6.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-B0S8NzO6.js → index-C2iVSFpy.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BjHBpRYZ.js → index-CA6sO5VD.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Bzr0rB5T.js → index-CA90TZxG.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DDGaOWBV.js → index-CLhBh-qE.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Dt5KMjwJ.js → index-CRElRhyS.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BbPtzB8M.js → index-Cbe9G0V4.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-C_vRgTl9.js → index-ChvHubX3.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BpFJHiTv.js → index-Cis_Ip7H.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-KjgKdZe-.js → index-Cjxbu68P.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-vG4XSJ1d.js → index-CzKmteNS.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BUt8kTIB.js → index-D0pet5Uy.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BfuKP4qe.js → index-D1VLuww0.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-4SBta3d7.js → index-DXBcvUnf.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DC_OI9H2.js → index-Db1cLWir.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DuGszaWY.js → index-DoI2sZm7.js} +1 -1
  125. package/src/assets/web-panel/assets/index-DscU9iPI.js +1 -0
  126. package/src/assets/web-panel/assets/{index-4wcgvPoF.js → index-KRHroryI.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-D3lD5qap.js → index-d1xkbtNz.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-B_EvG_GW.js → index-dKx57io7.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-CXkJH-lK.js → index-j3O2EEJe.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BiC6dQZG.js → index-p85YaaKw.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-Cw0Rjy87.js → index-v3pTaEQm.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-C_XuRgGn.js → index-vXs3B8GJ.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-BDgd2YZ8.js → initDefaultProps-MCB7-raZ.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-ErtrXdYe.js → motion-UWDF9swV.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-_GPPNCgs.js → move-CsTmgXZU.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-DRLL-8V2.js → mtc-parser-fQy6KGLg.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-p86sl023.js → omit-woPnEjd1.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-BTu7cMLH.js → pickAttrs-Bs2dZ1gf.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-BEKz6Isr.js → placementArrow-C7EVgPkg.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-Ck42c9MP.js → responsiveObserve-BH2kYCTN.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-1CEvuPsB.js → slide-BFBP2V_e.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-BDfdx1GJ.js → statusUtils-xdDxDYse.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-B4K46m5w.js → styleChecker-aAWgPPQf.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-DoIVpJNq.js → useFlexGapSupport-DkGSAWoO.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DdAWi-ln.js → useFs-BrOih40N.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-iUu6k8dx.js → usePersonalDataHub-eIYrgMqO.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-DZz4R6Tw.js → vnode-B8lutNSE.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-DXlf9sA3.js → zoom-CffNre7V.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/agenda.js +254 -23
  151. package/src/commands/agent.js +105 -11
  152. package/src/commands/context.js +125 -14
  153. package/src/commands/hook.js +181 -7
  154. package/src/commands/plugin.js +239 -1
  155. package/src/commands/review.js +347 -2
  156. package/src/data/changelog.json +9 -4
  157. package/src/gateways/ws/remote-session-protocol.js +39 -0
  158. package/src/gateways/ws/session-protocol.js +5 -2
  159. package/src/lib/agent-schedule-store.js +289 -10
  160. package/src/lib/channels/channel-manager.js +54 -2
  161. package/src/lib/cli-context-engineering.js +9 -2
  162. package/src/lib/context-breakdown.js +116 -0
  163. package/src/lib/doctor-checkup.js +52 -0
  164. package/src/lib/hook-event-log.cjs +193 -0
  165. package/src/lib/interaction-adapter.js +34 -4
  166. package/src/lib/json-schema-output.js +46 -12
  167. package/src/lib/json-schema-validate.js +143 -3
  168. package/src/lib/plugin-runtime/capability-consent.js +175 -0
  169. package/src/lib/plugin-runtime/policy.js +62 -0
  170. package/src/lib/plugin-runtime/scopes.js +20 -4
  171. package/src/lib/project-instructions.js +128 -0
  172. package/src/lib/repl-goal.js +132 -0
  173. package/src/lib/repl-rewind.js +52 -0
  174. package/src/lib/schedule-planner.js +17 -1
  175. package/src/lib/settings-hook-events.cjs +13 -0
  176. package/src/lib/settings-loader.cjs +42 -0
  177. package/src/lib/side-effect-ledger-store.js +88 -0
  178. package/src/lib/side-effect-ledger.js +296 -0
  179. package/src/lib/sub-agent-context.js +74 -5
  180. package/src/lib/subagent-inheritance.js +112 -0
  181. package/src/lib/turn-binding-store.js +119 -0
  182. package/src/repl/agent-repl.js +136 -0
  183. package/src/repl/permission-tier.js +28 -0
  184. package/src/runtime/agent-core.js +376 -10
  185. package/src/runtime/coding-agent-contract-shared.cjs +60 -1
  186. package/src/runtime/headless-runner.js +267 -0
  187. package/src/runtime/headless-stream.js +60 -1
  188. package/src/assets/web-panel/assets/ChatBubbleRenderer-HwJR-C6B.js +0 -1
  189. package/src/assets/web-panel/assets/MobileProjects-D10VISps.js +0 -1
  190. package/src/assets/web-panel/assets/devWarning-DhiGrbsh.js +0 -1
  191. package/src/assets/web-panel/assets/index-B7a1TrU6.js +0 -1
  192. package/src/assets/web-panel/assets/index-C2B1KRja.js +0 -1
@@ -7,20 +7,24 @@
7
7
  * cc agenda list [--json] show all scheduled entries
8
8
  * cc agenda run [--json] [--dry-run] fire everything due right now
9
9
  * cc agenda cancel <id> remove one entry
10
+ * cc agenda prune [--older-than N] drop finished entries
10
11
  *
11
12
  * `run` for each due entry:
12
13
  * - wakeup → spawn `cc agent -p <prompt>`, mark fired
13
14
  * - cron → spawn `cc agent -p <prompt>`, advance to next fire time
14
- * - monitor → run <command>; if output matches stop_when, send the
15
- * notification and stop; else re-arm for the next interval
15
+ * - monitor → check its source (a shell <command>'s output, a <watchFile>'s
16
+ * content / appearance / modification, or a <watchUrl>'s response
17
+ * body / 2xx); if it matches stop_when, send the notification and
18
+ * stop; else re-arm for the next interval
16
19
  */
17
20
 
18
21
  import { spawn, execSync } from "node:child_process";
22
+ import { readFileSync, statSync } from "node:fs";
19
23
  import { fileURLToPath } from "node:url";
20
24
  import chalk from "chalk";
21
25
  import { AgentScheduleStore } from "../lib/agent-schedule-store.js";
22
26
  import { sendAgentNotification } from "../lib/agent-notify.js";
23
- import { nextWakeupAt } from "../lib/schedule-planner.js";
27
+ import { nextWakeupAt, partitionSchedule } from "../lib/schedule-planner.js";
24
28
 
25
29
  const BIN_PATH = fileURLToPath(
26
30
  new URL("../../bin/chainlesschain.js", import.meta.url),
@@ -58,6 +62,20 @@ export function registerAgendaCommand(program) {
58
62
  .action((id, options) => {
59
63
  process.exitCode = runAgendaCancel(id, options);
60
64
  });
65
+
66
+ cmd
67
+ .command("prune")
68
+ .description(
69
+ "Remove finished entries (fired / matched / exhausted / expired)",
70
+ )
71
+ .option(
72
+ "--older-than <seconds>",
73
+ "Only prune entries that finished more than N seconds ago",
74
+ )
75
+ .option("--json", "Machine-readable JSON output")
76
+ .action((options) => {
77
+ process.exitCode = runAgendaPrune(options);
78
+ });
61
79
  }
62
80
 
63
81
  export function runAgendaList(options = {}, _deps = {}) {
@@ -80,17 +98,30 @@ export function runAgendaList(options = {}, _deps = {}) {
80
98
  log("");
81
99
  log(chalk.bold(" Agenda:\n"));
82
100
  for (const e of entries) {
101
+ const monitorWhat =
102
+ e.source === "file"
103
+ ? `file ${e.watchFile}`
104
+ : e.source === "http"
105
+ ? `url ${e.watchUrl}`
106
+ : `every ${Math.round(e.intervalMs / 1000)}s`;
83
107
  const when =
84
108
  e.kind === "wakeup"
85
109
  ? new Date(e.dueAt).toISOString()
86
110
  : e.kind === "cron"
87
111
  ? `${e.cron} (next ${new Date(e.nextAt).toISOString()})`
88
- : `every ${Math.round(e.intervalMs / 1000)}s (next ${new Date(e.nextAt).toISOString()})`;
112
+ : `${monitorWhat} (next ${new Date(e.nextAt).toISOString()})`;
113
+ const expiry =
114
+ e.expiresAt != null
115
+ ? chalk.dim(` · expires ${new Date(e.expiresAt).toISOString()}`)
116
+ : "";
117
+ const policy = e.runPolicy
118
+ ? chalk.dim(` · ${describePolicy(e.runPolicy)}`)
119
+ : "";
89
120
  log(
90
121
  ` ${chalk.cyan(e.kind.padEnd(8))} ${chalk.dim(e.id.slice(0, 8))} ` +
91
- `${statusBadge(e.status)} ${e.label || truncate(e.prompt || e.command, 40)}`,
122
+ `${statusBadge(e.status)} ${e.label || truncate(e.prompt || e.command || e.watchFile || e.watchUrl, 40)}`,
92
123
  );
93
- log(` ${chalk.dim(when)}`);
124
+ log(` ${chalk.dim(when)}${expiry}${policy}`);
94
125
  }
95
126
  if (nextAt != null) {
96
127
  const rel = Math.max(0, Math.round((nextAt - now) / 1000));
@@ -120,6 +151,54 @@ export function runAgendaCancel(id, options = {}, _deps = {}) {
120
151
  return 2;
121
152
  }
122
153
 
154
+ /**
155
+ * Remove finished (terminal) entries so the store does not grow without bound.
156
+ * `--older-than <seconds>` keeps entries that finished more recently.
157
+ */
158
+ export function runAgendaPrune(options = {}, _deps = {}) {
159
+ const log = _deps.log || ((m) => console.log(m));
160
+ const store = _deps.store || new AgentScheduleStore();
161
+ const now = _deps.now ? _deps.now() : Date.now();
162
+
163
+ let before = Infinity;
164
+ if (options.olderThan != null) {
165
+ const secs = Number(options.olderThan);
166
+ if (!Number.isFinite(secs) || secs < 0) {
167
+ log(chalk.red(`\n ✖ invalid --older-than: ${options.olderThan}\n`));
168
+ return 2;
169
+ }
170
+ before = now - secs * 1000;
171
+ }
172
+
173
+ const removed = store.pruneTerminal({ before });
174
+ if (options.json) {
175
+ log(
176
+ JSON.stringify(
177
+ {
178
+ pruned: removed.map((e) => ({
179
+ id: e.id,
180
+ kind: e.kind,
181
+ status: e.status,
182
+ })),
183
+ },
184
+ null,
185
+ 2,
186
+ ),
187
+ );
188
+ return 0;
189
+ }
190
+ if (removed.length === 0) {
191
+ log(chalk.gray("\n No finished entries to prune.\n"));
192
+ return 0;
193
+ }
194
+ log(
195
+ chalk.green(
196
+ `\n ✓ Pruned ${removed.length} finished entr${removed.length === 1 ? "y" : "ies"}\n`,
197
+ ),
198
+ );
199
+ return 0;
200
+ }
201
+
123
202
  /**
124
203
  * Fire due entries. Returns 0 unless a monitor command / agent spawn failed in
125
204
  * a way worth signalling; JSON summary lists every action taken.
@@ -129,8 +208,16 @@ export async function runAgendaRun(options = {}, _deps = {}) {
129
208
  const store = _deps.store || new AgentScheduleStore();
130
209
  const spawnAgent = _deps.spawnAgent || defaultSpawnAgent;
131
210
  const runCommand = _deps.runCommand || defaultRunCommand;
211
+ const readWatchedFile = _deps.readWatchedFile || defaultReadWatchedFile;
212
+ const fetchUrl = _deps.fetchUrl || defaultFetchUrl;
132
213
  const notify = _deps.notify || sendAgentNotification;
133
- const due = store.due(null, _deps.now ? _deps.now() : null);
214
+ const now = _deps.now ? _deps.now() : Date.now();
215
+ // Retire expired entries BEFORE firing due ones — an expired task never gets
216
+ // a final fire (schedule-planner semantics). Dry-run inspects without mutating.
217
+ const retired = options.dryRun
218
+ ? partitionSchedule(store.list(), { now }).expired
219
+ : store.retireExpired(now);
220
+ const due = store.due(null, now);
134
221
  const actions = [];
135
222
 
136
223
  for (const entry of due) {
@@ -140,26 +227,65 @@ export async function runAgendaRun(options = {}, _deps = {}) {
140
227
  }
141
228
  try {
142
229
  if (entry.kind === "wakeup") {
143
- await spawnAgent(entry.prompt);
230
+ await spawnAgent(entry.prompt, entry.runPolicy);
144
231
  store.markWakeupFired(entry.id);
145
232
  actions.push({ id: entry.id, kind: "wakeup", action: "fired" });
146
233
  } else if (entry.kind === "cron") {
147
- await spawnAgent(entry.prompt);
234
+ await spawnAgent(entry.prompt, entry.runPolicy);
148
235
  store.advanceCron(entry.id);
149
236
  actions.push({ id: entry.id, kind: "cron", action: "fired" });
150
237
  } else if (entry.kind === "monitor") {
151
- const output = await runCommand(entry.command);
152
- const matched = entry.stopWhen
153
- ? new RegExp(entry.stopWhen).test(output)
154
- : false;
238
+ // Three sources: a shell command (match its output), a watched file
239
+ // (match its content, or — with no stopWhen — fire when it appears),
240
+ // or an HTTP endpoint (match its response body, or fire on 2xx).
241
+ let output = "";
242
+ let matched = false;
243
+ let mtimeMs = null;
244
+ if (entry.source === "file") {
245
+ const file = await readWatchedFile(entry.watchFile);
246
+ output = file.content;
247
+ mtimeMs = file.mtimeMs ?? null;
248
+ if (entry.watchChange) {
249
+ // Fire once the file's mtime advances past the baseline recorded on
250
+ // the first check. The first check only establishes the baseline.
251
+ matched =
252
+ entry.lastMtimeMs != null &&
253
+ mtimeMs != null &&
254
+ mtimeMs > entry.lastMtimeMs;
255
+ } else {
256
+ matched = entry.stopWhen
257
+ ? new RegExp(entry.stopWhen).test(file.content)
258
+ : file.exists; // no pattern → the file appearing is the signal
259
+ }
260
+ } else if (entry.source === "http") {
261
+ const res = await fetchUrl(entry.watchUrl);
262
+ output = res.body;
263
+ matched = entry.stopWhen
264
+ ? new RegExp(entry.stopWhen).test(res.body)
265
+ : res.ok; // no pattern → a 2xx response is the signal
266
+ } else {
267
+ output = await runCommand(entry.command);
268
+ matched = entry.stopWhen
269
+ ? new RegExp(entry.stopWhen).test(output)
270
+ : false;
271
+ }
155
272
  if (matched) {
273
+ const what =
274
+ entry.source === "file"
275
+ ? entry.watchFile
276
+ : entry.source === "http"
277
+ ? entry.watchUrl
278
+ : entry.command;
156
279
  await notify({
157
- title: entry.notify?.title || `Monitor matched: ${entry.command}`,
280
+ title: entry.notify?.title || `Monitor matched: ${what}`,
158
281
  body: truncate(output, 500),
159
282
  level: "success",
160
283
  });
161
284
  }
162
- const updated = store.recordMonitorCheck(entry.id, { matched });
285
+ const updated = store.recordMonitorCheck(entry.id, {
286
+ matched,
287
+ mtimeMs,
288
+ });
163
289
  actions.push({
164
290
  id: entry.id,
165
291
  kind: "monitor",
@@ -177,11 +303,25 @@ export async function runAgendaRun(options = {}, _deps = {}) {
177
303
  }
178
304
  }
179
305
 
306
+ const retiredSummary = retired.map((e) => ({ id: e.id, kind: e.kind }));
180
307
  if (options.json) {
181
- log(JSON.stringify({ due: due.length, actions }, null, 2));
182
- } else if (actions.length === 0) {
308
+ log(
309
+ JSON.stringify(
310
+ { due: due.length, retired: retiredSummary, actions },
311
+ null,
312
+ 2,
313
+ ),
314
+ );
315
+ } else if (actions.length === 0 && retired.length === 0) {
183
316
  log(chalk.gray(" Nothing due.\n"));
184
317
  } else {
318
+ for (const e of retired) {
319
+ log(
320
+ chalk.gray(
321
+ ` ${e.kind} ${e.id.slice(0, 8)}: ${options.dryRun ? "would-expire" : "expired"}`,
322
+ ),
323
+ );
324
+ }
185
325
  for (const a of actions) {
186
326
  const colour = a.action === "error" ? chalk.red : chalk.green;
187
327
  log(colour(` ${a.kind} ${a.id.slice(0, 8)}: ${a.action}`));
@@ -192,13 +332,58 @@ export async function runAgendaRun(options = {}, _deps = {}) {
192
332
 
193
333
  // ─── default effectful deps (overridable in tests) ─────────────────────────
194
334
 
195
- function defaultSpawnAgent(prompt) {
335
+ /**
336
+ * Build the `cc agent` argv for a scheduled prompt, appending the entry's
337
+ * per-task run policy as real `cc agent` flags. A missing / empty policy adds
338
+ * nothing, so an unpolicied task spawns exactly `cc agent -p <prompt>` as
339
+ * before (byte-identical). Exported for unit tests.
340
+ */
341
+ export function buildAgentArgs(prompt, policy = null) {
342
+ const args = ["agent", "-p", prompt];
343
+ if (policy && typeof policy === "object") {
344
+ if (
345
+ typeof policy.permissionMode === "string" &&
346
+ policy.permissionMode.length > 0
347
+ ) {
348
+ args.push("--permission-mode", policy.permissionMode);
349
+ }
350
+ if (policy.worktree === true) args.push("--worktree");
351
+ if (Number.isFinite(policy.maxTurns) && policy.maxTurns > 0) {
352
+ args.push("--max-turns", String(policy.maxTurns));
353
+ }
354
+ if (
355
+ typeof policy.goalCondition === "string" &&
356
+ policy.goalCondition.length > 0
357
+ ) {
358
+ args.push("--goal-condition", policy.goalCondition);
359
+ if (Number.isFinite(policy.maxOuterTurns) && policy.maxOuterTurns > 0) {
360
+ args.push("--max-outer-turns", String(policy.maxOuterTurns));
361
+ }
362
+ if (Number.isFinite(policy.goalMaxTokens) && policy.goalMaxTokens > 0) {
363
+ args.push("--goal-max-tokens", String(policy.goalMaxTokens));
364
+ }
365
+ if (Number.isFinite(policy.goalMaxCost) && policy.goalMaxCost > 0) {
366
+ args.push("--goal-max-cost", String(policy.goalMaxCost));
367
+ }
368
+ if (Number.isFinite(policy.goalMaxTime) && policy.goalMaxTime > 0) {
369
+ args.push("--goal-max-time", String(policy.goalMaxTime));
370
+ }
371
+ }
372
+ }
373
+ return args;
374
+ }
375
+
376
+ function defaultSpawnAgent(prompt, policy = null) {
196
377
  return new Promise((resolve, reject) => {
197
- const child = spawn(process.execPath, [BIN_PATH, "agent", "-p", prompt], {
198
- stdio: "ignore",
199
- detached: false,
200
- env: process.env,
201
- });
378
+ const child = spawn(
379
+ process.execPath,
380
+ [BIN_PATH, ...buildAgentArgs(prompt, policy)],
381
+ {
382
+ stdio: "ignore",
383
+ detached: false,
384
+ env: process.env,
385
+ },
386
+ );
202
387
  child.on("error", reject);
203
388
  child.on("close", (code) =>
204
389
  code === 0 || code == null
@@ -221,6 +406,40 @@ function defaultRunCommand(command) {
221
406
  }
222
407
  }
223
408
 
409
+ /**
410
+ * Read a watched file → { exists, content, mtimeMs }. A missing file is not an
411
+ * error. mtimeMs feeds watchChange monitors (fire when the file is modified).
412
+ */
413
+ function defaultReadWatchedFile(filePath) {
414
+ try {
415
+ const content = readFileSync(filePath, "utf-8");
416
+ let mtimeMs = null;
417
+ try {
418
+ mtimeMs = statSync(filePath).mtimeMs;
419
+ } catch {
420
+ /* content read but stat failed — treat mtime as unknown */
421
+ }
422
+ return { exists: true, content, mtimeMs };
423
+ } catch {
424
+ return { exists: false, content: "", mtimeMs: null };
425
+ }
426
+ }
427
+
428
+ /**
429
+ * GET a watched URL → { ok, status, body }. Any failure (network, timeout,
430
+ * non-2xx) is not an error here — it just yields ok:false so the monitor
431
+ * re-arms and tries again next interval.
432
+ */
433
+ async function defaultFetchUrl(url) {
434
+ try {
435
+ const res = await fetch(url, { signal: AbortSignal.timeout(30000) });
436
+ const body = await res.text();
437
+ return { ok: res.ok, status: res.status, body };
438
+ } catch {
439
+ return { ok: false, status: 0, body: "" };
440
+ }
441
+ }
442
+
224
443
  function statusBadge(status) {
225
444
  switch (status) {
226
445
  case "pending":
@@ -231,6 +450,8 @@ function statusBadge(status) {
231
450
  return chalk.cyan(status);
232
451
  case "exhausted":
233
452
  return chalk.yellow(status);
453
+ case "expired":
454
+ return chalk.gray(status);
234
455
  default:
235
456
  return status;
236
457
  }
@@ -240,3 +461,13 @@ function truncate(text, n) {
240
461
  const s = String(text || "");
241
462
  return s.length > n ? s.slice(0, n) + "…" : s;
242
463
  }
464
+
465
+ /** Compact human summary of a scheduled entry's per-task run policy. */
466
+ function describePolicy(policy) {
467
+ const parts = [];
468
+ if (policy.permissionMode) parts.push(policy.permissionMode);
469
+ if (policy.worktree === true) parts.push("worktree");
470
+ if (Number.isFinite(policy.maxTurns)) parts.push(`≤${policy.maxTurns} turns`);
471
+ if (policy.goalCondition) parts.push(`goal: ${policy.goalCondition}`);
472
+ return parts.join(", ");
473
+ }
@@ -204,6 +204,10 @@ export function registerAgentCommand(program) {
204
204
  "--otlp <file>",
205
205
  "Write the agent run's OpenTelemetry spans (model/tool/retry) as OTLP/JSON to <file>",
206
206
  )
207
+ .option(
208
+ "--otlp-content",
209
+ "Include prompt/response CONTENT in --otlp spans (off by default; content is redacted unless this is set)",
210
+ )
207
211
  .option(
208
212
  "--auto-rewake",
209
213
  "When an async `asyncRewake` hook (e.g. a background test) fails at turn end, re-engage the agent to fix it (bounded by --max-rewakes)",
@@ -311,6 +315,30 @@ export function registerAgentCommand(program) {
311
315
  "--goal-assess",
312
316
  "After the run, ask the model to assess goal progress and persist it (opt-in; costs one extra completion; requires --goal)",
313
317
  )
318
+ .option(
319
+ "--goal-condition <spec>",
320
+ "Session completion condition; re-drives outer turns until met. Prefix exit-zero:/file-exists:/contains:/regex: (deterministic) or model:<desc> (model-judged)",
321
+ )
322
+ .option(
323
+ "--max-outer-turns <n>",
324
+ "Max outer re-drive turns for --goal-condition (default 10, hard cap 100)",
325
+ (v) => parseInt(v, 10),
326
+ )
327
+ .option(
328
+ "--goal-max-tokens <n>",
329
+ "Token budget across --goal-condition outer turns",
330
+ (v) => parseInt(v, 10),
331
+ )
332
+ .option(
333
+ "--goal-max-cost <usd>",
334
+ "Cost budget (USD) across --goal-condition outer turns",
335
+ (v) => parseFloat(v),
336
+ )
337
+ .option(
338
+ "--goal-max-time <ms>",
339
+ "Wall-clock budget (ms) across --goal-condition outer turns",
340
+ (v) => parseInt(v, 10),
341
+ )
314
342
  .option(
315
343
  "--mcp-config <file>",
316
344
  "Load ad-hoc MCP servers from a JSON file for this run (headless); their tools become callable (mcp__<server>__<tool>)",
@@ -871,6 +899,11 @@ export function registerAgentCommand(program) {
871
899
  maxCostUsd,
872
900
  priceTable,
873
901
  chatFn: fallbackChatFn,
902
+ // --json-schema: structured output for stream-INPUT mode. The schema
903
+ // is resolved + meta-validated up front, its output contract injected
904
+ // into the system prompt, and a per-turn `structured_result` event is
905
+ // appended after each turn (parity with single-prompt --json-schema).
906
+ jsonSchema: options.jsonSchema || null,
874
907
  });
875
908
  } catch (err) {
876
909
  process.stderr.write(`Error: ${err.message}\n`);
@@ -1001,6 +1034,19 @@ export function registerAgentCommand(program) {
1001
1034
  }
1002
1035
  const { runAgentHeadless, parseToolList } =
1003
1036
  await import("../runtime/headless-runner.js");
1037
+ // --goal-condition: validate the spec up front so a bad prefix fails
1038
+ // fast (before any model call) with a clear message.
1039
+ if (options.goalCondition) {
1040
+ try {
1041
+ const { parseGoalCondition } =
1042
+ await import("../lib/goal-condition-engine.js");
1043
+ parseGoalCondition(options.goalCondition);
1044
+ } catch (e) {
1045
+ process.stderr.write(`--goal-condition: ${e.message}\n`);
1046
+ await _finishWorktree();
1047
+ process.exit(1);
1048
+ }
1049
+ }
1004
1050
  const maxTurns = options.maxTurns
1005
1051
  ? parseInt(options.maxTurns, 10)
1006
1052
  : undefined;
@@ -1027,6 +1073,9 @@ export function registerAgentCommand(program) {
1027
1073
  autoCheckpoint,
1028
1074
  // --otlp <file>: capture + export the run's OTel spans as OTLP/JSON.
1029
1075
  otlp: options.otlp || null,
1076
+ // --otlp-content: opt in to emitting prompt/response CONTENT in those
1077
+ // spans. Default false → content stays redacted (privacy by default).
1078
+ otlpContent: options.otlpContent === true,
1030
1079
  // --auto-rewake / --max-rewakes: bounded re-drive on async-hook failure.
1031
1080
  // --max-rewakes implies --auto-rewake (a positive budget turns it on).
1032
1081
  autoRewake:
@@ -1052,6 +1101,21 @@ export function registerAgentCommand(program) {
1052
1101
  goal: options.goal,
1053
1102
  // --goal-assess: run-end LLM progress assessment (Phase 2)
1054
1103
  goalAssess: options.goalAssess === true,
1104
+ // --goal-condition: session-level completion-condition engine — drives
1105
+ // outer turns until the condition is met or a budget is exhausted.
1106
+ goalCondition: options.goalCondition || null,
1107
+ maxOuterTurns: Number.isFinite(options.maxOuterTurns)
1108
+ ? options.maxOuterTurns
1109
+ : undefined,
1110
+ goalMaxTokens: Number.isFinite(options.goalMaxTokens)
1111
+ ? options.goalMaxTokens
1112
+ : undefined,
1113
+ goalMaxCostUsd: Number.isFinite(options.goalMaxCost)
1114
+ ? options.goalMaxCost
1115
+ : undefined,
1116
+ goalMaxTimeMs: Number.isFinite(options.goalMaxTime)
1117
+ ? options.goalMaxTime
1118
+ : undefined,
1055
1119
  // --mcp-config: connect ad-hoc MCP servers + expose their tools
1056
1120
  mcpConfig: options.mcpConfig || null,
1057
1121
  // --no-mcp: skip registered (cc mcp add) auto-connect servers
@@ -1081,21 +1145,51 @@ export function registerAgentCommand(program) {
1081
1145
  chatFn: fallbackChatFn,
1082
1146
  };
1083
1147
 
1084
- // --json-schema: structured output 鈥?wrap the runner with capture +
1085
- // validate + retry (json-schema-output.js); prints only the
1086
- // validated JSON. Incompatible with stream-json (event stream and a
1087
- // single JSON contract don't mix).
1148
+ // --json-schema: structured output. Accepts a file path OR inline JSON.
1149
+ // - text/json output: wrap the runner with capture + validate + retry
1150
+ // (json-schema-output.js); prints only the validated JSON.
1151
+ // - stream-json output: stream events normally, then emit a terminal
1152
+ // `structured_result` event (schema_digest + valid + value/errors) as
1153
+ // the last NDJSON line, so the two protocols are now compatible.
1088
1154
  if (options.jsonSchema) {
1155
+ const jso = await import("../lib/json-schema-output.js");
1089
1156
  if (options.outputFormat === "stream-json") {
1090
- process.stderr.write(
1091
- "--json-schema is incompatible with --output-format stream-json.\n",
1092
- );
1093
- process.exit(1);
1157
+ try {
1158
+ const schema = jso.loadSchemaFile(
1159
+ jso._deps.fs,
1160
+ options.jsonSchema,
1161
+ );
1162
+ const instruction = jso.buildSchemaInstruction(schema);
1163
+ const outcome = await runAgentHeadless({
1164
+ ...headlessOptions,
1165
+ appendSystemPrompt: [
1166
+ headlessOptions.appendSystemPrompt,
1167
+ instruction,
1168
+ ]
1169
+ .filter(Boolean)
1170
+ .join("\n\n"),
1171
+ });
1172
+ // The normal stream (init … result) has already been written to
1173
+ // stdout by the runner. Append the structured verdict as the final
1174
+ // NDJSON line, built from the run's final text.
1175
+ const parsed = jso.extractJsonPayload(
1176
+ String(outcome?.result ?? ""),
1177
+ );
1178
+ const evt = jso.buildStructuredResult(
1179
+ schema,
1180
+ parsed.ok ? parsed.value : null,
1181
+ );
1182
+ process.stdout.write(JSON.stringify(evt) + "\n");
1183
+ await _finishWorktree();
1184
+ process.exit(evt.valid ? 0 : 1);
1185
+ } catch (err) {
1186
+ process.stderr.write(`Error: ${err.message}\n`);
1187
+ await _finishWorktree();
1188
+ process.exit(1);
1189
+ }
1094
1190
  }
1095
1191
  try {
1096
- const { runJsonSchemaConstrained } =
1097
- await import("../lib/json-schema-output.js");
1098
- const code = await runJsonSchemaConstrained({
1192
+ const code = await jso.runJsonSchemaConstrained({
1099
1193
  schemaFile: options.jsonSchema,
1100
1194
  baseOptions: headlessOptions,
1101
1195
  runHeadless: runAgentHeadless,