chainlesschain 0.162.161 → 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 (178) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-DMn528q-.js → AIOps-ClI2ALgv.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-D9ZcDCJL.js → ActionButton-BDjRcJaK.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-A_K-8PX0.js → Analytics-DikSRsaT.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-D4pFW3X6.js → AppLayout-9mLhMFmz.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-Dz46COAd.js → Artifacts-BSr88BrH.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-cbMjbyg0.js → Audit-CdvyimOF.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-Dtf88GAj.js → BackgroundAgents-D3_hCedh.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-C5KRxn52.js → Backup-Cyvv9RHf.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-Dd2UK3BN.js → BaseInput-BZM14kt8.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-lptKTFdG.js → Chat-DrQSokVz.js} +6 -6
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-C2KLtwHz.js → ChatBubbleRenderer-HwJR-C6B.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-_yspG6rG.js → Checkbox-bFLYeWMV.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-BGq_GAha.js → Codegen-7mN7xJXT.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-CePor71g.js → Col-DAB5ZWm7.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-CLOWhIT7.js → Community-B_1TymaK.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-CivJt2vT.js → Compact-Bq1uSWLJ.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-Btmo1GGE.js → Compliance-CyiE1jEe.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-Ct35OlG4.js → Cowork-Cpk0D7vm.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-CTc2GmhB.js → Cron-d_Lv3rdB.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-ZtiBETk1.js → Crosschain-DMqhHjMR.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-BNqMQQXw.js → DID-Br-oPuBw.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-BmzZd-9V.js → Dashboard-CfLW_RaP.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-CMVgvP2M.js → Dropdown-DZnTOExV.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-CcqQFAfT.js → EmailListRenderer-DHIUHDv1.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CTqB1_4V.js → FamilyGuardDashboard-Bw3t3myu.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-CiKOOzlb.js → Federation-D9cpQPAY.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-Wk-OT0gr.js → FormItemContext-Jdczr9Mr.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-DbEQT6ue.js → GenericCardRenderer-BT8ZMpC5.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-0ZvCPSD9.js → Git-DjARip2b.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-CmVuKkPb.js → Governance-BgWbSRcj.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-CTWE3Jwg.js → Inference-CfouJciM.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-CBEIF00L.js → KnowledgeGraph-50l7mrAr.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-DLhxDwVG.js → Logs-rq50H84B.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-DUPIEdQH.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-BgsCBrnc.js → Marketplace-BYgPDmHU.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-ClBUGDS2.js → McpTools-5DVgcFdx.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-CnG_ZnYw.js → Memory-B70LPk7f.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-BUOAbR1Z.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-CLjt4kCD.js → Mtc-RJsOTsIA.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-f24U9Aes.js → MtcAudit-Crh-6BEK.js} +5 -5
  43. package/src/assets/web-panel/assets/{Multisig-GrmGQbSq.js → Multisig-wuoR5l3x.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-DADaWn-n.js → NLProgramming-U2Mji4fZ.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-xtf5RX6C.js → Notes-CdTZAyDH.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-Cd27Rgtx.js → NotificationSettings-C-FucWE7.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-fXRxlDjl.js → OrderTableRenderer-DCYFTeaI.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-joCFlNB0.js → Organization-an3OF85L.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-B1HoURq0.js → Overflow-C-1vmYFe.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-DiwsKbT7.js → P2P-Cwh47xKg.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-Xe-BsW6S.js → PdhVaultBrowser-Dm38GT0k.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-C23qi8kl.js → Permissions-DS2Y8awD.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-BS1syX2f.js → PersonalDataHub-BQNziA0I.js} +3 -3
  54. package/src/assets/web-panel/assets/{Pipeline-DRYwwDyh.js → Pipeline-DF85L1ue.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-BQKaDHPH.js → Privacy-BuLornF-.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-BC6F9xiG.js → ProjectInit-C3Y31IwX.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-ClFPc1Nd.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-CLWuOUNt.js → Providers-D3Wp022X.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-DiqULTxb.js → QrScannerModal-CUJfQkH4.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-9A03P-E_.js → QuickAsk-UAc0EO8l.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-CJZAnbq_.js → Recommend-BaxKupcs.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-C9A1R77h.js → RemoteSession-C75NNnXZ.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-BIZc6Rky.js → Reputation-BVXynZAk.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-DfIPNtuG.js → Row-C5MRZ0bi.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-C5znFcQ1.js → RssFeed-DAKkEYka.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-DSs2z3OX.js → Search-DvWv9YOA.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-CV6LcrLY.js → Security-B5peafW1.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-cmhoX0Xj.js → Services-UQmvPEaG.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-tCsg2po7.js → Skeleton-C3bhWpuF.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-BK1KUDiy.js → Skills-DfkKf9_H.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-yRgyeA82.js → Sla-DqMSjlBM.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-c1mo1mlp.js → SpeechSettings-C-WbxWUX.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-koIAfuT3.js → SyncSettings-CYuHZ4N2.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-DkkreAaB.js → Tasks-CyeAyB9d.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-fy5Ctf9M.js → Templates-BTCeQnpq.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-Bw70Jf6S.js → Tenant-BifQuA9Y.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-BKh6Rx-4.js → Terminal-Bx_9Bocf.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-Dg54WP0c.js → TimelineRenderer-BX0RFCOm.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-BvvD5SxJ.js → Tokens-CGHrCmUS.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-B7Ug9x4e.js → Trigger-DlqaY4SO.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-BjvuMORY.js → Trust-DfOmj-ay.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-CZqhkx8p.js → UkeySign-B72jH5fE.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-ClR50lhX.js → VideoEditing-BT-63fqv.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-DlfppYjx.js → Wallet-Dsul3sHr.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-BirgVr8B.js → WebAuthn-Izkwwdgy.js} +2 -2
  87. package/src/assets/web-panel/assets/{WorkflowEditor-B8DP_bax.js → WorkflowEditor-D3FHSiH1.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-DA8AUZzL.js → chat-CmVz5z3U.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-C38pLGp1.js → colors-CuLgyJBp.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-81rtr98o.js → compact-item-Ml1YkXpw.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-D34ixq8t.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-D4bzqTrZ.js → hasIn-DVC0157C.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-D_2VYPr1.js → index-4SBta3d7.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CoVgZw_b.js → index-4wcgvPoF.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-ZRJRItm0.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-CuS-UPoj.js → index-BUt8kTIB.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BtGxuQrO.js → index-B_EvG_GW.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-B0QArPQt.js → index-BbPtzB8M.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Cd3KuNK5.js → index-BbepULSc.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CbhE3j5j.js → index-BfuKP4qe.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BXd2_LmH.js → index-BiC6dQZG.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CP5alL4t.js → index-BjHBpRYZ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BvL_3J_n.js → index-BpFJHiTv.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DFyWKOBD.js → index-BydjdZnl.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-Ddk0e_Pi.js → index-BzG-x9p4.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BS3M5H9V.js → index-Bzr0rB5T.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DkHqMnn3.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-D6GfvEQG.js → index-CFYVQMvu.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BqIhLAbA.js → index-CXkJH-lK.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-B8mkLM4x.js → index-C_H_S-DY.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-D0Ih7Hwn.js → index-C_XuRgGn.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-COe_cOLJ.js → index-C_vRgTl9.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BIP62a6r.js → index-Ck_52dz_.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BP-zkXK8.js → index-CmFNvNLZ.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-D3zCCZRS.js → index-CudmwNBV.js} +3 -3
  119. package/src/assets/web-panel/assets/{index-DFHa1HIg.js → index-Cw0Rjy87.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BvD7b2IK.js → index-D3lD5qap.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DakfFZ-k.js → index-DC_OI9H2.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DvfCEJG-.js → index-DDGaOWBV.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-E1OJRXs4.js → index-DJyN28Et.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-Dmo3dFHb.js → index-DYXHFr4r.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-tKiC8T-4.js → index-Dt5KMjwJ.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-fI5sXMLg.js → index-DuGszaWY.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DUQLXLQM.js → index-DukDVXCv.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DMgnRwrK.js → index-KMqhcF7l.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-F76w1NiL.js → index-KjgKdZe-.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DBFSo1NF.js → index-QXzlT9KD.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BlxEphl7.js → index-vG4XSJ1d.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-9qoVpPt6.js → index-wvb_FtaV.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-ByyvflIR.js → initDefaultProps-BDgd2YZ8.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-B3FQm2By.js → motion-ErtrXdYe.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-BSfns6Ef.js → move-_GPPNCgs.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-CTG1Ieri.js → mtc-parser-DRLL-8V2.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-DObM_2j6.js → omit-p86sl023.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-DOfelQ1t.js → pickAttrs-BTu7cMLH.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-CHZILds_.js → placementArrow-BEKz6Isr.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-REbucKSx.js → responsiveObserve-Ck42c9MP.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-C6EZzD7o.js → slide-1CEvuPsB.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-BxkWS-g3.js → statusUtils-BDfdx1GJ.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-Chklyoq6.js → styleChecker-B4K46m5w.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-CRVwKrrI.js → useFlexGapSupport-DoIVpJNq.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DV24ik_J.js → useFs-DdAWi-ln.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-C7vIo4Pg.js → usePersonalDataHub-iUu6k8dx.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-By-v5GlF.js → vnode-DZz4R6Tw.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-B1w9Am3r.js → zoom-DXlf9sA3.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/review.js +6 -1
  151. package/src/data/changelog.json +7 -2
  152. package/src/lib/agents.js +19 -0
  153. package/src/lib/background-agent-supervisor.js +34 -7
  154. package/src/lib/docs-drift.js +166 -0
  155. package/src/lib/doctor-checkup.js +76 -0
  156. package/src/lib/hook-event-bus.cjs +0 -0
  157. package/src/lib/json-schema-output.js +30 -1
  158. package/src/lib/json-schema-validate.js +691 -0
  159. package/src/lib/lsp/__tests__/diagnostics-scheduler.test.js +93 -0
  160. package/src/lib/lsp/diagnostics-scheduler.js +127 -0
  161. package/src/lib/operation-fingerprint.js +95 -0
  162. package/src/lib/plugin-runtime/capabilities.js +371 -0
  163. package/src/lib/plugin-runtime/manifest.js +30 -0
  164. package/src/lib/remote-approval-bridge.js +16 -0
  165. package/src/lib/review-pipeline.js +227 -0
  166. package/src/lib/runtime-checkup.js +318 -0
  167. package/src/lib/session-lifecycle.js +305 -0
  168. package/src/lib/settings-hook-events.cjs +39 -13
  169. package/src/lib/subagent-contract.js +383 -0
  170. package/src/lib/telemetry-ids.js +190 -0
  171. package/src/lib/turn-binding.js +397 -0
  172. package/src/runtime/agent-core.js +13 -6
  173. package/src/assets/web-panel/assets/MarkdownRenderer-DxywvhTU.js +0 -1
  174. package/src/assets/web-panel/assets/MobileProjects-Dnt_9-WB.js +0 -1
  175. package/src/assets/web-panel/assets/Projects-Dn-f5aYe.js +0 -1
  176. package/src/assets/web-panel/assets/devWarning-CHDIEOeN.js +0 -1
  177. package/src/assets/web-panel/assets/index-CY1NURWZ.js +0 -1
  178. 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,24 @@
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
+
23
+ /**
24
+ * Stamp a unified-bus delivery id (event_id) onto a hook payload (P2 event bus).
25
+ * Additive — a hook that ignores it is unaffected; a hook that reads it gets a
26
+ * stable per-delivery id for correlation / `cc hook replay`. traceId/parentId
27
+ * are threaded when the caller provides them (else null).
28
+ */
29
+ function withDeliveryId(event, payload, { sessionId, traceId, parentId } = {}) {
30
+ const env = buildHookEnvelope({
31
+ eventType: event,
32
+ data: payload,
33
+ sessionId: sessionId || payload.session_id || null,
34
+ traceId: traceId || null,
35
+ parentId: parentId || null,
36
+ });
37
+ return { ...payload, event_id: env.event_id };
38
+ }
21
39
 
22
40
  /**
23
41
  * Split collected hooks into the blocking (sync) set and the fire-and-forget
@@ -70,12 +88,16 @@ function runUserPromptSubmitHooks(
70
88
  if (matched.length === 0) return { blocked: false, additionalContext: null };
71
89
  const { sync } = partitionAsyncHooks(matched);
72
90
  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
- };
91
+ const payload = withDeliveryId(
92
+ "UserPromptSubmit",
93
+ {
94
+ hook_event_name: "UserPromptSubmit",
95
+ prompt: String(prompt || ""),
96
+ cwd,
97
+ session_id: sessionId || null,
98
+ },
99
+ { sessionId },
100
+ );
79
101
  const outcome = runHooks(sync, payload, {
80
102
  cwd,
81
103
  event: "UserPromptSubmit",
@@ -129,12 +151,16 @@ function runSessionStartHooks(settingsHooks, { source, cwd, sessionId } = {}) {
129
151
  if (!settingsHooks) return { additionalContext: null };
130
152
  const matched = collectHooks(settingsHooks, "SessionStart", source || "");
131
153
  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
- };
154
+ const payload = withDeliveryId(
155
+ "SessionStart",
156
+ {
157
+ hook_event_name: "SessionStart",
158
+ source: source || "startup",
159
+ cwd,
160
+ session_id: sessionId || null,
161
+ },
162
+ { sessionId },
163
+ );
138
164
  const outcome = runHooks(matched, payload, { cwd, event: "SessionStart" });
139
165
  return { additionalContext: aggregateContext(outcome.results) };
140
166
  }
@@ -159,7 +185,7 @@ function runObserveHooks(
159
185
  if (sync.length === 0) return { decision: "continue", results: [] };
160
186
  return runHooks(
161
187
  sync,
162
- { hook_event_name: event, cwd, ...payload },
188
+ withDeliveryId(event, { hook_event_name: event, cwd, ...payload }),
163
189
  { cwd, event },
164
190
  );
165
191
  }