chainlesschain 0.162.123 → 0.162.125

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 (263) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/AIOps-DcqNhZhA.js +1 -0
  4. package/src/assets/web-panel/assets/{ActionButton-BugJtiJc.js → ActionButton-BigtmPoq.js} +1 -1
  5. package/src/assets/web-panel/assets/Analytics-1J_X_HF4.js +3 -0
  6. package/src/assets/web-panel/assets/{AppLayout-Cb0eHetM.js → AppLayout-DlcDG_a_.js} +5 -5
  7. package/src/assets/web-panel/assets/Audit-Do-y8_3w.js +1 -0
  8. package/src/assets/web-panel/assets/Backup-S2Df-50Y.js +1 -0
  9. package/src/assets/web-panel/assets/{BaseInput-CZ0c7QHk.js → BaseInput-D5kgWJfk.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-CaKDT3wV.js → Chat-aUcp3kN1.js} +6 -6
  11. package/src/assets/web-panel/assets/ChatBubbleRenderer-Pg7dIsiE.js +1 -0
  12. package/src/assets/web-panel/assets/{Checkbox-C6-Iu3bG.js → Checkbox-DTg1U7Xs.js} +1 -1
  13. package/src/assets/web-panel/assets/Codegen-YO9ZZ4Ky.js +1 -0
  14. package/src/assets/web-panel/assets/{Col-DVBrO7AN.js → Col-BHonE9fU.js} +1 -1
  15. package/src/assets/web-panel/assets/Community-DA2AlEFh.js +1 -0
  16. package/src/assets/web-panel/assets/{Compact-DM4ZgFSE.js → Compact-DULxLRNg.js} +1 -1
  17. package/src/assets/web-panel/assets/Compliance-BZqfuuZL.js +1 -0
  18. package/src/assets/web-panel/assets/{Cowork-CJwiWkUY.js → Cowork-CJe3vyMS.js} +2 -2
  19. package/src/assets/web-panel/assets/{Cron-dsdXHvrO.js → Cron-CeV8AtRu.js} +2 -2
  20. package/src/assets/web-panel/assets/Crosschain-CAg66zS5.js +1 -0
  21. package/src/assets/web-panel/assets/{DID-BB_LBLYT.js → DID-Dtup5JZm.js} +2 -2
  22. package/src/assets/web-panel/assets/Dashboard-DAR_8PNy.js +3 -0
  23. package/src/assets/web-panel/assets/{Dropdown-dTFtVnKk.js → Dropdown-K5bTaCYN.js} +1 -1
  24. package/src/assets/web-panel/assets/{EmailListRenderer-yqdttzXV.js → EmailListRenderer-C890uErx.js} +1 -1
  25. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DfX3YpUU.js → FamilyGuardDashboard-B-Tj_8yM.js} +1 -1
  26. package/src/assets/web-panel/assets/Federation-C6WZ98ni.js +1 -0
  27. package/src/assets/web-panel/assets/{FormItemContext-DNMwIh7X.js → FormItemContext-D-LTfGWd.js} +1 -1
  28. package/src/assets/web-panel/assets/{GenericCardRenderer-C-T8UQf0.js → GenericCardRenderer-C80DK4W7.js} +1 -1
  29. package/src/assets/web-panel/assets/{Git-SURFhkLi.js → Git-Cjvvv3zW.js} +2 -2
  30. package/src/assets/web-panel/assets/Governance-C0BND77H.js +1 -0
  31. package/src/assets/web-panel/assets/Inference-BL9kTtSu.js +1 -0
  32. package/src/assets/web-panel/assets/KnowledgeGraph-CnVTBmJf.js +1 -0
  33. package/src/assets/web-panel/assets/{Logs-DjuWREBJ.js → Logs-CbCbg7K6.js} +2 -2
  34. package/src/assets/web-panel/assets/Marketplace-CG5On-fH.js +1 -0
  35. package/src/assets/web-panel/assets/{McpTools-D0waMomb.js → McpTools-AYYDZZK7.js} +5 -5
  36. package/src/assets/web-panel/assets/{Memory-Ci768GQ3.js → Memory-tMWbMDQq.js} +2 -2
  37. package/src/assets/web-panel/assets/MobileBridge-BAOsf4wB.js +1 -0
  38. package/src/assets/web-panel/assets/{MobileProjects-RpFuAiNz.js → MobileProjects-BT-2komQ.js} +1 -1
  39. package/src/assets/web-panel/assets/{Mtc-DJdw8btT.js → Mtc-BFwfC63n.js} +5 -5
  40. package/src/assets/web-panel/assets/{MtcAudit-Dc596XgH.js → MtcAudit-DYG3CWdH.js} +4 -4
  41. package/src/assets/web-panel/assets/{Multisig--Qs7eGsH.js → Multisig-KJwd0yWT.js} +3 -3
  42. package/src/assets/web-panel/assets/NLProgramming-BmL5dNWm.js +1 -0
  43. package/src/assets/web-panel/assets/{Notes-uMxjj66_.js → Notes-DmkaVaMc.js} +3 -3
  44. package/src/assets/web-panel/assets/{NotificationSettings-C4ikBOsm.js → NotificationSettings-tmVQszDZ.js} +1 -1
  45. package/src/assets/web-panel/assets/{OrderTableRenderer-BpmdunAz.js → OrderTableRenderer-BtrR7anf.js} +1 -1
  46. package/src/assets/web-panel/assets/Organization-pppktQyW.js +4 -0
  47. package/src/assets/web-panel/assets/{Overflow-B-4_IpbB.js → Overflow-D3lBoQDA.js} +1 -1
  48. package/src/assets/web-panel/assets/{P2P-DdOf9BZs.js → P2P-BgE2qGlZ.js} +2 -2
  49. package/src/assets/web-panel/assets/PdhVaultBrowser-D26Bz5gS.js +7 -0
  50. package/src/assets/web-panel/assets/Permissions-BR8no2Xe.js +4 -0
  51. package/src/assets/web-panel/assets/{PersonalDataHub-B3WcvKni.js → PersonalDataHub-DabbyQEF.js} +2 -2
  52. package/src/assets/web-panel/assets/Pipeline-n4sNpEz8.js +1 -0
  53. package/src/assets/web-panel/assets/Privacy-CGNp4n8M.js +1 -0
  54. package/src/assets/web-panel/assets/{ProjectInit-BQjMDkML.js → ProjectInit-CAVA5VsX.js} +2 -2
  55. package/src/assets/web-panel/assets/ProjectSettings-GCgkfM7A.js +2 -0
  56. package/src/assets/web-panel/assets/Projects-BYK17p52.js +1 -0
  57. package/src/assets/web-panel/assets/{Providers-Bl0dw6cI.js → Providers-DpUT5W-d.js} +1 -1
  58. package/src/assets/web-panel/assets/{QuickAsk-BDrk7l3t.js → QuickAsk-ZJxTb7vi.js} +1 -1
  59. package/src/assets/web-panel/assets/Recommend-CIzFRDk1.js +1 -0
  60. package/src/assets/web-panel/assets/Reputation-lQ_WDNZn.js +1 -0
  61. package/src/assets/web-panel/assets/Row-1MEtrgtF.js +1 -0
  62. package/src/assets/web-panel/assets/{RssFeed-9-Fxh3_w.js → RssFeed-SSgcPPl4.js} +2 -2
  63. package/src/assets/web-panel/assets/Search-CcTMOGNY.js +1 -0
  64. package/src/assets/web-panel/assets/{Security-D6CySV7r.js → Security-BA1KMaDx.js} +3 -3
  65. package/src/assets/web-panel/assets/{Services-Co8Mk8qE.js → Services-_aj7czZn.js} +2 -2
  66. package/src/assets/web-panel/assets/{Skeleton-BdON8M-1.js → Skeleton-BWgg_0Zr.js} +1 -1
  67. package/src/assets/web-panel/assets/{Skills-CEJE1Go1.js → Skills-C2ZIziYM.js} +1 -1
  68. package/src/assets/web-panel/assets/Sla-CYxp4axY.js +1 -0
  69. package/src/assets/web-panel/assets/SpeechSettings-C7Csp1jV.js +1 -0
  70. package/src/assets/web-panel/assets/{SyncSettings-52NXsfyj.js → SyncSettings-svGeswiw.js} +2 -2
  71. package/src/assets/web-panel/assets/{Tasks-He7CweW6.js → Tasks-ClISj6oK.js} +1 -1
  72. package/src/assets/web-panel/assets/Templates-CmO-Fp7r.js +1 -0
  73. package/src/assets/web-panel/assets/Tenant-a3Ac3lxz.js +1 -0
  74. package/src/assets/web-panel/assets/Terminal-DyJIRh81.js +3 -0
  75. package/src/assets/web-panel/assets/{TimelineRenderer-BdfqBQmM.js → TimelineRenderer-acXS5N5h.js} +1 -1
  76. package/src/assets/web-panel/assets/Tokens-DJW0KqV4.js +1 -0
  77. package/src/assets/web-panel/assets/{Trigger-Dt_N_rvo.js → Trigger-0HeTi4r6.js} +1 -1
  78. package/src/assets/web-panel/assets/Trust-aFym34eo.js +1 -0
  79. package/src/assets/web-panel/assets/{UkeySign-CW8YdFxg.js → UkeySign-CYhszHJv.js} +1 -1
  80. package/src/assets/web-panel/assets/{VideoEditing-CU6o7qvp.js → VideoEditing-BbJxPF9X.js} +1 -1
  81. package/src/assets/web-panel/assets/Wallet-DDthEwiu.js +4 -0
  82. package/src/assets/web-panel/assets/WebAuthn-DQ6McYPg.js +5 -0
  83. package/src/assets/web-panel/assets/{WorkflowEditor-BcCA2U8H.js → WorkflowEditor-CnF8zRsi.js} +1 -1
  84. package/src/assets/web-panel/assets/{chat-Cxl5DGsc.js → chat-Dzkx2gTP.js} +1 -1
  85. package/src/assets/web-panel/assets/{colors-FS_7Xggs.js → colors-35T51Oo5.js} +1 -1
  86. package/src/assets/web-panel/assets/{compact-item-BzXjd3wJ.js → compact-item-TtzNrlu1.js} +1 -1
  87. package/src/assets/web-panel/assets/{createContext-DhF9bi_u.js → createContext-Y1LkWrYb.js} +1 -1
  88. package/src/assets/web-panel/assets/devWarning-D_lwLZ6O.js +1 -0
  89. package/src/assets/web-panel/assets/{hasIn-Y1a0aV9N.js → hasIn-BOYw1BgS.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-JUukFpxi.js → index-8jffdb6b.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-BBtULjKY.js → index-B7aKAZzS.js} +1 -1
  92. package/src/assets/web-panel/assets/index-BGPEaeB1.js +1 -0
  93. package/src/assets/web-panel/assets/index-BKRlWHUp.js +1 -0
  94. package/src/assets/web-panel/assets/{index-Dmjxpu0L.js → index-BSpFe6j5.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-1iacQgo5.js → index-BWgNn9As.js} +28 -26
  96. package/src/assets/web-panel/assets/{index-Na733kBa.js → index-Bs69SI96.js} +2 -2
  97. package/src/assets/web-panel/assets/{index-B48PZ-xM.js → index-BtlJWaSP.js} +2 -2
  98. package/src/assets/web-panel/assets/index-Bw1iOGhM.js +1 -0
  99. package/src/assets/web-panel/assets/{index-DVTex5M8.js → index-BxWUEFKy.js} +1 -1
  100. package/src/assets/web-panel/assets/index-C7Wt5feN.js +1 -0
  101. package/src/assets/web-panel/assets/index-CEwuCM44.js +1 -0
  102. package/src/assets/web-panel/assets/index-CLqC2sRT.js +3 -0
  103. package/src/assets/web-panel/assets/{index-DxGVbuqr.js → index-CPGNc2tF.js} +2 -2
  104. package/src/assets/web-panel/assets/{index-DfaSKEVD.js → index-CVrUs6rf.js} +1 -1
  105. package/src/assets/web-panel/assets/index-CZ16GlOs.js +1 -0
  106. package/src/assets/web-panel/assets/{index-CELVZPNt.js → index-CdyFg4FI.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CETvk0NN.js → index-CqhwG1ox.js} +4 -4
  108. package/src/assets/web-panel/assets/index-Cs82BHAj.js +1 -0
  109. package/src/assets/web-panel/assets/{index-D3IVAG9l.js → index-D2od7Bgx.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Sl-sLj3T.js → index-D47robkX.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Ci7PSoQn.js → index-D4BgCBa3.js} +2 -2
  112. package/src/assets/web-panel/assets/index-D6tG4B25.js +55 -0
  113. package/src/assets/web-panel/assets/index-D7dCBw_M.js +1 -0
  114. package/src/assets/web-panel/assets/index-DAizH273.js +21 -0
  115. package/src/assets/web-panel/assets/{index-GCd5hs58.js → index-DFhlelzN.js} +2 -2
  116. package/src/assets/web-panel/assets/index-DhMSOd_2.js +1 -0
  117. package/src/assets/web-panel/assets/index-Dhfw-BXs.js +12 -0
  118. package/src/assets/web-panel/assets/index-DjeUZxE5.js +1 -0
  119. package/src/assets/web-panel/assets/{index-BnvaUocg.js → index-DmkG479D.js} +2 -2
  120. package/src/assets/web-panel/assets/index-Dof7lWA_.js +13 -0
  121. package/src/assets/web-panel/assets/{index-BNmWkPqm.js → index-Dx0hIfC-.js} +1 -1
  122. package/src/assets/web-panel/assets/index-DzzgU4IU.js +1 -0
  123. package/src/assets/web-panel/assets/index-G4ClSmJc.js +1 -0
  124. package/src/assets/web-panel/assets/{index-Cn9lbqaS.js → index-KgXrlfNF.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DUcVF8e6.js → index-NM9Oke2k.js} +2 -2
  126. package/src/assets/web-panel/assets/{index-DLyIxnjc.js → index-XIuZV9by.js} +3 -3
  127. package/src/assets/web-panel/assets/{index-BTltEtoG.js → index-hyqEKkBT.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-uLoKviUT.js → index-zF77jnI2.js} +2 -2
  129. package/src/assets/web-panel/assets/{initDefaultProps-D6QmlnQ-.js → initDefaultProps-DD5y5msp.js} +1 -1
  130. package/src/assets/web-panel/assets/motion-BuoutMia.js +11 -0
  131. package/src/assets/web-panel/assets/{move-Bpph6B_N.js → move-DkpjqOXg.js} +1 -1
  132. package/src/assets/web-panel/assets/mtc-parser-BmGJD-v-.js +1 -0
  133. package/src/assets/web-panel/assets/{omit-Dx0YMbWn.js → omit-DftUE0Sz.js} +1 -1
  134. package/src/assets/web-panel/assets/{pickAttrs-D-a8Fm6C.js → pickAttrs-DDRb9FIu.js} +1 -1
  135. package/src/assets/web-panel/assets/{placementArrow-DXFSVgxv.js → placementArrow-DPgtxOOD.js} +1 -1
  136. package/src/assets/web-panel/assets/{responsiveObserve-CxhQ1EaK.js → responsiveObserve-BQFRzFeZ.js} +1 -1
  137. package/src/assets/web-panel/assets/{slide-IlBI0XU8.js → slide-DQ4lmUSz.js} +1 -1
  138. package/src/assets/web-panel/assets/{statusUtils-COoXVzje.js → statusUtils-C1TR4ZiQ.js} +1 -1
  139. package/src/assets/web-panel/assets/{styleChecker-DCuq89M_.js → styleChecker-B-Suj978.js} +1 -1
  140. package/src/assets/web-panel/assets/{useFlexGapSupport-tfNE1ALO.js → useFlexGapSupport-Q7bDZ3EI.js} +1 -1
  141. package/src/assets/web-panel/assets/{useFs-DD4bzBkU.js → useFs-DHjRLyQH.js} +1 -1
  142. package/src/assets/web-panel/assets/{usePersonalDataHub-DVkH1hbT.js → usePersonalDataHub-lQOvCvCr.js} +1 -1
  143. package/src/assets/web-panel/assets/{vnode-D1ZmXyxM.js → vnode-Ie0g4-p3.js} +1 -1
  144. package/src/assets/web-panel/assets/{zoom-DblBUHfi.js → zoom-CHP0YEoH.js} +1 -1
  145. package/src/assets/web-panel/index.html +1 -1
  146. package/src/commands/agent.js +10 -0
  147. package/src/commands/ask.js +24 -1
  148. package/src/commands/ide.js +60 -5
  149. package/src/commands/incentive.js +22 -22
  150. package/src/commands/init.js +5 -4
  151. package/src/commands/instinct.js +18 -5
  152. package/src/commands/mcp.js +30 -3
  153. package/src/commands/memory.js +18 -5
  154. package/src/commands/mtc.js +5 -1
  155. package/src/gateways/http/envelope-http-server.js +17 -2
  156. package/src/gateways/terminal/PtyManager.js +15 -2
  157. package/src/gateways/ws/message-dispatcher.js +4 -1
  158. package/src/gateways/ws/ws-server.js +20 -0
  159. package/src/gateways/ws/ws-session-gateway.js +72 -4
  160. package/src/harness/background-task-manager.js +5 -1
  161. package/src/harness/jsonl-session-store.js +51 -0
  162. package/src/harness/mcp-client.js +119 -21
  163. package/src/harness/prompt-compressor.js +24 -5
  164. package/src/harness/worktree-isolator.js +25 -10
  165. package/src/lib/agent-core.js +4 -0
  166. package/src/lib/autonomous-agent.js +36 -6
  167. package/src/lib/chat-intent-service.js +18 -4
  168. package/src/lib/checkpoint-store.js +54 -4
  169. package/src/lib/claude-code-bridge.js +48 -3
  170. package/src/lib/cost-budget.js +13 -2
  171. package/src/lib/credential-guard.js +45 -0
  172. package/src/lib/git-integration.js +68 -3
  173. package/src/lib/goal-store.js +11 -0
  174. package/src/lib/hook-manager.js +4 -0
  175. package/src/lib/hook-runner.cjs +11 -1
  176. package/src/lib/interactive-planner.js +4 -3
  177. package/src/lib/jetbrains-bridge.js +147 -0
  178. package/src/lib/json-schema-output.js +47 -0
  179. package/src/lib/jsonl-session-store.js +1 -0
  180. package/src/lib/learning/reflection-engine.js +4 -3
  181. package/src/lib/learning/skill-improver.js +4 -3
  182. package/src/lib/learning/skill-synthesizer.js +4 -3
  183. package/src/lib/llm-pricing.js +11 -4
  184. package/src/lib/llm-providers.js +11 -1
  185. package/src/lib/mcp-oauth.js +220 -20
  186. package/src/lib/mcp-serve.js +85 -6
  187. package/src/lib/orchestrator.js +42 -4
  188. package/src/lib/process-manager.js +31 -3
  189. package/src/lib/repl-bang-memorize.js +9 -1
  190. package/src/lib/repl-completer.js +29 -0
  191. package/src/lib/runnable-provider.js +7 -1
  192. package/src/lib/session-insights.js +13 -6
  193. package/src/lib/session-usage.js +21 -3
  194. package/src/lib/slot-filler.js +4 -3
  195. package/src/lib/status-line.cjs +4 -1
  196. package/src/lib/sub-agent-context.js +15 -0
  197. package/src/lib/web-fetch.js +54 -3
  198. package/src/lib/workflow-engine.js +35 -11
  199. package/src/repl/agent-repl.js +29 -0
  200. package/src/runtime/agent-core.js +252 -28
  201. package/src/runtime/headless-runner.js +13 -0
  202. package/src/runtime/headless-stream.js +133 -17
  203. package/src/runtime/mcp-config.js +72 -3
  204. package/src/runtime/pipe-safety.js +51 -0
  205. package/src/runtime/policies/agent-policy.js +3 -0
  206. package/src/assets/web-panel/assets/AIOps-C5CSd8pY.js +0 -1
  207. package/src/assets/web-panel/assets/Analytics-CzxpcV9E.js +0 -3
  208. package/src/assets/web-panel/assets/Audit-qjq04wh8.js +0 -1
  209. package/src/assets/web-panel/assets/Backup-C2Y_XNBA.js +0 -1
  210. package/src/assets/web-panel/assets/ChatBubbleRenderer-TeKnMwPj.js +0 -1
  211. package/src/assets/web-panel/assets/Codegen-TGLFk6P1.js +0 -1
  212. package/src/assets/web-panel/assets/Community-tg6-Rik1.js +0 -1
  213. package/src/assets/web-panel/assets/Compliance-DFeWryxt.js +0 -1
  214. package/src/assets/web-panel/assets/Crosschain-p7_5Gq-7.js +0 -1
  215. package/src/assets/web-panel/assets/Dashboard-Bh_HkNSD.js +0 -3
  216. package/src/assets/web-panel/assets/Federation-BJApfPV4.js +0 -1
  217. package/src/assets/web-panel/assets/Governance-B4u_KsfE.js +0 -1
  218. package/src/assets/web-panel/assets/Inference-2kr_rKQd.js +0 -1
  219. package/src/assets/web-panel/assets/KnowledgeGraph-h-nL7J6x.js +0 -1
  220. package/src/assets/web-panel/assets/Marketplace-W9Iz8b2m.js +0 -1
  221. package/src/assets/web-panel/assets/MobileBridge-4jAehfcz.js +0 -3
  222. package/src/assets/web-panel/assets/NLProgramming-D3y71FDX.js +0 -1
  223. package/src/assets/web-panel/assets/Organization-BBEmFNGM.js +0 -4
  224. package/src/assets/web-panel/assets/PdhVaultBrowser-DDU2Zc9Q.js +0 -7
  225. package/src/assets/web-panel/assets/Permissions-B1ebgevF.js +0 -4
  226. package/src/assets/web-panel/assets/Pipeline-DASuPDc3.js +0 -1
  227. package/src/assets/web-panel/assets/Privacy-DnixwmhJ.js +0 -1
  228. package/src/assets/web-panel/assets/ProjectSettings-dHyZ3SxW.js +0 -2
  229. package/src/assets/web-panel/assets/Projects-BGWEJIVT.js +0 -1
  230. package/src/assets/web-panel/assets/Recommend-BPKof_CA.js +0 -1
  231. package/src/assets/web-panel/assets/Reputation-6MNJLklK.js +0 -1
  232. package/src/assets/web-panel/assets/Row-DmGJwpfd.js +0 -1
  233. package/src/assets/web-panel/assets/Search-CCHjey_D.js +0 -1
  234. package/src/assets/web-panel/assets/Sla-BSxFvFX9.js +0 -1
  235. package/src/assets/web-panel/assets/SpeechSettings-C3rShYlV.js +0 -1
  236. package/src/assets/web-panel/assets/Templates-UtjBlJIO.js +0 -1
  237. package/src/assets/web-panel/assets/Tenant-Dd5XNbYF.js +0 -1
  238. package/src/assets/web-panel/assets/Terminal-pFyh42cu.js +0 -3
  239. package/src/assets/web-panel/assets/Tokens-CsgNsrdQ.js +0 -1
  240. package/src/assets/web-panel/assets/Trust-B1lJzsPn.js +0 -1
  241. package/src/assets/web-panel/assets/Wallet-DPxjEhbN.js +0 -4
  242. package/src/assets/web-panel/assets/WebAuthn-CuJ29YAF.js +0 -5
  243. package/src/assets/web-panel/assets/community-parser-Cuyslaku.js +0 -3
  244. package/src/assets/web-panel/assets/devWarning-B1j78JeH.js +0 -1
  245. package/src/assets/web-panel/assets/index--fcP34eT.js +0 -1
  246. package/src/assets/web-panel/assets/index-1khPb4ZS.js +0 -1
  247. package/src/assets/web-panel/assets/index-B_Z0PHC0.js +0 -12
  248. package/src/assets/web-panel/assets/index-BaN6wKWx.js +0 -1
  249. package/src/assets/web-panel/assets/index-BepInmSi.js +0 -1
  250. package/src/assets/web-panel/assets/index-BllceSdr.js +0 -1
  251. package/src/assets/web-panel/assets/index-Bt_0ygjA.js +0 -1
  252. package/src/assets/web-panel/assets/index-BzQkdgQW.js +0 -1
  253. package/src/assets/web-panel/assets/index-C2S8McM1.js +0 -55
  254. package/src/assets/web-panel/assets/index-CRSkKj9M.js +0 -1
  255. package/src/assets/web-panel/assets/index-Cgo1J_Kf.js +0 -1
  256. package/src/assets/web-panel/assets/index-CuTKOay7.js +0 -1
  257. package/src/assets/web-panel/assets/index-CxYHV8nb.js +0 -13
  258. package/src/assets/web-panel/assets/index-CzlCP4jP.js +0 -3
  259. package/src/assets/web-panel/assets/index-DZJMsVI1.js +0 -1
  260. package/src/assets/web-panel/assets/index-DgHWZVjv.js +0 -21
  261. package/src/assets/web-panel/assets/index-N7JKRLCC.js +0 -1
  262. package/src/assets/web-panel/assets/motion-BHrTGY1_.js +0 -11
  263. package/src/assets/web-panel/assets/mtc-parser-CEQffxds.js +0 -1
