chainlesschain 0.162.168 → 0.162.170

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 (167) hide show
  1. package/package.json +2 -2
  2. package/src/assets/web-panel/assets/{AIOps-CDzzuWZV.js → AIOps-BTXN24I3.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-Dy-8MmQi.js → ActionButton-BBB1VF4j.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-BtJ_VWkx.js → Analytics-BVCXq0k6.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-DkjiI4VS.js → AppLayout-C3EOo2s5.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-h8PyDO86.js → Artifacts-CsJTZSMv.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-DiDV0C6l.js → Audit-CP0AllJ_.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-DTw_aOaf.js → BackgroundAgents-BCAjs08m.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-CihFuUO9.js → Backup-BxQI5XH4.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-CqP2QNoR.js → BaseInput-Bu-PNzC5.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-tm-ygX-p.js → Chat-DjPQynup.js} +5 -5
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-CygkRopu.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-BFMyRN3Q.js → Checkbox-BxL6xm7Z.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-B1gTeoAS.js → Codegen-CcyB7h_6.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-1GP8MvKL.js → Col-8NQiKJJx.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-BLY938F6.js → Community-B31qy5OS.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-DVt_p36I.js → Compact-C-5fAsZ4.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-DxqEBXVG.js → Compliance-BTSccvF0.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-CyWrM-T8.js → Cowork-BXctU9IK.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-Domu6aI9.js → Cron-CxVFX9NG.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-C8LKpjHJ.js → Crosschain-DEF3dq8M.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-XBy15RZ5.js → DID-BmKcE9he.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-DDAyPD-V.js → Dashboard-CECBq_wG.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-BHV0MZ13.js → Dropdown-U-iuPB0n.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-DmFGW4PX.js → EmailListRenderer-D8BIlSPL.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CqYdbKvd.js → FamilyGuardDashboard-BzQGfUH-.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-YP5-Puv5.js → Federation-4_WlOqLZ.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-BOmcIcTe.js → FormItemContext-gtrW-DdF.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-CvTZdoc4.js → GenericCardRenderer-BLcvJJB6.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-Q2oxua0L.js → Git-Bk0janGD.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-D-WpyWEU.js → Governance-CcfyMlV8.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-NUtsYHQo.js → Inference-CBLrAf4M.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-BHQLa8fr.js → KnowledgeGraph-ClguhteZ.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-6WsR5Q1G.js → Logs-Dp_oeKfJ.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-DuWoDwMx.js → MarkdownRenderer-RQ1HbTY5.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-CigohRRW.js → Marketplace-BHk50smM.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-jcp6jVLf.js → McpTools-BAY3CV6J.js} +5 -5
  38. package/src/assets/web-panel/assets/{Memory-B00NU1ph.js → Memory-w0pG2AKx.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-Cs_p3PzU.js → MobileBridge-C0VkDUE3.js} +1 -1
  40. package/src/assets/web-panel/assets/MobileProjects-BCvQ80I9.js +1 -0
  41. package/src/assets/web-panel/assets/{Mtc-CSEflwzw.js → Mtc-DSIl41Tt.js} +6 -6
  42. package/src/assets/web-panel/assets/{MtcAudit-dxSiEZ96.js → MtcAudit-D1mzUlIr.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-CDAFEAyq.js → Multisig-CeJz4VSS.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-HoFpZNiB.js → NLProgramming-_O3aweu8.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-tAoQvVcE.js → Notes-Dt7_pO86.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-Czo2M9F4.js → NotificationSettings-Dhg3Nktv.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-CM1kNTp6.js → OrderTableRenderer-QsAPcTb2.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-Cudr0VOC.js → Organization-BuiaUPkv.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-BSXeDKiN.js → Overflow-C3jb6knl.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-Cqv_g-F9.js → P2P-BOMyrLkb.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-7rjprPh8.js → PdhVaultBrowser-UvLStCgS.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-97pIDQKE.js → Permissions-B9cN0dJH.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-bparK9eQ.js → PersonalDataHub-DNjW5qro.js} +2 -2
  54. package/src/assets/web-panel/assets/{Pipeline-CJgerM09.js → Pipeline-CFLyZ6Sh.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-JqFS3PN7.js → Privacy-D3w6vOxf.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-DMITd-2c.js → ProjectInit-cwcVsn8K.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-1UkdJ8Ii.js → ProjectSettings-aUoAroc-.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-CYZqegqw.js → Projects-CrzQv7Zv.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-ChfXz3U0.js → Providers-jZaPizKR.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-Rzkka-mf.js → QrScannerModal-DNzmwcfg.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-DEz9HaON.js → QuickAsk-m-5Gwt70.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-EFGP8AVu.js → Recommend-CrrAjNxP.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-6um0lGGy.js → RemoteSession-BDCMjRFe.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-CA1BXTup.js → Reputation-CPCt1Jc8.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-stTnDJ9y.js → Row-C-RorxHs.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-B10qCCoG.js → RssFeed-CnQs4TuY.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-DSJroZ6p.js → Search-B1jkeFZY.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-BpXkwiMW.js → Security-CGKI3vvd.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-BPQxjwK6.js → Services-DQjesYF8.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-CfbKAMul.js → Skeleton-C9Ipj0A6.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-CttYTfX0.js → Skills-D2j54i9O.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-ByluHich.js → Sla-xhrVjTeb.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-BdZXKsfV.js → SpeechSettings-C6KMOUCa.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-xCR7iDzy.js → SyncSettings-CmIPU5wR.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-qTko_INn.js → Tasks-B-T1J42r.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-CkIzhgDJ.js → Templates-Aq669sjw.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-BIaCaaK8.js → Tenant-BKWUWNXB.js} +1 -1
  78. package/src/assets/web-panel/assets/Terminal-DEeAvA8L.js +3 -0
  79. package/src/assets/web-panel/assets/{TimelineRenderer-D5eQDnEF.js → TimelineRenderer-C465ySdn.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-DmeJ8NMQ.js → Tokens-BgVqF-bX.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-C5sgb3C7.js → Trigger-BMNHmxwH.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-H2vOb3Rb.js → Trust-B_adk89L.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-NmPtHK2u.js → UkeySign-Biw38mog.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-DHv-Ti7K.js → VideoEditing-AC-eaw5k.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-BYZ3-PRP.js → Wallet-8lQ5_t0X.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-BaSlP7om.js → WebAuthn-JDmH_-DF.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-CZ7IHuIi.js → WorkflowEditor-DHQAdITG.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-Dd9kY8gn.js → chat-C3qauUVv.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-yhzo6QSV.js → colors-DLP70JGe.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-C1JneVdM.js → compact-item-DjsoqA5t.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-C_waVCg2.js → createContext-CfEdl1in.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-Cwuuqc_f.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-CkVg0Xnh.js → hasIn-DWwHHlBG.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DapftLUr.js → index-ArZCi1rK.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-C-Sscgnv.js → index-B-rD2rhi.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-CGtoL5mR.js → index-B4gCFHtB.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-Diquc7-l.js → index-BcDeMCUK.js} +1 -1
  98. package/src/assets/web-panel/assets/index-BkTk-zJK.js +1 -0
  99. package/src/assets/web-panel/assets/{index-D6UXDH2D.js → index-Bp_WNm2_.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DhZgULsY.js → index-BqPuyN4p.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DuGtLiTW.js → index-BydBh7oT.js} +1 -1
  102. package/src/assets/web-panel/assets/index-C-BSTEhe.js +1 -0
  103. package/src/assets/web-panel/assets/{index-D7kQ_4uL.js → index-C0xMzvk8.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Bx3A6X4J.js → index-C47EiQ7d.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-C4HNdMCt.js → index-C4k6JGm_.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BsSsurcn.js → index-C8wrUFzY.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CUDjGt_V.js → index-CNeLedtU.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Dez7TUgG.js → index-CbF27GqZ.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BlJNvg-2.js → index-CgruUvB3.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BYOyb5X2.js → index-CnhJbWsl.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-_M8El5iz.js → index-CsEk9s04.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-ClQhh6v3.js → index-D-te-S9f.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-_rZxgFtc.js → index-D5VL0NYy.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BrN2hAeE.js → index-DAcBi6F-.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DPkqQ2Pr.js → index-DKsWujXP.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CErewFeT.js → index-DTDpF8MS.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-C_qqYZT7.js → index-DTEcPJGf.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-U_nLXGIx.js → index-DbFnfyKh.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-Bwuu3Bf3.js → index-DbjUePgq.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CfEMjbhu.js → index-Dfka-vzJ.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-C_riAa6d.js → index-DgQ92xdO.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-D1xIbAxQ.js → index-Dmt1HDWZ.js} +3 -3
  123. package/src/assets/web-panel/assets/{index-BlzbSUK9.js → index-Dn2rNMmv.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-ewb0-LoH.js → index-Ds1BfT_C.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BjDnI7oM.js → index-Lh0e2afo.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DUA96wEj.js → index-OfMwkFIo.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CmiLmG0i.js → index-T5emAyFT.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-_VmzCYFn.js → index-UEbYl2pu.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-1VYhIr6D.js → index-VtCz3A64.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DXHk9Vof.js → index-_g5q2hF3.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CBwmwTNa.js → index-gu8zBhtp.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BTMiVIBI.js → index-nuZaMsMy.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-DNN6qtGa.js → initDefaultProps-C_pMmi75.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-BcM9exeo.js → motion-BvKgS96o.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-DsKJ-YvA.js → move-C-1U4R1I.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-glgGkxaK.js → mtc-parser-D6BC968-.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-BwHCU50S.js → omit-DxOZvcGQ.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-BVp-bLt7.js → pickAttrs-LpzmHdkw.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-CZNe0r-w.js → placementArrow-Bm--qom1.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-DUafj9pU.js → responsiveObserve-CVZZlhJf.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-H4NqXQxu.js → slide-DD1xINGx.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-UjMzzGZ5.js → statusUtils-LrjHAy63.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-qO5aCjyk.js → styleChecker-BqknYxTe.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-ChecYwC8.js → useFlexGapSupport-Dljy3diT.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-BR0QvwO3.js → useFs-OedUtPLc.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-DitK2Qgz.js → usePersonalDataHub-Cz-kxbP_.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-dxSb40dY.js → vnode-C8ZuTk8W.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-ekJk8YB2.js → zoom-CjbgJYXQ.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/plugin.js +49 -5
  151. package/src/data/changelog.json +22 -4
  152. package/src/lib/background-agent-phase.js +22 -0
  153. package/src/lib/background-agent-supervisor.js +41 -10
  154. package/src/lib/background-phase-reporter.js +37 -0
  155. package/src/lib/install-command-policy.js +29 -0
  156. package/src/lib/packer/smoke-runner.js +5 -0
  157. package/src/lib/session-lifecycle.js +3 -0
  158. package/src/runtime/agent-core.js +31 -7
  159. package/src/runtime/headless-runner.js +42 -9
  160. package/src/runtime/headless-stream.js +158 -2
  161. package/src/workers/background-agent-worker.js +17 -4
  162. package/src/assets/web-panel/assets/ChatBubbleRenderer-BVKEhj_y.js +0 -1
  163. package/src/assets/web-panel/assets/MobileProjects-BFs2WljZ.js +0 -1
  164. package/src/assets/web-panel/assets/Terminal-B2Qq38XP.js +0 -3
  165. package/src/assets/web-panel/assets/devWarning-DaDlyVDY.js +0 -1
  166. package/src/assets/web-panel/assets/index-BtsLExcc.js +0 -1
  167. package/src/assets/web-panel/assets/index-C3qDvvIb.js +0 -1
@@ -95,6 +95,43 @@ export function createBackgroundPhaseReporter(opts = {}) {
95
95
  patch({ pendingApprovals: pending });
96
96
  }
97
97
  },
