chainlesschain 0.162.125 → 0.162.127

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 (192) hide show
  1. package/package.json +2 -2
  2. package/src/assets/web-panel/assets/{AIOps-DcqNhZhA.js → AIOps-CsHJh6tT.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-BigtmPoq.js → ActionButton-ClDr78Wl.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-1J_X_HF4.js → Analytics-CQQBX5mv.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-DlcDG_a_.js → AppLayout-DIofBNeG.js} +5 -5
  6. package/src/assets/web-panel/assets/{Audit-Do-y8_3w.js → Audit-d8pxGJLK.js} +1 -1
  7. package/src/assets/web-panel/assets/{Backup-S2Df-50Y.js → Backup-ChZb31ek.js} +1 -1
  8. package/src/assets/web-panel/assets/{BaseInput-D5kgWJfk.js → BaseInput-CQsSXb5v.js} +1 -1
  9. package/src/assets/web-panel/assets/{Chat-aUcp3kN1.js → Chat-C_mjILx8.js} +6 -6
  10. package/src/assets/web-panel/assets/ChatBubbleRenderer-B545kWla.js +1 -0
  11. package/src/assets/web-panel/assets/{Checkbox-DTg1U7Xs.js → Checkbox-CWFmVuo8.js} +1 -1
  12. package/src/assets/web-panel/assets/{Codegen-YO9ZZ4Ky.js → Codegen-DwicePOQ.js} +1 -1
  13. package/src/assets/web-panel/assets/{Col-BHonE9fU.js → Col-B36SnRdL.js} +1 -1
  14. package/src/assets/web-panel/assets/{Community-DA2AlEFh.js → Community-C_sZ2HhK.js} +1 -1
  15. package/src/assets/web-panel/assets/{Compact-DULxLRNg.js → Compact-B2jzDcUl.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compliance-BZqfuuZL.js → Compliance-BD58_IHe.js} +1 -1
  17. package/src/assets/web-panel/assets/{Cowork-CJe3vyMS.js → Cowork-VIBdGc4D.js} +2 -2
  18. package/src/assets/web-panel/assets/{Cron-CeV8AtRu.js → Cron-CDTVWUmV.js} +2 -2
  19. package/src/assets/web-panel/assets/{Crosschain-CAg66zS5.js → Crosschain-D5DkGNKU.js} +1 -1
  20. package/src/assets/web-panel/assets/{DID-Dtup5JZm.js → DID-B23ae8PQ.js} +2 -2
  21. package/src/assets/web-panel/assets/{Dashboard-DAR_8PNy.js → Dashboard-DTG5MsSx.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dropdown-K5bTaCYN.js → Dropdown-D35RRnMZ.js} +1 -1
  23. package/src/assets/web-panel/assets/{EmailListRenderer-C890uErx.js → EmailListRenderer-DRvY2nPk.js} +1 -1
  24. package/src/assets/web-panel/assets/{FamilyGuardDashboard-B-Tj_8yM.js → FamilyGuardDashboard-BT_eESkk.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-C6WZ98ni.js → Federation-CxX9F7IL.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-D-LTfGWd.js → FormItemContext-C1xDz6D8.js} +1 -1
  27. package/src/assets/web-panel/assets/{GenericCardRenderer-C80DK4W7.js → GenericCardRenderer-CjBOAXcO.js} +1 -1
  28. package/src/assets/web-panel/assets/{Git-Cjvvv3zW.js → Git-I9I9NkPO.js} +2 -2
  29. package/src/assets/web-panel/assets/{Governance-C0BND77H.js → Governance-BiigWxQW.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-BL9kTtSu.js → Inference-C0KrSCgh.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-CnVTBmJf.js → KnowledgeGraph-B1Nd54ow.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-CbCbg7K6.js → Logs-CeOtvYIo.js} +2 -2
  33. package/src/assets/web-panel/assets/{Marketplace-CG5On-fH.js → Marketplace-CRhgvAVQ.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-AYYDZZK7.js → McpTools-BIJNah-H.js} +5 -5
  35. package/src/assets/web-panel/assets/{Memory-tMWbMDQq.js → Memory-D6gsbo4e.js} +2 -2
  36. package/src/assets/web-panel/assets/{MobileBridge-BAOsf4wB.js → MobileBridge-DDkIKA6U.js} +1 -1
  37. package/src/assets/web-panel/assets/{MobileProjects-BT-2komQ.js → MobileProjects-BBkI02Lf.js} +1 -1
  38. package/src/assets/web-panel/assets/{Mtc-BFwfC63n.js → Mtc-BziHK7HS.js} +5 -5
  39. package/src/assets/web-panel/assets/{MtcAudit-DYG3CWdH.js → MtcAudit-CWXjbkeF.js} +2 -2
  40. package/src/assets/web-panel/assets/{Multisig-KJwd0yWT.js → Multisig-9jNF_VWa.js} +3 -3
  41. package/src/assets/web-panel/assets/{NLProgramming-BmL5dNWm.js → NLProgramming-BUdF7I2G.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-DmkaVaMc.js → Notes-D5ls1r1T.js} +3 -3
  43. package/src/assets/web-panel/assets/{NotificationSettings-tmVQszDZ.js → NotificationSettings-B2WX_TMX.js} +1 -1
  44. package/src/assets/web-panel/assets/{OrderTableRenderer-BtrR7anf.js → OrderTableRenderer-COSzdTcE.js} +1 -1
  45. package/src/assets/web-panel/assets/{Organization-pppktQyW.js → Organization-tzWIgnbM.js} +4 -4
  46. package/src/assets/web-panel/assets/{Overflow-D3lBoQDA.js → Overflow-Ca1gp_3S.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P-BgE2qGlZ.js → P2P-XnfDfRK9.js} +2 -2
  48. package/src/assets/web-panel/assets/{PdhVaultBrowser-D26Bz5gS.js → PdhVaultBrowser-23Susb25.js} +3 -3
  49. package/src/assets/web-panel/assets/{Permissions-BR8no2Xe.js → Permissions-CnkhYPqt.js} +4 -4
  50. package/src/assets/web-panel/assets/{PersonalDataHub-DabbyQEF.js → PersonalDataHub-CZ1UasJc.js} +2 -2
  51. package/src/assets/web-panel/assets/{Pipeline-n4sNpEz8.js → Pipeline-DC1oC84K.js} +1 -1
  52. package/src/assets/web-panel/assets/{Privacy-CGNp4n8M.js → Privacy-CdqayaCE.js} +1 -1
  53. package/src/assets/web-panel/assets/{ProjectInit-CAVA5VsX.js → ProjectInit-DS7NbGQL.js} +2 -2
  54. package/src/assets/web-panel/assets/{ProjectSettings-GCgkfM7A.js → ProjectSettings-fdzwup3n.js} +2 -2
  55. package/src/assets/web-panel/assets/{Projects-BYK17p52.js → Projects-FvodeZob.js} +1 -1
  56. package/src/assets/web-panel/assets/{Providers-DpUT5W-d.js → Providers-IxYyzeSI.js} +1 -1
  57. package/src/assets/web-panel/assets/{QuickAsk-ZJxTb7vi.js → QuickAsk-BAtFET-B.js} +1 -1
  58. package/src/assets/web-panel/assets/{Recommend-CIzFRDk1.js → Recommend-DCNRV9Vr.js} +1 -1
  59. package/src/assets/web-panel/assets/{Reputation-lQ_WDNZn.js → Reputation-lg2JmIai.js} +1 -1
  60. package/src/assets/web-panel/assets/{Row-1MEtrgtF.js → Row-BAhyDuri.js} +1 -1
  61. package/src/assets/web-panel/assets/{RssFeed-SSgcPPl4.js → RssFeed-BA_3issD.js} +2 -2
  62. package/src/assets/web-panel/assets/{Search-CcTMOGNY.js → Search-CJheUJDl.js} +1 -1
  63. package/src/assets/web-panel/assets/{Security-BA1KMaDx.js → Security-D2VczQDD.js} +3 -3
  64. package/src/assets/web-panel/assets/{Services-_aj7czZn.js → Services-7ZFAzqVR.js} +2 -2
  65. package/src/assets/web-panel/assets/{Skeleton-BWgg_0Zr.js → Skeleton-CsBoTasD.js} +1 -1
  66. package/src/assets/web-panel/assets/{Skills-C2ZIziYM.js → Skills-DT_j_gj0.js} +1 -1
  67. package/src/assets/web-panel/assets/{Sla-CYxp4axY.js → Sla-C9D5geJx.js} +1 -1
  68. package/src/assets/web-panel/assets/{SpeechSettings-C7Csp1jV.js → SpeechSettings-CWCNVW42.js} +1 -1
  69. package/src/assets/web-panel/assets/{SyncSettings-svGeswiw.js → SyncSettings-TYCbf2kk.js} +2 -2
  70. package/src/assets/web-panel/assets/{Tasks-ClISj6oK.js → Tasks-CbRoXu1G.js} +1 -1
  71. package/src/assets/web-panel/assets/{Templates-CmO-Fp7r.js → Templates-CxBjIrkJ.js} +1 -1
  72. package/src/assets/web-panel/assets/{Tenant-a3Ac3lxz.js → Tenant-NKVyFsPM.js} +1 -1
  73. package/src/assets/web-panel/assets/{Terminal-DyJIRh81.js → Terminal-D5aAL5_H.js} +2 -2
  74. package/src/assets/web-panel/assets/TimelineRenderer-CD1JE0Rb.js +1 -0
  75. package/src/assets/web-panel/assets/{Tokens-DJW0KqV4.js → Tokens-CdepBSYe.js} +1 -1
  76. package/src/assets/web-panel/assets/{Trigger-0HeTi4r6.js → Trigger-Br3n_PEh.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trust-aFym34eo.js → Trust-CSac0pMf.js} +1 -1
  78. package/src/assets/web-panel/assets/{UkeySign-CYhszHJv.js → UkeySign-BiWo43zc.js} +1 -1
  79. package/src/assets/web-panel/assets/{VideoEditing-BbJxPF9X.js → VideoEditing-Dj50GuoF.js} +1 -1
  80. package/src/assets/web-panel/assets/{Wallet-DDthEwiu.js → Wallet-CQK_g4_U.js} +4 -4
  81. package/src/assets/web-panel/assets/{WebAuthn-DQ6McYPg.js → WebAuthn-D8mBl1NC.js} +3 -3
  82. package/src/assets/web-panel/assets/{WorkflowEditor-CnF8zRsi.js → WorkflowEditor-CHeS_vab.js} +1 -1
  83. package/src/assets/web-panel/assets/{chat-Dzkx2gTP.js → chat-CbeOf_lH.js} +1 -1
  84. package/src/assets/web-panel/assets/{colors-35T51Oo5.js → colors-CTFiyfed.js} +1 -1
  85. package/src/assets/web-panel/assets/{compact-item-TtzNrlu1.js → compact-item-CZsh8FXE.js} +1 -1
  86. package/src/assets/web-panel/assets/{createContext-Y1LkWrYb.js → createContext-UPhnXsap.js} +1 -1
  87. package/src/assets/web-panel/assets/devWarning-BFRqfgsk.js +1 -0
  88. package/src/assets/web-panel/assets/{hasIn-BOYw1BgS.js → hasIn-CCqQtSY-.js} +1 -1
  89. package/src/assets/web-panel/assets/{index-D2od7Bgx.js → index-1d4pat4L.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-NM9Oke2k.js → index-86dmMEZY.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-Dof7lWA_.js → index-B-OcSTNA.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-D7dCBw_M.js → index-B1tylGKq.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-CqhwG1ox.js → index-BAcem_Qs.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-CZ16GlOs.js → index-BKADY9Iw.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CPGNc2tF.js → index-Be0uH7m1.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DAizH273.js → index-Bga0UuWA.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DFhlelzN.js → index-Blgzobgm.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-D6tG4B25.js → index-BwPULPuj.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DjeUZxE5.js → index-CIQVlE9u.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CLqC2sRT.js → index-CMi91z74.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-hyqEKkBT.js → index-CNteMctn.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cs82BHAj.js → index-CQHQiYAO.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-zF77jnI2.js → index-CVKFSFkX.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-G4ClSmJc.js → index-CctoAd9I.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DhMSOd_2.js → index-CdiuRSqw.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BWgNn9As.js → index-Cy83zglX.js} +3 -3
  107. package/src/assets/web-panel/assets/{index-Bw1iOGhM.js → index-DBJjqjxL.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-D4BgCBa3.js → index-DFbyobqi.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BKRlWHUp.js → index-DcJxrM1F.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-B7aKAZzS.js → index-DgXn2sWa.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-D47robkX.js → index-Dkn1r09O.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CVrUs6rf.js → index-DmScDPXc.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Bs69SI96.js → index-DpTaXoo4.js} +1 -1
  114. package/src/assets/web-panel/assets/index-DqADH38Q.js +1 -0
  115. package/src/assets/web-panel/assets/{index-BxWUEFKy.js → index-DvaDSd9k.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BtlJWaSP.js → index-LIFKNtNq.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BGPEaeB1.js → index-LyzbfmcN.js} +1 -1
  118. package/src/assets/web-panel/assets/index-Pbx8iJt_.js +1 -0
  119. package/src/assets/web-panel/assets/{index-KgXrlfNF.js → index-WGmri9tu.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-XIuZV9by.js → index-Yd1x-xhs.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Dhfw-BXs.js → index-_OOipSkX.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-Dx0hIfC-.js → index-dwBroN51.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-C7Wt5feN.js → index-e6tj_vqt.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CdyFg4FI.js → index-lOxvi-gf.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-8jffdb6b.js → index-qTrskpW-.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DmkG479D.js → index-uH-glcG6.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BSpFe6j5.js → index-z5pyzCXj.js} +1 -1
  128. package/src/assets/web-panel/assets/{initDefaultProps-DD5y5msp.js → initDefaultProps-K95PVa57.js} +1 -1
  129. package/src/assets/web-panel/assets/{motion-BuoutMia.js → motion-DwTSyvvW.js} +1 -1
  130. package/src/assets/web-panel/assets/{move-DkpjqOXg.js → move-BuRSogkN.js} +1 -1
  131. package/src/assets/web-panel/assets/{mtc-parser-BmGJD-v-.js → mtc-parser-twEnPl1V.js} +1 -1
  132. package/src/assets/web-panel/assets/{omit-DftUE0Sz.js → omit-CdPhebkX.js} +1 -1
  133. package/src/assets/web-panel/assets/{pickAttrs-DDRb9FIu.js → pickAttrs-CGcIpdPi.js} +1 -1
  134. package/src/assets/web-panel/assets/{placementArrow-DPgtxOOD.js → placementArrow-DixkX9VV.js} +1 -1
  135. package/src/assets/web-panel/assets/{responsiveObserve-BQFRzFeZ.js → responsiveObserve-C74t3rB5.js} +1 -1
  136. package/src/assets/web-panel/assets/{slide-DQ4lmUSz.js → slide-TcNt6K6l.js} +1 -1
  137. package/src/assets/web-panel/assets/{statusUtils-C1TR4ZiQ.js → statusUtils-mpTN9mrS.js} +1 -1
  138. package/src/assets/web-panel/assets/{styleChecker-B-Suj978.js → styleChecker-Bwg8zmIj.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFlexGapSupport-Q7bDZ3EI.js → useFlexGapSupport-Cz4mN4sW.js} +1 -1
  140. package/src/assets/web-panel/assets/{useFs-DHjRLyQH.js → useFs-DF9zRfbI.js} +1 -1
  141. package/src/assets/web-panel/assets/{usePersonalDataHub-lQOvCvCr.js → usePersonalDataHub-BcQ4O2UK.js} +1 -1
  142. package/src/assets/web-panel/assets/{vnode-Ie0g4-p3.js → vnode-juM2dgAV.js} +1 -1
  143. package/src/assets/web-panel/assets/{zoom-CHP0YEoH.js → zoom-c4aJj-vC.js} +1 -1
  144. package/src/assets/web-panel/index.html +1 -1
  145. package/src/commands/agent.js +41 -2
  146. package/src/commands/compliance.js +3 -1
  147. package/src/commands/review.js +47 -17
  148. package/src/commands/session.js +11 -2
  149. package/src/gateways/ws/message-dispatcher.js +145 -165
  150. package/src/gateways/ws/ws-session-gateway.js +39 -5
  151. package/src/harness/jsonl-session-store.js +39 -12
  152. package/src/harness/worktree-isolator.js +5 -0
  153. package/src/lib/agent-core.js +1 -0
  154. package/src/lib/audit-logger.js +3 -1
  155. package/src/lib/chat-core.js +5 -2
  156. package/src/lib/chat-intent-service.js +68 -21
  157. package/src/lib/config-manager.js +25 -1
  158. package/src/lib/credential-guard.js +43 -1
  159. package/src/lib/git-integration.js +5 -0
  160. package/src/lib/interaction-adapter.js +32 -11
  161. package/src/lib/jsonl-session-store.js +1 -0
  162. package/src/lib/llm-config-defaults.js +37 -0
  163. package/src/lib/mcp-oauth.js +76 -10
  164. package/src/lib/permission-engine.js +4 -1
  165. package/src/lib/personal-data-hub-wiring.js +14 -0
  166. package/src/lib/pipeline-orchestrator.js +20 -2
  167. package/src/lib/project-detector.js +44 -3
  168. package/src/lib/project-instructions.js +13 -0
  169. package/src/lib/repl-denials.js +137 -0
  170. package/src/lib/safe-json.js +22 -0
  171. package/src/lib/sandbox-v2.js +7 -6
  172. package/src/lib/search-command.js +65 -0
  173. package/src/lib/settings-hooks.cjs +133 -0
  174. package/src/lib/settings-loader.cjs +16 -7
  175. package/src/lib/social-graph.js +3 -1
  176. package/src/lib/stream-retry.js +27 -0
  177. package/src/lib/turn-context.js +15 -5
  178. package/src/repl/agent-repl.js +81 -8
  179. package/src/runtime/__tests__/message-roles.test.js +36 -3
  180. package/src/runtime/agent-core.js +175 -50
  181. package/src/runtime/coding-agent-contract-shared.cjs +9 -2
  182. package/src/runtime/coding-agent-shell-policy.cjs +23 -2
  183. package/src/runtime/file-ref-expander.js +51 -7
  184. package/src/runtime/headless-runner.js +83 -2
  185. package/src/runtime/headless-stream.js +69 -3
  186. package/src/runtime/mcp-config.js +42 -1
  187. package/src/runtime/message-roles.js +14 -1
  188. package/src/assets/web-panel/assets/ChatBubbleRenderer-Pg7dIsiE.js +0 -1
  189. package/src/assets/web-panel/assets/TimelineRenderer-acXS5N5h.js +0 -1
  190. package/src/assets/web-panel/assets/devWarning-D_lwLZ6O.js +0 -1
  191. package/src/assets/web-panel/assets/index-CEwuCM44.js +0 -1
  192. package/src/assets/web-panel/assets/index-DzzgU4IU.js +0 -1