@@ -101,6 +101,17 @@ export function isLikelyConnectionError(err) {
101
101
  const MCP_DISCOVERY_RETRIES = 2; // up to 3 attempts total
102
102
  const MCP_DISCOVERY_BACKOFF_MS = 250; // 250ms, 500ms
103
103
 
104
+ /**
105
+ * Hard cap on the unterminated stdout tail buffered per stdio server. JSON-RPC
106
+ * frames are newline-delimited; a runaway or non-MCP process that streams
107
+ * without ever emitting a newline would otherwise grow `entry._buffer` without
108
+ * bound and exhaust memory. 16M chars is far above any legitimate single MCP
109
+ * message (even a tool result with embedded base64), so crossing it signals a
110
+ * misbehaving server. Override per server with `config.maxBufferChars` (0
111
+ * disables the cap).
112
+ */
113
+ const MCP_MAX_BUFFER_CHARS = 16 * 1024 * 1024;
114
+
104
115
  /**
105
116
  * Is this a TRANSIENT error worth a short retry during capability discovery?
106
117
  * Narrower than isLikelyConnectionError: connection-level failures and 5xx
@@ -293,6 +304,20 @@ export class MCPClient extends EventEmitter {
293
304
  failPending(`MCP server "${name}" process error: ${err.message}`);
294
305
  this.emit("server-error", { name, error: err.message });
295
306
  });
307
+
308
+ // Writing to a stdio server that closed its stdin read end (or died
309
+ // mid-write) makes the stdin pipe emit an asynchronous 'error' (EPIPE).
310
+ // An 'error' event with no listener is an uncaught exception in Node and
311
+ // would CRASH the whole CLI — and the try/catch around stdin.write only
312
+ // catches synchronous throws, not this async event. Handle it: drain
313
+ // in-flight requests and surface it, mirroring the process 'error' path.
314
+ if (proc.stdin && typeof proc.stdin.on === "function") {
315
+ proc.stdin.on("error", (err) => {
316
+ entry.state = ServerState.ERROR;
317
+ failPending(`MCP server "${name}" stdin error: ${err.message}`);
318
+ this.emit("server-error", { name, error: err.message });
319
+ });
320
+ }
296
321
  } else {
297
322
  throw new Error(
298
323
  `transport "${transportKind}" is not supported by this client`,
@@ -384,6 +409,7 @@ export class MCPClient extends EventEmitter {
384
409
  try {
385
410
  entry.process.stdout?.removeAllListeners();
386
411
  entry.process.stderr?.removeAllListeners();
412
+ entry.process.stdin?.removeAllListeners?.();
387
413
  entry.process.removeAllListeners();
388
414
  entry.process.kill();
389
415
  } catch {
@@ -788,19 +814,18 @@ export class MCPClient extends EventEmitter {
788
814
  const contentType = response.headers?.get
789
815
  ? String(response.headers.get("content-type") || "").toLowerCase()
790
816
  : "";
817
+ // Bound the response body the same way the stdio path bounds its line
818
+ // buffer (per-server maxBufferChars override; 0 disables).
819
+ const cap = Number.isFinite(entry.config?.maxBufferChars)
820
+ ? entry.config.maxBufferChars
821
+ : MCP_MAX_BUFFER_CHARS;
791
822
 
792
823
  let envelope;
793
824
  if (contentType.includes("text/event-stream")) {
794
- envelope = await _extractSseResponse(response, id);
825
+ envelope = await _extractSseResponse(response, id, cap);
795
826
  } else {
796
- envelope =
797
- typeof response.json === "function"
798
- ? await response.json()
799
- : JSON.parse(
800
- typeof response.text === "function"
801
- ? await response.text()
802
- : "",
803
- );
827
+ const text = await _readBodyCapped(response, cap);
828
+ envelope = text ? JSON.parse(text) : null;
804
829
  }
805
830
 
806
831
  if (!envelope || typeof envelope !== "object") {
@@ -868,6 +893,37 @@ export class MCPClient extends EventEmitter {
868
893
  // Skip malformed lines
869
894
  }
870
895
  }
896
+
897
+ // Guard against unbounded buffer growth: a runaway / non-MCP server that
898
+ // streams without ever sending a newline would grow the unterminated tail
899
+ // forever and exhaust memory. If the leftover partial line exceeds the cap,
900
+ // treat it as a fatal transport error (drop the buffer, drain in-flight
901
+ // requests, kill the process) rather than letting it grow without limit.
902
+ const cap = Number.isFinite(entry.config?.maxBufferChars)
903
+ ? entry.config.maxBufferChars
904
+ : MCP_MAX_BUFFER_CHARS;
905
+ if (cap > 0 && entry._buffer.length > cap) {
906
+ entry._buffer = "";
907
+ entry.state = ServerState.ERROR;
908
+ const errMsg = `MCP server "${serverName}" exceeded the ${cap}-char line buffer with no newline (runaway or non-MCP output)`;
909
+ for (const [, pending] of entry._pending) {
910
+ if (pending.timeout) clearTimeout(pending.timeout);
911
+ try {
912
+ pending.reject(new Error(errMsg));
913
+ } catch {
914
+ // already settled — ignore
915
+ }
916
+ }
917
+ entry._pending.clear();
918
+ if (entry.process) {
919
+ try {
920
+ entry.process.kill();
921
+ } catch {
922
+ // best-effort — surfacing the error is what matters
923
+ }
924
+ }
925
+ this.emit("server-error", { name: serverName, error: errMsg });
926
+ }
871
927
  }
872
928
 
873
929
  _handleMessage(serverName, msg) {
@@ -900,28 +956,70 @@ export class MCPClient extends EventEmitter {
900
956
  }
901
957
 
902
958
  /**
903
- * Parse a `text/event-stream` HTTP response body and return the first
904
- * JSON-RPC envelope whose id matches `requestId`. Tolerates multiple
905
- * `data:` chunks, comments, and non-JSON-RPC events.
959
+ * Read an HTTP response body to text with a hard size cap, mirroring the stdio
960
+ * transport's MCP_MAX_BUFFER_CHARS guard. The per-call timeout bounds TIME but
961
+ * not MEMORY: a malicious/buggy HTTP MCP server could stream a multi-GB body
962
+ * within the timeout and OOM the client. When the body is a readable stream
963
+ * (real fetch) we accumulate with a running cap and cancel on overflow; for a
964
+ * non-stream response (test mocks) we fall back to text() + a post-hoc check.
965
+ * A declared Content-Length over the cap is rejected before any read. cap<=0
966
+ * disables the limit.
906
967
  */
907
- async function _extractSseResponse(response, requestId) {
908
- let text;
909
- if (typeof response.text === "function") {
910
- text = await response.text();
911
- } else if (response.body && typeof response.body.getReader === "function") {
968
+ async function _readBodyCapped(response, cap) {
969
+ if (
970
+ cap > 0 &&
971
+ response.headers &&
972
+ typeof response.headers.get === "function"
973
+ ) {
974
+ const declared = Number(response.headers.get("content-length"));
975
+ if (Number.isFinite(declared) && declared > cap) {
976
+ throw new Error(
977
+ `MCP HTTP response exceeds the ${cap}-byte cap (content-length ${declared})`,
978
+ );
979
+ }
980
+ }
981
+ if (response.body && typeof response.body.getReader === "function") {
912
982
  const reader = response.body.getReader();
913
983
  const decoder = new TextDecoder("utf-8");
914
984
  const chunks = [];
915
- while (true) {
985
+ let total = 0;
986
+ for (;;) {
916
987
  const { value, done } = await reader.read();
917
988
  if (done) break;
989
+ total += value && value.length ? value.length : 0;
990
+ if (cap > 0 && total > cap) {
991
+ try {
992
+ await reader.cancel();
993
+ } catch {
994
+ /* best-effort — the throw below is what matters */
995
+ }
996
+ throw new Error(
997
+ `MCP HTTP response exceeded the ${cap}-byte cap (runaway server)`,
998
+ );
999
+ }
918
1000
  chunks.push(decoder.decode(value, { stream: true }));
919
1001
  }
920
1002
  chunks.push(decoder.decode());
921
- text = chunks.join("");
922
- } else {
923
- throw new Error("SSE response is not readable");
1003
+ return chunks.join("");
1004
+ }
1005
+ if (typeof response.text !== "function") {
1006
+ throw new Error("HTTP response is not readable");
1007
+ }
1008
+ const text = await response.text();
1009
+ if (cap > 0 && text.length > cap) {
1010
+ throw new Error(`MCP HTTP response exceeded the ${cap}-char cap`);
924
1011
  }
1012
+ return text;
1013
+ }
1014
+
1015
+ /**
1016
+ * Parse a `text/event-stream` HTTP response body and return the first
1017
+ * JSON-RPC envelope whose id matches `requestId`. Tolerates multiple
1018
+ * `data:` chunks, comments, and non-JSON-RPC events. `cap` bounds the body
1019
+ * size (see _readBodyCapped).
1020
+ */
1021
+ async function _extractSseResponse(response, requestId, cap = 0) {
1022
+ const text = await _readBodyCapped(response, cap);
925
1023
 
926
1024
  // Split into events on blank line, parse each event's concatenated `data:` lines.
927
1025
  const events = text.split(/\r?\n\r?\n/);
@@ -12,6 +12,12 @@
12
12
  import { createHash } from "node:crypto";
13
13
  import { feature, featureVariant } from "../lib/feature-flags.js";
14
14
 
15
+ // Bounds for the fuzzy near-dup pass in _deduplicate (see there). Generous
16
+ // enough that real near-dups are still caught; small enough that compaction of
17
+ // a long, tool-heavy history stays sub-second instead of O(n²·content).
18
+ const DEDUP_JACCARD_MAX_CHARS = 4000;
19
+ const DEDUP_FUZZY_WINDOW = 100;
20
+
15
21
  export function estimateTokens(text) {
16
22
  if (!text) return 0;
17
23
  const chineseChars = (text.match(/[\u4e00-\u9fa5]/g) || []).length;
@@ -320,8 +326,18 @@ export class PromptCompressor {
320
326
  const last = [...messages].reverse().find((m) => m.role === "user");
321
327
  const rest = messages.filter((m) => m.role !== "system" && m !== last);
322
328
 
329
+ // Bound the fuzzy pass so it stays usable on the very conversations
330
+ // compaction targets (long + tool-heavy). The naive version compared every
331
+ // message against EVERY prior one (O(n²)) and ran jaccard over full content
332
+ // (O(content) per compare) — 300 msgs × ~8 KB tool results blocked ~3.4 s.
333
+ // • Exact dedup (the md5 hash set) still covers ALL messages.
334
+ // • The fuzzy near-dup compare uses a capped prefix (char-set jaccard is
335
+ // alphabet-bounded, so a prefix barely changes the decision) and only the
336
+ // most-recent kept entries (near-dups cluster; this can only UNDER-dedup,
337
+ // never drop a genuinely-distinct message).
323
338
  const seen = new Map();
324
339
  const deduped = [];
340
+ const recent = []; // rolling window of recent kept {capped} contents
325
341
 
326
342
  for (const msg of rest) {
327
343
  const content = getContent(msg);
@@ -329,12 +345,13 @@ export class PromptCompressor {
329
345
 
330
346
  if (seen.has(hash)) continue;
331
347
 
348
+ const capped =
349
+ content.length > DEDUP_JACCARD_MAX_CHARS
350
+ ? content.slice(0, DEDUP_JACCARD_MAX_CHARS)
351
+ : content;
332
352
  let isDup = false;
333
- for (const [, existing] of seen) {
334
- if (
335
- jaccardSimilarity(content, getContent(existing)) >=
336
- this.similarityThreshold
337
- ) {
353
+ for (const prev of recent) {
354
+ if (jaccardSimilarity(capped, prev) >= this.similarityThreshold) {
338
355
  isDup = true;
339
356
  break;
340
357
  }
@@ -343,6 +360,8 @@ export class PromptCompressor {
343
360
  if (!isDup) {
344
361
  seen.set(hash, msg);
345
362
  deduped.push(msg);
363
+ recent.push(capped);
364
+ if (recent.length > DEDUP_FUZZY_WINDOW) recent.shift();
346
365
  }
347
366
  }
348
367
 
@@ -13,7 +13,9 @@ import { resolve } from "node:path";
13
13
  import {
14
14
  isGitRepo,
15
15
  gitExec,
16
+ gitExecArgs,
16
17
  assertSafeGitRef,
18
+ assertSafeGitPath,
17
19
  } from "../lib/git-integration.js";
18
20
 
19
21
  const WORKTREE_DIR = ".worktrees";
@@ -167,6 +169,7 @@ export function diffWorktree(repoDir, branchName, options = {}) {
167
169
  if (options.baseBranch) assertSafeGitRef(options.baseBranch, "base branch");
168
170
  const base = options.baseBranch || "HEAD";
169
171
  const filePath = options.filePath || null;
172
+ if (filePath) assertSafeGitPath(filePath, "file path");
170
173
  const fileArg = filePath ? ` -- "${filePath}"` : "";
171
174
  const statOutput = gitExec(
172
175
  `diff ${base}...${branchName} --stat --numstat${fileArg}`,
@@ -223,14 +226,13 @@ export function mergeWorktree(repoDir, branchName, options = {}) {
223
226
  const msg =
224
227
  options.message ||
225
228
  `feat: merge agent work from ${branchName} (squashed)`;
226
- gitExec(`commit -m "${msg.replace(/"/g, '\\"')}"`, repoDir);
229
+ // Free-text message → argv (no shell) so `$()`/backticks/quotes in it
230
+ // can't inject a command (the old `-m "…"` only escaped `"`).
231
+ gitExecArgs(["commit", "-m", msg], repoDir);
227
232
  } else {
228
233
  const msg =
229
234
  options.message || `Merge branch '${branchName}' (agent task)`;
230
- gitExec(
231
- `merge ${branchName} --no-edit -m "${msg.replace(/"/g, '\\"')}"`,
232
- repoDir,
233
- );
235
+ gitExecArgs(["merge", branchName, "--no-edit", "-m", msg], repoDir);
234
236
  }
235
237
 
236
238
  if (deleteBranch) {
@@ -393,6 +395,7 @@ export function applyWorktreeAutomationCandidate(
393
395
  const conflictType = options.conflictType || null;
394
396
  const baseBranch = _resolveBaseBranchForMerge(repoDir, options.baseBranch);
395
397
 
398
+ if (filePath) assertSafeGitPath(filePath, "file path");
396
399
  if (!filePath) {
397
400
  throw new Error("filePath is required");
398
401
  }
@@ -459,7 +462,11 @@ export function applyWorktreeAutomationCandidate(
459
462
  const commitMessage =
460
463
  options.commitMessage ||
461
464
  `Resolve ${filePath} via ${candidate.label || candidateId}`;
462
- gitExec(`commit -m "${commitMessage.replace(/"/g, '\\"')}"`, worktree.path);
465
+ // Free-text message → argv (no shell) so `$()` / backticks / quotes in a
466
+ // caller-supplied commitMessage can't inject a command. The prior
467
+ // `-m "${msg.replace(/"/g,'\\"')}"` only neutralized double quotes — the
468
+ // same gap already fixed in mergeWorktree.
469
+ gitExecArgs(["commit", "-m", commitMessage], worktree.path);
463
470
 
464
471
  const diff = diffWorktree(repoDir, branchName, { baseBranch });
465
472
  const filesChanged = diff.summary?.filesChanged || 0;
@@ -514,8 +521,10 @@ export function worktreeLog(repoDir, branchName, baseBranch = "HEAD") {
514
521
 
515
522
  function _fileStatus(repoDir, base, branch, filePath) {
516
523
  try {
517
- const output = gitExec(
518
- `diff ${base}...${branch} --name-status -- "${filePath}"`,
524
+ // filePath here comes from git's conflict output (a tracked file name);
525
+ // pass it via argv so an exotic / maliciously-named file can't inject.
526
+ const output = gitExecArgs(
527
+ ["diff", `${base}...${branch}`, "--name-status", "--", filePath],
519
528
  repoDir,
520
529
  );
521
530
  const status = output.trim().charAt(0);
@@ -603,7 +612,10 @@ function _buildConflictSummary(repoDir, filePath, branchName) {
603
612
 
604
613
  function _conflictStatusCode(repoDir, filePath) {
605
614
  try {
606
- const output = gitExec(`status --porcelain -- "${filePath}"`, repoDir);
615
+ const output = gitExecArgs(
616
+ ["status", "--porcelain", "--", filePath],
617
+ repoDir,
618
+ );
607
619
  const firstLine = output.split("\n").find((line) => line.trim());
608
620
  return firstLine ? firstLine.slice(0, 2) : "UU";
609
621
  } catch (_e) {
@@ -779,7 +791,10 @@ function _buildDiffPreview(repoDir, filePath, branchName) {
779
791
  };
780
792
 
781
793
  try {
782
- const diff = gitExec(`diff HEAD...${branchName} -- "${filePath}"`, repoDir);
794
+ const diff = gitExecArgs(
795
+ ["diff", `HEAD...${branchName}`, "--", filePath],
796
+ repoDir,
797
+ );
783
798
  preview.snippet =
784
799
  diff.length > 2000
785
800
  ? `${diff.slice(0, 2000)}\n... [diff truncated]`
@@ -3,8 +3,11 @@ export {
3
3
  AGENT_TOOLS,
4
4
  AGENT_TOOL_REGISTRY,
5
5
  MAX_SUB_AGENT_DEPTH,
6
+ MAX_SUB_AGENTS_PER_RUN,
6
7
  MAX_TOOL_RESULT_CHARS,
7
8
  capToolResultString,
9
+ safeStringifyToolResult,
10
+ tokenizeShellWords,
8
11
  reloadSkills,
9
12
  getAgentToolDefinitions,
10
13
  getAgentToolDescriptors,
@@ -25,6 +28,7 @@ export {
25
28
  listBackgroundShellTasks,
26
29
  killBackgroundShellTask,
27
30
  killAllBackgroundShellTasks,
31
+ reapIdleBackgroundShellTasks,
28
32
  writeFileVerified,
29
33
  formatProviderHttpError,
30
34
  _accumulateOllamaStream,
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import { EventEmitter } from "events";
11
+ import { firstBalancedJson } from "./json-schema-output.js";
11
12
 
12
13
  // Exported for test injection
13
14
  export const _deps = {
@@ -47,6 +48,12 @@ export class CLIAutonomousAgent extends EventEmitter {
47
48
  this._initialized = false;
48
49
  this._maxIterations = 20;
49
50
  this._maxRetries = 3;
51
+ // Cap retained goal history. submitGoal stores every goal in _goals and
52
+ // nothing removes it; the REPL keeps one long-lived agent for the whole
53
+ // session, so goals (each holding steps/errors/result) accumulate without
54
+ // bound — and listGoals() returns the whole growing Map. Prune the oldest
55
+ // TERMINAL goals beyond this cap; running/pending/paused goals are kept.
56
+ this._maxGoals = 200;
50
57
  }
51
58
 
52
59
  /**
@@ -57,12 +64,14 @@ export class CLIAutonomousAgent extends EventEmitter {
57
64
  toolExecutor,
58
65
  hookManager,
59
66
  maxIterations,
67
+ maxGoals,
60
68
  iterationBudget,
61
69
  } = {}) {
62
70
  this._llmChat = llmChat || null;
63
71
  this._toolExecutor = toolExecutor || null;
64
72
  this._hookManager = hookManager || null;
65
73
  if (maxIterations) this._maxIterations = maxIterations;
74
+ if (Number.isFinite(maxGoals) && maxGoals > 0) this._maxGoals = maxGoals;
66
75
  this._iterationBudget = iterationBudget || null; // shared budget from caller
67
76
  this._initialized = true;
68
77
  }
@@ -95,6 +104,7 @@ export class CLIAutonomousAgent extends EventEmitter {
95
104
  };
96
105
 
97
106
  this._goals.set(goalId, goal);
107
+ this._pruneGoals();
98
108
  this.emit("goal:submitted", { goalId, description });
99
109
 
100
110
  // Start the ReAct loop asynchronously
@@ -107,6 +117,26 @@ export class CLIAutonomousAgent extends EventEmitter {
107
117
  return { goalId };
108
118
  }
109
119
 
120
+ /**
121
+ * Bound retained goal history to _maxGoals. Evicts the oldest TERMINAL goals
122
+ * (completed/failed/cancelled) FIFO; a still-active goal (pending/running/
123
+ * paused) is never dropped, so a burst of concurrent goals can briefly exceed
124
+ * the cap rather than losing live work.
125
+ */
126
+ _pruneGoals() {
127
+ if (this._goals.size <= this._maxGoals) return;
128
+ for (const [id, g] of this._goals) {
129
+ if (this._goals.size <= this._maxGoals) break;
130
+ if (
131
+ g.status === GoalStatus.COMPLETED ||
132
+ g.status === GoalStatus.FAILED ||
133
+ g.status === GoalStatus.CANCELLED
134
+ ) {
135
+ this._goals.delete(id);
136
+ }
137
+ }
138
+ }
139
+
110
140
  /**
111
141
  * Pause a running goal.
112
142
  */
@@ -471,10 +501,10 @@ Reply with a JSON object: { "action": "retry|add_step|skip", "newParams": {...},
471
501
 
472
502
  _parseSteps(text) {
473
503
  try {
474
- // Extract JSON array from response
475
- const match = text.match(/\[[\s\S]*\]/);
476
- if (match) {
477
- return JSON.parse(match[0]);
504
+ // Extract the first balanced JSON array from the response
505
+ const jsonText = firstBalancedJson(text, "[");
506
+ if (jsonText) {
507
+ return JSON.parse(jsonText);
478
508
  }
479
509
  } catch (_err) {
480
510
  // Parse failure
@@ -484,8 +514,8 @@ Reply with a JSON object: { "action": "retry|add_step|skip", "newParams": {...},
484
514
 
485
515
  _parseJSON(text) {
486
516
  try {
487
- const match = text.match(/\{[\s\S]*\}/);
488
- if (match) return JSON.parse(match[0]);
517
+ const jsonText = firstBalancedJson(text, "{");
518
+ if (jsonText) return JSON.parse(jsonText);
489
519
  } catch (_err) {
490
520
  // Parse failure
491
521
  }
@@ -18,6 +18,7 @@
18
18
  */
19
19
 
20
20
  import { chatWithStreaming, chatStream } from "./chat-core.js";
21
+ import { firstBalancedJson } from "./json-schema-output.js";
21
22
 
22
23
  const DEFAULT_INTENT_TIMEOUT_MS = 15000;
23
24
 
@@ -76,8 +77,9 @@ function extractJson(content) {
76
77
  if (fenced) return fenced[1].trim();
77
78
  const generic = content.match(/```\s*([\s\S]*?)```/);
78
79
  if (generic) return generic[1].trim();
79
- const bare = content.match(/\{[\s\S]*\}/);
80
- return bare ? bare[0] : null;
80
+ // Balanced extraction stops at the first complete object instead of the old
81
+ // greedy /\{[\s\S]*\}/, which over-captured trailing prose with a stray }.
82
+ return firstBalancedJson(content, "{");
81
83
  }
82
84
 
83
85
  /**
@@ -317,7 +319,12 @@ const FOLLOWUP_RULES = {
317
319
  /(颜色|字体|大小|位置|标题).*(是|用|为)/,
318
320
  /^.{1,20}(应该|具体)(是|为)/,
319
321
  /^数据(来源|是)/,
320
- /.*(用|采用).*(字体|颜色|大小)/,
322
+ // De-catastrophized: the old `/.*(用|采用).*(字体…)/` had a leading greedy
323
+ // `.*` overlapping `(用)`, causing exponential backtracking (a 2 KB input
324
+ // of "用" took ~3 s). `.test()` already searches anywhere, so the leading
325
+ // `.*` is redundant; a lazy middle drops it to quadratic, and the input
326
+ // cap in ruleBasedClassify bounds that.
327
+ /(用|采用)[\s\S]*?(字体|颜色|大小)/,
321
328
  ],
322
329
  },
323
330
  CANCEL_TASK: {
@@ -332,8 +339,15 @@ const FOLLOWUP_RULES = {
332
339
  },
333
340
  };
334
341
 
342
+ // A follow-up intent (continue / modify / clarify / cancel) is determinable
343
+ // from the start of the message; cap the text the heuristic regexes see so a
344
+ // huge paste can't drive their (quadratic) backtracking into a DoS. The full
345
+ // input is still forwarded to the LLM path / downstream — only this local
346
+ // classification is truncated.
347
+ const MAX_CLASSIFY_INPUT = 2000;
348
+
335
349
  function ruleBasedClassify(userInput) {
336
- const input = (userInput || "").trim();
350
+ const input = (userInput || "").trim().slice(0, MAX_CLASSIFY_INPUT);
337
351
 
338
352
  if (input.length === 0) {
339
353
  return {
@@ -76,9 +76,19 @@ function gitDir(root) {
76
76
  return path.resolve(root, git(["rev-parse", "--git-dir"], { cwd: root }));
77
77
  }
78
78
 
79
- /** Ref-safe session segment. */
79
+ /**
80
+ * Ref-safe session segment. Beyond the charset filter, enforce git's per-
81
+ * component ref rules so a legit-looking name never makes every checkpoint op
82
+ * throw: a component may not begin with `.`, contain `..`, or end with `.lock`
83
+ * (`/` is already collapsed to `-`, so the session is always a single segment
84
+ * and cannot traverse the ref namespace).
85
+ */
80
86
  function sanitizeSession(session) {
81
- const s = String(session || "default").replace(/[^A-Za-z0-9._-]/g, "-");
87
+ let s = String(session || "default").replace(/[^A-Za-z0-9._-]/g, "-");
88
+ s = s.replace(/\.{2,}/g, "."); // git forbids ".." in a refname
89
+ s = s.replace(/^\.+/, ""); // a component may not begin with "."
90
+ s = s.replace(/\.lock$/i, "-lock"); // nor end with ".lock"
91
+ s = s.replace(/\.+$/, ""); // nor end with a dot
82
92
  return s || "default";
83
93
  }
84
94
 
@@ -86,9 +96,20 @@ function sessionPrefix(session) {
86
96
  return `${REF_NS}/${sanitizeSession(session)}`;
87
97
  }
88
98
 
99
+ // Monotonic per-process counter so two temp-index paths minted in the same
100
+ // millisecond never collide. pid disambiguates across processes; Date.now() +
101
+ // this counter disambiguate within one. A collision would mean two operations
102
+ // sharing one GIT_INDEX_FILE (corrupt snapshot) or one's cleanup deleting the
103
+ // other's live index.
104
+ let _indexSeq = 0;
105
+
89
106
  /** Unique temp index path inside .git (never the real index). */
90
107
  function tempIndexPath(dir) {
91
- return path.join(dir, `cc-checkpoint-index-${process.pid}-${Date.now()}`);
108
+ _indexSeq = (_indexSeq + 1) >>> 0;
109
+ return path.join(
110
+ dir,
111
+ `cc-checkpoint-index-${process.pid}-${Date.now()}-${_indexSeq}`,
112
+ );
92
113
  }
93
114
 
94
115
  /**
@@ -256,6 +277,29 @@ export function createCheckpoint(cwd = process.cwd(), opts = {}) {
256
277
  /* non-critical */
257
278
  }
258
279
 
280
+ // Bound the session's checkpoint history when asked. Auto-checkpointing
281
+ // (one ref per mutating tool) passes a cap so a long agentic run can't
282
+ // accumulate unbounded refs — which also keeps nextId's per-create
283
+ // for-each-ref scan bounded. Best-effort: pruning never fails the
284
+ // checkpoint, and the dropped commit objects stay reachable via newer
285
+ // checkpoints' parent chain (only addressability by id is lost for the
286
+ // oldest entries). Manual `cc checkpoint create` omits the cap → unbounded.
287
+ if (Number.isFinite(opts.maxPerSession) && opts.maxPerSession > 0) {
288
+ try {
289
+ const rows = listRefs(root, session); // oldest-first (creatordate)
290
+ const excess = rows.length - opts.maxPerSession;
291
+ for (let i = 0; i < excess; i++) {
292
+ try {
293
+ git(["update-ref", "-d", rows[i].ref], { cwd: root });
294
+ } catch {
295
+ /* best-effort — a failed prune never affects the new checkpoint */
296
+ }
297
+ }
298
+ } catch {
299
+ /* pruning is entirely best-effort */
300
+ }
301
+ }
302
+
259
303
  return { id, ref, commit, tree, parent, label, session, createdAt, files };
260
304
  } finally {
261
305
  rmQuiet(tmpIndex);
@@ -557,4 +601,10 @@ export function clearCheckpoints(cwd = process.cwd(), opts = {}) {
557
601
  }
558
602
 
559
603
  // Exposed for unit tests / advanced callers.
560
- export const _internals = { git, repoRoot, sanitizeSession, snapshotTree };
604
+ export const _internals = {
605
+ git,
606
+ repoRoot,
607
+ sanitizeSession,
608
+ snapshotTree,
609
+ tempIndexPath,
610
+ };