98
+ /**
99
+ * A resumed run found side-effect ops whose outcome is UNKNOWN (the
100
+ * ledger reconcile "inspect" bucket): surface `uncertain_side_effect` so
101
+ * the dashboard demands a look before anything is replayed. The worker
102
+ * clears the count at the next turn/idle boundary (the verify turn has
103
+ * then run its course), so this is a within-window annotation, not a
104
+ * sticky flag. Best-effort; returns whether the state was patched.
105
+ */
106
+ reportUncertainSideEffects(count) {
107
+ if (!enabled) return false;
108
+ const n = Number(count);
109
+ if (!Number.isFinite(n) || n <= 0) return false;
110
+ return patch({
111
+ phase: "uncertain_side_effect",
112
+ uncertainSideEffects: Math.floor(n),
113
+ });
114
+ },
115
+ /**
116
+ * The model asked the user a question in a background turn
117
+ * (`ask_user_question`): there is no live channel to a human mid-turn, so
118
+ * the question is PARKED — recorded in the shared state as
119
+ * `pendingQuestion` with `phase: "needs_input"` (distinct from
120
+ * waiting_permission: this is a question, not an approval). The worker
121
+ * keeps `needs_input` through the idle boundary while the question is
122
+ * unanswered and clears it when the user's reply starts the next turn.
123
+ */
124
+ reportQuestion(question) {
125
+ if (!enabled) return false;
126
+ const q = question && typeof question === "object" ? question : null;
127
+ return patch({
128
+ phase: "needs_input",
129
+ pendingQuestion: {
130
+ question: typeof q?.question === "string" ? q.question : "",
131
+ options: Array.isArray(q?.options) ? q.options : null,
132
+ },
133
+ });
134
+ },
98
135
  /**
99
136
  * Wrap a (potentially human-blocking) confirmer so the pending window is
100
137
  * visible in the background state. Disabled → returns `confirmer`
@@ -279,6 +279,35 @@ export function classifyCodeAcquisition(command) {
279
279
  };
280
280
  }
281
281
 
282
+ /**
283
+ * Classify a PLUGIN install (`cc plugin add`/`upgrade`) into the unified
284
+ * "downloading and running third-party code" trail. A plugin ships executable
285
+ * components (bin/hooks/LSP/MCP servers run as processes), so installing one
286
+ * is the plugin-runtime sibling of a global package install — same audit
287
+ * file, same opt-in policy. Pure; returns null for a nameless input.
288
+ */
289
+ export function classifyPluginInstall({
290
+ name,
291
+ version,
292
+ scope,
293
+ source,
294
+ capabilities,
295
+ } = {}) {
296
+ if (!name || typeof name !== "string") return null;
297
+ return {
298
+ manager: "cc-plugin",
299
+ packages: [version ? `${name}@${version}` : name],
300
+ // A user-scope plugin loads in EVERY repo the user runs cc in — the
301
+ // plugin-runtime analogue of `npm install -g`. Project/local scopes stay
302
+ // confined to one repository.
303
+ global: scope === "user",
304
+ ...(source ? { source: String(source) } : {}),
305
+ ...(Array.isArray(capabilities) && capabilities.length
306
+ ? { capabilities }
307
+ : {}),
308
+ };
309
+ }
310
+
282
311
  /**
283
312
  * Resolve the install-command policy from env/settings (opt-in, so the default
284
313
  * shell path is byte-unchanged). `audit` enables the audit trail; `riskFloor`
@@ -111,6 +111,11 @@ export async function smokeTestExe(ctx) {
111
111
  // child.kill on Windows doesn't traverse the process tree; taskkill
112
112
  // does. We spawned a single exe with no subprocesses so plain kill
113
113
  // is enough in practice, but /T protects us if that ever changes.
114
+ // Skip when the child has ALREADY exited: Windows recycles pids
115
+ // within seconds, so taskkilling a dead child's pid can murder an
116
+ // unrelated process tree that inherited the number (on CI: another
117
+ // vitest worker or a test's helper child).
118
+ if (child.exitCode !== null || child.signalCode !== null) return;
114
119
  spawn("taskkill", ["/F", "/T", "/PID", String(child.pid)], {
115
120
  stdio: "ignore",
116
121
  }).unref();
@@ -86,6 +86,9 @@ const STATE_ALIASES = new Map([
86
86
  ["waiting-permission", SESSION_STATES.WAITING_APPROVAL],
87
87
  ["waiting_approval", SESSION_STATES.WAITING_APPROVAL],
88
88
  ["awaiting_approval", SESSION_STATES.WAITING_APPROVAL],
89
+ // A resumed turn with UNKNOWN-outcome side effects blocks on confirmation.
90
+ ["uncertain_side_effect", SESSION_STATES.WAITING_APPROVAL],
91
+ ["uncertain-side-effect", SESSION_STATES.WAITING_APPROVAL],
89
92
  // supervisor liveness — a running process that vanished is a failure.
90
93
  ["lost", SESSION_STATES.FAILED],
91
94
  // IDE connection / transport
@@ -2210,17 +2210,29 @@ const EDIT_DIAGNOSTICS_WALL_MS = 6000;
2210
2210
  * never cold-starts a server that isn't there) and fully bounded in time.
2211
2211
  * Disable entirely with CC_EDIT_DIAGNOSTICS=0.
2212
2212
  */
