chainlesschain 0.166.34 → 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-D8PJmSrh.js → AIOps-B_Nt1Qe4.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DG6DqHA3.js → ActionButton-CHQbie4X.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DiLleeWs.js → Analytics-Bm9364Lw.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DxYkhlHK.js → AppLayout-DTwlIh2v.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-BAW3Kdsi.js → Artifacts-OibP0qB4.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DBr4uY6E.js → Audit-B8Fwavkg.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-Dw0r-bHX.js → BackgroundAgents-p1Vb2K2B.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-AyhwYsio.js → Backup-BI3QT-wI.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Cj8qJotr.js → BaseInput-BW_VRWWu.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DcvimQ0u.js → Chat-CUEhDQfq.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer--joJ1e2f.js → ChatBubbleRenderer-C5UVXdVE.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-C2mlU77l.js → Checkbox-URYsaaxv.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CAfc3YaV.js → Codegen-DJAwC80a.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CIxoxtJI.js → Col-DfRKFzc9.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DvufZoRg.js → Community-GOkGHuly.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-55JTa99J.js → Compact-CIMs13NC.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BllWog6P.js → Compliance-BE-TQIxV.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-ThsFGc5p.js → Cowork-CYmbJiWw.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-CZjl5Ig2.js → Cron-Be3OIWyM.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DIAKAoQT.js → Crosschain-2xB2HS2D.js} +1 -1
- package/src/assets/web-panel/assets/{DID-ayygwEil.js → DID-B_lJO2yF.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-C3Xjm2UO.js → Dashboard-DnVWqUER.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-C1S3zf-j.js → Dropdown-C7ZJvHVa.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DRvriU-H.js → EmailListRenderer-DLevT-0F.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-iSX2bmsD.js → FamilyGuardDashboard-BwhQ5BRU.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-Dk0Stkv-.js → Federation-BkGI-Xkq.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DasXSGff.js → FormItemContext-BgIIbMd7.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-Dv9XGlxI.js → GenericCardRenderer-qL-qN8Nb.js} +1 -1
- package/src/assets/web-panel/assets/{Git-B5PgXL3Z.js → Git-BTzgrC4U.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-B78x11e_.js → Governance-p28vdCN4.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-b7CX1RqW.js → Inference-Cqephrur.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph--MgBzMVp.js → KnowledgeGraph-C3v5Q4Zp.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CYCiGmVc.js → Logs-T6f0w32w.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-yrl10SWw.js → MarkdownRenderer-Dj5XPm3S.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-CsbwQ0eM.js → Marketplace-ClDsvHDH.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-0GBWHVra.js → McpTools-C4P-_snx.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-fAywmAYr.js → Memory-BZZfG6y6.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BeNEyy3s.js → MobileBridge-DpIv_BLs.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-Buz3NIQg.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-CqRhJB70.js → Mtc-HS2PJB79.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-BEyGZflc.js → MtcAudit-UmBrcU27.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-luz3EBT2.js → Multisig-C00sXiZf.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Dkbvp8IX.js → NLProgramming-Dj1ng_ph.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Cf9I2K8j.js → Notes-C4LEVKlk.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-BTfuzynL.js → NotificationSettings-BWXwnekv.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CZHfK8Nn.js +1 -0
- package/src/assets/web-panel/assets/{Organization-BbG3-vDy.js → Organization-CFEtFPrV.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CMtFKEIQ.js → Overflow-DSz4MJK1.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-6SAEb_lU.js → P2P-DyTEytwG.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-B5QNGLVx.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-ClZxZKZl.js → Permissions-wEC_X7x4.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BYkyoZDT.js → PersonalDataHub-If5GZlGZ.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-B28ZgADI.js → Pipeline-LB4gqqmX.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BxtZywbB.js → Privacy-QVLhoxcN.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DPqoRnxK.js → ProjectInit-FaAbTYTo.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-QjKvMkau.js → ProjectSettings-DhMeNA21.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Cp0Z8ZG0.js → Projects-5L4lkWAZ.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-CrS-rbyb.js → Providers-DhZFp8fO.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-xTrhbVAU.js → QrScannerModal-BZuJ7_xi.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DFOqJoq-.js → QuickAsk-TXddPYYb.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DXlKqu2-.js → Recommend-DDuXIKYt.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BLLaZH8K.js → RemoteSession-CDrTXImv.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-CUgAdJ11.js → Reputation-B2xKaKzt.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DzOO3wP-.js → Row-DzAjWAOX.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-B6GrRvh8.js → RssFeed-CKH38BLF.js} +3 -3
- package/src/assets/web-panel/assets/{Search-Cj3zKLb3.js → Search-CuPqJYoQ.js} +1 -1
- package/src/assets/web-panel/assets/{Security-LnktzidI.js → Security-DmAd2hxz.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BQ6sIc2K.js → Services-Bt3nljnY.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CCSV3Jbw.js → Skeleton-CP7RH093.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CpoQ4SNk.js → Skills-DKNeMBTx.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DbAbgRPq.js → Sla-CbpM8Nrd.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-C4cJ_hoD.js → SpeechSettings-DTk-vTzF.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BKFzjN9Z.js → SyncSettings-DKiplwmc.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-z-lOm8fx.js +1 -0
- package/src/assets/web-panel/assets/{Templates-DTEQfFAQ.js → Templates-Rb-Q0DGX.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-wToJWKXB.js → Tenant-C2sqDUnR.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-1s3c7kmF.js → Terminal-DcyzskEE.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BcfsYW3u.js → TimelineRenderer-lg0Eam7N.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-Byez6FY0.js → Tokens-D9USIMIA.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-Cq8mZO88.js → Trigger-BQpaQJ5k.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DUrMmEwR.js → Trust-C-Mik04s.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CVHwXrVU.js → UkeySign-DrnZ_o56.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CDKbTRUS.js → VideoEditing-BDOsoZi0.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BqnEBIOy.js → Wallet-Ct_bleI7.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-CeTMpj7_.js → WebAuthn-B6b6FDao.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-F6OgjV3C.js → WorkflowEditor-ZUJMaIHr.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CMHUCwq3.js → chat-BII20OJy.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DzVM_fpJ.js → colors-DEsJRV3n.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Hhmg51yT.js → compact-item-DoDYAzN9.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-SwHt7x3Q.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-HGNKkn9A.js → hasIn-CAsABTHC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cc-d6f0F.js → index-B0ApxOMw.js} +1 -1
- package/src/assets/web-panel/assets/{index-E-bI8HpV.js → index-B4SfcwZA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJqNTF1S.js → index-B9Z75LfA.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgoefbLs.js → index-B9hYHPhs.js} +1 -1
- package/src/assets/web-panel/assets/{index-CDtzUt75.js → index-BDRh3v44.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_HgLPC5.js → index-BcYbQoY7.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1QJxiOL.js → index-Bmwo5wQG.js} +1 -1
- package/src/assets/web-panel/assets/{index-ChLHLBkF.js → index-Bn2ZPUXO.js} +1 -1
- package/src/assets/web-panel/assets/{index-D5dcWBqg.js → index-BnbnmRqg.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bq9MMKno.js → index-Bqyg3PME.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ba2Kszdy.js → index-BrIIJ2s-.js} +1 -1
- package/src/assets/web-panel/assets/{index-BDt4wSl6.js → index-BxT8yTwS.js} +1 -1
- package/src/assets/web-panel/assets/{index-COOcGOyK.js → index-Bycj011d.js} +1 -1
- package/src/assets/web-panel/assets/{index-DQT5thlV.js → index-BzMA6Ra3.js} +1 -1
- package/src/assets/web-panel/assets/{index-F7Grz3-G.js → index-C1YDdyQ1.js} +1 -1
- package/src/assets/web-panel/assets/{index-jrRtmiUh.js → index-CE2J04dA.js} +1 -1
- package/src/assets/web-panel/assets/index-CFF8XEFE.js +1 -0
- package/src/assets/web-panel/assets/{index-BDE-WiE5.js → index-CQE-WMEU.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6YQN5_R.js → index-CVQSl1uY.js} +1 -1
- package/src/assets/web-panel/assets/{index-BlHB5eax.js → index-CeJSTcXw.js} +1 -1
- package/src/assets/web-panel/assets/{index-C-YkiVyL.js → index-CeWJqXNX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRikmONJ.js → index-Cf3sFEoE.js} +1 -1
- package/src/assets/web-panel/assets/{index-MDdTkUpA.js → index-D5Fd4tdL.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMG_2fK2.js → index-DDQzBK3l.js} +1 -1
- package/src/assets/web-panel/assets/{index-Co4RcBua.js → index-DQmX_-hk.js} +1 -1
- package/src/assets/web-panel/assets/{index--iOqM_AI.js → index-DRAA2McB.js} +1 -1
- package/src/assets/web-panel/assets/{index-pwLuJGFh.js → index-DU2Rpx1m.js} +1 -1
- package/src/assets/web-panel/assets/index-DVUEISry.js +1 -0
- package/src/assets/web-panel/assets/{index-AQcDW8bB.js → index-DWZNYecl.js} +3 -3
- package/src/assets/web-panel/assets/{index-oz0Xxs8D.js → index-D_CMB_wC.js} +1 -1
- package/src/assets/web-panel/assets/{index-aKV4Py_2.js → index-Dbb34cAr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BstLYy9q.js → index-DciLb_Ww.js} +1 -1
- package/src/assets/web-panel/assets/{index-DyZUrnje.js → index-Dgt1mKby.js} +1 -1
- package/src/assets/web-panel/assets/{index-sYkFp9A4.js → index-Dhnau6uw.js} +1 -1
- package/src/assets/web-panel/assets/{index-BDhCx17_.js → index-DrsAF-Ar.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dcg7nQDe.js → index-DsoFTdA2.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWkWh48N.js → index-TSwbtCYG.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cnm4zroJ.js → index-kIyLNyoF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BmPYmoY3.js → index-tCeNfxv4.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-C_2QM45F.js → initDefaultProps-CNjeeoKE.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DkxY04tR.js → motion-CBT8IZDm.js} +1 -1
- package/src/assets/web-panel/assets/{move-D0CJlA7D.js → move-h1MOxjqv.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DjP38YUD.js → mtc-parser-C_3pqNmn.js} +1 -1
- package/src/assets/web-panel/assets/{omit-C9CJDaGT.js → omit-CMUnPFDk.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-D5Wq2fa8.js → pickAttrs-p8_c1xq0.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DJ6Dixs7.js → placementArrow-D6O67r23.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DMmM0Dee.js → responsiveObserve-DquhepAK.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DipmpYA-.js → slide-CZ-m6gmS.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DSqw6_nU.js → statusUtils-k9ejZFtd.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-t828MYjI.js → styleChecker-NX6_137R.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-COsSNzDQ.js → useFlexGapSupport-bCqR70lP.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-Bq5IEzv_.js → useFs-2l0vSqyY.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CUKM0HdW.js → usePersonalDataHub-1yccm4O7.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-l-JwAt2M.js → vnode-CxQzajDo.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-D7xkW-gD.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/llm.js +4 -3
- package/src/commands/stream.js +28 -7
- package/src/commands/ui.js +10 -2
- package/src/data/changelog.json +39 -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-artifact-ports.js +40 -3
- package/src/lib/evolution/evolution-deployment-loader.js +7 -1
- package/src/lib/evolution/evolution-file-identity.js +50 -0
- package/src/lib/evolution/evolution-file-witness.js +206 -91
- package/src/lib/evolution/evolution-ledger-file-backend.js +4 -0
- package/src/lib/evolution/evolution-ledger.js +37 -5
- package/src/lib/evolution/evolution-witness-segments.js +330 -0
- 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/evolution/progressive-canary-external-watchdog.js +6 -4
- package/src/lib/evolution/skill-candidate-registry.js +45 -11
- package/src/lib/evolution/skill-release-registry.js +46 -12
- package/src/lib/llm-connection-probe.js +21 -4
- 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/MobileProjects-Zt1cpErd.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-DBmCtl2H.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-CWG2OLvh.js +0 -7
- package/src/assets/web-panel/assets/Tasks-Cf4bl7NS.js +0 -1
- package/src/assets/web-panel/assets/devWarning-5q_Y0E88.js +0 -1
- package/src/assets/web-panel/assets/index-dl9TJMh5.js +0 -1
- package/src/assets/web-panel/assets/index-x0DDgUaT.js +0 -1
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { types as utilTypes } from "node:util";
|
|
5
5
|
import { getHomeDir } from "../paths.js";
|
|
6
6
|
import { ensurePrivateDirectory, ensurePrivateFile } from "../secure-fs.js";
|
|
7
|
+
import { withEvolutionFileIdentity } from "./evolution-file-identity.js";
|
|
7
8
|
import {
|
|
8
9
|
verifySkillDependencyLock,
|
|
9
10
|
verifySkillRuntimeManifest,
|
|
@@ -1793,6 +1794,15 @@ export class SkillCandidateRegistry {
|
|
|
1793
1794
|
}
|
|
1794
1795
|
|
|
1795
1796
|
_initializeTenantMarker() {
|
|
1797
|
+
return withEvolutionFileIdentity(
|
|
1798
|
+
this._fs,
|
|
1799
|
+
this._markerPath,
|
|
1800
|
+
(_full, sameStable) =>
|
|
1801
|
+
this.#initializeTenantMarkerWithIdentity(sameStable),
|
|
1802
|
+
);
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
#initializeTenantMarkerWithIdentity(sameStable) {
|
|
1796
1806
|
const existing = lstatOrNull(this._fs, this._markerPath);
|
|
1797
1807
|
if (existing) {
|
|
1798
1808
|
const verified = this._readAndVerifyTenantMarker();
|
|
@@ -1832,7 +1842,6 @@ export class SkillCandidateRegistry {
|
|
|
1832
1842
|
"candidate tenant marker temporary file is unsafe",
|
|
1833
1843
|
);
|
|
1834
1844
|
}
|
|
1835
|
-
const writtenIdentity = entryIdentity(written);
|
|
1836
1845
|
this._fs.closeSync(descriptor);
|
|
1837
1846
|
descriptor = null;
|
|
1838
1847
|
if (this._secure) {
|
|
@@ -1847,7 +1856,7 @@ export class SkillCandidateRegistry {
|
|
|
1847
1856
|
staged.isSymbolicLink() ||
|
|
1848
1857
|
Number(staged.nlink) !== 1 ||
|
|
1849
1858
|
staged.size !== bytes.length ||
|
|
1850
|
-
|
|
1859
|
+
!sameStable(staged, written) ||
|
|
1851
1860
|
!samePath(realpath(this._fs, temporaryPath), temporaryPath)
|
|
1852
1861
|
) {
|
|
1853
1862
|
throw registryError(
|
|
@@ -1870,7 +1879,7 @@ export class SkillCandidateRegistry {
|
|
|
1870
1879
|
!linked.isFile() ||
|
|
1871
1880
|
linked.isSymbolicLink() ||
|
|
1872
1881
|
Number(linked.nlink) !== 2 ||
|
|
1873
|
-
entryIdentity(linked) !==
|
|
1882
|
+
entryIdentity(linked) !== entryIdentity(staged)
|
|
1874
1883
|
) {
|
|
1875
1884
|
throw registryError(
|
|
1876
1885
|
"SKILL_CANDIDATE_STORE_UNSAFE",
|
|
@@ -1901,6 +1910,24 @@ export class SkillCandidateRegistry {
|
|
|
1901
1910
|
}
|
|
1902
1911
|
|
|
1903
1912
|
_readBoundedRegularFile(filePath, maximum, code, label) {
|
|
1913
|
+
return withEvolutionFileIdentity(this._fs, filePath, (samePathHandle) =>
|
|
1914
|
+
this.#readBoundedRegularFileWithIdentity(
|
|
1915
|
+
filePath,
|
|
1916
|
+
maximum,
|
|
1917
|
+
code,
|
|
1918
|
+
label,
|
|
1919
|
+
samePathHandle,
|
|
1920
|
+
),
|
|
1921
|
+
);
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
#readBoundedRegularFileWithIdentity(
|
|
1925
|
+
filePath,
|
|
1926
|
+
maximum,
|
|
1927
|
+
code,
|
|
1928
|
+
label,
|
|
1929
|
+
samePathHandle,
|
|
1930
|
+
) {
|
|
1904
1931
|
let descriptor = null;
|
|
1905
1932
|
try {
|
|
1906
1933
|
const before = this._fs.lstatSync(filePath);
|
|
@@ -1925,7 +1952,7 @@ export class SkillCandidateRegistry {
|
|
|
1925
1952
|
if (
|
|
1926
1953
|
!opened.isFile() ||
|
|
1927
1954
|
Number(opened.nlink) !== 1 ||
|
|
1928
|
-
|
|
1955
|
+
!samePathHandle(before, opened) ||
|
|
1929
1956
|
opened.size !== before.size
|
|
1930
1957
|
) {
|
|
1931
1958
|
throw registryError(code, `${label} changed while it was opened`);
|
|
@@ -1941,12 +1968,12 @@ export class SkillCandidateRegistry {
|
|
|
1941
1968
|
!afterPath.isFile() ||
|
|
1942
1969
|
afterPath.isSymbolicLink() ||
|
|
1943
1970
|
Number(afterPath.nlink) !== 1 ||
|
|
1944
|
-
|
|
1971
|
+
!samePathHandle(afterPath, after) ||
|
|
1945
1972
|
!samePath(realpath(this._fs, filePath), filePath)
|
|
1946
1973
|
) {
|
|
1947
1974
|
throw registryError(code, `${label} changed while it was read`);
|
|
1948
1975
|
}
|
|
1949
|
-
return { bytes, identity: entryIdentity(
|
|
1976
|
+
return { bytes, identity: entryIdentity(before) };
|
|
1950
1977
|
} finally {
|
|
1951
1978
|
if (descriptor !== null) this._fs.closeSync(descriptor);
|
|
1952
1979
|
}
|
|
@@ -2637,6 +2664,14 @@ export class SkillCandidateRegistry {
|
|
|
2637
2664
|
"candidate create accepts only input; admission context is registry-owned",
|
|
2638
2665
|
);
|
|
2639
2666
|
}
|
|
2667
|
+
return withEvolutionFileIdentity(
|
|
2668
|
+
this._fs,
|
|
2669
|
+
this._markerPath,
|
|
2670
|
+
(_full, sameStable) => this.#createWithIdentity(input, sameStable),
|
|
2671
|
+
);
|
|
2672
|
+
}
|
|
2673
|
+
|
|
2674
|
+
#createWithIdentity(input, sameStable) {
|
|
2640
2675
|
this._assertNoMixedTenantArtifacts();
|
|
2641
2676
|
const verificationContext = this._resolveAdmissionContext(input);
|
|
2642
2677
|
const candidate = buildSkillCandidateDraft(input, verificationContext);
|
|
@@ -2684,7 +2719,6 @@ export class SkillCandidateRegistry {
|
|
|
2684
2719
|
{ candidateId: candidate.candidateId, commitState: "not-committed" },
|
|
2685
2720
|
);
|
|
2686
2721
|
}
|
|
2687
|
-
const writtenIdentity = entryIdentity(written);
|
|
2688
2722
|
this._fs.closeSync(descriptor);
|
|
2689
2723
|
descriptor = null;
|
|
2690
2724
|
if (this._secure) {
|
|
@@ -2699,7 +2733,7 @@ export class SkillCandidateRegistry {
|
|
|
2699
2733
|
staged.isSymbolicLink() ||
|
|
2700
2734
|
Number(staged.nlink) !== 1 ||
|
|
2701
2735
|
staged.size !== bytes.length ||
|
|
2702
|
-
|
|
2736
|
+
!sameStable(staged, written) ||
|
|
2703
2737
|
!samePath(realpath(this._fs, temporaryPath), temporaryPath)
|
|
2704
2738
|
) {
|
|
2705
2739
|
throw registryError(
|
|
@@ -2742,7 +2776,7 @@ export class SkillCandidateRegistry {
|
|
|
2742
2776
|
linked.isSymbolicLink() ||
|
|
2743
2777
|
Number(linked.nlink) !== 2 ||
|
|
2744
2778
|
linked.size !== bytes.length ||
|
|
2745
|
-
entryIdentity(linked) !==
|
|
2779
|
+
entryIdentity(linked) !== entryIdentity(staged) ||
|
|
2746
2780
|
!samePath(realpath(this._fs, filePath), filePath)
|
|
2747
2781
|
) {
|
|
2748
2782
|
throw registryError(
|
|
@@ -2759,7 +2793,7 @@ export class SkillCandidateRegistry {
|
|
|
2759
2793
|
finalized.isSymbolicLink() ||
|
|
2760
2794
|
Number(finalized.nlink) !== 1 ||
|
|
2761
2795
|
finalized.size !== bytes.length ||
|
|
2762
|
-
entryIdentity(finalized) !==
|
|
2796
|
+
entryIdentity(finalized) !== entryIdentity(staged)
|
|
2763
2797
|
) {
|
|
2764
2798
|
throw registryError(
|
|
2765
2799
|
"SKILL_CANDIDATE_COMMIT_UNKNOWN",
|
|
@@ -2794,7 +2828,7 @@ export class SkillCandidateRegistry {
|
|
|
2794
2828
|
);
|
|
2795
2829
|
}
|
|
2796
2830
|
if (
|
|
2797
|
-
stored.identity !==
|
|
2831
|
+
stored.identity !== entryIdentity(staged) ||
|
|
2798
2832
|
verified.tenantId !== this.tenantId ||
|
|
2799
2833
|
verified.candidateId !== candidate.candidateId ||
|
|
2800
2834
|
!stored.bytes.equals(bytes) ||
|
|
@@ -5,6 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import { types as utilTypes } from "node:util";
|
|
6
6
|
import { getHomeDir } from "../paths.js";
|
|
7
7
|
import { ensurePrivateDirectory, ensurePrivateFile } from "../secure-fs.js";
|
|
8
|
+
import { withEvolutionFileIdentity } from "./evolution-file-identity.js";
|
|
8
9
|
import {
|
|
9
10
|
SKILL_CANDIDATE_MIGRATION_RECEIPT_SCHEMA,
|
|
10
11
|
SKILL_CANDIDATE_MIGRATION_RECORD_SCHEMA,
|
|
@@ -2543,6 +2544,26 @@ function lstatOrNull(fsImpl, target) {
|
|
|
2543
2544
|
}
|
|
2544
2545
|
|
|
2545
2546
|
function readBoundedSingleLinkFile(fsImpl, filePath, maximum, code, label) {
|
|
2547
|
+
return withEvolutionFileIdentity(fsImpl, filePath, (samePathHandle) =>
|
|
2548
|
+
readBoundedSingleLinkFileWithIdentity(
|
|
2549
|
+
fsImpl,
|
|
2550
|
+
filePath,
|
|
2551
|
+
maximum,
|
|
2552
|
+
code,
|
|
2553
|
+
label,
|
|
2554
|
+
samePathHandle,
|
|
2555
|
+
),
|
|
2556
|
+
);
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
function readBoundedSingleLinkFileWithIdentity(
|
|
2560
|
+
fsImpl,
|
|
2561
|
+
filePath,
|
|
2562
|
+
maximum,
|
|
2563
|
+
code,
|
|
2564
|
+
label,
|
|
2565
|
+
samePathHandle,
|
|
2566
|
+
) {
|
|
2546
2567
|
let descriptor = null;
|
|
2547
2568
|
try {
|
|
2548
2569
|
const before = fsImpl.lstatSync(filePath);
|
|
@@ -2564,7 +2585,7 @@ function readBoundedSingleLinkFile(fsImpl, filePath, maximum, code, label) {
|
|
|
2564
2585
|
if (
|
|
2565
2586
|
!opened.isFile() ||
|
|
2566
2587
|
Number(opened.nlink) !== 1 ||
|
|
2567
|
-
|
|
2588
|
+
!samePathHandle(before, opened) ||
|
|
2568
2589
|
opened.size !== before.size
|
|
2569
2590
|
) {
|
|
2570
2591
|
throw failure(code, `${label} changed while opening`);
|
|
@@ -2580,12 +2601,12 @@ function readBoundedSingleLinkFile(fsImpl, filePath, maximum, code, label) {
|
|
|
2580
2601
|
!afterPath.isFile() ||
|
|
2581
2602
|
afterPath.isSymbolicLink() ||
|
|
2582
2603
|
Number(afterPath.nlink) !== 1 ||
|
|
2583
|
-
|
|
2604
|
+
!samePathHandle(afterPath, after) ||
|
|
2584
2605
|
!samePath(realpath(fsImpl, filePath), filePath)
|
|
2585
2606
|
) {
|
|
2586
2607
|
throw failure(code, `${label} changed while reading`);
|
|
2587
2608
|
}
|
|
2588
|
-
return { bytes, identity: identity(
|
|
2609
|
+
return { bytes, identity: identity(before) };
|
|
2589
2610
|
} finally {
|
|
2590
2611
|
if (descriptor !== null) fsImpl.closeSync(descriptor);
|
|
2591
2612
|
}
|
|
@@ -2786,8 +2807,7 @@ export class SkillReleaseRegistry {
|
|
|
2786
2807
|
readActive: SkillReleaseRegistry.prototype.readActive.bind(this),
|
|
2787
2808
|
readState: SkillReleaseRegistry.prototype.readState.bind(this),
|
|
2788
2809
|
readRelease: SkillReleaseRegistry.prototype.readRelease.bind(this),
|
|
2789
|
-
readInventory:
|
|
2790
|
-
SkillReleaseRegistry.prototype.readInventory.bind(this),
|
|
2810
|
+
readInventory: SkillReleaseRegistry.prototype.readInventory.bind(this),
|
|
2791
2811
|
matchesTransactionLedger: (value) => value === transactionLedger,
|
|
2792
2812
|
}),
|
|
2793
2813
|
);
|
|
@@ -2870,6 +2890,15 @@ export class SkillReleaseRegistry {
|
|
|
2870
2890
|
}
|
|
2871
2891
|
|
|
2872
2892
|
#initializeTenantMarker() {
|
|
2893
|
+
return withEvolutionFileIdentity(
|
|
2894
|
+
this.#fs,
|
|
2895
|
+
this.#markerPath,
|
|
2896
|
+
(_full, sameStable) =>
|
|
2897
|
+
this.#initializeTenantMarkerWithIdentity(sameStable),
|
|
2898
|
+
);
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
#initializeTenantMarkerWithIdentity(sameStable) {
|
|
2873
2902
|
const existing = lstatOrNull(this.#fs, this.#markerPath);
|
|
2874
2903
|
if (existing) {
|
|
2875
2904
|
const verified = this.#readAndVerifyTenantMarker();
|
|
@@ -2905,7 +2934,6 @@ export class SkillReleaseRegistry {
|
|
|
2905
2934
|
"release tenant marker temporary file is unsafe",
|
|
2906
2935
|
);
|
|
2907
2936
|
}
|
|
2908
|
-
const writtenIdentity = identity(written);
|
|
2909
2937
|
this.#fs.closeSync(descriptor);
|
|
2910
2938
|
descriptor = null;
|
|
2911
2939
|
if (this.#secure) {
|
|
@@ -2920,7 +2948,7 @@ export class SkillReleaseRegistry {
|
|
|
2920
2948
|
staged.isSymbolicLink() ||
|
|
2921
2949
|
Number(staged.nlink) !== 1 ||
|
|
2922
2950
|
staged.size !== bytes.length ||
|
|
2923
|
-
|
|
2951
|
+
!sameStable(staged, written) ||
|
|
2924
2952
|
!samePath(realpath(this.#fs, temporaryPath), temporaryPath)
|
|
2925
2953
|
) {
|
|
2926
2954
|
throw failure(
|
|
@@ -2943,7 +2971,7 @@ export class SkillReleaseRegistry {
|
|
|
2943
2971
|
!linked.isFile() ||
|
|
2944
2972
|
linked.isSymbolicLink() ||
|
|
2945
2973
|
Number(linked.nlink) !== 2 ||
|
|
2946
|
-
identity(linked) !==
|
|
2974
|
+
identity(linked) !== identity(staged)
|
|
2947
2975
|
) {
|
|
2948
2976
|
throw failure(
|
|
2949
2977
|
"SKILL_RELEASE_STORE_UNSAFE",
|
|
@@ -3229,6 +3257,12 @@ export class SkillReleaseRegistry {
|
|
|
3229
3257
|
throw failure("SKILL_RELEASE_STORE_UNSAFE", "temporary name is invalid");
|
|
3230
3258
|
}
|
|
3231
3259
|
const filePath = this.#path("staging", fileName);
|
|
3260
|
+
return withEvolutionFileIdentity(this.#fs, filePath, (_full, sameStable) =>
|
|
3261
|
+
this.#writeTemporaryWithIdentity(filePath, value, sameStable),
|
|
3262
|
+
);
|
|
3263
|
+
}
|
|
3264
|
+
|
|
3265
|
+
#writeTemporaryWithIdentity(filePath, value, sameStable) {
|
|
3232
3266
|
const bytes = serialize(value);
|
|
3233
3267
|
let descriptor = null;
|
|
3234
3268
|
let writtenIdentity = null;
|
|
@@ -3248,7 +3282,7 @@ export class SkillReleaseRegistry {
|
|
|
3248
3282
|
"temporary write was incomplete",
|
|
3249
3283
|
);
|
|
3250
3284
|
}
|
|
3251
|
-
writtenIdentity =
|
|
3285
|
+
writtenIdentity = written;
|
|
3252
3286
|
} finally {
|
|
3253
3287
|
if (descriptor !== null) this.#fs.closeSync(descriptor);
|
|
3254
3288
|
}
|
|
@@ -3263,7 +3297,7 @@ export class SkillReleaseRegistry {
|
|
|
3263
3297
|
!staged.isFile() ||
|
|
3264
3298
|
staged.isSymbolicLink() ||
|
|
3265
3299
|
Number(staged.nlink) !== 1 ||
|
|
3266
|
-
|
|
3300
|
+
!sameStable(staged, writtenIdentity) ||
|
|
3267
3301
|
staged.size !== bytes.length ||
|
|
3268
3302
|
!samePath(realpath(this.#fs, filePath), filePath)
|
|
3269
3303
|
) {
|
|
@@ -4006,8 +4040,8 @@ export class SkillReleaseRegistry {
|
|
|
4006
4040
|
}
|
|
4007
4041
|
const afterReleases = this.#readInventoryNames("artifacts", releasePattern);
|
|
4008
4042
|
const afterStates = this.#readInventoryNames("active", statePattern);
|
|
4009
|
-
const afterStateDigests = afterStates.map(
|
|
4010
|
-
this.readState(name.slice(0, -".json".length)).stateDigest,
|
|
4043
|
+
const afterStateDigests = afterStates.map(
|
|
4044
|
+
(name) => this.readState(name.slice(0, -".json".length)).stateDigest,
|
|
4011
4045
|
);
|
|
4012
4046
|
if (
|
|
4013
4047
|
canonicalJson(beforeReleases) !== canonicalJson(afterReleases) ||
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { formatProviderResponseError } from "./provider-http-error.js";
|
|
2
2
|
|
|
3
|
+
// Reasoning models spend the same output allowance on thinking before the
|
|
4
|
+
// final answer. A 16-token probe can therefore receive HTTP 200 with only
|
|
5
|
+
// reasoning_content and finish_reason=length from an otherwise usable model.
|
|
6
|
+
const PROBE_OUTPUT_TOKENS = 1024;
|
|
7
|
+
|
|
3
8
|
export async function probeLlmConnection(
|
|
4
9
|
target,
|
|
5
10
|
{ fetchImpl = fetch, timeoutMs = 20_000 } = {},
|
|
@@ -13,6 +18,7 @@ export async function probeLlmConnection(
|
|
|
13
18
|
model: target.model,
|
|
14
19
|
prompt: "Say hi in one word.",
|
|
15
20
|
stream: false,
|
|
21
|
+
options: { num_predict: PROBE_OUTPUT_TOKENS },
|
|
16
22
|
};
|
|
17
23
|
} else {
|
|
18
24
|
if (!target.apiKey) throw new Error("API key required");
|
|
@@ -22,7 +28,7 @@ export async function probeLlmConnection(
|
|
|
22
28
|
headers["anthropic-version"] = "2023-06-01";
|
|
23
29
|
body = {
|
|
24
30
|
model: target.model,
|
|
25
|
-
max_tokens:
|
|
31
|
+
max_tokens: PROBE_OUTPUT_TOKENS,
|
|
26
32
|
messages: [{ role: "user", content: "Say hi in one word." }],
|
|
27
33
|
};
|
|
28
34
|
} else if (target.provider === "gemini") {
|
|
@@ -30,7 +36,7 @@ export async function probeLlmConnection(
|
|
|
30
36
|
headers["x-goog-api-key"] = target.apiKey;
|
|
31
37
|
body = {
|
|
32
38
|
contents: [{ parts: [{ text: "Say hi in one word." }] }],
|
|
33
|
-
generationConfig: { maxOutputTokens:
|
|
39
|
+
generationConfig: { maxOutputTokens: PROBE_OUTPUT_TOKENS },
|
|
34
40
|
};
|
|
35
41
|
} else {
|
|
36
42
|
url = `${base}/chat/completions`;
|
|
@@ -38,7 +44,7 @@ export async function probeLlmConnection(
|
|
|
38
44
|
body = {
|
|
39
45
|
model: target.model,
|
|
40
46
|
messages: [{ role: "user", content: "Say hi in one word." }],
|
|
41
|
-
max_tokens:
|
|
47
|
+
max_tokens: PROBE_OUTPUT_TOKENS,
|
|
42
48
|
};
|
|
43
49
|
}
|
|
44
50
|
}
|
|
@@ -67,13 +73,24 @@ export async function probeLlmConnection(
|
|
|
67
73
|
.join("")
|
|
68
74
|
: target.provider === "gemini"
|
|
69
75
|
? data.candidates?.[0]?.content?.parts
|
|
76
|
+
?.filter((p) => p.thought !== true)
|
|
70
77
|
?.map((p) => p.text || "")
|
|
71
78
|
.join("")
|
|
72
79
|
: data.choices?.[0]?.message?.content;
|
|
73
|
-
if (typeof text !== "string" || !text.trim())
|
|
80
|
+
if (typeof text !== "string" || !text.trim()) {
|
|
81
|
+
const reachedLimit =
|
|
82
|
+
data.choices?.[0]?.finish_reason === "length" ||
|
|
83
|
+
data.candidates?.[0]?.finishReason === "MAX_TOKENS" ||
|
|
84
|
+
data.stop_reason === "max_tokens" ||
|
|
85
|
+
data.done_reason === "length";
|
|
86
|
+
if (reachedLimit)
|
|
87
|
+
throw new Error(
|
|
88
|
+
"The model responded but exhausted the test output limit before a final answer; this does not mean the connection settings are invalid",
|
|
89
|
+
);
|
|
74
90
|
throw new Error(
|
|
75
91
|
"The endpoint returned no model text; check the protocol and model",
|
|
76
92
|
);
|
|
93
|
+
}
|
|
77
94
|
return text.trim().slice(0, 100);
|
|
78
95
|
} catch (error) {
|
|
79
96
|
if (controller.signal.aborted)
|
|
@@ -467,6 +467,7 @@ async function initHub() {
|
|
|
467
467
|
|
|
468
468
|
// Phase 8 — EntityResolver pipeline
|
|
469
469
|
const entityResolver = new EntityResolver({ vault });
|
|
470
|
+
let resolverEmbeddingStage = null;
|
|
470
471
|
// Plan A v0.1 — in-APK Android cc has no Ollama on localhost:11434.
|
|
471
472
|
// Every embedding call would TCP-timeout (measured: ~60s extra per sync
|
|
472
473
|
// on Xiaomi 24115RA8EC 2026-05-21). Detect Termux $PREFIX for our APK
|
|
@@ -504,6 +505,7 @@ async function initHub() {
|
|
|
504
505
|
vault,
|
|
505
506
|
});
|
|
506
507
|
entityResolver._embeddingStage = embeddingStage.asStageFn();
|
|
508
|
+
resolverEmbeddingStage = embeddingStage;
|
|
507
509
|
}
|
|
508
510
|
} catch (_err) {
|
|
509
511
|
// Fall back to rule-only — registry still works
|
|
@@ -1071,6 +1073,23 @@ async function initHub() {
|
|
|
1071
1073
|
alipayAccountsPath,
|
|
1072
1074
|
entityResolver,
|
|
1073
1075
|
aichatAccountsStore,
|
|
1076
|
+
async drainResolver(options = {}, factory = null) {
|
|
1077
|
+
if (factory === null) return entityResolver.drain(options);
|
|
1078
|
+
const { createGovernedHubResolver } =
|
|
1079
|
+
await import("./evolution/governed-hub-resolver.js");
|
|
1080
|
+
const scoped = createGovernedHubResolver(
|
|
1081
|
+
{
|
|
1082
|
+
resolver: entityResolver,
|
|
1083
|
+
llm,
|
|
1084
|
+
embeddingStage: resolverEmbeddingStage,
|
|
1085
|
+
EntityResolver,
|
|
1086
|
+
EmbeddingStage: EntityResolverEmbeddingStage,
|
|
1087
|
+
LLMStage: EntityResolverLLMStage,
|
|
1088
|
+
},
|
|
1089
|
+
factory,
|
|
1090
|
+
);
|
|
1091
|
+
return scoped.drain(options);
|
|
1092
|
+
},
|
|
1074
1093
|
aiChatAdapter,
|
|
1075
1094
|
aichatWizard,
|
|
1076
1095
|
aichatHealthChecker,
|
|
@@ -2091,6 +2110,36 @@ export async function getHub() {
|
|
|
2091
2110
|
return _initPromise;
|
|
2092
2111
|
}
|
|
2093
2112
|
|
|
2113
|
+
export async function getGovernedAnalysisHub(evolutionCompositionFactory) {
|
|
2114
|
+
const hub = await getHub();
|
|
2115
|
+
const { createGovernedHubLlm } =
|
|
2116
|
+
await import("./evolution/governed-hub-llm.js");
|
|
2117
|
+
const llm = createGovernedHubLlm(hub.engine.llm, evolutionCompositionFactory);
|
|
2118
|
+
const engine = new AnalysisEngine({
|
|
2119
|
+
vault: hub.engine.vault,
|
|
2120
|
+
llm,
|
|
2121
|
+
ragRetriever: hub.engine.ragRetriever,
|
|
2122
|
+
maxFacts: hub.engine.maxFacts,
|
|
2123
|
+
maxQueryLimit: hub.engine.maxQueryLimit,
|
|
2124
|
+
systemPrompt: hub.engine.systemPrompt,
|
|
2125
|
+
});
|
|
2126
|
+
const { runGovernedHubSkill } =
|
|
2127
|
+
await import("./evolution/governed-hub-skill.js");
|
|
2128
|
+
return Object.freeze({
|
|
2129
|
+
...hub,
|
|
2130
|
+
llm,
|
|
2131
|
+
engine,
|
|
2132
|
+
runSkill: (name, options = {}) =>
|
|
2133
|
+
runGovernedHubSkill(
|
|
2134
|
+
hub,
|
|
2135
|
+
evolutionCompositionFactory,
|
|
2136
|
+
runAnalysisSkill,
|
|
2137
|
+
name,
|
|
2138
|
+
options,
|
|
2139
|
+
),
|
|
2140
|
+
});
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2094
2143
|
// ─── Minimal hub bootstrap for read-only / LLM-free commands ────────────
|
|
2095
2144
|
//
|
|
2096
2145
|
// 2026-05-27 — `cc hub retrieve-context` cold-start was 90s+ on Android
|
|
@@ -6,37 +6,77 @@
|
|
|
6
6
|
|
|
7
7
|
import { BUILT_IN_PROVIDERS } from "./llm-providers.js";
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
const
|
|
9
|
+
async function* responseLines(body) {
|
|
10
|
+
const reader = body.getReader();
|
|
11
|
+
const decoder = new TextDecoder();
|
|
12
|
+
let buffer = "";
|
|
13
|
+
let ended = false;
|
|
14
|
+
try {
|
|
15
|
+
while (true) {
|
|
16
|
+
const { value, done } = await reader.read();
|
|
17
|
+
if (done) {
|
|
18
|
+
ended = true;
|
|
19
|
+
buffer += decoder.decode();
|
|
20
|
+
if (buffer) yield buffer;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
buffer += decoder.decode(value, { stream: true });
|
|
24
|
+
let newline;
|
|
25
|
+
while ((newline = buffer.indexOf("\n")) >= 0) {
|
|
26
|
+
const line = buffer.slice(0, newline);
|
|
27
|
+
buffer = buffer.slice(newline + 1);
|
|
28
|
+
yield line;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
} finally {
|
|
32
|
+
try {
|
|
33
|
+
if (!ended) await reader.cancel();
|
|
34
|
+
} catch {
|
|
35
|
+
// Preserve the original parser/transport failure during cleanup.
|
|
36
|
+
} finally {
|
|
37
|
+
reader.releaseLock();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function* ollamaTokenStream({
|
|
43
|
+
baseUrl,
|
|
44
|
+
model,
|
|
45
|
+
prompt,
|
|
46
|
+
messages,
|
|
47
|
+
signal,
|
|
48
|
+
requireCompletion = false,
|
|
49
|
+
}) {
|
|
50
|
+
const res = await fetch(`${baseUrl}/api/${messages ? "chat" : "generate"}`, {
|
|
11
51
|
method: "POST",
|
|
12
52
|
headers: { "Content-Type": "application/json" },
|
|
13
|
-
body: JSON.stringify(
|
|
53
|
+
body: JSON.stringify(
|
|
54
|
+
messages
|
|
55
|
+
? { model, messages, stream: true }
|
|
56
|
+
: { model, prompt, stream: true },
|
|
57
|
+
),
|
|
14
58
|
signal,
|
|
15
59
|
});
|
|
16
60
|
if (!res.ok || !res.body) {
|
|
17
61
|
throw new Error(`Ollama ${res.status} ${res.statusText}`);
|
|
18
62
|
}
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
const obj = JSON.parse(line);
|
|
33
|
-
if (obj.response) yield obj.response;
|
|
34
|
-
if (obj.done) return;
|
|
35
|
-
} catch {
|
|
36
|
-
/* skip malformed */
|
|
37
|
-
}
|
|
63
|
+
for await (const raw of responseLines(res.body)) {
|
|
64
|
+
const line = raw.trim();
|
|
65
|
+
if (!line) continue;
|
|
66
|
+
try {
|
|
67
|
+
const obj = JSON.parse(line);
|
|
68
|
+
if (requireCompletion && obj.error)
|
|
69
|
+
throw new Error("Ollama stream returned an error");
|
|
70
|
+
const delta = messages ? obj.message?.content : obj.response;
|
|
71
|
+
if (delta) yield delta;
|
|
72
|
+
if (obj.done) return;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (requireCompletion) throw error;
|
|
75
|
+
/* skip malformed */
|
|
38
76
|
}
|
|
39
77
|
}
|
|
78
|
+
if (requireCompletion)
|
|
79
|
+
throw new Error("Ollama stream ended before its completion marker");
|
|
40
80
|
}
|
|
41
81
|
|
|
42
82
|
export async function* openAIStream({
|
|
@@ -44,7 +84,9 @@ export async function* openAIStream({
|
|
|
44
84
|
apiKey,
|
|
45
85
|
model,
|
|
46
86
|
prompt,
|
|
87
|
+
messages,
|
|
47
88
|
signal,
|
|
89
|
+
requireCompletion = false,
|
|
48
90
|
}) {
|
|
49
91
|
const res = await fetch(`${baseUrl}/chat/completions`, {
|
|
50
92
|
method: "POST",
|
|
@@ -54,7 +96,7 @@ export async function* openAIStream({
|
|
|
54
96
|
},
|
|
55
97
|
body: JSON.stringify({
|
|
56
98
|
model,
|
|
57
|
-
messages: [{ role: "user", content: prompt }],
|
|
99
|
+
messages: messages ?? [{ role: "user", content: prompt }],
|
|
58
100
|
stream: true,
|
|
59
101
|
}),
|
|
60
102
|
signal,
|
|
@@ -62,29 +104,24 @@ export async function* openAIStream({
|
|
|
62
104
|
if (!res.ok || !res.body) {
|
|
63
105
|
throw new Error(`${res.status} ${res.statusText}`);
|
|
64
106
|
}
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
const obj = JSON.parse(payload);
|
|
81
|
-
const delta = obj?.choices?.[0]?.delta?.content;
|
|
82
|
-
if (delta) yield delta;
|
|
83
|
-
} catch {
|
|
84
|
-
/* skip */
|
|
85
|
-
}
|
|
107
|
+
for await (const raw of responseLines(res.body)) {
|
|
108
|
+
const line = raw.trim();
|
|
109
|
+
if (!line || !line.startsWith("data:")) continue;
|
|
110
|
+
const payload = line.slice(5).trim();
|
|
111
|
+
if (payload === "[DONE]") return;
|
|
112
|
+
try {
|
|
113
|
+
const obj = JSON.parse(payload);
|
|
114
|
+
if (requireCompletion && obj.error)
|
|
115
|
+
throw new Error("OpenAI stream returned an error");
|
|
116
|
+
const delta = obj?.choices?.[0]?.delta?.content;
|
|
117
|
+
if (delta) yield delta;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (requireCompletion) throw error;
|
|
120
|
+
/* skip */
|
|
86
121
|
}
|
|
87
122
|
}
|
|
123
|
+
if (requireCompletion)
|
|
124
|
+
throw new Error("OpenAI stream ended before its completion marker");
|
|
88
125
|
}
|
|
89
126
|
|
|
90
127
|
/**
|
|
@@ -92,13 +129,23 @@ export async function* openAIStream({
|
|
|
92
129
|
* Throws on unsupported provider / missing API key.
|
|
93
130
|
*/
|
|
94
131
|
export function buildProviderSource(provider, opts = {}) {
|
|
95
|
-
const {
|
|
132
|
+
const {
|
|
133
|
+
model,
|
|
134
|
+
baseUrl,
|
|
135
|
+
apiKey,
|
|
136
|
+
prompt,
|
|
137
|
+
messages,
|
|
138
|
+
signal,
|
|
139
|
+
requireCompletion,
|
|
140
|
+
} = opts;
|
|
96
141
|
if (provider === "ollama") {
|
|
97
142
|
return ollamaTokenStream({
|
|
98
143
|
baseUrl: baseUrl || "http://localhost:11434",
|
|
99
144
|
model: model || "qwen2:7b",
|
|
100
145
|
prompt,
|
|
146
|
+
messages,
|
|
101
147
|
signal,
|
|
148
|
+
requireCompletion,
|
|
102
149
|
});
|
|
103
150
|
}
|
|
104
151
|
const def = BUILT_IN_PROVIDERS[provider];
|
|
@@ -115,7 +162,9 @@ export function buildProviderSource(provider, opts = {}) {
|
|
|
115
162
|
apiKey: finalKey,
|
|
116
163
|
model: model || def.models[0],
|
|
117
164
|
prompt,
|
|
165
|
+
messages,
|
|
118
166
|
signal,
|
|
167
|
+
requireCompletion,
|
|
119
168
|
});
|
|
120
169
|
}
|
|
121
170
|
|