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,397 @@
1
+ /**
2
+ * Turn binding table — explicit `turn → checkpoint` binding + restore planning
3
+ * (P1 "显式绑定 Turn、Checkpoint 和恢复").
4
+ *
5
+ * Today the rewind path binds a turn to its file checkpoint IMPLICITLY: a flat
6
+ * `{atMessageCount, id, tool}` marks array plus the heuristic
7
+ * `pickCheckpointForTurn` in [[repl-rewind.js]]. This module upgrades that to
8
+ * the EXPLICIT table the gap analysis calls for — one record per turn:
9
+ *
10
+ * turnId
11
+ * -> conversationOffset (message index to truncate back to)
12
+ * -> fileCheckpointId (checkpoint-store cp id; see checkpoint-store.js)
13
+ * -> toolCallIds[] (tool_use_ids this turn issued)
14
+ * -> permissionDecisionIds[] (approval decisions made this turn)
15
+ * -> childAgentIds[] (sub-agents spawned this turn)
16
+ * -> worktreeId (isolation worktree, if any)
17
+ * -> coverage: full | partial | none
18
+ *
19
+ * Everything here is PURE (no timers, no I/O, no git): a caller feeds it events
20
+ * from the agent loop (checkpoint, tool-executing, permission decision,
21
+ * spawn_sub_agent, worktree create) and reads back a table + a restore plan.
22
+ *
23
+ * `coverage` is computed HONESTLY — a turn that ran a shell command, spawned an
24
+ * external process, or saw a user edit can never be `full`, because those
25
+ * effects live outside the file tree a checkpoint captures and cannot be
26
+ * promised back. This is the "不能承诺完全恢复" invariant from the gap doc.
27
+ */
28
+
29
+ export const TURN_COVERAGE = Object.freeze({
30
+ FULL: "full",
31
+ PARTIAL: "partial",
32
+ NONE: "none",
33
+ });
34
+
35
+ export const RESTORE_SCOPE = Object.freeze({
36
+ CONVERSATION: "conversation", // conversation only
37
+ FILES: "files", // files only
38
+ BOTH: "both", // conversation and files
39
+ });
40
+
41
+ /**
42
+ * Classify a tool by how restorable its effects are:
43
+ * - "shell" external side-effects (shell / run_code / spawn) — irreversible.
44
+ * - "edit" in-tree file mutation — reversible via a file checkpoint.
45
+ * - "other" read-only / inert.
46
+ * Substring heuristics keep this robust across tool-name spellings.
47
+ */
48
+ export function classifyToolKind(name) {
49
+ const n = String(name || "").toLowerCase();
50
+ if (/shell|bash|(^|_)exec|run_code|spawn|process|subprocess/.test(n)) {
51
+ return "shell";
52
+ }
53
+ if (
54
+ /write|edit|patch|replace|create_file|delete_file|apply|mkdir|move_file/.test(
55
+ n,
56
+ )
57
+ ) {
58
+ return "edit";
59
+ }
60
+ return "other";
61
+ }
62
+
63
+ /**
64
+ * Deterministic coverage from a turn's side-effect flags.
65
+ * side-effects (shell / external process / user edit) → PARTIAL (can't promise)
66
+ * no file mutation → FULL (nothing to undo)
67
+ * mutation WITH a checkpoint → FULL (fully restorable)
68
+ * mutation WITHOUT a checkpoint → NONE (files unrecoverable)
69
+ */
70
+ export function computeCoverage(flags = {}) {
71
+ const sideEffects = Boolean(
72
+ flags.ranShell ||
73
+ flags.spawnedExternalProcess ||
74
+ flags.userEditedDuringTurn,
75
+ );
76
+ if (sideEffects) return TURN_COVERAGE.PARTIAL;
77
+ if (!flags.mutatedFiles) return TURN_COVERAGE.FULL;
78
+ if (flags.hasFileCheckpoint) return TURN_COVERAGE.FULL;
79
+ return TURN_COVERAGE.NONE;
80
+ }
81
+
82
+ function emptyRecord(turnId, conversationOffset) {
83
+ return {
84
+ turnId,
85
+ conversationOffset:
86
+ Number.isFinite(Number(conversationOffset)) && conversationOffset != null
87
+ ? Number(conversationOffset)
88
+ : null,
89
+ fileCheckpointId: null,
90
+ toolCallIds: [],
91
+ permissionDecisionIds: [],
92
+ childAgentIds: [],
93
+ worktreeId: null,
94
+ // side-effect flags → coverage (kept internal; surfaced via `coverage`)
95
+ _flags: {
96
+ ranShell: false,
97
+ spawnedExternalProcess: false,
98
+ userEditedDuringTurn: false,
99
+ mutatedFiles: false,
100
+ hasFileCheckpoint: false,
101
+ },
102
+ };
103
+ }
104
+
105
+ function pushUnique(list, value) {
106
+ const v = value == null ? null : String(value);
107
+ if (v == null || list.includes(v)) return;
108
+ list.push(v);
109
+ }
110
+
111
+ /**
112
+ * The explicit turn-binding table. In-memory + serializable (toJSON/fromJSON)
113
+ * so a caller can persist it alongside the session record — no I/O here.
114
+ */
115
+ export class TurnBindingLog {
116
+ constructor() {
117
+ this._turns = new Map();
118
+ this._order = [];
119
+ }
120
+
121
+ /** Begin (or re-open) a turn record. Idempotent on turnId. */
122
+ startTurn(turnId, { conversationOffset } = {}) {
123
+ const id = String(turnId);
124
+ if (!this._turns.has(id)) {
125
+ this._turns.set(id, emptyRecord(id, conversationOffset));
126
+ this._order.push(id);
127
+ } else if (conversationOffset != null) {
128
+ const rec = this._turns.get(id);
129
+ if (rec.conversationOffset == null) {
130
+ rec.conversationOffset = Number(conversationOffset);
131
+ }
132
+ }
133
+ return this;
134
+ }
135
+
136
+ _rec(turnId) {
137
+ const id = String(turnId);
138
+ if (!this._turns.has(id)) this.startTurn(id);
139
+ return this._turns.get(id);
140
+ }
141
+
142
+ /** Record a tool call; `kind` (or the tool `name`) drives coverage. */
143
+ recordToolCall(turnId, toolCallId, { kind, name } = {}) {
144
+ const rec = this._rec(turnId);
145
+ pushUnique(rec.toolCallIds, toolCallId);
146
+ const k = kind || classifyToolKind(name);
147
+ if (k === "shell") rec._flags.ranShell = true;
148
+ else if (k === "external-process") rec._flags.spawnedExternalProcess = true;
149
+ else if (k === "edit") rec._flags.mutatedFiles = true;
150
+ return this;
151
+ }
152
+
153
+ recordPermissionDecision(turnId, decisionId) {
154
+ pushUnique(this._rec(turnId).permissionDecisionIds, decisionId);
155
+ return this;
156
+ }
157
+
158
+ recordChildAgent(turnId, childAgentId) {
159
+ pushUnique(this._rec(turnId).childAgentIds, childAgentId);
160
+ return this;
161
+ }
162
+
163
+ /** Bind (or re-bind to the earliest) file checkpoint for this turn. */
164
+ bindCheckpoint(turnId, checkpointId) {
165
+ const rec = this._rec(turnId);
166
+ if (checkpointId != null) {
167
+ if (rec.fileCheckpointId == null)
168
+ rec.fileCheckpointId = String(checkpointId);
169
+ rec._flags.hasFileCheckpoint = true;
170
+ }
171
+ return this;
172
+ }
173
+
174
+ setWorktree(turnId, worktreeId) {
175
+ this._rec(turnId).worktreeId =
176
+ worktreeId == null ? null : String(worktreeId);
177
+ return this;
178
+ }
179
+
180
+ /** Flag an out-of-band user edit during this turn → forces PARTIAL coverage. */
181
+ markUserEdit(turnId) {
182
+ this._rec(turnId)._flags.userEditedDuringTurn = true;
183
+ return this;
184
+ }
185
+
186
+ /** One turn record with its computed coverage (or null). */
187
+ get(turnId) {
188
+ const rec = this._turns.get(String(turnId));
189
+ return rec ? this._view(rec) : null;
190
+ }
191
+
192
+ /** All turns in insertion order, each with computed coverage. */
193
+ list() {
194
+ return this._order.map((id) => this._view(this._turns.get(id)));
195
+ }
196
+
197
+ _view(rec) {
198
+ return {
199
+ turnId: rec.turnId,
200
+ conversationOffset: rec.conversationOffset,
201
+ fileCheckpointId: rec.fileCheckpointId,
202
+ toolCallIds: [...rec.toolCallIds],
203
+ permissionDecisionIds: [...rec.permissionDecisionIds],
204
+ childAgentIds: [...rec.childAgentIds],
205
+ worktreeId: rec.worktreeId,
206
+ coverage: computeCoverage(rec._flags),
207
+ };
208
+ }
209
+
210
+ toJSON() {
211
+ return {
212
+ turns: this._order.map((id) => {
213
+ const r = this._turns.get(id);
214
+ return { ...r, _flags: { ...r._flags } };
215
+ }),
216
+ };
217
+ }
218
+
219
+ static fromJSON(json) {
220
+ const log = new TurnBindingLog();
221
+ for (const r of json?.turns || []) {
222
+ if (!r || r.turnId == null) continue;
223
+ const id = String(r.turnId);
224
+ log._turns.set(id, {
225
+ ...emptyRecord(id, r.conversationOffset),
226
+ fileCheckpointId: r.fileCheckpointId ?? null,
227
+ toolCallIds: Array.isArray(r.toolCallIds) ? [...r.toolCallIds] : [],
228
+ permissionDecisionIds: Array.isArray(r.permissionDecisionIds)
229
+ ? [...r.permissionDecisionIds]
230
+ : [],
231
+ childAgentIds: Array.isArray(r.childAgentIds)
232
+ ? [...r.childAgentIds]
233
+ : [],
234
+ worktreeId: r.worktreeId ?? null,
235
+ _flags: { ...emptyRecord(id)._flags, ...(r._flags || {}) },
236
+ });
237
+ log._order.push(id);
238
+ }
239
+ return log;
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Plan a restore of one turn under a chosen scope, with HONEST warnings — the
245
+ * restore UI ("Conversation only / Files only / Both") consumes this. It never
246
+ * over-promises: a `partial`/`none` coverage turn, or a scope that would leave
247
+ * files and conversation out of sync, surfaces a warning instead of silently
248
+ * doing a lossy restore.
249
+ *
250
+ * @param {object} turn a record from TurnBindingLog.get()/list()
251
+ * @param {string} scope one of RESTORE_SCOPE
252
+ * @returns {{scope, coverage, conversation:{truncateTo:number}|null,
253
+ * files:{rewindTo:string}|null, warnings:string[]}}
254
+ */
255
+ export function resolveRestorePlan(turn, scope = RESTORE_SCOPE.BOTH) {
256
+ const warnings = [];
257
+ const s = Object.values(RESTORE_SCOPE).includes(scope)
258
+ ? scope
259
+ : RESTORE_SCOPE.BOTH;
260
+ if (!turn) {
261
+ return {
262
+ scope: s,
263
+ coverage: TURN_COVERAGE.NONE,
264
+ conversation: null,
265
+ files: null,
266
+ warnings: ["unknown turn — nothing to restore"],
267
+ };
268
+ }
269
+ const wantConversation =
270
+ s === RESTORE_SCOPE.CONVERSATION || s === RESTORE_SCOPE.BOTH;
271
+ const wantFiles = s === RESTORE_SCOPE.FILES || s === RESTORE_SCOPE.BOTH;
272
+
273
+ let conversation = null;
274
+ if (wantConversation) {
275
+ if (turn.conversationOffset == null) {
276
+ warnings.push(
277
+ "no conversation offset recorded — cannot rewind the conversation",
278
+ );
279
+ } else {
280
+ conversation = { truncateTo: turn.conversationOffset };
281
+ }
282
+ }
283
+
284
+ let files = null;
285
+ if (wantFiles) {
286
+ if (!turn.fileCheckpointId) {
287
+ warnings.push(
288
+ "no file checkpoint for this turn — files cannot be restored",
289
+ );
290
+ } else {
291
+ files = { rewindTo: turn.fileCheckpointId };
292
+ if (turn.coverage === TURN_COVERAGE.PARTIAL) {
293
+ warnings.push(
294
+ "irreversible side-effects (shell / external process / user edits) can't be undone — file restore is best-effort",
295
+ );
296
+ }
297
+ }
298
+ }
299
+
300
+ // Cross-scope drift: restoring one side but not the other leaves them out of
301
+ // sync when this turn touched files.
302
+ const mutatedFiles =
303
+ turn.coverage !== TURN_COVERAGE.FULL || Boolean(turn.fileCheckpointId);
304
+ if (s === RESTORE_SCOPE.CONVERSATION && turn.fileCheckpointId) {
305
+ warnings.push(
306
+ "conversation-only: files will NOT be reverted — the working tree stays ahead of the conversation",
307
+ );
308
+ } else if (
309
+ s === RESTORE_SCOPE.FILES &&
310
+ turn.conversationOffset != null &&
311
+ mutatedFiles
312
+ ) {
313
+ warnings.push(
314
+ "files-only: the conversation is NOT rewound — it will reference a working tree that no longer matches",
315
+ );
316
+ }
317
+
318
+ return { scope: s, coverage: turn.coverage, conversation, files, warnings };
319
+ }
320
+
321
+ /**
322
+ * Select an inclusive turn range for "Summarize from here / up to here". Pure
323
+ * selection over the log's ordered turns; returns the slice plus the
324
+ * conversation offsets that bound it (nulls preserved when a turn lacks one).
325
+ *
326
+ * @returns {{turns:Array, fromOffset:number|null, toOffset:number|null}}
327
+ */
328
+ export function selectTurnRange(
329
+ log,
330
+ { fromTurnId = null, toTurnId = null } = {},
331
+ ) {
332
+ const all =
333
+ log instanceof TurnBindingLog ? log.list() : Array.isArray(log) ? log : [];
334
+ const idxOf = (id) =>
335
+ id == null ? -1 : all.findIndex((t) => t.turnId === String(id));
336
+ let start = fromTurnId == null ? 0 : idxOf(fromTurnId);
337
+ let end = toTurnId == null ? all.length - 1 : idxOf(toTurnId);
338
+ if (start < 0) start = 0;
339
+ if (end < 0) end = all.length - 1;
340
+ if (start > end) [start, end] = [end, start];
341
+ const turns = all.slice(start, end + 1);
342
+ return {
343
+ turns,
344
+ fromOffset: turns.length ? turns[0].conversationOffset : null,
345
+ toOffset: turns.length ? turns[turns.length - 1].conversationOffset : null,
346
+ };
347
+ }
348
+
349
+ /**
350
+ * Bridge: build an explicit TurnBindingLog from the REPL's existing implicit
351
+ * state — `listUserTurns()` output (`{n, index, preview}`, newest-first) and the
352
+ * `{atMessageCount, id, tool}` checkpoint marks. Each mark is attributed to the
353
+ * turn during which it was taken (the turn with the greatest message index still
354
+ * below the mark's `atMessageCount`); the turn's `fileCheckpointId` is its
355
+ * EARLIEST such mark — the pre-mutation snapshot, matching pickCheckpointForTurn.
356
+ *
357
+ * This lets a coverage-aware restore be derived today from what the REPL already
358
+ * tracks, without threading new per-turn events through the agent loop.
359
+ *
360
+ * @param {Array<{n:number,index:number}>} turns
361
+ * @param {Array<{atMessageCount:number,id:string,tool?:string}>} marks
362
+ * @returns {TurnBindingLog}
363
+ */
364
+ export function buildTurnBindingFromMarks(turns = [], marks = []) {
365
+ const log = new TurnBindingLog();
366
+ // Ascending by message index so "owning turn" = greatest index below a mark.
367
+ const ordered = [...(turns || [])]
368
+ .filter((t) => t && Number.isFinite(Number(t.index)))
369
+ .sort((a, b) => Number(a.index) - Number(b.index));
370
+ for (const t of ordered) {
371
+ log.startTurn(`turn-${t.index}`, { conversationOffset: Number(t.index) });
372
+ }
373
+ // Group marks by owning turn, then bind earliest-first so bindCheckpoint keeps
374
+ // the pre-mutation snapshot.
375
+ const owned = new Map(); // turnId -> marks[]
376
+ for (const m of marks || []) {
377
+ const c = Number(m?.atMessageCount);
378
+ if (!Number.isFinite(c) || m?.id == null) continue;
379
+ let owner = null;
380
+ for (const t of ordered) {
381
+ if (Number(t.index) < c) owner = t;
382
+ else break;
383
+ }
384
+ if (!owner) continue;
385
+ const key = `turn-${owner.index}`;
386
+ if (!owned.has(key)) owned.set(key, []);
387
+ owned.get(key).push({ ...m, _c: c });
388
+ }
389
+ for (const [turnId, list] of owned) {
390
+ list.sort((a, b) => a._c - b._c);
391
+ for (const m of list) {
392
+ log.recordToolCall(turnId, m.id, { name: m.tool });
393
+ log.bindCheckpoint(turnId, m.id);
394
+ }
395
+ }
396
+ return log;
397
+ }
@@ -26,6 +26,7 @@ import sharedHookRunner from "../lib/hook-runner.cjs";
26
26
  import sharedHookEvents from "../lib/settings-hook-events.cjs";
27
27
  import { mergeProviderOptions } from "../lib/provider-options.js";
28
28
  import { applyCredentialProxy } from "../lib/credential-proxy.js";
29
+ import { buildTelemetryAttributes } from "../lib/telemetry-ids.js";
29
30
  import { getPlanModeManager } from "../lib/plan-mode.js";
30
31
  import { CLISkillLoader } from "../lib/skill-loader.js";
31
32
  import { executeHooks, HookEvents } from "../lib/hook-manager.js";
@@ -6234,12 +6235,18 @@ export async function* agentLoop(messages, options) {
6234
6235
  // an optional caller-provided workflow name) onto EVERY span this run emits,
6235
6236
  // so a collector can group one run's model/tool spans into one workflow.
6236
6237
  if (recorder && typeof recorder.setDefaultAttribute === "function") {
6237
- recorder.setDefaultAttribute("workflow.run_id", runId);
6238
- if (options.workflowName) {
6239
- recorder.setDefaultAttribute(
6240
- "workflow.name",
6241
- String(options.workflowName),
6242
- );
6238
+ // Normalize the unified id set (P2 observability): every id is charset-
6239
+ // sanitized + length-capped, and only these allow-listed keys are stamped,
6240
+ // so span cardinality stays bounded and no content leaks in as an id.
6241
+ const idAttrs = buildTelemetryAttributes({
6242
+ "workflow.run_id": runId,
6243
+ "workflow.name": options.workflowName || undefined,
6244
+ "session.id": options.sessionId || undefined,
6245
+ "agent.id": options.agentId || undefined,
6246
+ "parent_agent.id": options.parentAgentId || undefined,
6247
+ });
6248
+ for (const [k, v] of Object.entries(idAttrs)) {
6249
+ recorder.setDefaultAttribute(k, v);
6243
6250
  }
6244
6251
  }
6245
6252
  yield {
@@ -1 +0,0 @@
1
- import{P as c,J as p,b as s}from"./vendor-Bn1JzzAn.js";import{r as a}from"./markdown-Yo_c6Krv.js";import{_ as d}from"./index-D3zCCZRS.js";const _={__name:"MarkdownRenderer",props:{content:{type:String,default:""}},setup(t,{expose:o}){o();const e=t,r=s(()=>a(e.content||"")),n={props:e,rendered:r,computed:s,get renderMarkdown(){return a}};return Object.defineProperty(n,"__isScriptSetup",{enumerable:!1,value:!0}),n}},u=["innerHTML"];function i(t,o,e,r,n,m){return c(),p("div",{class:"markdown-content",innerHTML:r.rendered},null,8,u)}const w=d(_,[["render",i],["__scopeId","data-v-0b0b9f63"],["__file","/tmp/cc-web-panel-rPUuJu/repo/packages/web-panel/src/components/MarkdownRenderer.vue"]]);export{w as M};
@@ -1 +0,0 @@
1
- import{I as n,J as g,c as l,K as r,N as u,P as h,U as o,R as t,Q as b}from"./vendor-Bn1JzzAn.js";import{_ as v,b as m}from"./index-D3zCCZRS.js";import{a8 as y,M}from"./icons-DG5UD-6K.js";const B={__name:"MobileProjects",setup(p,{expose:i}){i();const c=u(),{t:e}=m();function a(){c.push("/projects")}function _(){c.push("/mobile-bridge")}const s={router:c,t:e,goToProjects:a,goToMobileBridge:_,get useRouter(){return u},get useI18n(){return m},get MobileOutlined(){return M},get FolderOutlined(){return y}};return Object.defineProperty(s,"__isScriptSetup",{enumerable:!1,value:!0}),s}},x={class:"mobile-projects-placeholder"},O={class:"title"},k={class:"subtitle"},w={class:"explainer"};function T(p,i,c,e,a,_){const s=n("a-alert"),d=n("a-button"),f=n("a-space"),P=n("a-empty"),j=n("a-card");return h(),g("div",x,[l(j,null,{default:r(()=>[l(P,{description:!1},{image:r(()=>[l(e.MobileOutlined,{style:{fontSize:"64px",color:"#bfbfbf"}})]),default:r(()=>[o("h2",O,t(e.t("mobileProjects.title")),1),o("p",k,t(e.t("mobileProjects.subtitle")),1),l(s,{message:e.t("mobileProjects.v02Banner"),type:"info","show-icon":"",class:"banner"},null,8,["message"]),o("div",w,[o("h3",null,t(e.t("mobileProjects.currentDirection")),1),o("p",null,t(e.t("mobileProjects.currentDirectionBody")),1),o("ul",null,[o("li",null,t(e.t("mobileProjects.stepPhone")),1),o("li",null,t(e.t("mobileProjects.stepTap")),1),o("li",null,t(e.t("mobileProjects.stepPull")),1)]),o("h3",null,t(e.t("mobileProjects.reverseDirection")),1),o("p",null,t(e.t("mobileProjects.reverseDirectionBody")),1)]),l(f,{class:"actions"},{default:r(()=>[l(d,{type:"primary",onClick:e.goToProjects},{default:r(()=>[l(e.FolderOutlined),b(" "+t(e.t("mobileProjects.viewLocalProjects")),1)]),_:1}),l(d,{onClick:e.goToMobileBridge},{default:r(()=>[l(e.MobileOutlined),b(" "+t(e.t("mobileProjects.checkBridge")),1)]),_:1})]),_:1})]),_:1})]),_:1})])}const N=v(B,[["render",T],["__scopeId","data-v-9262cc45"],["__file","/tmp/cc-web-panel-rPUuJu/repo/packages/web-panel/src/views/MobileProjects.vue"]]);export{N as default};
@@ -1 +0,0 @@
1
- import{I as i,J as A,U as I,R as s,Q as r,c as t,K as a,V as _,S as x,o as oe,b as ee,r as u,P as p,F as ne,a2 as ge}from"./vendor-Bn1JzzAn.js";import{_ as we,b as re,u as ie,d}from"./index-D3zCCZRS.js";import{u as de}from"./useShellMode-CgR0wCYM.js";import{aN as ke,aO as Fe,aP as be,f as je,a5 as Ce,a8 as xe,at as Oe,R as Se}from"./icons-DG5UD-6K.js";const Pe={__name:"Projects",setup(se,{expose:l}){l();const{t:te}=re(),e=ie(),{isEmbedded:J}=de();async function w(o,f,ye,ae=8e3){if(J){const Z=await e.sendRaw({...f,type:o},ae);if(!Z?.ok){const $=Z?.error;throw new Error(typeof $=="string"?$:$?.message||`${o} failed`)}return Z.result}return e.executeJson(ye,ae)}const B=u(!1),y=u([]),O=u(""),S=u(""),P=u(!1),v=u(null),h=u([]),k=u(!1),E=u(!1),L=u(!1),F=u({name:"",description:"",type:"document",rootPath:""}),z=[{title:"ID",key:"id",width:110},{title:"名称",key:"name"},{title:"类型",key:"project_type",width:120},{title:"状态",key:"status",width:100},{title:"同步",key:"sync_status",width:100},{title:"更新",key:"updated_at",width:160},{title:"操作",key:"actions",width:180,fixed:"right"}],V=ee(()=>{const o={active:0,draft:0,completed:0,archived:0};for(const f of y.value)o[f.status]!==void 0&&(o[f.status]+=1);return o}),W=ee(()=>y.value.filter(o=>!(O.value&&o.status!==O.value||S.value&&!o.name.toLowerCase().includes(S.value.toLowerCase()))));function g(o){switch(o){case"active":return"green";case"draft":return"default";case"completed":return"cyan";case"archived":return"gold";default:return"default"}}function K(o){switch(o){case"synced":return"green";case"pending":return"orange";case"conflict":return"red";case"error":return"red";default:return"default"}}function Q(o){return!o&&o!==0?"-":new Date(typeof o=="number"?o:parseInt(o,10)).toLocaleString("zh-CN",{hour12:!1})}async function C(){B.value=!0;try{const o=await w("project.list",{limit:500},"project list --limit 500 --json",1e4);y.value=Array.isArray(o?.projects)?o.projects:Array.isArray(o)?o:[]}catch(o){d.error("加载失败: "+(o.message||o)),y.value=[]}finally{B.value=!1}}function G(o){return{onClick:()=>M(o)}}async function M(o){v.value=o,P.value=!0,k.value=!0,h.value=[];try{const f=await w("project.listFiles",{projectId:o.id,limit:200},`project list-files ${o.id} --json`,8e3);h.value=Array.isArray(f?.files)?f.files:[]}catch{h.value=[]}finally{k.value=!1}}async function H(o){try{(await w("project.delete",{id:o.id},`project delete ${o.id} --json`,8e3))?.ok?(d.success(`已删除项目 '${o.name}'`),P.value=!1,await C()):d.error("删除失败")}catch(f){d.error(f.message||String(f))}}function X(){F.value={name:"",description:"",type:"document",rootPath:""},E.value=!0}async function T(){if(!F.value.name.trim()){d.warning("请输入项目名称");return}L.value=!0;try{const o=await w("project.init",{name:F.value.name.trim(),description:F.value.description||null,projectType:F.value.type,rootPath:F.value.rootPath||null},`project init "${F.value.name.trim()}" --type ${F.value.type} --json`,8e3);o?.id?(d.success(`已创建项目 '${o.name}'`),E.value=!1,await C()):d.error("创建失败")}catch(o){d.error(o.message||String(o))}finally{L.value=!1}}const b=u(!1),D=u(!1),j=u({path:"",content:""}),U=u(!1),m=u(!1),R=u({path:""}),n=u(!1),c=u(null),q=u(""),Y=u(!1);function ue(){j.value={path:"",content:""},b.value=!0}function ce(){R.value={path:""},U.value=!0}async function N(){if(v.value){k.value=!0;try{const o=await w("project.listFiles",{projectId:v.value.id,limit:200},`project list-files ${v.value.id} --json`,8e3);h.value=Array.isArray(o?.files)?o.files:[]}catch(o){d.error("刷新文件失败: "+(o.message||o))}finally{k.value=!1}}}async function fe(){if(!j.value.path.trim()){d.warning("请输入文件路径");return}if(v.value){D.value=!0;try{const o=await w("project.createFile",{projectId:v.value.id,filePath:j.value.path.trim(),content:j.value.content||""},`project create-file ${v.value.id} "${j.value.path.trim()}" --json`,8e3);o?.id?(d.success(`已创建文件 '${o.file_name}'`),b.value=!1,await N(),await C()):d.error("创建文件失败")}catch(o){d.error(o.message||String(o))}finally{D.value=!1}}}async function pe(){if(!R.value.path.trim()){d.warning("请输入文件夹路径");return}if(v.value){m.value=!0;try{const o=await w("project.createFolder",{projectId:v.value.id,folderPath:R.value.path.trim()},`project create-folder ${v.value.id} "${R.value.path.trim()}" --json`,8e3);o?.id?(d.success(`已创建文件夹 '${o.file_name}'`),U.value=!1,await N(),await C()):d.error("创建文件夹失败")}catch(o){d.error(o.message||String(o))}finally{m.value=!1}}}async function ve(o){try{(await w("project.deleteFile",{fileId:o.id},`project delete-file ${o.id} --json`,8e3))?.deleted?(d.success(`已删除 '${o.file_name}'`),await N(),await C()):d.error("删除失败")}catch(f){d.error(f.message||String(f))}}async function me(o){if(o.is_folder){d.info("文件夹无内容可编辑");return}try{const f=await w("project.getFile",{fileId:o.id},`project get-file ${o.id} --json`,8e3);c.value=o,q.value=f?.content||"",n.value=!0}catch(f){d.error("读取文件失败: "+(f.message||f))}}async function _e(){if(c.value){Y.value=!0;try{(await w("project.writeFile",{fileId:c.value.id,content:q.value},`project write-file ${c.value.id} --json`,8e3))?.id?(d.success("已保存"),n.value=!1,await N()):d.error("保存失败")}catch(o){d.error(o.message||String(o))}finally{Y.value=!1}}}oe(()=>{C()});const le={t:te,ws:e,isEmbedded:J,callProjectTopic:w,loading:B,projects:y,statusFilter:O,nameFilter:S,detailOpen:P,detail:v,files:h,filesLoading:k,createOpen:E,creating:L,newProject:F,columns:z,countByStatus:V,filteredProjects:W,statusColor:g,syncColor:K,formatTime:Q,loadAll:C,onRowClick:G,openDetail:M,onDelete:H,onShowCreate:X,onCreate:T,createFileOpen:b,creatingFile:D,newFile:j,createFolderOpen:U,creatingFolder:m,newFolder:R,editFileOpen:n,editingFile:c,editFileContent:q,savingFile:Y,openCreateFile:ue,openCreateFolder:ce,refreshFiles:N,onCreateFile:fe,onCreateFolder:pe,onDeleteFile:ve,openEditFile:me,onSaveFile:_e,ref:u,computed:ee,onMounted:oe,get ReloadOutlined(){return Se},get PlusOutlined(){return Oe},get FolderOutlined(){return xe},get FileTextOutlined(){return Ce},get CheckCircleOutlined(){return je},get CheckSquareOutlined(){return be},get FileAddOutlined(){return Fe},get FolderAddOutlined(){return ke},get message(){return d},get useI18n(){return re},get useWsStore(){return ie},get useShellMode(){return de}};return Object.defineProperty(le,"__isScriptSetup",{enumerable:!1,value:!0}),le}},ze={style:{display:"flex","align-items":"center","justify-content":"space-between","margin-bottom":"24px"}},Ue={class:"page-title"},Ae={class:"page-sub"},Ie={class:"filter-bar",style:{"margin-bottom":"12px",display:"flex",gap:"12px","align-items":"center"}},he={key:0,style:{"font-size":"12px",color:"#888"}},De={key:0,style:{color:"#888","font-size":"12px"}},Re={key:0},Te={key:1,style:{color:"#888"}},Be=["onClick"],Ee={key:0,style:{color:"#888","font-size":"12px"}};function Le(se,l,te,e,J,w){const B=i("router-link"),y=i("a-button"),O=i("a-space"),S=i("a-statistic"),P=i("a-card"),v=i("a-col"),h=i("a-row"),k=i("a-radio-button"),E=i("a-radio-group"),L=i("a-input-search"),F=i("a-alert"),z=i("a-tag"),V=i("a-popconfirm"),W=i("a-table"),g=i("a-descriptions-item"),K=i("a-descriptions"),Q=i("a-divider"),C=i("a-empty"),G=i("a-list-item"),M=i("a-list"),H=i("a-spin"),X=i("a-drawer"),T=i("a-input"),b=i("a-form-item"),D=i("a-textarea"),j=i("a-form"),U=i("a-modal"),m=i("a-select-option"),R=i("a-select");return p(),A("div",null,[I("div",ze,[I("div",null,[I("h2",Ue,s(e.t("projects.title")),1),I("p",Ae,[l[17]||(l[17]=r("桌面 / CLI / 手机 三端共享同一份项目数据,Phase 3d sync 自动同步 (",-1)),t(B,{to:"/project-init"},{default:a(()=>[...l[16]||(l[16]=[r("项目初始化 / 环境设置",-1)])]),_:1}),l[18]||(l[18]=r(")",-1))])]),t(O,null,{default:a(()=>[t(y,{loading:e.loading,onClick:e.loadAll},{icon:a(()=>[t(e.ReloadOutlined)]),default:a(()=>[l[19]||(l[19]=r(" 刷新 ",-1))]),_:1},8,["loading"]),t(y,{type:"primary",onClick:e.onShowCreate},{icon:a(()=>[t(e.PlusOutlined)]),default:a(()=>[l[20]||(l[20]=r(" 新建项目 ",-1))]),_:1})]),_:1})]),_(" Stats "),t(h,{gutter:[16,16],style:{"margin-bottom":"20px"}},{default:a(()=>[t(v,{xs:12,sm:8,lg:6},{default:a(()=>[t(P,{style:{background:"var(--bg-card)","border-color":"var(--border-color)"}},{default:a(()=>[t(S,{title:"总项目",value:e.projects.length,"value-style":{color:"#1677ff",fontSize:"20px"}},{prefix:a(()=>[t(e.FolderOutlined)]),_:1},8,["value"])]),_:1})]),_:1}),t(v,{xs:12,sm:8,lg:6},{default:a(()=>[t(P,{style:{background:"var(--bg-card)","border-color":"var(--border-color)"}},{default:a(()=>[t(S,{title:"活跃",value:e.countByStatus.active,"value-style":{color:"#52c41a",fontSize:"20px"}},{prefix:a(()=>[t(e.CheckCircleOutlined)]),_:1},8,["value"])]),_:1})]),_:1}),t(v,{xs:12,sm:8,lg:6},{default:a(()=>[t(P,{style:{background:"var(--bg-card)","border-color":"var(--border-color)"}},{default:a(()=>[t(S,{title:"草稿",value:e.countByStatus.draft,"value-style":{color:"#8c8c8c",fontSize:"20px"}},{prefix:a(()=>[t(e.FileTextOutlined)]),_:1},8,["value"])]),_:1})]),_:1}),t(v,{xs:12,sm:8,lg:6},{default:a(()=>[t(P,{style:{background:"var(--bg-card)","border-color":"var(--border-color)"}},{default:a(()=>[t(S,{title:"已完成",value:e.countByStatus.completed,"value-style":{color:"#13c2c2",fontSize:"20px"}},{prefix:a(()=>[t(e.CheckSquareOutlined)]),_:1},8,["value"])]),_:1})]),_:1})]),_:1}),_(" Filter "),I("div",Ie,[t(E,{value:e.statusFilter,"onUpdate:value":l[0]||(l[0]=n=>e.statusFilter=n),size:"small","button-style":"solid"},{default:a(()=>[t(k,{value:""},{default:a(()=>[...l[21]||(l[21]=[r("全部",-1)])]),_:1}),t(k,{value:"active"},{default:a(()=>[...l[22]||(l[22]=[r("活跃",-1)])]),_:1}),t(k,{value:"draft"},{default:a(()=>[...l[23]||(l[23]=[r("草稿",-1)])]),_:1}),t(k,{value:"completed"},{default:a(()=>[...l[24]||(l[24]=[r("已完成",-1)])]),_:1}),t(k,{value:"archived"},{default:a(()=>[...l[25]||(l[25]=[r("已归档",-1)])]),_:1})]),_:1},8,["value"]),t(L,{value:e.nameFilter,"onUpdate:value":l[1]||(l[1]=n=>e.nameFilter=n),placeholder:"按名称过滤",style:{"max-width":"260px"},"allow-clear":""},null,8,["value"])]),!e.projects.length&&!e.loading?(p(),x(F,{key:0,type:"info",message:"还没有项目",description:'运行 `cc project init <name>` 或点击右上角"新建项目"按钮创建。桌面 / CLI / 手机三端共享同一份数据。',"show-icon":"",style:{"margin-bottom":"16px"}})):_("v-if",!0),e.projects.length||e.loading?(p(),x(W,{key:1,columns:e.columns,"data-source":e.filteredProjects,loading:e.loading,pagination:{pageSize:20,showSizeChanger:!0},"row-key":"id","custom-row":e.onRowClick},{bodyCell:a(({column:n,record:c})=>[n.key==="id"?(p(),A("code",he,s(c.id.slice(0,8))+"…",1)):n.key==="name"?(p(),A(ne,{key:1},[I("strong",null,s(c.name),1),c.description?(p(),A("div",De,s(c.description),1)):_("v-if",!0)],64)):n.key==="project_type"?(p(),x(z,{key:2},{default:a(()=>[r(s(c.project_type),1)]),_:2},1024)):n.key==="status"?(p(),x(z,{key:3,color:e.statusColor(c.status)},{default:a(()=>[r(s(c.status),1)]),_:2},1032,["color"])):n.key==="sync_status"?(p(),x(z,{key:4,color:e.syncColor(c.sync_status)},{default:a(()=>[r(s(c.sync_status||"unknown"),1)]),_:2},1032,["color"])):n.key==="updated_at"?(p(),A(ne,{key:5},[r(s(e.formatTime(c.updated_at)),1)],64)):n.key==="actions"?(p(),x(O,{key:6,onClick:l[2]||(l[2]=ge(()=>{},["stop"]))},{default:a(()=>[t(y,{size:"small",onClick:q=>e.openDetail(c)},{default:a(()=>[...l[26]||(l[26]=[r("详情",-1)])]),_:1},8,["onClick"]),t(V,{title:`删除项目 '${c.name}' ?`,"ok-text":"删除","ok-type":"danger","cancel-text":"取消",onConfirm:q=>e.onDelete(c)},{default:a(()=>[t(y,{size:"small",danger:""},{default:a(()=>[...l[27]||(l[27]=[r("删除",-1)])]),_:1})]),_:1},8,["title","onConfirm"])]),_:2},1024)):_("v-if",!0)]),_:1},8,["data-source","loading"])):_("v-if",!0),_(" Detail drawer "),t(X,{open:e.detailOpen,"onUpdate:open":l[3]||(l[3]=n=>e.detailOpen=n),title:e.detail?.name||"项目详情",width:"640",placement:"right"},{default:a(()=>[e.detail?(p(),x(K,{key:0,column:1,bordered:"",size:"small"},{default:a(()=>[t(g,{label:"ID"},{default:a(()=>[I("code",null,s(e.detail.id),1)]),_:1}),t(g,{label:"名称"},{default:a(()=>[r(s(e.detail.name),1)]),_:1}),t(g,{label:"描述"},{default:a(()=>[r(s(e.detail.description||"-"),1)]),_:1}),t(g,{label:"类型"},{default:a(()=>[t(z,null,{default:a(()=>[r(s(e.detail.project_type),1)]),_:1})]),_:1}),t(g,{label:"状态"},{default:a(()=>[t(z,{color:e.statusColor(e.detail.status)},{default:a(()=>[r(s(e.detail.status),1)]),_:1},8,["color"])]),_:1}),t(g,{label:"同步状态"},{default:a(()=>[t(z,{color:e.syncColor(e.detail.sync_status)},{default:a(()=>[r(s(e.detail.sync_status||"unknown"),1)]),_:1},8,["color"])]),_:1}),t(g,{label:"用户"},{default:a(()=>[r(s(e.detail.user_id),1)]),_:1}),t(g,{label:"根路径"},{default:a(()=>[e.detail.root_path?(p(),A("code",Re,s(e.detail.root_path),1)):(p(),A("span",Te,"(无)"))]),_:1}),t(g,{label:"文件数"},{default:a(()=>[r(s(e.detail.file_count||0),1)]),_:1}),t(g,{label:"创建"},{default:a(()=>[r(s(e.formatTime(e.detail.created_at)),1)]),_:1}),t(g,{label:"更新"},{default:a(()=>[r(s(e.formatTime(e.detail.updated_at)),1)]),_:1})]),_:1})):_("v-if",!0),t(Q,null,{default:a(()=>[...l[28]||(l[28]=[r("文件",-1)])]),_:1}),_(" Sub-phase 7.3 (2026-05-17): 文件 CRUD toolbar "),t(O,{style:{"margin-bottom":"12px"}},{default:a(()=>[t(y,{size:"small",type:"primary",disabled:!e.detail,onClick:e.openCreateFile},{default:a(()=>[t(e.FileAddOutlined),l[29]||(l[29]=r(" 新建文件 ",-1))]),_:1},8,["disabled"]),t(y,{size:"small",disabled:!e.detail,onClick:e.openCreateFolder},{default:a(()=>[t(e.FolderAddOutlined),l[30]||(l[30]=r(" 新建文件夹 ",-1))]),_:1},8,["disabled"])]),_:1}),t(H,{spinning:e.filesLoading},{default:a(()=>[!e.files.length&&!e.filesLoading?(p(),x(C,{key:0,description:"无文件 (点上方按钮新建)"})):(p(),x(M,{key:1,size:"small","data-source":e.files},{renderItem:a(({item:n})=>[t(G,null,{extra:a(()=>[t(O,null,{default:a(()=>[n.file_size?(p(),A("span",Ee,s(n.file_size)+" B",1)):_("v-if",!0),t(V,{title:`删除 ${n.is_folder?"文件夹":"文件"} '${n.file_name}' ?`,"ok-text":"删除","ok-type":"danger","cancel-text":"取消",onConfirm:c=>e.onDeleteFile(n)},{default:a(()=>[t(y,{size:"small",danger:""},{default:a(()=>[...l[31]||(l[31]=[r("×",-1)])]),_:1})]),_:1},8,["title","onConfirm"])]),_:2},1024)]),default:a(()=>[I("span",{style:{cursor:"pointer"},onClick:c=>e.openEditFile(n)},s(n.is_folder?"📁":"📄")+" "+s(n.file_path),9,Be)]),_:2},1024)]),_:1},8,["data-source"]))]),_:1},8,["spinning"])]),_:1},8,["open","title"]),_(" Sub-phase 7.3: Create file modal "),t(U,{open:e.createFileOpen,"onUpdate:open":l[6]||(l[6]=n=>e.createFileOpen=n),title:"新建文件","confirm-loading":e.creatingFile,"ok-text":"创建","cancel-text":"取消",onOk:e.onCreateFile},{default:a(()=>[t(j,{layout:"vertical"},{default:a(()=>[t(b,{label:"文件路径(项目内)",required:""},{default:a(()=>[t(T,{value:e.newFile.path,"onUpdate:value":l[4]||(l[4]=n=>e.newFile.path=n),placeholder:"例如 README.md 或 src/main.kt"},null,8,["value"])]),_:1}),t(b,{label:"初始内容(可选)"},{default:a(()=>[t(D,{value:e.newFile.content,"onUpdate:value":l[5]||(l[5]=n=>e.newFile.content=n),rows:6,placeholder:"留空创建空文件"},null,8,["value"])]),_:1})]),_:1})]),_:1},8,["open","confirm-loading"]),_(" Sub-phase 7.3: Create folder modal "),t(U,{open:e.createFolderOpen,"onUpdate:open":l[8]||(l[8]=n=>e.createFolderOpen=n),title:"新建文件夹","confirm-loading":e.creatingFolder,"ok-text":"创建","cancel-text":"取消",onOk:e.onCreateFolder},{default:a(()=>[t(j,{layout:"vertical"},{default:a(()=>[t(b,{label:"文件夹路径(项目内)",required:""},{default:a(()=>[t(T,{value:e.newFolder.path,"onUpdate:value":l[7]||(l[7]=n=>e.newFolder.path=n),placeholder:"例如 src/utils"},null,8,["value"])]),_:1})]),_:1})]),_:1},8,["open","confirm-loading"]),_(" Sub-phase 7.3: Edit file content modal "),t(U,{open:e.editFileOpen,"onUpdate:open":l[10]||(l[10]=n=>e.editFileOpen=n),title:`编辑 ${e.editingFile?.file_name||""}`,"confirm-loading":e.savingFile,"ok-text":"保存","cancel-text":"取消",onOk:e.onSaveFile,width:"720"},{default:a(()=>[t(D,{value:e.editFileContent,"onUpdate:value":l[9]||(l[9]=n=>e.editFileContent=n),rows:18,"auto-size":{minRows:12,maxRows:24},style:{"font-family":"monospace"}},null,8,["value"])]),_:1},8,["open","title","confirm-loading"]),_(" Create modal "),t(U,{open:e.createOpen,"onUpdate:open":l[15]||(l[15]=n=>e.createOpen=n),title:"新建项目","confirm-loading":e.creating,"ok-text":"创建","cancel-text":"取消",onOk:e.onCreate},{default:a(()=>[t(j,{layout:"vertical"},{default:a(()=>[t(b,{label:"项目名称",required:""},{default:a(()=>[t(T,{value:e.newProject.name,"onUpdate:value":l[11]||(l[11]=n=>e.newProject.name=n),placeholder:"例如:旅行计划-上海"},null,8,["value"])]),_:1}),t(b,{label:"描述"},{default:a(()=>[t(D,{value:e.newProject.description,"onUpdate:value":l[12]||(l[12]=n=>e.newProject.description=n),rows:2},null,8,["value"])]),_:1}),t(b,{label:"类型"},{default:a(()=>[t(R,{value:e.newProject.type,"onUpdate:value":l[13]||(l[13]=n=>e.newProject.type=n)},{default:a(()=>[t(m,{value:"document"},{default:a(()=>[...l[32]||(l[32]=[r("文档 (document)",-1)])]),_:1}),t(m,{value:"data"},{default:a(()=>[...l[33]||(l[33]=[r("数据 (data)",-1)])]),_:1}),t(m,{value:"web"},{default:a(()=>[...l[34]||(l[34]=[r("Web",-1)])]),_:1}),t(m,{value:"app"},{default:a(()=>[...l[35]||(l[35]=[r("应用 (app)",-1)])]),_:1}),t(m,{value:"presentation"},{default:a(()=>[...l[36]||(l[36]=[r("演示文稿",-1)])]),_:1}),t(m,{value:"spreadsheet"},{default:a(()=>[...l[37]||(l[37]=[r("表格",-1)])]),_:1}),t(m,{value:"design"},{default:a(()=>[...l[38]||(l[38]=[r("设计",-1)])]),_:1}),t(m,{value:"code"},{default:a(()=>[...l[39]||(l[39]=[r("代码",-1)])]),_:1}),t(m,{value:"workflow"},{default:a(()=>[...l[40]||(l[40]=[r("工作流",-1)])]),_:1}),t(m,{value:"knowledge"},{default:a(()=>[...l[41]||(l[41]=[r("知识库",-1)])]),_:1})]),_:1},8,["value"])]),_:1}),t(b,{label:"根路径 (可选)"},{default:a(()=>[t(T,{value:e.newProject.rootPath,"onUpdate:value":l[14]||(l[14]=n=>e.newProject.rootPath=n),placeholder:"留空则仅元数据"},null,8,["value"])]),_:1})]),_:1})]),_:1},8,["open","confirm-loading"])])}const Je=we(Pe,[["render",Le],["__scopeId","data-v-019ac5aa"],["__file","/tmp/cc-web-panel-rPUuJu/repo/packages/web-panel/src/views/Projects.vue"]]);export{Je as default};
@@ -1 +0,0 @@
1
- import{Q as r}from"./index-D3zCCZRS.js";const o=((n,a,e)=>{r(n,`[ant-design-vue: ${a}] ${e}`)});export{o as d};
@@ -1 +0,0 @@
1
- import{A as o}from"./Row-DfIPNtuG.js";import{a0 as t}from"./index-D3zCCZRS.js";import"./vendor-Bn1JzzAn.js";import"./responsiveObserve-REbucKSx.js";import"./useFlexGapSupport-CRVwKrrI.js";import"./styleChecker-Chklyoq6.js";import"./index-BIP62a6r.js";import"./icons-DG5UD-6K.js";const l=t(o);export{l as default};
@@ -1 +0,0 @@
1
- import{C as o}from"./Col-CePor71g.js";import{a0 as t}from"./index-D3zCCZRS.js";import"./vendor-Bn1JzzAn.js";import"./index-BIP62a6r.js";import"./icons-DG5UD-6K.js";const s=t(o);export{s as default};