2213
- async function _postEditDiagnostics(filePath, cwd) {
2213
+ async function _postEditDiagnostics(filePath, cwd, additionalDirectories) {
2214
2214
  if (process.env.CC_EDIT_DIAGNOSTICS === "0") return null;
2215
+ // Multi-root workspace: resolve the server against the root that CONTAINS
2216
+ // the edited file (mirrors the code_intelligence handler) — editing a file
2217
+ // inside an `--add-dir` root probes/uses THAT project's language server
2218
+ // instead of degrading to "no diagnostics" via the cwd root. Single-root
2219
+ // sessions (no additionalDirectories) resolve to cwd exactly as before.
2220
+ let root;
2221
+ try {
2222
+ root = pickRootForFile(
2223
+ path.resolve(cwd || process.cwd(), filePath),
2224
+ workspaceRootsFor(cwd, additionalDirectories),
2225
+ );
2226
+ } catch {
2227
+ root = path.resolve(cwd || process.cwd());
2228
+ }
2215
2229
  let hasServer = false;
2216
2230
  try {
2217
2231
  const { languageIdForFile, resolveServer } =
2218
2232
  await import("../lib/lsp/lsp-server-registry.js");
2219
2233
  const languageId = languageIdForFile(filePath);
2220
2234
  if (!languageId) return null; // unsupported file type — no LSP, no cost
2221
- hasServer = Boolean(
2222
- resolveServer(languageId, path.resolve(cwd || process.cwd())),
2223
- );
2235
+ hasServer = Boolean(resolveServer(languageId, root));
2224
2236
  } catch {
2225
2237
  return null;
2226
2238
  }
@@ -2228,7 +2240,7 @@ async function _postEditDiagnostics(filePath, cwd) {
2228
2240
 
2229
2241
  const query = (async () => {
2230
2242
  try {
2231
- const ci = await _getSharedCodeIntel(cwd);
2243
+ const ci = await _getSharedCodeIntel(root);
2232
2244
  const res = await ci.refreshFile(filePath, { timeoutMs: 3000 });
2233
2245
  if (!res || res.available === false) return null;
2234
2246
  const diags = (res.diagnostics || []).filter(
@@ -2263,8 +2275,17 @@ async function _postEditDiagnostics(filePath, cwd) {
2263
2275
  }
2264
2276
 
2265
2277
  /** Merge post-edit diagnostics into a successful edit result (no-op if none). */
2266
- async function _withPostEditDiagnostics(result, filePath, cwd) {
2267
- const newDiagnostics = await _postEditDiagnostics(filePath, cwd);
2278
+ async function _withPostEditDiagnostics(
2279
+ result,
2280
+ filePath,
2281
+ cwd,
2282
+ additionalDirectories,
2283
+ ) {
2284
+ const newDiagnostics = await _postEditDiagnostics(
2285
+ filePath,
2286
+ cwd,
2287
+ additionalDirectories,
2288
+ );
2268
2289
  return newDiagnostics ? { ...result, newDiagnostics } : result;
2269
2290
  }
2270
2291
 
@@ -2570,6 +2591,7 @@ async function executeToolInner(
2570
2591
  { success: true, path: filePath, size: wrote.size },
2571
2592
  filePath,
2572
2593
  cwd,
2594
+ additionalDirectories,
2573
2595
  ),
2574
2596
  filePath,
2575
2597
  cwd,
@@ -2668,6 +2690,7 @@ async function executeToolInner(
2668
2690
  },
2669
2691
  filePath,
2670
2692
  cwd,
2693
+ additionalDirectories,
2671
2694
  ),
2672
2695
  filePath,
2673
2696
  cwd,
@@ -2731,6 +2754,7 @@ async function executeToolInner(
2731
2754
  },
2732
2755
  filePath,
2733
2756
  cwd,
2757
+ additionalDirectories,
2734
2758
  ),
2735
2759
  filePath,
2736
2760
  cwd,
@@ -703,6 +703,15 @@ export async function runAgentHeadless(options = {}, deps = {}) {
703
703
  // mid-flight does NOT blindly replay an effect that may already have landed.
704
704
  // Only active when persisting (an ephemeral/one-shot run can't be resumed);
705
705
  // the byte-for-byte default path is untouched when no dangerous tool runs.
706
+ // Background phase reporter (P0 state-machine producer): when this run IS a
707
+ // background agent's turn child (CC_BACKGROUND_AGENT_ID set by the worker),
708
+ // human-blocking windows are surfaced into the shared state file. Created
709
+ // here — before the resume reconcile below — so an UNKNOWN-outcome side
710
+ // effect found on resume can be reported too. For every non-background run
711
+ // this is a disabled no-op.
712
+ const _bgPhase =
713
+ deps.backgroundPhaseReporter || createBackgroundPhaseReporter();
714
+
706
715
  const sideEffectRunNonce = String(deps.now ? deps.now() : Date.now());
707
716
  let sideEffectLedger = new SideEffectLedger({ clock: deps.now || null });
708
717
  let sideEffectSeq = 0;
@@ -765,6 +774,11 @@ export async function runAgentHeadless(options = {}, deps = {}) {
765
774
  writeErr(
766
775
  `⚠ ${plan.inspect.length} interrupted side-effect(s) need verification before replay (resume ${resumeId}).\n`,
767
776
  );
777
+ // P0 state machine: a background child resuming with UNKNOWN-outcome
778
+ // ops advertises `uncertain_side_effect` (dashboard: Needs input) —
779
+ // someone must confirm before any replay. No-op unless this run is a
780
+ // background turn child; best-effort by construction.
781
+ _bgPhase.reportUncertainSideEffects(plan.inspect.length);
768
782
  }
769
783
  } catch {
770
784
  resumeSideEffectContext = null;
@@ -1156,15 +1170,9 @@ export async function runAgentHeadless(options = {}, deps = {}) {
1156
1170
  // IDE context is optional polish — never fail the run over it.
1157
1171
  }
1158
1172
 
1159
- // Background phase reporter (P0 state-machine producer): when this run IS a
1160
- // background agent's turn child (CC_BACKGROUND_AGENT_ID set by the worker),
1161
- // the human-blocking confirmers below surface their pending window as
1162
- // `phase: "waiting_permission"` + `pendingApprovals` in the shared state
1163
- // file so the dashboard's "Needs input" group is real, not inferred. For
1164
- // every non-background run this is a disabled no-op (wrapConfirmer returns
1165
- // the same function object).
1166
- const _bgPhase =
1167
- deps.backgroundPhaseReporter || createBackgroundPhaseReporter();
1173
+ // (`_bgPhase` — the background phase reporter — is created before the resume
1174
+ // reconcile block above; the confirmers below reuse it to surface their
1175
+ // pending window as `phase: "waiting_permission"` + `pendingApprovals`.)
1168
1176
 
1169
1177
  // --permission-prompt-tool: route every CONFIRM-tier approval to an MCP tool
1170
1178
  // (loaded via --mcp-config) instead of headless fail-closed. Overrides the
@@ -1328,6 +1336,31 @@ export async function runAgentHeadless(options = {}, deps = {}) {
1328
1336
  enabledToolNames,
1329
1337
  disabledTools,
1330
1338
  iterationBudget: budget,
1339
+ // `ask_user_question` in a BACKGROUND turn child (P0 state-machine
1340
+ // producer for `needs_input`): there is no live channel to a human
1341
+ // mid-turn, but the question IS the structured needs-input signal. Park
1342
+ // it — record `phase: "needs_input"` + the question in the shared state
1343
+ // file (dashboard "Needs input" gets real data; the user's reply arrives
1344
+ // as the next attach prompt) and tell the model to end the turn. A
1345
+ // non-background run supplies no interaction → the handler's existing
1346
+ // `user_not_reachable` path stays byte-identical.
1347
+ ...(_bgPhase.enabled
1348
+ ? {
1349
+ interaction: {
1350
+ askUser: async ({ question, options: qOptions } = {}) => {
1351
+ _bgPhase.reportQuestion({
1352
+ question: typeof question === "string" ? question : "",
1353
+ options: Array.isArray(qOptions) ? qOptions : null,
1354
+ });
1355
+ throw new Error(
1356
+ "question parked for the user (background session). End this " +
1357
+ "turn now — the user's reply will arrive as the next " +
1358
+ "message. Do not repeat the question.",
1359
+ );
1360
+ },
1361
+ },
1362
+ }
1363
+ : {}),
1331
1364
  // --mcp-config wiring: tool defs for the LLM + dispatch map + live client.
1332
1365
  mcpClient: mcp?.mcpClient || null,
1333
1366
  extraToolDefinitions: mcp?.extraToolDefinitions || undefined,
@@ -75,6 +75,15 @@ import {
75
75
  rebuildMessages as jsonlRebuildMessages,
76
76
  sessionExists as jsonlSessionExists,
77
77
  } from "../harness/jsonl-session-store.js";
78
+ import {
79
+ reconcileSideEffects,
80
+ classifyToolSideEffect,
81
+ } from "../lib/side-effect-ledger.js";
82
+ import {
83
+ loadSideEffectLedger,
84
+ persistSideEffectLedger,
85
+ } from "../lib/side-effect-ledger-store.js";
86
+ import { operationIdempotencyKey } from "../lib/idempotency.js";
78
87
  import { getPlanModeManager } from "../lib/plan-mode.js";
79
88
 
80
89
  /**
@@ -527,6 +536,46 @@ export async function* readJsonLines(input, opts = {}) {
527
536
  if (!overflow && buf.trim()) yield buf;
528
537
  }
529
538
 
539
+ /**
540
+ * P0-2: on a stream resume, surface any irreversible tool that was in flight
541
+ * when the prior run died. Same reconcile + wording as the single-prompt
542
+ * runner (headless-runner.js) and the WS bridge (session-protocol.js): a
543
+ * `started`-but-unsettled op's outcome is UNKNOWN, so the model must VERIFY
544
+ * before any replay rather than silently re-issue the effect. Returns
545
+ * {count, items, notice} or null when nothing needs verification — a clean
546
+ * (or absent) ledger keeps the resume byte-identical to today.
547
+ */
548
+ function buildSideEffectRecovery(ledger) {
549
+ try {
550
+ const plan = reconcileSideEffects(ledger);
551
+ if (!plan.inspect.length) return null;
552
+ const items = plan.plans
553
+ .filter((p) => p.action === "inspect")
554
+ .map((p) => {
555
+ const op = ledger.get(p.opId) || {};
556
+ return {
557
+ kind: op.kind || "unknown",
558
+ key: op.key || null,
559
+ reason: p.reason,
560
+ };
561
+ });
562
+ const notice =
563
+ "Recovery notice — the previous run was interrupted while these " +
564
+ "irreversible operations were in flight; their outcome is UNKNOWN. Do " +
565
+ "NOT blindly re-run them. Verify whether each already took effect before " +
566
+ "repeating it, and ask the user if unsure:\n" +
567
+ items
568
+ .map(
569
+ (it) =>
570
+ ` • [${it.kind}]${it.key ? ` (${it.key})` : ""} — ${it.reason}`,
571
+ )
572
+ .join("\n");
573
+ return { count: items.length, items, notice };
574
+ } catch {
575
+ return null;
576
+ }
577
+ }
578
+
530
579
  /**
531
580
  * Run a single turn through the core agent loop, emitting NDJSON events.
532
581
  * Returns the turn outcome so the caller can grow history + the result line.
@@ -534,7 +583,7 @@ export async function* readJsonLines(input, opts = {}) {
534
583
  async function runTurn(
535
584
  messages,
536
585
  loopOptions,
537
- { runLoop, emit, costBudget, nextToolUseId },
586
+ { runLoop, emit, costBudget, nextToolUseId, sideEffects },
538
587
  ) {
539
588
  const usage = {
540
589
  input_tokens: 0,
@@ -546,6 +595,10 @@ async function runTurn(
546
595
  let finalText = "";
547
596
  let endReason = "complete";
548
597
  let stopForCost = false;
598
+ // P0-2: the side-effect op currently in flight (at most one — the loop runs
599
+ // tools serially). Non-null only between a dangerous tool's tool-executing
600
+ // and its tool-result.
601
+ let currentSideEffectOpId = null;
549
602
 
550
603
  // Collapse consecutive same-role turns before the model call ONLY when the
551
604
  // caller flags a resume-degenerate transcript (`mergeRoles`): a resumed
@@ -583,10 +636,52 @@ async function runTurn(
583
636
  tool: event.tool,
584
637
  args: event.args,
585
638
  });
639
+ // P0-2: record an irreversible effect as STARTED (persisted before it
640
+ // settles) so a crash before the matching tool-result leaves a
641
+ // reconcilable "in flight" marker instead of a silent replay — mirrors
642
+ // the single-prompt runner and the WS bridge. `sideEffects` is null
643
+ // when the session doesn't persist, keeping that path byte-identical.
644
+ currentSideEffectOpId = null;
645
+ if (sideEffects) {
646
+ const se = classifyToolSideEffect(event.tool, event.args);
647
+ if (se) {
648
+ const opId = sideEffects.nextOpId();
649
+ currentSideEffectOpId = opId;
650
+ sideEffects.ledger
651
+ .prepare(opId, {
652
+ kind: se.kind,
653
+ key: se.key,
654
+ // Content-addressed key: a resumed replay of the SAME effect
655
+ // derives the SAME key, so an external provider can de-dupe
656
+ // and countDuplicateCommittedEffects can measure `0` repeats.
657
+ meta: {
658
+ tool: event.tool,
659
+ idempotencyKey: operationIdempotencyKey({
660
+ tool: event.tool,
661
+ args: event.args,
662
+ }),
663
+ },
664
+ })
665
+ .start(opId);
666
+ sideEffects.persist();
667
+ }
668
+ }
586
669
  break;
587
670
  }
588
671
  case "tool-result": {
589
672
  const err = event.error || event.result?.error || null;
673
+ // P0-2: settle the in-flight side-effect (commit on success, fail on
674
+ // a clean error) and persist the updated ledger snapshot.
675
+ if (sideEffects && currentSideEffectOpId) {
676
+ if (err)
677
+ sideEffects.ledger.fail(
678
+ currentSideEffectOpId,
679
+ String(err).slice(0, 200),
680
+ );
681
+ else sideEffects.ledger.commit(currentSideEffectOpId);
682
+ sideEffects.persist();
683
+ currentSideEffectOpId = null;
684
+ }
590
685
  // The loop runs tools serially, so this result settles the most
591
686
  // recent tool_use (same adjacency rule the is_error attribution
592
687
  // below has always used).
@@ -846,6 +941,39 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
846
941
  // id is kept for event correlation only; nothing is written or replayed).
847
942
  const persist = Boolean(options.sessionId) && options.ephemeral !== true;
848
943
 
944
+ // ── P0-2: crash-safe side-effect ledger — the stream twin of the
945
+ // single-prompt runner (headless-runner.js) and the WS bridge
946
+ // (ws-agent-handler.js). Dangerous tools (file writes, opaque shell,
947
+ // git push, publish/schedule/notify/browser actions) are recorded
948
+ // prepare→start→commit|fail with the snapshot persisted BEFORE the effect
949
+ // settles, so an IDE chat-panel worker killed mid-flight is surfaced for
950
+ // verification on resume instead of being blindly replayed. Only active when
951
+ // persisting (an anonymous/--ephemeral stream can't be resumed); a run that
952
+ // touches no dangerous tool writes nothing.
953
+ const loadLedger = deps.loadSideEffectLedger || loadSideEffectLedger;
954
+ const persistLedger = deps.persistSideEffectLedger || persistSideEffectLedger;
955
+ let sideEffectLedger = null;
956
+ if (persist) {
957
+ try {
958
+ sideEffectLedger = loadLedger(sessionId);
959
+ } catch {
960
+ // ledger is best-effort — never block the stream over it
961
+ sideEffectLedger = null;
962
+ }
963
+ }
964
+ let sideEffectSeq = 0;
965
+ // Run-scoped nonce keeps op ids unique even when two runs resume the same
966
+ // session (prepare() is idempotent on opId, so a collision would silently
967
+ // drop the newer record).
968
+ const sideEffectRunNonce = String(deps.now ? deps.now() : Date.now());
969
+ const sideEffects = sideEffectLedger
970
+ ? {
971
+ ledger: sideEffectLedger,
972
+ persist: () => persistLedger(sessionId, sideEffectLedger),
973
+ nextOpId: () => `${sideEffectRunNonce}:${sideEffectSeq++}`,
974
+ }
975
+ : null;
976
+
849
977
  // ── Interactive approvals (--interactive-approvals; chat-panel UX) ────────
850
978
  // CONFIRM-tier decisions (risky shell via the ApprovalGate, settings/hook
851
979
  // `ask`) normally fail closed in headless. With this opt-in they become a
@@ -1163,9 +1291,21 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
1163
1291
  // Resume: replay the persisted conversation (fresh system prompt always
1164
1292
  // leads; persisted system turns are dropped, mirroring runAgentHeadless).
1165
1293
  let resumedMessages = 0;
1294
+ let sideEffectRecovery = null;
1166
1295
  if (persist) {
1167
1296
  try {
1168
1297
  if (store.sessionExists(sessionId)) {
1298
+ // P0-2: reconcile the crash-safe ledger on resume (parity with
1299
+ // runAgentHeadless + the WS bridge). The verify-before-replay system
1300
+ // note goes BEFORE the replayed history — the same slot the
1301
+ // single-prompt runner uses — which also keeps the trailing-role
1302
+ // resume-degeneracy check below intact.
1303
+ if (sideEffectLedger) {
1304
+ sideEffectRecovery = buildSideEffectRecovery(sideEffectLedger);
1305
+ }
1306
+ if (sideEffectRecovery) {
1307
+ messages.push({ role: "system", content: sideEffectRecovery.notice });
1308
+ }
1169
1309
  const history = (store.rebuildMessages(sessionId) || []).filter(
1170
1310
  (m) => m && m.role !== "system",
1171
1311
  );
@@ -1225,6 +1365,22 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
1225
1365
  resumed_messages: resumedMessages,
1226
1366
  });
1227
1367
 
1368
+ // P0-2 visible recovery notice: same `raw` info-line contract as
1369
+ // provider_fallback / version_skew (the shipped IDE panels map `raw` → an
1370
+ // info line), so both panels render it with zero IDE changes. Emitted right
1371
+ // after `init` so consumers keep seeing init as the first line; absent
1372
+ // entirely on a clean (or ledger-free) resume.
1373
+ if (sideEffectRecovery) {
1374
+ emit({
1375
+ type: "raw",
1376
+ subtype: "side_effect_recovery",
1377
+ text: `⚠️ ${sideEffectRecovery.count} interrupted side-effect(s) need verification before replay (resume ${sessionId}).`,
1378
+ count: sideEffectRecovery.count,
1379
+ items: sideEffectRecovery.items,
1380
+ session_id: sessionId,
1381
+ });
1382
+ }
1383
+
1228
1384
  // Goal binding (cc goal, Phase 1) — resolved once and injected on every turn.
1229
1385
  // `--goal <id>` binds explicitly; `--goal` with no value auto-resolves.
1230
1386
  let goalPrepareCallFn;
@@ -2010,7 +2166,7 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
2010
2166
  // Resume-degenerate role merge for the first live model call only.
2011
2167
  mergeRoles: mergeRolesThisTurn,
2012
2168
  },
2013
- { runLoop, emit, costBudget, nextToolUseId },
2169
+ { runLoop, emit, costBudget, nextToolUseId, sideEffects },
2014
2170
  );
2015
2171
  } catch (err) {
2016
2172
  currentAbort = null;
@@ -24,6 +24,7 @@ import {
24
24
  writeBackgroundAgentState,
25
25
  } from "../lib/background-agent-supervisor.js";
26
26
  import { startBackgroundSessionServer } from "../lib/background-session-transport.js";
27
+ import { idlePhaseFor } from "../lib/background-agent-phase.js";
27
28
 
28
29
  const jobFile = process.argv[2];
29
30
  let job;
@@ -128,6 +129,11 @@ function startTurn(argv, promptText) {
128
129
  // if the previous turn's child was hard-killed mid-approval (the child's
129
130
  // phase reporter clears on settle, but SIGKILL never settles).
130
131
  pendingApprovals: 0,
132
+ // The incoming prompt IS the user's reply to any parked question, and the
133
+ // uncertain-side-effect annotation only covers the verify turn it was
134
+ // raised in — both reset at the turn boundary.
135
+ pendingQuestion: null,
136
+ uncertainSideEffects: 0,
131
137
  agentPid: child.pid,
132
138
  // Identity anchor for the supervisor's orphan reclaim (Gap 2): pairs
133
139
  // with agentPid the way startedAt pairs with the worker pid, so a
@@ -167,13 +173,20 @@ function maybeContinue() {
167
173
  return;
168
174
  }
169
175
  if (server && server.clientCount() > 0) {
170
- phase = "idle";
176
+ // Parked between turns. Normally `idle` — but a turn that parked an
177
+ // unanswered ask_user_question stays `needs_input` until the user's reply
178
+ // starts the next turn (the reply prompt clears pendingQuestion).
179
+ phase = idlePhaseFor(current);
171
180
  mergeState({
172
181
  phase,
173
- // Between turns nothing can be pending — the approval-owning child has
174
- // exited. Honest zero keeps a killed-mid-approval count from parking
175
- // the session in "Needs input" forever.
182
+ // Between turns no APPROVAL can be pending — the approval-owning child
183
+ // has exited. Honest zero keeps a killed-mid-approval count from
184
+ // parking the session in "Needs input" forever. (A parked QUESTION is
185
+ // different: it awaits the user, so pendingQuestion survives idle.)
176
186
  pendingApprovals: 0,
187
+ // The verify turn for any uncertain side effects has now run its
188
+ // course — the annotation does not outlive it.
189
+ uncertainSideEffects: 0,
177
190
  agentPid: null,
178
191
  agentStartedAt: null,
179
192
  heartbeatAt: Date.now(),
@@ -1 +0,0 @@
1
- import{I as v,P as u,J as S,U as n,R as a,S as y,K as C,Q as x,V as w,_ as B,b as s}from"./vendor-Bn1JzzAn.js";import{_ as k}from"./index-D1xIbAxQ.js";import"./icons-DG5UD-6K.js";const N={__name:"ChatBubbleRenderer",props:{event:{type:Object,required:!0}},setup(c,{expose:d}){d();const t=c,r=s(()=>{const e=t.event.content||{};return e.text||e.body||e.message||e.title||JSON.stringify(e).slice(0,200)}),i=s(()=>{const e=t.event.content||{};return e.from||e.sender||e.senderName||t.event.actor||"(unknown)"}),l=s(()=>{const e=(t.event.actor||"").toLowerCase();return e.includes("self")||e==="me"||e.endsWith("_self")}),o=s(()=>{const e=t.event.source.adapter||"";return e.startsWith("messaging-qq")?"magenta":e==="wechat"?"green":"blue"}),m=s(()=>{if(!t.event.occurredAt)return"";try{const e=new Date(t.event.occurredAt),p=e.getFullYear(),b=String(e.getMonth()+1).padStart(2,"0"),f=String(e.getDate()).padStart(2,"0"),g=String(e.getHours()).padStart(2,"0"),h=String(e.getMinutes()).padStart(2,"0");return`${p}-${b}-${f} ${g}:${h}`}catch{return""}}),_={props:t,messageText:r,actorLabel:i,isMine:l,adapterColor:o,formattedTime:m,computed:s};return Object.defineProperty(_,"__isScriptSetup",{enumerable:!1,value:!0}),_}},T={class:"bubble"},M={class:"meta"},O={class:"actor"},R={class:"time"},V={class:"body"};function q(c,d,t,r,i,l){const o=v("a-tag");return u(),S("div",{class:B(["chat-row",{mine:r.isMine}])},[n("div",T,[n("div",M,[n("span",O,a(r.actorLabel),1),n("span",R,a(r.formattedTime),1)]),n("div",V,a(r.messageText),1),t.event.source.adapter?(u(),y(o,{key:0,class:"src",color:r.adapterColor},{default:C(()=>[x(a(t.event.source.adapter),1)]),_:1},8,["color"])):w("v-if",!0)])],2)}const A=k(N,[["render",q],["__scopeId","data-v-49238629"],["__file","/tmp/cc-web-panel-tGOYbB/repo/packages/web-panel/src/components/pdh/renderers/ChatBubbleRenderer.vue"]]);export{A as default};
@@ -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-D1xIbAxQ.js";import{a8 as y,M as B}from"./icons-DG5UD-6K.js";const M={__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 B},get FolderOutlined(){return y}};return Object.defineProperty(s,"__isScriptSetup",{enumerable:!1,value:!0}),s}},O={class:"mobile-projects-placeholder"},x={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",O,[l(j,null,{default:r(()=>[l(P,{description:!1},{image:r(()=>[l(e.MobileOutlined,{style:{fontSize:"64px",color:"#bfbfbf"}})]),default:r(()=>[o("h2",x,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(M,[["render",T],["__scopeId","data-v-9262cc45"],["__file","/tmp/cc-web-panel-tGOYbB/repo/packages/web-panel/src/views/MobileProjects.vue"]]);export{N as default};
@@ -1,3 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./xterm-BZcWGsqw.js","./markdown-CsiA8-E5.js","./markdown-Dfs9RUU9.css","./addon-fit-CK6X9sAG.js","./xterm-DFuMZ0ql.css"])))=>i.map(i=>d[i]);
2
- import{u as fe,_ as me,d as V,e as U}from"./index-D1xIbAxQ.js";import{I as z,J as x,U as _,Q as B,S as J,K as T,V as R,c as A,F as K,Z as Q,R as E,o as Z,f as H,w as $,n as D,b as ee,r as S,P as y,_ as ve,a2 as we,a3 as he,a4 as _e}from"./vendor-Bn1JzzAn.js";import{R as ye,b as pe,at as ge}from"./icons-DG5UD-6K.js";const k=new Map,C=new Map,N=new Set,F=new Set;let te=!1;function xe(c){te||(te=!0,c.onMessage(o=>{if(!(!o||typeof o.type!="string")){if(o.type==="terminal.stdout"){const{sessionId:s,data:e,seq:i}=o.payload||{};if(!s)return;let w;try{const d=atob(e||""),f=new Uint8Array(d.length);for(let n=0;n<d.length;n++)f[n]=d.charCodeAt(n);w=new TextDecoder("utf-8").decode(f)}catch{w=""}const u={sessionId:s,data:w,seq:i};k.get(s)?.forEach(d=>d(u)),N.forEach(d=>d(u))}else if(o.type==="terminal.exit"){const{sessionId:s,exitCode:e,signal:i}=o.payload||{};if(!s)return;const w={sessionId:s,exitCode:e,signal:i};C.get(s)?.forEach(u=>u(w)),F.forEach(u=>u(w))}}}))}function ne(c){const o=new TextEncoder().encode(c);let s="";for(let e=0;e<o.length;e++)s+=String.fromCharCode(o[e]);return btoa(s)}function ae(c){const o=atob(c||""),s=new Uint8Array(o.length);for(let e=0;e<o.length;e++)s[e]=o.charCodeAt(e);return new TextDecoder("utf-8").decode(s)}function oe(){const c=fe();xe(c);async function o(n={}){const a=await c.sendRaw({type:"terminal.create",payload:{shell:n.shell,cwd:n.cwd,env:n.env,cols:n.cols,rows:n.rows}});if(a.ok===!1)throw new Error(a.error||"terminal_create_failed");return a.result??a}async function s(){const n=await c.sendRaw({type:"terminal.list",payload:{}});if(n.ok===!1)throw new Error(n.error||"terminal_list_failed");const a=n.result??n;return Array.isArray(a.sessions)?a.sessions:[]}async function e(n,a){const r=await c.sendRaw({type:"terminal.stdin",payload:{sessionId:n,data:ne(String(a))}});if(r.ok===!1)throw new Error(r.error||"terminal_stdin_failed");return r.result??r}async function i(n,a,r){const h=await c.sendRaw({type:"terminal.resize",payload:{sessionId:n,cols:a,rows:r}});if(h.ok===!1)throw new Error(h.error||"terminal_resize_failed");return h.result??h}async function w(n){const a=await c.sendRaw({type:"terminal.close",payload:{sessionId:n}});if(a.ok===!1)throw new Error(a.error||"terminal_close_failed");return a.result??a}async function u(n,a=0){const r=await c.sendRaw({type:"terminal.history",payload:{sessionId:n,fromSeq:a}});if(r.ok===!1)throw new Error(r.error||"terminal_history_failed");const h=r.result??r;return{truncated:!!h.truncated,chunks:(h.chunks||[]).map(L=>({seq:L.seq,data:ae(L.data)}))}}function d(n,a){return n?(k.has(n)||k.set(n,new Set),k.get(n).add(a),()=>{k.get(n)?.delete(a),k.get(n)?.size===0&&k.delete(n)}):(N.add(a),()=>N.delete(a))}function f(n,a){return n?(C.has(n)||C.set(n,new Set),C.get(n).add(a),()=>{C.get(n)?.delete(a),C.get(n)?.size===0&&C.delete(n)}):(F.add(a),()=>F.delete(a))}return{create:o,list:s,stdin:e,resize:i,close:w,history:u,onStdout:d,onExit:f,_internal:{stdoutSubs:k,exitSubs:C,toBase64Utf8:ne,fromBase64Utf8:ae}}}const Se={__name:"Terminal",setup(c,{expose:o}){o();const s=oe(),e=S([]),i=S(null),w=S("pwsh"),u=S(!1),d=S(!1),f=S(""),n=S(""),a=S([]),r=[{value:"pwsh",label:"PowerShell"},{value:"cmd",label:"CMD"},{value:"bash",label:"Bash"},{value:"wsl",label:"WSL"}],h=ee(()=>e.value.find(t=>t.id===i.value));function L(t){return t?t.slice(0,8):""}let O=null,M=null;async function I(){if(O)return{xtermMod:O,fitAddonMod:M};try{O=await U(()=>import("./xterm-BZcWGsqw.js").then(t=>t.x),__vite__mapDeps([0,1,2]),import.meta.url),M=await U(()=>import("./addon-fit-CK6X9sAG.js").then(t=>t.a),__vite__mapDeps([3,1,2]),import.meta.url),await U(()=>Promise.resolve({}),__vite__mapDeps([4]),import.meta.url)}catch(t){throw n.value="xterm 资源加载失败:"+(t?.message||"未知错误"),t}return{xtermMod:O,fitAddonMod:M}}async function q(t){await D();const{xtermMod:l,fitAddonMod:p}=await I(),b=a.value.find(v=>v?.dataset?.sessionId===t.id);if(!b)return;const m=new l.Terminal({cursorBlink:!0,fontFamily:'Consolas, "Courier New", monospace',fontSize:13,theme:{background:"#1e1e1e",foreground:"#d4d4d4"},convertEol:!1}),P=new p.FitAddon;m.loadAddon(P),m.open(b);try{P.fit()}catch{}t.xterm=m,t.fitAddon=P;const ie=m.onData(v=>{s.stdin(t.id,v).catch(g=>{String(g?.message||"").includes("dangerous_keyword_blocked")?V.warning("该命令被桌面端拦截(高危关键字)"):V.error("stdin 失败: "+(g?.message||g))})}),ce=s.onStdout(t.id,({data:v,seq:g})=>{t.lastSeq=g,m.write(v)}),de=s.onExit(t.id,({exitCode:v,signal:g})=>{t.alive=!1,t.exitCode=v,t.signal=g,m.writeln(`\r
3
- \x1B[33m[session exited, code=${v}, signal=${g??"-"}]\x1B[0m`)});t.offs=()=>{try{ie.dispose?.()}catch{}ce(),de()};try{const{chunks:v,truncated:g}=await s.history(t.id,0);g&&m.writeln("\x1B[2m[history truncated — earlier output was evicted]\x1B[0m");for(const G of v)m.write(G.data),t.lastSeq=G.seq}catch(v){m.writeln(`\x1B[31m[history fetch failed: ${v?.message||v}]\x1B[0m`)}const j=new ResizeObserver(()=>{try{P.fit(),s.resize(t.id,m.cols,m.rows).catch(()=>{})}catch{}});j.observe(b);const ue=t.offs;t.offs=()=>{try{j.disconnect()}catch{}ue()}}async function re(){u.value=!0,f.value="";try{const t=await s.create({shell:w.value,cols:80,rows:24}),l={id:t.sessionId,shell:t.shell,cwd:"",alive:!0,lastSeq:0,exitCode:null,xterm:null,fitAddon:null,offs:()=>{}};e.value.push(l),i.value=l.id,await q(l)}catch(t){f.value=t?.message||String(t)}finally{u.value=!1}}async function se(t){try{await s.close(t)}catch(l){f.value=l?.message||String(l)}setTimeout(()=>W(t),500)}function W(t){const l=e.value.findIndex(b=>b.id===t);if(l===-1)return;const p=e.value[l];try{p.offs?.()}catch{}try{p.xterm?.dispose?.()}catch{}e.value.splice(l,1),i.value===t&&(i.value=e.value[0]?.id||null)}function le(t){i.value=t,D(()=>{const l=e.value.find(p=>p.id===t);try{l?.fitAddon?.fit()}catch{}})}async function X(){d.value=!0,f.value="";try{const t=await s.list();for(const l of t){const p=e.value.find(m=>m.id===l.id);if(p){p.alive=l.alive,p.lastSeq=l.lastSeq;continue}const b={id:l.id,shell:l.shell,cwd:l.cwd,alive:l.alive,lastSeq:l.lastSeq,exitCode:null,xterm:null,fitAddon:null,offs:()=>{}};e.value.push(b),await q(b)}!i.value&&e.value.length>0&&(i.value=e.value[0].id)}catch(t){f.value=t?.message||String(t)}finally{d.value=!1}}Z(async()=>{await X()}),H(()=>{for(const t of e.value){try{t.offs?.()}catch{}try{t.xterm?.dispose?.()}catch{}}}),$(i,()=>{D(()=>{const t=h.value;try{t?.fitAddon?.fit()}catch{}})});const Y={term:s,sessions:e,activeId:i,newShell:w,creating:u,loadingList:d,error:f,warning:n,xtermContainers:a,shellOptions:r,active:h,shortId:L,get xtermMod(){return O},set xtermMod(t){O=t},get fitAddonMod(){return M},set fitAddonMod(t){M=t},loadXterm:I,mountXterm:q,onCreate:re,onClose:se,removeSession:W,activate:le,refreshList:X,ref:S,computed:ee,onMounted:Z,onBeforeUnmount:H,nextTick:D,watch:$,get message(){return V},get PlusOutlined(){return ge},get CloseOutlined(){return pe},get ReloadOutlined(){return ye},get useTerminal(){return oe}};return Object.defineProperty(Y,"__isScriptSetup",{enumerable:!1,value:!0}),Y}},be={class:"terminal-page"},ke={class:"terminal-header"},Ce={class:"page-sub"},Ae={class:"terminal-body"},Ee={class:"session-tabs"},Oe=["onClick"],Te={class:"session-shell"},Re={class:"session-id"},Me={key:0,class:"session-empty"},ze={class:"xterm-host"},Le=["data-session-id"],Pe={key:0,class:"xterm-placeholder"},Be={key:1,class:"terminal-footer"},De={key:0,class:"footer-exit"};function qe(c,o,s,e,i,w){const u=z("a-tag"),d=z("a-select"),f=z("a-button"),n=z("a-space"),a=z("a-alert");return y(),x("div",be,[_("div",ke,[_("div",null,[o[3]||(o[3]=_("h2",{class:"page-title"},"远程终端",-1)),_("p",Ce,[o[2]||(o[2]=B(" 桌面端托管的 PTY 会话;Android 端可远程操控同一通道 ",-1)),e.warning?(y(),J(u,{key:0,color:"orange",style:{"margin-left":"8px"}},{default:T(()=>[B(E(e.warning),1)]),_:1})):R("v-if",!0)])]),A(n,null,{default:T(()=>[A(d,{value:e.newShell,"onUpdate:value":o[0]||(o[0]=r=>e.newShell=r),style:{width:"130px"},size:"small",options:e.shellOptions},null,8,["value"]),A(f,{type:"primary",size:"small",loading:e.creating,onClick:e.onCreate},{icon:T(()=>[A(e.PlusOutlined)]),default:T(()=>[o[4]||(o[4]=B(" 新会话 ",-1))]),_:1},8,["loading"]),A(f,{size:"small",loading:e.loadingList,onClick:e.refreshList},{icon:T(()=>[A(e.ReloadOutlined)]),default:T(()=>[o[5]||(o[5]=B(" 刷新 ",-1))]),_:1},8,["loading"])]),_:1})]),e.error?(y(),J(a,{key:0,message:e.error,type:"error","show-icon":"",closable:"",style:{"margin-bottom":"12px"},onClose:o[1]||(o[1]=r=>e.error="")},null,8,["message"])):R("v-if",!0),_("div",Ae,[_("div",Ee,[(y(!0),x(K,null,Q(e.sessions,r=>(y(),x("div",{key:r.id,class:ve(["session-tab",{active:r.id===e.activeId,dead:!r.alive}]),onClick:h=>e.activate(r.id)},[_("span",Te,E(r.shell),1),_("span",Re,E(e.shortId(r.id)),1),A(e.CloseOutlined,{class:"session-close",onClick:we(h=>e.onClose(r.id),["stop"])},null,8,["onClick"])],10,Oe))),128)),e.sessions.length===0?(y(),x("div",Me,' 点击 "新会话" 创建第一个终端 ')):R("v-if",!0)]),_("div",ze,[(y(!0),x(K,null,Q(e.sessions,r=>he((y(),x("div",{key:r.id,ref_for:!0,ref:"xtermContainers","data-session-id":r.id,class:"xterm-container"},null,8,Le)),[[_e,r.id===e.activeId]])),128)),e.sessions.length===0?(y(),x("div",Pe,[...o[6]||(o[6]=[_("span",null,"无活跃会话",-1)])])):R("v-if",!0)])]),e.active?(y(),x("div",Be,[_("span",null,E(e.active.shell)+" · "+E(e.active.cwd||"(默认 cwd)")+" · seq "+E(e.active.lastSeq),1),e.active.alive?R("v-if",!0):(y(),x("span",De,"已退出 (code="+E(e.active.exitCode??"-")+")",1))])):R("v-if",!0)])}const Fe=me(Se,[["render",qe],["__scopeId","data-v-65366a29"],["__file","/tmp/cc-web-panel-tGOYbB/repo/packages/web-panel/src/views/Terminal.vue"]]);export{Fe as default};
@@ -1 +0,0 @@
1
- import{Q as r}from"./index-D1xIbAxQ.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-stTnDJ9y.js";import{a0 as t}from"./index-D1xIbAxQ.js";import"./vendor-Bn1JzzAn.js";import"./responsiveObserve-DUafj9pU.js";import"./useFlexGapSupport-ChecYwC8.js";import"./styleChecker-qO5aCjyk.js";import"./index-DhZgULsY.js";import"./icons-DG5UD-6K.js";const l=t(o);export{l as default};
@@ -1 +0,0 @@
1
- import{C as o}from"./Col-1GP8MvKL.js";import{a0 as t}from"./index-D1xIbAxQ.js";import"./vendor-Bn1JzzAn.js";import"./index-DhZgULsY.js";import"./icons-DG5UD-6K.js";const s=t(o);export{s as default};