chainlesschain 0.162.160 → 0.162.162

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 (199) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-CkRHjW8e.js → AIOps-ClI2ALgv.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-BP3MeNi2.js → ActionButton-BDjRcJaK.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-CjbjQsOr.js → Analytics-DikSRsaT.js} +2 -2
  5. package/src/assets/web-panel/assets/{AppLayout-i0adH1M8.js → AppLayout-9mLhMFmz.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-CCCG2GOU.js → Artifacts-BSr88BrH.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-DrsgvwHC.js → Audit-CdvyimOF.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-DkXNkbZM.js → BackgroundAgents-D3_hCedh.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-DarTWCex.js → Backup-Cyvv9RHf.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-DaDzhcto.js → BaseInput-BZM14kt8.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-C4EzGw6V.js → Chat-DrQSokVz.js} +5 -5
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-lkavIWMS.js → ChatBubbleRenderer-HwJR-C6B.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-gddNgaQi.js → Checkbox-bFLYeWMV.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-BKldFWf9.js → Codegen-7mN7xJXT.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-ChR-brDs.js → Col-DAB5ZWm7.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-DlHtZX86.js → Community-B_1TymaK.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-BAhfcBpC.js → Compact-Bq1uSWLJ.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-gIrKkm6m.js → Compliance-CyiE1jEe.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-BYnzLBO3.js → Cowork-Cpk0D7vm.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-ByUDIljI.js → Cron-d_Lv3rdB.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-DOdNbHFp.js → Crosschain-DMqhHjMR.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-BJHaNWD2.js → DID-Br-oPuBw.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-Drm_tClz.js → Dashboard-CfLW_RaP.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-CA1WLzGN.js → Dropdown-DZnTOExV.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-Di1SKjNO.js → EmailListRenderer-DHIUHDv1.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-44Ve7ZFe.js → FamilyGuardDashboard-Bw3t3myu.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-CubPwv_N.js → Federation-D9cpQPAY.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-CsUFe5bU.js → FormItemContext-Jdczr9Mr.js} +1 -1
  29. package/src/assets/web-panel/assets/GenericCardRenderer-BT8ZMpC5.js +1 -0
  30. package/src/assets/web-panel/assets/{Git-D-Qx9dhO.js → Git-DjARip2b.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-_YMYaU4n.js → Governance-BgWbSRcj.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-J7Yzh-K_.js → Inference-CfouJciM.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-D37iI_Mo.js → KnowledgeGraph-50l7mrAr.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-oMTSYfGY.js → Logs-rq50H84B.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-BqaPmC0W.js → MarkdownRenderer-DUPIEdQH.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-DKUqYDaF.js → Marketplace-BYgPDmHU.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-BEv3Gx61.js → McpTools-5DVgcFdx.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-CtA8XD2y.js → Memory-B70LPk7f.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-ug7c1mMj.js → MobileBridge-De8ssA5Y.js} +1 -1
  40. package/src/assets/web-panel/assets/MobileProjects-D10VISps.js +1 -0
  41. package/src/assets/web-panel/assets/{Mtc-WJaMs4i2.js → Mtc-RJsOTsIA.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-DXnK_-ru.js → MtcAudit-Crh-6BEK.js} +5 -5
  43. package/src/assets/web-panel/assets/{Multisig-C4Ev7726.js → Multisig-wuoR5l3x.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-BMFb9_zk.js → NLProgramming-U2Mji4fZ.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-Bl93nvBX.js → Notes-CdTZAyDH.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-BN2W5cl6.js → NotificationSettings-C-FucWE7.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-Dxc9oN4o.js → OrderTableRenderer-DCYFTeaI.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-CAs48637.js → Organization-an3OF85L.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-Bhq27Zoc.js → Overflow-C-1vmYFe.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-D61-2542.js → P2P-Cwh47xKg.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-C1j4iUxz.js → PdhVaultBrowser-Dm38GT0k.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-rJ2-fScM.js → Permissions-DS2Y8awD.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-DFIlTok6.js → PersonalDataHub-BQNziA0I.js} +3 -3
  54. package/src/assets/web-panel/assets/{Pipeline-BjIgEWoZ.js → Pipeline-DF85L1ue.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-BQoH5wcX.js → Privacy-BuLornF-.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-CYR_-_Ub.js → ProjectInit-C3Y31IwX.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-nkatCq-9.js → ProjectSettings-BCPiI8aD.js} +2 -2
  58. package/src/assets/web-panel/assets/Projects-BZ6s4k6g.js +1 -0
  59. package/src/assets/web-panel/assets/{Providers-BnKM9fsF.js → Providers-D3Wp022X.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-Cfe82dbz.js → QrScannerModal-CUJfQkH4.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-r1RbPufV.js → QuickAsk-UAc0EO8l.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-CjCbM2WE.js → Recommend-BaxKupcs.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-BuMGPH6V.js → RemoteSession-C75NNnXZ.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-Bmj3NY1F.js → Reputation-BVXynZAk.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-BIsNcE_1.js → Row-C5MRZ0bi.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-BObmojD2.js → RssFeed-DAKkEYka.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-BRmbkQNS.js → Search-DvWv9YOA.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-_rF3DRpY.js → Security-B5peafW1.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-D0If9ZV4.js → Services-UQmvPEaG.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-o7zIksUg.js → Skeleton-C3bhWpuF.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-oUR7zi9u.js → Skills-DfkKf9_H.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-fqKyGi03.js → Sla-DqMSjlBM.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-B1wZ_gxx.js → SpeechSettings-C-WbxWUX.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-B3qJWYXq.js → SyncSettings-CYuHZ4N2.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-BH5_dUfr.js → Tasks-CyeAyB9d.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-CCIVpjRP.js → Templates-BTCeQnpq.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-2GZtTGnB.js → Tenant-BifQuA9Y.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-BlMkfs_u.js → Terminal-Bx_9Bocf.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-U6hi5OMc.js → TimelineRenderer-BX0RFCOm.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-C-_BerGJ.js → Tokens-CGHrCmUS.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-DaKzBgN3.js → Trigger-DlqaY4SO.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-CLvcsco3.js → Trust-DfOmj-ay.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-BFn_Y1zZ.js → UkeySign-B72jH5fE.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-B0V18dLT.js → VideoEditing-BT-63fqv.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-BbmP7Imu.js → Wallet-Dsul3sHr.js} +3 -3
  86. package/src/assets/web-panel/assets/{WebAuthn-Dewj_0kt.js → WebAuthn-Izkwwdgy.js} +4 -4
  87. package/src/assets/web-panel/assets/{WorkflowEditor-DOxz3Lwa.js → WorkflowEditor-D3FHSiH1.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-B1OIpZnh.js → chat-CmVz5z3U.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-CSGNVdri.js → colors-CuLgyJBp.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-8S0vcg0u.js → compact-item-Ml1YkXpw.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-vZyc27TN.js → createContext-BvjJ48bs.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-DhiGrbsh.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-DWw3-UhA.js → hasIn-DVC0157C.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DLAvUtOH.js → index-4SBta3d7.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BZnauCqN.js → index-4wcgvPoF.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-t-q3rkVm.js → index-B0S8NzO6.js} +1 -1
  97. package/src/assets/web-panel/assets/index-B7a1TrU6.js +1 -0
  98. package/src/assets/web-panel/assets/{index-BwKrTVV0.js → index-BUt8kTIB.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BQssf0EQ.js → index-B_EvG_GW.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-xx5M5Rbj.js → index-BbPtzB8M.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DKwl1AkQ.js → index-BbepULSc.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-B4pP2ycr.js → index-BfuKP4qe.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-D1i_8oop.js → index-BiC6dQZG.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Dc40pX3F.js → index-BjHBpRYZ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DeCS12Mh.js → index-BpFJHiTv.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BjC-TrOb.js → index-BydjdZnl.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DcfLXAW7.js → index-BzG-x9p4.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CfEvacz7.js → index-Bzr0rB5T.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BoIPHP4R.js → index-C229ABfy.js} +1 -1
  110. package/src/assets/web-panel/assets/index-C2B1KRja.js +1 -0
  111. package/src/assets/web-panel/assets/{index-BdyokEau.js → index-CFYVQMvu.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CLPr2vC_.js → index-CXkJH-lK.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DtHWHJnj.js → index-C_H_S-DY.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Q6oyLnKC.js → index-C_XuRgGn.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BeBv896m.js → index-C_vRgTl9.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DLgkAhEE.js → index-Ck_52dz_.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-tr1x96W9.js → index-CmFNvNLZ.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CB2MOS_8.js → index-CudmwNBV.js} +3 -3
  119. package/src/assets/web-panel/assets/{index-CFQWZvak.js → index-Cw0Rjy87.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BuB5P0TR.js → index-D3lD5qap.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-D8qXVfoV.js → index-DC_OI9H2.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-C6r27A-o.js → index-DDGaOWBV.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CbZqw7pg.js → index-DJyN28Et.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CJgKP2cm.js → index-DYXHFr4r.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-CI3slBKI.js → index-Dt5KMjwJ.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CwXBblm-.js → index-DuGszaWY.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BKEmQQvR.js → index-DukDVXCv.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Dg0Izipr.js → index-KMqhcF7l.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BkWd6YL7.js → index-KjgKdZe-.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DPgY5GLy.js → index-QXzlT9KD.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BDHyovnK.js → index-vG4XSJ1d.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-dxocDVD_.js → index-wvb_FtaV.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-Di5YFId0.js → initDefaultProps-BDgd2YZ8.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-BVSN0aeL.js → motion-ErtrXdYe.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-DHNiaP2l.js → move-_GPPNCgs.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-xJPf3gQD.js → mtc-parser-DRLL-8V2.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-C7qzUdkN.js → omit-p86sl023.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-pSpjg3Le.js → pickAttrs-BTu7cMLH.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-cs_jL_UC.js → placementArrow-BEKz6Isr.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-DkS_Zs4r.js → responsiveObserve-Ck42c9MP.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-B5O1Kpxo.js → slide-1CEvuPsB.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-BX02GnZp.js → statusUtils-BDfdx1GJ.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-EmBYRftd.js → styleChecker-B4K46m5w.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-ZGzN1cU8.js → useFlexGapSupport-DoIVpJNq.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-CfP7M1m7.js → useFs-DdAWi-ln.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-CbIPHgNh.js → usePersonalDataHub-iUu6k8dx.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-7xh0If_O.js → vnode-DZz4R6Tw.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-DmitvTR2.js → zoom-DXlf9sA3.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/agenda.js +15 -1
  151. package/src/commands/agent.js +7 -0
  152. package/src/commands/background-session.js +16 -2
  153. package/src/commands/review.js +6 -1
  154. package/src/data/changelog.json +12 -2
  155. package/src/gateways/ws/background-agent-protocol.js +88 -14
  156. package/src/lib/agent-authority.js +251 -0
  157. package/src/lib/agents.js +19 -0
  158. package/src/lib/background-agent-phase.js +100 -0
  159. package/src/lib/background-agent-supervisor.js +322 -15
  160. package/src/lib/backpressure-policy.js +105 -0
  161. package/src/lib/capability-negotiation.js +212 -0
  162. package/src/lib/credential-guard.js +11 -0
  163. package/src/lib/credential-proxy.js +182 -0
  164. package/src/lib/docs-drift.js +166 -0
  165. package/src/lib/doctor-checkup.js +76 -0
  166. package/src/lib/event-seq-replay.js +167 -0
  167. package/src/lib/goal-condition-engine.js +369 -0
  168. package/src/lib/headless-manifest.js +13 -0
  169. package/src/lib/hook-event-bus.cjs +0 -0
  170. package/src/lib/ide-context.js +53 -14
  171. package/src/lib/json-schema-output.js +30 -1
  172. package/src/lib/json-schema-validate.js +691 -0
  173. package/src/lib/lsp/__tests__/diagnostics-scheduler.test.js +93 -0
  174. package/src/lib/lsp/diagnostics-scheduler.js +127 -0
  175. package/src/lib/operation-fingerprint.js +95 -0
  176. package/src/lib/plugin-runtime/capabilities.js +371 -0
  177. package/src/lib/plugin-runtime/manifest.js +30 -0
  178. package/src/lib/project-instructions.js +98 -1
  179. package/src/lib/remote-approval-bridge.js +16 -0
  180. package/src/lib/remote-path-mapping.js +229 -0
  181. package/src/lib/review-pipeline.js +227 -0
  182. package/src/lib/runtime-checkup.js +318 -0
  183. package/src/lib/schedule-planner.js +136 -0
  184. package/src/lib/session-lifecycle.js +305 -0
  185. package/src/lib/settings-hook-events.cjs +39 -13
  186. package/src/lib/subagent-contract.js +383 -0
  187. package/src/lib/telemetry-ids.js +190 -0
  188. package/src/lib/turn-binding.js +397 -0
  189. package/src/repl/bg-dashboard.js +13 -12
  190. package/src/runtime/agent-core.js +24 -10
  191. package/src/runtime/headless-stream.js +195 -11
  192. package/src/runtime/system-prompt.js +3 -0
  193. package/src/workers/background-agent-worker.js +37 -2
  194. package/src/assets/web-panel/assets/GenericCardRenderer-D7xRjPjZ.js +0 -1
  195. package/src/assets/web-panel/assets/MobileProjects-BpX5g9It.js +0 -1
  196. package/src/assets/web-panel/assets/Projects-SS9uZKCl.js +0 -1
  197. package/src/assets/web-panel/assets/devWarning-DYEZcY55.js +0 -1
  198. package/src/assets/web-panel/assets/index-DmVFfcNR.js +0 -1
  199. package/src/assets/web-panel/assets/index-x7YyhMUa.js +0 -1
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Runtime Checkup evaluators for `cc doctor` (P2 "Doctor").
3
+ *
4
+ * `cc doctor` today checks install/config/service health. The gap upgrades it to
5
+ * a RUNTIME checkup over live state: stale sessions/worktrees/agenda, orphaned
6
+ * background processes, slow or circuit-broken hooks, dead plugins/LSP servers,
7
+ * and verbose / code-derivable instruction files.
8
+ *
9
+ * This is the PURE evaluation core. The command layer collects a runtime
10
+ * SNAPSHOT (from session store, worktree list, agenda store, process table,
11
+ * hook/plugin stats) and a `now`, and this returns categorized findings. No I/O,
12
+ * no clock, no process access here → deterministic + unit-testable.
13
+ */
14
+
15
+ const DAY = 24 * 60 * 60 * 1000;
16
+
17
+ export const CHECKUP_SEVERITY = Object.freeze({
18
+ info: 1,
19
+ warn: 2,
20
+ error: 3,
21
+ });
22
+
23
+ function sev(level) {
24
+ return CHECKUP_SEVERITY[level] || CHECKUP_SEVERITY.info;
25
+ }
26
+
27
+ /** Default thresholds; every one is overridable per-call. */
28
+ export const DEFAULT_CHECKUP_THRESHOLDS = Object.freeze({
29
+ staleSessionMs: 30 * DAY,
30
+ staleWorktreeMs: 7 * DAY,
31
+ agendaMissedGraceMs: 60 * 60 * 1000, // due but never fired for >1h → missed
32
+ agendaFiredRetireMs: 30 * DAY, // long-fired entry left lying around
33
+ slowHookMs: 5000,
34
+ hookFailureThreshold: 3,
35
+ instructionsMaxBytes: 12 * 1024,
36
+ instructionsDerivableRatio: 0.6, // ≥60% derivable from code → prune candidate
37
+ });
38
+
39
+ function finding(id, category, level, message, remediation, extra = {}) {
40
+ return {
41
+ id,
42
+ category,
43
+ severity: level,
44
+ severityRank: sev(level),
45
+ message,
46
+ remediation,
47
+ ...extra,
48
+ };
49
+ }
50
+
51
+ /** Sessions untouched past the stale threshold. */
52
+ export function checkStaleSessions(sessions = [], now, t) {
53
+ const out = [];
54
+ for (const s of sessions) {
55
+ const last = Number(s?.lastActiveAt);
56
+ if (!Number.isFinite(last)) continue;
57
+ const age = now - last;
58
+ if (age > t.staleSessionMs) {
59
+ out.push(
60
+ finding(
61
+ "stale-session",
62
+ "sessions",
63
+ "warn",
64
+ `Session ${s.id} idle ${Math.floor(age / DAY)}d`,
65
+ "cc session prune",
66
+ { ref: s.id, ageMs: age },
67
+ ),
68
+ );
69
+ }
70
+ }
71
+ return out;
72
+ }
73
+
74
+ /** Worktrees that are merged, or simply old and untouched. */
75
+ export function checkStaleWorktrees(worktrees = [], now, t) {
76
+ const out = [];
77
+ for (const w of worktrees) {
78
+ const created = Number(w?.createdAt);
79
+ if (!Number.isFinite(created)) continue;
80
+ const age = now - created;
81
+ if (w?.merged) {
82
+ out.push(
83
+ finding(
84
+ "merged-worktree",
85
+ "worktrees",
86
+ "warn",
87
+ `Worktree ${w.path} is merged and can be removed`,
88
+ "cc team worktree rm / git worktree remove",
89
+ { ref: w.path, merged: true },
90
+ ),
91
+ );
92
+ } else if (age > t.staleWorktreeMs) {
93
+ out.push(
94
+ finding(
95
+ "stale-worktree",
96
+ "worktrees",
97
+ "info",
98
+ `Worktree ${w.path} unused ${Math.floor(age / DAY)}d`,
99
+ "review and remove if abandoned",
100
+ { ref: w.path, ageMs: age },
101
+ ),
102
+ );
103
+ }
104
+ }
105
+ return out;
106
+ }
107
+
108
+ /** Agenda entries that missed their fire window, or long-fired leftovers. */
109
+ export function checkAgenda(agenda = [], now, t) {
110
+ const out = [];
111
+ for (const a of agenda) {
112
+ const dueAt = Number(a?.dueAt);
113
+ const firedAt = Number(a?.firedAt);
114
+ if (Number.isFinite(dueAt) && !Number.isFinite(firedAt)) {
115
+ if (now - dueAt > t.agendaMissedGraceMs) {
116
+ out.push(
117
+ finding(
118
+ "agenda-missed",
119
+ "agenda",
120
+ "error",
121
+ `Agenda ${a.id} was due but never fired`,
122
+ "cc agenda list / re-arm or remove",
123
+ { ref: a.id, dueAt },
124
+ ),
125
+ );
126
+ }
127
+ } else if (
128
+ Number.isFinite(firedAt) &&
129
+ now - firedAt > t.agendaFiredRetireMs &&
130
+ !a?.recurring
131
+ ) {
132
+ out.push(
133
+ finding(
134
+ "agenda-retire",
135
+ "agenda",
136
+ "info",
137
+ `Agenda ${a.id} fired ${Math.floor((now - firedAt) / DAY)}d ago and can be retired`,
138
+ "cc agenda prune",
139
+ { ref: a.id, firedAt },
140
+ ),
141
+ );
142
+ }
143
+ }
144
+ return out;
145
+ }
146
+
147
+ /**
148
+ * Background processes whose recorded parent is gone (orphans), or that outlived
149
+ * the session that spawned them.
150
+ */
151
+ export function checkOrphanProcesses(processes = [], activeSessionIds = []) {
152
+ const active = new Set(activeSessionIds.map(String));
153
+ const out = [];
154
+ for (const p of processes) {
155
+ if (!p?.alive) continue;
156
+ const parentDead = p.parentAlive === false;
157
+ const sessionGone = p.sessionId != null && !active.has(String(p.sessionId));
158
+ if (parentDead || sessionGone) {
159
+ out.push(
160
+ finding(
161
+ "orphan-process",
162
+ "processes",
163
+ "error",
164
+ `Background process pid=${p.pid}${p.kind ? ` (${p.kind})` : ""} orphaned`,
165
+ "terminate stray process; check background-shell cleanup",
166
+ { ref: p.pid, reason: parentDead ? "parent-dead" : "session-gone" },
167
+ ),
168
+ );
169
+ }
170
+ }
171
+ return out;
172
+ }
173
+
174
+ /** Hooks that run slow or have tripped their circuit breaker. */
175
+ export function checkHooks(hooks = [], t) {
176
+ const out = [];
177
+ for (const h of hooks) {
178
+ if (h?.circuitOpen) {
179
+ out.push(
180
+ finding(
181
+ "hook-circuit-open",
182
+ "hooks",
183
+ "error",
184
+ `Hook ${h.id} circuit is open after ${h.failures ?? "?"} failures`,
185
+ "fix the hook or disable it in settings",
186
+ { ref: h.id, failures: h.failures },
187
+ ),
188
+ );
189
+ continue;
190
+ }
191
+ if (Number(h?.failures) >= t.hookFailureThreshold) {
192
+ out.push(
193
+ finding(
194
+ "hook-failing",
195
+ "hooks",
196
+ "warn",
197
+ `Hook ${h.id} failed ${h.failures} times`,
198
+ "inspect hook logs",
199
+ { ref: h.id, failures: h.failures },
200
+ ),
201
+ );
202
+ }
203
+ if (Number(h?.avgMs) > t.slowHookMs) {
204
+ out.push(
205
+ finding(
206
+ "hook-slow",
207
+ "hooks",
208
+ "warn",
209
+ `Hook ${h.id} averages ${Math.round(h.avgMs)}ms`,
210
+ "make the hook async or faster",
211
+ { ref: h.id, avgMs: h.avgMs },
212
+ ),
213
+ );
214
+ }
215
+ }
216
+ return out;
217
+ }
218
+
219
+ /** Plugins / LSP servers reported unhealthy or dead. */
220
+ export function checkPluginsAndLsp(plugins = []) {
221
+ const out = [];
222
+ for (const p of plugins) {
223
+ if (p?.healthy === false) {
224
+ const isLsp = p.kind === "lsp" || p.lsp === true;
225
+ out.push(
226
+ finding(
227
+ isLsp ? "lsp-dead" : "plugin-dead",
228
+ isLsp ? "lsp" : "plugins",
229
+ "warn",
230
+ `${isLsp ? "LSP server" : "Plugin"} ${p.id} is unavailable`,
231
+ isLsp ? "cc code-intel / restart server" : "cc plugin validate",
232
+ { ref: p.id },
233
+ ),
234
+ );
235
+ }
236
+ }
237
+ return out;
238
+ }
239
+
240
+ /** Instruction files (cc.md / AGENTS.md) that are oversized or code-derivable. */
241
+ export function checkInstructionFiles(files = [], t) {
242
+ const out = [];
243
+ for (const f of files) {
244
+ const bytes = Number(f?.bytes);
245
+ if (Number.isFinite(bytes) && bytes > t.instructionsMaxBytes) {
246
+ out.push(
247
+ finding(
248
+ "instructions-verbose",
249
+ "instructions",
250
+ "info",
251
+ `${f.path} is ${(bytes / 1024).toFixed(1)}KB — consider trimming`,
252
+ "move detail into referenced docs",
253
+ { ref: f.path, bytes },
254
+ ),
255
+ );
256
+ }
257
+ const ratio = Number(f?.derivableRatio);
258
+ if (Number.isFinite(ratio) && ratio >= t.instructionsDerivableRatio) {
259
+ out.push(
260
+ finding(
261
+ "instructions-derivable",
262
+ "instructions",
263
+ "info",
264
+ `${f.path} is ~${Math.round(ratio * 100)}% derivable from code`,
265
+ "delete code-derivable prose; keep only non-obvious guidance",
266
+ { ref: f.path, derivableRatio: ratio },
267
+ ),
268
+ );
269
+ }
270
+ }
271
+ return out;
272
+ }
273
+
274
+ /**
275
+ * Run every checkup against a runtime snapshot. Returns findings sorted most
276
+ * severe first, plus a rollup. Any missing snapshot section is simply skipped —
277
+ * partial snapshots produce partial (never crashing) results.
278
+ *
279
+ * @param {object} snapshot { sessions, worktrees, agenda, processes,
280
+ * activeSessionIds, hooks, plugins, instructionFiles, now }
281
+ * @param {object} [opts] { thresholds }
282
+ */
283
+ export function runRuntimeCheckup(snapshot = {}, { thresholds = {} } = {}) {
284
+ const t = { ...DEFAULT_CHECKUP_THRESHOLDS, ...thresholds };
285
+ const now = Number.isFinite(Number(snapshot.now))
286
+ ? Number(snapshot.now)
287
+ : null;
288
+ const findings = [];
289
+ if (now != null) {
290
+ findings.push(...checkStaleSessions(snapshot.sessions, now, t));
291
+ findings.push(...checkStaleWorktrees(snapshot.worktrees, now, t));
292
+ findings.push(...checkAgenda(snapshot.agenda, now, t));
293
+ }
294
+ findings.push(
295
+ ...checkOrphanProcesses(snapshot.processes, snapshot.activeSessionIds),
296
+ );
297
+ findings.push(...checkHooks(snapshot.hooks, t));
298
+ findings.push(...checkPluginsAndLsp(snapshot.plugins));
299
+ findings.push(...checkInstructionFiles(snapshot.instructionFiles, t));
300
+
301
+ findings.sort(
302
+ (a, b) =>
303
+ b.severityRank - a.severityRank ||
304
+ String(a.category).localeCompare(String(b.category)),
305
+ );
306
+
307
+ const byCategory = {};
308
+ const bySeverity = { error: 0, warn: 0, info: 0 };
309
+ for (const f of findings) {
310
+ byCategory[f.category] = (byCategory[f.category] || 0) + 1;
311
+ bySeverity[f.severity] = (bySeverity[f.severity] || 0) + 1;
312
+ }
313
+ return {
314
+ findings,
315
+ summary: { total: findings.length, byCategory, bySeverity },
316
+ ok: bySeverity.error === 0,
317
+ };
318
+ }
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Pure scheduling planner for the agent Scheduler / Monitor (P1 §"统一持久
3
+ * Scheduler、Monitor 和 Channel").
4
+ *
5
+ * The persisted store ([[agent-schedule-store.js]]) already guarantees the
6
+ * catch-up invariant — a cron down for N periods fires ONCE, not N times,
7
+ * because `advanceCron` jumps to `nextCronTime(now)` rather than stepping
8
+ * through every miss. This module adds the pieces a unified daemon event
9
+ * runtime still needs, all as PURE, clock-injected functions (no timers, no
10
+ * RNG, deterministic across restarts):
11
+ *
12
+ * - **deterministic jitter** — spread tasks that share a cron minute (e.g.
13
+ * every `0 * * * *`) by a STABLE per-task offset, so a shared tick is not a
14
+ * thundering herd. The offset is a hash of the task id, so it never moves
15
+ * across a restart (an RNG offset would, defeating the point).
16
+ * - **adaptive next-wakeup** — the earliest future fire time, so a daemon
17
+ * sleeps until then instead of polling.
18
+ * - **expiry** — a task past `expiresAt` retires without a final fire.
19
+ *
20
+ * A daemon uses `partitionSchedule` (retire expired, fire due) then
21
+ * `msUntilNextWakeup` (sleep precisely). `jitterMs = 0` (the default) makes the
22
+ * due set byte-identical to the store's existing `due()`.
23
+ */
24
+
25
+ // FNV-1a → a stable 32-bit hash. Deterministic and dependency-free, so the same
26
+ // task always draws the same jitter offset (across processes and restarts).
27
+ function hash32(str) {
28
+ let h = 0x811c9dc5;
29
+ const s = String(str ?? "");
30
+ for (let i = 0; i < s.length; i += 1) {
31
+ h ^= s.charCodeAt(i);
32
+ h = Math.imul(h, 0x01000193);
33
+ }
34
+ return h >>> 0;
35
+ }
36
+
37
+ /**
38
+ * A stable jitter offset in `[0, jitterMs)` derived from the task id. Same id +
39
+ * same jitterMs → same offset, every time. `jitterMs <= 0` → no jitter (0).
40
+ */
41
+ export function jitterOffsetMs(id, jitterMs) {
42
+ const j = Math.floor(Number(jitterMs) || 0);
43
+ if (j <= 0) return 0;
44
+ return hash32(id) % j;
45
+ }
46
+
47
+ /** The entry's base fire time: `dueAt` for a wakeup, `nextAt` otherwise. */
48
+ export function baseFireAt(entry) {
49
+ if (!entry) return null;
50
+ const raw = entry.kind === "wakeup" ? entry.dueAt : entry.nextAt;
51
+ const n = Number(raw);
52
+ return Number.isFinite(n) ? n : null;
53
+ }
54
+
55
+ /** Base fire time with the entry's deterministic jitter applied. */
56
+ export function effectiveFireAt(entry, jitterMs = 0) {
57
+ const base = baseFireAt(entry);
58
+ if (base == null) return null;
59
+ return base + jitterOffsetMs(entry?.id, jitterMs);
60
+ }
61
+
62
+ /** Is an entry still pending/active (not in a terminal state)? */
63
+ export function isSchedulable(entry) {
64
+ if (!entry) return false;
65
+ if (entry.kind === "wakeup") return entry.status === "pending";
66
+ return entry.status === "active";
67
+ }
68
+
69
+ /**
70
+ * Expiry: a task with an `expiresAt` at or before `now` is retired regardless
71
+ * of its schedule. A missing / non-positive `expiresAt` never expires.
72
+ */
73
+ export function isEntryExpired(entry, now) {
74
+ const exp = Number(entry?.expiresAt);
75
+ return Number.isFinite(exp) && exp > 0 && exp <= Number(now);
76
+ }
77
+
78
+ /**
79
+ * Partition schedulable entries at `now` into what a daemon should do:
80
+ * - `expired` — retire (status → expired). Checked BEFORE due so an expired
81
+ * task never gets a final fire.
82
+ * - `due` — fire now (jittered fire time has arrived).
83
+ * - `waiting` — still pending.
84
+ * Terminal entries are skipped entirely.
85
+ */
86
+ export function partitionSchedule(entries, { now, jitterMs = 0 } = {}) {
87
+ const t = Number(now);
88
+ const expired = [];
89
+ const due = [];
90
+ const waiting = [];
91
+ for (const entry of entries || []) {
92
+ if (!isSchedulable(entry)) continue;
93
+ if (isEntryExpired(entry, t)) {
94
+ expired.push(entry);
95
+ continue;
96
+ }
97
+ const fireAt = effectiveFireAt(entry, jitterMs);
98
+ if (fireAt != null && fireAt <= t) due.push(entry);
99
+ else waiting.push(entry);
100
+ }
101
+ return { expired, due, waiting };
102
+ }
103
+
104
+ /**
105
+ * The earliest future jittered fire time among schedulable, non-expired
106
+ * entries — what a daemon sleeps until (adaptive wakeup). An already-due entry
107
+ * contributes `now` (fire immediately). Returns null when nothing is scheduled.
108
+ */
109
+ export function nextWakeupAt(entries, { now, jitterMs = 0 } = {}) {
110
+ const t = Number(now);
111
+ let min = null;
112
+ for (const entry of entries || []) {
113
+ if (!isSchedulable(entry) || isEntryExpired(entry, t)) continue;
114
+ const fireAt = effectiveFireAt(entry, jitterMs);
115
+ if (fireAt == null) continue;
116
+ const at = fireAt <= t ? t : fireAt;
117
+ if (min == null || at < min) min = at;
118
+ }
119
+ return min;
120
+ }
121
+
122
+ /**
123
+ * Milliseconds until the next wakeup, clamped to `[0, maxMs]` (a cap keeps a
124
+ * far-future task from parking the daemon forever — it re-checks by `maxMs`).
125
+ * null when nothing is scheduled.
126
+ */
127
+ export function msUntilNextWakeup(
128
+ entries,
129
+ { now, jitterMs = 0, maxMs = null } = {},
130
+ ) {
131
+ const at = nextWakeupAt(entries, { now, jitterMs });
132
+ if (at == null) return null;
133
+ let ms = Math.max(0, at - Number(now));
134
+ if (maxMs != null && Number.isFinite(maxMs)) ms = Math.min(ms, Number(maxMs));
135
+ return ms;
136
+ }