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.
- package/package.json +2 -2
- package/src/assets/web-panel/assets/{AIOps-Zs-zPwVG.js → AIOps-B_Nt1Qe4.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-HZlxM6H0.js → ActionButton-CHQbie4X.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CXyThRDb.js → Analytics-Bm9364Lw.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DeyYh_EF.js → AppLayout-DTwlIh2v.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-BQDtMwkx.js → Artifacts-OibP0qB4.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DNH4UOux.js → Audit-B8Fwavkg.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BkKdIxE6.js → BackgroundAgents-p1Vb2K2B.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DYy4aJom.js → Backup-BI3QT-wI.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-DjoyGVYm.js → BaseInput-BW_VRWWu.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-D86ZYgOD.js → Chat-CUEhDQfq.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-RxI_YYth.js → ChatBubbleRenderer-C5UVXdVE.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-C7p8UY99.js → Checkbox-URYsaaxv.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DnapqgV5.js → Codegen-DJAwC80a.js} +1 -1
- package/src/assets/web-panel/assets/{Col-C29J2DBT.js → Col-DfRKFzc9.js} +1 -1
- package/src/assets/web-panel/assets/{Community-Bf3KeLCr.js → Community-GOkGHuly.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DA83wB48.js → Compact-CIMs13NC.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CXXVgRFG.js → Compliance-BE-TQIxV.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-B28Ftuqv.js → Cowork-CYmbJiWw.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-CO-BcnDf.js → Cron-Be3OIWyM.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BmgPMFCs.js → Crosschain-2xB2HS2D.js} +1 -1
- package/src/assets/web-panel/assets/{DID-CYW9WslA.js → DID-B_lJO2yF.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CWWhVWds.js → Dashboard-DnVWqUER.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CjTuc1-n.js → Dropdown-C7ZJvHVa.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BrROAu4d.js → EmailListRenderer-DLevT-0F.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Ci8Lmk8W.js → FamilyGuardDashboard-BwhQ5BRU.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CKzKVLE_.js → Federation-BkGI-Xkq.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CC2CK5PQ.js → FormItemContext-BgIIbMd7.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BYa3ElyT.js → GenericCardRenderer-qL-qN8Nb.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Bmn3ucA4.js → Git-BTzgrC4U.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DqEOXitg.js → Governance-p28vdCN4.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BjfeExba.js → Inference-Cqephrur.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CBTyIFlJ.js → KnowledgeGraph-C3v5Q4Zp.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-KZfA_M9Q.js → Logs-T6f0w32w.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-BG7vi3V4.js → MarkdownRenderer-Dj5XPm3S.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BFeK4he0.js → Marketplace-ClDsvHDH.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Brg7uUoK.js → McpTools-C4P-_snx.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-Ce4AmyX3.js → Memory-BZZfG6y6.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-Dx1VBL_E.js → MobileBridge-DpIv_BLs.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DPyRr2Xl.js → MobileProjects-Buz3NIQg.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BzRmtDQU.js → Mtc-HS2PJB79.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-DvZ5kLvJ.js → MtcAudit-UmBrcU27.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-D8uGUjX1.js → Multisig-C00sXiZf.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-igc-TQE5.js → NLProgramming-Dj1ng_ph.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CTqDYlek.js → Notes-C4LEVKlk.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-CkcLa2rb.js → NotificationSettings-BWXwnekv.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BfScYvnH.js → OrderTableRenderer-CZHfK8Nn.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BjKK9SdB.js → Organization-CFEtFPrV.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BkcghTPE.js → Overflow-DSz4MJK1.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-l9f34Xu8.js → P2P-DyTEytwG.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-yVXWmwWe.js → PdhVaultBrowser-B5QNGLVx.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DeSYs0VV.js → Permissions-wEC_X7x4.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CmKmhy0q.js → PersonalDataHub-If5GZlGZ.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-DBxS4R_r.js → Pipeline-LB4gqqmX.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CKngdGw3.js → Privacy-QVLhoxcN.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-0Pso0jXN.js → ProjectInit-FaAbTYTo.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BHrqWQ6r.js → ProjectSettings-DhMeNA21.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-B8wIh9PK.js → Projects-5L4lkWAZ.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-CXFTOHRV.js → Providers-DhZFp8fO.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DfN0c_BN.js → QrScannerModal-BZuJ7_xi.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-v1Z5hty_.js → QuickAsk-TXddPYYb.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DOaVmZUi.js → Recommend-DDuXIKYt.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession--EWcPBY1.js → RemoteSession-CDrTXImv.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DUWFSNvV.js → Reputation-B2xKaKzt.js} +1 -1
- package/src/assets/web-panel/assets/{Row-0-bFnXWF.js → Row-DzAjWAOX.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-B_uTfubW.js → RssFeed-CKH38BLF.js} +3 -3
- package/src/assets/web-panel/assets/{Search-CqxiTLfB.js → Search-CuPqJYoQ.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CGoHOz5B.js → Security-DmAd2hxz.js} +4 -4
- package/src/assets/web-panel/assets/{Services-COhAbS45.js → Services-Bt3nljnY.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-B97crp6v.js → Skeleton-CP7RH093.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-er_XdavV.js → Skills-DKNeMBTx.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-RpkWkzOL.js → Sla-CbpM8Nrd.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-DTk-vTzF.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-Bbiw6ohw.js → SyncSettings-DKiplwmc.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CRghr7lo.js → Tasks-z-lOm8fx.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BUQkfydG.js → Templates-Rb-Q0DGX.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CfUFOsZu.js → Tenant-C2sqDUnR.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BGY7gi4M.js → Terminal-DcyzskEE.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-B3Len0l7.js → TimelineRenderer-lg0Eam7N.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BjeHn2zI.js → Tokens-D9USIMIA.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-ctcZ2mcv.js → Trigger-BQpaQJ5k.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-oncF3TpO.js → Trust-C-Mik04s.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BwKbVYCd.js → UkeySign-DrnZ_o56.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-D1dgL-3U.js → VideoEditing-BDOsoZi0.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-4hXAfjBL.js → Wallet-Ct_bleI7.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-C8p3cotb.js → WebAuthn-B6b6FDao.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-BnrjG77E.js → WorkflowEditor-ZUJMaIHr.js} +1 -1
- package/src/assets/web-panel/assets/{chat-B-YNK7EI.js → chat-BII20OJy.js} +1 -1
- package/src/assets/web-panel/assets/{colors-B9HYvK-e.js → colors-DEsJRV3n.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BJr0quBA.js → compact-item-DoDYAzN9.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CXfbQEWD.js → createContext-o_nofnVW.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-0tgDCszN.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-B7Q02hdZ.js → hasIn-CAsABTHC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsV1HA0Z.js → index-B0ApxOMw.js} +1 -1
- package/src/assets/web-panel/assets/{index-Gjegl5Ig.js → index-B4SfcwZA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BoNVnnAq.js → index-B9Z75LfA.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7IZWdoB.js → index-B9hYHPhs.js} +1 -1
- package/src/assets/web-panel/assets/{index-DPUg6cVc.js → index-BDRh3v44.js} +1 -1
- package/src/assets/web-panel/assets/{index-DutYFyC0.js → index-BcYbQoY7.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1GP2MQz.js → index-Bmwo5wQG.js} +1 -1
- package/src/assets/web-panel/assets/{index-kAggAkrB.js → index-Bn2ZPUXO.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMoUa3dF.js → index-BnbnmRqg.js} +1 -1
- package/src/assets/web-panel/assets/{index-BmvpVSmM.js → index-Bqyg3PME.js} +1 -1
- package/src/assets/web-panel/assets/{index-BmCD1_HC.js → index-BrIIJ2s-.js} +1 -1
- package/src/assets/web-panel/assets/{index-G7Z_On_n.js → index-BxT8yTwS.js} +1 -1
- package/src/assets/web-panel/assets/{index-BClzZrmx.js → index-Bycj011d.js} +1 -1
- package/src/assets/web-panel/assets/{index-4gZEpDAG.js → index-BzMA6Ra3.js} +1 -1
- package/src/assets/web-panel/assets/{index-CEujQqdl.js → index-C1YDdyQ1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BvRqE3Un.js → index-CE2J04dA.js} +1 -1
- package/src/assets/web-panel/assets/index-CFF8XEFE.js +1 -0
- package/src/assets/web-panel/assets/{index-D5g5_oua.js → index-CQE-WMEU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQ7qYZS4.js → index-CVQSl1uY.js} +1 -1
- package/src/assets/web-panel/assets/{index-BleXwr2w.js → index-CeJSTcXw.js} +1 -1
- package/src/assets/web-panel/assets/{index-BU_adeUp.js → index-CeWJqXNX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BNL9lmWL.js → index-Cf3sFEoE.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtunVZuS.js → index-D5Fd4tdL.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXgHZ_HG.js → index-DDQzBK3l.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3M8M9_U.js → index-DQmX_-hk.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6h86qjh.js → index-DRAA2McB.js} +1 -1
- package/src/assets/web-panel/assets/{index-BB4KIBso.js → index-DU2Rpx1m.js} +1 -1
- package/src/assets/web-panel/assets/index-DVUEISry.js +1 -0
- package/src/assets/web-panel/assets/{index-cq5Q-RHa.js → index-DWZNYecl.js} +3 -3
- package/src/assets/web-panel/assets/{index-COdAugDt.js → index-D_CMB_wC.js} +1 -1
- package/src/assets/web-panel/assets/{index-iCKFl_jt.js → index-Dbb34cAr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bf0OxvOh.js → index-DciLb_Ww.js} +1 -1
- package/src/assets/web-panel/assets/{index-D0zuXz4s.js → index-Dgt1mKby.js} +1 -1
- package/src/assets/web-panel/assets/{index-CcyxVqzn.js → index-Dhnau6uw.js} +1 -1
- package/src/assets/web-panel/assets/{index-CU7cBzr9.js → index-DrsAF-Ar.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_UneDq3.js → index-DsoFTdA2.js} +1 -1
- package/src/assets/web-panel/assets/{index-6wWd08X-.js → index-TSwbtCYG.js} +1 -1
- package/src/assets/web-panel/assets/{index-HzCaU25O.js → index-kIyLNyoF.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bd3nX6aj.js → index-tCeNfxv4.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DrzGM6fO.js → initDefaultProps-CNjeeoKE.js} +1 -1
- package/src/assets/web-panel/assets/{motion-SE_xFnGL.js → motion-CBT8IZDm.js} +1 -1
- package/src/assets/web-panel/assets/{move-CrPjKNiG.js → move-h1MOxjqv.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CpcyFPK6.js → mtc-parser-C_3pqNmn.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BV0g7vf4.js → omit-CMUnPFDk.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-lWpsc-k6.js → pickAttrs-p8_c1xq0.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CiewvU4X.js → placementArrow-D6O67r23.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DbF8pamO.js → responsiveObserve-DquhepAK.js} +1 -1
- package/src/assets/web-panel/assets/{slide-Bn82Ip9-.js → slide-CZ-m6gmS.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-J76gk_-G.js → statusUtils-k9ejZFtd.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BL95J74a.js → styleChecker-NX6_137R.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BUzhdQsv.js → useFlexGapSupport-bCqR70lP.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-B2Fppepb.js → useFs-2l0vSqyY.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DjTASfYE.js → usePersonalDataHub-1yccm4O7.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BwrOgBqP.js → vnode-CxQzajDo.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-B6MeT_Gw.js → zoom-CrTKwFyb.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/hub.js +21 -6
- package/src/commands/stream.js +28 -7
- package/src/commands/ui.js +10 -2
- package/src/data/changelog.json +13 -0
- package/src/gateways/ws/chat-intent-protocol.js +4 -1
- package/src/gateways/ws/llm-chat-protocol.js +29 -3
- package/src/gateways/ws/message-dispatcher.js +3 -1
- package/src/gateways/ws/personal-data-hub-protocol.js +24 -10
- package/src/gateways/ws/session-core-protocol.js +23 -6
- package/src/gateways/ws/session-protocol.js +1 -0
- package/src/lib/__tests__/provider-stream.test.js +6 -8
- package/src/lib/chat-core.js +67 -9
- package/src/lib/chat-intent-service.js +138 -39
- package/src/lib/evolution/evolution-deployment-loader.js +7 -1
- package/src/lib/evolution/governed-hub-llm.js +56 -0
- package/src/lib/evolution/governed-hub-resolver.js +65 -0
- package/src/lib/evolution/governed-hub-skill.js +36 -0
- package/src/lib/evolution/governed-model-turn.js +88 -0
- package/src/lib/personal-data-hub-wiring.js +49 -0
- package/src/lib/provider-stream.js +94 -45
- package/src/lib/ws-chat-handler.js +19 -2
- package/src/assets/web-panel/assets/SpeechSettings-DDVXmT5y.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DNpXMZaQ.js +0 -1
- package/src/assets/web-panel/assets/index-2hcdmWpb.js +0 -1
- package/src/assets/web-panel/assets/index-DbQCUG4D.js +0 -1
package/src/lib/chat-core.js
CHANGED
|
@@ -98,7 +98,11 @@ async function _deliverToken(onToken, token, guard) {
|
|
|
98
98
|
try {
|
|
99
99
|
await onToken(token);
|
|
100
100
|
} catch (error) {
|
|
101
|
-
if (
|
|
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:
|
|
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 (
|
|
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:
|
|
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]")
|
|
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 (
|
|
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:
|
|
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 (
|
|
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(
|
|
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
|
-
*
|
|
44
|
-
* the timer is unref'd
|
|
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
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
//
|
|
245
|
-
//
|
|
246
|
-
|
|
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.
|
|
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(
|
|
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
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
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(
|
|
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
|
+
}
|