silentlake 2026.3.24
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/CHANGELOG.md +4587 -0
- package/LICENSE +21 -0
- package/README.md +248 -0
- package/assets/avatar-placeholder.svg +19 -0
- package/assets/chrome-extension/icons/icon128.png +0 -0
- package/assets/chrome-extension/icons/icon16.png +0 -0
- package/assets/chrome-extension/icons/icon32.png +0 -0
- package/assets/chrome-extension/icons/icon48.png +0 -0
- package/assets/dmg-background-small.png +0 -0
- package/assets/dmg-background.png +0 -0
- package/assets/silentlake-banner.png +0 -0
- package/dist/APEv2Parser-BZv_dP9t.js +269 -0
- package/dist/APEv2Parser-CPzxFNBB.js +5 -0
- package/dist/AbstractID3Parser-mvDFcjYV.js +47 -0
- package/dist/AiffParser-BXQ9SRZk.js +145 -0
- package/dist/AsfParser-CmBDUlZE.js +631 -0
- package/dist/BasicParser-DhmXREDo.js +853 -0
- package/dist/DsdiffParser-CTKKGyZg.js +150 -0
- package/dist/DsfParser-Ds-YQe4Z.js +101 -0
- package/dist/FlacParser-B1XVPgXF.js +5 -0
- package/dist/FlacParser-DMPyL1y4.js +367 -0
- package/dist/ID3v1Parser-BICWWVDG.js +289 -0
- package/dist/ID3v2Parser-BmZHSUqs.js +650 -0
- package/dist/ID3v2Token-DeJf4tYQ.js +145 -0
- package/dist/MP4Parser-Cjf-Zs8T.js +1061 -0
- package/dist/MatroskaParser-DgBzBe8t.js +909 -0
- package/dist/MpegParser-D5swTpA1.js +744 -0
- package/dist/MusepackParser-D8EQXnpK.js +285 -0
- package/dist/OggParser-BcIYPHwP.js +390 -0
- package/dist/Util-D_zGsr97.js +170 -0
- package/dist/WavPackParser-hosU8gfo.js +166 -0
- package/dist/WaveParser-CA00FZrC.js +273 -0
- package/dist/abort-cutoff-CERmtgZI.js +56 -0
- package/dist/abort-cutoff.runtime-DZkGKKzv.js +61 -0
- package/dist/abort-signal-CsrBEr94.js +13 -0
- package/dist/account-helpers-D3c_eI7c.js +37 -0
- package/dist/account-helpers-ru3jdZSV.js +12 -0
- package/dist/account-id-DZnNZg8x.js +1 -0
- package/dist/account-id-ZCrgXl7Z.js +44 -0
- package/dist/account-lookup-nkoa-foB.js +10 -0
- package/dist/account-snapshot-fields-Cvq7803C.js +116 -0
- package/dist/account-summary-B5Xzvntm.js +36 -0
- package/dist/accounts-43SvCDEA.js +212 -0
- package/dist/accounts-CYgFhv2o.js +105 -0
- package/dist/accounts-ChlyF7cx.js +112 -0
- package/dist/ack-reactions-CNVwfOBj.js +43 -0
- package/dist/acp-cli-B4Rv7-xU.js +2033 -0
- package/dist/acp-runtime-BdLdT-QY.js +1 -0
- package/dist/actions.runtime-CqnQssoB.js +217 -0
- package/dist/actions.runtime-FLmCvVRd.js +236 -0
- package/dist/agent-CBOdzEvR.js +1 -0
- package/dist/agent-scope-BLhzf-o0.js +17 -0
- package/dist/agent-scope-DPP4Z_UU.js +193 -0
- package/dist/agents-D8pBK0II.js +855 -0
- package/dist/agents-Dihz1Ihx.js +323 -0
- package/dist/agents.config-C_lrnc9J.js +18 -0
- package/dist/agents.config-D1VqC78r.js +121 -0
- package/dist/allow-from-BPSBITdd.js +9 -0
- package/dist/allow-from-BwTLpvhp.js +20 -0
- package/dist/allow-from-C4iBpqFI.js +62 -0
- package/dist/allowlist-config-edit-CKbnMmwS.js +279 -0
- package/dist/allowlist-match-CYmPgg1K.js +63 -0
- package/dist/ansi-BEJF8NKS.js +54 -0
- package/dist/anthropic-vertex-provider-Dd5agCN9.js +60 -0
- package/dist/apply.runtime-DhKxNSJE.js +370 -0
- package/dist/apply.runtime-ghlh-P6X.js +211 -0
- package/dist/archive-Tr0wIUO-.js +532 -0
- package/dist/arg-split-Dtda0YDl.js +38 -0
- package/dist/artifacts-C_4LekPC.js +39 -0
- package/dist/audit-BOPSQQtd.js +54 -0
- package/dist/audit-C5kdrCi_.js +788 -0
- package/dist/audit-channel.allow-from.runtime-B7BHNblL.js +17 -0
- package/dist/audit-channel.collect.runtime-CjAbXFBV.js +521 -0
- package/dist/audit-channel.discord.runtime-BBY6S9lg.js +5 -0
- package/dist/audit-channel.telegram.runtime-SJnxOJH2.js +8 -0
- package/dist/audit-channel.zalouser.runtime-SGRWvHxT.js +5 -0
- package/dist/audit-extra.async-DY8v7LXH.js +817 -0
- package/dist/audit-fs-oDMUa5N_.js +375 -0
- package/dist/audit-membership-runtime-BPjFryEx.js +261 -0
- package/dist/audit.deep.runtime-WFf-TpsD.js +31 -0
- package/dist/audit.nondeep.runtime-B4BaEaRU.js +842 -0
- package/dist/audit.runtime-rFjCrods.js +74 -0
- package/dist/auth-O6LQFLHJ.js +416 -0
- package/dist/auth-choice-DIBaxmAQ.js +219 -0
- package/dist/auth-choice-PrbpIjyg.js +610 -0
- package/dist/auth-choice-legacy-Clyw2lVc.js +17 -0
- package/dist/auth-choice-options-ohUw8QR-.js +127 -0
- package/dist/auth-choice-prompt-Cm0s-9Du.js +215 -0
- package/dist/auth-choice-prompt-DI-Xl1Nv.js +36 -0
- package/dist/auth-choice-rKBOd02a.js +64 -0
- package/dist/auth-choice.apply-helpers-BibBSEl9.js +66 -0
- package/dist/auth-choice.plugin-providers.runtime-gqF9NO7_.js +219 -0
- package/dist/auth-health-TWboMYA5.js +166 -0
- package/dist/auth-mode-policy-DywddkT-.js +18 -0
- package/dist/auth-profiles-CWEIQV77.js +1047 -0
- package/dist/auth-profiles.runtime-B98lwopF.js +48 -0
- package/dist/avatar-policy-Ds9e6uHI.js +67 -0
- package/dist/axios-xDDnM0KG.js +12831 -0
- package/dist/backup-create-B6JAR6jJ.js +461 -0
- package/dist/banner-bez5CpOK.js +351 -0
- package/dist/base-session-key-Cf2rkwag.js +14 -0
- package/dist/bindings-BV4AtNSY.js +21 -0
- package/dist/bindings-kjwuC11Q.js +69 -0
- package/dist/bluebubbles-C1M3Geg0.js +87 -0
- package/dist/bluebubbles-DRW3JdOY.js +603 -0
- package/dist/bluebubbles-dEl4QpYz.js +37 -0
- package/dist/bonjour-discovery-C2oY96BG.js +376 -0
- package/dist/boolean-DKtCJu_W.js +29 -0
- package/dist/boolean-param-xAGXUSSN.js +13 -0
- package/dist/boundary-file-read-BP6VMpqH.js +106 -0
- package/dist/boundary-path-B3FFLYNx.js +557 -0
- package/dist/brave-CkimJe4j.js +405 -0
- package/dist/brew-DSwWqzLd.js +44 -0
- package/dist/browser-cli-D3kBUBNc.js +1502 -0
- package/dist/bundled/boot-md/handler.js +381 -0
- package/dist/bundled/bootstrap-extra-files/handler.js +56 -0
- package/dist/bundled/command-logger/handler.js +62 -0
- package/dist/bundled/session-memory/handler.js +401 -0
- package/dist/call-BDvaXe4i.js +44 -0
- package/dist/call-BmLt3xO1.js +639 -0
- package/dist/catalog-BwAYUfL7.js +240 -0
- package/dist/channel-BBCuV5OT.js +4945 -0
- package/dist/channel-BDBXuqeg.js +321 -0
- package/dist/channel-C8h1Irxm.js +1284 -0
- package/dist/channel-CbGpFzo4.js +1602 -0
- package/dist/channel-Ci3K8fI9.js +1006 -0
- package/dist/channel-DGT5N1v7.js +1077 -0
- package/dist/channel-account-context-Bwa-YH_o.js +104 -0
- package/dist/channel-actions-DU2CR3xW.js +37 -0
- package/dist/channel-activity-B8aReQoE.js +35 -0
- package/dist/channel-config-3Uv6ve2_.js +115 -0
- package/dist/channel-config-helpers-CieQWILI.js +377 -0
- package/dist/channel-config-schema-DEVsCZpj.js +1 -0
- package/dist/channel-feedback-G6zh8efr.js +245 -0
- package/dist/channel-inbound-DwzVf2PK.js +395 -0
- package/dist/channel-lifecycle-CpU1dRbh.js +354 -0
- package/dist/channel-options-DJaIP4Dv.js +38 -0
- package/dist/channel-pairing-D54mn51y.js +66 -0
- package/dist/channel-plugin-common-BhTxCE5t.js +1 -0
- package/dist/channel-plugin-ids-CFeS3qir.js +26 -0
- package/dist/channel-plugin-resolution-DUngfdFj.js +112 -0
- package/dist/channel-policy-C4GKHvhz.js +1 -0
- package/dist/channel-reply-pipeline-CPTuaW8n.js +15 -0
- package/dist/channel-send-result-By8EpCPw.js +40 -0
- package/dist/channel-setup-Ck35g7zI.js +49 -0
- package/dist/channel-shared-LkXtTPXk.js +308 -0
- package/dist/channel-summary-CdYLGMVt.js +137 -0
- package/dist/channel-summary-D33z52ft.js +41 -0
- package/dist/channel-targets-DfnKGXez.js +87 -0
- package/dist/channel.runtime-DAyBR2A5.js +324 -0
- package/dist/channel.runtime-DVq5tC2D.js +35 -0
- package/dist/channel.runtime-DaLTDGtF.js +288 -0
- package/dist/channel.runtime-FKfTev2g.js +512 -0
- package/dist/channel.runtime-owqedh1t.js +268 -0
- package/dist/channel.runtime-wOTeiifp.js +230 -0
- package/dist/channels-CIHgkPea.js +408 -0
- package/dist/channels-ZXK6Jiuk.js +1393 -0
- package/dist/channels-cli-Bj6qSlkE.js +412 -0
- package/dist/channels-status-issues-C_U44M8Y.js +16 -0
- package/dist/chat-type-C-n03mQY.js +10 -0
- package/dist/clack-prompter-DuzDnaLi.js +112 -0
- package/dist/clawbot-cli-YNPuwmTB.js +218 -0
- package/dist/cli/daemon-cli.js +88 -0
- package/dist/cli-CW46WAZn.js +254 -0
- package/dist/cli-name-Daok7A7-.js +25 -0
- package/dist/cli-runtime-aAVwbEYy.js +7 -0
- package/dist/cli-utils-Np4NAAtt.js +39 -0
- package/dist/command-format-CYK9XiUC.js +16 -0
- package/dist/command-format-g8YUHNir.js +2 -0
- package/dist/command-gating-BQXGSqc9.js +40 -0
- package/dist/command-options-5coRiipK.js +25 -0
- package/dist/command-poll-backoff-CpkSns-6.js +56 -0
- package/dist/command-poll-backoff.runtime-YT6EGcLN.js +7 -0
- package/dist/command-registry-BI2MOs89.js +242 -0
- package/dist/command-registry-BMsxnuoC.js +14 -0
- package/dist/command-secret-gateway-ChXyZwos.js +211 -0
- package/dist/command-secret-targets-COcwhn-D.js +88 -0
- package/dist/command-secret-targets-CQJT3viO.js +3 -0
- package/dist/commands-Bb9xUwz9.js +42 -0
- package/dist/commands-core-C1usZXC2.js +4923 -0
- package/dist/commands-core.runtime-OTZivlO2.js +232 -0
- package/dist/commands-registry-ChCep1KJ.js +295 -0
- package/dist/commands-registry.data-XyUTELK9.js +904 -0
- package/dist/commands-registry.runtime-m5WTxFtv.js +25 -0
- package/dist/commands-status.runtime-C8_hpNgj.js +211 -0
- package/dist/commands.runtime-BZPnQKcW.js +232 -0
- package/dist/common-CUBlLRXB.js +457 -0
- package/dist/compact.runtime-Djmzpbn6.js +216 -0
- package/dist/completion-cli-D8tLgE5W.js +445 -0
- package/dist/completion-cli-Dz89naVA.js +17 -0
- package/dist/config-6sZwvXJD.js +88 -0
- package/dist/config-B7tPwoHZ.js +38 -0
- package/dist/config-DdDLrP_v.js +273 -0
- package/dist/config-cli-C10R8azD.js +945 -0
- package/dist/config-guard-B1c73BYQ.js +126 -0
- package/dist/config-helpers-3u5wfLBu.js +117 -0
- package/dist/config-pn7LKJdW.js +23 -0
- package/dist/config-presence-BmUF_5K9.js +79 -0
- package/dist/config-regex-CvZFnWkO.js +39 -0
- package/dist/config-runtime-CstET7fq.js +142 -0
- package/dist/config-schema-5YkIW1xw.js +270 -0
- package/dist/config-schema-B1UGMwZ8.js +31 -0
- package/dist/config-schema-DzlnsY3D.js +33 -0
- package/dist/config-state-CE0CGjey.js +288 -0
- package/dist/config-validation-CkVqgkHr.js +272 -0
- package/dist/config-value-DgJrpclm.js +25 -0
- package/dist/configure-DMkp7Sr4.js +1126 -0
- package/dist/configure-DOrQthLy.js +344 -0
- package/dist/connection-auth-BSQJeDOU.js +30 -0
- package/dist/constants-C_Scc680.js +71 -0
- package/dist/control-ui-assets-DjqeIg6A.js +232 -0
- package/dist/control-ui-shared-DP000Pxd.js +29 -0
- package/dist/conversation-runtime-1O0Aaolb.js +1458 -0
- package/dist/core-C7aHA4Aq.js +187 -0
- package/dist/core-command-descriptors-DCUYAEZd.js +96 -0
- package/dist/credentials-BPwBlm1X.js +265 -0
- package/dist/cron-cli-N2Hw_02d.js +579 -0
- package/dist/daemon-cli-DgfaF9xx.js +354 -0
- package/dist/daemon-install-CbclJo5M.js +134 -0
- package/dist/daemon-install-plan.shared-DK6BHlWI.js +222 -0
- package/dist/daemon-runtime-CbClrCwc.js +12 -0
- package/dist/dangerous-config-flags-BJtLWIk7.js +15 -0
- package/dist/dangerous-name-matching-DZa_t0RM.js +44 -0
- package/dist/dangerous-tools-yGPDFTHh.js +27 -0
- package/dist/date-time-DCAyaBop.js +118 -0
- package/dist/dedupe-Cgnk5BbX.js +55 -0
- package/dist/defaults-CEdZhIIb.js +6 -0
- package/dist/delegate-D4ql5N70.js +43 -0
- package/dist/deliver-B004w1Mv.js +212 -0
- package/dist/deliver-runtime-IYvc0giI.js +211 -0
- package/dist/delivery-queue-B19wDCjT.js +3 -0
- package/dist/delivery-queue-DrrqB4Hi.js +299 -0
- package/dist/device-auth-GEXe9vqR.js +15 -0
- package/dist/device-bootstrap-CwwokLEY.js +96 -0
- package/dist/device-bootstrap-Dbhe6oe8.js +1 -0
- package/dist/device-metadata-normalization-BDSQ_eA7.js +21 -0
- package/dist/device-pairing-cFWbBray.js +553 -0
- package/dist/devices-cli-DzycjFzS.js +366 -0
- package/dist/diagnostic-DqJXx_4Q.js +310 -0
- package/dist/diagnostic-events-ktCoG8Br.js +48 -0
- package/dist/diagnostics-CMhyGsPu.js +33 -0
- package/dist/diagnostics-DpLHpQ9c.js +14 -0
- package/dist/directive-handling.fast-lane-toP_ri_H.js +273 -0
- package/dist/directive-handling.impl-BRARyrsT.js +638 -0
- package/dist/directive-handling.impl-CUB4MOnK.js +214 -0
- package/dist/directive-handling.levels-8vnMeuGX.js +2 -0
- package/dist/directive-handling.levels-CoruY1AA.js +13 -0
- package/dist/directive-handling.persist.runtime-78Du6PgL.js +170 -0
- package/dist/directive-handling.shared-DCGUCHjn.js +147 -0
- package/dist/directory-cli-DVsDcgIU.js +437 -0
- package/dist/directory-config-helpers-CURJ8mj7.js +129 -0
- package/dist/directory-runtime-DhC8QkMq.js +19 -0
- package/dist/directory.static-DQaG9ohH.js +44 -0
- package/dist/discord-CYj8s73O.js +214 -0
- package/dist/discord-L9zvSHVn.js +635 -0
- package/dist/discord-core-5tkl-BzP.js +1 -0
- package/dist/dm-policy-shared-6bCJzHOS.js +188 -0
- package/dist/dns-cli-BMvHy265.js +223 -0
- package/dist/docker-XFNiArwM.js +1254 -0
- package/dist/docs-cli-BTaH94wD.js +176 -0
- package/dist/doctor-completion-DKx5m2UC.js +90 -0
- package/dist/doctor-config-preflight-BzQgc3_t.js +40 -0
- package/dist/doctor-config-preflight-DxVCut8L.js +150 -0
- package/dist/doctor-state-migrations-CTF66iAy.js +732 -0
- package/dist/doctor-state-migrations-D0VP4dUh.js +212 -0
- package/dist/entry-status-B2OWAf0s.js +172 -0
- package/dist/entry.js +210 -0
- package/dist/env-BP70DGuy.js +30 -0
- package/dist/env-overrides-JneV60sd.js +434 -0
- package/dist/env-overrides.runtime-DLrwions.js +18 -0
- package/dist/env-substitution-D6t_sLS_.js +136 -0
- package/dist/errors-BxyFnvP3.js +58 -0
- package/dist/exec-Dmex2w_d.js +310 -0
- package/dist/exec-approvals-BJhuySBz.js +386 -0
- package/dist/exec-approvals-allowlist-B_wPddCb.js +384 -0
- package/dist/exec-approvals-cli-C2dwhSkX.js +427 -0
- package/dist/exec-safe-bin-runtime-policy-BZkObC8r.js +89 -0
- package/dist/exec-safety-CaaBy-Zw.js +24 -0
- package/dist/extension-shared-5txN7IXK.js +74 -0
- package/dist/extensionAPI.js +218 -0
- package/dist/extensions/amazon-bedrock/index.js +231 -0
- package/dist/extensions/anthropic/index.js +330 -0
- package/dist/extensions/bluebubbles/index.js +224 -0
- package/dist/extensions/bluebubbles/setup-entry.js +289 -0
- package/dist/extensions/brave/index.js +23 -0
- package/dist/extensions/byteplus/index.js +112 -0
- package/dist/extensions/chutes/index.js +221 -0
- package/dist/extensions/cloudflare-ai-gateway/index.js +218 -0
- package/dist/extensions/copilot-proxy/index.js +125 -0
- package/dist/extensions/device-pair/index.js +1040 -0
- package/dist/extensions/discord/index.js +215 -0
- package/dist/extensions/discord/setup-entry.js +215 -0
- package/dist/extensions/elevenlabs/index.js +223 -0
- package/dist/extensions/fal/index.js +112 -0
- package/dist/extensions/feishu/index.js +227 -0
- package/dist/extensions/feishu/setup-entry.js +112 -0
- package/dist/extensions/firecrawl/index.js +211 -0
- package/dist/extensions/github-copilot/index.js +490 -0
- package/dist/extensions/google/index.js +211 -0
- package/dist/extensions/huggingface/index.js +108 -0
- package/dist/extensions/imessage/index.js +223 -0
- package/dist/extensions/imessage/setup-entry.js +220 -0
- package/dist/extensions/irc/index.js +220 -0
- package/dist/extensions/irc/setup-entry.js +222 -0
- package/dist/extensions/kilocode/index.js +282 -0
- package/dist/extensions/kimi-coding/index.js +148 -0
- package/dist/extensions/line/index.js +57 -0
- package/dist/extensions/line/setup-entry.js +49 -0
- package/dist/extensions/llm-task/index.js +157 -0
- package/dist/extensions/lobster/index.js +261 -0
- package/dist/extensions/mattermost/index.js +220 -0
- package/dist/extensions/mattermost/setup-entry.js +222 -0
- package/dist/extensions/memory-core/index.js +36 -0
- package/dist/extensions/microsoft/index.js +223 -0
- package/dist/extensions/minimax/index.js +437 -0
- package/dist/extensions/mistral/index.js +149 -0
- package/dist/extensions/modelstudio/index.js +144 -0
- package/dist/extensions/moonshot/index.js +211 -0
- package/dist/extensions/nextcloud-talk/index.js +221 -0
- package/dist/extensions/nextcloud-talk/setup-entry.js +223 -0
- package/dist/extensions/nvidia/index.js +29 -0
- package/dist/extensions/ollama/index.js +118 -0
- package/dist/extensions/open-prose/index.js +10 -0
- package/dist/extensions/openai/index.js +677 -0
- package/dist/extensions/opencode/index.js +116 -0
- package/dist/extensions/opencode-go/index.js +114 -0
- package/dist/extensions/openrouter/index.js +398 -0
- package/dist/extensions/openshell/index.js +923 -0
- package/dist/extensions/perplexity/index.js +23 -0
- package/dist/extensions/phone-control/index.js +276 -0
- package/dist/extensions/qianfan/index.js +114 -0
- package/dist/extensions/qwen-portal-auth/index.js +350 -0
- package/dist/extensions/sglang/index.js +285 -0
- package/dist/extensions/signal/index.js +218 -0
- package/dist/extensions/signal/setup-entry.js +218 -0
- package/dist/extensions/slack/index.js +222 -0
- package/dist/extensions/slack/setup-entry.js +220 -0
- package/dist/extensions/synology-chat/index.js +56 -0
- package/dist/extensions/synology-chat/setup-entry.js +58 -0
- package/dist/extensions/synthetic/index.js +112 -0
- package/dist/extensions/talk-voice/index.js +197 -0
- package/dist/extensions/tavily/index.js +211 -0
- package/dist/extensions/telegram/index.js +221 -0
- package/dist/extensions/telegram/setup-entry.js +221 -0
- package/dist/extensions/thread-ownership/index.js +70 -0
- package/dist/extensions/together/index.js +113 -0
- package/dist/extensions/venice/index.js +132 -0
- package/dist/extensions/vercel-ai-gateway/index.js +86 -0
- package/dist/extensions/vllm/index.js +285 -0
- package/dist/extensions/voice-call/index.js +5715 -0
- package/dist/extensions/volcengine/index.js +112 -0
- package/dist/extensions/xai/index.js +211 -0
- package/dist/extensions/xiaomi/index.js +115 -0
- package/dist/extensions/zai/index.js +559 -0
- package/dist/extensions/zalo/index.js +225 -0
- package/dist/extensions/zalo/setup-entry.js +226 -0
- package/dist/external-content-BUdUOqkv.js +238 -0
- package/dist/feishu-CgbwAF0e.js +2664 -0
- package/dist/feishu-Dh5fEbh5.js +59127 -0
- package/dist/fetch-guard-DIyN1HW5.js +165 -0
- package/dist/fetch-timeout-C5xpMuGd.js +36 -0
- package/dist/file-identity-Cw0fQxYY.js +11 -0
- package/dist/file-lock-WbEmczmY.js +107 -0
- package/dist/filter-oMGaNOM1.js +20 -0
- package/dist/format-DH8ysi7s.js +19 -0
- package/dist/format-datetime-BGS6tLDE.js +73 -0
- package/dist/format-duration-CO0BGWB0.js +57 -0
- package/dist/format-relative-C3nDxnXz.js +54 -0
- package/dist/frontmatter-S5vS-I4a.js +309 -0
- package/dist/fs-safe-D3qzH-ab.js +731 -0
- package/dist/gateway-cli-PQNp7o0j.js +28378 -0
- package/dist/gateway-install-token-DV5KjD4F.js +164 -0
- package/dist/gateway-rpc-C0Ey-rik.js +26 -0
- package/dist/gateway-runtime-ih2e7a2K.js +42 -0
- package/dist/gaxios-fetch-compat-KX6bsqFm.js +165 -0
- package/dist/gemini-auth-B5ljg7jr.js +29 -0
- package/dist/git-commit-BIdLubm5.js +2 -0
- package/dist/git-commit-OvUvjri2.js +177 -0
- package/dist/github-copilot-auth-Ccm-cBwy.js +104 -0
- package/dist/global-singleton-4KwY5RvX.js +13 -0
- package/dist/globals-41sdSaKv.js +38 -0
- package/dist/gmail-setup-utils-BX68dZla.js +419 -0
- package/dist/group-access-DJZrYPx1.js +113 -0
- package/dist/group-keys-BD_IYSMs.js +44 -0
- package/dist/group-policy-CWFxv3iB.js +201 -0
- package/dist/group-policy-warnings-Ddu6lBkh.js +175 -0
- package/dist/health-Bu1sbyYy.js +573 -0
- package/dist/health-Cbxc9Bn3.js +59 -0
- package/dist/health-format-B5XfOTuJ.js +26 -0
- package/dist/heartbeat-7aHh0m3d.js +169 -0
- package/dist/heartbeat-summary-Das49TYq.js +57 -0
- package/dist/help-CcbF7-ha.js +81 -0
- package/dist/help-format-Dv45FpYu.js +15 -0
- package/dist/helpers-DJ-5HEbE.js +24 -0
- package/dist/helpers-MxyaLZUk.js +32 -0
- package/dist/history-CHjo8B5W.js +102 -0
- package/dist/hook-runtime-BnNBi_q4.js +1 -0
- package/dist/hooks-cli-DUYK4RM1.js +1102 -0
- package/dist/hooks-policy-BL6HDLUn.js +20 -0
- package/dist/hooks-status-gzNmo3li.js +78 -0
- package/dist/host-env-security-BogNN146.js +223 -0
- package/dist/http-body-CCiSfloA.js +237 -0
- package/dist/http-registry-WFFbLYRd.js +153 -0
- package/dist/identity-DovQV4zD.js +112 -0
- package/dist/identity-cyBYcoXS.js +84 -0
- package/dist/identity-file-EndG1nfc.js +60 -0
- package/dist/image-generation-CNKc-mFK.js +441 -0
- package/dist/image-kJ7Tbov4.js +211 -0
- package/dist/image-ops-j01UkxEv.js +371 -0
- package/dist/imessage-B5pSMT47.js +219 -0
- package/dist/imessage-CoIuY1Ro.js +1451 -0
- package/dist/imessage-Cqjsq4VW.js +190 -0
- package/dist/imessage-core-CsYJuaRZ.js +1 -0
- package/dist/inbound-envelope-4P3IIJc3.js +61 -0
- package/dist/inbound-reply-dispatch-i2Vekqyy.js +72 -0
- package/dist/includes-7XyL3p1c.js +188 -0
- package/dist/includes-scan-y-rS6tTw.js +55 -0
- package/dist/index.js +57 -0
- package/dist/infra/warning-filter.js +2 -0
- package/dist/inspect-CcxlJ1ba.js +279 -0
- package/dist/install-safe-path-Rwbw1XCZ.js +62 -0
- package/dist/installs-iHi2aSjM.js +532 -0
- package/dist/interactive-F7iY0yED.js +8 -0
- package/dist/interactive-runtime-OweOj_Vv.js +90 -0
- package/dist/internal-hooks-0uipqzRY.js +156 -0
- package/dist/io-BX49DsSJ.js +35 -0
- package/dist/io-jOnQRia2.js +7178 -0
- package/dist/ip-C8vmzVu0.js +203 -0
- package/dist/ipv4-DAmsJVOV.js +82 -0
- package/dist/irc-AZ-Ec8be.js +12 -0
- package/dist/irc-CCSRuEC2.js +660 -0
- package/dist/is-main-YViS6wOn.js +27 -0
- package/dist/issue-format-CBEXVico.js +31 -0
- package/dist/issue-format-D3HehoKZ.js +4 -0
- package/dist/json-file-C2zjA0Gv.js +23 -0
- package/dist/json-files-WW-H_psG.js +60 -0
- package/dist/json-pointer-f9dEnBoR.js +43 -0
- package/dist/json-store-O1LwpnBH.js +37 -0
- package/dist/kb-cli-DMZs6PCu.js +65 -0
- package/dist/keyed-async-queue-CPUWV5Pm.js +32 -0
- package/dist/kill-tree-CbjXBw3z.js +149 -0
- package/dist/kilocode-shared-DS7_0IMs.js +29 -0
- package/dist/launchd-tyqGVx9U.js +491 -0
- package/dist/lazy-runtime-BcXbyAaC.js +1 -0
- package/dist/lazy-runtime-bWkd2cs3.js +29 -0
- package/dist/legacy-names-CUNZ4vHN.js +7 -0
- package/dist/legacy-web-search-BgZjNG2h.js +222 -0
- package/dist/lib-CERS7N4b.js +503 -0
- package/dist/lib-PPICrHv1.js +1938 -0
- package/dist/library-CZ461krl.js +211 -0
- package/dist/lifecycle-core-Dnxnw0oy.js +382 -0
- package/dist/line/accounts.js +10 -0
- package/dist/line/send.js +39 -0
- package/dist/line/template-messages.js +2 -0
- package/dist/line-CJSvwApm.js +1 -0
- package/dist/line-N9vL-2JB.js +688 -0
- package/dist/line-core-BOIxkjgu.js +1 -0
- package/dist/links-Bilm-v0z.js +13 -0
- package/dist/llm-slug-generator-BuAuQ5Ft.js +68 -0
- package/dist/llm-slug-generator.js +212 -0
- package/dist/llm-task-Dx8ymRFr.js +1 -0
- package/dist/local-roots-DAzCjWbC.js +34 -0
- package/dist/location-DefAH9WS.js +42 -0
- package/dist/logger-CoEtkjhn.js +550 -0
- package/dist/logger-Cqy7-Maj.js +70 -0
- package/dist/logging-B2wMcpWV.js +13 -0
- package/dist/logging-Bz1qZDPg.js +16 -0
- package/dist/logging-CArEWRgI.js +36 -0
- package/dist/logging-CbTTfADU.js +1 -0
- package/dist/logs-cli-BSjKwaur.js +261 -0
- package/dist/magic-string.es-DJPWMt-n.js +1011 -0
- package/dist/main-session-DKr0lBVk.js +36 -0
- package/dist/manager-ChTGDe87.js +2005 -0
- package/dist/manager-DuwFn87U.js +4226 -0
- package/dist/manager-runtime-E16jsvRe.js +59 -0
- package/dist/manager.runtime-F9F1eFiB.js +827 -0
- package/dist/manifest-registry-B90TyTWl.js +1350 -0
- package/dist/map-size-CMTQVKUV.js +15 -0
- package/dist/markdown-to-line-BWwaRx5F.js +640 -0
- package/dist/mask-api-key-CprzEe7l.js +10 -0
- package/dist/matrix-DzvdUw97.js +228 -0
- package/dist/matrix-migration-snapshot-adoDbNii.js +702 -0
- package/dist/mattermost-DO0BCfF3.js +1 -0
- package/dist/mattermost-SjOt4QDb.js +15 -0
- package/dist/mcp-cli-BC_VPl_o.js +94 -0
- package/dist/mcp-config-Coky4zS4.js +108 -0
- package/dist/media-limits-Cuvmmhop.js +14 -0
- package/dist/media-understanding-DD2uMjK8.js +48 -0
- package/dist/media-understanding.runtime-Kbb2bRmk.js +216 -0
- package/dist/memory-DBjQ0TPd.js +1 -0
- package/dist/memory-cli-Cm4Df0hJ.js +215 -0
- package/dist/memory-cli-yzqneSF8.js +541 -0
- package/dist/memory-search-Das1tiuB.js +204 -0
- package/dist/memory-search-DxmSTjHq.js +18 -0
- package/dist/mention-gating-B_q-EHFx.js +25 -0
- package/dist/mentions-Bxys_va0.js +154 -0
- package/dist/message-channel-Cy-gN4K2.js +106 -0
- package/dist/message-hook-mappers-BBTV3JRQ.js +249 -0
- package/dist/method-scopes-DgypDW23.js +2649 -0
- package/dist/mime-C4vVTBso.js +150 -0
- package/dist/minimal-C5yUxtHy.js +2120 -0
- package/dist/model-auth-B__TJTPw.js +309 -0
- package/dist/model-auth-env-CF9ts7Th.js +111 -0
- package/dist/model-catalog.runtime-CWh17vcc.js +211 -0
- package/dist/model-id-normalization-Y-MIsyK_.js +16 -0
- package/dist/model-input-BB2wSAHb.js +20 -0
- package/dist/model-overrides-sIzKU2wo.js +84 -0
- package/dist/model-param-b-DIFEhICm.js +15 -0
- package/dist/model-picker-CAPjetT3.js +400 -0
- package/dist/model-picker-DEw9viWc.js +215 -0
- package/dist/model-picker.runtime-ixYl7lB5.js +224 -0
- package/dist/model-selection-BTpJnslv.js +437 -0
- package/dist/model-selection-Ci9cPkL2.js +765 -0
- package/dist/model-suppression.runtime-D8cIb6Y5.js +216 -0
- package/dist/models-BQtc3khN.js +226 -0
- package/dist/models-CQgBV5dW.js +2536 -0
- package/dist/models-cli-DbQ-QpQk.js +418 -0
- package/dist/models-config-D2xK-G6c.js +211 -0
- package/dist/models-config.providers.discovery-BaIk1NKL.js +141 -0
- package/dist/models-config.runtime-Cf7q9uAQ.js +211 -0
- package/dist/monitor-B5QmKaD7.js +3272 -0
- package/dist/monitor-CL5OYLih.js +878 -0
- package/dist/monitor-CNZxrM4d.js +3145 -0
- package/dist/monitor-CyQVZdDh.js +223 -0
- package/dist/multimodal-DC43jYNv.js +75 -0
- package/dist/mutable-allowlist-detectors-C6EAzWYE.js +62 -0
- package/dist/net-DlJFp95v.js +270 -0
- package/dist/network-mode-DOgvmom4.js +17 -0
- package/dist/nextcloud-talk-ChMP88s-.js +12 -0
- package/dist/nextcloud-talk-CwnkUy8E.js +1 -0
- package/dist/node-cli-BZDC7rXg.js +2484 -0
- package/dist/node-command-policy-Bg2g6Xjp.js +192 -0
- package/dist/node-commands-B6W6Eo0b.js +11 -0
- package/dist/node-require-BgDD9bTi.js +14 -0
- package/dist/node-resolve-BunMro3f.js +69 -0
- package/dist/node-service-CEZZaqba.js +65 -0
- package/dist/node-startup-env-Gz8ZQniA.js +50 -0
- package/dist/nodes-cli-B4Jr9vct.js +1330 -0
- package/dist/nodes-screen-CQ7IvP62.js +401 -0
- package/dist/normalize-secret-input-_PgpexOG.js +32 -0
- package/dist/note-dfjacCV8.js +109 -0
- package/dist/npm-pack-install-CYNRv-vM.js +574 -0
- package/dist/npm-resolution-Ml2aA6Nu.js +60 -0
- package/dist/oauth.runtime-DA_48MPQ.js +687 -0
- package/dist/oauth.runtime-DS1ry5__.js +318 -0
- package/dist/oauth.runtime-qCkidk8J.js +180 -0
- package/dist/ollama-defaults-asNuGW4_.js +4 -0
- package/dist/onboard-BM6gO6Uw.js +589 -0
- package/dist/onboard-D9IU-7uw.js +48 -0
- package/dist/onboard-DQaHGPRm.js +25 -0
- package/dist/onboard-channels-BdQtLjYb.js +300 -0
- package/dist/onboard-channels-DIVUygs5.js +1257 -0
- package/dist/onboard-config-DFKb-0sE.js +29 -0
- package/dist/onboard-config-DYykzJhx.js +2 -0
- package/dist/onboard-custom-CDP4w1AT.js +216 -0
- package/dist/onboard-custom-DhJN13UV.js +644 -0
- package/dist/onboard-helpers-B7XTd4Pw.js +335 -0
- package/dist/onboard-helpers-BUKtx5Bq.js +54 -0
- package/dist/onboard-hooks-BHSSLAhI.js +73 -0
- package/dist/onboard-remote-BOzEPdHA.js +59 -0
- package/dist/onboard-remote-DVza19_k.js +182 -0
- package/dist/onboard-search-CrS-n9_3.js +446 -0
- package/dist/onboard-skills-BojzIPvk.js +133 -0
- package/dist/onboard-skills-D7HyCVjz.js +69 -0
- package/dist/openai-codex-provider.runtime-BFsopDHI.js +2 -0
- package/dist/openai-defaults-B7FUywsh.js +10 -0
- package/dist/openclaw-exec-env-AcZ9we1N.js +14 -0
- package/dist/openclaw-root-TUHYdr9B.js +88 -0
- package/dist/openclaw-tools.runtime-draZJo5r.js +211 -0
- package/dist/outbound-media-69yrWRDt.js +11 -0
- package/dist/outbound-runtime-ic_7ulJJ.js +1 -0
- package/dist/pairing-challenge-CNrPmmi9.js +48 -0
- package/dist/pairing-cli-BohXW2BK.js +150 -0
- package/dist/pairing-labels-CNKCSmBK.js +7 -0
- package/dist/pairing-message-CBv2njJT.js +4 -0
- package/dist/pairing-store-C4lsd4pO.js +590 -0
- package/dist/pairing-token-gKj4SNFJ.js +55 -0
- package/dist/parse-duration-BBGYkY0S.js +41 -0
- package/dist/parse-finite-number-CP4MQF_w.js +30 -0
- package/dist/parse-log-line-CVh9zu3Q.js +43 -0
- package/dist/parse-port-COyt3COn.js +8 -0
- package/dist/path-alias-guards-ZTKqurNH.js +40 -0
- package/dist/path-env-CPkz6U0Y.js +87 -0
- package/dist/paths-CTjJI9l0.js +179 -0
- package/dist/paths-GHJ97ebE.js +268 -0
- package/dist/paths-nCHyK08H.js +56 -0
- package/dist/perplexity-Beshd9zu.js +422 -0
- package/dist/persistent-dedupe-bjKjVI5u.js +116 -0
- package/dist/pi-embedded-CSQySvOV.js +168518 -0
- package/dist/pi-model-discovery-CuX5CDyZ.js +125 -0
- package/dist/pi-model-discovery-runtime-DNsMrX1n.js +44 -0
- package/dist/pi-tools.before-tool-call.runtime-DxVqzMVf.js +387 -0
- package/dist/platform-launcher-CqGy6UhP.js +83 -0
- package/dist/plugin-entry-CwuwM1jC.js +17 -0
- package/dist/plugin-install-JJwfOXtg.js +216 -0
- package/dist/plugin-install-plan-cixz1_W4.js +49 -0
- package/dist/plugin-install-vkpI1UNd.js +184 -0
- package/dist/plugin-registry-C3j_DUnj.js +51 -0
- package/dist/plugin-registry-DB_yxabS.js +213 -0
- package/dist/plugin-sdk/account-helpers.js +3 -0
- package/dist/plugin-sdk/account-id.js +2 -0
- package/dist/plugin-sdk/account-resolution.js +216 -0
- package/dist/plugin-sdk/acp-runtime.js +46 -0
- package/dist/plugin-sdk/agent-runtime.js +215 -0
- package/dist/plugin-sdk/allow-from.js +17 -0
- package/dist/plugin-sdk/allowlist-config-edit.js +2 -0
- package/dist/plugin-sdk/bluebubbles.js +232 -0
- package/dist/plugin-sdk/boolean-param.js +2 -0
- package/dist/plugin-sdk/channel-actions.js +16 -0
- package/dist/plugin-sdk/channel-config-helpers.js +15 -0
- package/dist/plugin-sdk/channel-config-schema.js +5 -0
- package/dist/plugin-sdk/channel-contract.js +1 -0
- package/dist/plugin-sdk/channel-feedback.js +4 -0
- package/dist/plugin-sdk/channel-inbound.js +53 -0
- package/dist/plugin-sdk/channel-lifecycle.js +2 -0
- package/dist/plugin-sdk/channel-pairing.js +2 -0
- package/dist/plugin-sdk/channel-policy.js +19 -0
- package/dist/plugin-sdk/channel-reply-pipeline.js +23 -0
- package/dist/plugin-sdk/channel-runtime.js +33 -0
- package/dist/plugin-sdk/channel-send-result.js +2 -0
- package/dist/plugin-sdk/channel-setup.js +24 -0
- package/dist/plugin-sdk/channel-targets.js +3 -0
- package/dist/plugin-sdk/cli-runtime.js +5 -0
- package/dist/plugin-sdk/command-auth.js +212 -0
- package/dist/plugin-sdk/compat.js +59 -0
- package/dist/plugin-sdk/config-runtime.js +51 -0
- package/dist/plugin-sdk/conversation-runtime.js +55 -0
- package/dist/plugin-sdk/core.js +40 -0
- package/dist/plugin-sdk/device-bootstrap.js +6 -0
- package/dist/plugin-sdk/diagnostics-otel.js +7 -0
- package/dist/plugin-sdk/diffs.js +3 -0
- package/dist/plugin-sdk/directory-runtime.js +5 -0
- package/dist/plugin-sdk/discord-core.js +26 -0
- package/dist/plugin-sdk/discord.js +219 -0
- package/dist/plugin-sdk/extension-shared.js +15 -0
- package/dist/plugin-sdk/feishu.js +101 -0
- package/dist/plugin-sdk/gateway-runtime.js +46 -0
- package/dist/plugin-sdk/googlechat.js +93 -0
- package/dist/plugin-sdk/group-access.js +2 -0
- package/dist/plugin-sdk/hook-runtime.js +12 -0
- package/dist/plugin-sdk/image-generation.js +51 -0
- package/dist/plugin-sdk/imessage-core.js +214 -0
- package/dist/plugin-sdk/imessage.js +223 -0
- package/dist/plugin-sdk/index.js +55 -0
- package/dist/plugin-sdk/infra-runtime.js +223 -0
- package/dist/plugin-sdk/interactive-runtime.js +3 -0
- package/dist/plugin-sdk/irc.js +232 -0
- package/dist/plugin-sdk/json-store.js +9 -0
- package/dist/plugin-sdk/keyed-async-queue.js +2 -0
- package/dist/plugin-sdk/lazy-runtime.js +2 -0
- package/dist/plugin-sdk/line-core.js +29 -0
- package/dist/plugin-sdk/line.js +22 -0
- package/dist/plugin-sdk/llm-task.js +7 -0
- package/dist/plugin-sdk/matrix-runtime-heavy.js +223 -0
- package/dist/plugin-sdk/matrix-runtime-shared.js +2 -0
- package/dist/plugin-sdk/matrix.js +84 -0
- package/dist/plugin-sdk/mattermost.js +233 -0
- package/dist/plugin-sdk/media-runtime.js +212 -0
- package/dist/plugin-sdk/media-understanding-runtime.js +211 -0
- package/dist/plugin-sdk/media-understanding.js +15 -0
- package/dist/plugin-sdk/memory-core.js +2 -0
- package/dist/plugin-sdk/memory-lancedb.js +2 -0
- package/dist/plugin-sdk/msteams.js +244 -0
- package/dist/plugin-sdk/nextcloud-talk.js +231 -0
- package/dist/plugin-sdk/nostr.js +50 -0
- package/dist/plugin-sdk/ollama-setup.js +33 -0
- package/dist/plugin-sdk/outbound-runtime.js +23 -0
- package/dist/plugin-sdk/plugin-entry.js +3 -0
- package/dist/plugin-sdk/plugin-runtime.js +211 -0
- package/dist/plugin-sdk/process-runtime.js +12 -0
- package/dist/plugin-sdk/provider-auth-api-key.js +53 -0
- package/dist/plugin-sdk/provider-auth-login.js +2 -0
- package/dist/plugin-sdk/provider-auth.js +37 -0
- package/dist/plugin-sdk/provider-catalog.js +2 -0
- package/dist/plugin-sdk/provider-env-vars.js +2 -0
- package/dist/plugin-sdk/provider-google.js +3 -0
- package/dist/plugin-sdk/provider-models.js +27 -0
- package/dist/plugin-sdk/provider-onboard.js +20 -0
- package/dist/plugin-sdk/provider-setup.js +58 -0
- package/dist/plugin-sdk/provider-stream.js +211 -0
- package/dist/plugin-sdk/provider-usage.js +18 -0
- package/dist/plugin-sdk/provider-web-search.js +23 -0
- package/dist/plugin-sdk/provider-zai-endpoint.js +3 -0
- package/dist/plugin-sdk/reply-history.js +23 -0
- package/dist/plugin-sdk/reply-payload.js +2 -0
- package/dist/plugin-sdk/reply-runtime.js +211 -0
- package/dist/plugin-sdk/request-url.js +2 -0
- package/dist/plugin-sdk/routing.js +25 -0
- package/dist/plugin-sdk/runtime-env.js +15 -0
- package/dist/plugin-sdk/runtime-store.js +2 -0
- package/dist/plugin-sdk/runtime.js +15 -0
- package/dist/plugin-sdk/sandbox.js +66 -0
- package/dist/plugin-sdk/secret-input.js +3 -0
- package/dist/plugin-sdk/security-runtime.js +18 -0
- package/dist/plugin-sdk/self-hosted-provider-setup.js +33 -0
- package/dist/plugin-sdk/setup-adapter-runtime.js +2 -0
- package/dist/plugin-sdk/setup-runtime.js +17 -0
- package/dist/plugin-sdk/setup-tools.js +21 -0
- package/dist/plugin-sdk/setup.js +26 -0
- package/dist/plugin-sdk/signal.js +220 -0
- package/dist/plugin-sdk/slack-core.js +22 -0
- package/dist/plugin-sdk/slack.js +223 -0
- package/dist/plugin-sdk/speech-runtime.js +213 -0
- package/dist/plugin-sdk/speech.js +212 -0
- package/dist/plugin-sdk/ssrf-runtime.js +5 -0
- package/dist/plugin-sdk/state-paths.js +3 -0
- package/dist/plugin-sdk/status-helpers.js +9 -0
- package/dist/plugin-sdk/telegram-core.js +23 -0
- package/dist/plugin-sdk/telegram.js +224 -0
- package/dist/plugin-sdk/testing.js +13174 -0
- package/dist/plugin-sdk/text-runtime.js +45 -0
- package/dist/plugin-sdk/thread-bindings-runtime.js +2 -0
- package/dist/plugin-sdk/thread-ownership.js +2 -0
- package/dist/plugin-sdk/tlon.js +57 -0
- package/dist/plugin-sdk/tool-send.js +2 -0
- package/dist/plugin-sdk/twitch.js +46 -0
- package/dist/plugin-sdk/voice-call.js +213 -0
- package/dist/plugin-sdk/web-media.js +28 -0
- package/dist/plugin-sdk/webhook-ingress.js +7 -0
- package/dist/plugin-sdk/webhook-path.js +2 -0
- package/dist/plugin-sdk/whatsapp-core.js +219 -0
- package/dist/plugin-sdk/whatsapp-shared.js +18 -0
- package/dist/plugin-sdk/windows-spawn.js +2 -0
- package/dist/plugin-sdk/zalo.js +239 -0
- package/dist/plugin-sdk/zalouser.js +240 -0
- package/dist/plugins/build-smoke-entry.js +211 -0
- package/dist/plugins/runtime/index.js +229 -0
- package/dist/plugins-1Z50ecJ6.js +1 -0
- package/dist/plugins-C6fKmNuA.js +7 -0
- package/dist/plugins-cli-BkgQkGaU.js +1192 -0
- package/dist/policy-CpkbSAfm.js +60 -0
- package/dist/polls-B2VH7SN9.js +35 -0
- package/dist/ports-BjWuIIQw.js +262 -0
- package/dist/ports-DFiK_Jc-.js +385 -0
- package/dist/ports-lsof-DtJqhFOr.js +25 -0
- package/dist/ports-probe-BQqp8l8E.js +14 -0
- package/dist/preflight-audio.runtime-Fi9mofpp.js +216 -0
- package/dist/probe-BM9sbCgS.js +20 -0
- package/dist/probe-DLBOZftS.js +134 -0
- package/dist/probe-auth-Bjp3G4CI.js +48 -0
- package/dist/probe-auth-DMSPTRRk.js +45 -0
- package/dist/process-runtime-C7el-Ri4.js +1 -0
- package/dist/process-scoped-map-C4gOa-gv.js +61 -0
- package/dist/profile-utils-BcMYGFPT.js +15 -0
- package/dist/profiles-D17eMKQZ.js +683 -0
- package/dist/program-Ch-76sgl.js +155 -0
- package/dist/program-context-BMWNUfqL.js +10 -0
- package/dist/program-context-CD_RvRYh.js +2 -0
- package/dist/progress-D1r9bZU1.js +132 -0
- package/dist/prompt-select-styled-NUKYS9QR.js +4879 -0
- package/dist/prompt-style-BvciNCqy.js +7 -0
- package/dist/prompts-NtuylUyl.js +9 -0
- package/dist/prototype-keys-Cm_8mWvq.js +11 -0
- package/dist/provider-api-key-auth-BE0taXiB.js +108 -0
- package/dist/provider-api-key-auth.runtime-jDZZUAMX.js +34 -0
- package/dist/provider-auth-Bw8x1a3o.js +58 -0
- package/dist/provider-auth-api-key-BrQYvdxi.js +1 -0
- package/dist/provider-auth-choice-BYbPq0eC.js +128 -0
- package/dist/provider-auth-choice-helpers-Bj1GkOSn.js +48 -0
- package/dist/provider-auth-choice-preference-tKq5gaJL.js +192 -0
- package/dist/provider-auth-choice.runtime-DegPpvRJ.js +223 -0
- package/dist/provider-auth-choices-QSilukI1.js +58 -0
- package/dist/provider-auth-guidance-gninjjq8.js +34 -0
- package/dist/provider-auth-helpers-B0dS-1WK.js +86 -0
- package/dist/provider-auth-input-BftBdgvW.js +112 -0
- package/dist/provider-auth-login-D0n0lMuc.js +8 -0
- package/dist/provider-auth-login.runtime-LvuBkQrc.js +243 -0
- package/dist/provider-auth-mode-sTdccIKL.js +20 -0
- package/dist/provider-auth-ref-BS3gwrNr.js +168 -0
- package/dist/provider-auth-ref-BmEcEN7K.js +3 -0
- package/dist/provider-catalog--18-pW5t.js +11 -0
- package/dist/provider-catalog-2P2hel74.js +48 -0
- package/dist/provider-catalog-B0FqWSwe.js +48 -0
- package/dist/provider-catalog-BvORKzzD.js +91 -0
- package/dist/provider-catalog-C34j1_or.js +26 -0
- package/dist/provider-catalog-C5vmXjmb.js +11 -0
- package/dist/provider-catalog-CBufm2Dr.js +36 -0
- package/dist/provider-catalog-D7QvsUXS.js +12 -0
- package/dist/provider-catalog-DKy_dzQZ.js +41 -0
- package/dist/provider-env-vars-CsQlY7bF.js +110 -0
- package/dist/provider-id-BpXo5t6v.js +31 -0
- package/dist/provider-model-allowlist-4HSOnlX-.js +24 -0
- package/dist/provider-model-primary-NJ-xlhec.js +53 -0
- package/dist/provider-models-C2EjYMwW.js +2416 -0
- package/dist/provider-oauth-flow-BQN6F6EC.js +33 -0
- package/dist/provider-ollama-setup-DhQvDwAj.js +309 -0
- package/dist/provider-onboard-CjOfyeQG.js +1 -0
- package/dist/provider-onboarding-config-DOZ3pFA6.js +165 -0
- package/dist/provider-openai-codex-oauth-tls-Bo8U4D3E.js +101 -0
- package/dist/provider-runtime.runtime-DnP2jpoM.js +211 -0
- package/dist/provider-self-hosted-setup-CUrmsugW.js +182 -0
- package/dist/provider-usage-ClDVmkhl.js +633 -0
- package/dist/provider-usage-DIC6cn-3.js +211 -0
- package/dist/provider-web-search-NzK8ep1E.js +507 -0
- package/dist/provider-wizard-C6jCuyQe.js +236 -0
- package/dist/provider-zai-endpoint-DeDABzT4.js +106 -0
- package/dist/proxy-H5O2p6AP.js +121 -0
- package/dist/proxy-env-DG2u55RW.js +40 -0
- package/dist/push-apns-D4zD2tmP.js +1050 -0
- package/dist/pw-ai-BuPUVeUK.js +1876 -0
- package/dist/qmd-manager-BpygGMW9.js +1571 -0
- package/dist/qr-cli-DnWHXcxh.js +370 -0
- package/dist/qr-cli-yaZ0FZ6z.js +213 -0
- package/dist/query-expansion-Do45hILP.js +1114 -0
- package/dist/reactions-BcC_XZqD.js +281 -0
- package/dist/read-only-account-inspect-DPJzadPo.js +42 -0
- package/dist/read-only-account-inspect.discord.runtime-CW9DDKH8.js +216 -0
- package/dist/read-only-account-inspect.slack.runtime-BcXBPyh3.js +216 -0
- package/dist/read-only-account-inspect.telegram.runtime-Y7h0Jbdj.js +216 -0
- package/dist/redact-BDinS1q9.js +102 -0
- package/dist/redact-identifier-FUiWQxv5.js +13 -0
- package/dist/redact-snapshot-DBPmeYy2.js +2654 -0
- package/dist/ref-contract-CCBBbf1r.js +53 -0
- package/dist/register-CppP7Ddc.js +43 -0
- package/dist/register.agent-BOD5ROGQ.js +546 -0
- package/dist/register.backup-Y2VGqcRu.js +269 -0
- package/dist/register.configure-1qiTINph.js +354 -0
- package/dist/register.maintenance-shn-zigv.js +694 -0
- package/dist/register.message-mR4CLSoo.js +812 -0
- package/dist/register.onboard-CkDryVid.js +298 -0
- package/dist/register.setup-B0xW5olD.js +318 -0
- package/dist/register.status-health-sessions-CuhWc03j.js +604 -0
- package/dist/register.subclis-BazXM5TW.js +315 -0
- package/dist/register.subclis-C2d8UDhH.js +13 -0
- package/dist/registry-C3q59Qj0.js +55 -0
- package/dist/registry-CPsHw6xU.js +219 -0
- package/dist/registry-CxgtJ09C.js +28 -0
- package/dist/registry-rgYi7KoO.js +160 -0
- package/dist/repair-qXnOAvDy.js +105 -0
- package/dist/replies-EiwmmZ_W.js +122 -0
- package/dist/reply-history-CVCD5oE9.js +1 -0
- package/dist/reply-payload-DBGc074f.js +232 -0
- package/dist/report-cli-DB1jQx32.js +42 -0
- package/dist/request-url-BKfWAQx8.js +10 -0
- package/dist/resolve-Ckjd8TAk.js +14 -0
- package/dist/resolve-T2q_0ARF.js +619 -0
- package/dist/resolve-route-vEY3ONZ2.js +466 -0
- package/dist/resolve-utils-CbqJY2bs.js +102 -0
- package/dist/response-generator-VdoCcQ3y.js +153 -0
- package/dist/restart-stale-pids-CLGiqU2E.js +187 -0
- package/dist/retry-D15TD1S3.js +168 -0
- package/dist/root-help-B9Aou4ho.js +32 -0
- package/dist/routes-TpLEcKO8.js +7084 -0
- package/dist/routing-Y3m0o-kB.js +26 -0
- package/dist/rpc-C6MN-nVc.js +67 -0
- package/dist/run-command-DRKv5Lj6.js +32 -0
- package/dist/run-main-YZSMdx0B.js +424 -0
- package/dist/run-with-concurrency-BrSjWzpg.js +41 -0
- package/dist/runtime-B66W9flm.js +43 -0
- package/dist/runtime-C9VaVKYZ.js +2338 -0
- package/dist/runtime-CT2LIJZu.js +91 -0
- package/dist/runtime-CqDQ81eY.js +143 -0
- package/dist/runtime-CuvWMN7E.js +89 -0
- package/dist/runtime-D4_OpzA1.js +5 -0
- package/dist/runtime-DP-4DZja.js +5 -0
- package/dist/runtime-Dl17x_cV.js +1 -0
- package/dist/runtime-Z35JoYPC.js +30 -0
- package/dist/runtime-api-D79M0lQN.js +1 -0
- package/dist/runtime-api-y3zfnQGK.js +39 -0
- package/dist/runtime-discord-ops.runtime-Bg5h5v9-.js +234 -0
- package/dist/runtime-env-a_iwdJIv.js +1 -0
- package/dist/runtime-forwarders-DtMc8rBP.js +44 -0
- package/dist/runtime-group-policy-B7irU4eu.js +59 -0
- package/dist/runtime-guard-y62lPDGY.js +58 -0
- package/dist/runtime-parse-CeqXmZHJ.js +84 -0
- package/dist/runtime-paths-CstaCCMi.js +334 -0
- package/dist/runtime-slack-ops.runtime-BumgKDhS.js +226 -0
- package/dist/runtime-status-CgL02wYX.js +15 -0
- package/dist/runtime-store-Bt3Sdbrn.js +22 -0
- package/dist/runtime-telegram-ops.runtime-rSLQ3KrE.js +233 -0
- package/dist/runtime-whatsapp-boundary-xZem0NyQ.js +364 -0
- package/dist/safe-open-sync-Bt9R1Mnf.js +83 -0
- package/dist/safe-regex-tLlDZYfM.js +244 -0
- package/dist/safe-text-B_CQuica.js +16 -0
- package/dist/sandbox-CUUouiKs.js +2795 -0
- package/dist/sandbox-cli-BN8y0Get.js +499 -0
- package/dist/sandbox-paths-fqp_TZdO.js +144 -0
- package/dist/sandbox-qSs4h3sk.js +1 -0
- package/dist/sanitize-env-vars-vNSNqm0y.js +74 -0
- package/dist/scan-paths-BJmvUZ1E.js +28 -0
- package/dist/search-manager-DWhFgwyp.js +17 -0
- package/dist/search-manager-r8Cw4ZRv.js +392 -0
- package/dist/secret-equal-ObQfyZGa.js +9 -0
- package/dist/secret-file-Ch0yuOXR.js +11 -0
- package/dist/secret-file-DYJtH6kf.js +92 -0
- package/dist/secret-input-4REZ4sHo.js +35 -0
- package/dist/secrets-cli-D1df8b0o.js +2304 -0
- package/dist/secure-random-Cs8tw_HQ.js +10 -0
- package/dist/security-cli-V66ESmdT.js +676 -0
- package/dist/security-runtime-BuEhpJVE.js +23 -0
- package/dist/send-3tabvle6.js +100 -0
- package/dist/send-CC5J3tyW.js +1026 -0
- package/dist/send-deps-CrFMNvqO.js +19 -0
- package/dist/send-i2-mdtiE.js +250 -0
- package/dist/server-BTOjmlyi.js +116 -0
- package/dist/server-middleware-CCqKhKUb.js +106 -0
- package/dist/server-node-events-D6y22Tt8.js +611 -0
- package/dist/server-startup-matrix-migration-DHWSoS73.js +1595 -0
- package/dist/service-Bxc9uL2e.js +774 -0
- package/dist/service-CBLajPZL.js +21 -0
- package/dist/session-cost-usage-BmbaBvk4.js +212 -0
- package/dist/session-cost-usage-C30Jl2SI.js +615 -0
- package/dist/session-fork.runtime-BZfcC1Nc.js +51 -0
- package/dist/session-key-gFFk3uv9.js +216 -0
- package/dist/session-write-lock-DNKvpjKf.js +324 -0
- package/dist/sessions-BIH_j_XS.js +222 -0
- package/dist/sessions-D5dWcxC_.js +212 -0
- package/dist/sessions-DaSBVNwD.js +669 -0
- package/dist/setup-C2XF1YH3.js +397 -0
- package/dist/setup-CN-teRpz.js +8 -0
- package/dist/setup-adapter-runtime-Bjv2adwG.js +1 -0
- package/dist/setup-binary-BOJA7zdN.js +30 -0
- package/dist/setup-browser-BhNPCUtK.js +71 -0
- package/dist/setup-core-BsG09DZH.js +149 -0
- package/dist/setup-core-D-O1GQax.js +162 -0
- package/dist/setup-core-Dtm54Rcq.js +510 -0
- package/dist/setup-entry-B1mTa7bU.js +10 -0
- package/dist/setup-entry-CTMgw-K5.js +13 -0
- package/dist/setup-entry-Cmd_cufO.js +13 -0
- package/dist/setup-entry-CybgA3zP.js +12 -0
- package/dist/setup-entry-DED_hL6i.js +12 -0
- package/dist/setup-entry-WCq9VMWx.js +14 -0
- package/dist/setup-group-access-BtPApRvE.js +70 -0
- package/dist/setup-helpers-B62Ecg9r.js +362 -0
- package/dist/setup-surface-B7A7qowY.js +452 -0
- package/dist/setup-surface-BBYJVRXc.js +380 -0
- package/dist/setup-surface-CFUz_BJi.js +298 -0
- package/dist/setup-tools-BPiMjAN7.js +1 -0
- package/dist/setup-wizard-helpers-COZ1UAdX.js +770 -0
- package/dist/setup-wizard-proxy-Slwi-1gX.js +116 -0
- package/dist/setup.finalize-B8O01nge.js +633 -0
- package/dist/setup.gateway-config-BPDIFk__.js +288 -0
- package/dist/setup.secret-input-BL-bqJpt.js +25 -0
- package/dist/shared-AygSbeCK.js +50 -0
- package/dist/shared-BHqDLkMG.js +127 -0
- package/dist/shared-BPtG8PgB.js +70 -0
- package/dist/shared-BU0QgVMZ.js +36 -0
- package/dist/shared-Bzr2UyEm.js +351 -0
- package/dist/shared-C_XXbGIF.js +87 -0
- package/dist/shared-Diw3KzwZ.js +82 -0
- package/dist/shared-DngjQumT.js +196 -0
- package/dist/shared-DzH3zmAy.js +64 -0
- package/dist/shared-LeP8iUTz.js +54 -0
- package/dist/shell-argv-DWV43Vya.js +72 -0
- package/dist/shell-env-cD92jEyV.js +181 -0
- package/dist/signal-BQd9f9dF.js +315 -0
- package/dist/signal-Ca7y47bM.js +46 -0
- package/dist/signal-Did9U_fa.js +214 -0
- package/dist/signal-cli-install-DxoL8CgF.js +188 -0
- package/dist/skill-commands-CiSwTFBQ.js +652 -0
- package/dist/skill-commands.runtime-CEwlWT4j.js +34 -0
- package/dist/skill-scanner-DG7MT7pu.js +354 -0
- package/dist/skills-BC8GJ9Rp.js +22 -0
- package/dist/skills-CCgKs_NJ.js +863 -0
- package/dist/skills-cli-dhYXJCuL.js +339 -0
- package/dist/skills-install-DiriUXJd.js +763 -0
- package/dist/skills-status-BmQTn4jL.js +23 -0
- package/dist/skills-status-a9b899Y3.js +169 -0
- package/dist/slack-CXgv7nu7.js +730 -0
- package/dist/slack-CcSByPzI.js +217 -0
- package/dist/slack-CtcCh0Lj.js +24537 -0
- package/dist/slack-core-DcsbATUs.js +1 -0
- package/dist/slash-commands.runtime-kO8EUKYW.js +228 -0
- package/dist/slash-dispatch.runtime-Bu2yMeFy.js +238 -0
- package/dist/slash-skill-commands.runtime-wwX3tF84.js +216 -0
- package/dist/speech-bSreRuDH.js +1 -0
- package/dist/speech-runtime-y1FcnGVA.js +1 -0
- package/dist/src-CmXHIz5f.js +846 -0
- package/dist/ssh-config-ChqR6ijV.js +77 -0
- package/dist/ssh-tunnel-Cz51VBAt.js +159 -0
- package/dist/ssh-tunnel-DWze2IQS.js +16 -0
- package/dist/ssrf-Dk9XaoKN.js +220 -0
- package/dist/ssrf-policy-Dk6oMa20.js +69 -0
- package/dist/ssrf-runtime-C-mAQLVA.js +1 -0
- package/dist/stagger-DU7FjHYo.js +54 -0
- package/dist/state-paths-DJIGEFq_.js +1 -0
- package/dist/status-69r8-Zey.js +75 -0
- package/dist/status-BdLTvZOL.js +44 -0
- package/dist/status-Bt7DQmRI.js +1665 -0
- package/dist/status-CoUFSBgt.js +202 -0
- package/dist/status-DbI3Kbh5.js +235 -0
- package/dist/status-DeKlzu_o.js +212 -0
- package/dist/status-Haie42Fc.js +606 -0
- package/dist/status-helpers-Cda-rGLX.js +101 -0
- package/dist/status-json-DS1M_MWJ.js +322 -0
- package/dist/status.link-channel-Cb8bZ_Od.js +40 -0
- package/dist/status.scan.deps.runtime-7_6VUs50.js +77 -0
- package/dist/status.scan.runtime-DtR8BIE9.js +14 -0
- package/dist/status.summary-l_Bi1buR.js +600 -0
- package/dist/status.summary.runtime-Cng6MzRU.js +151 -0
- package/dist/status.update-DtbnnPKx.js +79 -0
- package/dist/store-CvL8MPei.js +1446 -0
- package/dist/store.runtime-hgnvmZgO.js +43 -0
- package/dist/string-normalization-CvzuCAZv.js +19 -0
- package/dist/string-sample-BOLqzr4Y.js +11 -0
- package/dist/subagent-orphan-recovery-si1z2iBu.js +407 -0
- package/dist/subagent-registry-runtime-CXUDI8gL.js +211 -0
- package/dist/subcli-descriptors-CY_nHzpZ.js +151 -0
- package/dist/subsystem-CUp-6QQf.js +421 -0
- package/dist/synology-chat-CdejNfs0.js +12 -0
- package/dist/system-cli-DkOaXHkQ.js +99 -0
- package/dist/system-events-mAu6Ap6K.js +75 -0
- package/dist/system-message-DA9eUYzB.js +16 -0
- package/dist/system-run-command-Cxq2F1MB.js +258 -0
- package/dist/systemd-CrxZBFae.js +557 -0
- package/dist/systemd-hints-y-zJ9aTm.js +315 -0
- package/dist/systemd-linger-BdklDcLg.js +16 -0
- package/dist/systemd-linger-DLrbG9_d.js +68 -0
- package/dist/table-DFMOhmNZ.js +305 -0
- package/dist/tailnet-ofqBrXzu.js +38 -0
- package/dist/tailscale-Cbsx-2HB.js +254 -0
- package/dist/target-errors-ksphhzJg.js +26 -0
- package/dist/target-registry-krAVlXi_.js +1321 -0
- package/dist/telegram/audit.js +2 -0
- package/dist/telegram/token.js +211 -0
- package/dist/telegram-BjDUP22F.js +10910 -0
- package/dist/telegram-DMiNSGAJ.js +575 -0
- package/dist/telegram-Dt11B3JL.js +218 -0
- package/dist/telegram-core-B4Jo-uko.js +1 -0
- package/dist/template-messages-kh7VfgOb.js +214 -0
- package/dist/text-chunking-CUf5WgqG.js +19 -0
- package/dist/text-format-sFXlJfHH.js +8 -0
- package/dist/text-runtime-C_Roi_Je.js +1418 -0
- package/dist/theme-B5HDbQfl.js +2 -0
- package/dist/theme-CdOoMzRk.js +34 -0
- package/dist/thinking-BBD_0HSp.js +68 -0
- package/dist/thinking.shared-CncvRHts.js +246 -0
- package/dist/thread-bindings-messages-Cdo8jSa9.js +229 -0
- package/dist/thread-bindings-policy-DMjOaNyR.js +119 -0
- package/dist/thread-bindings-runtime-Ckwk3Uuz.js +1 -0
- package/dist/threading-helpers-Cq55SUtb.js +14 -0
- package/dist/timeouts-BwR1sGom.js +72 -0
- package/dist/tmp-openclaw-dir-idKIOMmb.js +102 -0
- package/dist/token-Bgv8XEsC.js +50 -0
- package/dist/tool-catalog-BV6FcEWS.js +337 -0
- package/dist/tool-policy-match-CHqTCSdK.js +46 -0
- package/dist/tool-send-9LXKcrda.js +16 -0
- package/dist/topology-cli-BhUXVViF.js +43 -0
- package/dist/transcript-events-B1V6z5ct.js +29 -0
- package/dist/tui-DDJMGCFK.js +3838 -0
- package/dist/tui-cli-ByN-ZH6y.js +237 -0
- package/dist/typebox-D0SHDJST.js +175 -0
- package/dist/types-BCKGVVld.js +83 -0
- package/dist/types-CtpUGsDP.js +30 -0
- package/dist/types.secrets-BWSeXrF4.js +80 -0
- package/dist/types.tools-BBO8HCi6.js +22 -0
- package/dist/typing-DG_YqWJ7.js +224 -0
- package/dist/unhandled-rejections-CDJ8dOVP.js +170 -0
- package/dist/unhandled-rejections-O6cVOz2D.js +4 -0
- package/dist/update-Br8U-txJ.js +1039 -0
- package/dist/update-check-C3TeQaWg.js +464 -0
- package/dist/update-cli-XHfIntD0.js +1625 -0
- package/dist/update-offset-store-36vzzZXw.js +211 -0
- package/dist/upsert-with-lock-Bb96JHpb.js +34 -0
- package/dist/url-userinfo-Db63ng4y.js +14 -0
- package/dist/utils-Bxk6BLTg.js +236 -0
- package/dist/utils-vDeUf98G.js +7 -0
- package/dist/version-DCY9_obP.js +64 -0
- package/dist/version-DRF-wKTV.js +2 -0
- package/dist/voice-call-D4fgwZNO.js +1 -0
- package/dist/warning-filter-CgvLQB4Y.js +56 -0
- package/dist/web-media-BfBb8i48.js +1 -0
- package/dist/web-media-CtU6jM5V.js +498 -0
- package/dist/webhook-ingress-CupqYpKM.js +338 -0
- package/dist/webhook-memory-guards-BHrFZ4yq.js +129 -0
- package/dist/webhook-path-BGFZ55ML.js +22 -0
- package/dist/webhook-shared-Cvk3b0ac.js +349 -0
- package/dist/webhooks-cli-vOAoBF9b.js +357 -0
- package/dist/whatsapp-D5nD0rGG.js +58 -0
- package/dist/whatsapp-DXbWlm3A.js +82 -0
- package/dist/whatsapp-core-C2WGMsaY.js +89451 -0
- package/dist/whatsapp-heartbeat-CSWnPQ7q.js +84 -0
- package/dist/whatsapp-shared-BmHKqTtR.js +95 -0
- package/dist/widearea-dns-CXimgJzu.js +125 -0
- package/dist/windows-argv-IXrdWrJj.js +145 -0
- package/dist/windows-spawn-vMJGZo89.js +154 -0
- package/dist/with-timeout-2AKTISee.js +58 -0
- package/dist/workspace-BH7CXmrr.js +479 -0
- package/dist/workspace-dirs-_O4V3xCR.js +13 -0
- package/dist/workspace-v5XppK5M.js +302 -0
- package/dist/ws-By-QcLjg.js +11 -0
- package/dist/wsl-BV3Cb66X.js +57 -0
- package/dist/zalo-CHQzsLhE.js +301 -0
- package/dist/zalo-CcJ3J9f2.js +13 -0
- package/dist/zod-schema.agent-runtime-T_EC_6fg.js +600 -0
- package/dist/zod-schema.core-BdgRr-F1.js +545 -0
- package/dist/zod-schema.providers-core-Dgq7MTqU.js +1613 -0
- package/docs/.i18n/README.md +31 -0
- package/docs/.i18n/glossary.ja-JP.json +14 -0
- package/docs/.i18n/glossary.zh-CN.json +242 -0
- package/docs/.i18n/ja-JP.tm.jsonl +0 -0
- package/docs/assets/install-script.svg +1 -0
- package/docs/assets/macos-onboarding/01-macos-warning.jpeg +0 -0
- package/docs/assets/macos-onboarding/02-local-networks.jpeg +0 -0
- package/docs/assets/macos-onboarding/03-security-notice.png +0 -0
- package/docs/assets/macos-onboarding/04-choose-gateway.png +0 -0
- package/docs/assets/macos-onboarding/05-permissions.png +0 -0
- package/docs/assets/openclaw-logo-text-dark.png +0 -0
- package/docs/assets/openclaw-logo-text-dark.svg +418 -0
- package/docs/assets/openclaw-logo-text.png +0 -0
- package/docs/assets/openclaw-logo-text.svg +418 -0
- package/docs/assets/pixel-lobster.svg +60 -0
- package/docs/assets/showcase/agents-ui.jpg +0 -0
- package/docs/assets/showcase/bambu-cli.png +0 -0
- package/docs/assets/showcase/codexmonitor.png +0 -0
- package/docs/assets/showcase/gohome-grafana.png +0 -0
- package/docs/assets/showcase/ios-testflight.jpg +0 -0
- package/docs/assets/showcase/oura-health.png +0 -0
- package/docs/assets/showcase/padel-cli.svg +11 -0
- package/docs/assets/showcase/padel-screenshot.jpg +0 -0
- package/docs/assets/showcase/papla-tts.jpg +0 -0
- package/docs/assets/showcase/pr-review-telegram.jpg +0 -0
- package/docs/assets/showcase/roborock-screenshot.jpg +0 -0
- package/docs/assets/showcase/roborock-status.svg +13 -0
- package/docs/assets/showcase/roof-camera-sky.jpg +0 -0
- package/docs/assets/showcase/snag.png +0 -0
- package/docs/assets/showcase/tesco-shop.jpg +0 -0
- package/docs/assets/showcase/wienerlinien.png +0 -0
- package/docs/assets/showcase/wine-cellar-skill.jpg +0 -0
- package/docs/assets/showcase/winix-air-purifier.jpg +0 -0
- package/docs/assets/showcase/xuezh-pronunciation.jpeg +0 -0
- package/docs/assets/sponsors/blacksmith.svg +14 -0
- package/docs/assets/sponsors/convex.svg +16 -0
- package/docs/assets/sponsors/openai.svg +3 -0
- package/docs/assets/sponsors/vercel.svg +5 -0
- package/docs/auth-credential-semantics.md +53 -0
- package/docs/automation/auth-monitoring.md +44 -0
- package/docs/automation/cron-jobs.md +727 -0
- package/docs/automation/cron-vs-heartbeat.md +286 -0
- package/docs/automation/gmail-pubsub.md +256 -0
- package/docs/automation/hooks.md +1049 -0
- package/docs/automation/poll.md +86 -0
- package/docs/automation/standing-orders.md +251 -0
- package/docs/automation/troubleshooting.md +122 -0
- package/docs/automation/webhook.md +217 -0
- package/docs/brave-search.md +93 -0
- package/docs/channels/bluebubbles.md +347 -0
- package/docs/channels/broadcast-groups.md +442 -0
- package/docs/channels/channel-routing.md +139 -0
- package/docs/channels/discord.md +1229 -0
- package/docs/channels/feishu.md +747 -0
- package/docs/channels/googlechat.md +261 -0
- package/docs/channels/group-messages.md +84 -0
- package/docs/channels/groups.md +379 -0
- package/docs/channels/imessage.md +367 -0
- package/docs/channels/index.md +47 -0
- package/docs/channels/irc.md +242 -0
- package/docs/channels/line.md +194 -0
- package/docs/channels/location.md +56 -0
- package/docs/channels/matrix.md +677 -0
- package/docs/channels/mattermost.md +427 -0
- package/docs/channels/msteams.md +780 -0
- package/docs/channels/nextcloud-talk.md +138 -0
- package/docs/channels/nostr.md +242 -0
- package/docs/channels/pairing.md +114 -0
- package/docs/channels/signal.md +329 -0
- package/docs/channels/slack.md +603 -0
- package/docs/channels/synology-chat.md +132 -0
- package/docs/channels/telegram.md +987 -0
- package/docs/channels/tlon.md +276 -0
- package/docs/channels/troubleshooting.md +118 -0
- package/docs/channels/twitch.md +379 -0
- package/docs/channels/whatsapp.md +460 -0
- package/docs/channels/zalo.md +243 -0
- package/docs/channels/zalouser.md +181 -0
- package/docs/ci.md +55 -0
- package/docs/cli/acp.md +288 -0
- package/docs/cli/agent.md +29 -0
- package/docs/cli/agents.md +123 -0
- package/docs/cli/approvals.md +50 -0
- package/docs/cli/backup.md +76 -0
- package/docs/cli/browser.md +106 -0
- package/docs/cli/channels.md +102 -0
- package/docs/cli/clawbot.md +21 -0
- package/docs/cli/completion.md +35 -0
- package/docs/cli/config.md +295 -0
- package/docs/cli/configure.md +36 -0
- package/docs/cli/cron.md +77 -0
- package/docs/cli/daemon.md +53 -0
- package/docs/cli/dashboard.md +22 -0
- package/docs/cli/devices.md +139 -0
- package/docs/cli/directory.md +63 -0
- package/docs/cli/dns.md +23 -0
- package/docs/cli/docs.md +15 -0
- package/docs/cli/doctor.md +48 -0
- package/docs/cli/gateway.md +235 -0
- package/docs/cli/health.md +21 -0
- package/docs/cli/hooks.md +318 -0
- package/docs/cli/index.md +1147 -0
- package/docs/cli/logs.md +28 -0
- package/docs/cli/memory.md +66 -0
- package/docs/cli/message.md +278 -0
- package/docs/cli/models.md +81 -0
- package/docs/cli/node.md +127 -0
- package/docs/cli/nodes.md +75 -0
- package/docs/cli/onboard.md +157 -0
- package/docs/cli/pairing.md +32 -0
- package/docs/cli/plugins.md +186 -0
- package/docs/cli/qr.md +46 -0
- package/docs/cli/reset.md +20 -0
- package/docs/cli/sandbox.md +197 -0
- package/docs/cli/secrets.md +188 -0
- package/docs/cli/security.md +79 -0
- package/docs/cli/sessions.md +110 -0
- package/docs/cli/setup.md +29 -0
- package/docs/cli/skills.md +26 -0
- package/docs/cli/status.md +30 -0
- package/docs/cli/system.md +60 -0
- package/docs/cli/tui.md +30 -0
- package/docs/cli/uninstall.md +20 -0
- package/docs/cli/update.md +103 -0
- package/docs/cli/voicecall.md +34 -0
- package/docs/cli/webhooks.md +25 -0
- package/docs/concepts/agent-loop.md +148 -0
- package/docs/concepts/agent-workspace.md +236 -0
- package/docs/concepts/agent.md +122 -0
- package/docs/concepts/architecture.md +137 -0
- package/docs/concepts/compaction.md +123 -0
- package/docs/concepts/context-engine.md +268 -0
- package/docs/concepts/context.md +172 -0
- package/docs/concepts/delegate-architecture.md +296 -0
- package/docs/concepts/features.md +73 -0
- package/docs/concepts/markdown-formatting.md +130 -0
- package/docs/concepts/memory.md +108 -0
- package/docs/concepts/messages.md +154 -0
- package/docs/concepts/model-failover.md +152 -0
- package/docs/concepts/model-providers.md +607 -0
- package/docs/concepts/models.md +225 -0
- package/docs/concepts/multi-agent.md +552 -0
- package/docs/concepts/oauth.md +158 -0
- package/docs/concepts/presence.md +102 -0
- package/docs/concepts/queue.md +89 -0
- package/docs/concepts/retry.md +69 -0
- package/docs/concepts/session-pruning.md +121 -0
- package/docs/concepts/session-tool.md +242 -0
- package/docs/concepts/session.md +310 -0
- package/docs/concepts/streaming.md +155 -0
- package/docs/concepts/system-prompt.md +132 -0
- package/docs/concepts/timezone.md +91 -0
- package/docs/concepts/typebox.md +291 -0
- package/docs/concepts/typing-indicators.md +68 -0
- package/docs/concepts/usage-tracking.md +35 -0
- package/docs/date-time.md +128 -0
- package/docs/debug/node-issue.md +85 -0
- package/docs/diagnostics/flags.md +91 -0
- package/docs/docs.json +2061 -0
- package/docs/gateway/authentication.md +179 -0
- package/docs/gateway/background-process.md +97 -0
- package/docs/gateway/bonjour.md +177 -0
- package/docs/gateway/bridge-protocol.md +91 -0
- package/docs/gateway/cli-backends.md +225 -0
- package/docs/gateway/configuration-examples.md +651 -0
- package/docs/gateway/configuration-reference.md +3123 -0
- package/docs/gateway/configuration.md +633 -0
- package/docs/gateway/discovery.md +123 -0
- package/docs/gateway/doctor.md +362 -0
- package/docs/gateway/gateway-lock.md +34 -0
- package/docs/gateway/health.md +44 -0
- package/docs/gateway/heartbeat.md +393 -0
- package/docs/gateway/index.md +261 -0
- package/docs/gateway/local-models.md +152 -0
- package/docs/gateway/logging.md +113 -0
- package/docs/gateway/multiple-gateways.md +112 -0
- package/docs/gateway/network-model.md +22 -0
- package/docs/gateway/openai-http-api.md +132 -0
- package/docs/gateway/openresponses-http-api.md +295 -0
- package/docs/gateway/openshell.md +307 -0
- package/docs/gateway/pairing.md +99 -0
- package/docs/gateway/protocol.md +267 -0
- package/docs/gateway/remote-gateway-readme.md +158 -0
- package/docs/gateway/remote.md +153 -0
- package/docs/gateway/sandbox-vs-tool-policy-vs-elevated.md +134 -0
- package/docs/gateway/sandboxing.md +469 -0
- package/docs/gateway/secrets-plan-contract.md +116 -0
- package/docs/gateway/secrets.md +503 -0
- package/docs/gateway/security/index.md +1213 -0
- package/docs/gateway/tailscale.md +132 -0
- package/docs/gateway/tools-invoke-http-api.md +110 -0
- package/docs/gateway/troubleshooting.md +378 -0
- package/docs/gateway/trusted-proxy-auth.md +330 -0
- package/docs/help/debugging.md +168 -0
- package/docs/help/environment.md +163 -0
- package/docs/help/faq.md +2999 -0
- package/docs/help/index.md +28 -0
- package/docs/help/scripts.md +28 -0
- package/docs/help/testing.md +524 -0
- package/docs/help/troubleshooting.md +297 -0
- package/docs/images/configure-model-picker-unsearchable.png +0 -0
- package/docs/images/feishu-step2-create-app.png +0 -0
- package/docs/images/feishu-step3-credentials.png +0 -0
- package/docs/images/feishu-step4-permissions.png +0 -0
- package/docs/images/feishu-step5-bot-capability.png +0 -0
- package/docs/images/feishu-step6-event-subscription.png +0 -0
- package/docs/images/feishu-verification-token.png +0 -0
- package/docs/images/groups-flow.svg +52 -0
- package/docs/images/mobile-ui-screenshot.png +0 -0
- package/docs/index.md +196 -0
- package/docs/install/ansible.md +230 -0
- package/docs/install/azure.md +311 -0
- package/docs/install/bun.md +55 -0
- package/docs/install/development-channels.md +120 -0
- package/docs/install/digitalocean.md +129 -0
- package/docs/install/docker-vm-runtime.md +142 -0
- package/docs/install/docker.md +375 -0
- package/docs/install/exe-dev.md +126 -0
- package/docs/install/fly.md +501 -0
- package/docs/install/gcp.md +402 -0
- package/docs/install/hetzner.md +251 -0
- package/docs/install/index.md +183 -0
- package/docs/install/installer.md +415 -0
- package/docs/install/kubernetes.md +191 -0
- package/docs/install/macos-vm.md +281 -0
- package/docs/install/migrating-matrix.md +346 -0
- package/docs/install/migrating.md +110 -0
- package/docs/install/nix.md +89 -0
- package/docs/install/node.md +138 -0
- package/docs/install/northflank.mdx +54 -0
- package/docs/install/oracle.md +156 -0
- package/docs/install/podman.md +133 -0
- package/docs/install/railway.mdx +100 -0
- package/docs/install/raspberry-pi.md +159 -0
- package/docs/install/render.mdx +169 -0
- package/docs/install/uninstall.md +128 -0
- package/docs/install/updating.md +128 -0
- package/docs/ja-JP/index.md +186 -0
- package/docs/ja-JP/start/getting-started.md +125 -0
- package/docs/ja-JP/start/wizard.md +77 -0
- package/docs/logging.md +352 -0
- package/docs/nav-tabs-underline.js +100 -0
- package/docs/network.md +54 -0
- package/docs/nodes/audio.md +187 -0
- package/docs/nodes/camera.md +162 -0
- package/docs/nodes/images.md +72 -0
- package/docs/nodes/index.md +393 -0
- package/docs/nodes/location-command.md +98 -0
- package/docs/nodes/media-understanding.md +394 -0
- package/docs/nodes/talk.md +92 -0
- package/docs/nodes/troubleshooting.md +114 -0
- package/docs/nodes/voicewake.md +66 -0
- package/docs/perplexity.md +174 -0
- package/docs/pi-dev.md +80 -0
- package/docs/pi.md +567 -0
- package/docs/platforms/android.md +168 -0
- package/docs/platforms/digitalocean.md +266 -0
- package/docs/platforms/index.md +54 -0
- package/docs/platforms/ios.md +220 -0
- package/docs/platforms/linux.md +94 -0
- package/docs/platforms/mac/bundled-gateway.md +73 -0
- package/docs/platforms/mac/canvas.md +125 -0
- package/docs/platforms/mac/child-process.md +69 -0
- package/docs/platforms/mac/dev-setup.md +104 -0
- package/docs/platforms/mac/health.md +34 -0
- package/docs/platforms/mac/icon.md +31 -0
- package/docs/platforms/mac/logging.md +57 -0
- package/docs/platforms/mac/menu-bar.md +81 -0
- package/docs/platforms/mac/peekaboo.md +65 -0
- package/docs/platforms/mac/permissions.md +50 -0
- package/docs/platforms/mac/remote.md +84 -0
- package/docs/platforms/mac/signing.md +47 -0
- package/docs/platforms/mac/skills.md +33 -0
- package/docs/platforms/mac/voice-overlay.md +60 -0
- package/docs/platforms/mac/voicewake.md +67 -0
- package/docs/platforms/mac/webchat.md +43 -0
- package/docs/platforms/mac/xpc.md +61 -0
- package/docs/platforms/macos.md +226 -0
- package/docs/platforms/oracle.md +303 -0
- package/docs/platforms/raspberry-pi.md +412 -0
- package/docs/platforms/windows.md +241 -0
- package/docs/plugins/agent-tools.md +10 -0
- package/docs/plugins/architecture.md +1363 -0
- package/docs/plugins/building-extensions.md +10 -0
- package/docs/plugins/building-plugins.md +376 -0
- package/docs/plugins/bundles.md +181 -0
- package/docs/plugins/community.md +141 -0
- package/docs/plugins/manifest.md +145 -0
- package/docs/plugins/sdk-migration.md +169 -0
- package/docs/plugins/voice-call.md +380 -0
- package/docs/plugins/zalouser.md +77 -0
- package/docs/prose.md +134 -0
- package/docs/providers/anthropic.md +259 -0
- package/docs/providers/bedrock.md +176 -0
- package/docs/providers/claude-max-api-proxy.md +154 -0
- package/docs/providers/cloudflare-ai-gateway.md +71 -0
- package/docs/providers/deepgram.md +93 -0
- package/docs/providers/github-copilot.md +72 -0
- package/docs/providers/glm.md +43 -0
- package/docs/providers/google.md +78 -0
- package/docs/providers/groq.md +96 -0
- package/docs/providers/huggingface.md +209 -0
- package/docs/providers/index.md +69 -0
- package/docs/providers/kilocode.md +74 -0
- package/docs/providers/litellm.md +154 -0
- package/docs/providers/minimax.md +224 -0
- package/docs/providers/mistral.md +54 -0
- package/docs/providers/models.md +45 -0
- package/docs/providers/modelstudio.md +66 -0
- package/docs/providers/moonshot.md +175 -0
- package/docs/providers/nvidia.md +55 -0
- package/docs/providers/ollama.md +352 -0
- package/docs/providers/openai.md +303 -0
- package/docs/providers/opencode-go.md +45 -0
- package/docs/providers/opencode.md +64 -0
- package/docs/providers/openrouter.md +37 -0
- package/docs/providers/perplexity-provider.md +62 -0
- package/docs/providers/qianfan.md +38 -0
- package/docs/providers/qwen.md +53 -0
- package/docs/providers/sglang.md +104 -0
- package/docs/providers/synthetic.md +99 -0
- package/docs/providers/together.md +66 -0
- package/docs/providers/venice.md +282 -0
- package/docs/providers/vercel-ai-gateway.md +60 -0
- package/docs/providers/vllm.md +92 -0
- package/docs/providers/volcengine.md +74 -0
- package/docs/providers/xai.md +60 -0
- package/docs/providers/xiaomi.md +86 -0
- package/docs/providers/zai.md +46 -0
- package/docs/reference/AGENTS.default.md +126 -0
- package/docs/reference/RELEASING.md +42 -0
- package/docs/reference/api-usage-costs.md +144 -0
- package/docs/reference/credits.md +30 -0
- package/docs/reference/device-models.md +47 -0
- package/docs/reference/memory-config.md +711 -0
- package/docs/reference/prompt-caching.md +185 -0
- package/docs/reference/rpc.md +43 -0
- package/docs/reference/secretref-credential-surface.md +140 -0
- package/docs/reference/secretref-user-supplied-credentials-matrix.json +563 -0
- package/docs/reference/session-management-compaction.md +324 -0
- package/docs/reference/templates/AGENTS.dev.md +83 -0
- package/docs/reference/templates/AGENTS.md +219 -0
- package/docs/reference/templates/BOOT.md +11 -0
- package/docs/reference/templates/BOOTSTRAP.md +62 -0
- package/docs/reference/templates/HEARTBEAT.md +14 -0
- package/docs/reference/templates/IDENTITY.dev.md +47 -0
- package/docs/reference/templates/IDENTITY.md +29 -0
- package/docs/reference/templates/SOUL.dev.md +76 -0
- package/docs/reference/templates/SOUL.md +43 -0
- package/docs/reference/templates/TOOLS.dev.md +24 -0
- package/docs/reference/templates/TOOLS.md +47 -0
- package/docs/reference/templates/USER.dev.md +18 -0
- package/docs/reference/templates/USER.md +23 -0
- package/docs/reference/test.md +90 -0
- package/docs/reference/token-use.md +175 -0
- package/docs/reference/transcript-hygiene.md +151 -0
- package/docs/reference/wizard.md +235 -0
- package/docs/security/CONTRIBUTING-THREAT-MODEL.md +98 -0
- package/docs/security/THREAT-MODEL-ATLAS.md +611 -0
- package/docs/security/formal-verification.md +167 -0
- package/docs/start/bootstrapping.md +41 -0
- package/docs/start/docs-directory.md +66 -0
- package/docs/start/getting-started.md +116 -0
- package/docs/start/hubs.md +198 -0
- package/docs/start/lore.md +219 -0
- package/docs/start/onboarding-overview.md +67 -0
- package/docs/start/onboarding.md +91 -0
- package/docs/start/openclaw.md +216 -0
- package/docs/start/quickstart.md +22 -0
- package/docs/start/setup.md +164 -0
- package/docs/start/showcase.md +418 -0
- package/docs/start/wizard-cli-automation.md +215 -0
- package/docs/start/wizard-cli-reference.md +299 -0
- package/docs/start/wizard.md +125 -0
- package/docs/style.css +37 -0
- package/docs/tools/acp-agents.md +623 -0
- package/docs/tools/agent-send.md +100 -0
- package/docs/tools/apply-patch.md +51 -0
- package/docs/tools/brave-search.md +93 -0
- package/docs/tools/browser-linux-troubleshooting.md +138 -0
- package/docs/tools/browser-login.md +73 -0
- package/docs/tools/browser-wsl2-windows-remote-cdp-troubleshooting.md +211 -0
- package/docs/tools/browser.md +731 -0
- package/docs/tools/btw.md +142 -0
- package/docs/tools/capability-cookbook.md +119 -0
- package/docs/tools/clawhub.md +257 -0
- package/docs/tools/creating-skills.md +117 -0
- package/docs/tools/diffs.md +386 -0
- package/docs/tools/elevated.md +114 -0
- package/docs/tools/exec-approvals.md +400 -0
- package/docs/tools/exec.md +204 -0
- package/docs/tools/firecrawl.md +140 -0
- package/docs/tools/index.md +137 -0
- package/docs/tools/llm-task.md +119 -0
- package/docs/tools/lobster.md +340 -0
- package/docs/tools/loop-detection.md +100 -0
- package/docs/tools/multi-agent-sandbox-tools.md +364 -0
- package/docs/tools/pdf.md +156 -0
- package/docs/tools/perplexity-search.md +174 -0
- package/docs/tools/plugin.md +251 -0
- package/docs/tools/reactions.md +64 -0
- package/docs/tools/skills-config.md +86 -0
- package/docs/tools/skills.md +306 -0
- package/docs/tools/slash-commands.md +294 -0
- package/docs/tools/subagents.md +295 -0
- package/docs/tools/tavily.md +125 -0
- package/docs/tools/thinking.md +96 -0
- package/docs/tools/tts.md +406 -0
- package/docs/tools/web.md +516 -0
- package/docs/tts.md +406 -0
- package/docs/vps.md +112 -0
- package/docs/web/control-ui.md +275 -0
- package/docs/web/dashboard.md +54 -0
- package/docs/web/index.md +120 -0
- package/docs/web/tui.md +170 -0
- package/docs/web/webchat.md +61 -0
- package/docs/whatsapp-openclaw-ai-zh.jpg +0 -0
- package/docs/whatsapp-openclaw.jpg +0 -0
- package/docs/zh-CN/AGENTS.md +61 -0
- package/docs/zh-CN/automation/auth-monitoring.md +47 -0
- package/docs/zh-CN/automation/cron-jobs.md +435 -0
- package/docs/zh-CN/automation/cron-vs-heartbeat.md +286 -0
- package/docs/zh-CN/automation/gmail-pubsub.md +249 -0
- package/docs/zh-CN/automation/hooks.md +1051 -0
- package/docs/zh-CN/automation/poll.md +76 -0
- package/docs/zh-CN/automation/troubleshooting.md +8 -0
- package/docs/zh-CN/automation/webhook.md +163 -0
- package/docs/zh-CN/brave-search.md +60 -0
- package/docs/zh-CN/channels/bluebubbles.md +354 -0
- package/docs/zh-CN/channels/broadcast-groups.md +449 -0
- package/docs/zh-CN/channels/channel-routing.md +117 -0
- package/docs/zh-CN/channels/discord.md +468 -0
- package/docs/zh-CN/channels/feishu.md +728 -0
- package/docs/zh-CN/channels/googlechat.md +257 -0
- package/docs/zh-CN/channels/grammy.md +38 -0
- package/docs/zh-CN/channels/group-messages.md +91 -0
- package/docs/zh-CN/channels/groups.md +379 -0
- package/docs/zh-CN/channels/imessage.md +302 -0
- package/docs/zh-CN/channels/index.md +53 -0
- package/docs/zh-CN/channels/line.md +180 -0
- package/docs/zh-CN/channels/location.md +63 -0
- package/docs/zh-CN/channels/matrix.md +221 -0
- package/docs/zh-CN/channels/mattermost.md +144 -0
- package/docs/zh-CN/channels/msteams.md +775 -0
- package/docs/zh-CN/channels/nextcloud-talk.md +142 -0
- package/docs/zh-CN/channels/nostr.md +249 -0
- package/docs/zh-CN/channels/pairing.md +89 -0
- package/docs/zh-CN/channels/signal.md +209 -0
- package/docs/zh-CN/channels/slack.md +531 -0
- package/docs/zh-CN/channels/synology-chat.md +138 -0
- package/docs/zh-CN/channels/telegram.md +751 -0
- package/docs/zh-CN/channels/tlon.md +136 -0
- package/docs/zh-CN/channels/troubleshooting.md +36 -0
- package/docs/zh-CN/channels/twitch.md +385 -0
- package/docs/zh-CN/channels/whatsapp.md +411 -0
- package/docs/zh-CN/channels/zalo.md +196 -0
- package/docs/zh-CN/channels/zalouser.md +147 -0
- package/docs/zh-CN/cli/acp.md +173 -0
- package/docs/zh-CN/cli/agent.md +30 -0
- package/docs/zh-CN/cli/agents.md +82 -0
- package/docs/zh-CN/cli/approvals.md +57 -0
- package/docs/zh-CN/cli/browser.md +114 -0
- package/docs/zh-CN/cli/channels.md +86 -0
- package/docs/zh-CN/cli/config.md +57 -0
- package/docs/zh-CN/cli/configure.md +38 -0
- package/docs/zh-CN/cli/cron.md +43 -0
- package/docs/zh-CN/cli/dashboard.md +23 -0
- package/docs/zh-CN/cli/devices.md +74 -0
- package/docs/zh-CN/cli/directory.md +70 -0
- package/docs/zh-CN/cli/dns.md +30 -0
- package/docs/zh-CN/cli/docs.md +22 -0
- package/docs/zh-CN/cli/doctor.md +48 -0
- package/docs/zh-CN/cli/gateway.md +206 -0
- package/docs/zh-CN/cli/health.md +28 -0
- package/docs/zh-CN/cli/hooks.md +298 -0
- package/docs/zh-CN/cli/index.md +1143 -0
- package/docs/zh-CN/cli/logs.md +31 -0
- package/docs/zh-CN/cli/memory.md +52 -0
- package/docs/zh-CN/cli/message.md +246 -0
- package/docs/zh-CN/cli/models.md +85 -0
- package/docs/zh-CN/cli/node.md +115 -0
- package/docs/zh-CN/cli/nodes.md +80 -0
- package/docs/zh-CN/cli/onboard.md +164 -0
- package/docs/zh-CN/cli/pairing.md +28 -0
- package/docs/zh-CN/cli/plugins.md +66 -0
- package/docs/zh-CN/cli/reset.md +24 -0
- package/docs/zh-CN/cli/sandbox.md +158 -0
- package/docs/zh-CN/cli/security.md +33 -0
- package/docs/zh-CN/cli/sessions.md +23 -0
- package/docs/zh-CN/cli/setup.md +36 -0
- package/docs/zh-CN/cli/skills.md +33 -0
- package/docs/zh-CN/cli/status.md +33 -0
- package/docs/zh-CN/cli/system.md +63 -0
- package/docs/zh-CN/cli/tui.md +30 -0
- package/docs/zh-CN/cli/uninstall.md +24 -0
- package/docs/zh-CN/cli/update.md +101 -0
- package/docs/zh-CN/cli/voicecall.md +41 -0
- package/docs/zh-CN/cli/webhooks.md +32 -0
- package/docs/zh-CN/concepts/agent-loop.md +146 -0
- package/docs/zh-CN/concepts/agent-workspace.md +219 -0
- package/docs/zh-CN/concepts/agent.md +115 -0
- package/docs/zh-CN/concepts/architecture.md +123 -0
- package/docs/zh-CN/concepts/compaction.md +67 -0
- package/docs/zh-CN/concepts/context.md +168 -0
- package/docs/zh-CN/concepts/features.md +59 -0
- package/docs/zh-CN/concepts/markdown-formatting.md +117 -0
- package/docs/zh-CN/concepts/memory.md +412 -0
- package/docs/zh-CN/concepts/messages.md +141 -0
- package/docs/zh-CN/concepts/model-failover.md +145 -0
- package/docs/zh-CN/concepts/model-providers.md +606 -0
- package/docs/zh-CN/concepts/models.md +225 -0
- package/docs/zh-CN/concepts/multi-agent.md +372 -0
- package/docs/zh-CN/concepts/oauth.md +164 -0
- package/docs/zh-CN/concepts/presence.md +99 -0
- package/docs/zh-CN/concepts/queue.md +94 -0
- package/docs/zh-CN/concepts/retry.md +76 -0
- package/docs/zh-CN/concepts/session-pruning.md +129 -0
- package/docs/zh-CN/concepts/session-tool.md +200 -0
- package/docs/zh-CN/concepts/session.md +166 -0
- package/docs/zh-CN/concepts/streaming.md +133 -0
- package/docs/zh-CN/concepts/system-prompt.md +101 -0
- package/docs/zh-CN/concepts/timezone.md +96 -0
- package/docs/zh-CN/concepts/typebox.md +284 -0
- package/docs/zh-CN/concepts/typing-indicators.md +74 -0
- package/docs/zh-CN/concepts/usage-tracking.md +42 -0
- package/docs/zh-CN/date-time.md +129 -0
- package/docs/zh-CN/debug/node-issue.md +90 -0
- package/docs/zh-CN/diagnostics/flags.md +98 -0
- package/docs/zh-CN/gateway/authentication.md +184 -0
- package/docs/zh-CN/gateway/background-process.md +100 -0
- package/docs/zh-CN/gateway/bonjour.md +174 -0
- package/docs/zh-CN/gateway/bridge-protocol.md +86 -0
- package/docs/zh-CN/gateway/cli-backends.md +213 -0
- package/docs/zh-CN/gateway/configuration-examples.md +587 -0
- package/docs/zh-CN/gateway/configuration-reference.md +3103 -0
- package/docs/zh-CN/gateway/configuration.md +640 -0
- package/docs/zh-CN/gateway/discovery.md +123 -0
- package/docs/zh-CN/gateway/doctor.md +238 -0
- package/docs/zh-CN/gateway/gateway-lock.md +41 -0
- package/docs/zh-CN/gateway/health.md +42 -0
- package/docs/zh-CN/gateway/heartbeat.md +274 -0
- package/docs/zh-CN/gateway/index.md +335 -0
- package/docs/zh-CN/gateway/local-models.md +159 -0
- package/docs/zh-CN/gateway/logging.md +114 -0
- package/docs/zh-CN/gateway/multiple-gateways.md +119 -0
- package/docs/zh-CN/gateway/network-model.md +23 -0
- package/docs/zh-CN/gateway/openai-http-api.md +125 -0
- package/docs/zh-CN/gateway/openresponses-http-api.md +317 -0
- package/docs/zh-CN/gateway/pairing.md +99 -0
- package/docs/zh-CN/gateway/protocol.md +220 -0
- package/docs/zh-CN/gateway/remote-gateway-readme.md +164 -0
- package/docs/zh-CN/gateway/remote.md +133 -0
- package/docs/zh-CN/gateway/sandbox-vs-tool-policy-vs-elevated.md +135 -0
- package/docs/zh-CN/gateway/sandboxing.md +188 -0
- package/docs/zh-CN/gateway/security/index.md +777 -0
- package/docs/zh-CN/gateway/tailscale.md +124 -0
- package/docs/zh-CN/gateway/tools-invoke-http-api.md +92 -0
- package/docs/zh-CN/gateway/troubleshooting.md +771 -0
- package/docs/zh-CN/help/debugging.md +160 -0
- package/docs/zh-CN/help/environment.md +88 -0
- package/docs/zh-CN/help/faq.md +2640 -0
- package/docs/zh-CN/help/index.md +28 -0
- package/docs/zh-CN/help/scripts.md +35 -0
- package/docs/zh-CN/help/testing.md +375 -0
- package/docs/zh-CN/help/troubleshooting.md +104 -0
- package/docs/zh-CN/index.md +186 -0
- package/docs/zh-CN/install/ansible.md +215 -0
- package/docs/zh-CN/install/bun.md +65 -0
- package/docs/zh-CN/install/development-channels.md +81 -0
- package/docs/zh-CN/install/docker.md +532 -0
- package/docs/zh-CN/install/exe-dev.md +133 -0
- package/docs/zh-CN/install/fly.md +490 -0
- package/docs/zh-CN/install/gcp.md +510 -0
- package/docs/zh-CN/install/hetzner.md +337 -0
- package/docs/zh-CN/install/index.md +235 -0
- package/docs/zh-CN/install/installer.md +422 -0
- package/docs/zh-CN/install/macos-vm.md +288 -0
- package/docs/zh-CN/install/migrating.md +199 -0
- package/docs/zh-CN/install/nix.md +99 -0
- package/docs/zh-CN/install/node.md +8 -0
- package/docs/zh-CN/install/northflank.mdx +60 -0
- package/docs/zh-CN/install/railway.mdx +106 -0
- package/docs/zh-CN/install/render.mdx +169 -0
- package/docs/zh-CN/install/uninstall.md +135 -0
- package/docs/zh-CN/install/updating.md +233 -0
- package/docs/zh-CN/logging.md +329 -0
- package/docs/zh-CN/network.md +59 -0
- package/docs/zh-CN/nodes/audio.md +120 -0
- package/docs/zh-CN/nodes/camera.md +162 -0
- package/docs/zh-CN/nodes/images.md +79 -0
- package/docs/zh-CN/nodes/index.md +348 -0
- package/docs/zh-CN/nodes/location-command.md +120 -0
- package/docs/zh-CN/nodes/media-understanding.md +380 -0
- package/docs/zh-CN/nodes/talk.md +97 -0
- package/docs/zh-CN/nodes/troubleshooting.md +8 -0
- package/docs/zh-CN/nodes/voicewake.md +72 -0
- package/docs/zh-CN/perplexity.md +102 -0
- package/docs/zh-CN/pi-dev.md +77 -0
- package/docs/zh-CN/pi.md +619 -0
- package/docs/zh-CN/platforms/android.md +155 -0
- package/docs/zh-CN/platforms/digitalocean.md +273 -0
- package/docs/zh-CN/platforms/index.md +60 -0
- package/docs/zh-CN/platforms/ios.md +114 -0
- package/docs/zh-CN/platforms/linux.md +100 -0
- package/docs/zh-CN/platforms/mac/bundled-gateway.md +75 -0
- package/docs/zh-CN/platforms/mac/canvas.md +128 -0
- package/docs/zh-CN/platforms/mac/child-process.md +73 -0
- package/docs/zh-CN/platforms/mac/dev-setup.md +109 -0
- package/docs/zh-CN/platforms/mac/health.md +41 -0
- package/docs/zh-CN/platforms/mac/icon.md +38 -0
- package/docs/zh-CN/platforms/mac/logging.md +64 -0
- package/docs/zh-CN/platforms/mac/menu-bar.md +88 -0
- package/docs/zh-CN/platforms/mac/peekaboo.md +62 -0
- package/docs/zh-CN/platforms/mac/permissions.md +46 -0
- package/docs/zh-CN/platforms/mac/remote.md +90 -0
- package/docs/zh-CN/platforms/mac/signing.md +54 -0
- package/docs/zh-CN/platforms/mac/skills.md +40 -0
- package/docs/zh-CN/platforms/mac/voice-overlay.md +67 -0
- package/docs/zh-CN/platforms/mac/voicewake.md +74 -0
- package/docs/zh-CN/platforms/mac/webchat.md +43 -0
- package/docs/zh-CN/platforms/mac/xpc.md +68 -0
- package/docs/zh-CN/platforms/macos.md +193 -0
- package/docs/zh-CN/platforms/oracle.md +310 -0
- package/docs/zh-CN/platforms/raspberry-pi.md +416 -0
- package/docs/zh-CN/platforms/windows.md +247 -0
- package/docs/zh-CN/plugins/agent-tools.md +99 -0
- package/docs/zh-CN/plugins/manifest.md +68 -0
- package/docs/zh-CN/plugins/voice-call.md +250 -0
- package/docs/zh-CN/plugins/zalouser.md +88 -0
- package/docs/zh-CN/prose.md +141 -0
- package/docs/zh-CN/providers/anthropic.md +265 -0
- package/docs/zh-CN/providers/bedrock.md +170 -0
- package/docs/zh-CN/providers/claude-max-api-proxy.md +155 -0
- package/docs/zh-CN/providers/cloudflare-ai-gateway.md +78 -0
- package/docs/zh-CN/providers/deepgram.md +97 -0
- package/docs/zh-CN/providers/github-copilot.md +67 -0
- package/docs/zh-CN/providers/glm.md +50 -0
- package/docs/zh-CN/providers/huggingface.md +216 -0
- package/docs/zh-CN/providers/index.md +69 -0
- package/docs/zh-CN/providers/kilocode.md +80 -0
- package/docs/zh-CN/providers/litellm.md +160 -0
- package/docs/zh-CN/providers/minimax.md +222 -0
- package/docs/zh-CN/providers/mistral.md +61 -0
- package/docs/zh-CN/providers/models.md +51 -0
- package/docs/zh-CN/providers/moonshot.md +182 -0
- package/docs/zh-CN/providers/nvidia.md +62 -0
- package/docs/zh-CN/providers/ollama.md +359 -0
- package/docs/zh-CN/providers/openai.md +308 -0
- package/docs/zh-CN/providers/opencode-go.md +52 -0
- package/docs/zh-CN/providers/opencode.md +71 -0
- package/docs/zh-CN/providers/openrouter.md +44 -0
- package/docs/zh-CN/providers/qianfan.md +45 -0
- package/docs/zh-CN/providers/qwen.md +55 -0
- package/docs/zh-CN/providers/sglang.md +111 -0
- package/docs/zh-CN/providers/synthetic.md +106 -0
- package/docs/zh-CN/providers/together.md +72 -0
- package/docs/zh-CN/providers/venice.md +289 -0
- package/docs/zh-CN/providers/vercel-ai-gateway.md +66 -0
- package/docs/zh-CN/providers/xiaomi.md +93 -0
- package/docs/zh-CN/providers/zai.md +53 -0
- package/docs/zh-CN/reference/AGENTS.default.md +131 -0
- package/docs/zh-CN/reference/RELEASING.md +48 -0
- package/docs/zh-CN/reference/api-usage-costs.md +141 -0
- package/docs/zh-CN/reference/credits.md +34 -0
- package/docs/zh-CN/reference/device-models.md +54 -0
- package/docs/zh-CN/reference/rpc.md +48 -0
- package/docs/zh-CN/reference/session-management-compaction.md +287 -0
- package/docs/zh-CN/reference/templates/AGENTS.dev.md +89 -0
- package/docs/zh-CN/reference/templates/AGENTS.md +225 -0
- package/docs/zh-CN/reference/templates/BOOT.md +17 -0
- package/docs/zh-CN/reference/templates/BOOTSTRAP.md +68 -0
- package/docs/zh-CN/reference/templates/HEARTBEAT.md +18 -0
- package/docs/zh-CN/reference/templates/IDENTITY.dev.md +54 -0
- package/docs/zh-CN/reference/templates/IDENTITY.md +36 -0
- package/docs/zh-CN/reference/templates/SOUL.dev.md +83 -0
- package/docs/zh-CN/reference/templates/SOUL.md +49 -0
- package/docs/zh-CN/reference/templates/TOOLS.dev.md +31 -0
- package/docs/zh-CN/reference/templates/TOOLS.md +53 -0
- package/docs/zh-CN/reference/templates/USER.dev.md +25 -0
- package/docs/zh-CN/reference/templates/USER.md +30 -0
- package/docs/zh-CN/reference/test.md +57 -0
- package/docs/zh-CN/reference/token-use.md +119 -0
- package/docs/zh-CN/reference/transcript-hygiene.md +109 -0
- package/docs/zh-CN/reference/wizard.md +242 -0
- package/docs/zh-CN/security/formal-verification.md +171 -0
- package/docs/zh-CN/start/bootstrapping.md +9 -0
- package/docs/zh-CN/start/docs-directory.md +70 -0
- package/docs/zh-CN/start/getting-started.md +143 -0
- package/docs/zh-CN/start/hubs.md +194 -0
- package/docs/zh-CN/start/lore.md +226 -0
- package/docs/zh-CN/start/onboarding-overview.md +58 -0
- package/docs/zh-CN/start/onboarding.md +105 -0
- package/docs/zh-CN/start/openclaw.md +248 -0
- package/docs/zh-CN/start/quickstart.md +88 -0
- package/docs/zh-CN/start/setup.md +153 -0
- package/docs/zh-CN/start/showcase.md +423 -0
- package/docs/zh-CN/start/wizard-cli-automation.md +222 -0
- package/docs/zh-CN/start/wizard-cli-reference.md +306 -0
- package/docs/zh-CN/start/wizard.md +132 -0
- package/docs/zh-CN/tools/agent-send.md +59 -0
- package/docs/zh-CN/tools/apply-patch.md +57 -0
- package/docs/zh-CN/tools/browser-linux-troubleshooting.md +144 -0
- package/docs/zh-CN/tools/browser-login.md +75 -0
- package/docs/zh-CN/tools/browser.md +553 -0
- package/docs/zh-CN/tools/chrome-extension.md +183 -0
- package/docs/zh-CN/tools/clawhub.md +209 -0
- package/docs/zh-CN/tools/creating-skills.md +61 -0
- package/docs/zh-CN/tools/elevated.md +64 -0
- package/docs/zh-CN/tools/exec-approvals.md +234 -0
- package/docs/zh-CN/tools/exec.md +169 -0
- package/docs/zh-CN/tools/firecrawl.md +68 -0
- package/docs/zh-CN/tools/index.md +515 -0
- package/docs/zh-CN/tools/llm-task.md +117 -0
- package/docs/zh-CN/tools/lobster.md +349 -0
- package/docs/zh-CN/tools/multi-agent-sandbox-tools.md +401 -0
- package/docs/zh-CN/tools/plugin.md +1612 -0
- package/docs/zh-CN/tools/reactions.md +29 -0
- package/docs/zh-CN/tools/skills-config.md +78 -0
- package/docs/zh-CN/tools/skills.md +279 -0
- package/docs/zh-CN/tools/slash-commands.md +205 -0
- package/docs/zh-CN/tools/subagents.md +167 -0
- package/docs/zh-CN/tools/thinking.md +80 -0
- package/docs/zh-CN/tools/web.md +289 -0
- package/docs/zh-CN/tts.md +375 -0
- package/docs/zh-CN/vps.md +47 -0
- package/docs/zh-CN/web/control-ui.md +191 -0
- package/docs/zh-CN/web/dashboard.md +53 -0
- package/docs/zh-CN/web/index.md +118 -0
- package/docs/zh-CN/web/tui.md +166 -0
- package/docs/zh-CN/web/webchat.md +56 -0
- package/openclaw.mjs +135 -0
- package/package.json +835 -0
- package/skills/1password/SKILL.md +70 -0
- package/skills/1password/references/cli-examples.md +29 -0
- package/skills/1password/references/get-started.md +17 -0
- package/skills/apple-notes/SKILL.md +77 -0
- package/skills/apple-reminders/SKILL.md +118 -0
- package/skills/bear-notes/SKILL.md +107 -0
- package/skills/blogwatcher/SKILL.md +69 -0
- package/skills/blucli/SKILL.md +47 -0
- package/skills/bluebubbles/SKILL.md +131 -0
- package/skills/camsnap/SKILL.md +45 -0
- package/skills/canvas/SKILL.md +198 -0
- package/skills/clawhub/SKILL.md +77 -0
- package/skills/coding-agent/SKILL.md +295 -0
- package/skills/discord/SKILL.md +197 -0
- package/skills/doubao-code/SKILL.md +43 -0
- package/skills/eightctl/SKILL.md +50 -0
- package/skills/gemini/SKILL.md +43 -0
- package/skills/gh-issues/SKILL.md +865 -0
- package/skills/gifgrep/SKILL.md +79 -0
- package/skills/github/SKILL.md +163 -0
- package/skills/gog/SKILL.md +116 -0
- package/skills/goplaces/SKILL.md +52 -0
- package/skills/healthcheck/SKILL.md +245 -0
- package/skills/himalaya/SKILL.md +257 -0
- package/skills/himalaya/references/configuration.md +184 -0
- package/skills/himalaya/references/message-composition.md +199 -0
- package/skills/imsg/SKILL.md +122 -0
- package/skills/kimi-code/SKILL.md +42 -0
- package/skills/mcporter/SKILL.md +61 -0
- package/skills/model-usage/SKILL.md +69 -0
- package/skills/model-usage/references/codexbar-cli.md +33 -0
- package/skills/model-usage/scripts/model_usage.py +320 -0
- package/skills/model-usage/scripts/test_model_usage.py +40 -0
- package/skills/nano-pdf/SKILL.md +38 -0
- package/skills/node-connect/SKILL.md +142 -0
- package/skills/notion/SKILL.md +174 -0
- package/skills/obsidian/SKILL.md +81 -0
- package/skills/openai-image-gen/SKILL.md +92 -0
- package/skills/openai-image-gen/scripts/gen.py +328 -0
- package/skills/openai-image-gen/scripts/test_gen.py +140 -0
- package/skills/openai-whisper/SKILL.md +38 -0
- package/skills/openai-whisper-api/SKILL.md +52 -0
- package/skills/openai-whisper-api/scripts/transcribe.sh +85 -0
- package/skills/openhue/SKILL.md +112 -0
- package/skills/oracle/SKILL.md +125 -0
- package/skills/ordercli/SKILL.md +78 -0
- package/skills/peekaboo/SKILL.md +190 -0
- package/skills/sag/SKILL.md +87 -0
- package/skills/session-logs/SKILL.md +115 -0
- package/skills/sherpa-onnx-tts/SKILL.md +103 -0
- package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +178 -0
- package/skills/skill-creator/SKILL.md +372 -0
- package/skills/skill-creator/license.txt +202 -0
- package/skills/skill-creator/scripts/init_skill.py +378 -0
- package/skills/skill-creator/scripts/package_skill.py +139 -0
- package/skills/skill-creator/scripts/quick_validate.py +159 -0
- package/skills/skill-creator/scripts/test_package_skill.py +160 -0
- package/skills/skill-creator/scripts/test_quick_validate.py +72 -0
- package/skills/slack/SKILL.md +144 -0
- package/skills/songsee/SKILL.md +49 -0
- package/skills/sonoscli/SKILL.md +65 -0
- package/skills/spotify-player/SKILL.md +64 -0
- package/skills/summarize/SKILL.md +87 -0
- package/skills/things-mac/SKILL.md +86 -0
- package/skills/tmux/SKILL.md +153 -0
- package/skills/tmux/scripts/find-sessions.sh +112 -0
- package/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/skills/trello/SKILL.md +95 -0
- package/skills/video-frames/SKILL.md +46 -0
- package/skills/video-frames/scripts/frame.sh +81 -0
- package/skills/voice-call/SKILL.md +45 -0
- package/skills/wacli/SKILL.md +72 -0
- package/skills/weather/SKILL.md +112 -0
- package/skills/xurl/SKILL.md +461 -0
|
@@ -0,0 +1,4226 @@
|
|
|
1
|
+
import { r as formatErrorMessage } from "./errors-BxyFnvP3.js";
|
|
2
|
+
import { t as createSubsystemLogger } from "./subsystem-CUp-6QQf.js";
|
|
3
|
+
import { t as isTruthyEnvValue } from "./env-BP70DGuy.js";
|
|
4
|
+
import { E as truncateUtf16Safe, y as resolveUserPath } from "./utils-Bxk6BLTg.js";
|
|
5
|
+
import { a as resolveAgentDir, p as resolveAgentWorkspaceDir } from "./agent-scope-DPP4Z_UU.js";
|
|
6
|
+
import { r as normalizeProviderId } from "./provider-id-BpXo5t6v.js";
|
|
7
|
+
import { a as hasConfiguredSecretInput, c as normalizeResolvedSecretInputString } from "./types.secrets-BWSeXrF4.js";
|
|
8
|
+
import { t as normalizeOptionalSecretInput } from "./normalize-secret-input-_PgpexOG.js";
|
|
9
|
+
import { c as resolveSessionTranscriptsDirForAgent } from "./paths-CTjJI9l0.js";
|
|
10
|
+
import { n as onSessionTranscriptUpdate } from "./transcript-events-B1V6z5ct.js";
|
|
11
|
+
import { t as resolveEnvApiKey } from "./model-auth-env-CF9ts7Th.js";
|
|
12
|
+
import { o as requireApiKey, s as resolveApiKeyForProvider } from "./model-auth-B__TJTPw.js";
|
|
13
|
+
import { r as OPENAI_DEFAULT_EMBEDDING_MODEL } from "./openai-defaults-B7FUywsh.js";
|
|
14
|
+
import { n as retryAsync, u as resolveOllamaApiBase } from "./retry-D15TD1S3.js";
|
|
15
|
+
import { n as fetchWithSsrFGuard } from "./fetch-guard-DIyN1HW5.js";
|
|
16
|
+
import { t as parseGeminiAuth } from "./gemini-auth-B5ljg7jr.js";
|
|
17
|
+
import { i as getMemoryMultimodalExtensions, r as classifyMemoryMultimodalPath, t as buildCaseInsensitiveExtensionGlob } from "./multimodal-DC43jYNv.js";
|
|
18
|
+
import { t as resolveMemorySearchConfig } from "./memory-search-Das1tiuB.js";
|
|
19
|
+
import { C as estimateUtf8Bytes, S as estimateStructuredEmbeddingInputBytes, _ as remapChunkLines, a as listSessionFilesForAgent, b as statRegularFile, c as buildMultimodalChunkForIndexing, d as ensureDir, f as hashText, g as parseEmbedding, h as normalizeExtraMemoryPaths, i as buildSessionEntry, l as chunkMarkdown, m as listMemoryFiles, o as sessionPathForFile, p as isMemoryPath, r as requireNodeSqlite, s as buildFileEntry, t as extractKeywords, u as cosineSimilarity, v as runWithConcurrency, w as splitTextToUtf8ByteLimit, x as hasNonTextEmbeddingParts, y as isFileMissingError } from "./query-expansion-Do45hILP.js";
|
|
20
|
+
import fs from "node:fs";
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
import fs$1 from "node:fs/promises";
|
|
23
|
+
import { randomUUID } from "node:crypto";
|
|
24
|
+
import { Readable } from "node:stream";
|
|
25
|
+
import chokidar from "chokidar";
|
|
26
|
+
import { createInterface } from "node:readline";
|
|
27
|
+
//#region src/agents/live-auth-keys.ts
|
|
28
|
+
const KEY_SPLIT_RE = /[\s,;]+/g;
|
|
29
|
+
const GOOGLE_LIVE_SINGLE_KEY = "OPENCLAW_LIVE_GEMINI_KEY";
|
|
30
|
+
const PROVIDER_PREFIX_OVERRIDES = {
|
|
31
|
+
google: "GEMINI",
|
|
32
|
+
"google-vertex": "GEMINI"
|
|
33
|
+
};
|
|
34
|
+
const PROVIDER_API_KEY_CONFIG = {
|
|
35
|
+
anthropic: {
|
|
36
|
+
liveSingle: "OPENCLAW_LIVE_ANTHROPIC_KEY",
|
|
37
|
+
listVar: "OPENCLAW_LIVE_ANTHROPIC_KEYS",
|
|
38
|
+
primaryVar: "ANTHROPIC_API_KEY",
|
|
39
|
+
prefixedVar: "ANTHROPIC_API_KEY_"
|
|
40
|
+
},
|
|
41
|
+
google: {
|
|
42
|
+
liveSingle: GOOGLE_LIVE_SINGLE_KEY,
|
|
43
|
+
listVar: "GEMINI_API_KEYS",
|
|
44
|
+
primaryVar: "GEMINI_API_KEY",
|
|
45
|
+
prefixedVar: "GEMINI_API_KEY_"
|
|
46
|
+
},
|
|
47
|
+
"google-vertex": {
|
|
48
|
+
liveSingle: GOOGLE_LIVE_SINGLE_KEY,
|
|
49
|
+
listVar: "GEMINI_API_KEYS",
|
|
50
|
+
primaryVar: "GEMINI_API_KEY",
|
|
51
|
+
prefixedVar: "GEMINI_API_KEY_"
|
|
52
|
+
},
|
|
53
|
+
openai: {
|
|
54
|
+
liveSingle: "OPENCLAW_LIVE_OPENAI_KEY",
|
|
55
|
+
listVar: "OPENAI_API_KEYS",
|
|
56
|
+
primaryVar: "OPENAI_API_KEY",
|
|
57
|
+
prefixedVar: "OPENAI_API_KEY_"
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
function parseKeyList(raw) {
|
|
61
|
+
if (!raw) return [];
|
|
62
|
+
return raw.split(KEY_SPLIT_RE).map((value) => value.trim()).filter(Boolean);
|
|
63
|
+
}
|
|
64
|
+
function collectEnvPrefixedKeys(prefix) {
|
|
65
|
+
const keys = [];
|
|
66
|
+
for (const [name, value] of Object.entries(process.env)) {
|
|
67
|
+
if (!name.startsWith(prefix)) continue;
|
|
68
|
+
const trimmed = value?.trim();
|
|
69
|
+
if (!trimmed) continue;
|
|
70
|
+
keys.push(trimmed);
|
|
71
|
+
}
|
|
72
|
+
return keys;
|
|
73
|
+
}
|
|
74
|
+
function resolveProviderApiKeyConfig(provider) {
|
|
75
|
+
const normalized = normalizeProviderId(provider);
|
|
76
|
+
const custom = PROVIDER_API_KEY_CONFIG[normalized];
|
|
77
|
+
const base = PROVIDER_PREFIX_OVERRIDES[normalized] ?? normalized.toUpperCase().replace(/-/g, "_");
|
|
78
|
+
const liveSingle = custom?.liveSingle ?? `OPENCLAW_LIVE_${base}_KEY`;
|
|
79
|
+
const listVar = custom?.listVar ?? `${base}_API_KEYS`;
|
|
80
|
+
const primaryVar = custom?.primaryVar ?? `${base}_API_KEY`;
|
|
81
|
+
const prefixedVar = custom?.prefixedVar ?? `${base}_API_KEY_`;
|
|
82
|
+
if (normalized === "google" || normalized === "google-vertex") return {
|
|
83
|
+
liveSingle,
|
|
84
|
+
listVar,
|
|
85
|
+
primaryVar,
|
|
86
|
+
prefixedVar,
|
|
87
|
+
fallbackVars: ["GOOGLE_API_KEY"]
|
|
88
|
+
};
|
|
89
|
+
return {
|
|
90
|
+
liveSingle,
|
|
91
|
+
listVar,
|
|
92
|
+
primaryVar,
|
|
93
|
+
prefixedVar,
|
|
94
|
+
fallbackVars: []
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function collectProviderApiKeys(provider) {
|
|
98
|
+
const config = resolveProviderApiKeyConfig(provider);
|
|
99
|
+
const forcedSingle = config.liveSingle ? process.env[config.liveSingle]?.trim() : void 0;
|
|
100
|
+
if (forcedSingle) return [forcedSingle];
|
|
101
|
+
const fromList = parseKeyList(config.listVar ? process.env[config.listVar] : void 0);
|
|
102
|
+
const primary = config.primaryVar ? process.env[config.primaryVar]?.trim() : void 0;
|
|
103
|
+
const fromPrefixed = config.prefixedVar ? collectEnvPrefixedKeys(config.prefixedVar) : [];
|
|
104
|
+
const fallback = config.fallbackVars.map((envVar) => process.env[envVar]?.trim()).filter(Boolean);
|
|
105
|
+
const seen = /* @__PURE__ */ new Set();
|
|
106
|
+
const add = (value) => {
|
|
107
|
+
if (!value) return;
|
|
108
|
+
if (seen.has(value)) return;
|
|
109
|
+
seen.add(value);
|
|
110
|
+
};
|
|
111
|
+
for (const value of fromList) add(value);
|
|
112
|
+
add(primary);
|
|
113
|
+
for (const value of fromPrefixed) add(value);
|
|
114
|
+
for (const value of fallback) add(value);
|
|
115
|
+
return Array.from(seen);
|
|
116
|
+
}
|
|
117
|
+
function isApiKeyRateLimitError(message) {
|
|
118
|
+
const lower = message.toLowerCase();
|
|
119
|
+
if (lower.includes("rate_limit")) return true;
|
|
120
|
+
if (lower.includes("rate limit")) return true;
|
|
121
|
+
if (lower.includes("429")) return true;
|
|
122
|
+
if (lower.includes("quota exceeded") || lower.includes("quota_exceeded")) return true;
|
|
123
|
+
if (lower.includes("resource exhausted") || lower.includes("resource_exhausted")) return true;
|
|
124
|
+
if (lower.includes("too many requests")) return true;
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/agents/api-key-rotation.ts
|
|
129
|
+
function dedupeApiKeys(raw) {
|
|
130
|
+
const seen = /* @__PURE__ */ new Set();
|
|
131
|
+
const keys = [];
|
|
132
|
+
for (const value of raw) {
|
|
133
|
+
const apiKey = value.trim();
|
|
134
|
+
if (!apiKey || seen.has(apiKey)) continue;
|
|
135
|
+
seen.add(apiKey);
|
|
136
|
+
keys.push(apiKey);
|
|
137
|
+
}
|
|
138
|
+
return keys;
|
|
139
|
+
}
|
|
140
|
+
function collectProviderApiKeysForExecution(params) {
|
|
141
|
+
const { primaryApiKey, provider } = params;
|
|
142
|
+
return dedupeApiKeys([primaryApiKey?.trim() ?? "", ...collectProviderApiKeys(provider)]);
|
|
143
|
+
}
|
|
144
|
+
async function executeWithApiKeyRotation(params) {
|
|
145
|
+
const keys = dedupeApiKeys(params.apiKeys);
|
|
146
|
+
if (keys.length === 0) throw new Error(`No API keys configured for provider "${params.provider}".`);
|
|
147
|
+
let lastError;
|
|
148
|
+
for (let attempt = 0; attempt < keys.length; attempt += 1) {
|
|
149
|
+
const apiKey = keys[attempt];
|
|
150
|
+
try {
|
|
151
|
+
return await params.execute(apiKey);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
lastError = error;
|
|
154
|
+
const message = formatErrorMessage(error);
|
|
155
|
+
if (!(params.shouldRetry ? params.shouldRetry({
|
|
156
|
+
apiKey,
|
|
157
|
+
error,
|
|
158
|
+
attempt,
|
|
159
|
+
message
|
|
160
|
+
}) : isApiKeyRateLimitError(message)) || attempt + 1 >= keys.length) break;
|
|
161
|
+
params.onRetry?.({
|
|
162
|
+
apiKey,
|
|
163
|
+
error,
|
|
164
|
+
attempt,
|
|
165
|
+
message
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (lastError === void 0) throw new Error(`Failed to run API request for ${params.provider}.`);
|
|
170
|
+
throw lastError;
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/memory/embedding-vectors.ts
|
|
174
|
+
function sanitizeAndNormalizeEmbedding(vec) {
|
|
175
|
+
const sanitized = vec.map((value) => Number.isFinite(value) ? value : 0);
|
|
176
|
+
const magnitude = Math.sqrt(sanitized.reduce((sum, value) => sum + value * value, 0));
|
|
177
|
+
if (magnitude < 1e-10) return sanitized;
|
|
178
|
+
return sanitized.map((value) => value / magnitude);
|
|
179
|
+
}
|
|
180
|
+
//#endregion
|
|
181
|
+
//#region src/memory/embeddings-debug.ts
|
|
182
|
+
const debugEmbeddings = isTruthyEnvValue(process.env.OPENCLAW_DEBUG_MEMORY_EMBEDDINGS);
|
|
183
|
+
const log$3 = createSubsystemLogger("memory/embeddings");
|
|
184
|
+
function debugEmbeddingsLog(message, meta) {
|
|
185
|
+
if (!debugEmbeddings) return;
|
|
186
|
+
const suffix = meta ? ` ${JSON.stringify(meta)}` : "";
|
|
187
|
+
log$3.raw(`${message}${suffix}`);
|
|
188
|
+
}
|
|
189
|
+
//#endregion
|
|
190
|
+
//#region src/memory/remote-http.ts
|
|
191
|
+
function buildRemoteBaseUrlPolicy(baseUrl) {
|
|
192
|
+
const trimmed = baseUrl.trim();
|
|
193
|
+
if (!trimmed) return;
|
|
194
|
+
try {
|
|
195
|
+
const parsed = new URL(trimmed);
|
|
196
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return;
|
|
197
|
+
return { allowedHostnames: [parsed.hostname] };
|
|
198
|
+
} catch {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async function withRemoteHttpResponse(params) {
|
|
203
|
+
const { response, release } = await fetchWithSsrFGuard({
|
|
204
|
+
url: params.url,
|
|
205
|
+
init: params.init,
|
|
206
|
+
policy: params.ssrfPolicy,
|
|
207
|
+
auditContext: params.auditContext ?? "memory-remote"
|
|
208
|
+
});
|
|
209
|
+
try {
|
|
210
|
+
return await params.onResponse(response);
|
|
211
|
+
} finally {
|
|
212
|
+
await release();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/memory/secret-input.ts
|
|
217
|
+
function hasConfiguredMemorySecretInput(value) {
|
|
218
|
+
return hasConfiguredSecretInput(value);
|
|
219
|
+
}
|
|
220
|
+
function resolveMemorySecretInputString(params) {
|
|
221
|
+
return normalizeResolvedSecretInputString({
|
|
222
|
+
value: params.value,
|
|
223
|
+
path: params.path
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region src/memory/embeddings-gemini.ts
|
|
228
|
+
const DEFAULT_GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
|
|
229
|
+
const DEFAULT_GEMINI_EMBEDDING_MODEL = "gemini-embedding-001";
|
|
230
|
+
const GEMINI_MAX_INPUT_TOKENS = { "text-embedding-004": 2048 };
|
|
231
|
+
const GEMINI_EMBEDDING_2_MODELS = new Set(["gemini-embedding-2-preview"]);
|
|
232
|
+
const GEMINI_EMBEDDING_2_DEFAULT_DIMENSIONS = 3072;
|
|
233
|
+
const GEMINI_EMBEDDING_2_VALID_DIMENSIONS = [
|
|
234
|
+
768,
|
|
235
|
+
1536,
|
|
236
|
+
3072
|
|
237
|
+
];
|
|
238
|
+
/** Builds the text-only Gemini embedding request shape used across direct and batch APIs. */
|
|
239
|
+
function buildGeminiTextEmbeddingRequest(params) {
|
|
240
|
+
return buildGeminiEmbeddingRequest({
|
|
241
|
+
input: { text: params.text },
|
|
242
|
+
taskType: params.taskType,
|
|
243
|
+
outputDimensionality: params.outputDimensionality,
|
|
244
|
+
modelPath: params.modelPath
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
function buildGeminiEmbeddingRequest(params) {
|
|
248
|
+
const request = {
|
|
249
|
+
content: { parts: params.input.parts?.map((part) => part.type === "text" ? { text: part.text } : { inlineData: {
|
|
250
|
+
mimeType: part.mimeType,
|
|
251
|
+
data: part.data
|
|
252
|
+
} }) ?? [{ text: params.input.text }] },
|
|
253
|
+
taskType: params.taskType
|
|
254
|
+
};
|
|
255
|
+
if (params.modelPath) request.model = params.modelPath;
|
|
256
|
+
if (params.outputDimensionality != null) request.outputDimensionality = params.outputDimensionality;
|
|
257
|
+
return request;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Returns true if the given model name is a gemini-embedding-2 variant that
|
|
261
|
+
* supports `outputDimensionality` and extended task types.
|
|
262
|
+
*/
|
|
263
|
+
function isGeminiEmbedding2Model(model) {
|
|
264
|
+
return GEMINI_EMBEDDING_2_MODELS.has(model);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Validate and return the `outputDimensionality` for gemini-embedding-2 models.
|
|
268
|
+
* Returns `undefined` for older models (they don't support the param).
|
|
269
|
+
*/
|
|
270
|
+
function resolveGeminiOutputDimensionality(model, requested) {
|
|
271
|
+
if (!isGeminiEmbedding2Model(model)) return;
|
|
272
|
+
if (requested == null) return GEMINI_EMBEDDING_2_DEFAULT_DIMENSIONS;
|
|
273
|
+
const valid = GEMINI_EMBEDDING_2_VALID_DIMENSIONS;
|
|
274
|
+
if (!valid.includes(requested)) throw new Error(`Invalid outputDimensionality ${requested} for ${model}. Valid values: ${valid.join(", ")}`);
|
|
275
|
+
return requested;
|
|
276
|
+
}
|
|
277
|
+
function resolveRemoteApiKey(remoteApiKey) {
|
|
278
|
+
const trimmed = resolveMemorySecretInputString({
|
|
279
|
+
value: remoteApiKey,
|
|
280
|
+
path: "agents.*.memorySearch.remote.apiKey"
|
|
281
|
+
});
|
|
282
|
+
if (!trimmed) return;
|
|
283
|
+
if (trimmed === "GOOGLE_API_KEY" || trimmed === "GEMINI_API_KEY") return process.env[trimmed]?.trim();
|
|
284
|
+
return trimmed;
|
|
285
|
+
}
|
|
286
|
+
function normalizeGeminiModel(model) {
|
|
287
|
+
const trimmed = model.trim();
|
|
288
|
+
if (!trimmed) return DEFAULT_GEMINI_EMBEDDING_MODEL;
|
|
289
|
+
const withoutPrefix = trimmed.replace(/^models\//, "");
|
|
290
|
+
if (withoutPrefix.startsWith("gemini/")) return withoutPrefix.slice(7);
|
|
291
|
+
if (withoutPrefix.startsWith("google/")) return withoutPrefix.slice(7);
|
|
292
|
+
return withoutPrefix;
|
|
293
|
+
}
|
|
294
|
+
async function fetchGeminiEmbeddingPayload(params) {
|
|
295
|
+
return await executeWithApiKeyRotation({
|
|
296
|
+
provider: "google",
|
|
297
|
+
apiKeys: params.client.apiKeys,
|
|
298
|
+
execute: async (apiKey) => {
|
|
299
|
+
const headers = {
|
|
300
|
+
...parseGeminiAuth(apiKey).headers,
|
|
301
|
+
...params.client.headers
|
|
302
|
+
};
|
|
303
|
+
return await withRemoteHttpResponse({
|
|
304
|
+
url: params.endpoint,
|
|
305
|
+
ssrfPolicy: params.client.ssrfPolicy,
|
|
306
|
+
init: {
|
|
307
|
+
method: "POST",
|
|
308
|
+
headers,
|
|
309
|
+
body: JSON.stringify(params.body)
|
|
310
|
+
},
|
|
311
|
+
onResponse: async (res) => {
|
|
312
|
+
if (!res.ok) {
|
|
313
|
+
const text = await res.text();
|
|
314
|
+
throw new Error(`gemini embeddings failed: ${res.status} ${text}`);
|
|
315
|
+
}
|
|
316
|
+
return await res.json();
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
function normalizeGeminiBaseUrl(raw) {
|
|
323
|
+
const trimmed = raw.replace(/\/+$/, "");
|
|
324
|
+
const openAiIndex = trimmed.indexOf("/openai");
|
|
325
|
+
if (openAiIndex > -1) return trimmed.slice(0, openAiIndex);
|
|
326
|
+
return trimmed;
|
|
327
|
+
}
|
|
328
|
+
function buildGeminiModelPath(model) {
|
|
329
|
+
return model.startsWith("models/") ? model : `models/${model}`;
|
|
330
|
+
}
|
|
331
|
+
async function createGeminiEmbeddingProvider(options) {
|
|
332
|
+
const client = await resolveGeminiEmbeddingClient(options);
|
|
333
|
+
const baseUrl = client.baseUrl.replace(/\/$/, "");
|
|
334
|
+
const embedUrl = `${baseUrl}/${client.modelPath}:embedContent`;
|
|
335
|
+
const batchUrl = `${baseUrl}/${client.modelPath}:batchEmbedContents`;
|
|
336
|
+
const isV2 = isGeminiEmbedding2Model(client.model);
|
|
337
|
+
const outputDimensionality = client.outputDimensionality;
|
|
338
|
+
const embedQuery = async (text) => {
|
|
339
|
+
if (!text.trim()) return [];
|
|
340
|
+
return sanitizeAndNormalizeEmbedding((await fetchGeminiEmbeddingPayload({
|
|
341
|
+
client,
|
|
342
|
+
endpoint: embedUrl,
|
|
343
|
+
body: buildGeminiTextEmbeddingRequest({
|
|
344
|
+
text,
|
|
345
|
+
taskType: options.taskType ?? "RETRIEVAL_QUERY",
|
|
346
|
+
outputDimensionality: isV2 ? outputDimensionality : void 0
|
|
347
|
+
})
|
|
348
|
+
})).embedding?.values ?? []);
|
|
349
|
+
};
|
|
350
|
+
const embedBatchInputs = async (inputs) => {
|
|
351
|
+
if (inputs.length === 0) return [];
|
|
352
|
+
const payload = await fetchGeminiEmbeddingPayload({
|
|
353
|
+
client,
|
|
354
|
+
endpoint: batchUrl,
|
|
355
|
+
body: { requests: inputs.map((input) => buildGeminiEmbeddingRequest({
|
|
356
|
+
input,
|
|
357
|
+
modelPath: client.modelPath,
|
|
358
|
+
taskType: options.taskType ?? "RETRIEVAL_DOCUMENT",
|
|
359
|
+
outputDimensionality: isV2 ? outputDimensionality : void 0
|
|
360
|
+
})) }
|
|
361
|
+
});
|
|
362
|
+
const embeddings = Array.isArray(payload.embeddings) ? payload.embeddings : [];
|
|
363
|
+
return inputs.map((_, index) => sanitizeAndNormalizeEmbedding(embeddings[index]?.values ?? []));
|
|
364
|
+
};
|
|
365
|
+
const embedBatch = async (texts) => {
|
|
366
|
+
return await embedBatchInputs(texts.map((text) => ({ text })));
|
|
367
|
+
};
|
|
368
|
+
return {
|
|
369
|
+
provider: {
|
|
370
|
+
id: "gemini",
|
|
371
|
+
model: client.model,
|
|
372
|
+
maxInputTokens: GEMINI_MAX_INPUT_TOKENS[client.model],
|
|
373
|
+
embedQuery,
|
|
374
|
+
embedBatch,
|
|
375
|
+
embedBatchInputs
|
|
376
|
+
},
|
|
377
|
+
client
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
async function resolveGeminiEmbeddingClient(options) {
|
|
381
|
+
const remote = options.remote;
|
|
382
|
+
const remoteApiKey = resolveRemoteApiKey(remote?.apiKey);
|
|
383
|
+
const remoteBaseUrl = remote?.baseUrl?.trim();
|
|
384
|
+
const apiKey = remoteApiKey ? remoteApiKey : requireApiKey(await resolveApiKeyForProvider({
|
|
385
|
+
provider: "google",
|
|
386
|
+
cfg: options.config,
|
|
387
|
+
agentDir: options.agentDir
|
|
388
|
+
}), "google");
|
|
389
|
+
const providerConfig = options.config.models?.providers?.google;
|
|
390
|
+
const rawBaseUrl = remoteBaseUrl || providerConfig?.baseUrl?.trim() || DEFAULT_GEMINI_BASE_URL;
|
|
391
|
+
const baseUrl = normalizeGeminiBaseUrl(rawBaseUrl);
|
|
392
|
+
const ssrfPolicy = buildRemoteBaseUrlPolicy(baseUrl);
|
|
393
|
+
const headers = { ...Object.assign({}, providerConfig?.headers, remote?.headers) };
|
|
394
|
+
const apiKeys = collectProviderApiKeysForExecution({
|
|
395
|
+
provider: "google",
|
|
396
|
+
primaryApiKey: apiKey
|
|
397
|
+
});
|
|
398
|
+
const model = normalizeGeminiModel(options.model);
|
|
399
|
+
const modelPath = buildGeminiModelPath(model);
|
|
400
|
+
const outputDimensionality = resolveGeminiOutputDimensionality(model, options.outputDimensionality);
|
|
401
|
+
debugEmbeddingsLog("memory embeddings: gemini client", {
|
|
402
|
+
rawBaseUrl,
|
|
403
|
+
baseUrl,
|
|
404
|
+
model,
|
|
405
|
+
modelPath,
|
|
406
|
+
outputDimensionality,
|
|
407
|
+
embedEndpoint: `${baseUrl}/${modelPath}:embedContent`,
|
|
408
|
+
batchEndpoint: `${baseUrl}/${modelPath}:batchEmbedContents`
|
|
409
|
+
});
|
|
410
|
+
return {
|
|
411
|
+
baseUrl,
|
|
412
|
+
headers,
|
|
413
|
+
ssrfPolicy,
|
|
414
|
+
model,
|
|
415
|
+
modelPath,
|
|
416
|
+
apiKeys,
|
|
417
|
+
outputDimensionality
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region src/memory/embeddings-model-normalize.ts
|
|
422
|
+
function normalizeEmbeddingModelWithPrefixes(params) {
|
|
423
|
+
const trimmed = params.model.trim();
|
|
424
|
+
if (!trimmed) return params.defaultModel;
|
|
425
|
+
for (const prefix of params.prefixes) if (trimmed.startsWith(prefix)) return trimmed.slice(prefix.length);
|
|
426
|
+
return trimmed;
|
|
427
|
+
}
|
|
428
|
+
//#endregion
|
|
429
|
+
//#region src/memory/embeddings-remote-client.ts
|
|
430
|
+
async function resolveRemoteEmbeddingBearerClient(params) {
|
|
431
|
+
const remote = params.options.remote;
|
|
432
|
+
const remoteApiKey = resolveMemorySecretInputString({
|
|
433
|
+
value: remote?.apiKey,
|
|
434
|
+
path: "agents.*.memorySearch.remote.apiKey"
|
|
435
|
+
});
|
|
436
|
+
const remoteBaseUrl = remote?.baseUrl?.trim();
|
|
437
|
+
const providerConfig = params.options.config.models?.providers?.[params.provider];
|
|
438
|
+
const apiKey = remoteApiKey ? remoteApiKey : requireApiKey(await resolveApiKeyForProvider({
|
|
439
|
+
provider: params.provider,
|
|
440
|
+
cfg: params.options.config,
|
|
441
|
+
agentDir: params.options.agentDir
|
|
442
|
+
}), params.provider);
|
|
443
|
+
const baseUrl = remoteBaseUrl || providerConfig?.baseUrl?.trim() || params.defaultBaseUrl;
|
|
444
|
+
const headerOverrides = Object.assign({}, providerConfig?.headers, remote?.headers);
|
|
445
|
+
return {
|
|
446
|
+
baseUrl,
|
|
447
|
+
headers: {
|
|
448
|
+
"Content-Type": "application/json",
|
|
449
|
+
Authorization: `Bearer ${apiKey}`,
|
|
450
|
+
...headerOverrides
|
|
451
|
+
},
|
|
452
|
+
ssrfPolicy: buildRemoteBaseUrlPolicy(baseUrl)
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
//#endregion
|
|
456
|
+
//#region src/memory/post-json.ts
|
|
457
|
+
async function postJson(params) {
|
|
458
|
+
return await withRemoteHttpResponse({
|
|
459
|
+
url: params.url,
|
|
460
|
+
ssrfPolicy: params.ssrfPolicy,
|
|
461
|
+
init: {
|
|
462
|
+
method: "POST",
|
|
463
|
+
headers: params.headers,
|
|
464
|
+
body: JSON.stringify(params.body)
|
|
465
|
+
},
|
|
466
|
+
onResponse: async (res) => {
|
|
467
|
+
if (!res.ok) {
|
|
468
|
+
const text = await res.text();
|
|
469
|
+
const err = /* @__PURE__ */ new Error(`${params.errorPrefix}: ${res.status} ${text}`);
|
|
470
|
+
if (params.attachStatus) err.status = res.status;
|
|
471
|
+
throw err;
|
|
472
|
+
}
|
|
473
|
+
return await params.parse(await res.json());
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
//#endregion
|
|
478
|
+
//#region src/memory/embeddings-remote-fetch.ts
|
|
479
|
+
async function fetchRemoteEmbeddingVectors(params) {
|
|
480
|
+
return await postJson({
|
|
481
|
+
url: params.url,
|
|
482
|
+
headers: params.headers,
|
|
483
|
+
ssrfPolicy: params.ssrfPolicy,
|
|
484
|
+
body: params.body,
|
|
485
|
+
errorPrefix: params.errorPrefix,
|
|
486
|
+
parse: (payload) => {
|
|
487
|
+
return (payload.data ?? []).map((entry) => entry.embedding ?? []);
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
//#endregion
|
|
492
|
+
//#region src/memory/embeddings-remote-provider.ts
|
|
493
|
+
function createRemoteEmbeddingProvider(params) {
|
|
494
|
+
const { client } = params;
|
|
495
|
+
const url = `${client.baseUrl.replace(/\/$/, "")}/embeddings`;
|
|
496
|
+
const embed = async (input) => {
|
|
497
|
+
if (input.length === 0) return [];
|
|
498
|
+
return await fetchRemoteEmbeddingVectors({
|
|
499
|
+
url,
|
|
500
|
+
headers: client.headers,
|
|
501
|
+
ssrfPolicy: client.ssrfPolicy,
|
|
502
|
+
body: {
|
|
503
|
+
model: client.model,
|
|
504
|
+
input
|
|
505
|
+
},
|
|
506
|
+
errorPrefix: params.errorPrefix
|
|
507
|
+
});
|
|
508
|
+
};
|
|
509
|
+
return {
|
|
510
|
+
id: params.id,
|
|
511
|
+
model: client.model,
|
|
512
|
+
...typeof params.maxInputTokens === "number" ? { maxInputTokens: params.maxInputTokens } : {},
|
|
513
|
+
embedQuery: async (text) => {
|
|
514
|
+
const [vec] = await embed([text]);
|
|
515
|
+
return vec ?? [];
|
|
516
|
+
},
|
|
517
|
+
embedBatch: embed
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
async function resolveRemoteEmbeddingClient(params) {
|
|
521
|
+
const { baseUrl, headers, ssrfPolicy } = await resolveRemoteEmbeddingBearerClient({
|
|
522
|
+
provider: params.provider,
|
|
523
|
+
options: params.options,
|
|
524
|
+
defaultBaseUrl: params.defaultBaseUrl
|
|
525
|
+
});
|
|
526
|
+
return {
|
|
527
|
+
baseUrl,
|
|
528
|
+
headers,
|
|
529
|
+
ssrfPolicy,
|
|
530
|
+
model: params.normalizeModel(params.options.model)
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
//#endregion
|
|
534
|
+
//#region src/memory/embeddings-mistral.ts
|
|
535
|
+
const DEFAULT_MISTRAL_EMBEDDING_MODEL = "mistral-embed";
|
|
536
|
+
const DEFAULT_MISTRAL_BASE_URL = "https://api.mistral.ai/v1";
|
|
537
|
+
function normalizeMistralModel(model) {
|
|
538
|
+
return normalizeEmbeddingModelWithPrefixes({
|
|
539
|
+
model,
|
|
540
|
+
defaultModel: DEFAULT_MISTRAL_EMBEDDING_MODEL,
|
|
541
|
+
prefixes: ["mistral/"]
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
async function createMistralEmbeddingProvider(options) {
|
|
545
|
+
const client = await resolveMistralEmbeddingClient(options);
|
|
546
|
+
return {
|
|
547
|
+
provider: createRemoteEmbeddingProvider({
|
|
548
|
+
id: "mistral",
|
|
549
|
+
client,
|
|
550
|
+
errorPrefix: "mistral embeddings failed"
|
|
551
|
+
}),
|
|
552
|
+
client
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
async function resolveMistralEmbeddingClient(options) {
|
|
556
|
+
return await resolveRemoteEmbeddingClient({
|
|
557
|
+
provider: "mistral",
|
|
558
|
+
options,
|
|
559
|
+
defaultBaseUrl: DEFAULT_MISTRAL_BASE_URL,
|
|
560
|
+
normalizeModel: normalizeMistralModel
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
//#endregion
|
|
564
|
+
//#region src/memory/embeddings-ollama.ts
|
|
565
|
+
const DEFAULT_OLLAMA_EMBEDDING_MODEL = "nomic-embed-text";
|
|
566
|
+
function normalizeOllamaModel(model) {
|
|
567
|
+
return normalizeEmbeddingModelWithPrefixes({
|
|
568
|
+
model,
|
|
569
|
+
defaultModel: DEFAULT_OLLAMA_EMBEDDING_MODEL,
|
|
570
|
+
prefixes: ["ollama/"]
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
function resolveOllamaApiKey(options) {
|
|
574
|
+
const remoteApiKey = resolveMemorySecretInputString({
|
|
575
|
+
value: options.remote?.apiKey,
|
|
576
|
+
path: "agents.*.memorySearch.remote.apiKey"
|
|
577
|
+
});
|
|
578
|
+
if (remoteApiKey) return remoteApiKey;
|
|
579
|
+
const providerApiKey = normalizeOptionalSecretInput(options.config.models?.providers?.ollama?.apiKey);
|
|
580
|
+
if (providerApiKey) return providerApiKey;
|
|
581
|
+
return resolveEnvApiKey("ollama")?.apiKey;
|
|
582
|
+
}
|
|
583
|
+
function resolveOllamaEmbeddingClient(options) {
|
|
584
|
+
const providerConfig = options.config.models?.providers?.ollama;
|
|
585
|
+
const baseUrl = resolveOllamaApiBase(options.remote?.baseUrl?.trim() || providerConfig?.baseUrl?.trim());
|
|
586
|
+
const model = normalizeOllamaModel(options.model);
|
|
587
|
+
const headerOverrides = Object.assign({}, providerConfig?.headers, options.remote?.headers);
|
|
588
|
+
const headers = {
|
|
589
|
+
"Content-Type": "application/json",
|
|
590
|
+
...headerOverrides
|
|
591
|
+
};
|
|
592
|
+
const apiKey = resolveOllamaApiKey(options);
|
|
593
|
+
if (apiKey) headers.Authorization = `Bearer ${apiKey}`;
|
|
594
|
+
return {
|
|
595
|
+
baseUrl,
|
|
596
|
+
headers,
|
|
597
|
+
ssrfPolicy: buildRemoteBaseUrlPolicy(baseUrl),
|
|
598
|
+
model
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
async function createOllamaEmbeddingProvider(options) {
|
|
602
|
+
const client = resolveOllamaEmbeddingClient(options);
|
|
603
|
+
const embedUrl = `${client.baseUrl.replace(/\/$/, "")}/api/embeddings`;
|
|
604
|
+
const embedOne = async (text) => {
|
|
605
|
+
const json = await withRemoteHttpResponse({
|
|
606
|
+
url: embedUrl,
|
|
607
|
+
ssrfPolicy: client.ssrfPolicy,
|
|
608
|
+
init: {
|
|
609
|
+
method: "POST",
|
|
610
|
+
headers: client.headers,
|
|
611
|
+
body: JSON.stringify({
|
|
612
|
+
model: client.model,
|
|
613
|
+
prompt: text
|
|
614
|
+
})
|
|
615
|
+
},
|
|
616
|
+
onResponse: async (res) => {
|
|
617
|
+
if (!res.ok) throw new Error(`Ollama embeddings HTTP ${res.status}: ${await res.text()}`);
|
|
618
|
+
return await res.json();
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
if (!Array.isArray(json.embedding)) throw new Error(`Ollama embeddings response missing embedding[]`);
|
|
622
|
+
return sanitizeAndNormalizeEmbedding(json.embedding);
|
|
623
|
+
};
|
|
624
|
+
const provider = {
|
|
625
|
+
id: "ollama",
|
|
626
|
+
model: client.model,
|
|
627
|
+
embedQuery: embedOne,
|
|
628
|
+
embedBatch: async (texts) => {
|
|
629
|
+
return await Promise.all(texts.map(embedOne));
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
return {
|
|
633
|
+
provider,
|
|
634
|
+
client: {
|
|
635
|
+
...client,
|
|
636
|
+
embedBatch: async (texts) => {
|
|
637
|
+
try {
|
|
638
|
+
return await provider.embedBatch(texts);
|
|
639
|
+
} catch (err) {
|
|
640
|
+
throw new Error(formatErrorMessage(err), { cause: err });
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
//#endregion
|
|
647
|
+
//#region src/memory/embeddings-openai.ts
|
|
648
|
+
const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
649
|
+
const DEFAULT_OPENAI_EMBEDDING_MODEL = OPENAI_DEFAULT_EMBEDDING_MODEL;
|
|
650
|
+
const OPENAI_MAX_INPUT_TOKENS = {
|
|
651
|
+
"text-embedding-3-small": 8192,
|
|
652
|
+
"text-embedding-3-large": 8192,
|
|
653
|
+
"text-embedding-ada-002": 8191
|
|
654
|
+
};
|
|
655
|
+
function normalizeOpenAiModel(model) {
|
|
656
|
+
return normalizeEmbeddingModelWithPrefixes({
|
|
657
|
+
model,
|
|
658
|
+
defaultModel: DEFAULT_OPENAI_EMBEDDING_MODEL,
|
|
659
|
+
prefixes: ["openai/"]
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
async function createOpenAiEmbeddingProvider(options) {
|
|
663
|
+
const client = await resolveOpenAiEmbeddingClient(options);
|
|
664
|
+
return {
|
|
665
|
+
provider: createRemoteEmbeddingProvider({
|
|
666
|
+
id: "openai",
|
|
667
|
+
client,
|
|
668
|
+
errorPrefix: "openai embeddings failed",
|
|
669
|
+
maxInputTokens: OPENAI_MAX_INPUT_TOKENS[client.model]
|
|
670
|
+
}),
|
|
671
|
+
client
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
async function resolveOpenAiEmbeddingClient(options) {
|
|
675
|
+
return await resolveRemoteEmbeddingClient({
|
|
676
|
+
provider: "openai",
|
|
677
|
+
options,
|
|
678
|
+
defaultBaseUrl: DEFAULT_OPENAI_BASE_URL,
|
|
679
|
+
normalizeModel: normalizeOpenAiModel
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
//#endregion
|
|
683
|
+
//#region src/memory/embeddings-voyage.ts
|
|
684
|
+
const DEFAULT_VOYAGE_EMBEDDING_MODEL = "voyage-4-large";
|
|
685
|
+
const DEFAULT_VOYAGE_BASE_URL = "https://api.voyageai.com/v1";
|
|
686
|
+
const VOYAGE_MAX_INPUT_TOKENS = {
|
|
687
|
+
"voyage-3": 32e3,
|
|
688
|
+
"voyage-3-lite": 16e3,
|
|
689
|
+
"voyage-code-3": 32e3
|
|
690
|
+
};
|
|
691
|
+
function normalizeVoyageModel(model) {
|
|
692
|
+
return normalizeEmbeddingModelWithPrefixes({
|
|
693
|
+
model,
|
|
694
|
+
defaultModel: DEFAULT_VOYAGE_EMBEDDING_MODEL,
|
|
695
|
+
prefixes: ["voyage/"]
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
async function createVoyageEmbeddingProvider(options) {
|
|
699
|
+
const client = await resolveVoyageEmbeddingClient(options);
|
|
700
|
+
const url = `${client.baseUrl.replace(/\/$/, "")}/embeddings`;
|
|
701
|
+
const embed = async (input, input_type) => {
|
|
702
|
+
if (input.length === 0) return [];
|
|
703
|
+
const body = {
|
|
704
|
+
model: client.model,
|
|
705
|
+
input
|
|
706
|
+
};
|
|
707
|
+
if (input_type) body.input_type = input_type;
|
|
708
|
+
return await fetchRemoteEmbeddingVectors({
|
|
709
|
+
url,
|
|
710
|
+
headers: client.headers,
|
|
711
|
+
ssrfPolicy: client.ssrfPolicy,
|
|
712
|
+
body,
|
|
713
|
+
errorPrefix: "voyage embeddings failed"
|
|
714
|
+
});
|
|
715
|
+
};
|
|
716
|
+
return {
|
|
717
|
+
provider: {
|
|
718
|
+
id: "voyage",
|
|
719
|
+
model: client.model,
|
|
720
|
+
maxInputTokens: VOYAGE_MAX_INPUT_TOKENS[client.model],
|
|
721
|
+
embedQuery: async (text) => {
|
|
722
|
+
const [vec] = await embed([text], "query");
|
|
723
|
+
return vec ?? [];
|
|
724
|
+
},
|
|
725
|
+
embedBatch: async (texts) => embed(texts, "document")
|
|
726
|
+
},
|
|
727
|
+
client
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
async function resolveVoyageEmbeddingClient(options) {
|
|
731
|
+
const { baseUrl, headers, ssrfPolicy } = await resolveRemoteEmbeddingBearerClient({
|
|
732
|
+
provider: "voyage",
|
|
733
|
+
options,
|
|
734
|
+
defaultBaseUrl: DEFAULT_VOYAGE_BASE_URL
|
|
735
|
+
});
|
|
736
|
+
return {
|
|
737
|
+
baseUrl,
|
|
738
|
+
headers,
|
|
739
|
+
ssrfPolicy,
|
|
740
|
+
model: normalizeVoyageModel(options.model)
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
//#endregion
|
|
744
|
+
//#region src/memory/node-llama.ts
|
|
745
|
+
async function importNodeLlamaCpp() {
|
|
746
|
+
return import("node-llama-cpp");
|
|
747
|
+
}
|
|
748
|
+
//#endregion
|
|
749
|
+
//#region src/memory/embeddings.ts
|
|
750
|
+
const REMOTE_EMBEDDING_PROVIDER_IDS = [
|
|
751
|
+
"openai",
|
|
752
|
+
"gemini",
|
|
753
|
+
"voyage",
|
|
754
|
+
"mistral"
|
|
755
|
+
];
|
|
756
|
+
const DEFAULT_LOCAL_MODEL = "hf:ggml-org/embeddinggemma-300m-qat-q8_0-GGUF/embeddinggemma-300m-qat-Q8_0.gguf";
|
|
757
|
+
function canAutoSelectLocal(options) {
|
|
758
|
+
const modelPath = options.local?.modelPath?.trim();
|
|
759
|
+
if (!modelPath) return false;
|
|
760
|
+
if (/^(hf:|https?:)/i.test(modelPath)) return false;
|
|
761
|
+
const resolved = resolveUserPath(modelPath);
|
|
762
|
+
try {
|
|
763
|
+
return fs.statSync(resolved).isFile();
|
|
764
|
+
} catch {
|
|
765
|
+
return false;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
function isMissingApiKeyError(err) {
|
|
769
|
+
return formatErrorMessage(err).includes("No API key found for provider");
|
|
770
|
+
}
|
|
771
|
+
async function createLocalEmbeddingProvider(options) {
|
|
772
|
+
const modelPath = options.local?.modelPath?.trim() || "hf:ggml-org/embeddinggemma-300m-qat-q8_0-GGUF/embeddinggemma-300m-qat-Q8_0.gguf";
|
|
773
|
+
const modelCacheDir = options.local?.modelCacheDir?.trim();
|
|
774
|
+
const { getLlama, resolveModelFile, LlamaLogLevel } = await importNodeLlamaCpp();
|
|
775
|
+
let llama = null;
|
|
776
|
+
let embeddingModel = null;
|
|
777
|
+
let embeddingContext = null;
|
|
778
|
+
let initPromise = null;
|
|
779
|
+
const ensureContext = async () => {
|
|
780
|
+
if (embeddingContext) return embeddingContext;
|
|
781
|
+
if (initPromise) return initPromise;
|
|
782
|
+
initPromise = (async () => {
|
|
783
|
+
try {
|
|
784
|
+
if (!llama) llama = await getLlama({ logLevel: LlamaLogLevel.error });
|
|
785
|
+
if (!embeddingModel) {
|
|
786
|
+
const resolved = await resolveModelFile(modelPath, modelCacheDir || void 0);
|
|
787
|
+
embeddingModel = await llama.loadModel({ modelPath: resolved });
|
|
788
|
+
}
|
|
789
|
+
if (!embeddingContext) embeddingContext = await embeddingModel.createEmbeddingContext();
|
|
790
|
+
return embeddingContext;
|
|
791
|
+
} catch (err) {
|
|
792
|
+
initPromise = null;
|
|
793
|
+
throw err;
|
|
794
|
+
}
|
|
795
|
+
})();
|
|
796
|
+
return initPromise;
|
|
797
|
+
};
|
|
798
|
+
return {
|
|
799
|
+
id: "local",
|
|
800
|
+
model: modelPath,
|
|
801
|
+
embedQuery: async (text) => {
|
|
802
|
+
const embedding = await (await ensureContext()).getEmbeddingFor(text);
|
|
803
|
+
return sanitizeAndNormalizeEmbedding(Array.from(embedding.vector));
|
|
804
|
+
},
|
|
805
|
+
embedBatch: async (texts) => {
|
|
806
|
+
const ctx = await ensureContext();
|
|
807
|
+
return await Promise.all(texts.map(async (text) => {
|
|
808
|
+
const embedding = await ctx.getEmbeddingFor(text);
|
|
809
|
+
return sanitizeAndNormalizeEmbedding(Array.from(embedding.vector));
|
|
810
|
+
}));
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
async function createEmbeddingProvider(options) {
|
|
815
|
+
const requestedProvider = options.provider;
|
|
816
|
+
const fallback = options.fallback;
|
|
817
|
+
const createProvider = async (id) => {
|
|
818
|
+
if (id === "local") return { provider: await createLocalEmbeddingProvider(options) };
|
|
819
|
+
if (id === "ollama") {
|
|
820
|
+
const { provider, client } = await createOllamaEmbeddingProvider(options);
|
|
821
|
+
return {
|
|
822
|
+
provider,
|
|
823
|
+
ollama: client
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
if (id === "gemini") {
|
|
827
|
+
const { provider, client } = await createGeminiEmbeddingProvider(options);
|
|
828
|
+
return {
|
|
829
|
+
provider,
|
|
830
|
+
gemini: client
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
if (id === "voyage") {
|
|
834
|
+
const { provider, client } = await createVoyageEmbeddingProvider(options);
|
|
835
|
+
return {
|
|
836
|
+
provider,
|
|
837
|
+
voyage: client
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
if (id === "mistral") {
|
|
841
|
+
const { provider, client } = await createMistralEmbeddingProvider(options);
|
|
842
|
+
return {
|
|
843
|
+
provider,
|
|
844
|
+
mistral: client
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
const { provider, client } = await createOpenAiEmbeddingProvider(options);
|
|
848
|
+
return {
|
|
849
|
+
provider,
|
|
850
|
+
openAi: client
|
|
851
|
+
};
|
|
852
|
+
};
|
|
853
|
+
const formatPrimaryError = (err, provider) => provider === "local" ? formatLocalSetupError(err) : formatErrorMessage(err);
|
|
854
|
+
if (requestedProvider === "auto") {
|
|
855
|
+
const missingKeyErrors = [];
|
|
856
|
+
let localError = null;
|
|
857
|
+
if (canAutoSelectLocal(options)) try {
|
|
858
|
+
return {
|
|
859
|
+
...await createProvider("local"),
|
|
860
|
+
requestedProvider
|
|
861
|
+
};
|
|
862
|
+
} catch (err) {
|
|
863
|
+
localError = formatLocalSetupError(err);
|
|
864
|
+
}
|
|
865
|
+
for (const provider of REMOTE_EMBEDDING_PROVIDER_IDS) try {
|
|
866
|
+
return {
|
|
867
|
+
...await createProvider(provider),
|
|
868
|
+
requestedProvider
|
|
869
|
+
};
|
|
870
|
+
} catch (err) {
|
|
871
|
+
const message = formatPrimaryError(err, provider);
|
|
872
|
+
if (isMissingApiKeyError(err)) {
|
|
873
|
+
missingKeyErrors.push(message);
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
const wrapped = new Error(message);
|
|
877
|
+
wrapped.cause = err;
|
|
878
|
+
throw wrapped;
|
|
879
|
+
}
|
|
880
|
+
const details = [...missingKeyErrors, localError].filter(Boolean);
|
|
881
|
+
return {
|
|
882
|
+
provider: null,
|
|
883
|
+
requestedProvider,
|
|
884
|
+
providerUnavailableReason: details.length > 0 ? details.join("\n\n") : "No embeddings provider available."
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
try {
|
|
888
|
+
return {
|
|
889
|
+
...await createProvider(requestedProvider),
|
|
890
|
+
requestedProvider
|
|
891
|
+
};
|
|
892
|
+
} catch (primaryErr) {
|
|
893
|
+
const reason = formatPrimaryError(primaryErr, requestedProvider);
|
|
894
|
+
if (fallback && fallback !== "none" && fallback !== requestedProvider) try {
|
|
895
|
+
return {
|
|
896
|
+
...await createProvider(fallback),
|
|
897
|
+
requestedProvider,
|
|
898
|
+
fallbackFrom: requestedProvider,
|
|
899
|
+
fallbackReason: reason
|
|
900
|
+
};
|
|
901
|
+
} catch (fallbackErr) {
|
|
902
|
+
const combinedReason = `${reason}\n\nFallback to ${fallback} failed: ${formatErrorMessage(fallbackErr)}`;
|
|
903
|
+
if (isMissingApiKeyError(primaryErr) && isMissingApiKeyError(fallbackErr)) return {
|
|
904
|
+
provider: null,
|
|
905
|
+
requestedProvider,
|
|
906
|
+
fallbackFrom: requestedProvider,
|
|
907
|
+
fallbackReason: reason,
|
|
908
|
+
providerUnavailableReason: combinedReason
|
|
909
|
+
};
|
|
910
|
+
const wrapped = new Error(combinedReason);
|
|
911
|
+
wrapped.cause = fallbackErr;
|
|
912
|
+
throw wrapped;
|
|
913
|
+
}
|
|
914
|
+
if (isMissingApiKeyError(primaryErr)) return {
|
|
915
|
+
provider: null,
|
|
916
|
+
requestedProvider,
|
|
917
|
+
providerUnavailableReason: reason
|
|
918
|
+
};
|
|
919
|
+
const wrapped = new Error(reason);
|
|
920
|
+
wrapped.cause = primaryErr;
|
|
921
|
+
throw wrapped;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
function isNodeLlamaCppMissing(err) {
|
|
925
|
+
if (!(err instanceof Error)) return false;
|
|
926
|
+
if (err.code === "ERR_MODULE_NOT_FOUND") return err.message.includes("node-llama-cpp");
|
|
927
|
+
return false;
|
|
928
|
+
}
|
|
929
|
+
function formatLocalSetupError(err) {
|
|
930
|
+
const detail = formatErrorMessage(err);
|
|
931
|
+
const missing = isNodeLlamaCppMissing(err);
|
|
932
|
+
return [
|
|
933
|
+
"Local embeddings unavailable.",
|
|
934
|
+
missing ? "Reason: optional dependency node-llama-cpp is missing (or failed to install)." : detail ? `Reason: ${detail}` : void 0,
|
|
935
|
+
missing && detail ? `Detail: ${detail}` : null,
|
|
936
|
+
"To enable local embeddings:",
|
|
937
|
+
"1) Use Node 24 (recommended for installs/updates; Node 22 LTS, currently 22.16+, remains supported)",
|
|
938
|
+
missing ? "2) Reinstall OpenClaw (this should install node-llama-cpp): npm i -g openclaw@latest" : null,
|
|
939
|
+
"3) If you use pnpm: pnpm approve-builds (select node-llama-cpp), then pnpm rebuild node-llama-cpp",
|
|
940
|
+
...REMOTE_EMBEDDING_PROVIDER_IDS.map((provider) => `Or set agents.defaults.memorySearch.provider = "${provider}" (remote).`)
|
|
941
|
+
].filter(Boolean).join("\n");
|
|
942
|
+
}
|
|
943
|
+
//#endregion
|
|
944
|
+
//#region src/memory/mmr.ts
|
|
945
|
+
const DEFAULT_MMR_CONFIG = {
|
|
946
|
+
enabled: false,
|
|
947
|
+
lambda: .7
|
|
948
|
+
};
|
|
949
|
+
/**
|
|
950
|
+
* Tokenize text for Jaccard similarity computation.
|
|
951
|
+
* Extracts alphanumeric tokens and normalizes to lowercase.
|
|
952
|
+
*/
|
|
953
|
+
function tokenize(text) {
|
|
954
|
+
const tokens = text.toLowerCase().match(/[a-z0-9_]+/g) ?? [];
|
|
955
|
+
return new Set(tokens);
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Compute Jaccard similarity between two token sets.
|
|
959
|
+
* Returns a value in [0, 1] where 1 means identical sets.
|
|
960
|
+
*/
|
|
961
|
+
function jaccardSimilarity(setA, setB) {
|
|
962
|
+
if (setA.size === 0 && setB.size === 0) return 1;
|
|
963
|
+
if (setA.size === 0 || setB.size === 0) return 0;
|
|
964
|
+
let intersectionSize = 0;
|
|
965
|
+
const smaller = setA.size <= setB.size ? setA : setB;
|
|
966
|
+
const larger = setA.size <= setB.size ? setB : setA;
|
|
967
|
+
for (const token of smaller) if (larger.has(token)) intersectionSize++;
|
|
968
|
+
const unionSize = setA.size + setB.size - intersectionSize;
|
|
969
|
+
return unionSize === 0 ? 0 : intersectionSize / unionSize;
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* Compute the maximum similarity between an item and all selected items.
|
|
973
|
+
*/
|
|
974
|
+
function maxSimilarityToSelected(item, selectedItems, tokenCache) {
|
|
975
|
+
if (selectedItems.length === 0) return 0;
|
|
976
|
+
let maxSim = 0;
|
|
977
|
+
const itemTokens = tokenCache.get(item.id) ?? tokenize(item.content);
|
|
978
|
+
for (const selected of selectedItems) {
|
|
979
|
+
const sim = jaccardSimilarity(itemTokens, tokenCache.get(selected.id) ?? tokenize(selected.content));
|
|
980
|
+
if (sim > maxSim) maxSim = sim;
|
|
981
|
+
}
|
|
982
|
+
return maxSim;
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* Compute MMR score for a candidate item.
|
|
986
|
+
* MMR = λ * relevance - (1-λ) * max_similarity_to_selected
|
|
987
|
+
*/
|
|
988
|
+
function computeMMRScore(relevance, maxSimilarity, lambda) {
|
|
989
|
+
return lambda * relevance - (1 - lambda) * maxSimilarity;
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Re-rank items using Maximal Marginal Relevance (MMR).
|
|
993
|
+
*
|
|
994
|
+
* The algorithm iteratively selects items that balance relevance with diversity:
|
|
995
|
+
* 1. Start with the highest-scoring item
|
|
996
|
+
* 2. For each remaining slot, select the item that maximizes the MMR score
|
|
997
|
+
* 3. MMR score = λ * relevance - (1-λ) * max_similarity_to_already_selected
|
|
998
|
+
*
|
|
999
|
+
* @param items - Items to re-rank, must have score and content
|
|
1000
|
+
* @param config - MMR configuration (lambda, enabled)
|
|
1001
|
+
* @returns Re-ranked items in MMR order
|
|
1002
|
+
*/
|
|
1003
|
+
function mmrRerank(items, config = {}) {
|
|
1004
|
+
const { enabled = DEFAULT_MMR_CONFIG.enabled, lambda = DEFAULT_MMR_CONFIG.lambda } = config;
|
|
1005
|
+
if (!enabled || items.length <= 1) return [...items];
|
|
1006
|
+
const clampedLambda = Math.max(0, Math.min(1, lambda));
|
|
1007
|
+
if (clampedLambda === 1) return [...items].toSorted((a, b) => b.score - a.score);
|
|
1008
|
+
const tokenCache = /* @__PURE__ */ new Map();
|
|
1009
|
+
for (const item of items) tokenCache.set(item.id, tokenize(item.content));
|
|
1010
|
+
const maxScore = Math.max(...items.map((i) => i.score));
|
|
1011
|
+
const minScore = Math.min(...items.map((i) => i.score));
|
|
1012
|
+
const scoreRange = maxScore - minScore;
|
|
1013
|
+
const normalizeScore = (score) => {
|
|
1014
|
+
if (scoreRange === 0) return 1;
|
|
1015
|
+
return (score - minScore) / scoreRange;
|
|
1016
|
+
};
|
|
1017
|
+
const selected = [];
|
|
1018
|
+
const remaining = new Set(items);
|
|
1019
|
+
while (remaining.size > 0) {
|
|
1020
|
+
let bestItem = null;
|
|
1021
|
+
let bestMMRScore = -Infinity;
|
|
1022
|
+
for (const candidate of remaining) {
|
|
1023
|
+
const mmrScore = computeMMRScore(normalizeScore(candidate.score), maxSimilarityToSelected(candidate, selected, tokenCache), clampedLambda);
|
|
1024
|
+
if (mmrScore > bestMMRScore || mmrScore === bestMMRScore && candidate.score > (bestItem?.score ?? -Infinity)) {
|
|
1025
|
+
bestMMRScore = mmrScore;
|
|
1026
|
+
bestItem = candidate;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
if (bestItem) {
|
|
1030
|
+
selected.push(bestItem);
|
|
1031
|
+
remaining.delete(bestItem);
|
|
1032
|
+
} else break;
|
|
1033
|
+
}
|
|
1034
|
+
return selected;
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Apply MMR re-ranking to hybrid search results.
|
|
1038
|
+
* Adapts the generic MMR function to work with the hybrid search result format.
|
|
1039
|
+
*/
|
|
1040
|
+
function applyMMRToHybridResults(results, config = {}) {
|
|
1041
|
+
if (results.length === 0) return results;
|
|
1042
|
+
const itemById = /* @__PURE__ */ new Map();
|
|
1043
|
+
return mmrRerank(results.map((r, index) => {
|
|
1044
|
+
const id = `${r.path}:${r.startLine}:${index}`;
|
|
1045
|
+
itemById.set(id, r);
|
|
1046
|
+
return {
|
|
1047
|
+
id,
|
|
1048
|
+
score: r.score,
|
|
1049
|
+
content: r.snippet
|
|
1050
|
+
};
|
|
1051
|
+
}), config).map((item) => itemById.get(item.id));
|
|
1052
|
+
}
|
|
1053
|
+
//#endregion
|
|
1054
|
+
//#region src/memory/temporal-decay.ts
|
|
1055
|
+
const DEFAULT_TEMPORAL_DECAY_CONFIG = {
|
|
1056
|
+
enabled: false,
|
|
1057
|
+
halfLifeDays: 30
|
|
1058
|
+
};
|
|
1059
|
+
const DAY_MS = 1440 * 60 * 1e3;
|
|
1060
|
+
const DATED_MEMORY_PATH_RE = /(?:^|\/)memory\/(\d{4})-(\d{2})-(\d{2})\.md$/;
|
|
1061
|
+
function toDecayLambda(halfLifeDays) {
|
|
1062
|
+
if (!Number.isFinite(halfLifeDays) || halfLifeDays <= 0) return 0;
|
|
1063
|
+
return Math.LN2 / halfLifeDays;
|
|
1064
|
+
}
|
|
1065
|
+
function calculateTemporalDecayMultiplier(params) {
|
|
1066
|
+
const lambda = toDecayLambda(params.halfLifeDays);
|
|
1067
|
+
const clampedAge = Math.max(0, params.ageInDays);
|
|
1068
|
+
if (lambda <= 0 || !Number.isFinite(clampedAge)) return 1;
|
|
1069
|
+
return Math.exp(-lambda * clampedAge);
|
|
1070
|
+
}
|
|
1071
|
+
function applyTemporalDecayToScore(params) {
|
|
1072
|
+
return params.score * calculateTemporalDecayMultiplier(params);
|
|
1073
|
+
}
|
|
1074
|
+
function parseMemoryDateFromPath(filePath) {
|
|
1075
|
+
const normalized = filePath.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
1076
|
+
const match = DATED_MEMORY_PATH_RE.exec(normalized);
|
|
1077
|
+
if (!match) return null;
|
|
1078
|
+
const year = Number(match[1]);
|
|
1079
|
+
const month = Number(match[2]);
|
|
1080
|
+
const day = Number(match[3]);
|
|
1081
|
+
if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) return null;
|
|
1082
|
+
const timestamp = Date.UTC(year, month - 1, day);
|
|
1083
|
+
const parsed = new Date(timestamp);
|
|
1084
|
+
if (parsed.getUTCFullYear() !== year || parsed.getUTCMonth() !== month - 1 || parsed.getUTCDate() !== day) return null;
|
|
1085
|
+
return parsed;
|
|
1086
|
+
}
|
|
1087
|
+
function isEvergreenMemoryPath(filePath) {
|
|
1088
|
+
const normalized = filePath.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
1089
|
+
if (normalized === "MEMORY.md" || normalized === "memory.md") return true;
|
|
1090
|
+
if (!normalized.startsWith("memory/")) return false;
|
|
1091
|
+
return !DATED_MEMORY_PATH_RE.test(normalized);
|
|
1092
|
+
}
|
|
1093
|
+
async function extractTimestamp(params) {
|
|
1094
|
+
const fromPath = parseMemoryDateFromPath(params.filePath);
|
|
1095
|
+
if (fromPath) return fromPath;
|
|
1096
|
+
if (params.source === "memory" && isEvergreenMemoryPath(params.filePath)) return null;
|
|
1097
|
+
if (!params.workspaceDir) return null;
|
|
1098
|
+
const absolutePath = path.isAbsolute(params.filePath) ? params.filePath : path.resolve(params.workspaceDir, params.filePath);
|
|
1099
|
+
try {
|
|
1100
|
+
const stat = await fs$1.stat(absolutePath);
|
|
1101
|
+
if (!Number.isFinite(stat.mtimeMs)) return null;
|
|
1102
|
+
return new Date(stat.mtimeMs);
|
|
1103
|
+
} catch {
|
|
1104
|
+
return null;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
function ageInDaysFromTimestamp(timestamp, nowMs) {
|
|
1108
|
+
return Math.max(0, nowMs - timestamp.getTime()) / DAY_MS;
|
|
1109
|
+
}
|
|
1110
|
+
async function applyTemporalDecayToHybridResults(params) {
|
|
1111
|
+
const config = {
|
|
1112
|
+
...DEFAULT_TEMPORAL_DECAY_CONFIG,
|
|
1113
|
+
...params.temporalDecay
|
|
1114
|
+
};
|
|
1115
|
+
if (!config.enabled) return [...params.results];
|
|
1116
|
+
const nowMs = params.nowMs ?? Date.now();
|
|
1117
|
+
const timestampPromiseCache = /* @__PURE__ */ new Map();
|
|
1118
|
+
return Promise.all(params.results.map(async (entry) => {
|
|
1119
|
+
const cacheKey = `${entry.source}:${entry.path}`;
|
|
1120
|
+
let timestampPromise = timestampPromiseCache.get(cacheKey);
|
|
1121
|
+
if (!timestampPromise) {
|
|
1122
|
+
timestampPromise = extractTimestamp({
|
|
1123
|
+
filePath: entry.path,
|
|
1124
|
+
source: entry.source,
|
|
1125
|
+
workspaceDir: params.workspaceDir
|
|
1126
|
+
});
|
|
1127
|
+
timestampPromiseCache.set(cacheKey, timestampPromise);
|
|
1128
|
+
}
|
|
1129
|
+
const timestamp = await timestampPromise;
|
|
1130
|
+
if (!timestamp) return entry;
|
|
1131
|
+
const decayedScore = applyTemporalDecayToScore({
|
|
1132
|
+
score: entry.score,
|
|
1133
|
+
ageInDays: ageInDaysFromTimestamp(timestamp, nowMs),
|
|
1134
|
+
halfLifeDays: config.halfLifeDays
|
|
1135
|
+
});
|
|
1136
|
+
return {
|
|
1137
|
+
...entry,
|
|
1138
|
+
score: decayedScore
|
|
1139
|
+
};
|
|
1140
|
+
}));
|
|
1141
|
+
}
|
|
1142
|
+
//#endregion
|
|
1143
|
+
//#region src/memory/hybrid.ts
|
|
1144
|
+
function buildFtsQuery(raw) {
|
|
1145
|
+
const tokens = raw.match(/[\p{L}\p{N}_]+/gu)?.map((t) => t.trim()).filter(Boolean) ?? [];
|
|
1146
|
+
if (tokens.length === 0) return null;
|
|
1147
|
+
return tokens.map((t) => `"${t.replaceAll("\"", "")}"`).join(" AND ");
|
|
1148
|
+
}
|
|
1149
|
+
function bm25RankToScore(rank) {
|
|
1150
|
+
if (!Number.isFinite(rank)) return 1 / 1e3;
|
|
1151
|
+
if (rank < 0) {
|
|
1152
|
+
const relevance = -rank;
|
|
1153
|
+
return relevance / (1 + relevance);
|
|
1154
|
+
}
|
|
1155
|
+
return 1 / (1 + rank);
|
|
1156
|
+
}
|
|
1157
|
+
async function mergeHybridResults(params) {
|
|
1158
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1159
|
+
for (const r of params.vector) byId.set(r.id, {
|
|
1160
|
+
id: r.id,
|
|
1161
|
+
path: r.path,
|
|
1162
|
+
startLine: r.startLine,
|
|
1163
|
+
endLine: r.endLine,
|
|
1164
|
+
source: r.source,
|
|
1165
|
+
snippet: r.snippet,
|
|
1166
|
+
vectorScore: r.vectorScore,
|
|
1167
|
+
textScore: 0
|
|
1168
|
+
});
|
|
1169
|
+
for (const r of params.keyword) {
|
|
1170
|
+
const existing = byId.get(r.id);
|
|
1171
|
+
if (existing) {
|
|
1172
|
+
existing.textScore = r.textScore;
|
|
1173
|
+
if (r.snippet && r.snippet.length > 0) existing.snippet = r.snippet;
|
|
1174
|
+
} else byId.set(r.id, {
|
|
1175
|
+
id: r.id,
|
|
1176
|
+
path: r.path,
|
|
1177
|
+
startLine: r.startLine,
|
|
1178
|
+
endLine: r.endLine,
|
|
1179
|
+
source: r.source,
|
|
1180
|
+
snippet: r.snippet,
|
|
1181
|
+
vectorScore: 0,
|
|
1182
|
+
textScore: r.textScore
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
const sorted = (await applyTemporalDecayToHybridResults({
|
|
1186
|
+
results: Array.from(byId.values()).map((entry) => {
|
|
1187
|
+
const score = params.vectorWeight * entry.vectorScore + params.textWeight * entry.textScore;
|
|
1188
|
+
return {
|
|
1189
|
+
path: entry.path,
|
|
1190
|
+
startLine: entry.startLine,
|
|
1191
|
+
endLine: entry.endLine,
|
|
1192
|
+
score,
|
|
1193
|
+
snippet: entry.snippet,
|
|
1194
|
+
source: entry.source
|
|
1195
|
+
};
|
|
1196
|
+
}),
|
|
1197
|
+
temporalDecay: {
|
|
1198
|
+
...DEFAULT_TEMPORAL_DECAY_CONFIG,
|
|
1199
|
+
...params.temporalDecay
|
|
1200
|
+
},
|
|
1201
|
+
workspaceDir: params.workspaceDir,
|
|
1202
|
+
nowMs: params.nowMs
|
|
1203
|
+
})).toSorted((a, b) => b.score - a.score);
|
|
1204
|
+
const mmrConfig = {
|
|
1205
|
+
...DEFAULT_MMR_CONFIG,
|
|
1206
|
+
...params.mmr
|
|
1207
|
+
};
|
|
1208
|
+
if (mmrConfig.enabled) return applyMMRToHybridResults(sorted, mmrConfig);
|
|
1209
|
+
return sorted;
|
|
1210
|
+
}
|
|
1211
|
+
//#endregion
|
|
1212
|
+
//#region src/memory/batch-utils.ts
|
|
1213
|
+
function normalizeBatchBaseUrl(client) {
|
|
1214
|
+
return client.baseUrl?.replace(/\/$/, "") ?? "";
|
|
1215
|
+
}
|
|
1216
|
+
function buildBatchHeaders(client, params) {
|
|
1217
|
+
const headers = client.headers ? { ...client.headers } : {};
|
|
1218
|
+
if (params.json) {
|
|
1219
|
+
if (!headers["Content-Type"] && !headers["content-type"]) headers["Content-Type"] = "application/json";
|
|
1220
|
+
} else {
|
|
1221
|
+
delete headers["Content-Type"];
|
|
1222
|
+
delete headers["content-type"];
|
|
1223
|
+
}
|
|
1224
|
+
return headers;
|
|
1225
|
+
}
|
|
1226
|
+
function splitBatchRequests(requests, maxRequests) {
|
|
1227
|
+
if (requests.length <= maxRequests) return [requests];
|
|
1228
|
+
const groups = [];
|
|
1229
|
+
for (let i = 0; i < requests.length; i += maxRequests) groups.push(requests.slice(i, i + maxRequests));
|
|
1230
|
+
return groups;
|
|
1231
|
+
}
|
|
1232
|
+
//#endregion
|
|
1233
|
+
//#region src/memory/batch-runner.ts
|
|
1234
|
+
async function runEmbeddingBatchGroups(params) {
|
|
1235
|
+
if (params.requests.length === 0) return /* @__PURE__ */ new Map();
|
|
1236
|
+
const groups = splitBatchRequests(params.requests, params.maxRequests);
|
|
1237
|
+
const byCustomId = /* @__PURE__ */ new Map();
|
|
1238
|
+
const tasks = groups.map((group, groupIndex) => async () => {
|
|
1239
|
+
await params.runGroup({
|
|
1240
|
+
group,
|
|
1241
|
+
groupIndex,
|
|
1242
|
+
groups: groups.length,
|
|
1243
|
+
byCustomId
|
|
1244
|
+
});
|
|
1245
|
+
});
|
|
1246
|
+
params.debug?.(params.debugLabel, {
|
|
1247
|
+
requests: params.requests.length,
|
|
1248
|
+
groups: groups.length,
|
|
1249
|
+
wait: params.wait,
|
|
1250
|
+
concurrency: params.concurrency,
|
|
1251
|
+
pollIntervalMs: params.pollIntervalMs,
|
|
1252
|
+
timeoutMs: params.timeoutMs
|
|
1253
|
+
});
|
|
1254
|
+
await runWithConcurrency(tasks, params.concurrency);
|
|
1255
|
+
return byCustomId;
|
|
1256
|
+
}
|
|
1257
|
+
function buildEmbeddingBatchGroupOptions(params, options) {
|
|
1258
|
+
return {
|
|
1259
|
+
requests: params.requests,
|
|
1260
|
+
maxRequests: options.maxRequests,
|
|
1261
|
+
wait: params.wait,
|
|
1262
|
+
pollIntervalMs: params.pollIntervalMs,
|
|
1263
|
+
timeoutMs: params.timeoutMs,
|
|
1264
|
+
concurrency: params.concurrency,
|
|
1265
|
+
debug: params.debug,
|
|
1266
|
+
debugLabel: options.debugLabel
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
//#endregion
|
|
1270
|
+
//#region src/memory/batch-gemini.ts
|
|
1271
|
+
const GEMINI_BATCH_MAX_REQUESTS = 5e4;
|
|
1272
|
+
function getGeminiUploadUrl(baseUrl) {
|
|
1273
|
+
if (baseUrl.includes("/v1beta")) return baseUrl.replace(/\/v1beta\/?$/, "/upload/v1beta");
|
|
1274
|
+
return `${baseUrl.replace(/\/$/, "")}/upload`;
|
|
1275
|
+
}
|
|
1276
|
+
function buildGeminiUploadBody(params) {
|
|
1277
|
+
const boundary = `openclaw-${hashText(params.displayName)}`;
|
|
1278
|
+
const jsonPart = JSON.stringify({ file: {
|
|
1279
|
+
displayName: params.displayName,
|
|
1280
|
+
mimeType: "application/jsonl"
|
|
1281
|
+
} });
|
|
1282
|
+
const delimiter = `--${boundary}\r\n`;
|
|
1283
|
+
const closeDelimiter = `--${boundary}--\r\n`;
|
|
1284
|
+
const parts = [
|
|
1285
|
+
`${delimiter}Content-Type: application/json; charset=UTF-8\r\n\r\n${jsonPart}\r\n`,
|
|
1286
|
+
`${delimiter}Content-Type: application/jsonl; charset=UTF-8\r\n\r\n${params.jsonl}\r\n`,
|
|
1287
|
+
closeDelimiter
|
|
1288
|
+
];
|
|
1289
|
+
return {
|
|
1290
|
+
body: new Blob([parts.join("")], { type: "multipart/related" }),
|
|
1291
|
+
contentType: `multipart/related; boundary=${boundary}`
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
async function submitGeminiBatch(params) {
|
|
1295
|
+
const baseUrl = normalizeBatchBaseUrl(params.gemini);
|
|
1296
|
+
const uploadPayload = buildGeminiUploadBody({
|
|
1297
|
+
jsonl: params.requests.map((request) => JSON.stringify({
|
|
1298
|
+
key: request.custom_id,
|
|
1299
|
+
request: request.request
|
|
1300
|
+
})).join("\n"),
|
|
1301
|
+
displayName: `memory-embeddings-${hashText(String(Date.now()))}`
|
|
1302
|
+
});
|
|
1303
|
+
const uploadUrl = `${getGeminiUploadUrl(baseUrl)}/files?uploadType=multipart`;
|
|
1304
|
+
debugEmbeddingsLog("memory embeddings: gemini batch upload", {
|
|
1305
|
+
uploadUrl,
|
|
1306
|
+
baseUrl,
|
|
1307
|
+
requests: params.requests.length
|
|
1308
|
+
});
|
|
1309
|
+
const filePayload = await withRemoteHttpResponse({
|
|
1310
|
+
url: uploadUrl,
|
|
1311
|
+
ssrfPolicy: params.gemini.ssrfPolicy,
|
|
1312
|
+
init: {
|
|
1313
|
+
method: "POST",
|
|
1314
|
+
headers: {
|
|
1315
|
+
...buildBatchHeaders(params.gemini, { json: false }),
|
|
1316
|
+
"Content-Type": uploadPayload.contentType
|
|
1317
|
+
},
|
|
1318
|
+
body: uploadPayload.body
|
|
1319
|
+
},
|
|
1320
|
+
onResponse: async (fileRes) => {
|
|
1321
|
+
if (!fileRes.ok) {
|
|
1322
|
+
const text = await fileRes.text();
|
|
1323
|
+
throw new Error(`gemini batch file upload failed: ${fileRes.status} ${text}`);
|
|
1324
|
+
}
|
|
1325
|
+
return await fileRes.json();
|
|
1326
|
+
}
|
|
1327
|
+
});
|
|
1328
|
+
const fileId = filePayload.name ?? filePayload.file?.name;
|
|
1329
|
+
if (!fileId) throw new Error("gemini batch file upload failed: missing file id");
|
|
1330
|
+
const batchBody = { batch: {
|
|
1331
|
+
displayName: `memory-embeddings-${params.agentId}`,
|
|
1332
|
+
inputConfig: { file_name: fileId }
|
|
1333
|
+
} };
|
|
1334
|
+
const batchEndpoint = `${baseUrl}/${params.gemini.modelPath}:asyncBatchEmbedContent`;
|
|
1335
|
+
debugEmbeddingsLog("memory embeddings: gemini batch create", {
|
|
1336
|
+
batchEndpoint,
|
|
1337
|
+
fileId
|
|
1338
|
+
});
|
|
1339
|
+
return await withRemoteHttpResponse({
|
|
1340
|
+
url: batchEndpoint,
|
|
1341
|
+
ssrfPolicy: params.gemini.ssrfPolicy,
|
|
1342
|
+
init: {
|
|
1343
|
+
method: "POST",
|
|
1344
|
+
headers: buildBatchHeaders(params.gemini, { json: true }),
|
|
1345
|
+
body: JSON.stringify(batchBody)
|
|
1346
|
+
},
|
|
1347
|
+
onResponse: async (batchRes) => {
|
|
1348
|
+
if (batchRes.ok) return await batchRes.json();
|
|
1349
|
+
const text = await batchRes.text();
|
|
1350
|
+
if (batchRes.status === 404) throw new Error("gemini batch create failed: 404 (asyncBatchEmbedContent not available for this model/baseUrl). Disable remote.batch.enabled or switch providers.");
|
|
1351
|
+
throw new Error(`gemini batch create failed: ${batchRes.status} ${text}`);
|
|
1352
|
+
}
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
async function fetchGeminiBatchStatus(params) {
|
|
1356
|
+
const statusUrl = `${normalizeBatchBaseUrl(params.gemini)}/${params.batchName.startsWith("batches/") ? params.batchName : `batches/${params.batchName}`}`;
|
|
1357
|
+
debugEmbeddingsLog("memory embeddings: gemini batch status", { statusUrl });
|
|
1358
|
+
return await withRemoteHttpResponse({
|
|
1359
|
+
url: statusUrl,
|
|
1360
|
+
ssrfPolicy: params.gemini.ssrfPolicy,
|
|
1361
|
+
init: { headers: buildBatchHeaders(params.gemini, { json: true }) },
|
|
1362
|
+
onResponse: async (res) => {
|
|
1363
|
+
if (!res.ok) {
|
|
1364
|
+
const text = await res.text();
|
|
1365
|
+
throw new Error(`gemini batch status failed: ${res.status} ${text}`);
|
|
1366
|
+
}
|
|
1367
|
+
return await res.json();
|
|
1368
|
+
}
|
|
1369
|
+
});
|
|
1370
|
+
}
|
|
1371
|
+
async function fetchGeminiFileContent(params) {
|
|
1372
|
+
const downloadUrl = `${normalizeBatchBaseUrl(params.gemini)}/${params.fileId.startsWith("files/") ? params.fileId : `files/${params.fileId}`}:download`;
|
|
1373
|
+
debugEmbeddingsLog("memory embeddings: gemini batch download", { downloadUrl });
|
|
1374
|
+
return await withRemoteHttpResponse({
|
|
1375
|
+
url: downloadUrl,
|
|
1376
|
+
ssrfPolicy: params.gemini.ssrfPolicy,
|
|
1377
|
+
init: { headers: buildBatchHeaders(params.gemini, { json: true }) },
|
|
1378
|
+
onResponse: async (res) => {
|
|
1379
|
+
if (!res.ok) {
|
|
1380
|
+
const text = await res.text();
|
|
1381
|
+
throw new Error(`gemini batch file content failed: ${res.status} ${text}`);
|
|
1382
|
+
}
|
|
1383
|
+
return await res.text();
|
|
1384
|
+
}
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
function parseGeminiBatchOutput(text) {
|
|
1388
|
+
if (!text.trim()) return [];
|
|
1389
|
+
return text.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line));
|
|
1390
|
+
}
|
|
1391
|
+
async function waitForGeminiBatch(params) {
|
|
1392
|
+
const start = Date.now();
|
|
1393
|
+
let current = params.initial;
|
|
1394
|
+
while (true) {
|
|
1395
|
+
const status = current ?? await fetchGeminiBatchStatus({
|
|
1396
|
+
gemini: params.gemini,
|
|
1397
|
+
batchName: params.batchName
|
|
1398
|
+
});
|
|
1399
|
+
const state = status.state ?? "UNKNOWN";
|
|
1400
|
+
if ([
|
|
1401
|
+
"SUCCEEDED",
|
|
1402
|
+
"COMPLETED",
|
|
1403
|
+
"DONE"
|
|
1404
|
+
].includes(state)) {
|
|
1405
|
+
const outputFileId = status.outputConfig?.file ?? status.outputConfig?.fileId ?? status.metadata?.output?.responsesFile;
|
|
1406
|
+
if (!outputFileId) throw new Error(`gemini batch ${params.batchName} completed without output file`);
|
|
1407
|
+
return { outputFileId };
|
|
1408
|
+
}
|
|
1409
|
+
if ([
|
|
1410
|
+
"FAILED",
|
|
1411
|
+
"CANCELLED",
|
|
1412
|
+
"CANCELED",
|
|
1413
|
+
"EXPIRED"
|
|
1414
|
+
].includes(state)) {
|
|
1415
|
+
const message = status.error?.message ?? "unknown error";
|
|
1416
|
+
throw new Error(`gemini batch ${params.batchName} ${state}: ${message}`);
|
|
1417
|
+
}
|
|
1418
|
+
if (!params.wait) throw new Error(`gemini batch ${params.batchName} still ${state}; wait disabled`);
|
|
1419
|
+
if (Date.now() - start > params.timeoutMs) throw new Error(`gemini batch ${params.batchName} timed out after ${params.timeoutMs}ms`);
|
|
1420
|
+
params.debug?.(`gemini batch ${params.batchName} ${state}; waiting ${params.pollIntervalMs}ms`);
|
|
1421
|
+
await new Promise((resolve) => setTimeout(resolve, params.pollIntervalMs));
|
|
1422
|
+
current = void 0;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
async function runGeminiEmbeddingBatches(params) {
|
|
1426
|
+
return await runEmbeddingBatchGroups({
|
|
1427
|
+
...buildEmbeddingBatchGroupOptions(params, {
|
|
1428
|
+
maxRequests: GEMINI_BATCH_MAX_REQUESTS,
|
|
1429
|
+
debugLabel: "memory embeddings: gemini batch submit"
|
|
1430
|
+
}),
|
|
1431
|
+
runGroup: async ({ group, groupIndex, groups, byCustomId }) => {
|
|
1432
|
+
const batchInfo = await submitGeminiBatch({
|
|
1433
|
+
gemini: params.gemini,
|
|
1434
|
+
requests: group,
|
|
1435
|
+
agentId: params.agentId
|
|
1436
|
+
});
|
|
1437
|
+
const batchName = batchInfo.name ?? "";
|
|
1438
|
+
if (!batchName) throw new Error("gemini batch create failed: missing batch name");
|
|
1439
|
+
params.debug?.("memory embeddings: gemini batch created", {
|
|
1440
|
+
batchName,
|
|
1441
|
+
state: batchInfo.state,
|
|
1442
|
+
group: groupIndex + 1,
|
|
1443
|
+
groups,
|
|
1444
|
+
requests: group.length
|
|
1445
|
+
});
|
|
1446
|
+
if (!params.wait && batchInfo.state && ![
|
|
1447
|
+
"SUCCEEDED",
|
|
1448
|
+
"COMPLETED",
|
|
1449
|
+
"DONE"
|
|
1450
|
+
].includes(batchInfo.state)) throw new Error(`gemini batch ${batchName} submitted; enable remote.batch.wait to await completion`);
|
|
1451
|
+
const completed = batchInfo.state && [
|
|
1452
|
+
"SUCCEEDED",
|
|
1453
|
+
"COMPLETED",
|
|
1454
|
+
"DONE"
|
|
1455
|
+
].includes(batchInfo.state) ? { outputFileId: batchInfo.outputConfig?.file ?? batchInfo.outputConfig?.fileId ?? batchInfo.metadata?.output?.responsesFile ?? "" } : await waitForGeminiBatch({
|
|
1456
|
+
gemini: params.gemini,
|
|
1457
|
+
batchName,
|
|
1458
|
+
wait: params.wait,
|
|
1459
|
+
pollIntervalMs: params.pollIntervalMs,
|
|
1460
|
+
timeoutMs: params.timeoutMs,
|
|
1461
|
+
debug: params.debug,
|
|
1462
|
+
initial: batchInfo
|
|
1463
|
+
});
|
|
1464
|
+
if (!completed.outputFileId) throw new Error(`gemini batch ${batchName} completed without output file`);
|
|
1465
|
+
const outputLines = parseGeminiBatchOutput(await fetchGeminiFileContent({
|
|
1466
|
+
gemini: params.gemini,
|
|
1467
|
+
fileId: completed.outputFileId
|
|
1468
|
+
}));
|
|
1469
|
+
const errors = [];
|
|
1470
|
+
const remaining = new Set(group.map((request) => request.custom_id));
|
|
1471
|
+
for (const line of outputLines) {
|
|
1472
|
+
const customId = line.key ?? line.custom_id ?? line.request_id;
|
|
1473
|
+
if (!customId) continue;
|
|
1474
|
+
remaining.delete(customId);
|
|
1475
|
+
if (line.error?.message) {
|
|
1476
|
+
errors.push(`${customId}: ${line.error.message}`);
|
|
1477
|
+
continue;
|
|
1478
|
+
}
|
|
1479
|
+
if (line.response?.error?.message) {
|
|
1480
|
+
errors.push(`${customId}: ${line.response.error.message}`);
|
|
1481
|
+
continue;
|
|
1482
|
+
}
|
|
1483
|
+
const embedding = sanitizeAndNormalizeEmbedding(line.embedding?.values ?? line.response?.embedding?.values ?? []);
|
|
1484
|
+
if (embedding.length === 0) {
|
|
1485
|
+
errors.push(`${customId}: empty embedding`);
|
|
1486
|
+
continue;
|
|
1487
|
+
}
|
|
1488
|
+
byCustomId.set(customId, embedding);
|
|
1489
|
+
}
|
|
1490
|
+
if (errors.length > 0) throw new Error(`gemini batch ${batchName} failed: ${errors.join("; ")}`);
|
|
1491
|
+
if (remaining.size > 0) throw new Error(`gemini batch ${batchName} missing ${remaining.size} embedding responses`);
|
|
1492
|
+
}
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
//#endregion
|
|
1496
|
+
//#region src/memory/batch-error-utils.ts
|
|
1497
|
+
function getResponseErrorMessage(line) {
|
|
1498
|
+
const body = line?.response?.body;
|
|
1499
|
+
if (typeof body === "string") return body || void 0;
|
|
1500
|
+
if (!body || typeof body !== "object") return;
|
|
1501
|
+
return typeof body.error?.message === "string" ? body.error.message : void 0;
|
|
1502
|
+
}
|
|
1503
|
+
function extractBatchErrorMessage(lines) {
|
|
1504
|
+
const first = lines.find((line) => line.error?.message || getResponseErrorMessage(line));
|
|
1505
|
+
return first?.error?.message ?? getResponseErrorMessage(first);
|
|
1506
|
+
}
|
|
1507
|
+
function formatUnavailableBatchError(err) {
|
|
1508
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1509
|
+
return message ? `error file unavailable: ${message}` : void 0;
|
|
1510
|
+
}
|
|
1511
|
+
//#endregion
|
|
1512
|
+
//#region src/memory/batch-http.ts
|
|
1513
|
+
async function postJsonWithRetry(params) {
|
|
1514
|
+
return await retryAsync(async () => {
|
|
1515
|
+
return await postJson({
|
|
1516
|
+
url: params.url,
|
|
1517
|
+
headers: params.headers,
|
|
1518
|
+
ssrfPolicy: params.ssrfPolicy,
|
|
1519
|
+
body: params.body,
|
|
1520
|
+
errorPrefix: params.errorPrefix,
|
|
1521
|
+
attachStatus: true,
|
|
1522
|
+
parse: async (payload) => payload
|
|
1523
|
+
});
|
|
1524
|
+
}, {
|
|
1525
|
+
attempts: 3,
|
|
1526
|
+
minDelayMs: 300,
|
|
1527
|
+
maxDelayMs: 2e3,
|
|
1528
|
+
jitter: .2,
|
|
1529
|
+
shouldRetry: (err) => {
|
|
1530
|
+
const status = err.status;
|
|
1531
|
+
return status === 429 || typeof status === "number" && status >= 500;
|
|
1532
|
+
}
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
//#endregion
|
|
1536
|
+
//#region src/memory/batch-output.ts
|
|
1537
|
+
function applyEmbeddingBatchOutputLine(params) {
|
|
1538
|
+
const customId = params.line.custom_id;
|
|
1539
|
+
if (!customId) return;
|
|
1540
|
+
params.remaining.delete(customId);
|
|
1541
|
+
const errorMessage = params.line.error?.message;
|
|
1542
|
+
if (errorMessage) {
|
|
1543
|
+
params.errors.push(`${customId}: ${errorMessage}`);
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
const response = params.line.response;
|
|
1547
|
+
if ((response?.status_code ?? 0) >= 400) {
|
|
1548
|
+
const messageFromObject = response?.body && typeof response.body === "object" ? response.body.error?.message : void 0;
|
|
1549
|
+
const messageFromString = typeof response?.body === "string" ? response.body : void 0;
|
|
1550
|
+
params.errors.push(`${customId}: ${messageFromObject ?? messageFromString ?? "unknown error"}`);
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
const embedding = (response?.body && typeof response.body === "object" ? response.body.data ?? [] : [])[0]?.embedding ?? [];
|
|
1554
|
+
if (embedding.length === 0) {
|
|
1555
|
+
params.errors.push(`${customId}: empty embedding`);
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
params.byCustomId.set(customId, embedding);
|
|
1559
|
+
}
|
|
1560
|
+
//#endregion
|
|
1561
|
+
//#region src/memory/batch-status.ts
|
|
1562
|
+
const TERMINAL_FAILURE_STATES = new Set([
|
|
1563
|
+
"failed",
|
|
1564
|
+
"expired",
|
|
1565
|
+
"cancelled",
|
|
1566
|
+
"canceled"
|
|
1567
|
+
]);
|
|
1568
|
+
function resolveBatchCompletionFromStatus(params) {
|
|
1569
|
+
if (!params.status.output_file_id) throw new Error(`${params.provider} batch ${params.batchId} completed without output file`);
|
|
1570
|
+
return {
|
|
1571
|
+
outputFileId: params.status.output_file_id,
|
|
1572
|
+
errorFileId: params.status.error_file_id ?? void 0
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
async function throwIfBatchTerminalFailure(params) {
|
|
1576
|
+
const state = params.status.status ?? "unknown";
|
|
1577
|
+
if (!TERMINAL_FAILURE_STATES.has(state)) return;
|
|
1578
|
+
const detail = params.status.error_file_id ? await params.readError(params.status.error_file_id) : void 0;
|
|
1579
|
+
const suffix = detail ? `: ${detail}` : "";
|
|
1580
|
+
throw new Error(`${params.provider} batch ${params.status.id ?? "<unknown>"} ${state}${suffix}`);
|
|
1581
|
+
}
|
|
1582
|
+
async function resolveCompletedBatchResult(params) {
|
|
1583
|
+
const batchId = params.status.id ?? "<unknown>";
|
|
1584
|
+
if (!params.wait && params.status.status !== "completed") throw new Error(`${params.provider} batch ${batchId} submitted; enable remote.batch.wait to await completion`);
|
|
1585
|
+
const completed = params.status.status === "completed" ? resolveBatchCompletionFromStatus({
|
|
1586
|
+
provider: params.provider,
|
|
1587
|
+
batchId,
|
|
1588
|
+
status: params.status
|
|
1589
|
+
}) : await params.waitForBatch();
|
|
1590
|
+
if (!completed.outputFileId) throw new Error(`${params.provider} batch ${batchId} completed without output file`);
|
|
1591
|
+
return completed;
|
|
1592
|
+
}
|
|
1593
|
+
//#endregion
|
|
1594
|
+
//#region src/memory/batch-provider-common.ts
|
|
1595
|
+
const EMBEDDING_BATCH_ENDPOINT = "/v1/embeddings";
|
|
1596
|
+
//#endregion
|
|
1597
|
+
//#region src/memory/batch-upload.ts
|
|
1598
|
+
async function uploadBatchJsonlFile(params) {
|
|
1599
|
+
const baseUrl = normalizeBatchBaseUrl(params.client);
|
|
1600
|
+
const jsonl = params.requests.map((request) => JSON.stringify(request)).join("\n");
|
|
1601
|
+
const form = new FormData();
|
|
1602
|
+
form.append("purpose", "batch");
|
|
1603
|
+
form.append("file", new Blob([jsonl], { type: "application/jsonl" }), `memory-embeddings.${hashText(String(Date.now()))}.jsonl`);
|
|
1604
|
+
const filePayload = await withRemoteHttpResponse({
|
|
1605
|
+
url: `${baseUrl}/files`,
|
|
1606
|
+
ssrfPolicy: params.client.ssrfPolicy,
|
|
1607
|
+
init: {
|
|
1608
|
+
method: "POST",
|
|
1609
|
+
headers: buildBatchHeaders(params.client, { json: false }),
|
|
1610
|
+
body: form
|
|
1611
|
+
},
|
|
1612
|
+
onResponse: async (fileRes) => {
|
|
1613
|
+
if (!fileRes.ok) {
|
|
1614
|
+
const text = await fileRes.text();
|
|
1615
|
+
throw new Error(`${params.errorPrefix}: ${fileRes.status} ${text}`);
|
|
1616
|
+
}
|
|
1617
|
+
return await fileRes.json();
|
|
1618
|
+
}
|
|
1619
|
+
});
|
|
1620
|
+
if (!filePayload.id) throw new Error(`${params.errorPrefix}: missing file id`);
|
|
1621
|
+
return filePayload.id;
|
|
1622
|
+
}
|
|
1623
|
+
//#endregion
|
|
1624
|
+
//#region src/memory/batch-openai.ts
|
|
1625
|
+
const OPENAI_BATCH_ENDPOINT = EMBEDDING_BATCH_ENDPOINT;
|
|
1626
|
+
const OPENAI_BATCH_COMPLETION_WINDOW = "24h";
|
|
1627
|
+
const OPENAI_BATCH_MAX_REQUESTS = 5e4;
|
|
1628
|
+
async function submitOpenAiBatch(params) {
|
|
1629
|
+
const baseUrl = normalizeBatchBaseUrl(params.openAi);
|
|
1630
|
+
const inputFileId = await uploadBatchJsonlFile({
|
|
1631
|
+
client: params.openAi,
|
|
1632
|
+
requests: params.requests,
|
|
1633
|
+
errorPrefix: "openai batch file upload failed"
|
|
1634
|
+
});
|
|
1635
|
+
return await postJsonWithRetry({
|
|
1636
|
+
url: `${baseUrl}/batches`,
|
|
1637
|
+
headers: buildBatchHeaders(params.openAi, { json: true }),
|
|
1638
|
+
ssrfPolicy: params.openAi.ssrfPolicy,
|
|
1639
|
+
body: {
|
|
1640
|
+
input_file_id: inputFileId,
|
|
1641
|
+
endpoint: OPENAI_BATCH_ENDPOINT,
|
|
1642
|
+
completion_window: OPENAI_BATCH_COMPLETION_WINDOW,
|
|
1643
|
+
metadata: {
|
|
1644
|
+
source: "openclaw-memory",
|
|
1645
|
+
agent: params.agentId
|
|
1646
|
+
}
|
|
1647
|
+
},
|
|
1648
|
+
errorPrefix: "openai batch create failed"
|
|
1649
|
+
});
|
|
1650
|
+
}
|
|
1651
|
+
async function fetchOpenAiBatchStatus(params) {
|
|
1652
|
+
return await fetchOpenAiBatchResource({
|
|
1653
|
+
openAi: params.openAi,
|
|
1654
|
+
path: `/batches/${params.batchId}`,
|
|
1655
|
+
errorPrefix: "openai batch status",
|
|
1656
|
+
parse: async (res) => await res.json()
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1659
|
+
async function fetchOpenAiFileContent(params) {
|
|
1660
|
+
return await fetchOpenAiBatchResource({
|
|
1661
|
+
openAi: params.openAi,
|
|
1662
|
+
path: `/files/${params.fileId}/content`,
|
|
1663
|
+
errorPrefix: "openai batch file content",
|
|
1664
|
+
parse: async (res) => await res.text()
|
|
1665
|
+
});
|
|
1666
|
+
}
|
|
1667
|
+
async function fetchOpenAiBatchResource(params) {
|
|
1668
|
+
return await withRemoteHttpResponse({
|
|
1669
|
+
url: `${normalizeBatchBaseUrl(params.openAi)}${params.path}`,
|
|
1670
|
+
ssrfPolicy: params.openAi.ssrfPolicy,
|
|
1671
|
+
init: { headers: buildBatchHeaders(params.openAi, { json: true }) },
|
|
1672
|
+
onResponse: async (res) => {
|
|
1673
|
+
if (!res.ok) {
|
|
1674
|
+
const text = await res.text();
|
|
1675
|
+
throw new Error(`${params.errorPrefix} failed: ${res.status} ${text}`);
|
|
1676
|
+
}
|
|
1677
|
+
return await params.parse(res);
|
|
1678
|
+
}
|
|
1679
|
+
});
|
|
1680
|
+
}
|
|
1681
|
+
function parseOpenAiBatchOutput(text) {
|
|
1682
|
+
if (!text.trim()) return [];
|
|
1683
|
+
return text.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line));
|
|
1684
|
+
}
|
|
1685
|
+
async function readOpenAiBatchError(params) {
|
|
1686
|
+
try {
|
|
1687
|
+
return extractBatchErrorMessage(parseOpenAiBatchOutput(await fetchOpenAiFileContent({
|
|
1688
|
+
openAi: params.openAi,
|
|
1689
|
+
fileId: params.errorFileId
|
|
1690
|
+
})));
|
|
1691
|
+
} catch (err) {
|
|
1692
|
+
return formatUnavailableBatchError(err);
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
async function waitForOpenAiBatch(params) {
|
|
1696
|
+
const start = Date.now();
|
|
1697
|
+
let current = params.initial;
|
|
1698
|
+
while (true) {
|
|
1699
|
+
const status = current ?? await fetchOpenAiBatchStatus({
|
|
1700
|
+
openAi: params.openAi,
|
|
1701
|
+
batchId: params.batchId
|
|
1702
|
+
});
|
|
1703
|
+
const state = status.status ?? "unknown";
|
|
1704
|
+
if (state === "completed") return resolveBatchCompletionFromStatus({
|
|
1705
|
+
provider: "openai",
|
|
1706
|
+
batchId: params.batchId,
|
|
1707
|
+
status
|
|
1708
|
+
});
|
|
1709
|
+
await throwIfBatchTerminalFailure({
|
|
1710
|
+
provider: "openai",
|
|
1711
|
+
status: {
|
|
1712
|
+
...status,
|
|
1713
|
+
id: params.batchId
|
|
1714
|
+
},
|
|
1715
|
+
readError: async (errorFileId) => await readOpenAiBatchError({
|
|
1716
|
+
openAi: params.openAi,
|
|
1717
|
+
errorFileId
|
|
1718
|
+
})
|
|
1719
|
+
});
|
|
1720
|
+
if (!params.wait) throw new Error(`openai batch ${params.batchId} still ${state}; wait disabled`);
|
|
1721
|
+
if (Date.now() - start > params.timeoutMs) throw new Error(`openai batch ${params.batchId} timed out after ${params.timeoutMs}ms`);
|
|
1722
|
+
params.debug?.(`openai batch ${params.batchId} ${state}; waiting ${params.pollIntervalMs}ms`);
|
|
1723
|
+
await new Promise((resolve) => setTimeout(resolve, params.pollIntervalMs));
|
|
1724
|
+
current = void 0;
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
async function runOpenAiEmbeddingBatches(params) {
|
|
1728
|
+
return await runEmbeddingBatchGroups({
|
|
1729
|
+
...buildEmbeddingBatchGroupOptions(params, {
|
|
1730
|
+
maxRequests: OPENAI_BATCH_MAX_REQUESTS,
|
|
1731
|
+
debugLabel: "memory embeddings: openai batch submit"
|
|
1732
|
+
}),
|
|
1733
|
+
runGroup: async ({ group, groupIndex, groups, byCustomId }) => {
|
|
1734
|
+
const batchInfo = await submitOpenAiBatch({
|
|
1735
|
+
openAi: params.openAi,
|
|
1736
|
+
requests: group,
|
|
1737
|
+
agentId: params.agentId
|
|
1738
|
+
});
|
|
1739
|
+
if (!batchInfo.id) throw new Error("openai batch create failed: missing batch id");
|
|
1740
|
+
const batchId = batchInfo.id;
|
|
1741
|
+
params.debug?.("memory embeddings: openai batch created", {
|
|
1742
|
+
batchId: batchInfo.id,
|
|
1743
|
+
status: batchInfo.status,
|
|
1744
|
+
group: groupIndex + 1,
|
|
1745
|
+
groups,
|
|
1746
|
+
requests: group.length
|
|
1747
|
+
});
|
|
1748
|
+
const completed = await resolveCompletedBatchResult({
|
|
1749
|
+
provider: "openai",
|
|
1750
|
+
status: batchInfo,
|
|
1751
|
+
wait: params.wait,
|
|
1752
|
+
waitForBatch: async () => await waitForOpenAiBatch({
|
|
1753
|
+
openAi: params.openAi,
|
|
1754
|
+
batchId,
|
|
1755
|
+
wait: params.wait,
|
|
1756
|
+
pollIntervalMs: params.pollIntervalMs,
|
|
1757
|
+
timeoutMs: params.timeoutMs,
|
|
1758
|
+
debug: params.debug,
|
|
1759
|
+
initial: batchInfo
|
|
1760
|
+
})
|
|
1761
|
+
});
|
|
1762
|
+
const outputLines = parseOpenAiBatchOutput(await fetchOpenAiFileContent({
|
|
1763
|
+
openAi: params.openAi,
|
|
1764
|
+
fileId: completed.outputFileId
|
|
1765
|
+
}));
|
|
1766
|
+
const errors = [];
|
|
1767
|
+
const remaining = new Set(group.map((request) => request.custom_id));
|
|
1768
|
+
for (const line of outputLines) applyEmbeddingBatchOutputLine({
|
|
1769
|
+
line,
|
|
1770
|
+
remaining,
|
|
1771
|
+
errors,
|
|
1772
|
+
byCustomId
|
|
1773
|
+
});
|
|
1774
|
+
if (errors.length > 0) throw new Error(`openai batch ${batchInfo.id} failed: ${errors.join("; ")}`);
|
|
1775
|
+
if (remaining.size > 0) throw new Error(`openai batch ${batchInfo.id} missing ${remaining.size} embedding responses`);
|
|
1776
|
+
}
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
//#endregion
|
|
1780
|
+
//#region src/memory/batch-voyage.ts
|
|
1781
|
+
const VOYAGE_BATCH_ENDPOINT = EMBEDDING_BATCH_ENDPOINT;
|
|
1782
|
+
const VOYAGE_BATCH_COMPLETION_WINDOW = "12h";
|
|
1783
|
+
const VOYAGE_BATCH_MAX_REQUESTS = 5e4;
|
|
1784
|
+
async function assertVoyageResponseOk(res, context) {
|
|
1785
|
+
if (!res.ok) {
|
|
1786
|
+
const text = await res.text();
|
|
1787
|
+
throw new Error(`${context}: ${res.status} ${text}`);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
function buildVoyageBatchRequest(params) {
|
|
1791
|
+
return {
|
|
1792
|
+
url: `${normalizeBatchBaseUrl(params.client)}/${params.path}`,
|
|
1793
|
+
ssrfPolicy: params.client.ssrfPolicy,
|
|
1794
|
+
init: { headers: buildBatchHeaders(params.client, { json: true }) },
|
|
1795
|
+
onResponse: params.onResponse
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
async function submitVoyageBatch(params) {
|
|
1799
|
+
const baseUrl = normalizeBatchBaseUrl(params.client);
|
|
1800
|
+
const inputFileId = await uploadBatchJsonlFile({
|
|
1801
|
+
client: params.client,
|
|
1802
|
+
requests: params.requests,
|
|
1803
|
+
errorPrefix: "voyage batch file upload failed"
|
|
1804
|
+
});
|
|
1805
|
+
return await postJsonWithRetry({
|
|
1806
|
+
url: `${baseUrl}/batches`,
|
|
1807
|
+
headers: buildBatchHeaders(params.client, { json: true }),
|
|
1808
|
+
ssrfPolicy: params.client.ssrfPolicy,
|
|
1809
|
+
body: {
|
|
1810
|
+
input_file_id: inputFileId,
|
|
1811
|
+
endpoint: VOYAGE_BATCH_ENDPOINT,
|
|
1812
|
+
completion_window: VOYAGE_BATCH_COMPLETION_WINDOW,
|
|
1813
|
+
request_params: {
|
|
1814
|
+
model: params.client.model,
|
|
1815
|
+
input_type: "document"
|
|
1816
|
+
},
|
|
1817
|
+
metadata: {
|
|
1818
|
+
source: "clawdbot-memory",
|
|
1819
|
+
agent: params.agentId
|
|
1820
|
+
}
|
|
1821
|
+
},
|
|
1822
|
+
errorPrefix: "voyage batch create failed"
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1825
|
+
async function fetchVoyageBatchStatus(params) {
|
|
1826
|
+
return await withRemoteHttpResponse(buildVoyageBatchRequest({
|
|
1827
|
+
client: params.client,
|
|
1828
|
+
path: `batches/${params.batchId}`,
|
|
1829
|
+
onResponse: async (res) => {
|
|
1830
|
+
await assertVoyageResponseOk(res, "voyage batch status failed");
|
|
1831
|
+
return await res.json();
|
|
1832
|
+
}
|
|
1833
|
+
}));
|
|
1834
|
+
}
|
|
1835
|
+
async function readVoyageBatchError(params) {
|
|
1836
|
+
try {
|
|
1837
|
+
return await withRemoteHttpResponse(buildVoyageBatchRequest({
|
|
1838
|
+
client: params.client,
|
|
1839
|
+
path: `files/${params.errorFileId}/content`,
|
|
1840
|
+
onResponse: async (res) => {
|
|
1841
|
+
await assertVoyageResponseOk(res, "voyage batch error file content failed");
|
|
1842
|
+
const text = await res.text();
|
|
1843
|
+
if (!text.trim()) return;
|
|
1844
|
+
return extractBatchErrorMessage(text.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line)));
|
|
1845
|
+
}
|
|
1846
|
+
}));
|
|
1847
|
+
} catch (err) {
|
|
1848
|
+
return formatUnavailableBatchError(err);
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
async function waitForVoyageBatch(params) {
|
|
1852
|
+
const start = Date.now();
|
|
1853
|
+
let current = params.initial;
|
|
1854
|
+
while (true) {
|
|
1855
|
+
const status = current ?? await fetchVoyageBatchStatus({
|
|
1856
|
+
client: params.client,
|
|
1857
|
+
batchId: params.batchId
|
|
1858
|
+
});
|
|
1859
|
+
const state = status.status ?? "unknown";
|
|
1860
|
+
if (state === "completed") return resolveBatchCompletionFromStatus({
|
|
1861
|
+
provider: "voyage",
|
|
1862
|
+
batchId: params.batchId,
|
|
1863
|
+
status
|
|
1864
|
+
});
|
|
1865
|
+
await throwIfBatchTerminalFailure({
|
|
1866
|
+
provider: "voyage",
|
|
1867
|
+
status: {
|
|
1868
|
+
...status,
|
|
1869
|
+
id: params.batchId
|
|
1870
|
+
},
|
|
1871
|
+
readError: async (errorFileId) => await readVoyageBatchError({
|
|
1872
|
+
client: params.client,
|
|
1873
|
+
errorFileId
|
|
1874
|
+
})
|
|
1875
|
+
});
|
|
1876
|
+
if (!params.wait) throw new Error(`voyage batch ${params.batchId} still ${state}; wait disabled`);
|
|
1877
|
+
if (Date.now() - start > params.timeoutMs) throw new Error(`voyage batch ${params.batchId} timed out after ${params.timeoutMs}ms`);
|
|
1878
|
+
params.debug?.(`voyage batch ${params.batchId} ${state}; waiting ${params.pollIntervalMs}ms`);
|
|
1879
|
+
await new Promise((resolve) => setTimeout(resolve, params.pollIntervalMs));
|
|
1880
|
+
current = void 0;
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
async function runVoyageEmbeddingBatches(params) {
|
|
1884
|
+
return await runEmbeddingBatchGroups({
|
|
1885
|
+
...buildEmbeddingBatchGroupOptions(params, {
|
|
1886
|
+
maxRequests: VOYAGE_BATCH_MAX_REQUESTS,
|
|
1887
|
+
debugLabel: "memory embeddings: voyage batch submit"
|
|
1888
|
+
}),
|
|
1889
|
+
runGroup: async ({ group, groupIndex, groups, byCustomId }) => {
|
|
1890
|
+
const batchInfo = await submitVoyageBatch({
|
|
1891
|
+
client: params.client,
|
|
1892
|
+
requests: group,
|
|
1893
|
+
agentId: params.agentId
|
|
1894
|
+
});
|
|
1895
|
+
if (!batchInfo.id) throw new Error("voyage batch create failed: missing batch id");
|
|
1896
|
+
const batchId = batchInfo.id;
|
|
1897
|
+
params.debug?.("memory embeddings: voyage batch created", {
|
|
1898
|
+
batchId: batchInfo.id,
|
|
1899
|
+
status: batchInfo.status,
|
|
1900
|
+
group: groupIndex + 1,
|
|
1901
|
+
groups,
|
|
1902
|
+
requests: group.length
|
|
1903
|
+
});
|
|
1904
|
+
const completed = await resolveCompletedBatchResult({
|
|
1905
|
+
provider: "voyage",
|
|
1906
|
+
status: batchInfo,
|
|
1907
|
+
wait: params.wait,
|
|
1908
|
+
waitForBatch: async () => await waitForVoyageBatch({
|
|
1909
|
+
client: params.client,
|
|
1910
|
+
batchId,
|
|
1911
|
+
wait: params.wait,
|
|
1912
|
+
pollIntervalMs: params.pollIntervalMs,
|
|
1913
|
+
timeoutMs: params.timeoutMs,
|
|
1914
|
+
debug: params.debug,
|
|
1915
|
+
initial: batchInfo
|
|
1916
|
+
})
|
|
1917
|
+
});
|
|
1918
|
+
const baseUrl = normalizeBatchBaseUrl(params.client);
|
|
1919
|
+
const errors = [];
|
|
1920
|
+
const remaining = new Set(group.map((request) => request.custom_id));
|
|
1921
|
+
await withRemoteHttpResponse({
|
|
1922
|
+
url: `${baseUrl}/files/${completed.outputFileId}/content`,
|
|
1923
|
+
ssrfPolicy: params.client.ssrfPolicy,
|
|
1924
|
+
init: { headers: buildBatchHeaders(params.client, { json: true }) },
|
|
1925
|
+
onResponse: async (contentRes) => {
|
|
1926
|
+
if (!contentRes.ok) {
|
|
1927
|
+
const text = await contentRes.text();
|
|
1928
|
+
throw new Error(`voyage batch file content failed: ${contentRes.status} ${text}`);
|
|
1929
|
+
}
|
|
1930
|
+
if (!contentRes.body) return;
|
|
1931
|
+
const reader = createInterface({
|
|
1932
|
+
input: Readable.fromWeb(contentRes.body),
|
|
1933
|
+
terminal: false
|
|
1934
|
+
});
|
|
1935
|
+
for await (const rawLine of reader) {
|
|
1936
|
+
if (!rawLine.trim()) continue;
|
|
1937
|
+
applyEmbeddingBatchOutputLine({
|
|
1938
|
+
line: JSON.parse(rawLine),
|
|
1939
|
+
remaining,
|
|
1940
|
+
errors,
|
|
1941
|
+
byCustomId
|
|
1942
|
+
});
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
});
|
|
1946
|
+
if (errors.length > 0) throw new Error(`voyage batch ${batchInfo.id} failed: ${errors.join("; ")}`);
|
|
1947
|
+
if (remaining.size > 0) throw new Error(`voyage batch ${batchInfo.id} missing ${remaining.size} embedding responses`);
|
|
1948
|
+
}
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1951
|
+
//#endregion
|
|
1952
|
+
//#region src/memory/embedding-model-limits.ts
|
|
1953
|
+
const DEFAULT_EMBEDDING_MAX_INPUT_TOKENS = 8192;
|
|
1954
|
+
const DEFAULT_LOCAL_EMBEDDING_MAX_INPUT_TOKENS = 2048;
|
|
1955
|
+
const KNOWN_EMBEDDING_MAX_INPUT_TOKENS = {
|
|
1956
|
+
"openai:text-embedding-3-small": 8192,
|
|
1957
|
+
"openai:text-embedding-3-large": 8192,
|
|
1958
|
+
"openai:text-embedding-ada-002": 8191,
|
|
1959
|
+
"gemini:text-embedding-004": 2048,
|
|
1960
|
+
"gemini:gemini-embedding-001": 2048,
|
|
1961
|
+
"gemini:gemini-embedding-2-preview": 8192,
|
|
1962
|
+
"voyage:voyage-3": 32e3,
|
|
1963
|
+
"voyage:voyage-3-lite": 16e3,
|
|
1964
|
+
"voyage:voyage-code-3": 32e3
|
|
1965
|
+
};
|
|
1966
|
+
function resolveEmbeddingMaxInputTokens(provider) {
|
|
1967
|
+
if (typeof provider.maxInputTokens === "number") return provider.maxInputTokens;
|
|
1968
|
+
const known = KNOWN_EMBEDDING_MAX_INPUT_TOKENS[`${provider.id}:${provider.model}`.toLowerCase()];
|
|
1969
|
+
if (typeof known === "number") return known;
|
|
1970
|
+
if (provider.id.toLowerCase() === "gemini") return 2048;
|
|
1971
|
+
if (provider.id.toLowerCase() === "local") return DEFAULT_LOCAL_EMBEDDING_MAX_INPUT_TOKENS;
|
|
1972
|
+
return DEFAULT_EMBEDDING_MAX_INPUT_TOKENS;
|
|
1973
|
+
}
|
|
1974
|
+
//#endregion
|
|
1975
|
+
//#region src/memory/embedding-chunk-limits.ts
|
|
1976
|
+
function enforceEmbeddingMaxInputTokens(provider, chunks, hardMaxInputTokens) {
|
|
1977
|
+
const providerMaxInputTokens = resolveEmbeddingMaxInputTokens(provider);
|
|
1978
|
+
const maxInputTokens = typeof hardMaxInputTokens === "number" && hardMaxInputTokens > 0 ? Math.min(providerMaxInputTokens, hardMaxInputTokens) : providerMaxInputTokens;
|
|
1979
|
+
const out = [];
|
|
1980
|
+
for (const chunk of chunks) {
|
|
1981
|
+
if (hasNonTextEmbeddingParts(chunk.embeddingInput)) {
|
|
1982
|
+
out.push(chunk);
|
|
1983
|
+
continue;
|
|
1984
|
+
}
|
|
1985
|
+
if (estimateUtf8Bytes(chunk.text) <= maxInputTokens) {
|
|
1986
|
+
out.push(chunk);
|
|
1987
|
+
continue;
|
|
1988
|
+
}
|
|
1989
|
+
for (const text of splitTextToUtf8ByteLimit(chunk.text, maxInputTokens)) out.push({
|
|
1990
|
+
startLine: chunk.startLine,
|
|
1991
|
+
endLine: chunk.endLine,
|
|
1992
|
+
text,
|
|
1993
|
+
hash: hashText(text),
|
|
1994
|
+
embeddingInput: { text }
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
return out;
|
|
1998
|
+
}
|
|
1999
|
+
//#endregion
|
|
2000
|
+
//#region src/memory/memory-schema.ts
|
|
2001
|
+
function ensureMemoryIndexSchema(params) {
|
|
2002
|
+
params.db.exec(`
|
|
2003
|
+
CREATE TABLE IF NOT EXISTS meta (
|
|
2004
|
+
key TEXT PRIMARY KEY,
|
|
2005
|
+
value TEXT NOT NULL
|
|
2006
|
+
);
|
|
2007
|
+
`);
|
|
2008
|
+
params.db.exec(`
|
|
2009
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
2010
|
+
path TEXT PRIMARY KEY,
|
|
2011
|
+
source TEXT NOT NULL DEFAULT 'memory',
|
|
2012
|
+
hash TEXT NOT NULL,
|
|
2013
|
+
mtime INTEGER NOT NULL,
|
|
2014
|
+
size INTEGER NOT NULL
|
|
2015
|
+
);
|
|
2016
|
+
`);
|
|
2017
|
+
params.db.exec(`
|
|
2018
|
+
CREATE TABLE IF NOT EXISTS chunks (
|
|
2019
|
+
id TEXT PRIMARY KEY,
|
|
2020
|
+
path TEXT NOT NULL,
|
|
2021
|
+
source TEXT NOT NULL DEFAULT 'memory',
|
|
2022
|
+
start_line INTEGER NOT NULL,
|
|
2023
|
+
end_line INTEGER NOT NULL,
|
|
2024
|
+
hash TEXT NOT NULL,
|
|
2025
|
+
model TEXT NOT NULL,
|
|
2026
|
+
text TEXT NOT NULL,
|
|
2027
|
+
embedding TEXT NOT NULL,
|
|
2028
|
+
updated_at INTEGER NOT NULL
|
|
2029
|
+
);
|
|
2030
|
+
`);
|
|
2031
|
+
if (params.cacheEnabled) {
|
|
2032
|
+
params.db.exec(`
|
|
2033
|
+
CREATE TABLE IF NOT EXISTS ${params.embeddingCacheTable} (
|
|
2034
|
+
provider TEXT NOT NULL,
|
|
2035
|
+
model TEXT NOT NULL,
|
|
2036
|
+
provider_key TEXT NOT NULL,
|
|
2037
|
+
hash TEXT NOT NULL,
|
|
2038
|
+
embedding TEXT NOT NULL,
|
|
2039
|
+
dims INTEGER,
|
|
2040
|
+
updated_at INTEGER NOT NULL,
|
|
2041
|
+
PRIMARY KEY (provider, model, provider_key, hash)
|
|
2042
|
+
);
|
|
2043
|
+
`);
|
|
2044
|
+
params.db.exec(`CREATE INDEX IF NOT EXISTS idx_embedding_cache_updated_at ON ${params.embeddingCacheTable}(updated_at);`);
|
|
2045
|
+
}
|
|
2046
|
+
let ftsAvailable = false;
|
|
2047
|
+
let ftsError;
|
|
2048
|
+
if (params.ftsEnabled) try {
|
|
2049
|
+
params.db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS ${params.ftsTable} USING fts5(\n text,\n id UNINDEXED,\n path UNINDEXED,\n source UNINDEXED,\n model UNINDEXED,\n start_line UNINDEXED,\n end_line UNINDEXED\n);`);
|
|
2050
|
+
ftsAvailable = true;
|
|
2051
|
+
} catch (err) {
|
|
2052
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2053
|
+
ftsAvailable = false;
|
|
2054
|
+
ftsError = message;
|
|
2055
|
+
}
|
|
2056
|
+
ensureColumn(params.db, "files", "source", "TEXT NOT NULL DEFAULT 'memory'");
|
|
2057
|
+
ensureColumn(params.db, "chunks", "source", "TEXT NOT NULL DEFAULT 'memory'");
|
|
2058
|
+
params.db.exec(`CREATE INDEX IF NOT EXISTS idx_chunks_path ON chunks(path);`);
|
|
2059
|
+
params.db.exec(`CREATE INDEX IF NOT EXISTS idx_chunks_source ON chunks(source);`);
|
|
2060
|
+
return {
|
|
2061
|
+
ftsAvailable,
|
|
2062
|
+
...ftsError ? { ftsError } : {}
|
|
2063
|
+
};
|
|
2064
|
+
}
|
|
2065
|
+
function ensureColumn(db, table, column, definition) {
|
|
2066
|
+
if (db.prepare(`PRAGMA table_info(${table})`).all().some((row) => row.name === column)) return;
|
|
2067
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
|
|
2068
|
+
}
|
|
2069
|
+
//#endregion
|
|
2070
|
+
//#region src/memory/sqlite-vec.ts
|
|
2071
|
+
async function loadSqliteVecExtension(params) {
|
|
2072
|
+
try {
|
|
2073
|
+
const sqliteVec = await import("sqlite-vec");
|
|
2074
|
+
const resolvedPath = params.extensionPath?.trim() ? params.extensionPath.trim() : void 0;
|
|
2075
|
+
const extensionPath = resolvedPath ?? sqliteVec.getLoadablePath();
|
|
2076
|
+
params.db.enableLoadExtension(true);
|
|
2077
|
+
if (resolvedPath) params.db.loadExtension(extensionPath);
|
|
2078
|
+
else sqliteVec.load(params.db);
|
|
2079
|
+
return {
|
|
2080
|
+
ok: true,
|
|
2081
|
+
extensionPath
|
|
2082
|
+
};
|
|
2083
|
+
} catch (err) {
|
|
2084
|
+
return {
|
|
2085
|
+
ok: false,
|
|
2086
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
//#endregion
|
|
2091
|
+
//#region src/memory/manager-sync-ops.ts
|
|
2092
|
+
const META_KEY = "memory_index_meta_v1";
|
|
2093
|
+
const VECTOR_TABLE$2 = "chunks_vec";
|
|
2094
|
+
const FTS_TABLE$2 = "chunks_fts";
|
|
2095
|
+
const EMBEDDING_CACHE_TABLE$2 = "embedding_cache";
|
|
2096
|
+
const SESSION_DIRTY_DEBOUNCE_MS = 5e3;
|
|
2097
|
+
const SESSION_DELTA_READ_CHUNK_BYTES = 64 * 1024;
|
|
2098
|
+
const VECTOR_LOAD_TIMEOUT_MS = 3e4;
|
|
2099
|
+
const IGNORED_MEMORY_WATCH_DIR_NAMES = new Set([
|
|
2100
|
+
".git",
|
|
2101
|
+
"node_modules",
|
|
2102
|
+
".pnpm-store",
|
|
2103
|
+
".venv",
|
|
2104
|
+
"venv",
|
|
2105
|
+
".tox",
|
|
2106
|
+
"__pycache__"
|
|
2107
|
+
]);
|
|
2108
|
+
const log$2 = createSubsystemLogger("memory");
|
|
2109
|
+
function shouldIgnoreMemoryWatchPath(watchPath) {
|
|
2110
|
+
return path.normalize(watchPath).split(path.sep).map((segment) => segment.trim().toLowerCase()).some((segment) => IGNORED_MEMORY_WATCH_DIR_NAMES.has(segment));
|
|
2111
|
+
}
|
|
2112
|
+
function runDetachedMemorySync(sync, reason) {
|
|
2113
|
+
sync().catch((err) => {
|
|
2114
|
+
log$2.warn(`memory sync failed (${reason}): ${String(err)}`);
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2117
|
+
var MemoryManagerSyncOps = class {
|
|
2118
|
+
constructor() {
|
|
2119
|
+
this.provider = null;
|
|
2120
|
+
this.sources = /* @__PURE__ */ new Set();
|
|
2121
|
+
this.providerKey = null;
|
|
2122
|
+
this.fts = {
|
|
2123
|
+
enabled: false,
|
|
2124
|
+
available: false
|
|
2125
|
+
};
|
|
2126
|
+
this.vectorReady = null;
|
|
2127
|
+
this.watcher = null;
|
|
2128
|
+
this.watchTimer = null;
|
|
2129
|
+
this.sessionWatchTimer = null;
|
|
2130
|
+
this.sessionUnsubscribe = null;
|
|
2131
|
+
this.intervalTimer = null;
|
|
2132
|
+
this.closed = false;
|
|
2133
|
+
this.dirty = false;
|
|
2134
|
+
this.sessionsDirty = false;
|
|
2135
|
+
this.sessionsDirtyFiles = /* @__PURE__ */ new Set();
|
|
2136
|
+
this.sessionPendingFiles = /* @__PURE__ */ new Set();
|
|
2137
|
+
this.sessionDeltas = /* @__PURE__ */ new Map();
|
|
2138
|
+
this.lastMetaSerialized = null;
|
|
2139
|
+
}
|
|
2140
|
+
async ensureVectorReady(dimensions) {
|
|
2141
|
+
if (!this.vector.enabled) return false;
|
|
2142
|
+
if (!this.vectorReady) this.vectorReady = this.withTimeout(this.loadVectorExtension(), VECTOR_LOAD_TIMEOUT_MS, `sqlite-vec load timed out after ${Math.round(VECTOR_LOAD_TIMEOUT_MS / 1e3)}s`);
|
|
2143
|
+
let ready = false;
|
|
2144
|
+
try {
|
|
2145
|
+
ready = await this.vectorReady || false;
|
|
2146
|
+
} catch (err) {
|
|
2147
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2148
|
+
this.vector.available = false;
|
|
2149
|
+
this.vector.loadError = message;
|
|
2150
|
+
this.vectorReady = null;
|
|
2151
|
+
log$2.warn(`sqlite-vec unavailable: ${message}`);
|
|
2152
|
+
return false;
|
|
2153
|
+
}
|
|
2154
|
+
if (ready && typeof dimensions === "number" && dimensions > 0) this.ensureVectorTable(dimensions);
|
|
2155
|
+
return ready;
|
|
2156
|
+
}
|
|
2157
|
+
async loadVectorExtension() {
|
|
2158
|
+
if (this.vector.available !== null) return this.vector.available;
|
|
2159
|
+
if (!this.vector.enabled) {
|
|
2160
|
+
this.vector.available = false;
|
|
2161
|
+
return false;
|
|
2162
|
+
}
|
|
2163
|
+
try {
|
|
2164
|
+
const resolvedPath = this.vector.extensionPath?.trim() ? resolveUserPath(this.vector.extensionPath) : void 0;
|
|
2165
|
+
const loaded = await loadSqliteVecExtension({
|
|
2166
|
+
db: this.db,
|
|
2167
|
+
extensionPath: resolvedPath
|
|
2168
|
+
});
|
|
2169
|
+
if (!loaded.ok) throw new Error(loaded.error ?? "unknown sqlite-vec load error");
|
|
2170
|
+
this.vector.extensionPath = loaded.extensionPath;
|
|
2171
|
+
this.vector.available = true;
|
|
2172
|
+
return true;
|
|
2173
|
+
} catch (err) {
|
|
2174
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2175
|
+
this.vector.available = false;
|
|
2176
|
+
this.vector.loadError = message;
|
|
2177
|
+
log$2.warn(`sqlite-vec unavailable: ${message}`);
|
|
2178
|
+
return false;
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
ensureVectorTable(dimensions) {
|
|
2182
|
+
if (this.vector.dims === dimensions) return;
|
|
2183
|
+
if (this.vector.dims && this.vector.dims !== dimensions) this.dropVectorTable();
|
|
2184
|
+
this.db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS ${VECTOR_TABLE$2} USING vec0(\n id TEXT PRIMARY KEY,\n embedding FLOAT[${dimensions}]\n)`);
|
|
2185
|
+
this.vector.dims = dimensions;
|
|
2186
|
+
}
|
|
2187
|
+
dropVectorTable() {
|
|
2188
|
+
try {
|
|
2189
|
+
this.db.exec(`DROP TABLE IF EXISTS ${VECTOR_TABLE$2}`);
|
|
2190
|
+
} catch (err) {
|
|
2191
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2192
|
+
log$2.debug(`Failed to drop ${VECTOR_TABLE$2}: ${message}`);
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
buildSourceFilter(alias) {
|
|
2196
|
+
const sources = Array.from(this.sources);
|
|
2197
|
+
if (sources.length === 0) return {
|
|
2198
|
+
sql: "",
|
|
2199
|
+
params: []
|
|
2200
|
+
};
|
|
2201
|
+
return {
|
|
2202
|
+
sql: ` AND ${alias ? `${alias}.source` : "source"} IN (${sources.map(() => "?").join(", ")})`,
|
|
2203
|
+
params: sources
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
openDatabase() {
|
|
2207
|
+
const dbPath = resolveUserPath(this.settings.store.path);
|
|
2208
|
+
return this.openDatabaseAtPath(dbPath);
|
|
2209
|
+
}
|
|
2210
|
+
openDatabaseAtPath(dbPath) {
|
|
2211
|
+
ensureDir(path.dirname(dbPath));
|
|
2212
|
+
const { DatabaseSync } = requireNodeSqlite();
|
|
2213
|
+
const db = new DatabaseSync(dbPath, { allowExtension: this.settings.store.vector.enabled });
|
|
2214
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
2215
|
+
return db;
|
|
2216
|
+
}
|
|
2217
|
+
seedEmbeddingCache(sourceDb) {
|
|
2218
|
+
if (!this.cache.enabled) return;
|
|
2219
|
+
try {
|
|
2220
|
+
const rows = sourceDb.prepare(`SELECT provider, model, provider_key, hash, embedding, dims, updated_at FROM ${EMBEDDING_CACHE_TABLE$2}`).all();
|
|
2221
|
+
if (!rows.length) return;
|
|
2222
|
+
const insert = this.db.prepare(`INSERT INTO ${EMBEDDING_CACHE_TABLE$2} (provider, model, provider_key, hash, embedding, dims, updated_at)
|
|
2223
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
2224
|
+
ON CONFLICT(provider, model, provider_key, hash) DO UPDATE SET
|
|
2225
|
+
embedding=excluded.embedding,
|
|
2226
|
+
dims=excluded.dims,
|
|
2227
|
+
updated_at=excluded.updated_at`);
|
|
2228
|
+
this.db.exec("BEGIN");
|
|
2229
|
+
for (const row of rows) insert.run(row.provider, row.model, row.provider_key, row.hash, row.embedding, row.dims, row.updated_at);
|
|
2230
|
+
this.db.exec("COMMIT");
|
|
2231
|
+
} catch (err) {
|
|
2232
|
+
try {
|
|
2233
|
+
this.db.exec("ROLLBACK");
|
|
2234
|
+
} catch {}
|
|
2235
|
+
throw err;
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
async swapIndexFiles(targetPath, tempPath) {
|
|
2239
|
+
const backupPath = `${targetPath}.backup-${randomUUID()}`;
|
|
2240
|
+
await this.moveIndexFiles(targetPath, backupPath);
|
|
2241
|
+
try {
|
|
2242
|
+
await this.moveIndexFiles(tempPath, targetPath);
|
|
2243
|
+
} catch (err) {
|
|
2244
|
+
await this.moveIndexFiles(backupPath, targetPath);
|
|
2245
|
+
throw err;
|
|
2246
|
+
}
|
|
2247
|
+
await this.removeIndexFiles(backupPath);
|
|
2248
|
+
}
|
|
2249
|
+
async moveIndexFiles(sourceBase, targetBase) {
|
|
2250
|
+
for (const suffix of [
|
|
2251
|
+
"",
|
|
2252
|
+
"-wal",
|
|
2253
|
+
"-shm"
|
|
2254
|
+
]) {
|
|
2255
|
+
const source = `${sourceBase}${suffix}`;
|
|
2256
|
+
const target = `${targetBase}${suffix}`;
|
|
2257
|
+
try {
|
|
2258
|
+
await fs$1.rename(source, target);
|
|
2259
|
+
} catch (err) {
|
|
2260
|
+
if (err.code !== "ENOENT") throw err;
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
async removeIndexFiles(basePath) {
|
|
2265
|
+
await Promise.all([
|
|
2266
|
+
"",
|
|
2267
|
+
"-wal",
|
|
2268
|
+
"-shm"
|
|
2269
|
+
].map((suffix) => fs$1.rm(`${basePath}${suffix}`, { force: true })));
|
|
2270
|
+
}
|
|
2271
|
+
ensureSchema() {
|
|
2272
|
+
const result = ensureMemoryIndexSchema({
|
|
2273
|
+
db: this.db,
|
|
2274
|
+
embeddingCacheTable: EMBEDDING_CACHE_TABLE$2,
|
|
2275
|
+
cacheEnabled: this.cache.enabled,
|
|
2276
|
+
ftsTable: FTS_TABLE$2,
|
|
2277
|
+
ftsEnabled: this.fts.enabled
|
|
2278
|
+
});
|
|
2279
|
+
this.fts.available = result.ftsAvailable;
|
|
2280
|
+
if (result.ftsError) {
|
|
2281
|
+
this.fts.loadError = result.ftsError;
|
|
2282
|
+
if (this.fts.enabled) log$2.warn(`fts unavailable: ${result.ftsError}`);
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
ensureWatcher() {
|
|
2286
|
+
if (!this.sources.has("memory") || !this.settings.sync.watch || this.watcher) return;
|
|
2287
|
+
const watchPaths = new Set([
|
|
2288
|
+
path.join(this.workspaceDir, "MEMORY.md"),
|
|
2289
|
+
path.join(this.workspaceDir, "memory.md"),
|
|
2290
|
+
path.join(this.workspaceDir, "memory", "**", "*.md")
|
|
2291
|
+
]);
|
|
2292
|
+
const additionalPaths = normalizeExtraMemoryPaths(this.workspaceDir, this.settings.extraPaths);
|
|
2293
|
+
for (const entry of additionalPaths) try {
|
|
2294
|
+
const stat = fs.lstatSync(entry);
|
|
2295
|
+
if (stat.isSymbolicLink()) continue;
|
|
2296
|
+
if (stat.isDirectory()) {
|
|
2297
|
+
watchPaths.add(path.join(entry, "**", "*.md"));
|
|
2298
|
+
if (this.settings.multimodal.enabled) for (const modality of this.settings.multimodal.modalities) for (const extension of getMemoryMultimodalExtensions(modality)) watchPaths.add(path.join(entry, "**", buildCaseInsensitiveExtensionGlob(extension)));
|
|
2299
|
+
continue;
|
|
2300
|
+
}
|
|
2301
|
+
if (stat.isFile() && (entry.toLowerCase().endsWith(".md") || classifyMemoryMultimodalPath(entry, this.settings.multimodal) !== null)) watchPaths.add(entry);
|
|
2302
|
+
} catch {}
|
|
2303
|
+
this.watcher = chokidar.watch(Array.from(watchPaths), {
|
|
2304
|
+
ignoreInitial: true,
|
|
2305
|
+
ignored: (watchPath) => shouldIgnoreMemoryWatchPath(String(watchPath)),
|
|
2306
|
+
awaitWriteFinish: {
|
|
2307
|
+
stabilityThreshold: this.settings.sync.watchDebounceMs,
|
|
2308
|
+
pollInterval: 100
|
|
2309
|
+
}
|
|
2310
|
+
});
|
|
2311
|
+
const markDirty = () => {
|
|
2312
|
+
this.dirty = true;
|
|
2313
|
+
this.scheduleWatchSync();
|
|
2314
|
+
};
|
|
2315
|
+
this.watcher.on("add", markDirty);
|
|
2316
|
+
this.watcher.on("change", markDirty);
|
|
2317
|
+
this.watcher.on("unlink", markDirty);
|
|
2318
|
+
}
|
|
2319
|
+
ensureSessionListener() {
|
|
2320
|
+
if (!this.sources.has("sessions") || this.sessionUnsubscribe) return;
|
|
2321
|
+
this.sessionUnsubscribe = onSessionTranscriptUpdate((update) => {
|
|
2322
|
+
if (this.closed) return;
|
|
2323
|
+
const sessionFile = update.sessionFile;
|
|
2324
|
+
if (!this.isSessionFileForAgent(sessionFile)) return;
|
|
2325
|
+
this.scheduleSessionDirty(sessionFile);
|
|
2326
|
+
});
|
|
2327
|
+
}
|
|
2328
|
+
scheduleSessionDirty(sessionFile) {
|
|
2329
|
+
this.sessionPendingFiles.add(sessionFile);
|
|
2330
|
+
if (this.sessionWatchTimer) return;
|
|
2331
|
+
this.sessionWatchTimer = setTimeout(() => {
|
|
2332
|
+
this.sessionWatchTimer = null;
|
|
2333
|
+
this.processSessionDeltaBatch().catch((err) => {
|
|
2334
|
+
log$2.warn(`memory session delta failed: ${String(err)}`);
|
|
2335
|
+
});
|
|
2336
|
+
}, SESSION_DIRTY_DEBOUNCE_MS);
|
|
2337
|
+
}
|
|
2338
|
+
async processSessionDeltaBatch() {
|
|
2339
|
+
if (this.sessionPendingFiles.size === 0) return;
|
|
2340
|
+
const pending = Array.from(this.sessionPendingFiles);
|
|
2341
|
+
this.sessionPendingFiles.clear();
|
|
2342
|
+
let shouldSync = false;
|
|
2343
|
+
for (const sessionFile of pending) {
|
|
2344
|
+
const delta = await this.updateSessionDelta(sessionFile);
|
|
2345
|
+
if (!delta) continue;
|
|
2346
|
+
const bytesThreshold = delta.deltaBytes;
|
|
2347
|
+
const messagesThreshold = delta.deltaMessages;
|
|
2348
|
+
const bytesHit = bytesThreshold <= 0 ? delta.pendingBytes > 0 : delta.pendingBytes >= bytesThreshold;
|
|
2349
|
+
const messagesHit = messagesThreshold <= 0 ? delta.pendingMessages > 0 : delta.pendingMessages >= messagesThreshold;
|
|
2350
|
+
if (!bytesHit && !messagesHit) continue;
|
|
2351
|
+
this.sessionsDirtyFiles.add(sessionFile);
|
|
2352
|
+
this.sessionsDirty = true;
|
|
2353
|
+
delta.pendingBytes = bytesThreshold > 0 ? Math.max(0, delta.pendingBytes - bytesThreshold) : 0;
|
|
2354
|
+
delta.pendingMessages = messagesThreshold > 0 ? Math.max(0, delta.pendingMessages - messagesThreshold) : 0;
|
|
2355
|
+
shouldSync = true;
|
|
2356
|
+
}
|
|
2357
|
+
if (shouldSync) this.sync({ reason: "session-delta" }).catch((err) => {
|
|
2358
|
+
log$2.warn(`memory sync failed (session-delta): ${String(err)}`);
|
|
2359
|
+
});
|
|
2360
|
+
}
|
|
2361
|
+
async updateSessionDelta(sessionFile) {
|
|
2362
|
+
const thresholds = this.settings.sync.sessions;
|
|
2363
|
+
if (!thresholds) return null;
|
|
2364
|
+
let stat;
|
|
2365
|
+
try {
|
|
2366
|
+
stat = await fs$1.stat(sessionFile);
|
|
2367
|
+
} catch {
|
|
2368
|
+
return null;
|
|
2369
|
+
}
|
|
2370
|
+
const size = stat.size;
|
|
2371
|
+
let state = this.sessionDeltas.get(sessionFile);
|
|
2372
|
+
if (!state) {
|
|
2373
|
+
state = {
|
|
2374
|
+
lastSize: 0,
|
|
2375
|
+
pendingBytes: 0,
|
|
2376
|
+
pendingMessages: 0
|
|
2377
|
+
};
|
|
2378
|
+
this.sessionDeltas.set(sessionFile, state);
|
|
2379
|
+
}
|
|
2380
|
+
const deltaBytes = Math.max(0, size - state.lastSize);
|
|
2381
|
+
if (deltaBytes === 0 && size === state.lastSize) return {
|
|
2382
|
+
deltaBytes: thresholds.deltaBytes,
|
|
2383
|
+
deltaMessages: thresholds.deltaMessages,
|
|
2384
|
+
pendingBytes: state.pendingBytes,
|
|
2385
|
+
pendingMessages: state.pendingMessages
|
|
2386
|
+
};
|
|
2387
|
+
if (size < state.lastSize) {
|
|
2388
|
+
state.lastSize = size;
|
|
2389
|
+
state.pendingBytes += size;
|
|
2390
|
+
if (thresholds.deltaMessages > 0 && (thresholds.deltaBytes <= 0 || state.pendingBytes < thresholds.deltaBytes)) state.pendingMessages += await this.countNewlines(sessionFile, 0, size);
|
|
2391
|
+
} else {
|
|
2392
|
+
state.pendingBytes += deltaBytes;
|
|
2393
|
+
if (thresholds.deltaMessages > 0 && (thresholds.deltaBytes <= 0 || state.pendingBytes < thresholds.deltaBytes)) state.pendingMessages += await this.countNewlines(sessionFile, state.lastSize, size);
|
|
2394
|
+
state.lastSize = size;
|
|
2395
|
+
}
|
|
2396
|
+
this.sessionDeltas.set(sessionFile, state);
|
|
2397
|
+
return {
|
|
2398
|
+
deltaBytes: thresholds.deltaBytes,
|
|
2399
|
+
deltaMessages: thresholds.deltaMessages,
|
|
2400
|
+
pendingBytes: state.pendingBytes,
|
|
2401
|
+
pendingMessages: state.pendingMessages
|
|
2402
|
+
};
|
|
2403
|
+
}
|
|
2404
|
+
async countNewlines(absPath, start, end) {
|
|
2405
|
+
if (end <= start) return 0;
|
|
2406
|
+
let handle;
|
|
2407
|
+
try {
|
|
2408
|
+
handle = await fs$1.open(absPath, "r");
|
|
2409
|
+
} catch (err) {
|
|
2410
|
+
if (isFileMissingError(err)) return 0;
|
|
2411
|
+
throw err;
|
|
2412
|
+
}
|
|
2413
|
+
try {
|
|
2414
|
+
let offset = start;
|
|
2415
|
+
let count = 0;
|
|
2416
|
+
const buffer = Buffer.alloc(SESSION_DELTA_READ_CHUNK_BYTES);
|
|
2417
|
+
while (offset < end) {
|
|
2418
|
+
const toRead = Math.min(buffer.length, end - offset);
|
|
2419
|
+
const { bytesRead } = await handle.read(buffer, 0, toRead, offset);
|
|
2420
|
+
if (bytesRead <= 0) break;
|
|
2421
|
+
for (let i = 0; i < bytesRead; i += 1) if (buffer[i] === 10) count += 1;
|
|
2422
|
+
offset += bytesRead;
|
|
2423
|
+
}
|
|
2424
|
+
return count;
|
|
2425
|
+
} finally {
|
|
2426
|
+
await handle.close();
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
resetSessionDelta(absPath, size) {
|
|
2430
|
+
const state = this.sessionDeltas.get(absPath);
|
|
2431
|
+
if (!state) return;
|
|
2432
|
+
state.lastSize = size;
|
|
2433
|
+
state.pendingBytes = 0;
|
|
2434
|
+
state.pendingMessages = 0;
|
|
2435
|
+
}
|
|
2436
|
+
isSessionFileForAgent(sessionFile) {
|
|
2437
|
+
if (!sessionFile) return false;
|
|
2438
|
+
const sessionsDir = resolveSessionTranscriptsDirForAgent(this.agentId);
|
|
2439
|
+
const resolvedFile = path.resolve(sessionFile);
|
|
2440
|
+
const resolvedDir = path.resolve(sessionsDir);
|
|
2441
|
+
return resolvedFile.startsWith(`${resolvedDir}${path.sep}`);
|
|
2442
|
+
}
|
|
2443
|
+
normalizeTargetSessionFiles(sessionFiles) {
|
|
2444
|
+
if (!sessionFiles || sessionFiles.length === 0) return null;
|
|
2445
|
+
const normalized = /* @__PURE__ */ new Set();
|
|
2446
|
+
for (const sessionFile of sessionFiles) {
|
|
2447
|
+
const trimmed = sessionFile.trim();
|
|
2448
|
+
if (!trimmed) continue;
|
|
2449
|
+
const resolved = path.resolve(trimmed);
|
|
2450
|
+
if (this.isSessionFileForAgent(resolved)) normalized.add(resolved);
|
|
2451
|
+
}
|
|
2452
|
+
return normalized.size > 0 ? normalized : null;
|
|
2453
|
+
}
|
|
2454
|
+
clearSyncedSessionFiles(targetSessionFiles) {
|
|
2455
|
+
if (!targetSessionFiles) this.sessionsDirtyFiles.clear();
|
|
2456
|
+
else for (const targetSessionFile of targetSessionFiles) this.sessionsDirtyFiles.delete(targetSessionFile);
|
|
2457
|
+
this.sessionsDirty = this.sessionsDirtyFiles.size > 0;
|
|
2458
|
+
}
|
|
2459
|
+
ensureIntervalSync() {
|
|
2460
|
+
const minutes = this.settings.sync.intervalMinutes;
|
|
2461
|
+
if (!minutes || minutes <= 0 || this.intervalTimer) return;
|
|
2462
|
+
const ms = minutes * 60 * 1e3;
|
|
2463
|
+
this.intervalTimer = setInterval(() => {
|
|
2464
|
+
runDetachedMemorySync(() => this.sync({ reason: "interval" }), "interval");
|
|
2465
|
+
}, ms);
|
|
2466
|
+
}
|
|
2467
|
+
scheduleWatchSync() {
|
|
2468
|
+
if (!this.sources.has("memory") || !this.settings.sync.watch) return;
|
|
2469
|
+
if (this.watchTimer) clearTimeout(this.watchTimer);
|
|
2470
|
+
this.watchTimer = setTimeout(() => {
|
|
2471
|
+
this.watchTimer = null;
|
|
2472
|
+
runDetachedMemorySync(() => this.sync({ reason: "watch" }), "watch");
|
|
2473
|
+
}, this.settings.sync.watchDebounceMs);
|
|
2474
|
+
}
|
|
2475
|
+
shouldSyncSessions(params, needsFullReindex = false) {
|
|
2476
|
+
if (!this.sources.has("sessions")) return false;
|
|
2477
|
+
if (params?.sessionFiles?.some((sessionFile) => sessionFile.trim().length > 0)) return true;
|
|
2478
|
+
if (params?.force) return true;
|
|
2479
|
+
const reason = params?.reason;
|
|
2480
|
+
if (reason === "session-start" || reason === "watch") return false;
|
|
2481
|
+
if (needsFullReindex) return true;
|
|
2482
|
+
return this.sessionsDirty && this.sessionsDirtyFiles.size > 0;
|
|
2483
|
+
}
|
|
2484
|
+
async syncMemoryFiles(params) {
|
|
2485
|
+
if (!this.provider) {
|
|
2486
|
+
log$2.debug("Skipping memory file sync in FTS-only mode (no embedding provider)");
|
|
2487
|
+
return;
|
|
2488
|
+
}
|
|
2489
|
+
const fileEntries = (await runWithConcurrency((await listMemoryFiles(this.workspaceDir, this.settings.extraPaths, this.settings.multimodal)).map((file) => async () => await buildFileEntry(file, this.workspaceDir, this.settings.multimodal)), this.getIndexConcurrency())).filter((entry) => entry !== null);
|
|
2490
|
+
log$2.debug("memory sync: indexing memory files", {
|
|
2491
|
+
files: fileEntries.length,
|
|
2492
|
+
needsFullReindex: params.needsFullReindex,
|
|
2493
|
+
batch: this.batch.enabled,
|
|
2494
|
+
concurrency: this.getIndexConcurrency()
|
|
2495
|
+
});
|
|
2496
|
+
const activePaths = new Set(fileEntries.map((entry) => entry.path));
|
|
2497
|
+
if (params.progress) {
|
|
2498
|
+
params.progress.total += fileEntries.length;
|
|
2499
|
+
params.progress.report({
|
|
2500
|
+
completed: params.progress.completed,
|
|
2501
|
+
total: params.progress.total,
|
|
2502
|
+
label: this.batch.enabled ? "Indexing memory files (batch)..." : "Indexing memory files…"
|
|
2503
|
+
});
|
|
2504
|
+
}
|
|
2505
|
+
await runWithConcurrency(fileEntries.map((entry) => async () => {
|
|
2506
|
+
const record = this.db.prepare(`SELECT hash FROM files WHERE path = ? AND source = ?`).get(entry.path, "memory");
|
|
2507
|
+
if (!params.needsFullReindex && record?.hash === entry.hash) {
|
|
2508
|
+
if (params.progress) {
|
|
2509
|
+
params.progress.completed += 1;
|
|
2510
|
+
params.progress.report({
|
|
2511
|
+
completed: params.progress.completed,
|
|
2512
|
+
total: params.progress.total
|
|
2513
|
+
});
|
|
2514
|
+
}
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2517
|
+
await this.indexFile(entry, { source: "memory" });
|
|
2518
|
+
if (params.progress) {
|
|
2519
|
+
params.progress.completed += 1;
|
|
2520
|
+
params.progress.report({
|
|
2521
|
+
completed: params.progress.completed,
|
|
2522
|
+
total: params.progress.total
|
|
2523
|
+
});
|
|
2524
|
+
}
|
|
2525
|
+
}), this.getIndexConcurrency());
|
|
2526
|
+
const staleRows = this.db.prepare(`SELECT path FROM files WHERE source = ?`).all("memory");
|
|
2527
|
+
for (const stale of staleRows) {
|
|
2528
|
+
if (activePaths.has(stale.path)) continue;
|
|
2529
|
+
this.db.prepare(`DELETE FROM files WHERE path = ? AND source = ?`).run(stale.path, "memory");
|
|
2530
|
+
try {
|
|
2531
|
+
this.db.prepare(`DELETE FROM ${VECTOR_TABLE$2} WHERE id IN (SELECT id FROM chunks WHERE path = ? AND source = ?)`).run(stale.path, "memory");
|
|
2532
|
+
} catch {}
|
|
2533
|
+
this.db.prepare(`DELETE FROM chunks WHERE path = ? AND source = ?`).run(stale.path, "memory");
|
|
2534
|
+
if (this.fts.enabled && this.fts.available) try {
|
|
2535
|
+
this.db.prepare(`DELETE FROM ${FTS_TABLE$2} WHERE path = ? AND source = ? AND model = ?`).run(stale.path, "memory", this.provider.model);
|
|
2536
|
+
} catch {}
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
async syncSessionFiles(params) {
|
|
2540
|
+
if (!this.provider) {
|
|
2541
|
+
log$2.debug("Skipping session file sync in FTS-only mode (no embedding provider)");
|
|
2542
|
+
return;
|
|
2543
|
+
}
|
|
2544
|
+
const targetSessionFiles = params.needsFullReindex ? null : this.normalizeTargetSessionFiles(params.targetSessionFiles);
|
|
2545
|
+
const files = targetSessionFiles ? Array.from(targetSessionFiles) : await listSessionFilesForAgent(this.agentId);
|
|
2546
|
+
const activePaths = targetSessionFiles ? null : new Set(files.map((file) => sessionPathForFile(file)));
|
|
2547
|
+
const indexAll = params.needsFullReindex || Boolean(targetSessionFiles) || this.sessionsDirtyFiles.size === 0;
|
|
2548
|
+
log$2.debug("memory sync: indexing session files", {
|
|
2549
|
+
files: files.length,
|
|
2550
|
+
indexAll,
|
|
2551
|
+
dirtyFiles: this.sessionsDirtyFiles.size,
|
|
2552
|
+
targetedFiles: targetSessionFiles?.size ?? 0,
|
|
2553
|
+
batch: this.batch.enabled,
|
|
2554
|
+
concurrency: this.getIndexConcurrency()
|
|
2555
|
+
});
|
|
2556
|
+
if (params.progress) {
|
|
2557
|
+
params.progress.total += files.length;
|
|
2558
|
+
params.progress.report({
|
|
2559
|
+
completed: params.progress.completed,
|
|
2560
|
+
total: params.progress.total,
|
|
2561
|
+
label: this.batch.enabled ? "Indexing session files (batch)..." : "Indexing session files…"
|
|
2562
|
+
});
|
|
2563
|
+
}
|
|
2564
|
+
await runWithConcurrency(files.map((absPath) => async () => {
|
|
2565
|
+
if (!indexAll && !this.sessionsDirtyFiles.has(absPath)) {
|
|
2566
|
+
if (params.progress) {
|
|
2567
|
+
params.progress.completed += 1;
|
|
2568
|
+
params.progress.report({
|
|
2569
|
+
completed: params.progress.completed,
|
|
2570
|
+
total: params.progress.total
|
|
2571
|
+
});
|
|
2572
|
+
}
|
|
2573
|
+
return;
|
|
2574
|
+
}
|
|
2575
|
+
const entry = await buildSessionEntry(absPath);
|
|
2576
|
+
if (!entry) {
|
|
2577
|
+
if (params.progress) {
|
|
2578
|
+
params.progress.completed += 1;
|
|
2579
|
+
params.progress.report({
|
|
2580
|
+
completed: params.progress.completed,
|
|
2581
|
+
total: params.progress.total
|
|
2582
|
+
});
|
|
2583
|
+
}
|
|
2584
|
+
return;
|
|
2585
|
+
}
|
|
2586
|
+
const record = this.db.prepare(`SELECT hash FROM files WHERE path = ? AND source = ?`).get(entry.path, "sessions");
|
|
2587
|
+
if (!params.needsFullReindex && record?.hash === entry.hash) {
|
|
2588
|
+
if (params.progress) {
|
|
2589
|
+
params.progress.completed += 1;
|
|
2590
|
+
params.progress.report({
|
|
2591
|
+
completed: params.progress.completed,
|
|
2592
|
+
total: params.progress.total
|
|
2593
|
+
});
|
|
2594
|
+
}
|
|
2595
|
+
this.resetSessionDelta(absPath, entry.size);
|
|
2596
|
+
return;
|
|
2597
|
+
}
|
|
2598
|
+
await this.indexFile(entry, {
|
|
2599
|
+
source: "sessions",
|
|
2600
|
+
content: entry.content
|
|
2601
|
+
});
|
|
2602
|
+
this.resetSessionDelta(absPath, entry.size);
|
|
2603
|
+
if (params.progress) {
|
|
2604
|
+
params.progress.completed += 1;
|
|
2605
|
+
params.progress.report({
|
|
2606
|
+
completed: params.progress.completed,
|
|
2607
|
+
total: params.progress.total
|
|
2608
|
+
});
|
|
2609
|
+
}
|
|
2610
|
+
}), this.getIndexConcurrency());
|
|
2611
|
+
if (activePaths === null) return;
|
|
2612
|
+
const staleRows = this.db.prepare(`SELECT path FROM files WHERE source = ?`).all("sessions");
|
|
2613
|
+
for (const stale of staleRows) {
|
|
2614
|
+
if (activePaths.has(stale.path)) continue;
|
|
2615
|
+
this.db.prepare(`DELETE FROM files WHERE path = ? AND source = ?`).run(stale.path, "sessions");
|
|
2616
|
+
try {
|
|
2617
|
+
this.db.prepare(`DELETE FROM ${VECTOR_TABLE$2} WHERE id IN (SELECT id FROM chunks WHERE path = ? AND source = ?)`).run(stale.path, "sessions");
|
|
2618
|
+
} catch {}
|
|
2619
|
+
this.db.prepare(`DELETE FROM chunks WHERE path = ? AND source = ?`).run(stale.path, "sessions");
|
|
2620
|
+
if (this.fts.enabled && this.fts.available) try {
|
|
2621
|
+
this.db.prepare(`DELETE FROM ${FTS_TABLE$2} WHERE path = ? AND source = ? AND model = ?`).run(stale.path, "sessions", this.provider.model);
|
|
2622
|
+
} catch {}
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
createSyncProgress(onProgress) {
|
|
2626
|
+
const state = {
|
|
2627
|
+
completed: 0,
|
|
2628
|
+
total: 0,
|
|
2629
|
+
label: void 0,
|
|
2630
|
+
report: (update) => {
|
|
2631
|
+
if (update.label) state.label = update.label;
|
|
2632
|
+
const label = update.total > 0 && state.label ? `${state.label} ${update.completed}/${update.total}` : state.label;
|
|
2633
|
+
onProgress({
|
|
2634
|
+
completed: update.completed,
|
|
2635
|
+
total: update.total,
|
|
2636
|
+
label
|
|
2637
|
+
});
|
|
2638
|
+
}
|
|
2639
|
+
};
|
|
2640
|
+
return state;
|
|
2641
|
+
}
|
|
2642
|
+
async runSync(params) {
|
|
2643
|
+
const progress = params?.progress ? this.createSyncProgress(params.progress) : void 0;
|
|
2644
|
+
if (progress) progress.report({
|
|
2645
|
+
completed: progress.completed,
|
|
2646
|
+
total: progress.total,
|
|
2647
|
+
label: "Loading vector extension…"
|
|
2648
|
+
});
|
|
2649
|
+
const vectorReady = await this.ensureVectorReady();
|
|
2650
|
+
const meta = this.readMeta();
|
|
2651
|
+
const configuredSources = this.resolveConfiguredSourcesForMeta();
|
|
2652
|
+
const configuredScopeHash = this.resolveConfiguredScopeHash();
|
|
2653
|
+
const targetSessionFiles = this.normalizeTargetSessionFiles(params?.sessionFiles);
|
|
2654
|
+
const hasTargetSessionFiles = targetSessionFiles !== null;
|
|
2655
|
+
if (hasTargetSessionFiles && targetSessionFiles && this.sources.has("sessions")) {
|
|
2656
|
+
try {
|
|
2657
|
+
await this.syncSessionFiles({
|
|
2658
|
+
needsFullReindex: false,
|
|
2659
|
+
targetSessionFiles: Array.from(targetSessionFiles),
|
|
2660
|
+
progress: progress ?? void 0
|
|
2661
|
+
});
|
|
2662
|
+
this.clearSyncedSessionFiles(targetSessionFiles);
|
|
2663
|
+
} catch (err) {
|
|
2664
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2665
|
+
if (this.shouldFallbackOnError(reason) && await this.activateFallbackProvider(reason)) {
|
|
2666
|
+
if (process.env.OPENCLAW_TEST_FAST === "1" && process.env.OPENCLAW_TEST_MEMORY_UNSAFE_REINDEX === "1") await this.runUnsafeReindex({
|
|
2667
|
+
reason: params?.reason,
|
|
2668
|
+
force: true,
|
|
2669
|
+
progress: progress ?? void 0
|
|
2670
|
+
});
|
|
2671
|
+
else await this.runSafeReindex({
|
|
2672
|
+
reason: params?.reason,
|
|
2673
|
+
force: true,
|
|
2674
|
+
progress: progress ?? void 0
|
|
2675
|
+
});
|
|
2676
|
+
return;
|
|
2677
|
+
}
|
|
2678
|
+
throw err;
|
|
2679
|
+
}
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2682
|
+
const needsFullReindex = params?.force && !hasTargetSessionFiles || !meta || this.provider && meta.model !== this.provider.model || this.provider && meta.provider !== this.provider.id || meta.providerKey !== this.providerKey || this.metaSourcesDiffer(meta, configuredSources) || meta.scopeHash !== configuredScopeHash || meta.chunkTokens !== this.settings.chunking.tokens || meta.chunkOverlap !== this.settings.chunking.overlap || vectorReady && !meta?.vectorDims;
|
|
2683
|
+
try {
|
|
2684
|
+
if (needsFullReindex) {
|
|
2685
|
+
if (process.env.OPENCLAW_TEST_FAST === "1" && process.env.OPENCLAW_TEST_MEMORY_UNSAFE_REINDEX === "1") await this.runUnsafeReindex({
|
|
2686
|
+
reason: params?.reason,
|
|
2687
|
+
force: params?.force,
|
|
2688
|
+
progress: progress ?? void 0
|
|
2689
|
+
});
|
|
2690
|
+
else await this.runSafeReindex({
|
|
2691
|
+
reason: params?.reason,
|
|
2692
|
+
force: params?.force,
|
|
2693
|
+
progress: progress ?? void 0
|
|
2694
|
+
});
|
|
2695
|
+
return;
|
|
2696
|
+
}
|
|
2697
|
+
const shouldSyncMemory = this.sources.has("memory") && (!hasTargetSessionFiles && params?.force || needsFullReindex || this.dirty);
|
|
2698
|
+
const shouldSyncSessions = this.shouldSyncSessions(params, needsFullReindex);
|
|
2699
|
+
if (shouldSyncMemory) {
|
|
2700
|
+
await this.syncMemoryFiles({
|
|
2701
|
+
needsFullReindex,
|
|
2702
|
+
progress: progress ?? void 0
|
|
2703
|
+
});
|
|
2704
|
+
this.dirty = false;
|
|
2705
|
+
}
|
|
2706
|
+
if (shouldSyncSessions) {
|
|
2707
|
+
await this.syncSessionFiles({
|
|
2708
|
+
needsFullReindex,
|
|
2709
|
+
targetSessionFiles: targetSessionFiles ? Array.from(targetSessionFiles) : void 0,
|
|
2710
|
+
progress: progress ?? void 0
|
|
2711
|
+
});
|
|
2712
|
+
this.sessionsDirty = false;
|
|
2713
|
+
this.sessionsDirtyFiles.clear();
|
|
2714
|
+
} else if (this.sessionsDirtyFiles.size > 0) this.sessionsDirty = true;
|
|
2715
|
+
else this.sessionsDirty = false;
|
|
2716
|
+
} catch (err) {
|
|
2717
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2718
|
+
if (this.shouldFallbackOnError(reason) && await this.activateFallbackProvider(reason)) {
|
|
2719
|
+
await this.runSafeReindex({
|
|
2720
|
+
reason: params?.reason ?? "fallback",
|
|
2721
|
+
force: true,
|
|
2722
|
+
progress: progress ?? void 0
|
|
2723
|
+
});
|
|
2724
|
+
return;
|
|
2725
|
+
}
|
|
2726
|
+
throw err;
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
shouldFallbackOnError(message) {
|
|
2730
|
+
return /embedding|embeddings|batch/i.test(message);
|
|
2731
|
+
}
|
|
2732
|
+
resolveBatchConfig() {
|
|
2733
|
+
const batch = this.settings.remote?.batch;
|
|
2734
|
+
return {
|
|
2735
|
+
enabled: Boolean(batch?.enabled && this.provider && (this.openAi && this.provider.id === "openai" || this.gemini && this.provider.id === "gemini" || this.voyage && this.provider.id === "voyage")),
|
|
2736
|
+
wait: batch?.wait ?? true,
|
|
2737
|
+
concurrency: Math.max(1, batch?.concurrency ?? 2),
|
|
2738
|
+
pollIntervalMs: batch?.pollIntervalMs ?? 2e3,
|
|
2739
|
+
timeoutMs: (batch?.timeoutMinutes ?? 60) * 60 * 1e3
|
|
2740
|
+
};
|
|
2741
|
+
}
|
|
2742
|
+
async activateFallbackProvider(reason) {
|
|
2743
|
+
const fallback = this.settings.fallback;
|
|
2744
|
+
if (!fallback || fallback === "none" || !this.provider || fallback === this.provider.id) return false;
|
|
2745
|
+
if (this.fallbackFrom) return false;
|
|
2746
|
+
const fallbackFrom = this.provider.id;
|
|
2747
|
+
const fallbackModel = fallback === "gemini" ? DEFAULT_GEMINI_EMBEDDING_MODEL : fallback === "openai" ? DEFAULT_OPENAI_EMBEDDING_MODEL : fallback === "voyage" ? DEFAULT_VOYAGE_EMBEDDING_MODEL : fallback === "mistral" ? DEFAULT_MISTRAL_EMBEDDING_MODEL : fallback === "ollama" ? DEFAULT_OLLAMA_EMBEDDING_MODEL : this.settings.model;
|
|
2748
|
+
const fallbackResult = await createEmbeddingProvider({
|
|
2749
|
+
config: this.cfg,
|
|
2750
|
+
agentDir: resolveAgentDir(this.cfg, this.agentId),
|
|
2751
|
+
provider: fallback,
|
|
2752
|
+
remote: this.settings.remote,
|
|
2753
|
+
model: fallbackModel,
|
|
2754
|
+
outputDimensionality: this.settings.outputDimensionality,
|
|
2755
|
+
fallback: "none",
|
|
2756
|
+
local: this.settings.local
|
|
2757
|
+
});
|
|
2758
|
+
this.fallbackFrom = fallbackFrom;
|
|
2759
|
+
this.fallbackReason = reason;
|
|
2760
|
+
this.provider = fallbackResult.provider;
|
|
2761
|
+
this.openAi = fallbackResult.openAi;
|
|
2762
|
+
this.gemini = fallbackResult.gemini;
|
|
2763
|
+
this.voyage = fallbackResult.voyage;
|
|
2764
|
+
this.mistral = fallbackResult.mistral;
|
|
2765
|
+
this.ollama = fallbackResult.ollama;
|
|
2766
|
+
this.providerKey = this.computeProviderKey();
|
|
2767
|
+
this.batch = this.resolveBatchConfig();
|
|
2768
|
+
log$2.warn(`memory embeddings: switched to fallback provider (${fallback})`, { reason });
|
|
2769
|
+
return true;
|
|
2770
|
+
}
|
|
2771
|
+
async runSafeReindex(params) {
|
|
2772
|
+
const dbPath = resolveUserPath(this.settings.store.path);
|
|
2773
|
+
const tempDbPath = `${dbPath}.tmp-${randomUUID()}`;
|
|
2774
|
+
const tempDb = this.openDatabaseAtPath(tempDbPath);
|
|
2775
|
+
const originalDb = this.db;
|
|
2776
|
+
let originalDbClosed = false;
|
|
2777
|
+
const originalState = {
|
|
2778
|
+
ftsAvailable: this.fts.available,
|
|
2779
|
+
ftsError: this.fts.loadError,
|
|
2780
|
+
vectorAvailable: this.vector.available,
|
|
2781
|
+
vectorLoadError: this.vector.loadError,
|
|
2782
|
+
vectorDims: this.vector.dims,
|
|
2783
|
+
vectorReady: this.vectorReady
|
|
2784
|
+
};
|
|
2785
|
+
const restoreOriginalState = () => {
|
|
2786
|
+
if (originalDbClosed) this.db = this.openDatabaseAtPath(dbPath);
|
|
2787
|
+
else this.db = originalDb;
|
|
2788
|
+
this.fts.available = originalState.ftsAvailable;
|
|
2789
|
+
this.fts.loadError = originalState.ftsError;
|
|
2790
|
+
this.vector.available = originalDbClosed ? null : originalState.vectorAvailable;
|
|
2791
|
+
this.vector.loadError = originalState.vectorLoadError;
|
|
2792
|
+
this.vector.dims = originalState.vectorDims;
|
|
2793
|
+
this.vectorReady = originalDbClosed ? null : originalState.vectorReady;
|
|
2794
|
+
};
|
|
2795
|
+
this.db = tempDb;
|
|
2796
|
+
this.vectorReady = null;
|
|
2797
|
+
this.vector.available = null;
|
|
2798
|
+
this.vector.loadError = void 0;
|
|
2799
|
+
this.vector.dims = void 0;
|
|
2800
|
+
this.fts.available = false;
|
|
2801
|
+
this.fts.loadError = void 0;
|
|
2802
|
+
this.ensureSchema();
|
|
2803
|
+
let nextMeta = null;
|
|
2804
|
+
try {
|
|
2805
|
+
this.seedEmbeddingCache(originalDb);
|
|
2806
|
+
const shouldSyncMemory = this.sources.has("memory");
|
|
2807
|
+
const shouldSyncSessions = this.shouldSyncSessions({
|
|
2808
|
+
reason: params.reason,
|
|
2809
|
+
force: params.force
|
|
2810
|
+
}, true);
|
|
2811
|
+
if (shouldSyncMemory) {
|
|
2812
|
+
await this.syncMemoryFiles({
|
|
2813
|
+
needsFullReindex: true,
|
|
2814
|
+
progress: params.progress
|
|
2815
|
+
});
|
|
2816
|
+
this.dirty = false;
|
|
2817
|
+
}
|
|
2818
|
+
if (shouldSyncSessions) {
|
|
2819
|
+
await this.syncSessionFiles({
|
|
2820
|
+
needsFullReindex: true,
|
|
2821
|
+
progress: params.progress
|
|
2822
|
+
});
|
|
2823
|
+
this.sessionsDirty = false;
|
|
2824
|
+
this.sessionsDirtyFiles.clear();
|
|
2825
|
+
} else if (this.sessionsDirtyFiles.size > 0) this.sessionsDirty = true;
|
|
2826
|
+
else this.sessionsDirty = false;
|
|
2827
|
+
nextMeta = {
|
|
2828
|
+
model: this.provider?.model ?? "fts-only",
|
|
2829
|
+
provider: this.provider?.id ?? "none",
|
|
2830
|
+
providerKey: this.providerKey,
|
|
2831
|
+
sources: this.resolveConfiguredSourcesForMeta(),
|
|
2832
|
+
scopeHash: this.resolveConfiguredScopeHash(),
|
|
2833
|
+
chunkTokens: this.settings.chunking.tokens,
|
|
2834
|
+
chunkOverlap: this.settings.chunking.overlap
|
|
2835
|
+
};
|
|
2836
|
+
if (!nextMeta) throw new Error("Failed to compute memory index metadata for reindexing.");
|
|
2837
|
+
if (this.vector.available && this.vector.dims) nextMeta.vectorDims = this.vector.dims;
|
|
2838
|
+
this.writeMeta(nextMeta);
|
|
2839
|
+
this.pruneEmbeddingCacheIfNeeded?.();
|
|
2840
|
+
this.db.close();
|
|
2841
|
+
originalDb.close();
|
|
2842
|
+
originalDbClosed = true;
|
|
2843
|
+
await this.swapIndexFiles(dbPath, tempDbPath);
|
|
2844
|
+
this.db = this.openDatabaseAtPath(dbPath);
|
|
2845
|
+
this.vectorReady = null;
|
|
2846
|
+
this.vector.available = null;
|
|
2847
|
+
this.vector.loadError = void 0;
|
|
2848
|
+
this.ensureSchema();
|
|
2849
|
+
this.vector.dims = nextMeta?.vectorDims;
|
|
2850
|
+
} catch (err) {
|
|
2851
|
+
try {
|
|
2852
|
+
this.db.close();
|
|
2853
|
+
} catch {}
|
|
2854
|
+
await this.removeIndexFiles(tempDbPath);
|
|
2855
|
+
restoreOriginalState();
|
|
2856
|
+
throw err;
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
async runUnsafeReindex(params) {
|
|
2860
|
+
this.resetIndex();
|
|
2861
|
+
const shouldSyncMemory = this.sources.has("memory");
|
|
2862
|
+
const shouldSyncSessions = this.shouldSyncSessions({
|
|
2863
|
+
reason: params.reason,
|
|
2864
|
+
force: params.force
|
|
2865
|
+
}, true);
|
|
2866
|
+
if (shouldSyncMemory) {
|
|
2867
|
+
await this.syncMemoryFiles({
|
|
2868
|
+
needsFullReindex: true,
|
|
2869
|
+
progress: params.progress
|
|
2870
|
+
});
|
|
2871
|
+
this.dirty = false;
|
|
2872
|
+
}
|
|
2873
|
+
if (shouldSyncSessions) {
|
|
2874
|
+
await this.syncSessionFiles({
|
|
2875
|
+
needsFullReindex: true,
|
|
2876
|
+
progress: params.progress
|
|
2877
|
+
});
|
|
2878
|
+
this.sessionsDirty = false;
|
|
2879
|
+
this.sessionsDirtyFiles.clear();
|
|
2880
|
+
} else if (this.sessionsDirtyFiles.size > 0) this.sessionsDirty = true;
|
|
2881
|
+
else this.sessionsDirty = false;
|
|
2882
|
+
const nextMeta = {
|
|
2883
|
+
model: this.provider?.model ?? "fts-only",
|
|
2884
|
+
provider: this.provider?.id ?? "none",
|
|
2885
|
+
providerKey: this.providerKey,
|
|
2886
|
+
sources: this.resolveConfiguredSourcesForMeta(),
|
|
2887
|
+
scopeHash: this.resolveConfiguredScopeHash(),
|
|
2888
|
+
chunkTokens: this.settings.chunking.tokens,
|
|
2889
|
+
chunkOverlap: this.settings.chunking.overlap
|
|
2890
|
+
};
|
|
2891
|
+
if (this.vector.available && this.vector.dims) nextMeta.vectorDims = this.vector.dims;
|
|
2892
|
+
this.writeMeta(nextMeta);
|
|
2893
|
+
this.pruneEmbeddingCacheIfNeeded?.();
|
|
2894
|
+
}
|
|
2895
|
+
resetIndex() {
|
|
2896
|
+
this.db.exec(`DELETE FROM files`);
|
|
2897
|
+
this.db.exec(`DELETE FROM chunks`);
|
|
2898
|
+
if (this.fts.enabled && this.fts.available) try {
|
|
2899
|
+
this.db.exec(`DELETE FROM ${FTS_TABLE$2}`);
|
|
2900
|
+
} catch {}
|
|
2901
|
+
this.dropVectorTable();
|
|
2902
|
+
this.vector.dims = void 0;
|
|
2903
|
+
this.sessionsDirtyFiles.clear();
|
|
2904
|
+
}
|
|
2905
|
+
readMeta() {
|
|
2906
|
+
const row = this.db.prepare(`SELECT value FROM meta WHERE key = ?`).get(META_KEY);
|
|
2907
|
+
if (!row?.value) {
|
|
2908
|
+
this.lastMetaSerialized = null;
|
|
2909
|
+
return null;
|
|
2910
|
+
}
|
|
2911
|
+
try {
|
|
2912
|
+
const parsed = JSON.parse(row.value);
|
|
2913
|
+
this.lastMetaSerialized = row.value;
|
|
2914
|
+
return parsed;
|
|
2915
|
+
} catch {
|
|
2916
|
+
this.lastMetaSerialized = null;
|
|
2917
|
+
return null;
|
|
2918
|
+
}
|
|
2919
|
+
}
|
|
2920
|
+
writeMeta(meta) {
|
|
2921
|
+
const value = JSON.stringify(meta);
|
|
2922
|
+
if (this.lastMetaSerialized === value) return;
|
|
2923
|
+
this.db.prepare(`INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value=excluded.value`).run(META_KEY, value);
|
|
2924
|
+
this.lastMetaSerialized = value;
|
|
2925
|
+
}
|
|
2926
|
+
resolveConfiguredSourcesForMeta() {
|
|
2927
|
+
const normalized = Array.from(this.sources).filter((source) => source === "memory" || source === "sessions").toSorted();
|
|
2928
|
+
return normalized.length > 0 ? normalized : ["memory"];
|
|
2929
|
+
}
|
|
2930
|
+
normalizeMetaSources(meta) {
|
|
2931
|
+
if (!Array.isArray(meta.sources)) return ["memory"];
|
|
2932
|
+
const normalized = Array.from(new Set(meta.sources.filter((source) => source === "memory" || source === "sessions"))).toSorted();
|
|
2933
|
+
return normalized.length > 0 ? normalized : ["memory"];
|
|
2934
|
+
}
|
|
2935
|
+
resolveConfiguredScopeHash() {
|
|
2936
|
+
const extraPaths = normalizeExtraMemoryPaths(this.workspaceDir, this.settings.extraPaths).map((value) => value.replace(/\\/g, "/")).toSorted();
|
|
2937
|
+
return hashText(JSON.stringify({
|
|
2938
|
+
extraPaths,
|
|
2939
|
+
multimodal: {
|
|
2940
|
+
enabled: this.settings.multimodal.enabled,
|
|
2941
|
+
modalities: [...this.settings.multimodal.modalities].toSorted(),
|
|
2942
|
+
maxFileBytes: this.settings.multimodal.maxFileBytes
|
|
2943
|
+
}
|
|
2944
|
+
}));
|
|
2945
|
+
}
|
|
2946
|
+
metaSourcesDiffer(meta, configuredSources) {
|
|
2947
|
+
const metaSources = this.normalizeMetaSources(meta);
|
|
2948
|
+
if (metaSources.length !== configuredSources.length) return true;
|
|
2949
|
+
return metaSources.some((source, index) => source !== configuredSources[index]);
|
|
2950
|
+
}
|
|
2951
|
+
};
|
|
2952
|
+
//#endregion
|
|
2953
|
+
//#region src/memory/manager-embedding-ops.ts
|
|
2954
|
+
const VECTOR_TABLE$1 = "chunks_vec";
|
|
2955
|
+
const FTS_TABLE$1 = "chunks_fts";
|
|
2956
|
+
const EMBEDDING_CACHE_TABLE$1 = "embedding_cache";
|
|
2957
|
+
const EMBEDDING_BATCH_MAX_TOKENS = 8e3;
|
|
2958
|
+
const EMBEDDING_INDEX_CONCURRENCY = 4;
|
|
2959
|
+
const EMBEDDING_RETRY_MAX_ATTEMPTS = 3;
|
|
2960
|
+
const EMBEDDING_RETRY_BASE_DELAY_MS = 500;
|
|
2961
|
+
const EMBEDDING_RETRY_MAX_DELAY_MS = 8e3;
|
|
2962
|
+
const BATCH_FAILURE_LIMIT$1 = 2;
|
|
2963
|
+
const EMBEDDING_QUERY_TIMEOUT_REMOTE_MS = 6e4;
|
|
2964
|
+
const EMBEDDING_QUERY_TIMEOUT_LOCAL_MS = 5 * 6e4;
|
|
2965
|
+
const EMBEDDING_BATCH_TIMEOUT_REMOTE_MS = 2 * 6e4;
|
|
2966
|
+
const EMBEDDING_BATCH_TIMEOUT_LOCAL_MS = 10 * 6e4;
|
|
2967
|
+
const vectorToBlob$1 = (embedding) => Buffer.from(new Float32Array(embedding).buffer);
|
|
2968
|
+
const log$1 = createSubsystemLogger("memory");
|
|
2969
|
+
var MemoryManagerEmbeddingOps = class extends MemoryManagerSyncOps {
|
|
2970
|
+
buildEmbeddingBatches(chunks) {
|
|
2971
|
+
const batches = [];
|
|
2972
|
+
let current = [];
|
|
2973
|
+
let currentTokens = 0;
|
|
2974
|
+
for (const chunk of chunks) {
|
|
2975
|
+
const estimate = chunk.embeddingInput ? estimateStructuredEmbeddingInputBytes(chunk.embeddingInput) : estimateUtf8Bytes(chunk.text);
|
|
2976
|
+
if (current.length > 0 && currentTokens + estimate > EMBEDDING_BATCH_MAX_TOKENS) {
|
|
2977
|
+
batches.push(current);
|
|
2978
|
+
current = [];
|
|
2979
|
+
currentTokens = 0;
|
|
2980
|
+
}
|
|
2981
|
+
if (current.length === 0 && estimate > EMBEDDING_BATCH_MAX_TOKENS) {
|
|
2982
|
+
batches.push([chunk]);
|
|
2983
|
+
continue;
|
|
2984
|
+
}
|
|
2985
|
+
current.push(chunk);
|
|
2986
|
+
currentTokens += estimate;
|
|
2987
|
+
}
|
|
2988
|
+
if (current.length > 0) batches.push(current);
|
|
2989
|
+
return batches;
|
|
2990
|
+
}
|
|
2991
|
+
loadEmbeddingCache(hashes) {
|
|
2992
|
+
if (!this.cache.enabled || !this.provider) return /* @__PURE__ */ new Map();
|
|
2993
|
+
if (hashes.length === 0) return /* @__PURE__ */ new Map();
|
|
2994
|
+
const unique = [];
|
|
2995
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2996
|
+
for (const hash of hashes) {
|
|
2997
|
+
if (!hash) continue;
|
|
2998
|
+
if (seen.has(hash)) continue;
|
|
2999
|
+
seen.add(hash);
|
|
3000
|
+
unique.push(hash);
|
|
3001
|
+
}
|
|
3002
|
+
if (unique.length === 0) return /* @__PURE__ */ new Map();
|
|
3003
|
+
const out = /* @__PURE__ */ new Map();
|
|
3004
|
+
const baseParams = [
|
|
3005
|
+
this.provider.id,
|
|
3006
|
+
this.provider.model,
|
|
3007
|
+
this.providerKey
|
|
3008
|
+
];
|
|
3009
|
+
const batchSize = 400;
|
|
3010
|
+
for (let start = 0; start < unique.length; start += batchSize) {
|
|
3011
|
+
const batch = unique.slice(start, start + batchSize);
|
|
3012
|
+
const placeholders = batch.map(() => "?").join(", ");
|
|
3013
|
+
const rows = this.db.prepare(`SELECT hash, embedding FROM ${EMBEDDING_CACHE_TABLE$1}\n WHERE provider = ? AND model = ? AND provider_key = ? AND hash IN (${placeholders})`).all(...baseParams, ...batch);
|
|
3014
|
+
for (const row of rows) out.set(row.hash, parseEmbedding(row.embedding));
|
|
3015
|
+
}
|
|
3016
|
+
return out;
|
|
3017
|
+
}
|
|
3018
|
+
upsertEmbeddingCache(entries) {
|
|
3019
|
+
if (!this.cache.enabled || !this.provider) return;
|
|
3020
|
+
if (entries.length === 0) return;
|
|
3021
|
+
const now = Date.now();
|
|
3022
|
+
const stmt = this.db.prepare(`INSERT INTO ${EMBEDDING_CACHE_TABLE$1} (provider, model, provider_key, hash, embedding, dims, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(provider, model, provider_key, hash) DO UPDATE SET\n embedding=excluded.embedding,\n dims=excluded.dims,\n updated_at=excluded.updated_at`);
|
|
3023
|
+
for (const entry of entries) {
|
|
3024
|
+
const embedding = entry.embedding ?? [];
|
|
3025
|
+
stmt.run(this.provider.id, this.provider.model, this.providerKey, entry.hash, JSON.stringify(embedding), embedding.length, now);
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
pruneEmbeddingCacheIfNeeded() {
|
|
3029
|
+
if (!this.cache.enabled) return;
|
|
3030
|
+
const max = this.cache.maxEntries;
|
|
3031
|
+
if (!max || max <= 0) return;
|
|
3032
|
+
const count = this.db.prepare(`SELECT COUNT(*) as c FROM ${EMBEDDING_CACHE_TABLE$1}`).get()?.c ?? 0;
|
|
3033
|
+
if (count <= max) return;
|
|
3034
|
+
const excess = count - max;
|
|
3035
|
+
this.db.prepare(`DELETE FROM ${EMBEDDING_CACHE_TABLE$1}\n WHERE rowid IN (\n SELECT rowid FROM ${EMBEDDING_CACHE_TABLE$1}\n ORDER BY updated_at ASC\n LIMIT ?\n )`).run(excess);
|
|
3036
|
+
}
|
|
3037
|
+
async embedChunksInBatches(chunks) {
|
|
3038
|
+
if (chunks.length === 0) return [];
|
|
3039
|
+
const { embeddings, missing } = this.collectCachedEmbeddings(chunks);
|
|
3040
|
+
if (missing.length === 0) return embeddings;
|
|
3041
|
+
const missingChunks = missing.map((m) => m.chunk);
|
|
3042
|
+
const batches = this.buildEmbeddingBatches(missingChunks);
|
|
3043
|
+
const toCache = [];
|
|
3044
|
+
const provider = this.provider;
|
|
3045
|
+
if (!provider) throw new Error("Cannot embed batch in FTS-only mode (no embedding provider)");
|
|
3046
|
+
let cursor = 0;
|
|
3047
|
+
for (const batch of batches) {
|
|
3048
|
+
const inputs = batch.map((chunk) => chunk.embeddingInput ?? { text: chunk.text });
|
|
3049
|
+
const hasStructuredInputs = inputs.some((input) => hasNonTextEmbeddingParts(input));
|
|
3050
|
+
if (hasStructuredInputs && !provider.embedBatchInputs) throw new Error(`Embedding provider "${provider.id}" does not support multimodal memory inputs.`);
|
|
3051
|
+
const batchEmbeddings = hasStructuredInputs ? await this.embedBatchInputsWithRetry(inputs) : await this.embedBatchWithRetry(batch.map((chunk) => chunk.text));
|
|
3052
|
+
for (let i = 0; i < batch.length; i += 1) {
|
|
3053
|
+
const item = missing[cursor + i];
|
|
3054
|
+
const embedding = batchEmbeddings[i] ?? [];
|
|
3055
|
+
if (item) {
|
|
3056
|
+
embeddings[item.index] = embedding;
|
|
3057
|
+
toCache.push({
|
|
3058
|
+
hash: item.chunk.hash,
|
|
3059
|
+
embedding
|
|
3060
|
+
});
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
cursor += batch.length;
|
|
3064
|
+
}
|
|
3065
|
+
this.upsertEmbeddingCache(toCache);
|
|
3066
|
+
return embeddings;
|
|
3067
|
+
}
|
|
3068
|
+
computeProviderKey() {
|
|
3069
|
+
if (!this.provider) return hashText(JSON.stringify({
|
|
3070
|
+
provider: "none",
|
|
3071
|
+
model: "fts-only"
|
|
3072
|
+
}));
|
|
3073
|
+
if (this.provider.id === "openai" && this.openAi) {
|
|
3074
|
+
const entries = Object.entries(this.openAi.headers).filter(([key]) => key.toLowerCase() !== "authorization").toSorted(([a], [b]) => a.localeCompare(b)).map(([key, value]) => [key, value]);
|
|
3075
|
+
return hashText(JSON.stringify({
|
|
3076
|
+
provider: "openai",
|
|
3077
|
+
baseUrl: this.openAi.baseUrl,
|
|
3078
|
+
model: this.openAi.model,
|
|
3079
|
+
headers: entries
|
|
3080
|
+
}));
|
|
3081
|
+
}
|
|
3082
|
+
if (this.provider.id === "gemini" && this.gemini) {
|
|
3083
|
+
const entries = Object.entries(this.gemini.headers).filter(([key]) => {
|
|
3084
|
+
const lower = key.toLowerCase();
|
|
3085
|
+
return lower !== "authorization" && lower !== "x-goog-api-key";
|
|
3086
|
+
}).toSorted(([a], [b]) => a.localeCompare(b)).map(([key, value]) => [key, value]);
|
|
3087
|
+
return hashText(JSON.stringify({
|
|
3088
|
+
provider: "gemini",
|
|
3089
|
+
baseUrl: this.gemini.baseUrl,
|
|
3090
|
+
model: this.gemini.model,
|
|
3091
|
+
outputDimensionality: this.gemini.outputDimensionality,
|
|
3092
|
+
headers: entries
|
|
3093
|
+
}));
|
|
3094
|
+
}
|
|
3095
|
+
return hashText(JSON.stringify({
|
|
3096
|
+
provider: this.provider.id,
|
|
3097
|
+
model: this.provider.model
|
|
3098
|
+
}));
|
|
3099
|
+
}
|
|
3100
|
+
async embedChunksWithBatch(chunks, entry, source) {
|
|
3101
|
+
if (!this.provider) return this.embedChunksInBatches(chunks);
|
|
3102
|
+
if (this.provider.id === "openai" && this.openAi) return this.embedChunksWithOpenAiBatch(chunks, entry, source);
|
|
3103
|
+
if (this.provider.id === "gemini" && this.gemini) return this.embedChunksWithGeminiBatch(chunks, entry, source);
|
|
3104
|
+
if (this.provider.id === "voyage" && this.voyage) return this.embedChunksWithVoyageBatch(chunks, entry, source);
|
|
3105
|
+
return this.embedChunksInBatches(chunks);
|
|
3106
|
+
}
|
|
3107
|
+
collectCachedEmbeddings(chunks) {
|
|
3108
|
+
const cached = this.loadEmbeddingCache(chunks.map((chunk) => chunk.hash));
|
|
3109
|
+
const embeddings = Array.from({ length: chunks.length }, () => []);
|
|
3110
|
+
const missing = [];
|
|
3111
|
+
for (let i = 0; i < chunks.length; i += 1) {
|
|
3112
|
+
const chunk = chunks[i];
|
|
3113
|
+
const hit = chunk?.hash ? cached.get(chunk.hash) : void 0;
|
|
3114
|
+
if (hit && hit.length > 0) embeddings[i] = hit;
|
|
3115
|
+
else if (chunk) missing.push({
|
|
3116
|
+
index: i,
|
|
3117
|
+
chunk
|
|
3118
|
+
});
|
|
3119
|
+
}
|
|
3120
|
+
return {
|
|
3121
|
+
embeddings,
|
|
3122
|
+
missing
|
|
3123
|
+
};
|
|
3124
|
+
}
|
|
3125
|
+
buildBatchCustomId(params) {
|
|
3126
|
+
return hashText(`${params.source}:${params.entry.path}:${params.chunk.startLine}:${params.chunk.endLine}:${params.chunk.hash}:${params.index}`);
|
|
3127
|
+
}
|
|
3128
|
+
buildBatchRequests(params) {
|
|
3129
|
+
const requests = [];
|
|
3130
|
+
const mapping = /* @__PURE__ */ new Map();
|
|
3131
|
+
for (const item of params.missing) {
|
|
3132
|
+
const chunk = item.chunk;
|
|
3133
|
+
const customId = this.buildBatchCustomId({
|
|
3134
|
+
source: params.source,
|
|
3135
|
+
entry: params.entry,
|
|
3136
|
+
chunk,
|
|
3137
|
+
index: item.index
|
|
3138
|
+
});
|
|
3139
|
+
mapping.set(customId, {
|
|
3140
|
+
index: item.index,
|
|
3141
|
+
hash: chunk.hash
|
|
3142
|
+
});
|
|
3143
|
+
const built = params.build(chunk);
|
|
3144
|
+
requests.push({
|
|
3145
|
+
custom_id: customId,
|
|
3146
|
+
...built
|
|
3147
|
+
});
|
|
3148
|
+
}
|
|
3149
|
+
return {
|
|
3150
|
+
requests,
|
|
3151
|
+
mapping
|
|
3152
|
+
};
|
|
3153
|
+
}
|
|
3154
|
+
applyBatchEmbeddings(params) {
|
|
3155
|
+
const toCache = [];
|
|
3156
|
+
for (const [customId, embedding] of params.byCustomId.entries()) {
|
|
3157
|
+
const mapped = params.mapping.get(customId);
|
|
3158
|
+
if (!mapped) continue;
|
|
3159
|
+
params.embeddings[mapped.index] = embedding;
|
|
3160
|
+
toCache.push({
|
|
3161
|
+
hash: mapped.hash,
|
|
3162
|
+
embedding
|
|
3163
|
+
});
|
|
3164
|
+
}
|
|
3165
|
+
this.upsertEmbeddingCache(toCache);
|
|
3166
|
+
}
|
|
3167
|
+
buildEmbeddingBatchRunnerOptions(params) {
|
|
3168
|
+
const { requests, chunks, source } = params;
|
|
3169
|
+
return {
|
|
3170
|
+
agentId: this.agentId,
|
|
3171
|
+
requests,
|
|
3172
|
+
wait: this.batch.wait,
|
|
3173
|
+
concurrency: this.batch.concurrency,
|
|
3174
|
+
pollIntervalMs: this.batch.pollIntervalMs,
|
|
3175
|
+
timeoutMs: this.batch.timeoutMs,
|
|
3176
|
+
debug: (message, data) => log$1.debug(message, data ? {
|
|
3177
|
+
...data,
|
|
3178
|
+
source,
|
|
3179
|
+
chunks: chunks.length
|
|
3180
|
+
} : {
|
|
3181
|
+
source,
|
|
3182
|
+
chunks: chunks.length
|
|
3183
|
+
})
|
|
3184
|
+
};
|
|
3185
|
+
}
|
|
3186
|
+
async embedChunksWithProviderBatch(params) {
|
|
3187
|
+
if (!params.enabled) return this.embedChunksInBatches(params.chunks);
|
|
3188
|
+
if (params.chunks.length === 0) return [];
|
|
3189
|
+
const { embeddings, missing } = this.collectCachedEmbeddings(params.chunks);
|
|
3190
|
+
if (missing.length === 0) return embeddings;
|
|
3191
|
+
const { requests, mapping } = this.buildBatchRequests({
|
|
3192
|
+
missing,
|
|
3193
|
+
entry: params.entry,
|
|
3194
|
+
source: params.source,
|
|
3195
|
+
build: params.buildRequest
|
|
3196
|
+
});
|
|
3197
|
+
const runnerOptions = this.buildEmbeddingBatchRunnerOptions({
|
|
3198
|
+
requests,
|
|
3199
|
+
chunks: params.chunks,
|
|
3200
|
+
source: params.source
|
|
3201
|
+
});
|
|
3202
|
+
const batchResult = await this.runBatchWithFallback({
|
|
3203
|
+
provider: params.provider,
|
|
3204
|
+
run: async () => await params.runBatch(runnerOptions),
|
|
3205
|
+
fallback: async () => await this.embedChunksInBatches(params.chunks)
|
|
3206
|
+
});
|
|
3207
|
+
if (Array.isArray(batchResult)) return batchResult;
|
|
3208
|
+
this.applyBatchEmbeddings({
|
|
3209
|
+
byCustomId: batchResult,
|
|
3210
|
+
mapping,
|
|
3211
|
+
embeddings
|
|
3212
|
+
});
|
|
3213
|
+
return embeddings;
|
|
3214
|
+
}
|
|
3215
|
+
async embedChunksWithVoyageBatch(chunks, entry, source) {
|
|
3216
|
+
const voyage = this.voyage;
|
|
3217
|
+
return await this.embedChunksWithProviderBatch({
|
|
3218
|
+
chunks,
|
|
3219
|
+
entry,
|
|
3220
|
+
source,
|
|
3221
|
+
provider: "voyage",
|
|
3222
|
+
enabled: Boolean(voyage),
|
|
3223
|
+
buildRequest: (chunk) => ({ body: { input: chunk.text } }),
|
|
3224
|
+
runBatch: async (runnerOptions) => await runVoyageEmbeddingBatches({
|
|
3225
|
+
client: voyage,
|
|
3226
|
+
...runnerOptions
|
|
3227
|
+
})
|
|
3228
|
+
});
|
|
3229
|
+
}
|
|
3230
|
+
async embedChunksWithOpenAiBatch(chunks, entry, source) {
|
|
3231
|
+
const openAi = this.openAi;
|
|
3232
|
+
return await this.embedChunksWithProviderBatch({
|
|
3233
|
+
chunks,
|
|
3234
|
+
entry,
|
|
3235
|
+
source,
|
|
3236
|
+
provider: "openai",
|
|
3237
|
+
enabled: Boolean(openAi),
|
|
3238
|
+
buildRequest: (chunk) => ({
|
|
3239
|
+
method: "POST",
|
|
3240
|
+
url: OPENAI_BATCH_ENDPOINT,
|
|
3241
|
+
body: {
|
|
3242
|
+
model: openAi?.model ?? this.provider?.model ?? "text-embedding-3-small",
|
|
3243
|
+
input: chunk.text
|
|
3244
|
+
}
|
|
3245
|
+
}),
|
|
3246
|
+
runBatch: async (runnerOptions) => await runOpenAiEmbeddingBatches({
|
|
3247
|
+
openAi,
|
|
3248
|
+
...runnerOptions
|
|
3249
|
+
})
|
|
3250
|
+
});
|
|
3251
|
+
}
|
|
3252
|
+
async embedChunksWithGeminiBatch(chunks, entry, source) {
|
|
3253
|
+
const gemini = this.gemini;
|
|
3254
|
+
if (chunks.some((chunk) => hasNonTextEmbeddingParts(chunk.embeddingInput))) return await this.embedChunksInBatches(chunks);
|
|
3255
|
+
return await this.embedChunksWithProviderBatch({
|
|
3256
|
+
chunks,
|
|
3257
|
+
entry,
|
|
3258
|
+
source,
|
|
3259
|
+
provider: "gemini",
|
|
3260
|
+
enabled: Boolean(gemini),
|
|
3261
|
+
buildRequest: (chunk) => ({ request: buildGeminiEmbeddingRequest({
|
|
3262
|
+
input: chunk.embeddingInput ?? { text: chunk.text },
|
|
3263
|
+
taskType: "RETRIEVAL_DOCUMENT",
|
|
3264
|
+
modelPath: this.gemini?.modelPath,
|
|
3265
|
+
outputDimensionality: this.gemini?.outputDimensionality
|
|
3266
|
+
}) }),
|
|
3267
|
+
runBatch: async (runnerOptions) => await runGeminiEmbeddingBatches({
|
|
3268
|
+
gemini,
|
|
3269
|
+
...runnerOptions
|
|
3270
|
+
})
|
|
3271
|
+
});
|
|
3272
|
+
}
|
|
3273
|
+
async embedBatchWithRetry(texts) {
|
|
3274
|
+
if (texts.length === 0) return [];
|
|
3275
|
+
if (!this.provider) throw new Error("Cannot embed batch in FTS-only mode (no embedding provider)");
|
|
3276
|
+
let attempt = 0;
|
|
3277
|
+
let delayMs = EMBEDDING_RETRY_BASE_DELAY_MS;
|
|
3278
|
+
while (true) try {
|
|
3279
|
+
const timeoutMs = this.resolveEmbeddingTimeout("batch");
|
|
3280
|
+
log$1.debug("memory embeddings: batch start", {
|
|
3281
|
+
provider: this.provider.id,
|
|
3282
|
+
items: texts.length,
|
|
3283
|
+
timeoutMs
|
|
3284
|
+
});
|
|
3285
|
+
return await this.withTimeout(this.provider.embedBatch(texts), timeoutMs, `memory embeddings batch timed out after ${Math.round(timeoutMs / 1e3)}s`);
|
|
3286
|
+
} catch (err) {
|
|
3287
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3288
|
+
if (!this.isRetryableEmbeddingError(message) || attempt >= EMBEDDING_RETRY_MAX_ATTEMPTS) throw err;
|
|
3289
|
+
await this.waitForEmbeddingRetry(delayMs, "retrying");
|
|
3290
|
+
delayMs *= 2;
|
|
3291
|
+
attempt += 1;
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
async embedBatchInputsWithRetry(inputs) {
|
|
3295
|
+
if (inputs.length === 0) return [];
|
|
3296
|
+
if (!this.provider?.embedBatchInputs) return await this.embedBatchWithRetry(inputs.map((input) => input.text));
|
|
3297
|
+
let attempt = 0;
|
|
3298
|
+
let delayMs = EMBEDDING_RETRY_BASE_DELAY_MS;
|
|
3299
|
+
while (true) try {
|
|
3300
|
+
const timeoutMs = this.resolveEmbeddingTimeout("batch");
|
|
3301
|
+
log$1.debug("memory embeddings: structured batch start", {
|
|
3302
|
+
provider: this.provider.id,
|
|
3303
|
+
items: inputs.length,
|
|
3304
|
+
timeoutMs
|
|
3305
|
+
});
|
|
3306
|
+
return await this.withTimeout(this.provider.embedBatchInputs(inputs), timeoutMs, `memory embeddings batch timed out after ${Math.round(timeoutMs / 1e3)}s`);
|
|
3307
|
+
} catch (err) {
|
|
3308
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3309
|
+
if (!this.isRetryableEmbeddingError(message) || attempt >= EMBEDDING_RETRY_MAX_ATTEMPTS) throw err;
|
|
3310
|
+
await this.waitForEmbeddingRetry(delayMs, "retrying structured batch");
|
|
3311
|
+
delayMs *= 2;
|
|
3312
|
+
attempt += 1;
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
async waitForEmbeddingRetry(delayMs, action) {
|
|
3316
|
+
const waitMs = Math.min(EMBEDDING_RETRY_MAX_DELAY_MS, Math.round(delayMs * (1 + Math.random() * .2)));
|
|
3317
|
+
log$1.warn(`memory embeddings rate limited; ${action} in ${waitMs}ms`);
|
|
3318
|
+
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
3319
|
+
}
|
|
3320
|
+
isRetryableEmbeddingError(message) {
|
|
3321
|
+
return /(rate[_ ]limit|too many requests|429|resource has been exhausted|5\d\d|cloudflare|tokens per day)/i.test(message);
|
|
3322
|
+
}
|
|
3323
|
+
resolveEmbeddingTimeout(kind) {
|
|
3324
|
+
const isLocal = this.provider?.id === "local";
|
|
3325
|
+
if (kind === "query") return isLocal ? EMBEDDING_QUERY_TIMEOUT_LOCAL_MS : EMBEDDING_QUERY_TIMEOUT_REMOTE_MS;
|
|
3326
|
+
return isLocal ? EMBEDDING_BATCH_TIMEOUT_LOCAL_MS : EMBEDDING_BATCH_TIMEOUT_REMOTE_MS;
|
|
3327
|
+
}
|
|
3328
|
+
async embedQueryWithTimeout(text) {
|
|
3329
|
+
if (!this.provider) throw new Error("Cannot embed query in FTS-only mode (no embedding provider)");
|
|
3330
|
+
const timeoutMs = this.resolveEmbeddingTimeout("query");
|
|
3331
|
+
log$1.debug("memory embeddings: query start", {
|
|
3332
|
+
provider: this.provider.id,
|
|
3333
|
+
timeoutMs
|
|
3334
|
+
});
|
|
3335
|
+
return await this.withTimeout(this.provider.embedQuery(text), timeoutMs, `memory embeddings query timed out after ${Math.round(timeoutMs / 1e3)}s`);
|
|
3336
|
+
}
|
|
3337
|
+
async withTimeout(promise, timeoutMs, message) {
|
|
3338
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return await promise;
|
|
3339
|
+
let timer = null;
|
|
3340
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
3341
|
+
timer = setTimeout(() => reject(new Error(message)), timeoutMs);
|
|
3342
|
+
});
|
|
3343
|
+
try {
|
|
3344
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
3345
|
+
} finally {
|
|
3346
|
+
if (timer) clearTimeout(timer);
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
async withBatchFailureLock(fn) {
|
|
3350
|
+
let release;
|
|
3351
|
+
const wait = this.batchFailureLock;
|
|
3352
|
+
this.batchFailureLock = new Promise((resolve) => {
|
|
3353
|
+
release = resolve;
|
|
3354
|
+
});
|
|
3355
|
+
await wait;
|
|
3356
|
+
try {
|
|
3357
|
+
return await fn();
|
|
3358
|
+
} finally {
|
|
3359
|
+
release();
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
async resetBatchFailureCount() {
|
|
3363
|
+
await this.withBatchFailureLock(async () => {
|
|
3364
|
+
if (this.batchFailureCount > 0) log$1.debug("memory embeddings: batch recovered; resetting failure count");
|
|
3365
|
+
this.batchFailureCount = 0;
|
|
3366
|
+
this.batchFailureLastError = void 0;
|
|
3367
|
+
this.batchFailureLastProvider = void 0;
|
|
3368
|
+
});
|
|
3369
|
+
}
|
|
3370
|
+
async recordBatchFailure(params) {
|
|
3371
|
+
return await this.withBatchFailureLock(async () => {
|
|
3372
|
+
if (!this.batch.enabled) return {
|
|
3373
|
+
disabled: true,
|
|
3374
|
+
count: this.batchFailureCount
|
|
3375
|
+
};
|
|
3376
|
+
const increment = params.forceDisable ? BATCH_FAILURE_LIMIT$1 : Math.max(1, params.attempts ?? 1);
|
|
3377
|
+
this.batchFailureCount += increment;
|
|
3378
|
+
this.batchFailureLastError = params.message;
|
|
3379
|
+
this.batchFailureLastProvider = params.provider;
|
|
3380
|
+
const disabled = params.forceDisable || this.batchFailureCount >= BATCH_FAILURE_LIMIT$1;
|
|
3381
|
+
if (disabled) this.batch.enabled = false;
|
|
3382
|
+
return {
|
|
3383
|
+
disabled,
|
|
3384
|
+
count: this.batchFailureCount
|
|
3385
|
+
};
|
|
3386
|
+
});
|
|
3387
|
+
}
|
|
3388
|
+
isBatchTimeoutError(message) {
|
|
3389
|
+
return /timed out|timeout/i.test(message);
|
|
3390
|
+
}
|
|
3391
|
+
async runBatchWithTimeoutRetry(params) {
|
|
3392
|
+
try {
|
|
3393
|
+
return await params.run();
|
|
3394
|
+
} catch (err) {
|
|
3395
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3396
|
+
if (this.isBatchTimeoutError(message)) {
|
|
3397
|
+
log$1.warn(`memory embeddings: ${params.provider} batch timed out; retrying once`);
|
|
3398
|
+
try {
|
|
3399
|
+
return await params.run();
|
|
3400
|
+
} catch (retryErr) {
|
|
3401
|
+
retryErr.batchAttempts = 2;
|
|
3402
|
+
throw retryErr;
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
throw err;
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
async runBatchWithFallback(params) {
|
|
3409
|
+
if (!this.batch.enabled) return await params.fallback();
|
|
3410
|
+
try {
|
|
3411
|
+
const result = await this.runBatchWithTimeoutRetry({
|
|
3412
|
+
provider: params.provider,
|
|
3413
|
+
run: params.run
|
|
3414
|
+
});
|
|
3415
|
+
await this.resetBatchFailureCount();
|
|
3416
|
+
return result;
|
|
3417
|
+
} catch (err) {
|
|
3418
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3419
|
+
const attempts = err.batchAttempts ?? 1;
|
|
3420
|
+
const forceDisable = /asyncBatchEmbedContent not available/i.test(message);
|
|
3421
|
+
const failure = await this.recordBatchFailure({
|
|
3422
|
+
provider: params.provider,
|
|
3423
|
+
message,
|
|
3424
|
+
attempts,
|
|
3425
|
+
forceDisable
|
|
3426
|
+
});
|
|
3427
|
+
const suffix = failure.disabled ? "disabling batch" : "keeping batch enabled";
|
|
3428
|
+
log$1.warn(`memory embeddings: ${params.provider} batch failed (${failure.count}/${BATCH_FAILURE_LIMIT$1}); ${suffix}; falling back to non-batch embeddings: ${message}`);
|
|
3429
|
+
return await params.fallback();
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
getIndexConcurrency() {
|
|
3433
|
+
return this.batch.enabled ? this.batch.concurrency : EMBEDDING_INDEX_CONCURRENCY;
|
|
3434
|
+
}
|
|
3435
|
+
clearIndexedFileData(pathname, source) {
|
|
3436
|
+
if (this.vector.enabled) try {
|
|
3437
|
+
this.db.prepare(`DELETE FROM ${VECTOR_TABLE$1} WHERE id IN (SELECT id FROM chunks WHERE path = ? AND source = ?)`).run(pathname, source);
|
|
3438
|
+
} catch {}
|
|
3439
|
+
if (this.fts.enabled && this.fts.available && this.provider) try {
|
|
3440
|
+
this.db.prepare(`DELETE FROM ${FTS_TABLE$1} WHERE path = ? AND source = ? AND model = ?`).run(pathname, source, this.provider.model);
|
|
3441
|
+
} catch {}
|
|
3442
|
+
this.db.prepare(`DELETE FROM chunks WHERE path = ? AND source = ?`).run(pathname, source);
|
|
3443
|
+
}
|
|
3444
|
+
upsertFileRecord(entry, source) {
|
|
3445
|
+
this.db.prepare(`INSERT INTO files (path, source, hash, mtime, size) VALUES (?, ?, ?, ?, ?)
|
|
3446
|
+
ON CONFLICT(path) DO UPDATE SET
|
|
3447
|
+
source=excluded.source,
|
|
3448
|
+
hash=excluded.hash,
|
|
3449
|
+
mtime=excluded.mtime,
|
|
3450
|
+
size=excluded.size`).run(entry.path, source, entry.hash, entry.mtimeMs, entry.size);
|
|
3451
|
+
}
|
|
3452
|
+
deleteFileRecord(pathname, source) {
|
|
3453
|
+
this.db.prepare(`DELETE FROM files WHERE path = ? AND source = ?`).run(pathname, source);
|
|
3454
|
+
}
|
|
3455
|
+
isStructuredInputTooLargeError(message) {
|
|
3456
|
+
return /(413|payload too large|request too large|input too large|too many tokens|input limit|request size)/i.test(message);
|
|
3457
|
+
}
|
|
3458
|
+
async indexFile(entry, options) {
|
|
3459
|
+
if (!this.provider) {
|
|
3460
|
+
log$1.debug("Skipping embedding indexing in FTS-only mode", {
|
|
3461
|
+
path: entry.path,
|
|
3462
|
+
source: options.source
|
|
3463
|
+
});
|
|
3464
|
+
return;
|
|
3465
|
+
}
|
|
3466
|
+
let chunks;
|
|
3467
|
+
let structuredInputBytes;
|
|
3468
|
+
if ("kind" in entry && entry.kind === "multimodal") {
|
|
3469
|
+
const multimodalChunk = await buildMultimodalChunkForIndexing(entry);
|
|
3470
|
+
if (!multimodalChunk) {
|
|
3471
|
+
this.clearIndexedFileData(entry.path, options.source);
|
|
3472
|
+
this.deleteFileRecord(entry.path, options.source);
|
|
3473
|
+
return;
|
|
3474
|
+
}
|
|
3475
|
+
structuredInputBytes = multimodalChunk.structuredInputBytes;
|
|
3476
|
+
chunks = [multimodalChunk.chunk];
|
|
3477
|
+
} else {
|
|
3478
|
+
const content = options.content ?? await fs$1.readFile(entry.absPath, "utf-8");
|
|
3479
|
+
chunks = enforceEmbeddingMaxInputTokens(this.provider, chunkMarkdown(content, this.settings.chunking).filter((chunk) => chunk.text.trim().length > 0), EMBEDDING_BATCH_MAX_TOKENS);
|
|
3480
|
+
if (options.source === "sessions" && "lineMap" in entry) remapChunkLines(chunks, entry.lineMap);
|
|
3481
|
+
}
|
|
3482
|
+
let embeddings;
|
|
3483
|
+
try {
|
|
3484
|
+
embeddings = this.batch.enabled ? await this.embedChunksWithBatch(chunks, entry, options.source) : await this.embedChunksInBatches(chunks);
|
|
3485
|
+
} catch (err) {
|
|
3486
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3487
|
+
if ("kind" in entry && entry.kind === "multimodal" && this.isStructuredInputTooLargeError(message)) {
|
|
3488
|
+
log$1.warn("memory embeddings: skipping multimodal file rejected as too large", {
|
|
3489
|
+
path: entry.path,
|
|
3490
|
+
bytes: structuredInputBytes,
|
|
3491
|
+
provider: this.provider.id,
|
|
3492
|
+
model: this.provider.model,
|
|
3493
|
+
error: message
|
|
3494
|
+
});
|
|
3495
|
+
this.clearIndexedFileData(entry.path, options.source);
|
|
3496
|
+
this.upsertFileRecord(entry, options.source);
|
|
3497
|
+
return;
|
|
3498
|
+
}
|
|
3499
|
+
throw err;
|
|
3500
|
+
}
|
|
3501
|
+
const sample = embeddings.find((embedding) => embedding.length > 0);
|
|
3502
|
+
const vectorReady = sample ? await this.ensureVectorReady(sample.length) : false;
|
|
3503
|
+
const now = Date.now();
|
|
3504
|
+
this.clearIndexedFileData(entry.path, options.source);
|
|
3505
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
3506
|
+
const chunk = chunks[i];
|
|
3507
|
+
const embedding = embeddings[i] ?? [];
|
|
3508
|
+
const id = hashText(`${options.source}:${entry.path}:${chunk.startLine}:${chunk.endLine}:${chunk.hash}:${this.provider.model}`);
|
|
3509
|
+
this.db.prepare(`INSERT INTO chunks (id, path, source, start_line, end_line, hash, model, text, embedding, updated_at)
|
|
3510
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3511
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
3512
|
+
hash=excluded.hash,
|
|
3513
|
+
model=excluded.model,
|
|
3514
|
+
text=excluded.text,
|
|
3515
|
+
embedding=excluded.embedding,
|
|
3516
|
+
updated_at=excluded.updated_at`).run(id, entry.path, options.source, chunk.startLine, chunk.endLine, chunk.hash, this.provider.model, chunk.text, JSON.stringify(embedding), now);
|
|
3517
|
+
if (vectorReady && embedding.length > 0) {
|
|
3518
|
+
try {
|
|
3519
|
+
this.db.prepare(`DELETE FROM ${VECTOR_TABLE$1} WHERE id = ?`).run(id);
|
|
3520
|
+
} catch {}
|
|
3521
|
+
this.db.prepare(`INSERT INTO ${VECTOR_TABLE$1} (id, embedding) VALUES (?, ?)`).run(id, vectorToBlob$1(embedding));
|
|
3522
|
+
}
|
|
3523
|
+
if (this.fts.enabled && this.fts.available) this.db.prepare(`INSERT INTO ${FTS_TABLE$1} (text, id, path, source, model, start_line, end_line)\n VALUES (?, ?, ?, ?, ?, ?, ?)`).run(chunk.text, id, entry.path, options.source, this.provider.model, chunk.startLine, chunk.endLine);
|
|
3524
|
+
}
|
|
3525
|
+
this.upsertFileRecord(entry, options.source);
|
|
3526
|
+
}
|
|
3527
|
+
};
|
|
3528
|
+
//#endregion
|
|
3529
|
+
//#region src/memory/manager-search.ts
|
|
3530
|
+
const vectorToBlob = (embedding) => Buffer.from(new Float32Array(embedding).buffer);
|
|
3531
|
+
async function searchVector(params) {
|
|
3532
|
+
if (params.queryVec.length === 0 || params.limit <= 0) return [];
|
|
3533
|
+
if (await params.ensureVectorReady(params.queryVec.length)) return params.db.prepare(`SELECT c.id, c.path, c.start_line, c.end_line, c.text,
|
|
3534
|
+
c.source,
|
|
3535
|
+
vec_distance_cosine(v.embedding, ?) AS dist
|
|
3536
|
+
FROM ${params.vectorTable} v\n JOIN chunks c ON c.id = v.id\n WHERE c.model = ?${params.sourceFilterVec.sql}\n ORDER BY dist ASC\n LIMIT ?`).all(vectorToBlob(params.queryVec), params.providerModel, ...params.sourceFilterVec.params, params.limit).map((row) => ({
|
|
3537
|
+
id: row.id,
|
|
3538
|
+
path: row.path,
|
|
3539
|
+
startLine: row.start_line,
|
|
3540
|
+
endLine: row.end_line,
|
|
3541
|
+
score: 1 - row.dist,
|
|
3542
|
+
snippet: truncateUtf16Safe(row.text, params.snippetMaxChars),
|
|
3543
|
+
source: row.source
|
|
3544
|
+
}));
|
|
3545
|
+
return listChunks({
|
|
3546
|
+
db: params.db,
|
|
3547
|
+
providerModel: params.providerModel,
|
|
3548
|
+
sourceFilter: params.sourceFilterChunks
|
|
3549
|
+
}).map((chunk) => ({
|
|
3550
|
+
chunk,
|
|
3551
|
+
score: cosineSimilarity(params.queryVec, chunk.embedding)
|
|
3552
|
+
})).filter((entry) => Number.isFinite(entry.score)).toSorted((a, b) => b.score - a.score).slice(0, params.limit).map((entry) => ({
|
|
3553
|
+
id: entry.chunk.id,
|
|
3554
|
+
path: entry.chunk.path,
|
|
3555
|
+
startLine: entry.chunk.startLine,
|
|
3556
|
+
endLine: entry.chunk.endLine,
|
|
3557
|
+
score: entry.score,
|
|
3558
|
+
snippet: truncateUtf16Safe(entry.chunk.text, params.snippetMaxChars),
|
|
3559
|
+
source: entry.chunk.source
|
|
3560
|
+
}));
|
|
3561
|
+
}
|
|
3562
|
+
function listChunks(params) {
|
|
3563
|
+
return params.db.prepare(`SELECT id, path, start_line, end_line, text, embedding, source
|
|
3564
|
+
FROM chunks
|
|
3565
|
+
WHERE model = ?${params.sourceFilter.sql}`).all(params.providerModel, ...params.sourceFilter.params).map((row) => ({
|
|
3566
|
+
id: row.id,
|
|
3567
|
+
path: row.path,
|
|
3568
|
+
startLine: row.start_line,
|
|
3569
|
+
endLine: row.end_line,
|
|
3570
|
+
text: row.text,
|
|
3571
|
+
embedding: parseEmbedding(row.embedding),
|
|
3572
|
+
source: row.source
|
|
3573
|
+
}));
|
|
3574
|
+
}
|
|
3575
|
+
async function searchKeyword(params) {
|
|
3576
|
+
if (params.limit <= 0) return [];
|
|
3577
|
+
const ftsQuery = params.buildFtsQuery(params.query);
|
|
3578
|
+
if (!ftsQuery) return [];
|
|
3579
|
+
const modelClause = params.providerModel ? " AND model = ?" : "";
|
|
3580
|
+
const modelParams = params.providerModel ? [params.providerModel] : [];
|
|
3581
|
+
return params.db.prepare(`SELECT id, path, source, start_line, end_line, text,\n bm25(${params.ftsTable}) AS rank\n FROM ${params.ftsTable}\n WHERE ${params.ftsTable} MATCH ?${modelClause}${params.sourceFilter.sql}\n ORDER BY rank ASC\n LIMIT ?`).all(ftsQuery, ...modelParams, ...params.sourceFilter.params, params.limit).map((row) => {
|
|
3582
|
+
const textScore = params.bm25RankToScore(row.rank);
|
|
3583
|
+
return {
|
|
3584
|
+
id: row.id,
|
|
3585
|
+
path: row.path,
|
|
3586
|
+
startLine: row.start_line,
|
|
3587
|
+
endLine: row.end_line,
|
|
3588
|
+
score: textScore,
|
|
3589
|
+
textScore,
|
|
3590
|
+
snippet: truncateUtf16Safe(row.text, params.snippetMaxChars),
|
|
3591
|
+
source: row.source
|
|
3592
|
+
};
|
|
3593
|
+
});
|
|
3594
|
+
}
|
|
3595
|
+
//#endregion
|
|
3596
|
+
//#region src/memory/read-file.ts
|
|
3597
|
+
async function readMemoryFile(params) {
|
|
3598
|
+
const rawPath = params.relPath.trim();
|
|
3599
|
+
if (!rawPath) throw new Error("path required");
|
|
3600
|
+
const absPath = path.isAbsolute(rawPath) ? path.resolve(rawPath) : path.resolve(params.workspaceDir, rawPath);
|
|
3601
|
+
const relPath = path.relative(params.workspaceDir, absPath).replace(/\\/g, "/");
|
|
3602
|
+
const allowedWorkspace = relPath.length > 0 && !relPath.startsWith("..") && !path.isAbsolute(relPath) && isMemoryPath(relPath);
|
|
3603
|
+
let allowedAdditional = false;
|
|
3604
|
+
if (!allowedWorkspace && (params.extraPaths?.length ?? 0) > 0) {
|
|
3605
|
+
const additionalPaths = normalizeExtraMemoryPaths(params.workspaceDir, params.extraPaths);
|
|
3606
|
+
for (const additionalPath of additionalPaths) try {
|
|
3607
|
+
const stat = await fs$1.lstat(additionalPath);
|
|
3608
|
+
if (stat.isSymbolicLink()) continue;
|
|
3609
|
+
if (stat.isDirectory()) {
|
|
3610
|
+
if (absPath === additionalPath || absPath.startsWith(`${additionalPath}${path.sep}`)) {
|
|
3611
|
+
allowedAdditional = true;
|
|
3612
|
+
break;
|
|
3613
|
+
}
|
|
3614
|
+
continue;
|
|
3615
|
+
}
|
|
3616
|
+
if (stat.isFile() && absPath === additionalPath && absPath.endsWith(".md")) {
|
|
3617
|
+
allowedAdditional = true;
|
|
3618
|
+
break;
|
|
3619
|
+
}
|
|
3620
|
+
} catch {}
|
|
3621
|
+
}
|
|
3622
|
+
if (!allowedWorkspace && !allowedAdditional) throw new Error("path required");
|
|
3623
|
+
if (!absPath.endsWith(".md")) throw new Error("path required");
|
|
3624
|
+
if ((await statRegularFile(absPath)).missing) return {
|
|
3625
|
+
text: "",
|
|
3626
|
+
path: relPath
|
|
3627
|
+
};
|
|
3628
|
+
let content;
|
|
3629
|
+
try {
|
|
3630
|
+
content = await fs$1.readFile(absPath, "utf-8");
|
|
3631
|
+
} catch (err) {
|
|
3632
|
+
if (isFileMissingError(err)) return {
|
|
3633
|
+
text: "",
|
|
3634
|
+
path: relPath
|
|
3635
|
+
};
|
|
3636
|
+
throw err;
|
|
3637
|
+
}
|
|
3638
|
+
if (!params.from && !params.lines) return {
|
|
3639
|
+
text: content,
|
|
3640
|
+
path: relPath
|
|
3641
|
+
};
|
|
3642
|
+
const fileLines = content.split("\n");
|
|
3643
|
+
const start = Math.max(1, params.from ?? 1);
|
|
3644
|
+
const count = Math.max(1, params.lines ?? fileLines.length);
|
|
3645
|
+
return {
|
|
3646
|
+
text: fileLines.slice(start - 1, start - 1 + count).join("\n"),
|
|
3647
|
+
path: relPath
|
|
3648
|
+
};
|
|
3649
|
+
}
|
|
3650
|
+
async function readAgentMemoryFile(params) {
|
|
3651
|
+
const settings = resolveMemorySearchConfig(params.cfg, params.agentId);
|
|
3652
|
+
if (!settings) throw new Error("memory search disabled");
|
|
3653
|
+
return await readMemoryFile({
|
|
3654
|
+
workspaceDir: resolveAgentWorkspaceDir(params.cfg, params.agentId),
|
|
3655
|
+
extraPaths: settings.extraPaths,
|
|
3656
|
+
relPath: params.relPath,
|
|
3657
|
+
from: params.from,
|
|
3658
|
+
lines: params.lines
|
|
3659
|
+
});
|
|
3660
|
+
}
|
|
3661
|
+
//#endregion
|
|
3662
|
+
//#region src/memory/manager.ts
|
|
3663
|
+
const SNIPPET_MAX_CHARS = 700;
|
|
3664
|
+
const VECTOR_TABLE = "chunks_vec";
|
|
3665
|
+
const FTS_TABLE = "chunks_fts";
|
|
3666
|
+
const EMBEDDING_CACHE_TABLE = "embedding_cache";
|
|
3667
|
+
const BATCH_FAILURE_LIMIT = 2;
|
|
3668
|
+
const MEMORY_INDEX_MANAGER_CACHE_KEY = "__openclawMemoryIndexManagerCache";
|
|
3669
|
+
function getMemoryIndexManagerCacheStore() {
|
|
3670
|
+
const globalCache = globalThis;
|
|
3671
|
+
globalCache[MEMORY_INDEX_MANAGER_CACHE_KEY] ??= {
|
|
3672
|
+
indexCache: /* @__PURE__ */ new Map(),
|
|
3673
|
+
indexCachePending: /* @__PURE__ */ new Map()
|
|
3674
|
+
};
|
|
3675
|
+
return globalCache[MEMORY_INDEX_MANAGER_CACHE_KEY];
|
|
3676
|
+
}
|
|
3677
|
+
const log = createSubsystemLogger("memory");
|
|
3678
|
+
const { indexCache: INDEX_CACHE, indexCachePending: INDEX_CACHE_PENDING } = getMemoryIndexManagerCacheStore();
|
|
3679
|
+
async function closeAllMemoryIndexManagers() {
|
|
3680
|
+
const pending = Array.from(INDEX_CACHE_PENDING.values());
|
|
3681
|
+
if (pending.length > 0) await Promise.allSettled(pending);
|
|
3682
|
+
const managers = Array.from(INDEX_CACHE.values());
|
|
3683
|
+
INDEX_CACHE.clear();
|
|
3684
|
+
for (const manager of managers) try {
|
|
3685
|
+
await manager.close();
|
|
3686
|
+
} catch (err) {
|
|
3687
|
+
log.warn(`failed to close memory index manager: ${String(err)}`);
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
var MemoryIndexManager = class MemoryIndexManager extends MemoryManagerEmbeddingOps {
|
|
3691
|
+
static async loadProviderResult(params) {
|
|
3692
|
+
return await createEmbeddingProvider({
|
|
3693
|
+
config: params.cfg,
|
|
3694
|
+
agentDir: resolveAgentDir(params.cfg, params.agentId),
|
|
3695
|
+
provider: params.settings.provider,
|
|
3696
|
+
remote: params.settings.remote,
|
|
3697
|
+
model: params.settings.model,
|
|
3698
|
+
outputDimensionality: params.settings.outputDimensionality,
|
|
3699
|
+
fallback: params.settings.fallback,
|
|
3700
|
+
local: params.settings.local
|
|
3701
|
+
});
|
|
3702
|
+
}
|
|
3703
|
+
static async get(params) {
|
|
3704
|
+
const { cfg, agentId } = params;
|
|
3705
|
+
const settings = resolveMemorySearchConfig(cfg, agentId);
|
|
3706
|
+
if (!settings) return null;
|
|
3707
|
+
const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
|
|
3708
|
+
const purpose = params.purpose === "status" ? "status" : "default";
|
|
3709
|
+
const key = `${agentId}:${workspaceDir}:${JSON.stringify(settings)}:${purpose}`;
|
|
3710
|
+
const statusOnly = params.purpose === "status";
|
|
3711
|
+
const existing = INDEX_CACHE.get(key);
|
|
3712
|
+
if (existing) return existing;
|
|
3713
|
+
const pending = INDEX_CACHE_PENDING.get(key);
|
|
3714
|
+
if (pending) return pending;
|
|
3715
|
+
if (statusOnly) {
|
|
3716
|
+
const manager = new MemoryIndexManager({
|
|
3717
|
+
cacheKey: key,
|
|
3718
|
+
cfg,
|
|
3719
|
+
agentId,
|
|
3720
|
+
workspaceDir,
|
|
3721
|
+
settings,
|
|
3722
|
+
purpose: params.purpose
|
|
3723
|
+
});
|
|
3724
|
+
INDEX_CACHE.set(key, manager);
|
|
3725
|
+
return manager;
|
|
3726
|
+
}
|
|
3727
|
+
const createPromise = (async () => {
|
|
3728
|
+
const providerResult = await MemoryIndexManager.loadProviderResult({
|
|
3729
|
+
cfg,
|
|
3730
|
+
agentId,
|
|
3731
|
+
settings
|
|
3732
|
+
});
|
|
3733
|
+
const refreshed = INDEX_CACHE.get(key);
|
|
3734
|
+
if (refreshed) return refreshed;
|
|
3735
|
+
const manager = new MemoryIndexManager({
|
|
3736
|
+
cacheKey: key,
|
|
3737
|
+
cfg,
|
|
3738
|
+
agentId,
|
|
3739
|
+
workspaceDir,
|
|
3740
|
+
settings,
|
|
3741
|
+
providerResult,
|
|
3742
|
+
purpose: params.purpose
|
|
3743
|
+
});
|
|
3744
|
+
INDEX_CACHE.set(key, manager);
|
|
3745
|
+
return manager;
|
|
3746
|
+
})();
|
|
3747
|
+
INDEX_CACHE_PENDING.set(key, createPromise);
|
|
3748
|
+
try {
|
|
3749
|
+
return await createPromise;
|
|
3750
|
+
} finally {
|
|
3751
|
+
if (INDEX_CACHE_PENDING.get(key) === createPromise) INDEX_CACHE_PENDING.delete(key);
|
|
3752
|
+
}
|
|
3753
|
+
}
|
|
3754
|
+
constructor(params) {
|
|
3755
|
+
super();
|
|
3756
|
+
this.providerInitPromise = null;
|
|
3757
|
+
this.providerInitialized = false;
|
|
3758
|
+
this.batchFailureCount = 0;
|
|
3759
|
+
this.batchFailureLock = Promise.resolve();
|
|
3760
|
+
this.vectorReady = null;
|
|
3761
|
+
this.watcher = null;
|
|
3762
|
+
this.watchTimer = null;
|
|
3763
|
+
this.sessionWatchTimer = null;
|
|
3764
|
+
this.sessionUnsubscribe = null;
|
|
3765
|
+
this.intervalTimer = null;
|
|
3766
|
+
this.closed = false;
|
|
3767
|
+
this.dirty = false;
|
|
3768
|
+
this.sessionsDirty = false;
|
|
3769
|
+
this.sessionsDirtyFiles = /* @__PURE__ */ new Set();
|
|
3770
|
+
this.sessionPendingFiles = /* @__PURE__ */ new Set();
|
|
3771
|
+
this.sessionDeltas = /* @__PURE__ */ new Map();
|
|
3772
|
+
this.sessionWarm = /* @__PURE__ */ new Set();
|
|
3773
|
+
this.syncing = null;
|
|
3774
|
+
this.queuedSessionFiles = /* @__PURE__ */ new Set();
|
|
3775
|
+
this.queuedSessionSync = null;
|
|
3776
|
+
this.readonlyRecoveryAttempts = 0;
|
|
3777
|
+
this.readonlyRecoverySuccesses = 0;
|
|
3778
|
+
this.readonlyRecoveryFailures = 0;
|
|
3779
|
+
this.cacheKey = params.cacheKey;
|
|
3780
|
+
this.cfg = params.cfg;
|
|
3781
|
+
this.agentId = params.agentId;
|
|
3782
|
+
this.workspaceDir = params.workspaceDir;
|
|
3783
|
+
this.settings = params.settings;
|
|
3784
|
+
this.provider = null;
|
|
3785
|
+
this.requestedProvider = params.settings.provider;
|
|
3786
|
+
if (params.providerResult) this.applyProviderResult(params.providerResult);
|
|
3787
|
+
this.sources = new Set(params.settings.sources);
|
|
3788
|
+
this.db = this.openDatabase();
|
|
3789
|
+
this.providerKey = this.computeProviderKey();
|
|
3790
|
+
this.cache = {
|
|
3791
|
+
enabled: params.settings.cache.enabled,
|
|
3792
|
+
maxEntries: params.settings.cache.maxEntries
|
|
3793
|
+
};
|
|
3794
|
+
this.fts = {
|
|
3795
|
+
enabled: params.settings.query.hybrid.enabled,
|
|
3796
|
+
available: false
|
|
3797
|
+
};
|
|
3798
|
+
this.ensureSchema();
|
|
3799
|
+
this.vector = {
|
|
3800
|
+
enabled: params.settings.store.vector.enabled,
|
|
3801
|
+
available: null,
|
|
3802
|
+
extensionPath: params.settings.store.vector.extensionPath
|
|
3803
|
+
};
|
|
3804
|
+
const meta = this.readMeta();
|
|
3805
|
+
if (meta?.vectorDims) this.vector.dims = meta.vectorDims;
|
|
3806
|
+
const statusOnly = params.purpose === "status";
|
|
3807
|
+
if (!statusOnly) {
|
|
3808
|
+
this.ensureWatcher();
|
|
3809
|
+
this.ensureSessionListener();
|
|
3810
|
+
this.ensureIntervalSync();
|
|
3811
|
+
}
|
|
3812
|
+
this.dirty = this.sources.has("memory") && (statusOnly ? !meta : true);
|
|
3813
|
+
this.batch = this.resolveBatchConfig();
|
|
3814
|
+
}
|
|
3815
|
+
applyProviderResult(providerResult) {
|
|
3816
|
+
this.provider = providerResult.provider;
|
|
3817
|
+
this.fallbackFrom = providerResult.fallbackFrom;
|
|
3818
|
+
this.fallbackReason = providerResult.fallbackReason;
|
|
3819
|
+
this.providerUnavailableReason = providerResult.providerUnavailableReason;
|
|
3820
|
+
this.openAi = providerResult.openAi;
|
|
3821
|
+
this.gemini = providerResult.gemini;
|
|
3822
|
+
this.voyage = providerResult.voyage;
|
|
3823
|
+
this.mistral = providerResult.mistral;
|
|
3824
|
+
this.ollama = providerResult.ollama;
|
|
3825
|
+
this.providerInitialized = true;
|
|
3826
|
+
}
|
|
3827
|
+
async ensureProviderInitialized() {
|
|
3828
|
+
if (this.providerInitialized) return;
|
|
3829
|
+
if (!this.providerInitPromise) this.providerInitPromise = (async () => {
|
|
3830
|
+
const providerResult = await MemoryIndexManager.loadProviderResult({
|
|
3831
|
+
cfg: this.cfg,
|
|
3832
|
+
agentId: this.agentId,
|
|
3833
|
+
settings: this.settings
|
|
3834
|
+
});
|
|
3835
|
+
this.applyProviderResult(providerResult);
|
|
3836
|
+
this.providerKey = this.computeProviderKey();
|
|
3837
|
+
this.batch = this.resolveBatchConfig();
|
|
3838
|
+
})();
|
|
3839
|
+
try {
|
|
3840
|
+
await this.providerInitPromise;
|
|
3841
|
+
} finally {
|
|
3842
|
+
if (this.providerInitialized) this.providerInitPromise = null;
|
|
3843
|
+
}
|
|
3844
|
+
}
|
|
3845
|
+
async warmSession(sessionKey) {
|
|
3846
|
+
if (!this.settings.sync.onSessionStart) return;
|
|
3847
|
+
const key = sessionKey?.trim() || "";
|
|
3848
|
+
if (key && this.sessionWarm.has(key)) return;
|
|
3849
|
+
this.sync({ reason: "session-start" }).catch((err) => {
|
|
3850
|
+
log.warn(`memory sync failed (session-start): ${String(err)}`);
|
|
3851
|
+
});
|
|
3852
|
+
if (key) this.sessionWarm.add(key);
|
|
3853
|
+
}
|
|
3854
|
+
async search(query, opts) {
|
|
3855
|
+
await this.ensureProviderInitialized();
|
|
3856
|
+
this.warmSession(opts?.sessionKey);
|
|
3857
|
+
if (this.settings.sync.onSearch && (this.dirty || this.sessionsDirty)) this.sync({ reason: "search" }).catch((err) => {
|
|
3858
|
+
log.warn(`memory sync failed (search): ${String(err)}`);
|
|
3859
|
+
});
|
|
3860
|
+
const cleaned = query.trim();
|
|
3861
|
+
if (!cleaned) return [];
|
|
3862
|
+
const minScore = opts?.minScore ?? this.settings.query.minScore;
|
|
3863
|
+
const maxResults = opts?.maxResults ?? this.settings.query.maxResults;
|
|
3864
|
+
const hybrid = this.settings.query.hybrid;
|
|
3865
|
+
const candidates = Math.min(200, Math.max(1, Math.floor(maxResults * hybrid.candidateMultiplier)));
|
|
3866
|
+
if (!this.provider) {
|
|
3867
|
+
if (!this.fts.enabled || !this.fts.available) {
|
|
3868
|
+
log.warn("memory search: no provider and FTS unavailable");
|
|
3869
|
+
return [];
|
|
3870
|
+
}
|
|
3871
|
+
const keywords = extractKeywords(cleaned);
|
|
3872
|
+
const searchTerms = keywords.length > 0 ? keywords : [cleaned];
|
|
3873
|
+
const resultSets = await Promise.all(searchTerms.map((term) => this.searchKeyword(term, candidates).catch(() => [])));
|
|
3874
|
+
const seenIds = /* @__PURE__ */ new Map();
|
|
3875
|
+
for (const results of resultSets) for (const result of results) {
|
|
3876
|
+
const existing = seenIds.get(result.id);
|
|
3877
|
+
if (!existing || result.score > existing.score) seenIds.set(result.id, result);
|
|
3878
|
+
}
|
|
3879
|
+
return [...seenIds.values()].toSorted((a, b) => b.score - a.score).filter((entry) => entry.score >= minScore).slice(0, maxResults);
|
|
3880
|
+
}
|
|
3881
|
+
const keywordResults = hybrid.enabled && this.fts.enabled && this.fts.available ? await this.searchKeyword(cleaned, candidates).catch(() => []) : [];
|
|
3882
|
+
const queryVec = await this.embedQueryWithTimeout(cleaned);
|
|
3883
|
+
const vectorResults = queryVec.some((v) => v !== 0) ? await this.searchVector(queryVec, candidates).catch(() => []) : [];
|
|
3884
|
+
if (!hybrid.enabled || !this.fts.enabled || !this.fts.available) return vectorResults.filter((entry) => entry.score >= minScore).slice(0, maxResults);
|
|
3885
|
+
const merged = await this.mergeHybridResults({
|
|
3886
|
+
vector: vectorResults,
|
|
3887
|
+
keyword: keywordResults,
|
|
3888
|
+
vectorWeight: hybrid.vectorWeight,
|
|
3889
|
+
textWeight: hybrid.textWeight,
|
|
3890
|
+
mmr: hybrid.mmr,
|
|
3891
|
+
temporalDecay: hybrid.temporalDecay
|
|
3892
|
+
});
|
|
3893
|
+
const strict = merged.filter((entry) => entry.score >= minScore);
|
|
3894
|
+
if (strict.length > 0 || keywordResults.length === 0) return strict.slice(0, maxResults);
|
|
3895
|
+
const relaxedMinScore = Math.min(minScore, hybrid.textWeight);
|
|
3896
|
+
const keywordKeys = new Set(keywordResults.map((entry) => `${entry.source}:${entry.path}:${entry.startLine}:${entry.endLine}`));
|
|
3897
|
+
return merged.filter((entry) => keywordKeys.has(`${entry.source}:${entry.path}:${entry.startLine}:${entry.endLine}`) && entry.score >= relaxedMinScore).slice(0, maxResults);
|
|
3898
|
+
}
|
|
3899
|
+
async searchVector(queryVec, limit) {
|
|
3900
|
+
if (!this.provider) return [];
|
|
3901
|
+
return (await searchVector({
|
|
3902
|
+
db: this.db,
|
|
3903
|
+
vectorTable: VECTOR_TABLE,
|
|
3904
|
+
providerModel: this.provider.model,
|
|
3905
|
+
queryVec,
|
|
3906
|
+
limit,
|
|
3907
|
+
snippetMaxChars: SNIPPET_MAX_CHARS,
|
|
3908
|
+
ensureVectorReady: async (dimensions) => await this.ensureVectorReady(dimensions),
|
|
3909
|
+
sourceFilterVec: this.buildSourceFilter("c"),
|
|
3910
|
+
sourceFilterChunks: this.buildSourceFilter()
|
|
3911
|
+
})).map((entry) => entry);
|
|
3912
|
+
}
|
|
3913
|
+
buildFtsQuery(raw) {
|
|
3914
|
+
return buildFtsQuery(raw);
|
|
3915
|
+
}
|
|
3916
|
+
async searchKeyword(query, limit) {
|
|
3917
|
+
if (!this.fts.enabled || !this.fts.available) return [];
|
|
3918
|
+
const sourceFilter = this.buildSourceFilter();
|
|
3919
|
+
const providerModel = this.provider?.model;
|
|
3920
|
+
return (await searchKeyword({
|
|
3921
|
+
db: this.db,
|
|
3922
|
+
ftsTable: FTS_TABLE,
|
|
3923
|
+
providerModel,
|
|
3924
|
+
query,
|
|
3925
|
+
limit,
|
|
3926
|
+
snippetMaxChars: SNIPPET_MAX_CHARS,
|
|
3927
|
+
sourceFilter,
|
|
3928
|
+
buildFtsQuery: (raw) => this.buildFtsQuery(raw),
|
|
3929
|
+
bm25RankToScore
|
|
3930
|
+
})).map((entry) => entry);
|
|
3931
|
+
}
|
|
3932
|
+
mergeHybridResults(params) {
|
|
3933
|
+
return mergeHybridResults({
|
|
3934
|
+
vector: params.vector.map((r) => ({
|
|
3935
|
+
id: r.id,
|
|
3936
|
+
path: r.path,
|
|
3937
|
+
startLine: r.startLine,
|
|
3938
|
+
endLine: r.endLine,
|
|
3939
|
+
source: r.source,
|
|
3940
|
+
snippet: r.snippet,
|
|
3941
|
+
vectorScore: r.score
|
|
3942
|
+
})),
|
|
3943
|
+
keyword: params.keyword.map((r) => ({
|
|
3944
|
+
id: r.id,
|
|
3945
|
+
path: r.path,
|
|
3946
|
+
startLine: r.startLine,
|
|
3947
|
+
endLine: r.endLine,
|
|
3948
|
+
source: r.source,
|
|
3949
|
+
snippet: r.snippet,
|
|
3950
|
+
textScore: r.textScore
|
|
3951
|
+
})),
|
|
3952
|
+
vectorWeight: params.vectorWeight,
|
|
3953
|
+
textWeight: params.textWeight,
|
|
3954
|
+
mmr: params.mmr,
|
|
3955
|
+
temporalDecay: params.temporalDecay,
|
|
3956
|
+
workspaceDir: this.workspaceDir
|
|
3957
|
+
}).then((entries) => entries.map((entry) => entry));
|
|
3958
|
+
}
|
|
3959
|
+
async sync(params) {
|
|
3960
|
+
if (this.closed) return;
|
|
3961
|
+
await this.ensureProviderInitialized();
|
|
3962
|
+
if (this.syncing) {
|
|
3963
|
+
if (params?.sessionFiles?.some((sessionFile) => sessionFile.trim().length > 0)) return this.enqueueTargetedSessionSync(params.sessionFiles);
|
|
3964
|
+
return this.syncing;
|
|
3965
|
+
}
|
|
3966
|
+
this.syncing = this.runSyncWithReadonlyRecovery(params).finally(() => {
|
|
3967
|
+
this.syncing = null;
|
|
3968
|
+
});
|
|
3969
|
+
return this.syncing ?? Promise.resolve();
|
|
3970
|
+
}
|
|
3971
|
+
enqueueTargetedSessionSync(sessionFiles) {
|
|
3972
|
+
for (const sessionFile of sessionFiles ?? []) {
|
|
3973
|
+
const trimmed = sessionFile.trim();
|
|
3974
|
+
if (trimmed) this.queuedSessionFiles.add(trimmed);
|
|
3975
|
+
}
|
|
3976
|
+
if (this.queuedSessionFiles.size === 0) return this.syncing ?? Promise.resolve();
|
|
3977
|
+
if (!this.queuedSessionSync) this.queuedSessionSync = (async () => {
|
|
3978
|
+
try {
|
|
3979
|
+
await this.syncing?.catch(() => void 0);
|
|
3980
|
+
while (!this.closed && this.queuedSessionFiles.size > 0) {
|
|
3981
|
+
const queuedSessionFiles = Array.from(this.queuedSessionFiles);
|
|
3982
|
+
this.queuedSessionFiles.clear();
|
|
3983
|
+
await this.sync({
|
|
3984
|
+
reason: "queued-session-files",
|
|
3985
|
+
sessionFiles: queuedSessionFiles
|
|
3986
|
+
});
|
|
3987
|
+
}
|
|
3988
|
+
} finally {
|
|
3989
|
+
this.queuedSessionSync = null;
|
|
3990
|
+
}
|
|
3991
|
+
})();
|
|
3992
|
+
return this.queuedSessionSync;
|
|
3993
|
+
}
|
|
3994
|
+
isReadonlyDbError(err) {
|
|
3995
|
+
const readonlyPattern = /attempt to write a readonly database|database is read-only|SQLITE_READONLY/i;
|
|
3996
|
+
const messages = /* @__PURE__ */ new Set();
|
|
3997
|
+
const pushValue = (value) => {
|
|
3998
|
+
if (typeof value !== "string") return;
|
|
3999
|
+
const normalized = value.trim();
|
|
4000
|
+
if (!normalized) return;
|
|
4001
|
+
messages.add(normalized);
|
|
4002
|
+
};
|
|
4003
|
+
pushValue(err instanceof Error ? err.message : String(err));
|
|
4004
|
+
if (err && typeof err === "object") {
|
|
4005
|
+
const record = err;
|
|
4006
|
+
pushValue(record.message);
|
|
4007
|
+
pushValue(record.code);
|
|
4008
|
+
pushValue(record.name);
|
|
4009
|
+
if (record.cause && typeof record.cause === "object") {
|
|
4010
|
+
const cause = record.cause;
|
|
4011
|
+
pushValue(cause.message);
|
|
4012
|
+
pushValue(cause.code);
|
|
4013
|
+
pushValue(cause.name);
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
return [...messages].some((value) => readonlyPattern.test(value));
|
|
4017
|
+
}
|
|
4018
|
+
extractErrorReason(err) {
|
|
4019
|
+
if (err instanceof Error && err.message.trim()) return err.message;
|
|
4020
|
+
if (err && typeof err === "object") {
|
|
4021
|
+
const record = err;
|
|
4022
|
+
if (typeof record.message === "string" && record.message.trim()) return record.message;
|
|
4023
|
+
if (typeof record.code === "string" && record.code.trim()) return record.code;
|
|
4024
|
+
}
|
|
4025
|
+
return String(err);
|
|
4026
|
+
}
|
|
4027
|
+
async runSyncWithReadonlyRecovery(params) {
|
|
4028
|
+
try {
|
|
4029
|
+
await this.runSync(params);
|
|
4030
|
+
return;
|
|
4031
|
+
} catch (err) {
|
|
4032
|
+
if (!this.isReadonlyDbError(err) || this.closed) throw err;
|
|
4033
|
+
const reason = this.extractErrorReason(err);
|
|
4034
|
+
this.readonlyRecoveryAttempts += 1;
|
|
4035
|
+
this.readonlyRecoveryLastError = reason;
|
|
4036
|
+
log.warn(`memory sync readonly handle detected; reopening sqlite connection`, { reason });
|
|
4037
|
+
try {
|
|
4038
|
+
this.db.close();
|
|
4039
|
+
} catch {}
|
|
4040
|
+
this.db = this.openDatabase();
|
|
4041
|
+
this.vectorReady = null;
|
|
4042
|
+
this.vector.available = null;
|
|
4043
|
+
this.vector.loadError = void 0;
|
|
4044
|
+
this.ensureSchema();
|
|
4045
|
+
const meta = this.readMeta();
|
|
4046
|
+
this.vector.dims = meta?.vectorDims;
|
|
4047
|
+
try {
|
|
4048
|
+
await this.runSync(params);
|
|
4049
|
+
this.readonlyRecoverySuccesses += 1;
|
|
4050
|
+
} catch (retryErr) {
|
|
4051
|
+
this.readonlyRecoveryFailures += 1;
|
|
4052
|
+
throw retryErr;
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
4056
|
+
async readFile(params) {
|
|
4057
|
+
return await readMemoryFile({
|
|
4058
|
+
workspaceDir: this.workspaceDir,
|
|
4059
|
+
extraPaths: this.settings.extraPaths,
|
|
4060
|
+
relPath: params.relPath,
|
|
4061
|
+
from: params.from,
|
|
4062
|
+
lines: params.lines
|
|
4063
|
+
});
|
|
4064
|
+
}
|
|
4065
|
+
status() {
|
|
4066
|
+
const sourceFilter = this.buildSourceFilter();
|
|
4067
|
+
const files = this.db.prepare(`SELECT COUNT(*) as c FROM files WHERE 1=1${sourceFilter.sql}`).get(...sourceFilter.params);
|
|
4068
|
+
const chunks = this.db.prepare(`SELECT COUNT(*) as c FROM chunks WHERE 1=1${sourceFilter.sql}`).get(...sourceFilter.params);
|
|
4069
|
+
const sourceCounts = (() => {
|
|
4070
|
+
const sources = Array.from(this.sources);
|
|
4071
|
+
if (sources.length === 0) return [];
|
|
4072
|
+
const bySource = /* @__PURE__ */ new Map();
|
|
4073
|
+
for (const source of sources) bySource.set(source, {
|
|
4074
|
+
files: 0,
|
|
4075
|
+
chunks: 0
|
|
4076
|
+
});
|
|
4077
|
+
const fileRows = this.db.prepare(`SELECT source, COUNT(*) as c FROM files WHERE 1=1${sourceFilter.sql} GROUP BY source`).all(...sourceFilter.params);
|
|
4078
|
+
for (const row of fileRows) {
|
|
4079
|
+
const entry = bySource.get(row.source) ?? {
|
|
4080
|
+
files: 0,
|
|
4081
|
+
chunks: 0
|
|
4082
|
+
};
|
|
4083
|
+
entry.files = row.c ?? 0;
|
|
4084
|
+
bySource.set(row.source, entry);
|
|
4085
|
+
}
|
|
4086
|
+
const chunkRows = this.db.prepare(`SELECT source, COUNT(*) as c FROM chunks WHERE 1=1${sourceFilter.sql} GROUP BY source`).all(...sourceFilter.params);
|
|
4087
|
+
for (const row of chunkRows) {
|
|
4088
|
+
const entry = bySource.get(row.source) ?? {
|
|
4089
|
+
files: 0,
|
|
4090
|
+
chunks: 0
|
|
4091
|
+
};
|
|
4092
|
+
entry.chunks = row.c ?? 0;
|
|
4093
|
+
bySource.set(row.source, entry);
|
|
4094
|
+
}
|
|
4095
|
+
return sources.map((source) => Object.assign({ source }, bySource.get(source)));
|
|
4096
|
+
})();
|
|
4097
|
+
const searchMode = this.provider || !this.providerInitialized ? "hybrid" : "fts-only";
|
|
4098
|
+
const providerInfo = this.provider ? {
|
|
4099
|
+
provider: this.provider.id,
|
|
4100
|
+
model: this.provider.model
|
|
4101
|
+
} : this.providerInitialized ? {
|
|
4102
|
+
provider: "none",
|
|
4103
|
+
model: void 0
|
|
4104
|
+
} : {
|
|
4105
|
+
provider: this.requestedProvider,
|
|
4106
|
+
model: this.settings.model || void 0
|
|
4107
|
+
};
|
|
4108
|
+
return {
|
|
4109
|
+
backend: "builtin",
|
|
4110
|
+
files: files?.c ?? 0,
|
|
4111
|
+
chunks: chunks?.c ?? 0,
|
|
4112
|
+
dirty: this.dirty || this.sessionsDirty,
|
|
4113
|
+
workspaceDir: this.workspaceDir,
|
|
4114
|
+
dbPath: this.settings.store.path,
|
|
4115
|
+
provider: providerInfo.provider,
|
|
4116
|
+
model: providerInfo.model,
|
|
4117
|
+
requestedProvider: this.requestedProvider,
|
|
4118
|
+
sources: Array.from(this.sources),
|
|
4119
|
+
extraPaths: this.settings.extraPaths,
|
|
4120
|
+
sourceCounts,
|
|
4121
|
+
cache: this.cache.enabled ? {
|
|
4122
|
+
enabled: true,
|
|
4123
|
+
entries: this.db.prepare(`SELECT COUNT(*) as c FROM ${EMBEDDING_CACHE_TABLE}`).get()?.c ?? 0,
|
|
4124
|
+
maxEntries: this.cache.maxEntries
|
|
4125
|
+
} : {
|
|
4126
|
+
enabled: false,
|
|
4127
|
+
maxEntries: this.cache.maxEntries
|
|
4128
|
+
},
|
|
4129
|
+
fts: {
|
|
4130
|
+
enabled: this.fts.enabled,
|
|
4131
|
+
available: this.fts.available,
|
|
4132
|
+
error: this.fts.loadError
|
|
4133
|
+
},
|
|
4134
|
+
fallback: this.fallbackReason ? {
|
|
4135
|
+
from: this.fallbackFrom ?? "local",
|
|
4136
|
+
reason: this.fallbackReason
|
|
4137
|
+
} : void 0,
|
|
4138
|
+
vector: {
|
|
4139
|
+
enabled: this.vector.enabled,
|
|
4140
|
+
available: this.vector.available ?? void 0,
|
|
4141
|
+
extensionPath: this.vector.extensionPath,
|
|
4142
|
+
loadError: this.vector.loadError,
|
|
4143
|
+
dims: this.vector.dims
|
|
4144
|
+
},
|
|
4145
|
+
batch: {
|
|
4146
|
+
enabled: this.batch.enabled,
|
|
4147
|
+
failures: this.batchFailureCount,
|
|
4148
|
+
limit: BATCH_FAILURE_LIMIT,
|
|
4149
|
+
wait: this.batch.wait,
|
|
4150
|
+
concurrency: this.batch.concurrency,
|
|
4151
|
+
pollIntervalMs: this.batch.pollIntervalMs,
|
|
4152
|
+
timeoutMs: this.batch.timeoutMs,
|
|
4153
|
+
lastError: this.batchFailureLastError,
|
|
4154
|
+
lastProvider: this.batchFailureLastProvider
|
|
4155
|
+
},
|
|
4156
|
+
custom: {
|
|
4157
|
+
searchMode,
|
|
4158
|
+
providerUnavailableReason: this.providerUnavailableReason,
|
|
4159
|
+
readonlyRecovery: {
|
|
4160
|
+
attempts: this.readonlyRecoveryAttempts,
|
|
4161
|
+
successes: this.readonlyRecoverySuccesses,
|
|
4162
|
+
failures: this.readonlyRecoveryFailures,
|
|
4163
|
+
lastError: this.readonlyRecoveryLastError
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
};
|
|
4167
|
+
}
|
|
4168
|
+
async probeVectorAvailability() {
|
|
4169
|
+
if (!this.vector.enabled) return false;
|
|
4170
|
+
await this.ensureProviderInitialized();
|
|
4171
|
+
if (!this.provider) return false;
|
|
4172
|
+
return this.ensureVectorReady();
|
|
4173
|
+
}
|
|
4174
|
+
async probeEmbeddingAvailability() {
|
|
4175
|
+
await this.ensureProviderInitialized();
|
|
4176
|
+
if (!this.provider) return {
|
|
4177
|
+
ok: false,
|
|
4178
|
+
error: this.providerUnavailableReason ?? "No embedding provider available (FTS-only mode)"
|
|
4179
|
+
};
|
|
4180
|
+
try {
|
|
4181
|
+
await this.embedBatchWithRetry(["ping"]);
|
|
4182
|
+
return { ok: true };
|
|
4183
|
+
} catch (err) {
|
|
4184
|
+
return {
|
|
4185
|
+
ok: false,
|
|
4186
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4187
|
+
};
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4190
|
+
async close() {
|
|
4191
|
+
if (this.closed) return;
|
|
4192
|
+
this.closed = true;
|
|
4193
|
+
const pendingSync = this.syncing;
|
|
4194
|
+
const pendingProviderInit = this.providerInitPromise;
|
|
4195
|
+
if (this.watchTimer) {
|
|
4196
|
+
clearTimeout(this.watchTimer);
|
|
4197
|
+
this.watchTimer = null;
|
|
4198
|
+
}
|
|
4199
|
+
if (this.sessionWatchTimer) {
|
|
4200
|
+
clearTimeout(this.sessionWatchTimer);
|
|
4201
|
+
this.sessionWatchTimer = null;
|
|
4202
|
+
}
|
|
4203
|
+
if (this.intervalTimer) {
|
|
4204
|
+
clearInterval(this.intervalTimer);
|
|
4205
|
+
this.intervalTimer = null;
|
|
4206
|
+
}
|
|
4207
|
+
if (this.watcher) {
|
|
4208
|
+
await this.watcher.close();
|
|
4209
|
+
this.watcher = null;
|
|
4210
|
+
}
|
|
4211
|
+
if (this.sessionUnsubscribe) {
|
|
4212
|
+
this.sessionUnsubscribe();
|
|
4213
|
+
this.sessionUnsubscribe = null;
|
|
4214
|
+
}
|
|
4215
|
+
if (pendingSync) try {
|
|
4216
|
+
await pendingSync;
|
|
4217
|
+
} catch {}
|
|
4218
|
+
if (pendingProviderInit) try {
|
|
4219
|
+
await pendingProviderInit;
|
|
4220
|
+
} catch {}
|
|
4221
|
+
this.db.close();
|
|
4222
|
+
INDEX_CACHE.delete(this.cacheKey);
|
|
4223
|
+
}
|
|
4224
|
+
};
|
|
4225
|
+
//#endregion
|
|
4226
|
+
export { hasConfiguredMemorySecretInput as a, DEFAULT_LOCAL_MODEL as i, closeAllMemoryIndexManagers as n, collectProviderApiKeysForExecution as o, readAgentMemoryFile as r, executeWithApiKeyRotation as s, MemoryIndexManager as t };
|