chainlesschain 0.166.36 → 0.166.37

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 (174) hide show
  1. package/package.json +2 -2
  2. package/src/assets/web-panel/assets/{AIOps-Zs-zPwVG.js → AIOps-B_Nt1Qe4.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-HZlxM6H0.js → ActionButton-CHQbie4X.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-CXyThRDb.js → Analytics-Bm9364Lw.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-DeyYh_EF.js → AppLayout-DTwlIh2v.js} +4 -4
  6. package/src/assets/web-panel/assets/{Artifacts-BQDtMwkx.js → Artifacts-OibP0qB4.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-DNH4UOux.js → Audit-B8Fwavkg.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-BkKdIxE6.js → BackgroundAgents-p1Vb2K2B.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-DYy4aJom.js → Backup-BI3QT-wI.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-DjoyGVYm.js → BaseInput-BW_VRWWu.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-D86ZYgOD.js → Chat-CUEhDQfq.js} +5 -5
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-RxI_YYth.js → ChatBubbleRenderer-C5UVXdVE.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-C7p8UY99.js → Checkbox-URYsaaxv.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-DnapqgV5.js → Codegen-DJAwC80a.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-C29J2DBT.js → Col-DfRKFzc9.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-Bf3KeLCr.js → Community-GOkGHuly.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-DA83wB48.js → Compact-CIMs13NC.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-CXXVgRFG.js → Compliance-BE-TQIxV.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-B28Ftuqv.js → Cowork-CYmbJiWw.js} +4 -4
  20. package/src/assets/web-panel/assets/{Cron-CO-BcnDf.js → Cron-Be3OIWyM.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-BmgPMFCs.js → Crosschain-2xB2HS2D.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-CYW9WslA.js → DID-B_lJO2yF.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-CWWhVWds.js → Dashboard-DnVWqUER.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-CjTuc1-n.js → Dropdown-C7ZJvHVa.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-BrROAu4d.js → EmailListRenderer-DLevT-0F.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-Ci8Lmk8W.js → FamilyGuardDashboard-BwhQ5BRU.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-CKzKVLE_.js → Federation-BkGI-Xkq.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-CC2CK5PQ.js → FormItemContext-BgIIbMd7.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-BYa3ElyT.js → GenericCardRenderer-qL-qN8Nb.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-Bmn3ucA4.js → Git-BTzgrC4U.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-DqEOXitg.js → Governance-p28vdCN4.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-BjfeExba.js → Inference-Cqephrur.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-CBTyIFlJ.js → KnowledgeGraph-C3v5Q4Zp.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-KZfA_M9Q.js → Logs-T6f0w32w.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-BG7vi3V4.js → MarkdownRenderer-Dj5XPm3S.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-BFeK4he0.js → Marketplace-ClDsvHDH.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-Brg7uUoK.js → McpTools-C4P-_snx.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-Ce4AmyX3.js → Memory-BZZfG6y6.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-Dx1VBL_E.js → MobileBridge-DpIv_BLs.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-DPyRr2Xl.js → MobileProjects-Buz3NIQg.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-BzRmtDQU.js → Mtc-HS2PJB79.js} +4 -4
  42. package/src/assets/web-panel/assets/{MtcAudit-DvZ5kLvJ.js → MtcAudit-UmBrcU27.js} +4 -4
  43. package/src/assets/web-panel/assets/{Multisig-D8uGUjX1.js → Multisig-C00sXiZf.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-igc-TQE5.js → NLProgramming-Dj1ng_ph.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-CTqDYlek.js → Notes-C4LEVKlk.js} +4 -4
  46. package/src/assets/web-panel/assets/{NotificationSettings-CkcLa2rb.js → NotificationSettings-BWXwnekv.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-BfScYvnH.js → OrderTableRenderer-CZHfK8Nn.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-BjKK9SdB.js → Organization-CFEtFPrV.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-BkcghTPE.js → Overflow-DSz4MJK1.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-l9f34Xu8.js → P2P-DyTEytwG.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-yVXWmwWe.js → PdhVaultBrowser-B5QNGLVx.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-DeSYs0VV.js → Permissions-wEC_X7x4.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-CmKmhy0q.js → PersonalDataHub-If5GZlGZ.js} +3 -3
  54. package/src/assets/web-panel/assets/{Pipeline-DBxS4R_r.js → Pipeline-LB4gqqmX.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-CKngdGw3.js → Privacy-QVLhoxcN.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-0Pso0jXN.js → ProjectInit-FaAbTYTo.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-BHrqWQ6r.js → ProjectSettings-DhMeNA21.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-B8wIh9PK.js → Projects-5L4lkWAZ.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-CXFTOHRV.js → Providers-DhZFp8fO.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-DfN0c_BN.js → QrScannerModal-BZuJ7_xi.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-v1Z5hty_.js → QuickAsk-TXddPYYb.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-DOaVmZUi.js → Recommend-DDuXIKYt.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession--EWcPBY1.js → RemoteSession-CDrTXImv.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-DUWFSNvV.js → Reputation-B2xKaKzt.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-0-bFnXWF.js → Row-DzAjWAOX.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-B_uTfubW.js → RssFeed-CKH38BLF.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-CqxiTLfB.js → Search-CuPqJYoQ.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-CGoHOz5B.js → Security-DmAd2hxz.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-COhAbS45.js → Services-Bt3nljnY.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-B97crp6v.js → Skeleton-CP7RH093.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-er_XdavV.js → Skills-DKNeMBTx.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-RpkWkzOL.js → Sla-CbpM8Nrd.js} +1 -1
  73. package/src/assets/web-panel/assets/SpeechSettings-DTk-vTzF.js +1 -0
  74. package/src/assets/web-panel/assets/{SyncSettings-Bbiw6ohw.js → SyncSettings-DKiplwmc.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-CRghr7lo.js → Tasks-z-lOm8fx.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-BUQkfydG.js → Templates-Rb-Q0DGX.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-CfUFOsZu.js → Tenant-C2sqDUnR.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-BGY7gi4M.js → Terminal-DcyzskEE.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-B3Len0l7.js → TimelineRenderer-lg0Eam7N.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-BjeHn2zI.js → Tokens-D9USIMIA.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-ctcZ2mcv.js → Trigger-BQpaQJ5k.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-oncF3TpO.js → Trust-C-Mik04s.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-BwKbVYCd.js → UkeySign-DrnZ_o56.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-D1dgL-3U.js → VideoEditing-BDOsoZi0.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-4hXAfjBL.js → Wallet-Ct_bleI7.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-C8p3cotb.js → WebAuthn-B6b6FDao.js} +3 -3
  87. package/src/assets/web-panel/assets/{WorkflowEditor-BnrjG77E.js → WorkflowEditor-ZUJMaIHr.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-B-YNK7EI.js → chat-BII20OJy.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-B9HYvK-e.js → colors-DEsJRV3n.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-BJr0quBA.js → compact-item-DoDYAzN9.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-CXfbQEWD.js → createContext-o_nofnVW.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-0tgDCszN.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-B7Q02hdZ.js → hasIn-CAsABTHC.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DsV1HA0Z.js → index-B0ApxOMw.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-Gjegl5Ig.js → index-B4SfcwZA.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-BoNVnnAq.js → index-B9Z75LfA.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-C7IZWdoB.js → index-B9hYHPhs.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DPUg6cVc.js → index-BDRh3v44.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DutYFyC0.js → index-BcYbQoY7.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-D1GP2MQz.js → index-Bmwo5wQG.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-kAggAkrB.js → index-Bn2ZPUXO.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DMoUa3dF.js → index-BnbnmRqg.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BmvpVSmM.js → index-Bqyg3PME.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BmCD1_HC.js → index-BrIIJ2s-.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-G7Z_On_n.js → index-BxT8yTwS.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BClzZrmx.js → index-Bycj011d.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-4gZEpDAG.js → index-BzMA6Ra3.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CEujQqdl.js → index-C1YDdyQ1.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BvRqE3Un.js → index-CE2J04dA.js} +1 -1
  110. package/src/assets/web-panel/assets/index-CFF8XEFE.js +1 -0
  111. package/src/assets/web-panel/assets/{index-D5g5_oua.js → index-CQE-WMEU.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CQ7qYZS4.js → index-CVQSl1uY.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BleXwr2w.js → index-CeJSTcXw.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BU_adeUp.js → index-CeWJqXNX.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BNL9lmWL.js → index-Cf3sFEoE.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BtunVZuS.js → index-D5Fd4tdL.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CXgHZ_HG.js → index-DDQzBK3l.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-C3M8M9_U.js → index-DQmX_-hk.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-B6h86qjh.js → index-DRAA2McB.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BB4KIBso.js → index-DU2Rpx1m.js} +1 -1
  121. package/src/assets/web-panel/assets/index-DVUEISry.js +1 -0
  122. package/src/assets/web-panel/assets/{index-cq5Q-RHa.js → index-DWZNYecl.js} +3 -3
  123. package/src/assets/web-panel/assets/{index-COdAugDt.js → index-D_CMB_wC.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-iCKFl_jt.js → index-Dbb34cAr.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Bf0OxvOh.js → index-DciLb_Ww.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D0zuXz4s.js → index-Dgt1mKby.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CcyxVqzn.js → index-Dhnau6uw.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CU7cBzr9.js → index-DrsAF-Ar.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-D_UneDq3.js → index-DsoFTdA2.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-6wWd08X-.js → index-TSwbtCYG.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-HzCaU25O.js → index-kIyLNyoF.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-Bd3nX6aj.js → index-tCeNfxv4.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-DrzGM6fO.js → initDefaultProps-CNjeeoKE.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-SE_xFnGL.js → motion-CBT8IZDm.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-CrPjKNiG.js → move-h1MOxjqv.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-CpcyFPK6.js → mtc-parser-C_3pqNmn.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-BV0g7vf4.js → omit-CMUnPFDk.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-lWpsc-k6.js → pickAttrs-p8_c1xq0.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-CiewvU4X.js → placementArrow-D6O67r23.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-DbF8pamO.js → responsiveObserve-DquhepAK.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-Bn82Ip9-.js → slide-CZ-m6gmS.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-J76gk_-G.js → statusUtils-k9ejZFtd.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-BL95J74a.js → styleChecker-NX6_137R.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-BUzhdQsv.js → useFlexGapSupport-bCqR70lP.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-B2Fppepb.js → useFs-2l0vSqyY.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-DjTASfYE.js → usePersonalDataHub-1yccm4O7.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-BwrOgBqP.js → vnode-CxQzajDo.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-B6MeT_Gw.js → zoom-CrTKwFyb.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/hub.js +21 -6
  151. package/src/commands/stream.js +28 -7
  152. package/src/commands/ui.js +10 -2
  153. package/src/data/changelog.json +13 -0
  154. package/src/gateways/ws/chat-intent-protocol.js +4 -1
  155. package/src/gateways/ws/llm-chat-protocol.js +29 -3
  156. package/src/gateways/ws/message-dispatcher.js +3 -1
  157. package/src/gateways/ws/personal-data-hub-protocol.js +24 -10
  158. package/src/gateways/ws/session-core-protocol.js +23 -6
  159. package/src/gateways/ws/session-protocol.js +1 -0
  160. package/src/lib/__tests__/provider-stream.test.js +6 -8
  161. package/src/lib/chat-core.js +67 -9
  162. package/src/lib/chat-intent-service.js +138 -39
  163. package/src/lib/evolution/evolution-deployment-loader.js +7 -1
  164. package/src/lib/evolution/governed-hub-llm.js +56 -0
  165. package/src/lib/evolution/governed-hub-resolver.js +65 -0
  166. package/src/lib/evolution/governed-hub-skill.js +36 -0
  167. package/src/lib/evolution/governed-model-turn.js +88 -0
  168. package/src/lib/personal-data-hub-wiring.js +49 -0
  169. package/src/lib/provider-stream.js +94 -45
  170. package/src/lib/ws-chat-handler.js +19 -2
  171. package/src/assets/web-panel/assets/SpeechSettings-DDVXmT5y.js +0 -1
  172. package/src/assets/web-panel/assets/devWarning-DNpXMZaQ.js +0 -1
  173. package/src/assets/web-panel/assets/index-2hcdmWpb.js +0 -1
  174. package/src/assets/web-panel/assets/index-DbQCUG4D.js +0 -1
@@ -98,7 +98,11 @@ async function _deliverToken(onToken, token, guard) {
98
98
  try {
99
99
  await onToken(token);
100
100
  } catch (error) {
101
- if (error?.isOutputBackpressureFailure === true) throw error;
101
+ if (
102
+ options.requireCompletion ||
103
+ error?.isOutputBackpressureFailure === true
104
+ )
105
+ throw error;
102
106
  // Generic UI callbacks retain their historical best-effort semantics.
103
107
  } finally {
104
108
  guard.bump();
@@ -117,6 +121,7 @@ export async function streamOllama(
117
121
  onToken,
118
122
  onUsage,
119
123
  onStall,
124
+ options = {},
120
125
  ) {
121
126
  const guard = makeStallGuard(STREAM_STALL_MS, { onHint: onStall });
122
127
  guard.bump();
@@ -130,7 +135,9 @@ export async function streamOllama(
130
135
  messages,
131
136
  stream: true,
132
137
  }),
133
- signal: guard.signal,
138
+ signal: options.signal
139
+ ? AbortSignal.any([guard.signal, options.signal])
140
+ : guard.signal,
134
141
  });
135
142
  } catch (e) {
136
143
  guard.stop();
@@ -149,6 +156,7 @@ export async function streamOllama(
149
156
  const reader = response.body.getReader();
150
157
  const decoder = new TextDecoder();
151
158
  let fullResponse = "";
159
+ let completed = false;
152
160
  // Buffer incomplete lines across reads: an NDJSON line can be split across two
153
161
  // stream chunks (TCP doesn't align to '\n'), so parsing each chunk in
154
162
  // isolation would drop the split line entirely (both halves fail JSON.parse).
@@ -169,6 +177,9 @@ export async function streamOllama(
169
177
  if (!line) continue;
170
178
  try {
171
179
  const json = JSON.parse(line);
180
+ if (options.requireCompletion && json.error)
181
+ throw new Error("Ollama stream returned an error");
182
+ if (json.done === true) completed = true;
172
183
  if (json.message?.content) {
173
184
  fullResponse += json.message.content;
174
185
  await _deliverToken(onToken, json.message.content, guard);
@@ -181,7 +192,11 @@ export async function streamOllama(
181
192
  }
182
193
  }
183
194
  } catch (error) {
184
- if (error?.isOutputBackpressureFailure === true) throw error;
195
+ if (
196
+ options.requireCompletion ||
197
+ error?.isOutputBackpressureFailure === true
198
+ )
199
+ throw error;
185
200
  // Partial JSON, skip
186
201
  }
187
202
  }
@@ -196,6 +211,9 @@ export async function streamOllama(
196
211
  guard.stop();
197
212
  }
198
213
 
214
+ options.signal?.throwIfAborted();
215
+ if (options.requireCompletion && !completed)
216
+ throw new Error("Model stream ended before its completion marker");
199
217
  return fullResponse;
200
218
  }
201
219
 
@@ -210,6 +228,7 @@ export async function streamOpenAI(
210
228
  onToken,
211
229
  onUsage,
212
230
  onStall,
231
+ options = {},
213
232
  ) {
214
233
  const guard = makeStallGuard(STREAM_STALL_MS, { onHint: onStall });
215
234
  guard.bump();
@@ -229,7 +248,9 @@ export async function streamOpenAI(
229
248
  // Servers that don't understand it simply ignore it.
230
249
  stream_options: { include_usage: true },
231
250
  }),
232
- signal: guard.signal,
251
+ signal: options.signal
252
+ ? AbortSignal.any([guard.signal, options.signal])
253
+ : guard.signal,
233
254
  });
