chainlesschain 0.166.20 → 0.166.21
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-CcJst5Zs.js → AIOps-BjMnsUci.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BpHPFKd3.js → ActionButton-DdYFP03A.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DHAsASuS.js → Analytics-Bb8TuuK_.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BTKwo5RC.js → AppLayout-Kn3TIOtk.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BcCBTggA.js → Artifacts-4IxpqI_b.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-z1JvJYbT.js → Audit-BOV2caqU.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-CJ7DCMDR.js → BackgroundAgents-B86k-ay4.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CKpxw4uw.js → Backup-C5_ie1e3.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-2jI_IlUB.js → BaseInput-xpwg6QCW.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-C_4RfBeN.js → Chat-CVZOcZau.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-D8fPYhUu.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-BnF4z4N1.js → Checkbox-MKTq3zdP.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-m_LMamhM.js → Codegen-JUk3wzep.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DzMbA760.js → Col-JPvTA0kC.js} +1 -1
- package/src/assets/web-panel/assets/{Community-SScuZUcW.js → Community-Tqgl87NQ.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CiOHRvwQ.js → Compact-BuAFYBmN.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-97FzYz-6.js → Compliance-CXS9WIPZ.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-C0ss-SRl.js → Cowork-Ds2bFHrr.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-CCbdc9fp.js → Cron-C_dV_OeW.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BPnabXej.js → Crosschain-BsO6tIRo.js} +1 -1
- package/src/assets/web-panel/assets/{DID-B65hFnHs.js → DID-rNOFqBac.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-B6qR_TF2.js → Dashboard-C2fcKKVY.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-6c-2Fxm-.js → Dropdown-Y7Ndr0X_.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-GD57mfiz.js → EmailListRenderer-DcP_uPmL.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-B2mxG6LN.js → FamilyGuardDashboard-Cwn3gDzk.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-cZS-uvAo.js → Federation-ClQuOsqb.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-UbSfBIe1.js → FormItemContext-DfwtmlcK.js} +1 -1
- package/src/assets/web-panel/assets/GenericCardRenderer-zNZhtbC5.js +1 -0
- package/src/assets/web-panel/assets/{Git-DAlhOiDA.js → Git-BZmdW-G5.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-B-RRVaP8.js → Governance-CqBk4lEn.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DBl-pXYR.js → Inference-Dbmt3ZXk.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DjqOkvgY.js → KnowledgeGraph-hMTH6qf3.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CEV7AEAD.js → Logs-CkiYgiW2.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-BDCxa4d1.js → MarkdownRenderer-BL1FElXg.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-oRjzfmYJ.js → Marketplace-CbB6wfin.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BiTnznLw.js → McpTools-BSwiFtte.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-DR0C5K4N.js → Memory-B03PeHrz.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge--oR8wEcp.js → MobileBridge-CRGrnd-X.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DKqpYMuG.js → MobileProjects-CE_5v2kO.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Dg0D6fDQ.js → Mtc-Dp11QVx1.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-D4_-yWTK.js → MtcAudit-CYu-3JBu.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-Coz55FxA.js → Multisig-CGyJ0Xl3.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BWVjgW9Y.js → NLProgramming-CoWi_ov1.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BflVuGwr.js → Notes-CgU9DDo9.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-CT0F_dJ7.js → NotificationSettings-DX4I2b4W.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CwHc15Zs.js +1 -0
- package/src/assets/web-panel/assets/{Organization-CMDTM2z5.js → Organization-oopLO_tk.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-acKvvYpC.js → Overflow-BFVvjMyf.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CRq5j9Ax.js → P2P-CBy5t-Tk.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DmtzN6Kn.js → PdhVaultBrowser--YWJNsu6.js} +4 -4
- package/src/assets/web-panel/assets/{Permissions-SZ-kjJDa.js → Permissions-C2dZ8scb.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-DXh-ZfgJ.js → PersonalDataHub-Bs3l7Hlx.js} +6 -6
- package/src/assets/web-panel/assets/{Pipeline-DhBU9ftP.js → Pipeline-DEBWbZuD.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BgEXpO_V.js → Privacy-CjHBb6NU.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-B2DJ-dmK.js → ProjectInit-D2WipUz5.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-ve3SocWc.js → ProjectSettings-B6WfnrGB.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-5ZvFasSJ.js → Projects-FF_thRi6.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-C8fcF3BK.js → Providers-CCv5M_Dv.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-unjO8lNO.js → QrScannerModal-CGthxU54.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BT1QtT7D.js → QuickAsk-CnPmXPFb.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BSjFrY1w.js → Recommend-Df_tGEa9.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-CWV0bICg.js → RemoteSession-Z58N0Loe.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-BMjiMsk2.js → Reputation-CZTBQ2Vt.js} +1 -1
- package/src/assets/web-panel/assets/{Row-9XCY22PM.js → Row-CdK3YpCQ.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DQ9PDy7Q.js → RssFeed-CFMVT5sb.js} +3 -3
- package/src/assets/web-panel/assets/{Search-Boym2HwG.js → Search-DrF8RIvD.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Gbq3lGQb.js → Security-Ddk2f2DM.js} +4 -4
- package/src/assets/web-panel/assets/{Services-OcUfLdnu.js → Services-CCuK-VOo.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BjTog6cA.js → Skeleton-DxCpHzeM.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-Dny4ft_1.js → Skills-t2oTDgcv.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-Cy_VnhpZ.js → Sla-Df63M9_R.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CrFuL_lp.js → SpeechSettings-l7ox6KuX.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-k-FnjuOR.js → SyncSettings-COd_tgPp.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-ZEqDSo0_.js → Tasks-CzDCcrik.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CYYksYV5.js → Templates-D-vfxTGA.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BvTTRTHS.js → Tenant-BlpT_Gxc.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DMOQOtkj.js → Terminal-D5rq52Hg.js} +2 -2
- package/src/assets/web-panel/assets/TimelineRenderer-DdoMndkK.js +1 -0
- package/src/assets/web-panel/assets/{Tokens-VW6Wocdp.js → Tokens-BhgBsI6g.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BtOxZqP6.js → Trigger-DwwzoikZ.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-XcGJoTQx.js → Trust-HkTpzXFb.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-aASNCs5j.js → UkeySign-D9nk6Ehe.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-Bz89M52u.js → VideoEditing-BKk0rfoy.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-uU4dSS_N.js → Wallet-IVWIU_Lq.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-Brrroxgr.js → WebAuthn-CFTCpfsG.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-Cny-evI9.js → WorkflowEditor-Dnf4Obif.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CJ5iSwWq.js → chat-D2snERdj.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DWa-2flG.js → colors-DKEjK1N1.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DFwUfptU.js → compact-item-BEDLwQdt.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-FHWWcyeB.js → createContext-DA9iW1a0.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DyhgpXPf.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-QY_TElrN.js → hasIn-5l2xEcY9.js} +1 -1
- package/src/assets/web-panel/assets/{index-hw1J_Oxs.js → index-5pW4t1iD.js} +1 -1
- package/src/assets/web-panel/assets/{index-BR4RMPnB.js → index-7P1b8HZo.js} +1 -1
- package/src/assets/web-panel/assets/{index-BUNwuu5v.js → index-B00GeekA.js} +1 -1
- package/src/assets/web-panel/assets/{index-B18zERdP.js → index-B4QYBliR.js} +1 -1
- package/src/assets/web-panel/assets/{index-wqiOz655.js → index-B7_W_rYd.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQCtBurK.js → index-BO9dQjpt.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUfILsc-.js → index-BZ5lY2zx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUepuy7O.js → index-B_eCoZgW.js} +1 -1
- package/src/assets/web-panel/assets/{index-uRUZdL7I.js → index-BgfImyZI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUoLd1dt.js → index-Bq5P3piS.js} +1 -1
- package/src/assets/web-panel/assets/{index-BiHRihC0.js → index-Bqc8tdIF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcVr-LOk.js → index-C7sKbM5s.js} +1 -1
- package/src/assets/web-panel/assets/{index-DDpXDi4J.js → index-CGlotREA.js} +1 -1
- package/src/assets/web-panel/assets/{index-CjejB-z9.js → index-CRPrHE7h.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2rPJ6Sv.js → index-CX1x4AFn.js} +1 -1
- package/src/assets/web-panel/assets/{index-w3SdYwaC.js → index-CbTPvp2_.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-Mhq8K6.js → index-CjWltbi_.js} +1 -1
- package/src/assets/web-panel/assets/index-Ck1jhFE0.js +1 -0
- package/src/assets/web-panel/assets/{index-Bd7McYpJ.js → index-CkMKuqUs.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYbwQ7SP.js → index-CmruhzFi.js} +1 -1
- package/src/assets/web-panel/assets/{index-2ufsNuVn.js → index-Csfb-7hw.js} +1 -1
- package/src/assets/web-panel/assets/{index-0umhind6.js → index-CxEtQYdi.js} +1 -1
- package/src/assets/web-panel/assets/{index-B33frlaD.js → index-D7z4zB_a.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dy2PnKZx.js → index-DLoalqRX.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUiTugu3.js → index-DU2TXJu-.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGv5In_T.js → index-DXet1xol.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCHMVYG1.js → index-Dbd7eEex.js} +1 -1
- package/src/assets/web-panel/assets/{index-th467RN6.js → index-Dbkghqn_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkmgbT7h.js → index-Dg2a9owQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-aTiI7lOQ.js → index-DpqtbXdE.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5F1vwec.js → index-DqwPFbOb.js} +3 -3
- package/src/assets/web-panel/assets/{index-DaHvj4jA.js → index-DvV03Ayk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRsGZBHc.js → index-NJieh3T1.js} +1 -1
- package/src/assets/web-panel/assets/{index-CeGn0A4W.js → index-Td8ZwyoC.js} +1 -1
- package/src/assets/web-panel/assets/index-c8jJEHVs.js +1 -0
- package/src/assets/web-panel/assets/{index-va4Rt-yM.js → index-e-e40id6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CKS7n6mE.js → index-fccZmC_h.js} +1 -1
- package/src/assets/web-panel/assets/{index-BEjwv2x6.js → index-fgYk0QXO.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dlq-u9E4.js → index-yYEQXYaU.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BeSPUFDz.js → initDefaultProps-CPrcB1TC.js} +1 -1
- package/src/assets/web-panel/assets/{motion-Dcajvjm9.js → motion-BQ7mWRh_.js} +1 -1
- package/src/assets/web-panel/assets/{move-B76orau-.js → move-CzOEq99p.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-zB6yFwcX.js → mtc-parser-BlQI5jrw.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BsbNjz8w.js → omit-DlW4gxmv.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BiJ9tssQ.js → pickAttrs-BBYms5nP.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BdfMjMeN.js → placementArrow-az2MPJ_7.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-rV4LGpbU.js → responsiveObserve-D1-ibEol.js} +1 -1
- package/src/assets/web-panel/assets/{slide-HsVw1qFQ.js → slide-DHGqa4GA.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-D1ry8XPE.js → statusUtils-CYGDo6lD.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-B1t6aQQ_.js → styleChecker-Cl2EM9K6.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-C8qbl-9w.js → useFlexGapSupport-DdYhzuv1.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-R7g_mYTC.js → useFs-DWumPuBW.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-Cd-kCe7Z.js → usePersonalDataHub-DxAO-Aqi.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-9WhK5UWA.js → vnode-CplsQf_N.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CUqppPco.js → zoom-Cw5HXNSG.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +2 -2
- package/src/commands/agent.js +19 -1
- package/src/commands/evolution-knowledge.js +54 -0
- package/src/commands/evolution-workbench.js +83 -0
- package/src/commands/evolution.js +6 -1
- package/src/commands/serve.js +16 -0
- package/src/commands/skill.js +158 -16
- package/src/data/changelog.json +13 -0
- package/src/gateways/ws/session-protocol.js +2 -0
- package/src/gateways/ws/ws-agent-handler.js +124 -69
- package/src/gateways/ws/ws-server.js +19 -0
- package/src/generated/cc-agent-protocol.schema.json +7 -1
- package/src/harness/jsonl-session-store.js +25 -1
- package/src/index.js +1 -1
- package/src/lib/app-server/cli-agent-kernel-adapter.js +8 -0
- package/src/lib/app-server/graph-runtime.js +40 -1
- package/src/lib/app-server/server.js +181 -3
- package/src/lib/app-server/websocket-transport.js +2 -0
- package/src/lib/evolution/agent-evolution-runtime-composition-brand.js +17 -0
- package/src/lib/evolution/agent-evolution-runtime-composition.js +165 -1
- package/src/lib/evolution/controlled-skill-production-pilot.js +912 -0
- package/src/lib/evolution/evolution-artifact-ports.js +21 -0
- package/src/lib/evolution/evolution-raw-crypto-shred.js +185 -0
- package/src/lib/evolution/evolution-workbench-batch-executor.js +195 -0
- package/src/lib/evolution/evolution-workbench-cli-host.js +169 -0
- package/src/lib/evolution/evolution-workbench-metrics-ledger-adapter.js +648 -0
- package/src/lib/evolution/evolution-workbench-metrics.js +943 -0
- package/src/lib/evolution/evolution-workbench-projection.js +734 -0
- package/src/lib/evolution/evolution-workbench-version-control.js +272 -0
- package/src/lib/evolution/governed-knowledge-conflict-merge.js +352 -0
- package/src/lib/evolution/governed-knowledge-dependency-authority.js +273 -0
- package/src/lib/evolution/governed-knowledge-dependency-ledger-executor.js +646 -0
- package/src/lib/evolution/governed-knowledge-merge-ledger-executor.js +644 -0
- package/src/lib/evolution/governed-knowledge-merge-publisher-authority.js +282 -0
- package/src/lib/evolution/governed-knowledge-node-crypto-authority.js +348 -0
- package/src/lib/evolution/governed-knowledge-rbac-approval-authority.js +621 -0
- package/src/lib/evolution/governed-knowledge-review-host.js +249 -0
- package/src/lib/evolution/governed-knowledge-sync-ledger-adapter.js +643 -0
- package/src/lib/evolution/governed-knowledge-sync-merge-publisher.js +259 -0
- package/src/lib/evolution/governed-knowledge-sync.js +485 -0
- package/src/lib/evolution/governed-knowledge-trust-ledger.js +707 -0
- package/src/lib/evolution/governed-skill-marketplace.js +430 -0
- package/src/lib/evolution/governed-wiki-pruning.js +435 -0
- package/src/lib/evolution/skill-outcome-index-authority.js +268 -0
- package/src/lib/evolution/skill-outcome-source-catalog-authority.js +237 -0
- package/src/lib/public-ide-capability-manifest.js +4 -0
- package/src/lib/skill-outcome-authority.js +43 -0
- package/src/lib/skill-outcome-transcript-authority.js +252 -0
- package/src/lib/skill-retrieval-router-benchmark.js +135 -0
- package/src/lib/skill-retrieval-router.js +235 -0
- package/src/lib/skill-vector-authority.js +325 -0
- package/src/lib/skill-vector-process-authority.js +386 -0
- package/src/repl/agent-repl.js +7 -1
- package/src/runtime/agent-core.js +72 -6
- package/src/runtime/agent-runtime.js +42 -0
- package/src/runtime/headless-runner.js +32 -0
- package/src/runtime/headless-stream.js +36 -1
- package/src/runtime/runtime-factory.js +62 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-eMmw1eZF.js +0 -1
- package/src/assets/web-panel/assets/GenericCardRenderer-BMAtvGt1.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-Dyrnqy_o.js +0 -1
- package/src/assets/web-panel/assets/TimelineRenderer-CnudkJxk.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DxufPwXC.js +0 -1
- package/src/assets/web-panel/assets/index-ZMTB9p5z.js +0 -1
- package/src/assets/web-panel/assets/index-_PvIDelE.js +0 -1
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { BM25Search } from "./bm25-search.js";
|
|
2
|
+
|
|
3
|
+
export const SKILL_RETRIEVAL_RESULT_SCHEMA =
|
|
4
|
+
"chainlesschain.skill-retrieval-result/v1";
|
|
5
|
+
export const MAX_ROUTABLE_SKILLS = 10_000;
|
|
6
|
+
|
|
7
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
8
|
+
|
|
9
|
+
function finite(value, fallback = 0) {
|
|
10
|
+
return Number.isFinite(Number(value)) ? Number(value) : fallback;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalize(scores) {
|
|
14
|
+
const values = [...scores.values()];
|
|
15
|
+
const max = Math.max(0, ...values);
|
|
16
|
+
if (max === 0) return new Map([...scores].map(([key]) => [key, 0]));
|
|
17
|
+
return new Map([...scores].map(([key, value]) => [key, value / max]));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function digestFor(skill) {
|
|
21
|
+
const value = skill?.executionIdentity?.contentDigest;
|
|
22
|
+
return DIGEST.test(value || "") ? value : null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function containsPath(skill, targetPath) {
|
|
26
|
+
if (!targetPath || !Array.isArray(skill.paths) || skill.paths.length === 0)
|
|
27
|
+
return true;
|
|
28
|
+
const normalized = String(targetPath).replaceAll("\\", "/");
|
|
29
|
+
return skill.paths.some((scope) => {
|
|
30
|
+
const prefix = String(scope).replaceAll("\\", "/").replace(/\/$/u, "");
|
|
31
|
+
return normalized === prefix || normalized.startsWith(`${prefix}/`);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function compatible(skill, target) {
|
|
36
|
+
if (!target) return { accepted: true, reasons: [] };
|
|
37
|
+
const reasons = [];
|
|
38
|
+
if (
|
|
39
|
+
Array.isArray(skill.os) &&
|
|
40
|
+
skill.os.length > 0 &&
|
|
41
|
+
target.os &&
|
|
42
|
+
!skill.os.includes(target.os)
|
|
43
|
+
) {
|
|
44
|
+
reasons.push(`os:${target.os}`);
|
|
45
|
+
}
|
|
46
|
+
if (
|
|
47
|
+
Array.isArray(target.allowedCapabilities) &&
|
|
48
|
+
Array.isArray(skill.capabilities)
|
|
49
|
+
) {
|
|
50
|
+
const allowed = new Set(target.allowedCapabilities);
|
|
51
|
+
const denied = skill.capabilities.filter((value) => !allowed.has(value));
|
|
52
|
+
if (denied.length > 0) reasons.push(`capability:${denied.join(",")}`);
|
|
53
|
+
}
|
|
54
|
+
return { accepted: reasons.length === 0, reasons };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function metricFor(metrics, digest) {
|
|
58
|
+
const value = metrics?.[digest] ?? metrics?.get?.(digest) ?? null;
|
|
59
|
+
if (value === null) return { samples: 0, successRate: 0, correctionRate: 0 };
|
|
60
|
+
const samples = finite(value.samples);
|
|
61
|
+
const successRate = finite(value.successRate);
|
|
62
|
+
const correctionRate = finite(value.correctionRate);
|
|
63
|
+
if (
|
|
64
|
+
!Number.isSafeInteger(samples) ||
|
|
65
|
+
samples < 0 ||
|
|
66
|
+
successRate < 0 ||
|
|
67
|
+
successRate > 1 ||
|
|
68
|
+
correctionRate < 0 ||
|
|
69
|
+
correctionRate > 1
|
|
70
|
+
) {
|
|
71
|
+
throw new TypeError("Skill outcome metrics are invalid");
|
|
72
|
+
}
|
|
73
|
+
return { samples, successRate, correctionRate };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function routeSkillDescriptors({
|
|
77
|
+
skills,
|
|
78
|
+
query,
|
|
79
|
+
namespace = null,
|
|
80
|
+
tags = [],
|
|
81
|
+
targetPath = null,
|
|
82
|
+
target = null,
|
|
83
|
+
vectorScores = null,
|
|
84
|
+
outcomeMetrics = null,
|
|
85
|
+
topK = 5,
|
|
86
|
+
ambiguityMargin = 0.02,
|
|
87
|
+
} = {}) {
|
|
88
|
+
if (
|
|
89
|
+
!Array.isArray(skills) ||
|
|
90
|
+
skills.length > MAX_ROUTABLE_SKILLS ||
|
|
91
|
+
typeof query !== "string" ||
|
|
92
|
+
query.trim().length < 1 ||
|
|
93
|
+
query.length > 4096 ||
|
|
94
|
+
!Array.isArray(tags) ||
|
|
95
|
+
!Number.isSafeInteger(topK) ||
|
|
96
|
+
topK < 1 ||
|
|
97
|
+
topK > 64 ||
|
|
98
|
+
!Number.isFinite(ambiguityMargin) ||
|
|
99
|
+
ambiguityMargin < 0 ||
|
|
100
|
+
ambiguityMargin > 1
|
|
101
|
+
) {
|
|
102
|
+
throw new TypeError("Skill retrieval request is invalid or unbounded");
|
|
103
|
+
}
|
|
104
|
+
const requiredTags = new Set(
|
|
105
|
+
tags.map((value) => String(value).toLowerCase()),
|
|
106
|
+
);
|
|
107
|
+
const rejected = [];
|
|
108
|
+
const admitted = [];
|
|
109
|
+
for (const skill of skills) {
|
|
110
|
+
const digest = digestFor(skill);
|
|
111
|
+
const skillTags = new Set(
|
|
112
|
+
(Array.isArray(skill.tags) ? skill.tags : []).map((value) =>
|
|
113
|
+
String(value).toLowerCase(),
|
|
114
|
+
),
|
|
115
|
+
);
|
|
116
|
+
const mismatch = compatible(skill, target);
|
|
117
|
+
const reasons = [];
|
|
118
|
+
if (!digest) reasons.push("missing-content-digest");
|
|
119
|
+
if (namespace !== null && skill.source !== namespace)
|
|
120
|
+
reasons.push("namespace-mismatch");
|
|
121
|
+
if ([...requiredTags].some((value) => !skillTags.has(value)))
|
|
122
|
+
reasons.push("tag-mismatch");
|
|
123
|
+
if (!containsPath(skill, targetPath)) reasons.push("path-mismatch");
|
|
124
|
+
reasons.push(...mismatch.reasons);
|
|
125
|
+
if (reasons.length > 0) {
|
|
126
|
+
rejected.push({ id: skill.id, digest, reasons });
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
admitted.push({ skill, digest });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const bm25 = new BM25Search({ language: "auto" });
|
|
133
|
+
bm25.indexDocuments(
|
|
134
|
+
admitted.map(({ skill, digest }) => ({
|
|
135
|
+
id: digest,
|
|
136
|
+
title: `${skill.id} ${skill.displayName || ""} ${(skill.tags || []).join(" ")}`,
|
|
137
|
+
content: `${skill.description || ""} ${skill.category || ""}`,
|
|
138
|
+
})),
|
|
139
|
+
);
|
|
140
|
+
const bm25Scores = normalize(
|
|
141
|
+
new Map(
|
|
142
|
+
bm25
|
|
143
|
+
.search(query, { topK: admitted.length || 1, threshold: 0 })
|
|
144
|
+
.map(({ id, score }) => [id, score]),
|
|
145
|
+
),
|
|
146
|
+
);
|
|
147
|
+
const vector = normalize(
|
|
148
|
+
new Map(
|
|
149
|
+
admitted.map(({ digest }) => [
|
|
150
|
+
digest,
|
|
151
|
+
Math.max(
|
|
152
|
+
0,
|
|
153
|
+
finite(vectorScores?.[digest] ?? vectorScores?.get?.(digest)),
|
|
154
|
+
),
|
|
155
|
+
]),
|
|
156
|
+
),
|
|
157
|
+
);
|
|
158
|
+
const vectorAvailable = [...vector.values()].some((value) => value > 0);
|
|
159
|
+
const candidates = admitted
|
|
160
|
+
.map(({ skill, digest }) => {
|
|
161
|
+
const lexicalScore = bm25Scores.get(digest) || 0;
|
|
162
|
+
const vectorScore = vector.get(digest) || 0;
|
|
163
|
+
const outcome = metricFor(outcomeMetrics, digest);
|
|
164
|
+
const outcomeScore =
|
|
165
|
+
outcome.samples === 0
|
|
166
|
+
? 0.5
|
|
167
|
+
: outcome.successRate * (1 - outcome.correctionRate);
|
|
168
|
+
const score =
|
|
169
|
+
lexicalScore * (vectorAvailable ? 0.45 : 0.75) +
|
|
170
|
+
vectorScore * (vectorAvailable ? 0.35 : 0) +
|
|
171
|
+
outcomeScore * (vectorAvailable ? 0.2 : 0.25);
|
|
172
|
+
return {
|
|
173
|
+
id: skill.id,
|
|
174
|
+
displayName: skill.displayName || skill.id,
|
|
175
|
+
namespace: skill.source,
|
|
176
|
+
version: String(skill.version || "1.0.0"),
|
|
177
|
+
digest,
|
|
178
|
+
category: skill.category || "uncategorized",
|
|
179
|
+
contextCostTokens: Math.ceil(
|
|
180
|
+
`${skill.id}\n${skill.description || ""}\n${skill.category || ""}`
|
|
181
|
+
.length / 4,
|
|
182
|
+
),
|
|
183
|
+
score,
|
|
184
|
+
scores: {
|
|
185
|
+
lexical: lexicalScore,
|
|
186
|
+
vector: vectorScore,
|
|
187
|
+
outcome: outcomeScore,
|
|
188
|
+
},
|
|
189
|
+
outcome,
|
|
190
|
+
reason: `bm25=${lexicalScore.toFixed(3)}, vector=${vectorScore.toFixed(3)}, outcome=${outcomeScore.toFixed(3)}`,
|
|
191
|
+
};
|
|
192
|
+
})
|
|
193
|
+
.filter(({ scores }) => scores.lexical > 0 || scores.vector > 0)
|
|
194
|
+
.sort(
|
|
195
|
+
(left, right) =>
|
|
196
|
+
right.score - left.score || left.digest.localeCompare(right.digest),
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const conflicts = [];
|
|
200
|
+
const byName = new Map();
|
|
201
|
+
for (const candidate of candidates) {
|
|
202
|
+
const key = candidate.id.toLowerCase();
|
|
203
|
+
const prior = byName.get(key);
|
|
204
|
+
if (prior && prior.digest !== candidate.digest) {
|
|
205
|
+
conflicts.push({
|
|
206
|
+
type: "same-name-different-version",
|
|
207
|
+
name: candidate.id,
|
|
208
|
+
digests: [prior.digest, candidate.digest].sort(),
|
|
209
|
+
});
|
|
210
|
+
} else byName.set(key, candidate);
|
|
211
|
+
}
|
|
212
|
+
const returned = candidates.slice(0, topK);
|
|
213
|
+
const ambiguous =
|
|
214
|
+
returned.length > 1 &&
|
|
215
|
+
returned[0].score - returned[1].score < ambiguityMargin;
|
|
216
|
+
if (ambiguous) {
|
|
217
|
+
conflicts.push({
|
|
218
|
+
type: "ambiguous-top-score",
|
|
219
|
+
digests: [returned[0].digest, returned[1].digest],
|
|
220
|
+
margin: returned[0].score - returned[1].score,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
const topConflict = conflicts.some(({ digests }) =>
|
|
224
|
+
digests?.includes(returned[0]?.digest),
|
|
225
|
+
);
|
|
226
|
+
return Object.freeze({
|
|
227
|
+
schema: SKILL_RETRIEVAL_RESULT_SCHEMA,
|
|
228
|
+
query: query.trim(),
|
|
229
|
+
selected: returned.length > 0 && !topConflict ? returned[0] : null,
|
|
230
|
+
candidates: Object.freeze(returned.map(Object.freeze)),
|
|
231
|
+
conflicts: Object.freeze(conflicts.map(Object.freeze)),
|
|
232
|
+
rejected: Object.freeze(rejected.map(Object.freeze)),
|
|
233
|
+
vectorAvailable,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { types as utilTypes } from "node:util";
|
|
3
|
+
|
|
4
|
+
export const SKILL_VECTOR_REQUEST_SCHEMA =
|
|
5
|
+
"chainlesschain.skill-vector-request/v1";
|
|
6
|
+
export const SKILL_VECTOR_RESULT_SCHEMA =
|
|
7
|
+
"chainlesschain.skill-vector-result/v1";
|
|
8
|
+
export const SKILL_VECTOR_AUTHORITY_SCHEMA =
|
|
9
|
+
"chainlesschain.skill-vector-authority/v1";
|
|
10
|
+
export const SKILL_VECTOR_ATTESTATION_SCHEMA =
|
|
11
|
+
"chainlesschain.skill-vector-attestation/v1";
|
|
12
|
+
|
|
13
|
+
const AUTHORITIES = new WeakSet();
|
|
14
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
15
|
+
const SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,255}$/u;
|
|
16
|
+
const MAX_SKILLS = 10_000;
|
|
17
|
+
const MAX_TEXT_BYTES = 8 * 1024 * 1024;
|
|
18
|
+
const RESULT_KEYS = new Set([
|
|
19
|
+
"attestation",
|
|
20
|
+
"corpusDigest",
|
|
21
|
+
"indexDigest",
|
|
22
|
+
"modelId",
|
|
23
|
+
"modelRevision",
|
|
24
|
+
"requestDigest",
|
|
25
|
+
"resultDigest",
|
|
26
|
+
"schema",
|
|
27
|
+
"scores",
|
|
28
|
+
"tenantId",
|
|
29
|
+
]);
|
|
30
|
+
const SCORE_KEYS = new Set(["digest", "score"]);
|
|
31
|
+
const ATTESTATION_KEYS = new Set(["algorithm", "keyId", "schema", "value"]);
|
|
32
|
+
const VERIFICATION_KEYS = new Set([
|
|
33
|
+
"authenticated",
|
|
34
|
+
"durable",
|
|
35
|
+
"receiptDigest",
|
|
36
|
+
"requestDigest",
|
|
37
|
+
"resultDigest",
|
|
38
|
+
"tenantId",
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
function canonical(value) {
|
|
42
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
43
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
44
|
+
return `{${Object.keys(value)
|
|
45
|
+
.sort()
|
|
46
|
+
.map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
|
|
47
|
+
.join(",")}}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function hash(domain, value) {
|
|
51
|
+
return `sha256:${createHash("sha256")
|
|
52
|
+
.update(domain)
|
|
53
|
+
.update("\0")
|
|
54
|
+
.update(canonical(value))
|
|
55
|
+
.digest("hex")}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function exact(value, keys, label) {
|
|
59
|
+
if (
|
|
60
|
+
!value ||
|
|
61
|
+
typeof value !== "object" ||
|
|
62
|
+
Array.isArray(value) ||
|
|
63
|
+
utilTypes.isProxy(value) ||
|
|
64
|
+
Reflect.ownKeys(value).length !== keys.size ||
|
|
65
|
+
Reflect.ownKeys(value).some(
|
|
66
|
+
(key) => typeof key !== "string" || !keys.has(key),
|
|
67
|
+
)
|
|
68
|
+
) {
|
|
69
|
+
throw new TypeError(`${label} is invalid`);
|
|
70
|
+
}
|
|
71
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
72
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
73
|
+
if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) {
|
|
74
|
+
throw new TypeError(`${label}.${String(key)} is invalid`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function identifier(value, label) {
|
|
81
|
+
if (typeof value !== "string" || !SAFE_ID.test(value)) {
|
|
82
|
+
throw new TypeError(`${label} is invalid`);
|
|
83
|
+
}
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function boundedText(value, label, maximum = 4096) {
|
|
88
|
+
if (
|
|
89
|
+
typeof value !== "string" ||
|
|
90
|
+
value.trim() === "" ||
|
|
91
|
+
value.length > maximum
|
|
92
|
+
) {
|
|
93
|
+
throw new TypeError(`${label} is invalid or unbounded`);
|
|
94
|
+
}
|
|
95
|
+
return value.trim();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function capture(owner, method, label) {
|
|
99
|
+
if (!owner || typeof owner !== "object" || utilTypes.isProxy(owner)) {
|
|
100
|
+
throw new TypeError(`${label}.${method}() is required`);
|
|
101
|
+
}
|
|
102
|
+
const operation = owner[method];
|
|
103
|
+
if (typeof operation !== "function" || utilTypes.isProxy(operation)) {
|
|
104
|
+
throw new TypeError(`${label}.${method}() is required`);
|
|
105
|
+
}
|
|
106
|
+
return Object.freeze((...args) => Reflect.apply(operation, owner, args));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function corpusFor(skills) {
|
|
110
|
+
if (
|
|
111
|
+
!Array.isArray(skills) ||
|
|
112
|
+
utilTypes.isProxy(skills) ||
|
|
113
|
+
skills.length < 1 ||
|
|
114
|
+
skills.length > MAX_SKILLS
|
|
115
|
+
) {
|
|
116
|
+
throw new TypeError("Skill vector corpus is invalid or unbounded");
|
|
117
|
+
}
|
|
118
|
+
let bytes = 0;
|
|
119
|
+
const digests = new Set();
|
|
120
|
+
const corpus = skills.map((skill, index) => {
|
|
121
|
+
if (!skill || typeof skill !== "object" || utilTypes.isProxy(skill)) {
|
|
122
|
+
throw new TypeError(`Skill vector corpus[${index}] is invalid`);
|
|
123
|
+
}
|
|
124
|
+
const digest = skill.executionIdentity?.contentDigest;
|
|
125
|
+
if (!DIGEST.test(digest ?? "") || digests.has(digest)) {
|
|
126
|
+
throw new TypeError(
|
|
127
|
+
"Skill vector corpus has a missing or duplicate digest",
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
digests.add(digest);
|
|
131
|
+
const entry = {
|
|
132
|
+
digest,
|
|
133
|
+
id: boundedText(skill.id, `skills[${index}].id`, 256),
|
|
134
|
+
displayName: boundedText(
|
|
135
|
+
skill.displayName || skill.id,
|
|
136
|
+
`skills[${index}].displayName`,
|
|
137
|
+
512,
|
|
138
|
+
),
|
|
139
|
+
description: boundedText(
|
|
140
|
+
skill.description || "(no description)",
|
|
141
|
+
`skills[${index}].description`,
|
|
142
|
+
16_384,
|
|
143
|
+
),
|
|
144
|
+
category: boundedText(
|
|
145
|
+
skill.category || "uncategorized",
|
|
146
|
+
`skills[${index}].category`,
|
|
147
|
+
128,
|
|
148
|
+
),
|
|
149
|
+
tags: Array.isArray(skill.tags)
|
|
150
|
+
? skill.tags.map((tag, tagIndex) =>
|
|
151
|
+
boundedText(tag, `skills[${index}].tags[${tagIndex}]`, 128),
|
|
152
|
+
)
|
|
153
|
+
: [],
|
|
154
|
+
};
|
|
155
|
+
if (
|
|
156
|
+
entry.tags.length > 64 ||
|
|
157
|
+
new Set(entry.tags).size !== entry.tags.length
|
|
158
|
+
) {
|
|
159
|
+
throw new TypeError("Skill vector corpus tags are invalid or unbounded");
|
|
160
|
+
}
|
|
161
|
+
bytes += Buffer.byteLength(canonical(entry), "utf8");
|
|
162
|
+
if (bytes > MAX_TEXT_BYTES) {
|
|
163
|
+
throw new TypeError("Skill vector corpus is invalid or unbounded");
|
|
164
|
+
}
|
|
165
|
+
return Object.freeze({ ...entry, tags: Object.freeze(entry.tags) });
|
|
166
|
+
});
|
|
167
|
+
corpus.sort((left, right) => left.digest.localeCompare(right.digest));
|
|
168
|
+
return Object.freeze(corpus);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function digestSkillVectorResult(value) {
|
|
172
|
+
return hash(SKILL_VECTOR_RESULT_SCHEMA, {
|
|
173
|
+
schema: value.schema,
|
|
174
|
+
tenantId: value.tenantId,
|
|
175
|
+
requestDigest: value.requestDigest,
|
|
176
|
+
corpusDigest: value.corpusDigest,
|
|
177
|
+
modelId: value.modelId,
|
|
178
|
+
modelRevision: value.modelRevision,
|
|
179
|
+
indexDigest: value.indexDigest,
|
|
180
|
+
scores: value.scores,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function createSkillVectorAuthority({
|
|
185
|
+
tenantId: tenantIdInput,
|
|
186
|
+
provider,
|
|
187
|
+
verifier,
|
|
188
|
+
} = {}) {
|
|
189
|
+
const tenantId = identifier(tenantIdInput, "tenantId");
|
|
190
|
+
const scoreProvider = capture(provider, "score", "Skill vector provider");
|
|
191
|
+
const verifyResult = capture(verifier, "verify", "Skill vector verifier");
|
|
192
|
+
const authority = Object.freeze({
|
|
193
|
+
tenantId,
|
|
194
|
+
async score({ query: queryInput, skills } = {}) {
|
|
195
|
+
const query = boundedText(queryInput, "Skill vector query");
|
|
196
|
+
const corpus = corpusFor(skills);
|
|
197
|
+
const corpusDigest = hash(
|
|
198
|
+
"chainlesschain.skill-vector-corpus/v1",
|
|
199
|
+
corpus,
|
|
200
|
+
);
|
|
201
|
+
const requestCore = {
|
|
202
|
+
schema: SKILL_VECTOR_REQUEST_SCHEMA,
|
|
203
|
+
tenantId,
|
|
204
|
+
query,
|
|
205
|
+
corpusDigest,
|
|
206
|
+
corpus,
|
|
207
|
+
};
|
|
208
|
+
const request = Object.freeze({
|
|
209
|
+
...requestCore,
|
|
210
|
+
requestDigest: hash(SKILL_VECTOR_REQUEST_SCHEMA, requestCore),
|
|
211
|
+
});
|
|
212
|
+
const result = exact(
|
|
213
|
+
await scoreProvider(request),
|
|
214
|
+
RESULT_KEYS,
|
|
215
|
+
"Skill vector result",
|
|
216
|
+
);
|
|
217
|
+
if (
|
|
218
|
+
result.schema !== SKILL_VECTOR_RESULT_SCHEMA ||
|
|
219
|
+
result.tenantId !== tenantId ||
|
|
220
|
+
result.requestDigest !== request.requestDigest ||
|
|
221
|
+
result.corpusDigest !== corpusDigest ||
|
|
222
|
+
!SAFE_ID.test(result.modelId ?? "") ||
|
|
223
|
+
!SAFE_ID.test(result.modelRevision ?? "") ||
|
|
224
|
+
!DIGEST.test(result.indexDigest ?? "") ||
|
|
225
|
+
!DIGEST.test(result.resultDigest ?? "") ||
|
|
226
|
+
!Array.isArray(result.scores) ||
|
|
227
|
+
utilTypes.isProxy(result.scores) ||
|
|
228
|
+
result.scores.length !== corpus.length
|
|
229
|
+
) {
|
|
230
|
+
throw new Error("Skill vector result is not authoritative");
|
|
231
|
+
}
|
|
232
|
+
const expected = new Set(corpus.map(({ digest }) => digest));
|
|
233
|
+
const scores = new Map();
|
|
234
|
+
for (const [index, input] of result.scores.entries()) {
|
|
235
|
+
const entry = exact(input, SCORE_KEYS, `Skill vector scores[${index}]`);
|
|
236
|
+
if (
|
|
237
|
+
!expected.has(entry.digest) ||
|
|
238
|
+
scores.has(entry.digest) ||
|
|
239
|
+
typeof entry.score !== "number" ||
|
|
240
|
+
!Number.isFinite(entry.score) ||
|
|
241
|
+
entry.score < 0 ||
|
|
242
|
+
entry.score > 1
|
|
243
|
+
) {
|
|
244
|
+
throw new Error("Skill vector scores are invalid or incomplete");
|
|
245
|
+
}
|
|
246
|
+
scores.set(entry.digest, entry.score);
|
|
247
|
+
}
|
|
248
|
+
if (scores.size !== expected.size) {
|
|
249
|
+
throw new Error("Skill vector scores are invalid or incomplete");
|
|
250
|
+
}
|
|
251
|
+
exact(result.attestation, ATTESTATION_KEYS, "Skill vector attestation");
|
|
252
|
+
if (
|
|
253
|
+
result.attestation.schema !== SKILL_VECTOR_ATTESTATION_SCHEMA ||
|
|
254
|
+
!SAFE_ID.test(result.attestation.algorithm ?? "") ||
|
|
255
|
+
!SAFE_ID.test(result.attestation.keyId ?? "") ||
|
|
256
|
+
typeof result.attestation.value !== "string" ||
|
|
257
|
+
result.attestation.value.length < 16 ||
|
|
258
|
+
result.attestation.value.length > 4096
|
|
259
|
+
) {
|
|
260
|
+
throw new Error("Skill vector attestation is invalid");
|
|
261
|
+
}
|
|
262
|
+
if (result.resultDigest !== digestSkillVectorResult(result)) {
|
|
263
|
+
throw new Error("Skill vector result integrity is invalid");
|
|
264
|
+
}
|
|
265
|
+
const verification = exact(
|
|
266
|
+
await verifyResult({
|
|
267
|
+
tenantId,
|
|
268
|
+
requestDigest: request.requestDigest,
|
|
269
|
+
resultDigest: result.resultDigest,
|
|
270
|
+
attestation: result.attestation,
|
|
271
|
+
}),
|
|
272
|
+
VERIFICATION_KEYS,
|
|
273
|
+
"Skill vector verification",
|
|
274
|
+
);
|
|
275
|
+
if (
|
|
276
|
+
verification.authenticated !== true ||
|
|
277
|
+
verification.durable !== true ||
|
|
278
|
+
verification.tenantId !== tenantId ||
|
|
279
|
+
verification.requestDigest !== request.requestDigest ||
|
|
280
|
+
verification.resultDigest !== result.resultDigest ||
|
|
281
|
+
!DIGEST.test(verification.receiptDigest ?? "")
|
|
282
|
+
) {
|
|
283
|
+
throw new Error("Skill vector result is not independently verified");
|
|
284
|
+
}
|
|
285
|
+
return Object.freeze({
|
|
286
|
+
scores: Object.freeze(Object.fromEntries(scores)),
|
|
287
|
+
evidence: Object.freeze({
|
|
288
|
+
schema: SKILL_VECTOR_AUTHORITY_SCHEMA,
|
|
289
|
+
status: "verified",
|
|
290
|
+
tenantId,
|
|
291
|
+
requestDigest: request.requestDigest,
|
|
292
|
+
corpusDigest,
|
|
293
|
+
skillCount: corpus.length,
|
|
294
|
+
modelId: result.modelId,
|
|
295
|
+
modelRevision: result.modelRevision,
|
|
296
|
+
indexDigest: result.indexDigest,
|
|
297
|
+
resultDigest: result.resultDigest,
|
|
298
|
+
receiptDigest: verification.receiptDigest,
|
|
299
|
+
}),
|
|
300
|
+
});
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
AUTHORITIES.add(authority);
|
|
304
|
+
return authority;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export function captureSkillVectorAuthority(value) {
|
|
308
|
+
if (!AUTHORITIES.has(value)) {
|
|
309
|
+
throw new TypeError("a branded Skill vector authority is required");
|
|
310
|
+
}
|
|
311
|
+
return value;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function unavailableSkillVectorEvidence(
|
|
315
|
+
code = "CC_SKILL_VECTOR_AUTHORITY_UNCONFIGURED",
|
|
316
|
+
) {
|
|
317
|
+
if (!/^CC_SKILL_VECTOR_[A-Z0-9_]{1,96}$/u.test(code)) {
|
|
318
|
+
throw new TypeError("Skill vector unavailable code is invalid");
|
|
319
|
+
}
|
|
320
|
+
return Object.freeze({
|
|
321
|
+
schema: SKILL_VECTOR_AUTHORITY_SCHEMA,
|
|
322
|
+
status: "unavailable",
|
|
323
|
+
code,
|
|
324
|
+
});
|
|
325
|
+
}
|