chainlesschain 0.162.161 → 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 (209) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-DMn528q-.js → AIOps-BQ0kgfA1.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-D9ZcDCJL.js → ActionButton-Dr1MDANe.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-A_K-8PX0.js → Analytics-CZ5vsaXW.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-D4pFW3X6.js → AppLayout-BIBga881.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-Dz46COAd.js → Artifacts-vpXHRU3u.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-cbMjbyg0.js → Audit-B8exuoub.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-Dtf88GAj.js → BackgroundAgents-923evRRN.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-C5KRxn52.js → Backup-sr_sswlQ.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-Dd2UK3BN.js → BaseInput-CnW-4XKT.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-lptKTFdG.js → Chat-FZCsTZIL.js} +5 -5
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-DDPd9qT0.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-_yspG6rG.js → Checkbox-DFrxB59H.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-BGq_GAha.js → Codegen-_b6WEel8.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-CePor71g.js → Col-CkbnnZ-K.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-CLOWhIT7.js → Community-BxM_82zW.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-CivJt2vT.js → Compact-DYinP45R.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-Btmo1GGE.js → Compliance-DyXp3sSP.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-Ct35OlG4.js → Cowork-BEr2DhW7.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-CTc2GmhB.js → Cron-B-Vwo1hN.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-ZtiBETk1.js → Crosschain-DbLxKddd.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-BNqMQQXw.js → DID-BsQ_LFw9.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-BmzZd-9V.js → Dashboard-lVlXQHu5.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-CMVgvP2M.js → Dropdown-Beo_foO7.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-CcqQFAfT.js → EmailListRenderer-DszHIXED.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CTqB1_4V.js → FamilyGuardDashboard-DcAwoh81.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-CiKOOzlb.js → Federation-CuJLa2eb.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-Wk-OT0gr.js → FormItemContext-Cxd1lxhc.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-DbEQT6ue.js → GenericCardRenderer-BBvq5Jsw.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-0ZvCPSD9.js → Git-WqPKetx6.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-CmVuKkPb.js → Governance-CWYtY3ke.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-CTWE3Jwg.js → Inference-BnX1iiNV.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-CBEIF00L.js → KnowledgeGraph-BJuM8GN5.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-DLhxDwVG.js → Logs-DqLMNntm.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-BqYwEYou.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-BgsCBrnc.js → Marketplace-_vGfmhNl.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-ClBUGDS2.js → McpTools-BeJDOkDj.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-CnG_ZnYw.js → Memory-Da0EEZDO.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-BUOAbR1Z.js → MobileBridge-DSuzcCCy.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-Dnt_9-WB.js → MobileProjects-D0RAk4e8.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-CLjt4kCD.js → Mtc-BE9YgdA0.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-f24U9Aes.js → MtcAudit-fC2BpO5S.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-GrmGQbSq.js → Multisig-D24Xxqw3.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-DADaWn-n.js → NLProgramming-Bs3oGvS8.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-xtf5RX6C.js → Notes-BFjmAA-3.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-Cd27Rgtx.js → NotificationSettings-BX_9Bhv0.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-fXRxlDjl.js → OrderTableRenderer-wjkZwJXI.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-joCFlNB0.js → Organization-DBcdsVEP.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-B1HoURq0.js → Overflow-DDyaF9vC.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-DiwsKbT7.js → P2P-BB78L4he.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-Xe-BsW6S.js → PdhVaultBrowser-DqTiZFum.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-C23qi8kl.js → Permissions-INbnWv4j.js} +3 -3
  53. package/src/assets/web-panel/assets/{PersonalDataHub-BS1syX2f.js → PersonalDataHub-BST-VmTq.js} +2 -2
  54. package/src/assets/web-panel/assets/{Pipeline-DRYwwDyh.js → Pipeline-CE7_tS2k.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-BQKaDHPH.js → Privacy-hK5L8dOB.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-BC6F9xiG.js → ProjectInit-BvkkUoDn.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-ClFPc1Nd.js → ProjectSettings-u7_EUtkm.js} +2 -2
  58. package/src/assets/web-panel/assets/Projects-5Fmt_8Nw.js +1 -0
  59. package/src/assets/web-panel/assets/{Providers-CLWuOUNt.js → Providers-B9eanarR.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-DiqULTxb.js → QrScannerModal-C0dX5N2f.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-9A03P-E_.js → QuickAsk-CKTpG-PZ.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-CJZAnbq_.js → Recommend-Px_w_XQM.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-C9A1R77h.js → RemoteSession-Di0fnYkM.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-BIZc6Rky.js → Reputation-vesnWjXt.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-DfIPNtuG.js → Row-exbY6LkT.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-C5znFcQ1.js → RssFeed-B3O3x70t.js} +2 -2
  67. package/src/assets/web-panel/assets/{Search-DSs2z3OX.js → Search-uRA0DhNz.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-CV6LcrLY.js → Security-CViqdH6K.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-cmhoX0Xj.js → Services-CkXS97WN.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-tCsg2po7.js → Skeleton-DxW5WYnE.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-BK1KUDiy.js → Skills-CLxmGFse.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-yRgyeA82.js → Sla-Cr5fziXq.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-c1mo1mlp.js → SpeechSettings-H0JSb3cp.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-koIAfuT3.js → SyncSettings-DMYWMae7.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-DkkreAaB.js → Tasks-CbYyDdF4.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-fy5Ctf9M.js → Templates-Cpc8JjiK.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-Bw70Jf6S.js → Tenant-CRh77Jda.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-BKh6Rx-4.js → Terminal-4D8tNBO5.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-Dg54WP0c.js → TimelineRenderer-D41jzzbG.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-BvvD5SxJ.js → Tokens-C_jWEy6b.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-B7Ug9x4e.js → Trigger-Dk1ZUhsy.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-BjvuMORY.js → Trust-CLVy4l7m.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-CZqhkx8p.js → UkeySign-C-FeqFDj.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-ClR50lhX.js → VideoEditing-CqVZzyMm.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-DlfppYjx.js → Wallet-DX9BW9pu.js} +2 -2
  86. package/src/assets/web-panel/assets/{WebAuthn-BirgVr8B.js → WebAuthn-7hQDpWw_.js} +2 -2
  87. package/src/assets/web-panel/assets/{WorkflowEditor-B8DP_bax.js → WorkflowEditor-6XqUAO0j.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-DA8AUZzL.js → chat-5R_vkaJG.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-C38pLGp1.js → colors-huUPZdC-.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-81rtr98o.js → compact-item-CfJlgisE.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-D34ixq8t.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-D4bzqTrZ.js → hasIn-DY8Vxqx7.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-E1OJRXs4.js → index-A48PtXw9.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-D3zCCZRS.js → index-B300RPnT.js} +3 -3
  96. package/src/assets/web-panel/assets/{index-Cd3KuNK5.js → index-B74OXFxC.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-Dmo3dFHb.js → index-BAON7TzU.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Ddk0e_Pi.js → index-BCqxyvqp.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-9qoVpPt6.js → index-BHE8OIls.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DBFSo1NF.js → index-BQS-uPOK.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DUQLXLQM.js → index-BRXOP09X.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-D6GfvEQG.js → index-BYLBPpt8.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DFyWKOBD.js → index-BfCx4pFT.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BP-zkXK8.js → index-BliaT02i.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DMgnRwrK.js → index-BmqGerpC.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BIP62a6r.js → index-BsBtRWbA.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DkHqMnn3.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-B8mkLM4x.js → index-Bxax9vQ6.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-ZRJRItm0.js → index-C2iVSFpy.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CP5alL4t.js → index-CA6sO5VD.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BS3M5H9V.js → index-CA90TZxG.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DvfCEJG-.js → index-CLhBh-qE.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-tKiC8T-4.js → index-CRElRhyS.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-B0QArPQt.js → index-Cbe9G0V4.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-COe_cOLJ.js → index-ChvHubX3.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BvL_3J_n.js → index-Cis_Ip7H.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-F76w1NiL.js → index-Cjxbu68P.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BlxEphl7.js → index-CzKmteNS.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CuS-UPoj.js → index-D0pet5Uy.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CbhE3j5j.js → index-D1VLuww0.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-D_2VYPr1.js → index-DXBcvUnf.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DakfFZ-k.js → index-Db1cLWir.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-fI5sXMLg.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-CoVgZw_b.js → index-KRHroryI.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BvD7b2IK.js → index-d1xkbtNz.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BtGxuQrO.js → index-dKx57io7.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BqIhLAbA.js → index-j3O2EEJe.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BXd2_LmH.js → index-p85YaaKw.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DFHa1HIg.js → index-v3pTaEQm.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D0Ih7Hwn.js → index-vXs3B8GJ.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-ByyvflIR.js → initDefaultProps-MCB7-raZ.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-B3FQm2By.js → motion-UWDF9swV.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-BSfns6Ef.js → move-CsTmgXZU.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-CTG1Ieri.js → mtc-parser-fQy6KGLg.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-DObM_2j6.js → omit-woPnEjd1.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-DOfelQ1t.js → pickAttrs-Bs2dZ1gf.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-CHZILds_.js → placementArrow-C7EVgPkg.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-REbucKSx.js → responsiveObserve-BH2kYCTN.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-C6EZzD7o.js → slide-BFBP2V_e.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-BxkWS-g3.js → statusUtils-xdDxDYse.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-Chklyoq6.js → styleChecker-aAWgPPQf.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-CRVwKrrI.js → useFlexGapSupport-DkGSAWoO.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DV24ik_J.js → useFs-BrOih40N.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-C7vIo4Pg.js → usePersonalDataHub-eIYrgMqO.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-By-v5GlF.js → vnode-B8lutNSE.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-B1w9Am3r.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 +352 -2
  156. package/src/data/changelog.json +14 -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/agents.js +19 -0
  161. package/src/lib/background-agent-supervisor.js +34 -7
  162. package/src/lib/channels/channel-manager.js +54 -2
  163. package/src/lib/cli-context-engineering.js +9 -2
  164. package/src/lib/context-breakdown.js +116 -0
  165. package/src/lib/docs-drift.js +166 -0
  166. package/src/lib/doctor-checkup.js +128 -0
  167. package/src/lib/hook-event-bus.cjs +0 -0
  168. package/src/lib/hook-event-log.cjs +193 -0
  169. package/src/lib/interaction-adapter.js +34 -4
  170. package/src/lib/json-schema-output.js +75 -12
  171. package/src/lib/json-schema-validate.js +831 -0
  172. package/src/lib/lsp/__tests__/diagnostics-scheduler.test.js +93 -0
  173. package/src/lib/lsp/diagnostics-scheduler.js +127 -0
  174. package/src/lib/operation-fingerprint.js +95 -0
  175. package/src/lib/plugin-runtime/capabilities.js +371 -0
  176. package/src/lib/plugin-runtime/capability-consent.js +175 -0
  177. package/src/lib/plugin-runtime/manifest.js +30 -0
  178. package/src/lib/plugin-runtime/policy.js +62 -0
  179. package/src/lib/plugin-runtime/scopes.js +20 -4
  180. package/src/lib/project-instructions.js +128 -0
  181. package/src/lib/remote-approval-bridge.js +16 -0
  182. package/src/lib/repl-goal.js +132 -0
  183. package/src/lib/repl-rewind.js +52 -0
  184. package/src/lib/review-pipeline.js +227 -0
  185. package/src/lib/runtime-checkup.js +318 -0
  186. package/src/lib/schedule-planner.js +17 -1
  187. package/src/lib/session-lifecycle.js +305 -0
  188. package/src/lib/settings-hook-events.cjs +52 -13
  189. package/src/lib/settings-loader.cjs +42 -0
  190. package/src/lib/side-effect-ledger-store.js +88 -0
  191. package/src/lib/side-effect-ledger.js +296 -0
  192. package/src/lib/sub-agent-context.js +74 -5
  193. package/src/lib/subagent-contract.js +383 -0
  194. package/src/lib/subagent-inheritance.js +112 -0
  195. package/src/lib/telemetry-ids.js +190 -0
  196. package/src/lib/turn-binding-store.js +119 -0
  197. package/src/lib/turn-binding.js +397 -0
  198. package/src/repl/agent-repl.js +136 -0
  199. package/src/repl/permission-tier.js +28 -0
  200. package/src/runtime/agent-core.js +387 -14
  201. package/src/runtime/coding-agent-contract-shared.cjs +60 -1
  202. package/src/runtime/headless-runner.js +267 -0
  203. package/src/runtime/headless-stream.js +60 -1
  204. package/src/assets/web-panel/assets/ChatBubbleRenderer-C2KLtwHz.js +0 -1
  205. package/src/assets/web-panel/assets/MarkdownRenderer-DxywvhTU.js +0 -1
  206. package/src/assets/web-panel/assets/Projects-Dn-f5aYe.js +0 -1
  207. package/src/assets/web-panel/assets/devWarning-CHDIEOeN.js +0 -1
  208. package/src/assets/web-panel/assets/index-CY1NURWZ.js +0 -1
  209. package/src/assets/web-panel/assets/index-DokUYqpq.js +0 -1
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Unified session lifecycle state machine (P0#4 "会话生命周期可靠性 —
3
+ * 统一状态机" from CLAUDE_CODE_IDE_GAP_ANALYSIS.md).
4
+ *
5
+ * A running agent session is described by THREE divergent representations that
6
+ * grew independently and disagree on vocabulary:
7
+ *
8
+ * 1. supervisor `status` — running | completed | failed | stopped | lost
9
+ * (liveness reconciliation, owns the terminal verdict). See
10
+ * background-agent-supervisor.js.
11
+ * 2. worker `phase` — starting | working | idle | needs_input |
12
+ * waiting_permission (the live sub-state WHILE running). See
13
+ * [[background-agent-phase.js]].
14
+ * 3. IDE connection state — starting | ready | running | disconnected |
15
+ * crashed (the transport/process view the VS Code / JetBrains panel keeps).
16
+ *
17
+ * None of the three alone can answer the question that matters on reconnect:
18
+ * "is a dangerous tool call about to be silently retried?" The gap doc's P0#4
19
+ * asks for ONE state machine — starting / ready / running / waitingApproval /
20
+ * cancelling / disconnected / recovering / completed / failed / stopped — that
21
+ * every producer folds into, so the supervisor, the IDE bridge and the resume
22
+ * path reason about the same lifecycle.
23
+ *
24
+ * This module is that single source of truth. It is pure (no fs / process /
25
+ * clock): `deriveSessionState` collapses the three axes into one canonical
26
+ * state with a documented precedence, `canTransition` encodes the legal graph,
27
+ * and `requiresIdempotencyGuard` marks the `recovering` window during which a
28
+ * resumed session MUST re-check a dangerous tool's ledger before re-executing
29
+ * (the "危险工具恢复时不得静默重试" invariant) rather than blindly replaying it.
30
+ */
31
+
32
+ /**
33
+ * The canonical unified lifecycle. camelCase mirrors the gap doc's wording.
34
+ * `ready` is the parked/idle steady state (transport open, no active turn);
35
+ * `running` is a live turn. The three terminal states are mutually exclusive
36
+ * and absorbing.
37
+ */
38
+ export const SESSION_STATES = Object.freeze({
39
+ STARTING: "starting",
40
+ READY: "ready",
41
+ RUNNING: "running",
42
+ WAITING_APPROVAL: "waitingApproval",
43
+ CANCELLING: "cancelling",
44
+ DISCONNECTED: "disconnected",
45
+ RECOVERING: "recovering",
46
+ COMPLETED: "completed",
47
+ FAILED: "failed",
48
+ STOPPED: "stopped",
49
+ });
50
+
51
+ const ALL_STATES = new Set(Object.values(SESSION_STATES));
52
+
53
+ /** Terminal (absorbing) states — a session can never leave one. */
54
+ const TERMINAL_STATES = new Set([
55
+ SESSION_STATES.COMPLETED,
56
+ SESSION_STATES.FAILED,
57
+ SESSION_STATES.STOPPED,
58
+ ]);
59
+
60
+ /**
61
+ * Every vocabulary the three producers emit → canonical unified state. Callers
62
+ * that hold a native representation (a supervisor status, a worker phase, an
63
+ * IDE connection state) map it here before reasoning about the lifecycle.
64
+ */
65
+ const STATE_ALIASES = new Map([
66
+ // canonical
67
+ ["starting", SESSION_STATES.STARTING],
68
+ ["ready", SESSION_STATES.READY],
69
+ ["running", SESSION_STATES.RUNNING],
70
+ ["waitingapproval", SESSION_STATES.WAITING_APPROVAL],
71
+ ["cancelling", SESSION_STATES.CANCELLING],
72
+ ["disconnected", SESSION_STATES.DISCONNECTED],
73
+ ["recovering", SESSION_STATES.RECOVERING],
74
+ ["completed", SESSION_STATES.COMPLETED],
75
+ ["failed", SESSION_STATES.FAILED],
76
+ ["stopped", SESSION_STATES.STOPPED],
77
+ // worker phase (background-agent-phase.js)
78
+ ["working", SESSION_STATES.RUNNING],
79
+ ["turn", SESSION_STATES.RUNNING],
80
+ ["idle", SESSION_STATES.READY],
81
+ ["needs_input", SESSION_STATES.WAITING_APPROVAL],
82
+ ["needs-input", SESSION_STATES.WAITING_APPROVAL],
83
+ ["awaiting_input", SESSION_STATES.WAITING_APPROVAL],
84
+ ["question", SESSION_STATES.WAITING_APPROVAL],
85
+ ["waiting_permission", SESSION_STATES.WAITING_APPROVAL],
86
+ ["waiting-permission", SESSION_STATES.WAITING_APPROVAL],
87
+ ["waiting_approval", SESSION_STATES.WAITING_APPROVAL],
88
+ ["awaiting_approval", SESSION_STATES.WAITING_APPROVAL],
89
+ // supervisor liveness — a running process that vanished is a failure.
90
+ ["lost", SESSION_STATES.FAILED],
91
+ // IDE connection / transport
92
+ ["connected", SESSION_STATES.READY],
93
+ ["reconnecting", SESSION_STATES.RECOVERING],
94
+ ["resuming", SESSION_STATES.RECOVERING],
95
+ ["reattaching", SESSION_STATES.RECOVERING],
96
+ ["cancelled", SESSION_STATES.STOPPED],
97
+ ["canceled", SESSION_STATES.STOPPED],
98
+ ["cancelling", SESSION_STATES.CANCELLING],
99
+ ["stopping", SESSION_STATES.CANCELLING],
100
+ ["crashed", SESSION_STATES.FAILED],
101
+ ["error", SESSION_STATES.FAILED],
102
+ ]);
103
+
104
+ /**
105
+ * Normalize any producer's state/status/phase token to a canonical unified
106
+ * state, or null when absent / unrecognized. Case- and whitespace-tolerant;
107
+ * never throws.
108
+ */
109
+ export function normalizeSessionState(value) {
110
+ if (typeof value !== "string") return null;
111
+ return STATE_ALIASES.get(value.trim().toLowerCase()) || null;
112
+ }
113
+
114
+ /** Legal transition graph. Terminal states have no outgoing edges. */
115
+ const TRANSITIONS = Object.freeze({
116
+ starting: [
117
+ "ready",
118
+ "running",
119
+ "waitingApproval",
120
+ "disconnected",
121
+ "failed",
122
+ "stopped",
123
+ ],
124
+ ready: [
125
+ "running",
126
+ "waitingApproval",
127
+ "cancelling",
128
+ "disconnected",
129
+ "completed",
130
+ "failed",
131
+ "stopped",
132
+ ],
133
+ running: [
134
+ "ready",
135
+ "waitingApproval",
136
+ "cancelling",
137
+ "disconnected",
138
+ "completed",
139
+ "failed",
140
+ "stopped",
141
+ ],
142
+ waitingApproval: [
143
+ "running",
144
+ "ready",
145
+ "cancelling",
146
+ "disconnected",
147
+ "completed",
148
+ "failed",
149
+ "stopped",
150
+ ],
151
+ // a turn may finish (or die) before a cancel lands, so completed/failed are legal.
152
+ cancelling: ["stopped", "failed", "completed", "disconnected"],
153
+ disconnected: ["recovering", "failed", "stopped", "completed"],
154
+ recovering: [
155
+ "ready",
156
+ "running",
157
+ "waitingApproval",
158
+ "disconnected",
159
+ "completed",
160
+ "failed",
161
+ "stopped",
162
+ ],
163
+ completed: [],
164
+ failed: [],
165
+ stopped: [],
166
+ });
167
+
168
+ /** Is `state` a terminal (absorbing) state? */
169
+ export function isTerminalSessionState(state) {
170
+ return TERMINAL_STATES.has(state);
171
+ }
172
+
173
+ /** Is `state` a live (non-terminal, known) state? */
174
+ export function isActiveSessionState(state) {
175
+ return ALL_STATES.has(state) && !TERMINAL_STATES.has(state);
176
+ }
177
+
178
+ /**
179
+ * Is `from → to` a legal transition? A self-transition (from === to) is always
180
+ * allowed (idempotent re-assert of the current state). Unknown states → false.
181
+ */
182
+ export function canTransitionSessionState(from, to) {
183
+ if (!ALL_STATES.has(from) || !ALL_STATES.has(to)) return false;
184
+ if (from === to) return true;
185
+ return TRANSITIONS[from].includes(to);
186
+ }
187
+
188
+ /**
189
+ * States during which a resumed/reconnected session must NOT silently replay a
190
+ * dangerous tool call — it has to re-check the command ledger first (P0#4
191
+ * "危险工具恢复时不得静默重试"). `recovering` is the reattach/replay window;
192
+ * `disconnected` is guarded too because the very next legal step is recovery
193
+ * and a re-dispatch across the gap is exactly the at-most-once hazard.
194
+ */
195
+ export function requiresIdempotencyGuard(state) {
196
+ return (
197
+ state === SESSION_STATES.RECOVERING || state === SESSION_STATES.DISCONNECTED
198
+ );
199
+ }
200
+
201
+ /**
202
+ * Collapse the three divergent axes into ONE canonical unified state.
203
+ *
204
+ * Precedence (strongest first), each documented by WHY it outranks the next:
205
+ * 1. terminal `status` (completed/failed/stopped/lost) — a settled verdict
206
+ * from the supervisor is authoritative and absorbing; nothing un-settles it.
207
+ * 2. `recovering` (connection reconnecting/resuming, or explicit flag) — an
208
+ * active reattach is the state that carries the idempotency guard, so it
209
+ * must surface even mid-cancel.
210
+ * 3. `disconnected` (connection lost) — the transport is gone; we cannot
211
+ * observe cancel/approval progress, so report the disconnect, not a stale
212
+ * inner phase.
213
+ * 4. `cancelling` (cancel requested, not yet settled) — outranks approval /
214
+ * running because a pending cancel is what the operator is waiting on.
215
+ * 5. `waitingApproval` (pendingApprovals > 0 OR a blocking worker phase) — a
216
+ * human decision blocks progress; mirrors phaseGroupKey's approval-wins rule.
217
+ * 6. worker phase: working/turn → running, idle → ready, starting → starting.
218
+ * 7. fallback: a running session with an unknown/absent phase is `running`
219
+ * (never idle) — the safe default, matching phaseGroupKey.
220
+ *
221
+ * @param {object} input
222
+ * @param {string} [input.status] supervisor liveness status
223
+ * @param {string} [input.phase] worker sub-phase
224
+ * @param {string} [input.connection] IDE transport state
225
+ * @param {boolean} [input.cancelRequested] a cancel is in flight
226
+ * @param {boolean} [input.recovering] an explicit reattach/replay flag
227
+ * @param {number} [input.pendingApprovals] count of blocking approvals
228
+ * @returns {string} a canonical SESSION_STATES value
229
+ */
230
+ export function deriveSessionState(input) {
231
+ const s = input && typeof input === "object" ? input : {};
232
+ const status = normalizeSessionState(s.status);
233
+ const connection = normalizeSessionState(s.connection);
234
+ const phase = normalizeSessionState(s.phase);
235
+
236
+ // 1. terminal verdict absorbs everything.
237
+ if (status && TERMINAL_STATES.has(status)) return status;
238
+
239
+ // 2. active recovery (explicit flag or a reconnecting/resuming transport).
240
+ if (s.recovering === true || connection === SESSION_STATES.RECOVERING) {
241
+ return SESSION_STATES.RECOVERING;
242
+ }
243
+
244
+ // 3. transport gone.
245
+ if (connection === SESSION_STATES.DISCONNECTED)
246
+ return SESSION_STATES.DISCONNECTED;
247
+
248
+ // 4. cancel in flight (flag, or a producer that reported a cancelling state).
249
+ if (
250
+ s.cancelRequested === true ||
251
+ phase === SESSION_STATES.CANCELLING ||
252
+ connection === SESSION_STATES.CANCELLING
253
+ ) {
254
+ return SESSION_STATES.CANCELLING;
255
+ }
256
+
257
+ // 5. blocked on a human decision.
258
+ if (
259
+ Number(s.pendingApprovals) > 0 ||
260
+ phase === SESSION_STATES.WAITING_APPROVAL
261
+ ) {
262
+ return SESSION_STATES.WAITING_APPROVAL;
263
+ }
264
+
265
+ // 6. steady sub-states from the worker phase.
266
+ if (phase === SESSION_STATES.RUNNING) return SESSION_STATES.RUNNING;
267
+ if (phase === SESSION_STATES.READY) return SESSION_STATES.READY;
268
+ if (phase === SESSION_STATES.STARTING) return SESSION_STATES.STARTING;
269
+ if (connection === SESSION_STATES.STARTING) return SESSION_STATES.STARTING;
270
+ if (connection === SESSION_STATES.READY) return SESSION_STATES.READY;
271
+
272
+ // 7. a running session with no legible phase is running, never idle.
273
+ if (status === SESSION_STATES.RUNNING) return SESSION_STATES.RUNNING;
274
+
275
+ // nothing legible at all → the session is starting up.
276
+ return SESSION_STATES.STARTING;
277
+ }
278
+
279
+ /** Human-readable one-liner for a canonical state (dashboards / logs). */
280
+ export function describeSessionState(state) {
281
+ switch (state) {
282
+ case SESSION_STATES.STARTING:
283
+ return "starting: process spawned, first turn not begun";
284
+ case SESSION_STATES.READY:
285
+ return "ready: transport open, parked between turns";
286
+ case SESSION_STATES.RUNNING:
287
+ return "running: a turn is executing";
288
+ case SESSION_STATES.WAITING_APPROVAL:
289
+ return "waiting for approval: a human decision blocks progress";
290
+ case SESSION_STATES.CANCELLING:
291
+ return "cancelling: a cancel is in flight, not yet settled";
292
+ case SESSION_STATES.DISCONNECTED:
293
+ return "disconnected: transport lost, session may still be alive";
294
+ case SESSION_STATES.RECOVERING:
295
+ return "recovering: reattaching; dangerous tools must re-check the ledger";
296
+ case SESSION_STATES.COMPLETED:
297
+ return "completed: finished successfully (terminal)";
298
+ case SESSION_STATES.FAILED:
299
+ return "failed: ended in error or was lost (terminal)";
300
+ case SESSION_STATES.STOPPED:
301
+ return "stopped: cancelled by the user (terminal)";
302
+ default:
303
+ return "unknown session state";
304
+ }
305
+ }
@@ -18,6 +18,37 @@
18
18
 
19
19
  const { collectHooks } = require("./settings-hooks.cjs");
20
20
  const { runHooks } = require("./hook-runner.cjs");
21
+ const { buildHookEnvelope } = require("./hook-event-bus.cjs");
22
+ const hookEventLog = require("./hook-event-log.cjs");
23
+
24
+ /**
25
+ * Stamp a unified-bus delivery id (event_id) onto a hook payload (P2 event bus).
26
+ * Additive — a hook that ignores it is unaffected; a hook that reads it gets a
27
+ * stable per-delivery id for correlation / `cc hook replay`. traceId/parentId
28
+ * are threaded when the caller provides them (else null).
29
+ *
30
+ * When `CC_HOOK_EVENT_LOG` is enabled, the FULL envelope is best-effort recorded
31
+ * to the hash-chained hook event log so `cc hook replay <event-id>` has a real
32
+ * source. Recording is opt-in and never throws, so the default firing path is
33
+ * byte-for-byte unchanged.
34
+ */
35
+ function withDeliveryId(event, payload, { sessionId, traceId, parentId } = {}) {
36
+ const env = buildHookEnvelope({
37
+ eventType: event,
38
+ data: payload,
39
+ sessionId: sessionId || payload.session_id || null,
40
+ traceId: traceId || null,
41
+ parentId: parentId || null,
42
+ });
43
+ if (hookEventLog.isHookEventLogEnabled()) {
44
+ try {
45
+ hookEventLog.appendHookEvent(env);
46
+ } catch {
47
+ /* best-effort — recording must not break the turn */
48
+ }
49
+ }
50
+ return { ...payload, event_id: env.event_id };
51
+ }
21
52
 
22
53
  /**
23
54
  * Split collected hooks into the blocking (sync) set and the fire-and-forget
@@ -70,12 +101,16 @@ function runUserPromptSubmitHooks(
70
101
  if (matched.length === 0) return { blocked: false, additionalContext: null };
71
102
  const { sync } = partitionAsyncHooks(matched);
72
103
  if (sync.length === 0) return { blocked: false, additionalContext: null };
73
- const payload = {
74
- hook_event_name: "UserPromptSubmit",
75
- prompt: String(prompt || ""),
76
- cwd,
77
- session_id: sessionId || null,
78
- };
104
+ const payload = withDeliveryId(
105
+ "UserPromptSubmit",
106
+ {
107
+ hook_event_name: "UserPromptSubmit",
108
+ prompt: String(prompt || ""),
109
+ cwd,
110
+ session_id: sessionId || null,
111
+ },
112
+ { sessionId },
113
+ );
79
114
  const outcome = runHooks(sync, payload, {
80
115
  cwd,
81
116
  event: "UserPromptSubmit",
@@ -129,12 +164,16 @@ function runSessionStartHooks(settingsHooks, { source, cwd, sessionId } = {}) {
129
164
  if (!settingsHooks) return { additionalContext: null };
130
165
  const matched = collectHooks(settingsHooks, "SessionStart", source || "");
131
166
  if (matched.length === 0) return { additionalContext: null };
132
- const payload = {
133
- hook_event_name: "SessionStart",
134
- source: source || "startup",
135
- cwd,
136
- session_id: sessionId || null,
137
- };
167
+ const payload = withDeliveryId(
168
+ "SessionStart",
169
+ {
170
+ hook_event_name: "SessionStart",
171
+ source: source || "startup",
172
+ cwd,
173
+ session_id: sessionId || null,
174
+ },
175
+ { sessionId },
176
+ );
138
177
  const outcome = runHooks(matched, payload, { cwd, event: "SessionStart" });
139
178
  return { additionalContext: aggregateContext(outcome.results) };
140
179
  }
@@ -159,7 +198,7 @@ function runObserveHooks(
159
198
  if (sync.length === 0) return { decision: "continue", results: [] };
160
199
  return runHooks(
161
200
  sync,
162
- { hook_event_name: event, cwd, ...payload },
201
+ withDeliveryId(event, { hook_event_name: event, cwd, ...payload }),
163
202
  { cwd, event },
164
203
  );
165
204
  }
@@ -415,11 +415,53 @@ function readBooleanSetting(key, opts = {}) {
415
415
  return value;
416
416
  }
417
417
 
418
+ /**
419
+ * Read a string-array setting across the layered `.claude/settings.json` files
420
+ * plus managed settings. Unlike {@link readBooleanSetting} (last-layer wins),
421
+ * arrays UNION across layers (mirroring how `env`/`sandbox` accumulate in
422
+ * {@link loadSettingsConfig}) — a user-scope and a project-scope layer can each
423
+ * contribute entries. Non-array values and non-string members are ignored.
424
+ * Returns a de-duplicated array, or `undefined` when no layer sets it.
425
+ *
426
+ * @param {string} key settings key or dotted path (e.g. "instructionExcludes")
427
+ * @param {object} [opts]
428
+ * @param {string} [opts.cwd=process.cwd()]
429
+ * @param {string} [opts.settingsFile]
430
+ * @param {(msg:string)=>void} [opts.onWarn]
431
+ * @returns {string[]|undefined}
432
+ */
433
+ function readStringArraySetting(key, opts = {}) {
434
+ const cwd = opts.cwd || process.cwd();
435
+ const parts = String(key).split(".");
436
+ const collected = [];
437
+ let sawAny = false;
438
+ const absorb = (data) => {
439
+ let node = data;
440
+ for (const p of parts) {
441
+ node = node && typeof node === "object" ? node[p] : undefined;
442
+ }
443
+ if (Array.isArray(node)) {
444
+ sawAny = true;
445
+ for (const item of node) {
446
+ if (typeof item === "string" && item.trim()) collected.push(item);
447
+ }
448
+ }
449
+ };
450
+ for (const file of settingsPaths(cwd, opts.settingsFile)) {
451
+ absorb(readSettingsFile(file, { onWarn: opts.onWarn }));
452
+ }
453
+ const managed = loadManagedSettings(opts);
454
+ if (managed.settings) absorb(managed.settings);
455
+ if (!sawAny) return undefined;
456
+ return [...new Set(collected)];
457
+ }
458
+
418
459
  module.exports = {
419
460
  loadSettings,
420
461
  loadSettingsConfig,
421
462
  readSettingsFile,
422
463
  readBooleanSetting,
464
+ readStringArraySetting,
423
465
  settingsPaths,
424
466
  managedSettingsPath,
425
467
  loadManagedSettings,
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Persist the side-effect ledger ([[side-effect-ledger.js]]) as a tamper-evident
3
+ * chained session event so a worker that crashed mid `git push` / `file-write` /
4
+ * `package-install` can rebuild its ledger on recovery and reconcile WITHOUT
5
+ * repeating a side-effect it already issued.
6
+ *
7
+ * Same shape as [[turn-binding-store.js]]: each persist writes the FULL ledger
8
+ * snapshot (newest wins), all I/O is delegated to the canonical session store
9
+ * via `_deps` so the module stays pure-testable.
10
+ */
11
+
12
+ import {
13
+ appendEvent as storeAppendEvent,
14
+ readEvents as storeReadEvents,
15
+ } from "../harness/jsonl-session-store.js";
16
+ import {
17
+ SideEffectLedger,
18
+ reconcileSideEffects,
19
+ } from "./side-effect-ledger.js";
20
+
21
+ /** Event type carrying a full SideEffectLedger snapshot. */
22
+ export const SIDE_EFFECT_LEDGER_EVENT = "side_effect_ledger";
23
+
24
+ export const _deps = {
25
+ appendEvent: storeAppendEvent,
26
+ readEvents: storeReadEvents,
27
+ };
28
+
29
+ /**
30
+ * Append the current ledger as a chained event. Best-effort — a persistence
31
+ * failure must not crash the operation it is guarding.
32
+ *
33
+ * @param {string} sessionId
34
+ * @param {SideEffectLedger|object} ledger
35
+ * @returns {boolean}
36
+ */
37
+ export function persistSideEffectLedger(sessionId, ledger) {
38
+ if (!sessionId || !ledger) return false;
39
+ const snapshot =
40
+ ledger instanceof SideEffectLedger ? ledger.toJSON() : ledger;
41
+ if (!snapshot || !Array.isArray(snapshot.ops)) return false;
42
+ try {
43
+ _deps.appendEvent(sessionId, SIDE_EFFECT_LEDGER_EVENT, snapshot);
44
+ return true;
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Rebuild the ledger from the LATEST persisted snapshot, or an empty ledger when
52
+ * the session never persisted one (or on read error).
53
+ *
54
+ * @param {string} sessionId
55
+ * @param {{clock?:Function}} [opts]
56
+ * @returns {SideEffectLedger}
57
+ */
58
+ export function loadSideEffectLedger(sessionId, opts = {}) {
59
+ let events = [];
60
+ try {
61
+ events = _deps.readEvents(sessionId) || [];
62
+ } catch {
63
+ return new SideEffectLedger(opts);
64
+ }
65
+ for (let i = events.length - 1; i >= 0; i--) {
66
+ const e = events[i];
67
+ if (
68
+ e &&
69
+ e.type === SIDE_EFFECT_LEDGER_EVENT &&
70
+ e.data &&
71
+ Array.isArray(e.data.ops)
72
+ ) {
73
+ return SideEffectLedger.fromJSON(e.data, opts);
74
+ }
75
+ }
76
+ return new SideEffectLedger(opts);
77
+ }
78
+
79
+ /**
80
+ * Load + reconcile in one step: the recovery entry point. Returns the plan a
81
+ * restarting worker uses to decide, per op, redo / inspect / skip.
82
+ *
83
+ * @param {string} sessionId
84
+ * @returns {{plans:Array, redo:string[], inspect:string[], skip:string[]}}
85
+ */
86
+ export function reconcileSessionSideEffects(sessionId) {
87
+ return reconcileSideEffects(loadSideEffectLedger(sessionId));
88
+ }