234
255
  } catch (e) {
235
256
  guard.stop();
@@ -248,6 +269,7 @@ export async function streamOpenAI(
248
269
  const reader = response.body.getReader();
249
270
  const decoder = new TextDecoder();
250
271
  let fullResponse = "";
272
+ let completed = false;
251
273
  // Buffer incomplete lines across reads: an SSE `data:` line can be split
252
274
  // across two stream chunks, so parsing each chunk in isolation would drop the
253
275
  // split line (both halves fail JSON.parse) — losing content and usage. Keep
@@ -267,9 +289,14 @@ export async function streamOpenAI(
267
289
  for (const line of lines) {
268
290
  if (line.startsWith("data: ")) {
269
291
  const data = line.slice(6);
270
- if (data === "[DONE]") continue;
292
+ if (data === "[DONE]") {
293
+ completed = true;
294
+ continue;
295
+ }
271
296
  try {
272
297
  const json = JSON.parse(data);
298
+ if (options.requireCompletion && json.error)
299
+ throw new Error("OpenAI stream returned an error");
273
300
  const content = json.choices?.[0]?.delta?.content;
274
301
  if (content) {
275
302
  fullResponse += content;
@@ -287,7 +314,11 @@ export async function streamOpenAI(
287
314
  }
288
315
  }
289
316
  } catch (error) {
290
- if (error?.isOutputBackpressureFailure === true) throw error;
317
+ if (
318
+ options.requireCompletion ||
319
+ error?.isOutputBackpressureFailure === true
320
+ )
321
+ throw error;
291
322
  // Partial data
292
323
  }
293
324
  }
@@ -301,6 +332,9 @@ export async function streamOpenAI(
301
332
  guard.stop();
302
333
  }
303
334
 
335
+ options.signal?.throwIfAborted();
336
+ if (options.requireCompletion && !completed)
337
+ throw new Error("Model stream ended before its completion marker");
304
338
  return fullResponse;
305
339
  }
306
340
 
@@ -317,6 +351,7 @@ export async function streamAnthropic(
317
351
  onToken,
318
352
  onUsage,
319
353
  onStall,
354
+ options = {},
320
355
  ) {
321
356
  // Split out a leading system prompt (Anthropic requires it as top-level
322
357
  // `system`, not an OpenAI-style role=system message).
@@ -348,7 +383,9 @@ export async function streamAnthropic(
348
383
  ...(system ? { system } : {}),
349
384
  messages: convo,
350
385
  }),
351
- signal: guard.signal,
386
+ signal: options.signal
387
+ ? AbortSignal.any([guard.signal, options.signal])
388
+ : guard.signal,
352
389
  });
353
390
  } catch (e) {
354
391
  guard.stop();
@@ -369,6 +406,7 @@ export async function streamAnthropic(
369
406
  const reader = response.body.getReader();
370
407
  const decoder = new TextDecoder();
371
408
  let fullResponse = "";
409
+ let completed = false;
372
410
  let buf = "";
373
411
  let inputTokens = 0;
374
412
  let outputTokens = 0;
@@ -390,6 +428,9 @@ export async function streamAnthropic(
390
428
  if (!payload) continue;
391
429
  try {
392
430
  const obj = JSON.parse(payload);
431
+ if (options.requireCompletion && (obj.error || obj.type === "error"))
432
+ throw new Error("Anthropic stream returned an error");
433
+ if (obj.type === "message_stop") completed = true;
393
434
  if (obj.type === "content_block_delta") {
394
435
  const delta = obj.delta?.text;
395
436
  if (delta) {
@@ -408,7 +449,11 @@ export async function streamAnthropic(
408
449
  outputTokens = Number(obj.usage?.output_tokens) || outputTokens;
409
450
  }
410
451
  } catch (error) {
411
- if (error?.isOutputBackpressureFailure === true) throw error;
452
+ if (
453
+ options.requireCompletion ||
454
+ error?.isOutputBackpressureFailure === true
455
+ )
456
+ throw error;
412
457
  /* skip malformed */
413
458
  }
414
459
  }
@@ -435,6 +480,9 @@ export async function streamAnthropic(
435
480
  });
436
481
  }
437
482
 
483
+ options.signal?.throwIfAborted();
484
+ if (options.requireCompletion && !completed)
485
+ throw new Error("Model stream ended before its completion marker");
438
486
  return fullResponse;
439
487
  }
440
488
 
@@ -488,7 +536,15 @@ export async function* chatStream(messages, options) {
488
536
  let providerCall;
489
537
  if (provider === "ollama") {
490
538
  providerCall = () =>
491
- streamOllama(messages, model, baseUrl, onToken, onUsage, options.onStall);
539
+ streamOllama(
540
+ messages,
541
+ model,
542
+ baseUrl,
543
+ onToken,
544
+ onUsage,
545
+ options.onStall,
546
+ options,
547
+ );
492
548
  } else if (provider === "anthropic") {
493
549
  const providerDef = BUILT_IN_PROVIDERS.anthropic;
494
550
  const url =
@@ -512,6 +568,7 @@ export async function* chatStream(messages, options) {
512
568
  onToken,
513
569
  onUsage,
514
570
  options.onStall,
571
+ options,
515
572
  );
516
573
  } else {
517
574
  const providerDef = BUILT_IN_PROVIDERS[provider];
@@ -536,6 +593,7 @@ export async function* chatStream(messages, options) {
536
593
  onToken,
537
594
  onUsage,
538
595
  options.onStall,
596
+ options,
539
597
  );
540
598
  }
541
599
 
@@ -19,9 +19,97 @@
19
19
 
20
20
  import { chatWithStreaming, chatStream } from "./chat-core.js";
21
21
  import { firstBalancedJson } from "./json-schema-output.js";
22
+ import { prepareGovernedModelTurn } from "./evolution/governed-model-turn.js";
22
23
 
23
24
  const DEFAULT_INTENT_TIMEOUT_MS = 15000;
24
25
 
26
+ async function waitForIntentOperation(operation, signal) {
27
+ if (signal.aborted) {
28
+ void Promise.resolve(operation).catch(() => {});
29
+ throw signal.reason;
30
+ }
31
+ let abort;
32
+ const cancelled = new Promise((_resolve, reject) => {
33
+ abort = () => reject(signal.reason);
34
+ signal.addEventListener("abort", abort, { once: true });
35
+ });
36
+ try {
37
+ return await Promise.race([operation, cancelled]);
38
+ } finally {
39
+ signal.removeEventListener("abort", abort);
40
+ }
41
+ }
42
+
43
+ async function* intentModelStream(messages, options, factory, mode) {
44
+ if (factory === null) {
45
+ yield* chatStream(messages, options);
46
+ return;
47
+ }
48
+ const controller = new AbortController();
49
+ const timeoutMs = resolveIntentTimeout(options);
50
+ const timer =
51
+ timeoutMs > 0
52
+ ? setTimeout(
53
+ () =>
54
+ controller.abort(
55
+ new Error(`intent LLM call timed out after ${timeoutMs}ms`),
56
+ ),
57
+ timeoutMs,
58
+ )
59
+ : null;
60
+ timer?.unref?.();
61
+ let completed = false;
62
+ try {
63
+ const turn = await waitForIntentOperation(
64
+ prepareGovernedModelTurn(factory, {
65
+ mode,
66
+ messages,
67
+ signal: controller.signal,
68
+ }),
69
+ controller.signal,
70
+ );
71
+ let content = "";
72
+ for await (const event of chatStream(turn.messages, {
73
+ ...options,
74
+ signal: controller.signal,
75
+ requireCompletion: true,
76
+ })) {
77
+ controller.signal.throwIfAborted();
78
+ if (event.type === "response-token") {
79
+ content += event.token;
80
+ yield event;
81
+ } else if (event.type === "response-complete") {
82
+ content = event.content;
83
+ }
84
+ }
85
+ await waitForIntentOperation(turn.complete(content), controller.signal);
86
+ completed = true;
87
+ yield { type: "response-complete", content };
88
+ } finally {
89
+ clearTimeout(timer);
90
+ if (!completed) controller.abort(new Error("Intent stream cancelled"));
91
+ }
92
+ }
93
+
94
+ async function intentModelText(messages, options, factory, mode) {
95
+ if (factory === null) {
96
+ return withIntentTimeout(
97
+ chatWithStreaming(messages, options),
98
+ resolveIntentTimeout(options),
99
+ );
100
+ }
101
+ let content = "";
102
+ for await (const event of intentModelStream(
103
+ messages,
104
+ options,
105
+ factory,
106
+ mode,
107
+ )) {
108
+ if (event.type === "response-complete") content = event.content;
109
+ }
110
+ return content;
111
+ }
112
+
25
113
  /**
26
114
  * Resolve the per-call intent-classification budget. `llmOptions.intentTimeoutMs`
27
115
  * overrides the default; an explicit 0 disables the cap; NaN/negative/absent →
@@ -40,8 +128,8 @@ function resolveIntentTimeout(llmOptions) {
40
128
  * trips it, so without this the declared 15s intent budget was never enforced
41
129
  * and classification could block for minutes. Racing the deadline makes it fail
42
130
  * fast into the caller's graceful rule/verbatim fallback. `ms <= 0` disables.
43
- * The losing background request is harmless (chat-core releases its own socket);
44
- * the timer is unref'd so it never holds the process open.
131
+ * This helper is used only for the unconfigured legacy path; governed requests
132
+ * use the abortable intentModelStream deadline. The timer is unref'd.
45
133
  */
46
134
  function withIntentTimeout(promise, ms) {
47
135
  if (!(Number(ms) > 0)) return promise;
@@ -134,6 +222,7 @@ export async function understandIntent({
134
222
  contextMode = "global",
135
223
  history,
136
224
  llmOptions,
225
+ evolutionCompositionFactory = null,
137
226
  }) {
138
227
  if (!userInput || !userInput.trim()) {
139
228
  throw new Error("userInput required");
@@ -157,20 +246,19 @@ export async function understandIntent({
157
246
  );
158
247
 
159
248
  try {
160
- const fullContent = await withIntentTimeout(
161
- chatWithStreaming(
162
- [
163
- { role: "system", content: systemPrompt },
164
- { role: "user", content: userPrompt },
165
- ],
166
- {
167
- ...llmOptions,
168
- // Lower temperature → more deterministic JSON output.
169
- temperature: 0.3,
170
- maxTokens: 500,
171
- },
172
- ),
173
- resolveIntentTimeout(llmOptions),
249
+ const fullContent = await intentModelText(
250
+ [
251
+ { role: "system", content: systemPrompt },
252
+ { role: "user", content: userPrompt },
253
+ ],
254
+ {
255
+ ...llmOptions,
256
+ // Lower temperature → more deterministic JSON output.
257
+ temperature: 0.3,
258
+ maxTokens: 500,
259
+ },
260
+ evolutionCompositionFactory,
261
+ "intent-understand",
174
262
  );
175
263
 
176
264
  const jsonText = extractJson(fullContent);
@@ -218,6 +306,7 @@ export async function* understandIntentStream({
218
306
  contextMode = "global",
219
307
  history,
220
308
  llmOptions,
309
+ evolutionCompositionFactory = null,
221
310
  }) {
222
311
  if (!userInput || !userInput.trim()) {
223
312
  throw new Error("userInput required");
@@ -241,17 +330,16 @@ export async function* understandIntentStream({
241
330
  );
242
331
  let buffer = "";
243
332
  try {
244
- // Incremental token yielding makes a per-call deadline awkward to apply
245
- // here; this streaming variant is instead bounded by chat-core's own
246
- // silence-based stall guard (CC_CHAT_STALL_MS, default 180s). The awaited
247
- // understandIntent / classifyFollowupIntent paths enforce the tighter
248
- // intent budget (resolveIntentTimeout) directly.
249
- for await (const event of chatStream(
333
+ // Governed streams use an abortable per-call intent deadline. The legacy
334
+ // unconfigured stream retains chat-core's silence-based stall guard.
335
+ for await (const event of intentModelStream(
250
336
  [
251
337
  { role: "system", content: systemPrompt },
252
338
  { role: "user", content: userPrompt },
253
339
  ],
254
340
  { ...llmOptions, temperature: 0.3, maxTokens: 500 },
341
+ evolutionCompositionFactory,
342
+ "intent-understand-stream",
255
343
  )) {
256
344
  if (event.type === "response-token") {
257
345
  buffer += event.token;
@@ -461,10 +549,10 @@ function buildFollowupPrompts(userInput, context = {}) {
461
549
  - 示例: "算了"、"不用了"、"停止"、"先不做了"
462
550
 
463
551
  # 输出格式
464
- 严格返回 JSON 格式:
552
+ 严格返回 JSON 格式,confidence 为 0 到 1 之间的数值:
465
553
  {
466
554
  "intent": "CONTINUE_EXECUTION | MODIFY_REQUIREMENT | CLARIFICATION | CANCEL_TASK",
467
- "confidence": 0.0-1.0,
555
+ "confidence": 0.8,
468
556
  "reason": "判断理由(1-2句话)",
469
557
  "extractedInfo": "如果是 MODIFY_REQUIREMENT 或 CLARIFICATION,提取关键信息"
470
558
  }`;
@@ -492,21 +580,25 @@ ${
492
580
  return { systemPrompt, userPrompt };
493
581
  }
494
582
 
495
- async function llmBasedClassify(userInput, context, llmOptions) {
583
+ async function llmBasedClassify(
584
+ userInput,
585
+ context,
586
+ llmOptions,
587
+ evolutionCompositionFactory,
588
+ ) {
496
589
  const { systemPrompt, userPrompt } = buildFollowupPrompts(userInput, context);
497
- const fullContent = await withIntentTimeout(
498
- chatWithStreaming(
499
- [
500
- { role: "system", content: systemPrompt },
501
- { role: "user", content: userPrompt },
502
- ],
503
- {
504
- ...llmOptions,
505
- temperature: 0.1,
506
- maxTokens: 300,
507
- },
508
- ),
509
- resolveIntentTimeout(llmOptions),
590
+ const fullContent = await intentModelText(
591
+ [
592
+ { role: "system", content: systemPrompt },
593
+ { role: "user", content: userPrompt },
594
+ ],
595
+ {
596
+ ...llmOptions,
597
+ temperature: 0.1,
598
+ maxTokens: 300,
599
+ },
600
+ evolutionCompositionFactory,
601
+ "intent-classify",
510
602
  );
511
603
  const jsonText = extractJson(fullContent);
512
604
  if (!jsonText) throw new Error("LLM response missing JSON");
@@ -535,6 +627,7 @@ export async function classifyFollowupIntent({
535
627
  input,
536
628
  context = {},
537
629
  llmOptions,
630
+ evolutionCompositionFactory = null,
538
631
  }) {
539
632
  const startTime = Date.now();
540
633
  const ruleResult = ruleBasedClassify(input);
@@ -556,13 +649,19 @@ export async function classifyFollowupIntent({
556
649
  }
557
650
 
558
651
  try {
559
- const llmResult = await llmBasedClassify(input, context, llmOptions);
652
+ const llmResult = await llmBasedClassify(
653
+ input,
654
+ context,
655
+ llmOptions,
656
+ evolutionCompositionFactory,
657
+ );
560
658
  return {
561
659
  ...llmResult,
562
660
  method: "llm",
563
661
  latency: Date.now() - startTime,
564
662
  };
565
663
  } catch (_err) {
664
+ if (evolutionCompositionFactory !== null) throw _err;
566
665
  return ruleResult.confidence > 0
567
666
  ? {
568
667
  ...ruleResult,
@@ -16,9 +16,12 @@ const SUPPORTED_COMMANDS = new Set([
16
16
  "cowork",
17
17
  "desktop",
18
18
  "evolution",
19
+ "hub",
19
20
  "marketplace",
20
21
  "orchestrate",
21
22
  "serve",
23
+ "stream",
24
+ "ui",
22
25
  ]);
23
26
 
24
27
  function canonical(value) {
@@ -285,10 +288,13 @@ async function loadBuiltInFactories(commandName) {
285
288
  commandName === "chat" ||
286
289
  commandName === "compact" ||
287
290
  commandName === "complete" ||
291
+ commandName === "hub" ||
288
292
  commandName === "desktop" ||
289
293
  commandName === "cowork" ||
290
294
  commandName === "orchestrate" ||
291
- commandName === "serve"
295
+ commandName === "serve" ||
296
+ commandName === "stream" ||
297
+ commandName === "ui"
292
298
  ) {
293
299
  const { createAgentEvolutionRuntimeComposition } =
294
300
  await import("./agent-evolution-runtime-composition.js");
@@ -0,0 +1,56 @@
1
+ import { prepareGovernedModelTurn } from "./governed-model-turn.js";
2
+
3
+ // A scoped wrapper preserves the Hub's independent local/cloud consent check.
4
+ // Never replace llm on the process-wide cached Hub or its analysis engine.
5
+ export function createGovernedHubLlm(llm, factory) {
6
+ if (
7
+ !llm ||
8
+ typeof llm.chat !== "function" ||
9
+ typeof llm.isLocal !== "boolean"
10
+ ) {
11
+ throw new TypeError(
12
+ "A Hub model client with declared locality is required",
13
+ );
14
+ }
15
+ if (typeof factory !== "function")
16
+ throw new TypeError("A Hub evolution factory is required");
17
+ const identity = { name: llm.name, isLocal: llm.isLocal, chat: llm.chat };
18
+ const assertIdentity = () => {
19
+ if (
20
+ llm.name !== identity.name ||
21
+ llm.isLocal !== identity.isLocal ||
22
+ llm.chat !== identity.chat
23
+ ) {
24
+ throw new Error(
25
+ "Hub model identity changed; reopen the analysis invocation",
26
+ );
27
+ }
28
+ };
29
+ const chat = identity.chat.bind(llm);
30
+ return Object.freeze({
31
+ get name() {
32
+ assertIdentity();
33
+ return identity.name;
34
+ },
35
+ get isLocal() {
36
+ assertIdentity();
37
+ return identity.isLocal;
38
+ },
39
+ async chat(messages, options = {}) {
40
+ assertIdentity();
41
+ const turn = await prepareGovernedModelTurn(factory, {
42
+ mode: "hub-analysis",
43
+ messages,
44
+ signal: options.signal,
45
+ });
46
+ assertIdentity();
47
+ const response = await chat(turn.messages, options);
48
+ assertIdentity();
49
+ if (!response || typeof response.text !== "string") {
50
+ throw new Error("Hub model response must contain text");
51
+ }
52
+ await turn.complete(response.text);
53
+ return response;
54
+ },
55
+ });
56
+ }
@@ -0,0 +1,65 @@
1
+ import { prepareGovernedModelTurn } from "./governed-model-turn.js";
2
+ import { createGovernedHubLlm } from "./governed-hub-llm.js";
3
+
4
+ export function createGovernedHubResolver(ports, factory) {
5
+ if (typeof factory !== "function")
6
+ throw new TypeError("A resolver evolution factory is required");
7
+ const {
8
+ resolver,
9
+ llm,
10
+ embeddingStage,
11
+ EntityResolver,
12
+ EmbeddingStage,
13
+ LLMStage,
14
+ } = ports;
15
+ // Rebuild stages for each drain. Neither the shared resolver nor its embedding
16
+ // cache may acquire the authority of an individual WebSocket request.
17
+ const embedding =
18
+ embeddingStage &&
19
+ new EmbeddingStage({
20
+ vault: resolver.vault,
21
+ embedFn: async (profile) => {
22
+ const turn = await prepareGovernedModelTurn(factory, {
23
+ mode: "hub-embedding",
24
+ messages: [{ role: "user", content: profile }],
25
+ });
26
+ // Chat projection prepends host provenance. Embed only the projected
27
+ // profile: per-Run evidence IDs must not change semantic similarity.
28
+ const profileMessage = turn.messages[1];
29
+ if (
30
+ turn.messages.length !== 2 ||
31
+ turn.messages[0].role !== "system" ||
32
+ profileMessage?.role !== "user" ||
33
+ typeof profileMessage.content !== "string"
34
+ )
35
+ throw new Error(
36
+ "Projected embedding input must contain one text message",
37
+ );
38
+ const vector = await embeddingStage._embed(profileMessage.content);
39
+ if (
40
+ (!Array.isArray(vector) && !(vector instanceof Float32Array)) ||
41
+ vector.length === 0 ||
42
+ !Array.from(vector).every(Number.isFinite)
43
+ )
44
+ throw new Error(
45
+ "Embedding response must contain a nonempty finite vector",
46
+ );
47
+ await turn.complete(JSON.stringify(Array.from(vector)));
48
+ return vector;
49
+ },
50
+ });
51
+ const model =
52
+ resolver._llmStage &&
53
+ new LLMStage({
54
+ llm: createGovernedHubLlm(llm, factory),
55
+ acceptNonLocal: false,
56
+ });
57
+ return new EntityResolver({
58
+ vault: resolver.vault,
59
+ candidateLimit: resolver._candidateLimit,
60
+ embeddingHighThreshold: resolver._embeddingHighThreshold,
61
+ embeddingLowThreshold: resolver._embeddingLowThreshold,
62
+ embeddingStage: embedding ? embedding.asStageFn() : null,
63
+ llmStage: model ? model.asStageFn() : null,
64
+ });
65
+ }
@@ -0,0 +1,36 @@
1
+ import { createGovernedHubLlm } from "./governed-hub-llm.js";
2
+
3
+ // Analysis skills treat commentary failures as optional. A governed invocation
4
+ // must still surface a failed model turn after the SDK returns its report.
5
+ export async function runGovernedHubSkill(
6
+ hub,
7
+ factory,
8
+ runSkill,
9
+ name,
10
+ options,
11
+ ) {
12
+ const model = createGovernedHubLlm(hub.llm, factory);
13
+ let failure;
14
+ let failed = false;
15
+ const llm = Object.freeze({
16
+ get name() {
17
+ return model.name;
18
+ },
19
+ get isLocal() {
20
+ return model.isLocal;
21
+ },
22
+ async chat(messages, chatOptions) {
23
+ if (failed) throw failure;
24
+ try {
25
+ return await model.chat(messages, chatOptions);
26
+ } catch (error) {
27
+ failure = error;
28
+ failed = true;
29
+ throw error;
30
+ }
31
+ },
32
+ });
33
+ const result = await runSkill({ vault: hub.vault, llm }, name, options);
34
+ if (failed) throw failure;
35
+ return result;
36
+ }