@@ -106,199 +106,179 @@ export function createWsMessageDispatcher(server) {
106
106
  "llm.chat": () => server._handleLlmChat(id, ws, message),
107
107
  };
108
108
 
109
- // Phase I Hosted Session API streaming routes (stream.run).
110
- // Each intermediate event goes out as { id, type: "stream.event", event }
111
- // and the final response is sent by the normal ok/err wrapper.
112
- for (const streamingType of Object.keys(
113
- SESSION_CORE_STREAMING_HANDLERS,
114
- )) {
115
- routes[streamingType] = async () => {
116
- const controller = new AbortController();
117
- const client = server.clients.get(clientId);
118
- if (client) {
119
- client._streamAborts = client._streamAborts || new Map();
120
- client._streamAborts.set(id, controller);
121
- }
122
- const sender = (payload) => server._send(ws, { id, ...payload });
123
- const context = { server, ws, clientId };
124
- try {
125
- const result = await SESSION_CORE_STREAMING_HANDLERS[streamingType](
126
- message,
127
- sender,
128
- controller.signal,
129
- context,
130
- );
131
- server._send(ws, {
132
- id,
133
- type: `${streamingType}.end`,
134
- ...result,
135
- });
136
- } catch (err) {
137
- server._send(ws, {
138
- id,
139
- type: "error",
140
- code: "STREAM_RUN_ERROR",
141
- message: err?.message || String(err),
142
- });
143
- } finally {
144
- if (client?._streamAborts) client._streamAborts.delete(id);
145
- }
146
- };
109
+ // Static core routes take precedence and are dispatched directly. The
110
+ // feature handler maps below are consulted ONLY when no static route
111
+ // matches so a feature topic can never shadow a core protocol route
112
+ // (auth/cancel/ping/execute), and we no longer allocate a closure for all
113
+ // ~79 feature keys on every message (the old per-message Object.keys loops
114
+ // built the entire route table each dispatch). Only the matched handler
115
+ // runs.
116
+ const staticHandler = routes[type];
117
+ if (staticHandler) {
118
+ return staticHandler();
119
+ }
120
+
121
+ // Phase I — Hosted Session API streaming routes (stream.run). Each
122
+ // intermediate event goes out as { id, type:"stream.event", event } and
123
+ // the final response is sent by the normal ok/err wrapper.
124
+ if (SESSION_CORE_STREAMING_HANDLERS[type]) {
125
+ const controller = new AbortController();
126
+ const client = server.clients.get(clientId);
127
+ if (client) {
128
+ client._streamAborts = client._streamAborts || new Map();
129
+ client._streamAborts.set(id, controller);
130
+ }
131
+ const sender = (payload) => server._send(ws, { id, ...payload });
132
+ const context = { server, ws, clientId };
133
+ try {
134
+ const result = await SESSION_CORE_STREAMING_HANDLERS[type](
135
+ message,
136
+ sender,
137
+ controller.signal,
138
+ context,
139
+ );
140
+ server._send(ws, { id, type: `${type}.end`, ...result });
141
+ } catch (err) {
142
+ server._send(ws, {
143
+ id,
144
+ type: "error",
145
+ code: "STREAM_RUN_ERROR",
146
+ message: err?.message || String(err),
147
+ });
148
+ } finally {
149
+ if (client?._streamAborts) client._streamAborts.delete(id);
150
+ }
151
+ return;
147
152
  }
148
153
 
149
154
  // Video Editing streaming routes
150
- for (const videoStreamType of Object.keys(VIDEO_STREAMING_HANDLERS)) {
151
- routes[videoStreamType] = async () => {
152
- const controller = new AbortController();
153
- const client = server.clients.get(clientId);
154
- if (client) {
155
- client._streamAborts = client._streamAborts || new Map();
156
- client._streamAborts.set(id, controller);
157
- }
158
- const sender = (payload) => server._send(ws, { id, ...payload });
159
- try {
160
- const result = await VIDEO_STREAMING_HANDLERS[videoStreamType](
161
- message,
162
- sender,
163
- controller.signal,
164
- );
165
- server._send(ws, {
166
- id,
167
- type: `${videoStreamType}.end`,
168
- ...result,
169
- });
170
- } catch (err) {
171
- server._send(ws, {
172
- id,
173
- type: "error",
174
- code: "VIDEO_STREAM_ERROR",
175
- message: err?.message || String(err),
176
- });
177
- } finally {
178
- if (client?._streamAborts) client._streamAborts.delete(id);
179
- }
180
- };
155
+ if (VIDEO_STREAMING_HANDLERS[type]) {
156
+ const controller = new AbortController();
157
+ const client = server.clients.get(clientId);
158
+ if (client) {
159
+ client._streamAborts = client._streamAborts || new Map();
160
+ client._streamAborts.set(id, controller);
161
+ }
162
+ const sender = (payload) => server._send(ws, { id, ...payload });
163
+ try {
164
+ const result = await VIDEO_STREAMING_HANDLERS[type](
165
+ message,
166
+ sender,
167
+ controller.signal,
168
+ );
169
+ server._send(ws, { id, type: `${type}.end`, ...result });
170
+ } catch (err) {
171
+ server._send(ws, {
172
+ id,
173
+ type: "error",
174
+ code: "VIDEO_STREAM_ERROR",
175
+ message: err?.message || String(err),
176
+ });
177
+ } finally {
178
+ if (client?._streamAborts) client._streamAborts.delete(id);
179
+ }
180
+ return;
181
181
  }
182
182
 
183
183
  // Video Editing request/response routes
184
- for (const videoType of Object.keys(VIDEO_HANDLERS)) {
185
- routes[videoType] = async () => {
186
- try {
187
- const result = await VIDEO_HANDLERS[videoType](message);
188
- server._send(ws, {
189
- id,
190
- type: `${videoType}.response`,
191
- ...result,
192
- });
193
- } catch (err) {
194
- server._send(ws, {
195
- id,
196
- type: "error",
197
- code: "VIDEO_ERROR",
198
- message: err?.message || String(err),
199
- });
200
- }
201
- };
184
+ if (VIDEO_HANDLERS[type]) {
185
+ try {
186
+ const result = await VIDEO_HANDLERS[type](message);
187
+ server._send(ws, { id, type: `${type}.response`, ...result });
188
+ } catch (err) {
189
+ server._send(ws, {
190
+ id,
191
+ type: "error",
192
+ code: "VIDEO_ERROR",
193
+ message: err?.message || String(err),
194
+ });
195
+ }
196
+ return;
202
197
  }
203
198
 
204
199
  // Personal Data Hub topics — same 10 surfaces as the Electron IPC
205
200
  // (see desktop-app-vue/src/main/ipc/personal-data-hub-ipc.js). Each
206
201
  // handler returns { result } or { error }; we wrap with the standard
207
202
  // response envelope so renderer code is shell-agnostic.
208
- for (const hubType of Object.keys(PERSONAL_DATA_HUB_HANDLERS)) {
209
- routes[hubType] = async () => {
210
- try {
211
- const out = await PERSONAL_DATA_HUB_HANDLERS[hubType](message);
212
- if (out && out.error) {
213
- server._send(ws, {
214
- id,
215
- type: "error",
216
- code: "PERSONAL_DATA_HUB_ERROR",
217
- message: out.error,
218
- });
219
- } else {
220
- server._send(ws, {
221
- id,
222
- type: `${hubType}.response`,
223
- result: out && out.result !== undefined ? out.result : out,
224
- });
225
- }
226
- } catch (err) {
203
+ if (PERSONAL_DATA_HUB_HANDLERS[type]) {
204
+ try {
205
+ const out = await PERSONAL_DATA_HUB_HANDLERS[type](message);
206
+ if (out && out.error) {
227
207
  server._send(ws, {
228
208
  id,
229
209
  type: "error",
230
210
  code: "PERSONAL_DATA_HUB_ERROR",
231
- message: err?.message || String(err),
232
- });
233
- }
234
- };
235
- }
236
-
237
- // Phase 5.7 — streaming sync routes. Adapter progress events are
238
- // pushed as `<topic>.event` messages tagged with the request id;
239
- // the final report comes as `<topic>.end`.
240
- for (const streamType of Object.keys(
241
- PERSONAL_DATA_HUB_STREAMING_HANDLERS,
242
- )) {
243
- routes[streamType] = async () => {
244
- const sender = (payload) => server._send(ws, { id, ...payload });
245
- try {
246
- const out = await PERSONAL_DATA_HUB_STREAMING_HANDLERS[streamType](
247
- message,
248
- sender,
249
- );
250
- server._send(ws, {
251
- id,
252
- type: `${streamType}.end`,
253
- ...(out && out.result !== undefined
254
- ? { result: out.result }
255
- : out || {}),
211
+ message: out.error,
256
212
  });
257
- } catch (err) {
213
+ } else {
258
214
  server._send(ws, {
259
215
  id,
260
- type: "error",
261
- code: "PERSONAL_DATA_HUB_STREAM_ERROR",
262
- message: err?.message || String(err),
216
+ type: `${type}.response`,
217
+ result: out && out.result !== undefined ? out.result : out,
263
218
  });
264
219
  }
265
- };
220
+ } catch (err) {
221
+ server._send(ws, {
222
+ id,
223
+ type: "error",
224
+ code: "PERSONAL_DATA_HUB_ERROR",
225
+ message: err?.message || String(err),
226
+ });
227
+ }
228
+ return;
266
229
  }
267
230
 
268
- // Phase IHosted Session API (session-core, memory, beta, usage)
269
- for (const sessionCoreType of Object.keys(SESSION_CORE_HANDLERS)) {
270
- routes[sessionCoreType] = async () => {
271
- try {
272
- const result =
273
- await SESSION_CORE_HANDLERS[sessionCoreType](message);
274
- server._send(ws, {
275
- id,
276
- type: `${sessionCoreType}.response`,
277
- ...result,
278
- });
279
- } catch (err) {
280
- server._send(ws, {
281
- id,
282
- type: "error",
283
- code: "SESSION_CORE_ERROR",
284
- message: err?.message || String(err),
285
- });
286
- }
287
- };
231
+ // Phase 5.7streaming sync routes. Adapter progress events are pushed
232
+ // as `<topic>.event` messages tagged with the request id; the final
233
+ // report comes as `<topic>.end`.
234
+ if (PERSONAL_DATA_HUB_STREAMING_HANDLERS[type]) {
235
+ const sender = (payload) => server._send(ws, { id, ...payload });
236
+ try {
237
+ const out = await PERSONAL_DATA_HUB_STREAMING_HANDLERS[type](
238
+ message,
239
+ sender,
240
+ );
241
+ server._send(ws, {
242
+ id,
243
+ type: `${type}.end`,
244
+ ...(out && out.result !== undefined
245
+ ? { result: out.result }
246
+ : out || {}),
247
+ });
248
+ } catch (err) {
249
+ server._send(ws, {
250
+ id,
251
+ type: "error",
252
+ code: "PERSONAL_DATA_HUB_STREAM_ERROR",
253
+ message: err?.message || String(err),
254
+ });
255
+ }
256
+ return;
288
257
  }
289
258
 
290
- const handler = routes[type];
291
- if (!handler) {
292
- server._send(ws, {
293
- id,
294
- type: "error",
295
- code: "UNKNOWN_TYPE",
296
- message: `Unknown message type: ${type}`,
297
- });
259
+ // Phase I — Hosted Session API (session-core, memory, beta, usage)
260
+ if (SESSION_CORE_HANDLERS[type]) {
261
+ try {
262
+ const result = await SESSION_CORE_HANDLERS[type](message);
263
+ server._send(ws, { id, type: `${type}.response`, ...result });
264
+ } catch (err) {
265
+ server._send(ws, {
266
+ id,
267
+ type: "error",
268
+ code: "SESSION_CORE_ERROR",
269
+ message: err?.message || String(err),
270
+ });
271
+ }
298
272
  return;
299
273
  }
300
274
 
301
- return handler();
275
+ server._send(ws, {
276
+ id,
277
+ type: "error",
278
+ code: "UNKNOWN_TYPE",
279
+ message: `Unknown message type: ${type}`,
280
+ });
281
+ return;
302
282
  },
303
283
  };
304
284
  }
@@ -123,6 +123,33 @@ export class WSSessionManager {
123
123
  this.maxPendingPatches = Number.isFinite(options.maxPendingPatches)
124
124
  ? options.maxPendingPatches
125
125
  : 50;
126
+ // The entry-count caps above bound how MANY patches are kept, but a SINGLE
127
+ // proposed patch (a client-reachable `patch-propose`) could still carry an
128
+ // unbounded number of files, each with unbounded before/after/diff content
129
+ // — all held in memory AND serialized to disk by _persistSessionState. One
130
+ // file with a multi-GB diff string is enough to OOM. Bound a single patch's
131
+ // payload: cap the file count and truncate each content field.
132
+ this.maxPatchFiles = Number.isFinite(options.maxPatchFiles)
133
+ ? options.maxPatchFiles
134
+ : 200;
135
+ this.maxPatchContentChars = Number.isFinite(options.maxPatchContentChars)
136
+ ? options.maxPatchContentChars
137
+ : 512 * 1024; // 512 KB per before/after/diff field
138
+ }
139
+
140
+ /**
141
+ * Truncate a patch content field (before/after/diff) to maxPatchContentChars,
142
+ * appending a visible marker. Preserves the null-vs-string distinction (a null
143
+ * field stays null — it signals create/delete ops). Pure.
144
+ */
145
+ _capPatchContent(value) {
146
+ if (value == null) return null;
147
+ const s = String(value);
148
+ if (s.length <= this.maxPatchContentChars) return s;
149
+ return (
150
+ s.slice(0, this.maxPatchContentChars) +
151
+ `\n…[truncated for storage: field was ${s.length} chars]`
152
+ );
126
153
  }
127
154
 
128
155
  _normalizeEnabledToolNames(enabledToolNames) {
@@ -854,8 +881,13 @@ export class WSSessionManager {
854
881
  const session = this.sessions.get(sessionId);
855
882
  if (!session) return null;
856
883
 
857
- const files = Array.isArray(payload.files) ? payload.files : [];
858
- if (files.length === 0) return null;
884
+ const rawFiles = Array.isArray(payload.files) ? payload.files : [];
885
+ if (rawFiles.length === 0) return null;
886
+ // Cap the per-patch file count; a proposal touching more than this is
887
+ // pathological for review. Excess files are dropped (count recorded below)
888
+ // so one message can't pin unbounded memory / disk.
889
+ const files = rawFiles.slice(0, this.maxPatchFiles);
890
+ const droppedFiles = rawFiles.length - files.length;
859
891
 
860
892
  const patchId = `patch-${this._generateId()}`;
861
893
  const now = new Date().toISOString();
@@ -866,9 +898,9 @@ export class WSSessionManager {
866
898
  index,
867
899
  path: file.path || `unknown-${index}`,
868
900
  op,
869
- before: file.before == null ? null : String(file.before),
870
- after: file.after == null ? null : String(file.after),
871
- diff: file.diff == null ? null : String(file.diff),
901
+ before: this._capPatchContent(file.before),
902
+ after: this._capPatchContent(file.after),
903
+ diff: this._capPatchContent(file.diff),
872
904
  stats,
873
905
  };
874
906
  });
@@ -895,6 +927,8 @@ export class WSSessionManager {
895
927
  fileCount: normalizedFiles.length,
896
928
  added: totalStats.added,
897
929
  removed: totalStats.removed,
930
+ // Surfaced so the reviewer/UI knows the proposal was capped for storage.
931
+ ...(droppedFiles > 0 ? { droppedFiles } : {}),
898
932
  },
899
933
  };
900
934
 
@@ -113,20 +113,36 @@ export function readEvents(sessionId) {
113
113
  return events;
114
114
  }
115
115
 
116
+ /**
117
+ * A replayable chat message must be a `{ role, content }` object — guard
118
+ * against a corrupt / partially-written / hand-edited event whose `data` is
119
+ * missing, null, or not a message (it would otherwise inject `undefined` into
120
+ * the resumed history and break the next LLM request).
121
+ */
122
+ function isReplayableMessage(m) {
123
+ return Boolean(m) && typeof m === "object" && typeof m.role === "string";
124
+ }
125
+
116
126
  export function rebuildMessages(sessionId) {
117
127
  const events = readEvents(sessionId);
118
128
  const messages = [];
119
129
  let lastCompactIndex = -1;
120
130
 
131
+ // A `compact` event carries the pre-compaction snapshot in data.messages. A
132
+ // malformed compact line (type present but data null / messages not an array)
133
+ // must not crash the resume — skip it and look further back.
121
134
  for (let i = events.length - 1; i >= 0; i--) {
122
- if (events[i].type === "compact" && events[i].data.messages) {
135
+ const e = events[i];
136
+ if (e && e.type === "compact" && Array.isArray(e.data?.messages)) {
123
137
  lastCompactIndex = i;
124
138
  break;
125
139
  }
126
140
  }
127
141
 
128
- if (lastCompactIndex >= 0 && events[lastCompactIndex].data.messages) {
129
- messages.push(...events[lastCompactIndex].data.messages);
142
+ if (lastCompactIndex >= 0) {
143
+ for (const m of events[lastCompactIndex].data.messages) {
144
+ if (isReplayableMessage(m)) messages.push(m);
145
+ }
130
146
  }
131
147
 
132
148
  const startIndex = lastCompactIndex >= 0 ? lastCompactIndex + 1 : 0;
@@ -134,9 +150,11 @@ export function rebuildMessages(sessionId) {
134
150
  for (let i = startIndex; i < events.length; i++) {
135
151
  const event = events[i];
136
152
  if (
137
- event.type === "user_message" ||
138
- event.type === "assistant_message" ||
139
- event.type === "system"
153
+ event &&
154
+ (event.type === "user_message" ||
155
+ event.type === "assistant_message" ||
156
+ event.type === "system") &&
157
+ isReplayableMessage(event.data)
140
158
  ) {
141
159
  messages.push(event.data);
142
160
  }
@@ -145,6 +163,19 @@ export function rebuildMessages(sessionId) {
145
163
  return messages;
146
164
  }
147
165
 
166
+ /** ISO string for a numeric ms timestamp, or "" when missing / non-finite /
167
+ * invalid — `new Date(undefined).toISOString()` (and `new Date("garbage")`)
168
+ * throw "Invalid time value", and one corrupt event must not crash a whole
169
+ * `cc session list` / `cc session search`. Exported so command-layer readers of
170
+ * the same (hand-editable) JSONL share the guard. */
171
+ export function toIsoSafe(ts) {
172
+ if (ts == null) return ""; // null/undefined → "" (Number(null) is 0 = epoch)
173
+ const n = Number(ts);
174
+ if (!Number.isFinite(n)) return "";
175
+ const d = new Date(n);
176
+ return Number.isNaN(d.getTime()) ? "" : d.toISOString();
177
+ }
178
+
148
179
  export function listJsonlSessions(options = {}) {
149
180
  const dir = getSessionsDir();
150
181
  if (!existsSync(dir)) return [];
@@ -167,12 +198,8 @@ export function listJsonlSessions(options = {}) {
167
198
  provider: startEvent?.data?.provider || "",
168
199
  model: startEvent?.data?.model || "",
169
200
  message_count: messageCount,
170
- created_at: startEvent
171
- ? new Date(startEvent.timestamp).toISOString()
172
- : "",
173
- updated_at: lastEvent
174
- ? new Date(lastEvent.timestamp).toISOString()
175
- : "",
201
+ created_at: toIsoSafe(startEvent?.timestamp),
202
+ updated_at: toIsoSafe(lastEvent?.timestamp),
176
203
  _lastTs: lastEvent?.timestamp || 0,
177
204
  _eventCount: events.length,
178
205
  };
@@ -550,6 +550,11 @@ function _hasUncommittedChanges(worktreePath) {
550
550
  const output = execSync("git status --porcelain", {
551
551
  cwd: worktreePath,
552
552
  encoding: "utf-8",
553
+ // A worktree with many dirty files produces >1 MB of porcelain output and
554
+ // would ENOBUFS on the default cap — the catch below then reports "clean"
555
+ // (fail-OPEN), which could let a caller discard a worktree full of
556
+ // uncommitted work. Give it the same 64 MB headroom as gitExec.
557
+ maxBuffer: 64 * 1024 * 1024,
553
558
  });
554
559
  return output.trim().length > 0;
555
560
  } catch (_e) {
@@ -29,6 +29,7 @@ export {
29
29
  killBackgroundShellTask,
30
30
  killAllBackgroundShellTasks,
31
31
  reapIdleBackgroundShellTasks,
32
+ _ingestSearchOutput,
32
33
  writeFileVerified,
33
34
  formatProviderHttpError,
34
35
  _accumulateOllamaStream,
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import crypto from "crypto";
7
+ import { safeJsonParse } from "./safe-json.js";
7
8
 
8
9
  /**
9
10
  * Event types for audit logging.
@@ -232,7 +233,8 @@ export function queryLogs(db, filters = {}) {
232
233
  const rows = db.prepare(sql).all(...params);
233
234
  return rows.map((r) => ({
234
235
  ...r,
235
- details: r.details ? JSON.parse(r.details) : null,
236
+ // One corrupt details cell must not throw out of the whole query.
237
+ details: safeJsonParse(r.details, null),
236
238
  success: r.success === 1,
237
239
  }));
238
240
  }
@@ -13,8 +13,8 @@ import { BUILT_IN_PROVIDERS } from "./llm-providers.js";
13
13
  import { appendTokenUsage } from "../harness/jsonl-session-store.js";
14
14
  import {
15
15
  isRetryableStreamError,
16
- STREAM_RETRY_MAX,
17
16
  STREAM_RETRY_BASE_MS,
17
+ resolveStreamRetryMax,
18
18
  } from "./stream-retry.js";
19
19
 
20
20
  // A streaming chat call must not hang forever if the API accepts the connection
@@ -513,6 +513,9 @@ export async function* chatStream(messages, options) {
513
513
  // any token still terminates the generator cleanly.
514
514
  const streamPromise = (async () => {
515
515
  try {
516
+ // Budget honors CC_MAX_RETRIES / CLAUDE_CODE_MAX_RETRIES (capped 15),
517
+ // same as the agent path.
518
+ const maxRetries = resolveStreamRetryMax();
516
519
  let attempt = 0;
517
520
  for (;;) {
518
521
  try {
@@ -524,7 +527,7 @@ export async function* chatStream(messages, options) {
524
527
  // zero-output retry safety (cc agent parity). Stall aborts (180s) and
525
528
  // HTTP/auth errors are not retryable and surface immediately.
526
529
  if (
527
- attempt >= STREAM_RETRY_MAX ||
530
+ attempt >= maxRetries ||
528
531
  tokensEmitted > 0 ||
529
532
  !isRetryableStreamError(err)
530
533
  ) {