@xopcai/xopc 0.0.140 → 0.0.142
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/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DtONoxBN.js → agent-avatar-dicebear-BawPxiu5.js} +1 -1
- package/dist/gateway/static/root/assets/agents-CyvCIHnB.js +2 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-BRBxLECY.js → app-management-settings-panel-81xd1p9R.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-ChvyuSEt.js → appearance-settings-DYeQsYU8.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-DgHWTnjs.js → apps-page-DK_Vx6nl.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-CdIoagem.js → archive-plugin-D_oa7oti.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-Ch411Kht.js → attachment-load-Cd-bwI2N.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-SVq27yw2.js → automation-suggestion-card-B1LV3YYv.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-fzrovqhv.js +4 -0
- package/dist/gateway/static/root/assets/{binary-plugins-D8cV-emn.js → binary-plugins-O8svLKWw.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-CXDkby6y.js → block-editor-Dhj5f4Bx.js} +47 -47
- package/dist/gateway/static/root/assets/browser-settings-page-BDn-n-UB.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-DLxI--Ge.js → capability-presets-api-dJy1Eqgx.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-presets-settings-panel-DKyRbnlH.js → capability-presets-settings-panel-Co5ipo_Q.js} +1 -1
- package/dist/gateway/static/root/assets/{channel-recipient-api-DllrPmp-.js → channel-recipient-api-Bfuhp2ty.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-BWlpaxg9.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-D_jkIZLV.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-BcekbW4q.js +2 -0
- package/dist/gateway/static/root/assets/{desktop-pet-Bz9ommjr.js → desktop-pet-DFMfIb5F.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-BnLgxgoR.js → desktop-pet-settings-BJCUuL8Y.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-DLsCwFaf.js → directory-picker-path-field-BrYt1gAr.js} +1 -1
- package/dist/gateway/static/root/assets/{dreaming-settings-BzkF1-XT.js → dreaming-settings-BN1Z2mIo.js} +3 -3
- package/dist/gateway/static/root/assets/{extension-debug-page-CdERvf3L.js → extension-debug-page-Bh630pLD.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-D1fz474Z.js → extension-page-B-1KxvVy.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DoQMkNOK.js → extension-settings-page-BF9NACI3.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-BID89uCH.js → fetch-B1WtuprY.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-Rygo8UAs.js → gateway-config-api-De2N821l.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-kb8KIeQL.js → gateway-settings-DN9sNREz.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-BkrLLlpE.js → gateway-startup-retry-nEujFSDj.js} +1 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-BpoQW8cD.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-CBRSPd0b.js +4 -0
- package/dist/gateway/static/root/assets/{goals-config-api-BuY6-l0a.js → goals-config-api-DUy3eEse.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-BCcBi407.js +2 -0
- package/dist/gateway/static/root/assets/{goals-settings-DoH5fZWo.js → goals-settings-pkTXzXkh.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-BqDs48B-.js → heartbeat-settings-ezsufgLQ.js} +1 -1
- package/dist/gateway/static/root/assets/{index-CrPRlGMg.js → index-DcUaXYeb.js} +65 -65
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-CVIwwM15.js +1 -0
- package/dist/gateway/static/root/assets/logs-page-DQtTRXtj.js +7 -0
- package/dist/gateway/static/root/assets/{markdown-split-CSt32FWE.js → markdown-split-B_6us9j7.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-ByItHxBj.js → media-plugins-D_fryIyj.js} +1 -1
- package/dist/gateway/static/root/assets/messages-BXNELNBa.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-BP2M9n6Q.js +2 -0
- package/dist/gateway/static/root/assets/notes-page-CspveYaO.js +1 -0
- package/dist/gateway/static/root/assets/notes-workbench-BHwzJ0OC.js +2 -0
- package/dist/gateway/static/root/assets/{page-tabs-DOfMaq_L.js → page-tabs-D_8jGlqx.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-BArxASu7.js → preview-open-alternatives-BBLMK8Mp.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-Dy3rsobs.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-CWKpPE0L.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-CqNRyXEo.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-BRxlkhG8.js → save-bar-controls-DVS3FR5r.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-BzM1iyOc.js → schedule-field-D6vKd6-T.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-ChVcm0DX.js → schema-form-BPy4lbIh.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-MEME7ivF.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-JVd0qaSF.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-BcD9xrvY.js → settings-collapsible-section-WzdikxPt.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-Cu30CXVl.js → settings-form-section-CvfzhBV-.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-Cfd37IYb.js → settings-loading-skeleton-BxR-Ulhi.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-EINHyzLo.js → settings-page-DYEbB1EV.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-C15zixct.js → settings-page-layout-CKefvbYi.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-Bu7SdpD4.js → setup-status-panel-dWN8BJ3z.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-CjjAFV-v.js → share-preview-page-D8foINzP.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-Tgbv1s7G.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-D_YK0b_Z.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-DGytfoZK.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-DlQMtzPf.js → system-settings-panel-BokgAgf7.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-BS1BwExE.js → typed-models-lib-BwHlNL3L.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-EA4hfbsP.js → use-channel-catalog-DR11ap3A.js} +1 -1
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-B7RCdmSp.js → user-profile-settings-panel-8YtygcJp.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-BrhEgVaa.js → utils-BKUWB-TT.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-BVH71EFf.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-JeTE7XNM.js +1 -0
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-CL_rnAUB.js +3 -0
- package/dist/gateway/static/root/assets/{workflow-start-dialog-BZCY7N42.js → workflow-start-dialog-_u8Uc1ze.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-ixgk_y1l.js +18 -0
- package/dist/gateway/static/root/index.html +4 -4
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +13 -21
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/background-review/run-background-review.js +1 -0
- package/dist/src/agent/background-review/run-background-review.js.map +1 -1
- package/dist/src/agent/embedded/session-runner.js +5 -5
- package/dist/src/agent/embedded/session-runner.js.map +1 -1
- package/dist/src/agent/embedded/xopc-auth-storage.d.ts +4 -3
- package/dist/src/agent/embedded/xopc-auth-storage.js +6 -6
- package/dist/src/agent/embedded/xopc-auth-storage.js.map +1 -1
- package/dist/src/agent/embedded/xopc-stream-bridge.js +1 -0
- package/dist/src/agent/embedded/xopc-stream-bridge.js.map +1 -1
- package/dist/src/agent/goals/checklist-judge.js +3 -3
- package/dist/src/agent/goals/checklist-judge.js.map +1 -1
- package/dist/src/agent/goals/judge.js +2 -2
- package/dist/src/agent/goals/judge.js.map +1 -1
- package/dist/src/agent/image/understanding/pi-ai-provider.js +2 -2
- package/dist/src/agent/image/understanding/pi-ai-provider.js.map +1 -1
- package/dist/src/agent/memory/access-policy.d.ts +12 -0
- package/dist/src/agent/memory/access-policy.js +44 -0
- package/dist/src/agent/memory/access-policy.js.map +1 -0
- package/dist/src/agent/memory/builtin-provider.d.ts +4 -1
- package/dist/src/agent/memory/builtin-provider.js +51 -16
- package/dist/src/agent/memory/builtin-provider.js.map +1 -1
- package/dist/src/agent/memory/compaction.js +2 -2
- package/dist/src/agent/memory/compaction.js.map +1 -1
- package/dist/src/agent/memory/context/planner.d.ts +1 -0
- package/dist/src/agent/memory/context/planner.js +9 -5
- package/dist/src/agent/memory/context/planner.js.map +1 -1
- package/dist/src/agent/memory/create-memory-manager.d.ts +1 -1
- package/dist/src/agent/memory/create-memory-manager.js +8 -5
- package/dist/src/agent/memory/create-memory-manager.js.map +1 -1
- package/dist/src/agent/memory/dreaming/config.d.ts +2 -0
- package/dist/src/agent/memory/dreaming/config.js +13 -1
- package/dist/src/agent/memory/dreaming/config.js.map +1 -1
- package/dist/src/agent/memory/dreaming/deep-promotion.d.ts +2 -0
- package/dist/src/agent/memory/dreaming/deep-promotion.js +24 -3
- package/dist/src/agent/memory/dreaming/deep-promotion.js.map +1 -1
- package/dist/src/agent/memory/dreaming/light-sweep.d.ts +1 -0
- package/dist/src/agent/memory/dreaming/light-sweep.js +184 -16
- package/dist/src/agent/memory/dreaming/light-sweep.js.map +1 -1
- package/dist/src/agent/memory/dreaming/promotion-lifecycle.d.ts +30 -0
- package/dist/src/agent/memory/dreaming/promotion-lifecycle.js +85 -0
- package/dist/src/agent/memory/dreaming/promotion-lifecycle.js.map +1 -0
- package/dist/src/agent/memory/dreaming/rem-patterns.d.ts +4 -0
- package/dist/src/agent/memory/dreaming/rem-patterns.js +76 -53
- package/dist/src/agent/memory/dreaming/rem-patterns.js.map +1 -1
- package/dist/src/agent/memory/dreaming/short-term-store.d.ts +10 -0
- package/dist/src/agent/memory/dreaming/short-term-store.js +157 -79
- package/dist/src/agent/memory/dreaming/short-term-store.js.map +1 -1
- package/dist/src/agent/memory/manager.d.ts +7 -0
- package/dist/src/agent/memory/manager.js +31 -6
- package/dist/src/agent/memory/manager.js.map +1 -1
- package/dist/src/agent/memory/memory-config.d.ts +1 -1
- package/dist/src/agent/memory/memory-config.js +3 -3
- package/dist/src/agent/memory/memory-config.js.map +1 -1
- package/dist/src/agent/memory/sensitivity.d.ts +3 -0
- package/dist/src/agent/memory/sensitivity.js +13 -0
- package/dist/src/agent/memory/sensitivity.js.map +1 -0
- package/dist/src/agent/memory/understanding/service.js +10 -14
- package/dist/src/agent/memory/understanding/service.js.map +1 -1
- package/dist/src/agent/memory/user-context-coordinator.d.ts +1 -0
- package/dist/src/agent/memory/user-context-coordinator.js +2 -0
- package/dist/src/agent/memory/user-context-coordinator.js.map +1 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js +8 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
- package/dist/src/agent/service/btw-query.d.ts +4 -0
- package/dist/src/agent/service/btw-query.js +94 -13
- package/dist/src/agent/service/btw-query.js.map +1 -1
- package/dist/src/agent/session/session-inspector.js +9 -1
- package/dist/src/agent/session/session-inspector.js.map +1 -1
- package/dist/src/agent/tools/dreaming-tool.js +3 -12
- package/dist/src/agent/tools/dreaming-tool.js.map +1 -1
- package/dist/src/agent/tools/factory.js +14 -2
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/memory-tool.d.ts +3 -0
- package/dist/src/agent/tools/memory-tool.js +6 -3
- package/dist/src/agent/tools/memory-tool.js.map +1 -1
- package/dist/src/agent/tools/session-search-tool.js +2 -2
- package/dist/src/agent/tools/session-search-tool.js.map +1 -1
- package/dist/src/agent/tools/web-extract.js +2 -2
- package/dist/src/agent/tools/web-extract.js.map +1 -1
- package/dist/src/agent/workspace-runtime/registry.d.ts +6 -6
- package/dist/src/agent/workspace-runtime/registry.js +19 -12
- package/dist/src/agent/workspace-runtime/registry.js.map +1 -1
- package/dist/src/agent-manifest/schema.d.ts +4 -0
- package/dist/src/agent-manifest/schema.js +2 -1
- package/dist/src/agent-manifest/schema.js.map +1 -1
- package/dist/src/agent-runtime/index.js +1 -1
- package/dist/src/agent-runtime/runtime-profile.js +1 -1
- package/dist/src/ai-assist/text-assist.d.ts +2 -1
- package/dist/src/ai-assist/text-assist.js +10 -23
- package/dist/src/ai-assist/text-assist.js.map +1 -1
- package/dist/src/auth/credentials.js +5 -2
- package/dist/src/auth/credentials.js.map +1 -1
- package/dist/src/auth/oauth/github-copilot.d.ts +3 -4
- package/dist/src/auth/oauth/github-copilot.js +66 -2
- package/dist/src/auth/oauth/github-copilot.js.map +1 -0
- package/dist/src/auth/oauth/index.js +2 -2
- package/dist/src/automations/draft/automation-draft-service.js +3 -3
- package/dist/src/automations/draft/automation-draft-service.js.map +1 -1
- package/dist/src/chat-commands/builtins/review.js +22 -5
- package/dist/src/chat-commands/builtins/review.js.map +1 -1
- package/dist/src/chat-commands/types.d.ts +4 -0
- package/dist/src/config/schema.d.ts +4 -0
- package/dist/src/gateway/hono/lib/config-payload.d.ts +2 -0
- package/dist/src/gateway/hono/routes/dreaming.js +28 -21
- package/dist/src/gateway/hono/routes/dreaming.js.map +1 -1
- package/dist/src/gateway/hono/routes/memory.js +16 -4
- package/dist/src/gateway/hono/routes/memory.js.map +1 -1
- package/dist/src/gateway/hono/routes/models.js +1 -0
- package/dist/src/gateway/hono/routes/models.js.map +1 -1
- package/dist/src/gateway/hono/routes/voice.js +2 -2
- package/dist/src/gateway/hono/routes/voice.js.map +1 -1
- package/dist/src/goals/goal-contract-draft.js +2 -2
- package/dist/src/goals/goal-contract-draft.js.map +1 -1
- package/dist/src/goals/goal-evidence-review.js +2 -2
- package/dist/src/goals/goal-evidence-review.js.map +1 -1
- package/dist/src/notes/service.js +2 -2
- package/dist/src/notes/service.js.map +1 -1
- package/dist/src/providers/constants.d.ts +2 -0
- package/dist/src/providers/constants.js +11 -0
- package/dist/src/providers/constants.js.map +1 -0
- package/dist/src/providers/extension-stream-bridge.js +1 -0
- package/dist/src/providers/extension-stream-bridge.js.map +1 -1
- package/dist/src/providers/index.d.ts +3 -3
- package/dist/src/providers/index.js +5 -4
- package/dist/src/providers/index.js.map +1 -1
- package/dist/src/providers/model-call.d.ts +17 -0
- package/dist/src/providers/model-call.js +60 -0
- package/dist/src/providers/model-call.js.map +1 -0
- package/dist/src/session/session-title.js +2 -2
- package/dist/src/session/session-title.js.map +1 -1
- package/dist/src/storage/sqlite/fts.d.ts +6 -0
- package/dist/src/storage/sqlite/fts.js +70 -1
- package/dist/src/storage/sqlite/fts.js.map +1 -1
- package/dist/src/storage/sqlite/memory-index-repository.js +16 -7
- package/dist/src/storage/sqlite/memory-index-repository.js.map +1 -1
- package/dist/src/storage/sqlite/memory-records-repository.d.ts +18 -0
- package/dist/src/storage/sqlite/memory-records-repository.js +55 -8
- package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
- package/dist/src/voice/tts/summarize.js +2 -2
- package/dist/src/voice/tts/summarize.js.map +1 -1
- package/dist/src/workflows/draft/workflow-draft-service.js +2 -2
- package/dist/src/workflows/draft/workflow-draft-service.js.map +1 -1
- package/package.json +5 -5
- package/dist/gateway/static/root/assets/agents-Dl9EWnUh.js +0 -2
- package/dist/gateway/static/root/assets/automations-page-CFt5Ik4q.js +0 -4
- package/dist/gateway/static/root/assets/browser-settings-page-ColJPwer.js +0 -8
- package/dist/gateway/static/root/assets/channels-settings-BDLHV3Jx.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-EUwWYx8l.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-CA752gY8.js +0 -2
- package/dist/gateway/static/root/assets/goal-create-dialog-DVAHnifk.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-BuOEQZn_.js +0 -4
- package/dist/gateway/static/root/assets/goals-page-WV4cHvD-.js +0 -2
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-D51yogNZ.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-CL22jleU.js +0 -7
- package/dist/gateway/static/root/assets/messages-DbNQuFdA.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-BkV9hDc-.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-BK9yydrL.js +0 -1
- package/dist/gateway/static/root/assets/notes-workbench-Dd0v-hK2.js +0 -2
- package/dist/gateway/static/root/assets/project-detail-page-DSPhB3do.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-C-fZk1f7.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BGaVc3t7.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-DbnzgQOP.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-CuDBoz5k.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-06IYvwDt.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-0ODH0DXl.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-3W2-wDyO.js +0 -2
- package/dist/gateway/static/root/assets/voice-api-key-field-BPn1ZEga.js +0 -1
- package/dist/gateway/static/root/assets/work-item-detail-page-BQGTTsz1.js +0 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-na-N4jZ6.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-CjeVDs4h.js +0 -18
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["getPiAiModel","getPiAiProviders","getPiAiModels"],"sources":["../../../src/providers/index.ts"],"sourcesContent":["/**\n * Model provider module - integrates built-in models with custom models from models.json\n */\n\nimport {\n\tgetModel as getPiAiModel,\n\tgetModels as getPiAiModels,\n\tgetProviders as getPiAiProviders,\n\ttype Model,\n\ttype Api,\n} from '@earendil-works/pi-ai/compat';\nimport { getAgentDefaultModelRef, type Config } from '../config/schema.js';\nimport { getModelRegistry } from './model-registry.js';\nimport { CredentialResolver, resolveApiKey, hasCredentials } from '../auth/credentials.js';\nimport { hasProviderAuthOnDiskSync } from '../auth/sync-provider-auth.js';\nimport { getApiKeyFromEnv } from './env-keys.js';\nimport { getSupplementalModels } from './model-supplements.js';\nimport { getProviderRegistry } from './plugin-registry.js';\nimport type { ProviderModelDefinition } from '../extensions/types/providers.js';\n\nexport { getApiKeyFromEnv, PROVIDER_ENV_MAP } from './env-keys.js';\n\n/** Sentinel base URL: model is served by an extension {@link ProviderPluginRegistry} provider. */\nexport const EXTENSION_PROVIDER_BASE_URL = 'extension://provider-plugin';\nconst OPENAI_CODEX_CANONICAL_BASE_URL = 'https://chatgpt.com/backend-api/codex';\n\nfunction normalizeProviderModel(model: Model<Api>): Model<Api> {\n\tif (model.provider === 'openai-codex' && model.api === 'openai-codex-responses') {\n\t\treturn { ...model, baseUrl: OPENAI_CODEX_CANONICAL_BASE_URL } as Model<Api>;\n\t}\n\treturn model;\n}\n\n/** Map a plugin registry model to the pi-ai {@link Model} shape. */\nexport function pluginModelToModel(providerId: string, definition: ProviderModelDefinition): Model<Api> {\n\treturn {\n\t\tprovider: providerId,\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\tapi: 'openai-completions' as Api,\n\t\tbaseUrl: EXTENSION_PROVIDER_BASE_URL,\n\t\treasoning: false,\n\t\tinput: definition.supportsImages ? (['text', 'image'] as ('text' | 'image')[]) : (['text'] as ('text' | 'image')[]),\n\t\tcontextWindow: definition.contextWindow ?? 128000,\n\t\tmaxTokens: definition.maxOutputTokens ?? 4096,\n\t\tcost: {\n\t\t\tinput: definition.pricing?.input ?? 0,\n\t\t\toutput: definition.pricing?.output ?? 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t},\n\t} as Model<Api>;\n}\n\n/**\n * Get API key synchronously: checks registry (models.json) first, then environment variables.\n * Use this for Agent's getApiKey callback which must be synchronous.\n */\nexport function getApiKeySync(provider: string): string | undefined {\n const pluginRegistry = getProviderRegistry();\n if (pluginRegistry.has(provider)) return 'extension-managed';\n\n const registry = getModelRegistry();\n const registryKey = registry.getApiKey(provider);\n if (registryKey) {\n return registryKey;\n }\n return getApiKeyFromEnv(provider);\n}\n\n/**\n * Resolve model reference. Supports:\n * - \"provider/modelId\" format\n * - \"modelId\" auto-detection via pi-ai or custom models\n * @throws if model not found\n */\nexport function resolveModel(ref: string): Model<Api> {\n\tconst trimmedRef = typeof ref === 'string' ? ref.trim() : '';\n\tif (!trimmedRef) {\n\t\tthrow new Error(\n\t\t\t'No default model configured. Choose a model in onboarding or set agents.capabilityPresets.default.models.roles.<role>.model.',\n\t\t);\n\t}\n\n\t// First try ModelRegistry (includes custom models)\n\tconst registry = getModelRegistry();\n\tconst customModel = registry.resolve(trimmedRef);\n\tif (customModel) {\n\t\treturn customModel;\n\t}\n\n\tif (trimmedRef.includes('/')) {\n\t\tconst [provider, modelId] = trimmedRef.split('/');\n\t\tconst piAiModel = getPiAiModel(provider as any, modelId as any);\n\t\tif (piAiModel) return normalizeProviderModel(piAiModel as Model<Api>);\n\n\t\tconst supplementalModel = getSupplementalModels().find(m => m.provider === provider && m.id === modelId);\n\t\tif (supplementalModel) return normalizeProviderModel(supplementalModel);\n\n\t\tconst pluginRegistry = getProviderRegistry();\n\t\tconst plugin = pluginRegistry.get(provider);\n\t\tif (plugin) {\n\t\t\tconst pluginModel = plugin.models.find(m => m.id === modelId);\n\t\t\tif (pluginModel) return pluginModelToModel(provider, pluginModel);\n\t\t}\n\t\tthrow new Error(`Model not found: ${trimmedRef}`);\n\t}\n\n\tfor (const provider of getPiAiProviders()) {\n\t\ttry {\n\t\t\tconst models = getPiAiModels(provider);\n\t\t\tconst found = models.find(m => m.id === trimmedRef);\n\t\t\tif (found) return normalizeProviderModel(found as Model<Api>);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\tconst supplementalModel = getSupplementalModels().find(m => m.id === trimmedRef);\n\tif (supplementalModel) return normalizeProviderModel(supplementalModel);\n\n\tconst pluginRegistry = getProviderRegistry();\n\tfor (const plugin of pluginRegistry.listAll()) {\n\t\tconst found = plugin.models.find(m => m.id === trimmedRef);\n\t\tif (found) return pluginModelToModel(plugin.id, found);\n\t}\n\n\tthrow new Error(`Model not found: ${trimmedRef}. Use format: provider/model-id`);\n}\n\nexport function getModelsByProvider(provider: string): readonly Model<Api>[] {\n\tconst registry = getModelRegistry();\n\tconst fromRegistry = registry.getAll().filter(m => m.provider === provider).map(normalizeProviderModel);\n\tconst existingIds = new Set(fromRegistry.map(m => m.id));\n\tconst supplementalModels = getSupplementalModels()\n\t\t.filter(m => m.provider === provider && !existingIds.has(m.id))\n\t\t.map(normalizeProviderModel);\n\tconst plugin = getProviderRegistry().get(provider);\n\tif (!plugin) return [...fromRegistry, ...supplementalModels];\n\tconst pluginModels = plugin.models.map(m => pluginModelToModel(provider, m));\n\treturn [...fromRegistry, ...supplementalModels, ...pluginModels];\n}\n\nexport function getAllProviders(): string[] {\n\tconst registry = getModelRegistry();\n\tconst providers = new Set<string>();\n\n\t// Add built-in providers\n\tfor (const p of getPiAiProviders()) {\n\t\tproviders.add(p);\n\t}\n\n\t// Add custom providers from registry\n\tfor (const m of registry.getAll()) {\n\t\tproviders.add(m.provider);\n\t}\n\n\tfor (const plugin of getProviderRegistry().listAll()) {\n\t\tproviders.add(plugin.id);\n\t}\n\n\treturn Array.from(providers);\n}\n\nexport async function getApiKey(provider: string): Promise<string | undefined> {\n\tif (getProviderRegistry().has(provider)) return 'extension-managed';\n\n\t// Use new credential resolver first (checks: agent private > global > oauth > env)\n\tconst credentialKey = await resolveApiKey(provider);\n\tif (credentialKey) {\n\t\treturn credentialKey;\n\t}\n\n\t// Check registry for custom providers (from models.json)\n\tconst registry = getModelRegistry();\n\tconst registryKey = registry.getApiKey(provider);\n\tif (registryKey) {\n\t\treturn registryKey;\n\t}\n\n\t// Fallback to environment variables\n\treturn getApiKeyFromEnv(provider);\n}\n\n/**\n * Synchronous version for use in non-async contexts\n * Only checks environment variables and registry, not credential system\n */\nexport function isProviderConfiguredSync(provider: string): boolean {\n\tif (getProviderRegistry().has(provider)) return true;\n\n\t// Check registry for custom providers\n\tconst registry = getModelRegistry();\n\tif (registry.getApiKey(provider)) {\n\t\treturn true;\n\t}\n\t// Check environment variables\n\tif (getApiKeyFromEnv(provider)) {\n\t\treturn true;\n\t}\n\t// Gateway UI / CLI store keys in auth-profiles.json (async CredentialResolver); sync path for fallback list\n\treturn hasProviderAuthOnDiskSync(provider);\n}\n\nexport async function isProviderConfigured(provider: string): Promise<boolean> {\n if (getProviderRegistry().has(provider)) return true;\n\n // Check registry first for custom providers (from models.json)\n const registry = getModelRegistry();\n if (registry.getApiKey(provider)) {\n return true;\n }\n return await hasCredentials(provider);\n}\n\n/** Where runtime {@link getApiKey} resolves the key from (no secret values). */\nexport type ProviderActiveKeySource = 'none' | 'agent' | 'gateway' | 'oauth' | 'env' | 'models_json' | 'extension';\n\nexport type ProviderAuthMode = ProviderActiveKeySource;\nexport type ProviderAuthStatus = 'connected' | 'expired' | 'not_connected';\n\nexport interface ProviderAuthState {\n authMode: ProviderAuthMode;\n authStatus: ProviderAuthStatus;\n expiresAt?: number;\n}\n\nexport async function getProviderActiveKeySource(provider: string): Promise<ProviderActiveKeySource> {\n const authState = await getProviderAuthState(provider);\n return authState.authMode;\n}\n\nexport async function getProviderAuthState(provider: string): Promise<ProviderAuthState> {\n if (getProviderRegistry().has(provider)) {\n return { authMode: 'extension', authStatus: 'connected' };\n }\n\n const resolver = new CredentialResolver();\n const fromCredentials = await resolver.resolveApiKeySource(provider);\n if (fromCredentials === 'agent') return { authMode: 'agent', authStatus: 'connected' };\n if (fromCredentials === 'global') return { authMode: 'gateway', authStatus: 'connected' };\n if (fromCredentials === 'oauth') {\n const token = await resolver.loadOAuthTokenRecord(provider);\n const expired = Boolean(token?.expiresAt && token.expiresAt < Date.now());\n return {\n authMode: 'oauth',\n authStatus: expired ? 'expired' : 'connected',\n ...(token?.expiresAt ? { expiresAt: token.expiresAt } : {}),\n };\n }\n if (fromCredentials === 'env') return { authMode: 'env', authStatus: 'connected' };\n\n const expiredOAuthToken = await resolver.loadOAuthTokenRecord(provider);\n if (expiredOAuthToken?.expiresAt && expiredOAuthToken.expiresAt < Date.now()) {\n return {\n authMode: 'oauth',\n authStatus: 'expired',\n expiresAt: expiredOAuthToken.expiresAt,\n };\n }\n\n const registry = getModelRegistry();\n if (registry.getApiKey(provider)) {\n return { authMode: 'models_json', authStatus: 'connected' };\n }\n\n return { authMode: 'none', authStatus: 'not_connected' };\n}\n\nexport async function getConfiguredProviders(): Promise<string[]> {\n\tconst allProviders = getAllProviders();\n\tconst configured: string[] = [];\n\tfor (const p of allProviders) {\n\t\tif (await isProviderConfigured(p)) {\n\t\t\tconfigured.push(p);\n\t\t}\n\t}\n\treturn configured;\n}\n\nexport function getAllModels(): readonly Model<Api>[] {\n\tconst registry = getModelRegistry();\n\tconst registryModels = registry.getAll().map(normalizeProviderModel);\n\tconst existingIds = new Set(registryModels.map(m => `${m.provider}/${m.id}`));\n\tconst supplementalModels = getSupplementalModels()\n\t\t.filter(m => !existingIds.has(`${m.provider}/${m.id}`))\n\t\t.map(normalizeProviderModel);\n\tfor (const model of supplementalModels) {\n\t\texistingIds.add(`${model.provider}/${model.id}`);\n\t}\n\tconst pluginProviders = getProviderRegistry().listAll();\n\tif (pluginProviders.length === 0) return [...registryModels, ...supplementalModels];\n\n\tconst merged: Model<Api>[] = [...registryModels, ...supplementalModels];\n\tfor (const plugin of pluginProviders) {\n\t\tfor (const model of plugin.models) {\n\t\t\tconst compositeId = `${plugin.id}/${model.id}`;\n\t\t\tif (!existingIds.has(compositeId)) {\n\t\t\t\tmerged.push(pluginModelToModel(plugin.id, model));\n\t\t\t\texistingIds.add(compositeId);\n\t\t\t}\n\t\t}\n\t}\n\treturn merged;\n}\n\nexport async function getAvailableModels(): Promise<readonly Model<Api>[]> {\n\tconst allModels = getAllModels();\n\tconst pluginRegistry = getProviderRegistry();\n\tconst available: Model<Api>[] = [];\n\tconst configuredByProvider = new Map<string, boolean>();\n\n\tfor (const model of allModels) {\n\t\tif (pluginRegistry.has(model.provider)) {\n\t\t\tavailable.push(model);\n\t\t\tcontinue;\n\t\t}\n\t\tlet configured = configuredByProvider.get(model.provider);\n\t\tif (configured === undefined) {\n\t\t\tconfigured = await isProviderConfigured(model.provider);\n\t\t\tconfiguredByProvider.set(model.provider, configured);\n\t\t}\n\t\tif (configured) {\n\t\t\tavailable.push(model);\n\t\t}\n\t}\n\treturn available;\n}\n\nexport type { Model, Api } from '@earendil-works/pi-ai';\n\nexport type ProviderCategory = 'common' | 'domestic' | 'specialty' | 'oauth' | 'enterprise' | 'extension';\n\nexport interface ProviderMeta {\n name: string;\n category: ProviderCategory;\n supportsOAuth?: boolean;\n supportsApiKey?: boolean;\n}\n\nexport const PROVIDER_META: Record<string, ProviderMeta> = {\n 'openai': { name: 'OpenAI', category: 'common', supportsApiKey: true },\n 'anthropic': { name: 'Anthropic', category: 'common', supportsApiKey: true, supportsOAuth: true },\n 'deepseek': { name: 'DeepSeek', category: 'domestic', supportsApiKey: true },\n 'google': { name: 'Google AI', category: 'common', supportsApiKey: true },\n 'groq': { name: 'Groq', category: 'common', supportsApiKey: true },\n 'minimax': { name: 'MiniMax', category: 'domestic', supportsApiKey: true, supportsOAuth: true },\n 'minimax-cn': { name: 'MiniMax CN', category: 'domestic', supportsApiKey: true, supportsOAuth: true },\n 'kimi-coding': { name: 'Kimi For Coding', category: 'domestic', supportsApiKey: true, supportsOAuth: true },\n 'xai': { name: 'xAI', category: 'specialty', supportsApiKey: true },\n 'mistral': { name: 'Mistral AI', category: 'specialty', supportsApiKey: true },\n 'cerebras': { name: 'Cerebras', category: 'specialty', supportsApiKey: true },\n 'openrouter': { name: 'OpenRouter', category: 'specialty', supportsApiKey: true },\n 'huggingface': { name: 'Hugging Face', category: 'specialty', supportsApiKey: true },\n moonshotai: { name: 'Moonshot AI (Kimi · International)', category: 'domestic', supportsApiKey: true },\n 'moonshotai-cn': { name: 'Moonshot AI (Kimi · China)', category: 'domestic', supportsApiKey: true },\n fireworks: { name: 'Fireworks AI', category: 'specialty', supportsApiKey: true },\n together: { name: 'Together AI', category: 'specialty', supportsApiKey: true },\n 'cloudflare-workers-ai': { name: 'Cloudflare Workers AI', category: 'enterprise', supportsApiKey: true },\n 'cloudflare-ai-gateway': { name: 'Cloudflare AI Gateway', category: 'enterprise', supportsApiKey: true },\n xiaomi: { name: 'Xiaomi Mimo', category: 'domestic', supportsApiKey: true },\n 'xiaomi-token-plan-cn': { name: 'Xiaomi Token Plan (CN)', category: 'domestic', supportsApiKey: true },\n 'xiaomi-token-plan-ams': { name: 'Xiaomi Token Plan (AMS)', category: 'domestic', supportsApiKey: true },\n 'xiaomi-token-plan-sgp': { name: 'Xiaomi Token Plan (SGP)', category: 'domestic', supportsApiKey: true },\n 'opencode': { name: 'OpenCode', category: 'specialty', supportsApiKey: true },\n 'opencode-go': { name: 'OpenCode Go', category: 'specialty', supportsApiKey: true },\n 'dashscope': { name: 'DashScope (Alibaba)', category: 'domestic', supportsApiKey: true },\n 'dashscope-cn': { name: 'Alibaba Bailian / DashScope China', category: 'domestic', supportsApiKey: true },\n 'dashscope-intl': { name: 'Alibaba Bailian / DashScope International', category: 'domestic', supportsApiKey: true },\n 'volcengine-ark': { name: 'Volcengine Ark', category: 'domestic', supportsApiKey: true },\n 'volcengine-plan': { name: 'Volcengine Doubao Coding Plan', category: 'domestic', supportsApiKey: true },\n 'byteplus-plan': { name: 'BytePlus Doubao Coding Plan', category: 'domestic', supportsApiKey: true },\n 'stepfun': { name: 'StepFun', category: 'domestic', supportsApiKey: true },\n 'stepfun-cn': { name: 'StepFun China', category: 'domestic', supportsApiKey: true },\n 'stepfun-intl': { name: 'StepFun International', category: 'domestic', supportsApiKey: true },\n 'stepfun-plan': { name: 'StepFun Step Plan', category: 'domestic', supportsApiKey: true },\n 'stepfun-plan-cn': { name: 'StepFun Step Plan China', category: 'domestic', supportsApiKey: true },\n 'stepfun-plan-intl': { name: 'StepFun Step Plan International', category: 'domestic', supportsApiKey: true },\n 'ant-ling': { name: 'Ant Ling', category: 'domestic', supportsApiKey: true },\n 'zhipu-cn': { name: 'Zhipu GLM (China)', category: 'domestic', supportsApiKey: true },\n 'zai-coding-cn': { name: 'Zhipu GLM Coding Plan (China)', category: 'domestic', supportsApiKey: true },\n 'zai-coding-global': { name: 'Zhipu GLM Coding Plan (International)', category: 'domestic', supportsApiKey: true },\n 'zhipu-coding-cn': { name: 'Zhipu GLM Coding Plan (China)', category: 'domestic', supportsApiKey: true },\n /** International GLM (api.z.ai). Auth: API key (ZAI_API_KEY); no published OAuth for this HTTP API. */\n 'zai': { name: 'Zhipu GLM (International · z.ai)', category: 'domestic', supportsApiKey: true },\n 'amazon-bedrock': { name: 'Amazon Bedrock', category: 'enterprise', supportsApiKey: true },\n 'azure-openai-responses': { name: 'Azure OpenAI', category: 'enterprise', supportsApiKey: true },\n 'google-vertex': { name: 'Google Vertex AI', category: 'enterprise', supportsApiKey: true },\n 'vercel-ai-gateway': { name: 'Vercel AI Gateway', category: 'enterprise', supportsApiKey: true },\n 'github-copilot': { name: 'GitHub Copilot (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n 'openai-codex': { name: 'OpenAI Codex (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n 'google-gemini-cli': { name: 'Google Gemini CLI (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n 'google-antigravity': { name: 'Google Antigravity (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n};\n\nexport function getSortedProviders(): string[] {\n const all = getAllProviders();\n const catOrder: Record<ProviderCategory, number> = {\n common: 0,\n domestic: 1,\n specialty: 2,\n enterprise: 3,\n oauth: 4,\n extension: 5,\n };\n const pluginRegistry = getProviderRegistry();\n\n return [...all].sort((a, b) => {\n const catA = pluginRegistry.has(a) ? 'extension' : (PROVIDER_META[a]?.category ?? 'specialty');\n const catB = pluginRegistry.has(b) ? 'extension' : (PROVIDER_META[b]?.category ?? 'specialty');\n if (catOrder[catA] !== catOrder[catB]) {\n return catOrder[catA] - catOrder[catB];\n }\n return a.localeCompare(b);\n });\n}\n\nexport function getProviderDisplayName(provider: string): string {\n const plugin = getProviderRegistry().get(provider);\n if (plugin) return plugin.name;\n return PROVIDER_META[provider]?.name || provider;\n}\n\nexport function providerSupportsOAuth(provider: string): boolean {\n return PROVIDER_META[provider]?.supportsOAuth ?? false;\n}\n\nexport function providerSupportsApiKey(provider: string): boolean {\n return PROVIDER_META[provider]?.supportsApiKey ?? true;\n}\n\n// ============================================\n// Dynamic Default Model Resolution\n// ============================================\n\n/**\n * Get a default model reference.\n * Priority:\n * 1. Effective model inherited from the global default preset / agent presets / agent override\n * 2. Empty string only when no default model is present in the loaded config\n */\nexport async function getDefaultModel(config?: Config | null | undefined): Promise<string> {\n const modelRef = config ? getAgentDefaultModelRef(config) : undefined;\n if (modelRef) {\n const availableModels = await getAvailableModels();\n const configured = availableModels.find(m =>\n `${m.provider}/${m.id}` === modelRef ||\n m.id === modelRef\n );\n if (configured) {\n return `${configured.provider}/${configured.id}`;\n }\n return modelRef;\n }\n\n return '';\n}\n\n/**\n * Synchronous default model resolution for constructors and sync code paths.\n * Uses catalog/registry only (no async credential checks).\n *\n * When no model is present in the loaded config, returns an empty string so\n * setup flows can prompt for the global default model.\n */\nexport function getDefaultModelSync(config?: Config | null | undefined): string {\n const modelRef = config ? getAgentDefaultModelRef(config) : undefined;\n if (modelRef) {\n return modelRef;\n }\n\n return '';\n}\n\n// Re-export ModelRegistry for advanced use cases\nexport { ModelRegistry, getModelRegistry, resetModelRegistry, prewarmModelRegistry } from './model-registry.js';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["getPiAiModel","getPiAiProviders","getPiAiModels"],"sources":["../../../src/providers/index.ts"],"sourcesContent":["/**\n * Model provider module - integrates built-in models with custom models from models.json\n */\n\nimport {\n\tgetModel as getPiAiModel,\n\tgetModels as getPiAiModels,\n\tgetProviders as getPiAiProviders,\n\ttype Model,\n\ttype Api,\n} from '@earendil-works/pi-ai/compat';\nimport { getAgentDefaultModelRef, type Config } from '../config/schema.js';\nimport { getModelRegistry } from './model-registry.js';\nimport {\n\tCredentialResolver,\n\tresolveApiKey,\n\thasCredentials,\n\ttype CredentialResolverOptions,\n} from '../auth/credentials.js';\nimport { hasProviderAuthOnDiskSync } from '../auth/sync-provider-auth.js';\nimport { getApiKeyFromEnv } from './env-keys.js';\nimport { EXTENSION_PROVIDER_BASE_URL } from './constants.js';\nimport { getSupplementalModels } from './model-supplements.js';\nimport { getProviderRegistry } from './plugin-registry.js';\nimport type { ProviderModelDefinition } from '../extensions/types/providers.js';\n\nexport { EXTENSION_PROVIDER_BASE_URL } from './constants.js';\nexport { getApiKeyFromEnv, PROVIDER_ENV_MAP } from './env-keys.js';\nconst OPENAI_CODEX_CANONICAL_BASE_URL = 'https://chatgpt.com/backend-api/codex';\n\nfunction normalizeProviderModel(model: Model<Api>): Model<Api> {\n\tif (model.provider === 'openai-codex' && model.api === 'openai-codex-responses') {\n\t\treturn { ...model, baseUrl: OPENAI_CODEX_CANONICAL_BASE_URL } as Model<Api>;\n\t}\n\treturn model;\n}\n\n/** Map a plugin registry model to the pi-ai {@link Model} shape. */\nexport function pluginModelToModel(providerId: string, definition: ProviderModelDefinition): Model<Api> {\n\treturn {\n\t\tprovider: providerId,\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\tapi: 'openai-completions' as Api,\n\t\tbaseUrl: EXTENSION_PROVIDER_BASE_URL,\n\t\treasoning: false,\n\t\tinput: definition.supportsImages ? (['text', 'image'] as ('text' | 'image')[]) : (['text'] as ('text' | 'image')[]),\n\t\tcontextWindow: definition.contextWindow ?? 128000,\n\t\tmaxTokens: definition.maxOutputTokens ?? 4096,\n\t\tcost: {\n\t\t\tinput: definition.pricing?.input ?? 0,\n\t\t\toutput: definition.pricing?.output ?? 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t},\n\t} as Model<Api>;\n}\n\n/**\n * Get API key synchronously: checks registry (models.json) first, then environment variables.\n * Use this for Agent's getApiKey callback which must be synchronous.\n */\nexport function getApiKeySync(provider: string): string | undefined {\n const pluginRegistry = getProviderRegistry();\n if (pluginRegistry.has(provider)) return 'extension-managed';\n\n const registry = getModelRegistry();\n const registryKey = registry.getApiKey(provider);\n if (registryKey) {\n return registryKey;\n }\n return getApiKeyFromEnv(provider);\n}\n\n/**\n * Resolve model reference. Supports:\n * - \"provider/modelId\" format\n * - \"modelId\" auto-detection via pi-ai or custom models\n * @throws if model not found\n */\nexport function resolveModel(ref: string): Model<Api> {\n\tconst trimmedRef = typeof ref === 'string' ? ref.trim() : '';\n\tif (!trimmedRef) {\n\t\tthrow new Error(\n\t\t\t'No default model configured. Choose a model in onboarding or set agents.capabilityPresets.default.models.roles.<role>.model.',\n\t\t);\n\t}\n\n\t// First try ModelRegistry (includes custom models)\n\tconst registry = getModelRegistry();\n\tconst customModel = registry.resolve(trimmedRef);\n\tif (customModel) {\n\t\treturn customModel;\n\t}\n\n\tif (trimmedRef.includes('/')) {\n\t\tconst [provider, modelId] = trimmedRef.split('/');\n\t\tconst piAiModel = getPiAiModel(provider as any, modelId as any);\n\t\tif (piAiModel) return normalizeProviderModel(piAiModel as Model<Api>);\n\n\t\tconst supplementalModel = getSupplementalModels().find(m => m.provider === provider && m.id === modelId);\n\t\tif (supplementalModel) return normalizeProviderModel(supplementalModel);\n\n\t\tconst pluginRegistry = getProviderRegistry();\n\t\tconst plugin = pluginRegistry.get(provider);\n\t\tif (plugin) {\n\t\t\tconst pluginModel = plugin.models.find(m => m.id === modelId);\n\t\t\tif (pluginModel) return pluginModelToModel(provider, pluginModel);\n\t\t}\n\t\tthrow new Error(`Model not found: ${trimmedRef}`);\n\t}\n\n\tfor (const provider of getPiAiProviders()) {\n\t\ttry {\n\t\t\tconst models = getPiAiModels(provider);\n\t\t\tconst found = models.find(m => m.id === trimmedRef);\n\t\t\tif (found) return normalizeProviderModel(found as Model<Api>);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\tconst supplementalModel = getSupplementalModels().find(m => m.id === trimmedRef);\n\tif (supplementalModel) return normalizeProviderModel(supplementalModel);\n\n\tconst pluginRegistry = getProviderRegistry();\n\tfor (const plugin of pluginRegistry.listAll()) {\n\t\tconst found = plugin.models.find(m => m.id === trimmedRef);\n\t\tif (found) return pluginModelToModel(plugin.id, found);\n\t}\n\n\tthrow new Error(`Model not found: ${trimmedRef}. Use format: provider/model-id`);\n}\n\nexport function getModelsByProvider(provider: string): readonly Model<Api>[] {\n\tconst registry = getModelRegistry();\n\tconst fromRegistry = registry.getAll().filter(m => m.provider === provider).map(normalizeProviderModel);\n\tconst existingIds = new Set(fromRegistry.map(m => m.id));\n\tconst supplementalModels = getSupplementalModels()\n\t\t.filter(m => m.provider === provider && !existingIds.has(m.id))\n\t\t.map(normalizeProviderModel);\n\tconst plugin = getProviderRegistry().get(provider);\n\tif (!plugin) return [...fromRegistry, ...supplementalModels];\n\tconst pluginModels = plugin.models.map(m => pluginModelToModel(provider, m));\n\treturn [...fromRegistry, ...supplementalModels, ...pluginModels];\n}\n\nexport function getAllProviders(): string[] {\n\tconst registry = getModelRegistry();\n\tconst providers = new Set<string>();\n\n\t// Add built-in providers\n\tfor (const p of getPiAiProviders()) {\n\t\tproviders.add(p);\n\t}\n\n\t// Add custom providers from registry\n\tfor (const m of registry.getAll()) {\n\t\tproviders.add(m.provider);\n\t}\n\n\tfor (const plugin of getProviderRegistry().listAll()) {\n\t\tproviders.add(plugin.id);\n\t}\n\n\treturn Array.from(providers);\n}\n\nexport async function getApiKey(\n\tprovider: string,\n\tcredentialOptions?: CredentialResolverOptions,\n): Promise<string | undefined> {\n\tif (getProviderRegistry().has(provider)) return 'extension-managed';\n\n\t// Use new credential resolver first (checks: agent private > global > oauth > env)\n\tconst credentialKey = await resolveApiKey(provider, credentialOptions);\n\tif (credentialKey) {\n\t\treturn credentialKey;\n\t}\n\n\t// Check registry for custom providers (from models.json)\n\tconst registry = getModelRegistry();\n\tconst registryKey = registry.getApiKey(provider);\n\tif (registryKey) {\n\t\treturn registryKey;\n\t}\n\n\t// Fallback to environment variables\n\treturn getApiKeyFromEnv(provider);\n}\n\n/**\n * Synchronous version for use in non-async contexts\n * Only checks environment variables and registry, not credential system\n */\nexport function isProviderConfiguredSync(provider: string): boolean {\n\tif (getProviderRegistry().has(provider)) return true;\n\n\t// Check registry for custom providers\n\tconst registry = getModelRegistry();\n\tif (registry.getApiKey(provider)) {\n\t\treturn true;\n\t}\n\t// Check environment variables\n\tif (getApiKeyFromEnv(provider)) {\n\t\treturn true;\n\t}\n\t// Gateway UI / CLI store keys in auth-profiles.json (async CredentialResolver); sync path for fallback list\n\treturn hasProviderAuthOnDiskSync(provider);\n}\n\nexport async function isProviderConfigured(provider: string): Promise<boolean> {\n if (getProviderRegistry().has(provider)) return true;\n\n // Check registry first for custom providers (from models.json)\n const registry = getModelRegistry();\n if (registry.getApiKey(provider)) {\n return true;\n }\n return await hasCredentials(provider);\n}\n\n/** Where runtime {@link getApiKey} resolves the key from (no secret values). */\nexport type ProviderActiveKeySource = 'none' | 'agent' | 'gateway' | 'oauth' | 'env' | 'models_json' | 'extension';\n\nexport type ProviderAuthMode = ProviderActiveKeySource;\nexport type ProviderAuthStatus = 'connected' | 'expired' | 'not_connected';\n\nexport interface ProviderAuthState {\n authMode: ProviderAuthMode;\n authStatus: ProviderAuthStatus;\n expiresAt?: number;\n}\n\nexport async function getProviderActiveKeySource(provider: string): Promise<ProviderActiveKeySource> {\n const authState = await getProviderAuthState(provider);\n return authState.authMode;\n}\n\nexport async function getProviderAuthState(provider: string): Promise<ProviderAuthState> {\n if (getProviderRegistry().has(provider)) {\n return { authMode: 'extension', authStatus: 'connected' };\n }\n\n const resolver = new CredentialResolver();\n const fromCredentials = await resolver.resolveApiKeySource(provider);\n if (fromCredentials === 'agent') return { authMode: 'agent', authStatus: 'connected' };\n if (fromCredentials === 'global') return { authMode: 'gateway', authStatus: 'connected' };\n if (fromCredentials === 'oauth') {\n const token = await resolver.loadOAuthTokenRecord(provider);\n const expired = Boolean(token?.expiresAt && token.expiresAt < Date.now());\n return {\n authMode: 'oauth',\n authStatus: expired ? 'expired' : 'connected',\n ...(token?.expiresAt ? { expiresAt: token.expiresAt } : {}),\n };\n }\n if (fromCredentials === 'env') return { authMode: 'env', authStatus: 'connected' };\n\n const expiredOAuthToken = await resolver.loadOAuthTokenRecord(provider);\n if (expiredOAuthToken?.expiresAt && expiredOAuthToken.expiresAt < Date.now()) {\n return {\n authMode: 'oauth',\n authStatus: 'expired',\n expiresAt: expiredOAuthToken.expiresAt,\n };\n }\n\n const registry = getModelRegistry();\n if (registry.getApiKey(provider)) {\n return { authMode: 'models_json', authStatus: 'connected' };\n }\n\n return { authMode: 'none', authStatus: 'not_connected' };\n}\n\nexport async function getConfiguredProviders(): Promise<string[]> {\n\tconst allProviders = getAllProviders();\n\tconst configured: string[] = [];\n\tfor (const p of allProviders) {\n\t\tif (await isProviderConfigured(p)) {\n\t\t\tconfigured.push(p);\n\t\t}\n\t}\n\treturn configured;\n}\n\nexport function getAllModels(): readonly Model<Api>[] {\n\tconst registry = getModelRegistry();\n\tconst registryModels = registry.getAll().map(normalizeProviderModel);\n\tconst existingIds = new Set(registryModels.map(m => `${m.provider}/${m.id}`));\n\tconst supplementalModels = getSupplementalModels()\n\t\t.filter(m => !existingIds.has(`${m.provider}/${m.id}`))\n\t\t.map(normalizeProviderModel);\n\tfor (const model of supplementalModels) {\n\t\texistingIds.add(`${model.provider}/${model.id}`);\n\t}\n\tconst pluginProviders = getProviderRegistry().listAll();\n\tif (pluginProviders.length === 0) return [...registryModels, ...supplementalModels];\n\n\tconst merged: Model<Api>[] = [...registryModels, ...supplementalModels];\n\tfor (const plugin of pluginProviders) {\n\t\tfor (const model of plugin.models) {\n\t\t\tconst compositeId = `${plugin.id}/${model.id}`;\n\t\t\tif (!existingIds.has(compositeId)) {\n\t\t\t\tmerged.push(pluginModelToModel(plugin.id, model));\n\t\t\t\texistingIds.add(compositeId);\n\t\t\t}\n\t\t}\n\t}\n\treturn merged;\n}\n\nexport async function getAvailableModels(): Promise<readonly Model<Api>[]> {\n\tconst allModels = getAllModels();\n\tconst pluginRegistry = getProviderRegistry();\n\tconst available: Model<Api>[] = [];\n\tconst configuredByProvider = new Map<string, boolean>();\n\n\tfor (const model of allModels) {\n\t\tif (pluginRegistry.has(model.provider)) {\n\t\t\tavailable.push(model);\n\t\t\tcontinue;\n\t\t}\n\t\tlet configured = configuredByProvider.get(model.provider);\n\t\tif (configured === undefined) {\n\t\t\tconfigured = await isProviderConfigured(model.provider);\n\t\t\tconfiguredByProvider.set(model.provider, configured);\n\t\t}\n\t\tif (configured) {\n\t\t\tavailable.push(model);\n\t\t}\n\t}\n\treturn available;\n}\n\nexport type { Model, Api } from '@earendil-works/pi-ai';\n\nexport type ProviderCategory = 'common' | 'domestic' | 'specialty' | 'oauth' | 'enterprise' | 'extension';\n\nexport interface ProviderMeta {\n name: string;\n category: ProviderCategory;\n supportsOAuth?: boolean;\n supportsApiKey?: boolean;\n}\n\nexport const PROVIDER_META: Record<string, ProviderMeta> = {\n 'openai': { name: 'OpenAI', category: 'common', supportsApiKey: true },\n 'anthropic': { name: 'Anthropic', category: 'common', supportsApiKey: true, supportsOAuth: true },\n 'deepseek': { name: 'DeepSeek', category: 'domestic', supportsApiKey: true },\n 'google': { name: 'Google AI', category: 'common', supportsApiKey: true },\n 'groq': { name: 'Groq', category: 'common', supportsApiKey: true },\n 'minimax': { name: 'MiniMax', category: 'domestic', supportsApiKey: true, supportsOAuth: true },\n 'minimax-cn': { name: 'MiniMax CN', category: 'domestic', supportsApiKey: true, supportsOAuth: true },\n 'kimi-coding': { name: 'Kimi For Coding', category: 'domestic', supportsApiKey: true, supportsOAuth: true },\n 'xai': { name: 'xAI', category: 'specialty', supportsApiKey: true },\n 'mistral': { name: 'Mistral AI', category: 'specialty', supportsApiKey: true },\n 'cerebras': { name: 'Cerebras', category: 'specialty', supportsApiKey: true },\n 'openrouter': { name: 'OpenRouter', category: 'specialty', supportsApiKey: true },\n 'huggingface': { name: 'Hugging Face', category: 'specialty', supportsApiKey: true },\n moonshotai: { name: 'Moonshot AI (Kimi · International)', category: 'domestic', supportsApiKey: true },\n 'moonshotai-cn': { name: 'Moonshot AI (Kimi · China)', category: 'domestic', supportsApiKey: true },\n fireworks: { name: 'Fireworks AI', category: 'specialty', supportsApiKey: true },\n together: { name: 'Together AI', category: 'specialty', supportsApiKey: true },\n 'cloudflare-workers-ai': { name: 'Cloudflare Workers AI', category: 'enterprise', supportsApiKey: true },\n 'cloudflare-ai-gateway': { name: 'Cloudflare AI Gateway', category: 'enterprise', supportsApiKey: true },\n xiaomi: { name: 'Xiaomi Mimo', category: 'domestic', supportsApiKey: true },\n 'xiaomi-token-plan-cn': { name: 'Xiaomi Token Plan (CN)', category: 'domestic', supportsApiKey: true },\n 'xiaomi-token-plan-ams': { name: 'Xiaomi Token Plan (AMS)', category: 'domestic', supportsApiKey: true },\n 'xiaomi-token-plan-sgp': { name: 'Xiaomi Token Plan (SGP)', category: 'domestic', supportsApiKey: true },\n 'opencode': { name: 'OpenCode', category: 'specialty', supportsApiKey: true },\n 'opencode-go': { name: 'OpenCode Go', category: 'specialty', supportsApiKey: true },\n 'dashscope': { name: 'DashScope (Alibaba)', category: 'domestic', supportsApiKey: true },\n 'dashscope-cn': { name: 'Alibaba Bailian / DashScope China', category: 'domestic', supportsApiKey: true },\n 'dashscope-intl': { name: 'Alibaba Bailian / DashScope International', category: 'domestic', supportsApiKey: true },\n 'volcengine-ark': { name: 'Volcengine Ark', category: 'domestic', supportsApiKey: true },\n 'volcengine-plan': { name: 'Volcengine Doubao Coding Plan', category: 'domestic', supportsApiKey: true },\n 'byteplus-plan': { name: 'BytePlus Doubao Coding Plan', category: 'domestic', supportsApiKey: true },\n 'stepfun': { name: 'StepFun', category: 'domestic', supportsApiKey: true },\n 'stepfun-cn': { name: 'StepFun China', category: 'domestic', supportsApiKey: true },\n 'stepfun-intl': { name: 'StepFun International', category: 'domestic', supportsApiKey: true },\n 'stepfun-plan': { name: 'StepFun Step Plan', category: 'domestic', supportsApiKey: true },\n 'stepfun-plan-cn': { name: 'StepFun Step Plan China', category: 'domestic', supportsApiKey: true },\n 'stepfun-plan-intl': { name: 'StepFun Step Plan International', category: 'domestic', supportsApiKey: true },\n 'ant-ling': { name: 'Ant Ling', category: 'domestic', supportsApiKey: true },\n 'zhipu-cn': { name: 'Zhipu GLM (China)', category: 'domestic', supportsApiKey: true },\n 'zai-coding-cn': { name: 'Zhipu GLM Coding Plan (China)', category: 'domestic', supportsApiKey: true },\n 'zai-coding-global': { name: 'Zhipu GLM Coding Plan (International)', category: 'domestic', supportsApiKey: true },\n 'zhipu-coding-cn': { name: 'Zhipu GLM Coding Plan (China)', category: 'domestic', supportsApiKey: true },\n /** International GLM (api.z.ai). Auth: API key (ZAI_API_KEY); no published OAuth for this HTTP API. */\n 'zai': { name: 'Zhipu GLM (International · z.ai)', category: 'domestic', supportsApiKey: true },\n 'amazon-bedrock': { name: 'Amazon Bedrock', category: 'enterprise', supportsApiKey: true },\n 'azure-openai-responses': { name: 'Azure OpenAI', category: 'enterprise', supportsApiKey: true },\n 'google-vertex': { name: 'Google Vertex AI', category: 'enterprise', supportsApiKey: true },\n 'vercel-ai-gateway': { name: 'Vercel AI Gateway', category: 'enterprise', supportsApiKey: true },\n 'github-copilot': { name: 'GitHub Copilot (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n 'openai-codex': { name: 'OpenAI Codex (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n 'google-gemini-cli': { name: 'Google Gemini CLI (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n 'google-antigravity': { name: 'Google Antigravity (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n};\n\nexport function getSortedProviders(): string[] {\n const all = getAllProviders();\n const catOrder: Record<ProviderCategory, number> = {\n common: 0,\n domestic: 1,\n specialty: 2,\n enterprise: 3,\n oauth: 4,\n extension: 5,\n };\n const pluginRegistry = getProviderRegistry();\n\n return [...all].sort((a, b) => {\n const catA = pluginRegistry.has(a) ? 'extension' : (PROVIDER_META[a]?.category ?? 'specialty');\n const catB = pluginRegistry.has(b) ? 'extension' : (PROVIDER_META[b]?.category ?? 'specialty');\n if (catOrder[catA] !== catOrder[catB]) {\n return catOrder[catA] - catOrder[catB];\n }\n return a.localeCompare(b);\n });\n}\n\nexport function getProviderDisplayName(provider: string): string {\n const plugin = getProviderRegistry().get(provider);\n if (plugin) return plugin.name;\n return PROVIDER_META[provider]?.name || provider;\n}\n\nexport function providerSupportsOAuth(provider: string): boolean {\n return PROVIDER_META[provider]?.supportsOAuth ?? false;\n}\n\nexport function providerSupportsApiKey(provider: string): boolean {\n return PROVIDER_META[provider]?.supportsApiKey ?? true;\n}\n\n// ============================================\n// Dynamic Default Model Resolution\n// ============================================\n\n/**\n * Get a default model reference.\n * Priority:\n * 1. Effective model inherited from the global default preset / agent presets / agent override\n * 2. Empty string only when no default model is present in the loaded config\n */\nexport async function getDefaultModel(config?: Config | null | undefined): Promise<string> {\n const modelRef = config ? getAgentDefaultModelRef(config) : undefined;\n if (modelRef) {\n const availableModels = await getAvailableModels();\n const configured = availableModels.find(m =>\n `${m.provider}/${m.id}` === modelRef ||\n m.id === modelRef\n );\n if (configured) {\n return `${configured.provider}/${configured.id}`;\n }\n return modelRef;\n }\n\n return '';\n}\n\n/**\n * Synchronous default model resolution for constructors and sync code paths.\n * Uses catalog/registry only (no async credential checks).\n *\n * When no model is present in the loaded config, returns an empty string so\n * setup flows can prompt for the global default model.\n */\nexport function getDefaultModelSync(config?: Config | null | undefined): string {\n const modelRef = config ? getAgentDefaultModelRef(config) : undefined;\n if (modelRef) {\n return modelRef;\n }\n\n return '';\n}\n\n// Re-export ModelRegistry for advanced use cases\nexport { ModelRegistry, getModelRegistry, resetModelRegistry, prewarmModelRegistry } from './model-registry.js';\n"],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Api, type AssistantMessage, type Context, type Model, type SimpleStreamOptions } from '@earendil-works/pi-ai/compat';
|
|
2
|
+
import type { CredentialResolverOptions } from '../auth/credentials.js';
|
|
3
|
+
export declare function isLocalModelBaseUrl(baseUrl: string | undefined): boolean;
|
|
4
|
+
/** Codex Responses rejects the temperature request parameter. */
|
|
5
|
+
export declare function supportsModelTemperature(model: Model<Api>): boolean;
|
|
6
|
+
export declare function resolveModelCallApiKey(model: Model<Api>, credentialOptions?: CredentialResolverOptions): Promise<string | undefined>;
|
|
7
|
+
export declare function resolveModelCallOptions(model: Model<Api>, options?: SimpleStreamOptions, credentialOptions?: CredentialResolverOptions): Promise<SimpleStreamOptions>;
|
|
8
|
+
/**
|
|
9
|
+
* Complete a one-shot request with xopc credential resolution and extension-provider routing.
|
|
10
|
+
*/
|
|
11
|
+
export declare function completeWithResolvedCredentials(model: Model<Api>, context: Context, options?: SimpleStreamOptions, credentialOptions?: CredentialResolverOptions): Promise<AssistantMessage>;
|
|
12
|
+
/**
|
|
13
|
+
* Start a model stream with xopc credential resolution and extension-provider routing.
|
|
14
|
+
* Callers that need live visible output should consume the returned stream before calling
|
|
15
|
+
* its `result()` method.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createResolvedModelStream(model: Model<Api>, context: Context, options?: SimpleStreamOptions, credentialOptions?: CredentialResolverOptions): Promise<import("@earendil-works/pi-ai/compat").AssistantMessageEventStream>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { init_constants } from "./constants.js";
|
|
2
|
+
import { getApiKey, init_providers } from "./index.js";
|
|
3
|
+
import { createExtensionAwareStreamFn } from "./extension-stream-bridge.js";
|
|
4
|
+
import { complete } from "@earendil-works/pi-ai/compat";
|
|
5
|
+
//#region src/providers/model-call.ts
|
|
6
|
+
init_constants();
|
|
7
|
+
init_providers();
|
|
8
|
+
function isLocalModelBaseUrl(baseUrl) {
|
|
9
|
+
if (!baseUrl) return false;
|
|
10
|
+
try {
|
|
11
|
+
const host = new URL(baseUrl).hostname.toLowerCase();
|
|
12
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]" || host.startsWith("127.") || host.startsWith("10.") || host.startsWith("192.168.") || /^172\.(1[6-9]|2\d|3[0-1])\./.test(host);
|
|
13
|
+
} catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** Codex Responses rejects the temperature request parameter. */
|
|
18
|
+
function supportsModelTemperature(model) {
|
|
19
|
+
return model.api !== "openai-codex-responses";
|
|
20
|
+
}
|
|
21
|
+
async function resolveModelCallApiKey(model, credentialOptions) {
|
|
22
|
+
try {
|
|
23
|
+
const apiKey = credentialOptions ? await getApiKey(model.provider, credentialOptions) : await getApiKey(model.provider);
|
|
24
|
+
if (apiKey) return apiKey;
|
|
25
|
+
} catch {
|
|
26
|
+
if (!isLocalModelBaseUrl(model.baseUrl)) throw new Error(`Could not load credentials for provider: ${model.provider}`);
|
|
27
|
+
}
|
|
28
|
+
return isLocalModelBaseUrl(model.baseUrl) ? "xopc-local" : void 0;
|
|
29
|
+
}
|
|
30
|
+
async function resolveModelCallOptions(model, options = {}, credentialOptions) {
|
|
31
|
+
const apiKey = options.apiKey ?? await resolveModelCallApiKey(model, credentialOptions);
|
|
32
|
+
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
|
|
33
|
+
const { temperature, ...rest } = options;
|
|
34
|
+
return {
|
|
35
|
+
...rest,
|
|
36
|
+
apiKey,
|
|
37
|
+
...temperature !== void 0 && supportsModelTemperature(model) ? { temperature } : {}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Complete a one-shot request with xopc credential resolution and extension-provider routing.
|
|
42
|
+
*/
|
|
43
|
+
async function completeWithResolvedCredentials(model, context, options = {}, credentialOptions) {
|
|
44
|
+
const resolvedOptions = await resolveModelCallOptions(model, options, credentialOptions);
|
|
45
|
+
if (model.baseUrl === "extension://provider-plugin") return await (await createExtensionAwareStreamFn()(model, context, resolvedOptions)).result();
|
|
46
|
+
return await complete(model, context, resolvedOptions);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Start a model stream with xopc credential resolution and extension-provider routing.
|
|
50
|
+
* Callers that need live visible output should consume the returned stream before calling
|
|
51
|
+
* its `result()` method.
|
|
52
|
+
*/
|
|
53
|
+
async function createResolvedModelStream(model, context, options = {}, credentialOptions) {
|
|
54
|
+
const resolvedOptions = await resolveModelCallOptions(model, options, credentialOptions);
|
|
55
|
+
return await createExtensionAwareStreamFn()(model, context, resolvedOptions);
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { completeWithResolvedCredentials, createResolvedModelStream, isLocalModelBaseUrl, resolveModelCallApiKey, resolveModelCallOptions, supportsModelTemperature };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=model-call.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-call.js","names":[],"sources":["../../../src/providers/model-call.ts"],"sourcesContent":["import {\n complete,\n type Api,\n type AssistantMessage,\n type Context,\n type Model,\n type SimpleStreamOptions,\n} from '@earendil-works/pi-ai/compat';\n\nimport type { CredentialResolverOptions } from '../auth/credentials.js';\nimport { EXTENSION_PROVIDER_BASE_URL } from './constants.js';\nimport { getApiKey } from './index.js';\nimport { createExtensionAwareStreamFn } from './extension-stream-bridge.js';\n\nexport function isLocalModelBaseUrl(baseUrl: string | undefined): boolean {\n if (!baseUrl) return false;\n try {\n const url = new URL(baseUrl);\n const host = url.hostname.toLowerCase();\n return (\n host === 'localhost' ||\n host === '127.0.0.1' ||\n host === '::1' ||\n host === '[::1]' ||\n host.startsWith('127.') ||\n host.startsWith('10.') ||\n host.startsWith('192.168.') ||\n /^172\\.(1[6-9]|2\\d|3[0-1])\\./.test(host)\n );\n } catch {\n return false;\n }\n}\n\n/** Codex Responses rejects the temperature request parameter. */\nexport function supportsModelTemperature(model: Model<Api>): boolean {\n return model.api !== 'openai-codex-responses';\n}\n\nexport async function resolveModelCallApiKey(\n model: Model<Api>,\n credentialOptions?: CredentialResolverOptions,\n): Promise<string | undefined> {\n try {\n const apiKey = credentialOptions\n ? await getApiKey(model.provider, credentialOptions)\n : await getApiKey(model.provider);\n if (apiKey) return apiKey;\n } catch {\n if (!isLocalModelBaseUrl(model.baseUrl)) throw new Error(`Could not load credentials for provider: ${model.provider}`);\n }\n\n return isLocalModelBaseUrl(model.baseUrl) ? 'xopc-local' : undefined;\n}\n\nexport async function resolveModelCallOptions(\n model: Model<Api>,\n options: SimpleStreamOptions = {},\n credentialOptions?: CredentialResolverOptions,\n): Promise<SimpleStreamOptions> {\n const apiKey = options.apiKey ?? await resolveModelCallApiKey(model, credentialOptions);\n if (!apiKey) {\n throw new Error(`No API key for provider: ${model.provider}`);\n }\n\n const { temperature, ...rest } = options;\n return {\n ...rest,\n apiKey,\n ...(temperature !== undefined && supportsModelTemperature(model) ? { temperature } : {}),\n };\n}\n\n/**\n * Complete a one-shot request with xopc credential resolution and extension-provider routing.\n */\nexport async function completeWithResolvedCredentials(\n model: Model<Api>,\n context: Context,\n options: SimpleStreamOptions = {},\n credentialOptions?: CredentialResolverOptions,\n): Promise<AssistantMessage> {\n const resolvedOptions = await resolveModelCallOptions(model, options, credentialOptions);\n if (model.baseUrl === EXTENSION_PROVIDER_BASE_URL) {\n const stream = await createExtensionAwareStreamFn()(model, context, resolvedOptions);\n return await stream.result();\n }\n return await complete(model, context, resolvedOptions as never);\n}\n\n/**\n * Start a model stream with xopc credential resolution and extension-provider routing.\n * Callers that need live visible output should consume the returned stream before calling\n * its `result()` method.\n */\nexport async function createResolvedModelStream(\n model: Model<Api>,\n context: Context,\n options: SimpleStreamOptions = {},\n credentialOptions?: CredentialResolverOptions,\n) {\n const resolvedOptions = await resolveModelCallOptions(model, options, credentialOptions);\n return await createExtensionAwareStreamFn()(model, context, resolvedOptions);\n}\n"],"mappings":";;;;;gBAU6D;gBACtB;AAGvC,SAAgB,oBAAoB,SAAsC;AACxE,KAAI,CAAC,QAAS,QAAO;AACrB,KAAI;EAEF,MAAM,OAAO,IADG,IAAI,QACJ,CAAC,SAAS,aAAa;AACvC,SACE,SAAS,eACT,SAAS,eACT,SAAS,SACT,SAAS,WACT,KAAK,WAAW,OAAO,IACvB,KAAK,WAAW,MAAM,IACtB,KAAK,WAAW,WAAW,IAC3B,8BAA8B,KAAK,KAAK;SAEpC;AACN,SAAO;;;;AAKX,SAAgB,yBAAyB,OAA4B;AACnE,QAAO,MAAM,QAAQ;;AAGvB,eAAsB,uBACpB,OACA,mBAC6B;AAC7B,KAAI;EACF,MAAM,SAAS,oBACX,MAAM,UAAU,MAAM,UAAU,kBAAkB,GAClD,MAAM,UAAU,MAAM,SAAS;AACnC,MAAI,OAAQ,QAAO;SACb;AACN,MAAI,CAAC,oBAAoB,MAAM,QAAQ,CAAE,OAAM,IAAI,MAAM,4CAA4C,MAAM,WAAW;;AAGxH,QAAO,oBAAoB,MAAM,QAAQ,GAAG,eAAe,KAAA;;AAG7D,eAAsB,wBACpB,OACA,UAA+B,EAAE,EACjC,mBAC8B;CAC9B,MAAM,SAAS,QAAQ,UAAU,MAAM,uBAAuB,OAAO,kBAAkB;AACvF,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,4BAA4B,MAAM,WAAW;CAG/D,MAAM,EAAE,aAAa,GAAG,SAAS;AACjC,QAAO;EACL,GAAG;EACH;EACA,GAAI,gBAAgB,KAAA,KAAa,yBAAyB,MAAM,GAAG,EAAE,aAAa,GAAG,EAAE;EACxF;;;;;AAMH,eAAsB,gCACpB,OACA,SACA,UAA+B,EAAE,EACjC,mBAC2B;CAC3B,MAAM,kBAAkB,MAAM,wBAAwB,OAAO,SAAS,kBAAkB;AACxF,KAAI,MAAM,YAAA,8BAER,QAAO,OAAM,MADQ,8BAA8B,CAAC,OAAO,SAAS,gBAAgB,EAChE,QAAQ;AAE9B,QAAO,MAAM,SAAS,OAAO,SAAS,gBAAyB;;;;;;;AAQjE,eAAsB,0BACpB,OACA,SACA,UAA+B,EAAE,EACjC,mBACA;CACA,MAAM,kBAAkB,MAAM,wBAAwB,OAAO,SAAS,kBAAkB;AACxF,QAAO,MAAM,8BAA8B,CAAC,OAAO,SAAS,gBAAgB"}
|
|
@@ -3,10 +3,10 @@ import { init_logger } from "../utils/logger.js";
|
|
|
3
3
|
import { isCronSessionKey } from "../routing/session-key-utils.js";
|
|
4
4
|
import { init_session_key, parseSessionKey } from "../routing/session-key.js";
|
|
5
5
|
import { init_providers, resolveModel } from "../providers/index.js";
|
|
6
|
+
import { completeWithResolvedCredentials } from "../providers/model-call.js";
|
|
6
7
|
import { readAgentMessageContent } from "../agent/memory/agent-message-access.js";
|
|
7
8
|
import { stripSessionStartupContextFromUserText } from "../agent/reply/startup-context.js";
|
|
8
9
|
import { stripEnvelopeTimestampPrefix } from "../channels/envelope-timestamp.js";
|
|
9
|
-
import { complete } from "@earendil-works/pi-ai/compat";
|
|
10
10
|
//#region src/session/session-title.ts
|
|
11
11
|
init_session_key();
|
|
12
12
|
init_providers();
|
|
@@ -150,7 +150,7 @@ Title:`,
|
|
|
150
150
|
timestamp: Date.now()
|
|
151
151
|
};
|
|
152
152
|
try {
|
|
153
|
-
const result = await
|
|
153
|
+
const result = await completeWithResolvedCredentials(model, { messages: [userMsg] }, {
|
|
154
154
|
maxTokens: 64,
|
|
155
155
|
temperature: .35,
|
|
156
156
|
signal
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-title.js","names":[],"sources":["../../../src/session/session-title.ts"],"sourcesContent":["/**\n * LLM-generated session titles (webchat and any path using SessionStore).\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport { complete, type UserMessage } from '@earendil-works/pi-ai/compat';\n\nimport { stripSessionStartupContextFromUserText } from '../agent/reply/startup-context.js';\nimport { stripEnvelopeTimestampPrefix } from '../channels/envelope-timestamp.js';\nimport { isCronSessionKey, parseSessionKey } from '../routing/session-key.js';\nimport { resolveModel } from '../providers/index.js';\nimport { createLogger } from '../utils/logger.js';\nimport { readAgentMessageContent } from '../agent/memory/agent-message-access.js';\nimport type { SessionStore } from './store.js';\n\nconst log = createLogger('SessionAutoTitle');\n\nconst MAX_TITLE_LEN = 80;\n\n/** Collect visible text from any content block that exposes `text` (pi-ai / OpenAI / Anthropic shapes). */\nfunction extractTextFromMessage(m: AgentMessage): string {\n const raw = readAgentMessageContent(m);\n if (typeof raw === 'string') return raw.trim();\n if (Array.isArray(raw)) {\n const parts: string[] = [];\n for (const c of raw) {\n if (c && typeof c === 'object') {\n const o = c as unknown as Record<string, unknown>;\n const type = typeof o.type === 'string' ? o.type : '';\n if (type === 'toolCall' || type === 'tool_use' || type === 'tool_result') continue;\n if (typeof o.text === 'string' && o.text.trim()) {\n parts.push(o.text.trim());\n }\n }\n }\n return parts.join(' ').trim();\n }\n return '';\n}\n\nfunction firstUserText(messages: AgentMessage[]): string {\n const u = messages.find((m) => m.role === 'user');\n if (!u) return '';\n const raw = extractTextFromMessage(u);\n return normalizeUserTextForTitle(raw);\n}\n\nfunction stripMediaClaimChecks(text: string): string {\n if (!text.includes('[media attached:') && !text.includes('xopc-media-uri:')) {\n return text;\n }\n return text\n .replace(\n /\\s*\\[media attached:[^\\]]+\\]\\s*\\r?\\nxopc-media-uri:[^\\r\\n]+\\r?\\n\\s*xopc-media-path:[^\\r\\n]+(?:\\r?\\n\\s*Use the read_media tool[^\\r\\n]*)?/g,\n '',\n )\n .replace(/\\s*\\[media attached:[^\\]]+\\]\\s*/g, ' ')\n .replace(/\\s*xopc-media-uri:[^\\r\\n]+/g, '')\n .replace(/\\s*xopc-media-path:[^\\r\\n]+/g, '')\n .replace(/\\s*Use the read_media tool[^\\r\\n]*/g, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n\nfunction stripExpandedSkillBlockForTitle(text: string): string {\n const trimmed = text.trimStart();\n if (!/^##\\s+Skill:\\s*\\S+/i.test(trimmed)) {\n return text;\n }\n\n const markerRe = /(?:^|\\r?\\n)\\*\\*Arguments\\*\\*:\\s*/gi;\n let argsStart = -1;\n while (markerRe.exec(trimmed) !== null) {\n argsStart = markerRe.lastIndex;\n }\n\n if (argsStart !== -1) {\n const args = trimmed.slice(argsStart).trim();\n if (args) return args;\n }\n\n const firstLine = trimmed.split(/\\r?\\n/)[0]?.trim() ?? '';\n return firstLine.replace(/^##\\s+Skill:\\s*/i, '').trim();\n}\n\nfunction stripSkillCommandForTitle(text: string): string {\n const trimmed = text.trimStart();\n const match = trimmed.match(/^\\/skill:([^\\s]+)(?:\\s+([\\s\\S]*))?$/i);\n if (!match) {\n return text;\n }\n const args = match[2]?.trim();\n return args || match[1]?.trim() || text;\n}\n\nfunction normalizeUserTextForTitle(raw: string): string {\n return stripSkillCommandForTitle(\n stripExpandedSkillBlockForTitle(\n stripMediaClaimChecks(\n stripEnvelopeTimestampPrefix(stripSessionStartupContextFromUserText(raw)),\n ),\n ),\n ).trim();\n}\n\n/** First assistant message that has visible text (skips tool-only assistant rows). */\nfunction firstAssistantText(messages: AgentMessage[]): string {\n for (const m of messages) {\n if (m.role === 'assistant') {\n const t = extractTextFromMessage(m);\n if (t.length > 0) return t;\n }\n }\n return '';\n}\n\nexport function isWebchatSessionKey(sessionKey: string): boolean {\n const p = parseSessionKey(sessionKey);\n if (p?.source === 'webchat') return true;\n return sessionKey.includes(':webchat:');\n}\n\n/** Whether to run LLM/fallback session naming for this key (excludes cron, heartbeat). */\nexport function shouldAutoTitleSessionKey(sessionKey: string): boolean {\n const raw = (sessionKey ?? '').trim();\n if (!raw) return false;\n if (isCronSessionKey(raw)) return false;\n if (raw.toLowerCase().startsWith('heartbeat:')) return false;\n return true;\n}\n\nexport function sanitizeSessionTitle(raw: string): string {\n let s = raw.trim();\n if ((s.startsWith('\"') && s.endsWith('\"')) || (s.startsWith(\"'\") && s.endsWith(\"'\"))) {\n s = s.slice(1, -1).trim();\n }\n const lineBreak = s.indexOf('\\n');\n if (lineBreak !== -1) s = s.slice(0, lineBreak).trim();\n if (s.length > MAX_TITLE_LEN) s = s.slice(0, MAX_TITLE_LEN - 1).trimEnd() + '…';\n return s;\n}\n\nexport type SessionTitleSource = 'provisional' | 'llm' | 'user';\n\nexport function getSessionTitleSource(\n meta: { customData?: Record<string, unknown> } | null | undefined,\n): SessionTitleSource | null {\n const raw = meta?.customData?.titleSource;\n if (raw === 'provisional' || raw === 'llm' || raw === 'user') return raw;\n return null;\n}\n\n/** Title from a single user message (first line), for immediate sidebar labels. */\nexport function provisionalTitleFromUserText(raw: string): string | null {\n const text = normalizeUserTextForTitle((raw ?? '').trim());\n if (!text) return null;\n const line = text.split(/\\n/)[0]?.trim();\n if (!line) return null;\n return sanitizeSessionTitle(line);\n}\n\n/** Non-LLM title: first line of first user text, else first assistant line. */\nexport function fallbackTitleFromMessages(messages: AgentMessage[]): string | null {\n const u = firstUserText(messages);\n if (u) {\n const line = u.split(/\\n/)[0]?.trim();\n if (line) return sanitizeSessionTitle(line);\n }\n const a = firstAssistantText(messages);\n if (a) {\n const line = a.split(/\\n/)[0]?.trim();\n if (line) return sanitizeSessionTitle(line);\n }\n return null;\n}\n\n/**\n * Returns a title string, or null if generation should be skipped or failed.\n */\nexport async function generateSessionTitleFromMessages(\n modelRef: string,\n messages: AgentMessage[],\n signal?: AbortSignal,\n): Promise<string | null> {\n const userText = firstUserText(messages);\n const assistantText = firstAssistantText(messages);\n if (!userText && !assistantText) return null;\n\n let model: ReturnType<typeof resolveModel>;\n try {\n model = resolveModel(modelRef);\n } catch (err) {\n log.warn({ err, modelRef }, 'Cannot resolve model for session title');\n return null;\n }\n\n const prompt =\n userText && assistantText\n ? `You label chat sessions. Given the first user message and the start of the assistant reply, output ONE short title (max 8 words). No quotes. No punctuation at the end. Use the same language as the user when possible.\n\nUser: ${userText.slice(0, 2000)}\n\nAssistant: ${assistantText.slice(0, 2000)}\n\nTitle:`\n : userText\n ? `The assistant reply only used tools (no visible text yet). Output ONE short title (max 8 words) based only on the user's first message. No quotes. No punctuation at the end. Use the same language as the user.\n\nUser: ${userText.slice(0, 2000)}\n\nTitle:`\n : `Output ONE short title (max 8 words) for this assistant reply. No quotes. No punctuation at the end.\n\nAssistant: ${assistantText!.slice(0, 2000)}\n\nTitle:`;\n\n const userMsg: UserMessage = { role: 'user', content: prompt, timestamp: Date.now() };\n\n try {\n const result = await complete(\n model,\n { messages: [userMsg] },\n {\n maxTokens: 64,\n temperature: 0.35,\n signal: signal as AbortSignal,\n },\n );\n\n let text = '';\n if (Array.isArray(result.content)) {\n for (const c of result.content) {\n if (c && typeof c === 'object' && (c as { type?: string }).type === 'text') {\n text += String((c as { text?: string }).text || '');\n }\n }\n }\n\n const cleaned = sanitizeSessionTitle(text);\n return cleaned.length > 0 ? cleaned : null;\n } catch (err) {\n log.warn({ err }, 'Session title LLM call failed');\n return null;\n }\n}\n\nexport type SessionTitleUpdatedHook = (sessionKey: string, name: string) => void | Promise<void>;\n\n/** Whether post-turn LLM refine may run (provisional or still unnamed; not user-locked). */\nexport function shouldRefineSessionTitleWithLlm(\n meta: { name?: string; customData?: Record<string, unknown> } | null | undefined,\n): boolean {\n if (!meta) return false;\n const source = getSessionTitleSource(meta);\n if (source === 'user') return false;\n if (meta.name?.trim()) return source === 'provisional';\n return true;\n}\n\nfunction canAutoWriteTitle(meta: { name?: string; customData?: Record<string, unknown> } | null): boolean {\n if (!meta) return false;\n const source = getSessionTitleSource(meta);\n if (source === 'user') return false;\n return !meta.name?.trim();\n}\n\n/**\n * Set provisional title from first user text when session is still unnamed.\n * Skips cron/heartbeat keys and user-locked titles.\n */\nexport async function maybeSetProvisionalSessionTitle(\n sessionStore: SessionStore,\n sessionKey: string,\n userText?: string,\n onUpdated?: SessionTitleUpdatedHook,\n): Promise<void> {\n if (!shouldAutoTitleSessionKey(sessionKey)) return;\n\n let meta = await sessionStore.getMetadata(sessionKey);\n if (!meta) return;\n if (!canAutoWriteTitle(meta)) return;\n\n let title: string | null = null;\n if (userText?.trim()) {\n title = provisionalTitleFromUserText(userText);\n }\n if (!title) {\n const messages = await sessionStore.load(sessionKey);\n if (!messages.length) return;\n title = fallbackTitleFromMessages(messages);\n }\n if (!title) return;\n\n try {\n await sessionStore.updateMetadata(sessionKey, {\n name: title,\n customData: { ...(meta.customData ?? {}), titleSource: 'provisional' },\n });\n await onUpdated?.(sessionKey, title);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Session title: provisional updateMetadata failed');\n }\n}\n\n/**\n * LLM refine when title is empty or still provisional (not user-locked).\n */\nexport async function maybeRefineSessionTitleWithLlm(\n sessionStore: SessionStore,\n sessionKey: string,\n modelRef: string | undefined,\n onUpdated?: SessionTitleUpdatedHook,\n): Promise<void> {\n if (!shouldAutoTitleSessionKey(sessionKey)) return;\n\n let messages = await sessionStore.load(sessionKey);\n if (!messages.length) return;\n\n let meta = await sessionStore.getMetadata(sessionKey);\n if (!meta) {\n await sessionStore.saveMessages(sessionKey, messages);\n meta = await sessionStore.getMetadata(sessionKey);\n }\n if (!meta) {\n log.warn({ sessionKey }, 'Session title: metadata missing after save');\n return;\n }\n if (!shouldRefineSessionTitleWithLlm(meta)) return;\n\n const source = getSessionTitleSource(meta);\n let title: string | null = null;\n const ref = modelRef?.trim();\n if (ref) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 25_000);\n try {\n title = await generateSessionTitleFromMessages(ref, messages, controller.signal);\n } finally {\n clearTimeout(timeout);\n }\n }\n if (!title) {\n title = fallbackTitleFromMessages(messages);\n }\n if (!title) return;\n\n const existing = meta.name?.trim();\n if (existing === title) {\n if (source !== 'llm') {\n try {\n await sessionStore.updateMetadata(sessionKey, {\n customData: { ...(meta.customData ?? {}), titleSource: 'llm' },\n });\n } catch {\n /* ignore */\n }\n }\n return;\n }\n\n try {\n await sessionStore.updateMetadata(sessionKey, {\n name: title,\n customData: { ...(meta.customData ?? {}), titleSource: 'llm' },\n });\n await onUpdated?.(sessionKey, title);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Session title: refine updateMetadata failed');\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session-title.js","names":[],"sources":["../../../src/session/session-title.ts"],"sourcesContent":["/**\n * LLM-generated session titles (webchat and any path using SessionStore).\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport { type UserMessage } from '@earendil-works/pi-ai/compat';\n\nimport { stripSessionStartupContextFromUserText } from '../agent/reply/startup-context.js';\nimport { stripEnvelopeTimestampPrefix } from '../channels/envelope-timestamp.js';\nimport { isCronSessionKey, parseSessionKey } from '../routing/session-key.js';\nimport { resolveModel } from '../providers/index.js';\nimport { completeWithResolvedCredentials } from '../providers/model-call.js';\nimport { createLogger } from '../utils/logger.js';\nimport { readAgentMessageContent } from '../agent/memory/agent-message-access.js';\nimport type { SessionStore } from './store.js';\n\nconst log = createLogger('SessionAutoTitle');\n\nconst MAX_TITLE_LEN = 80;\n\n/** Collect visible text from any content block that exposes `text` (pi-ai / OpenAI / Anthropic shapes). */\nfunction extractTextFromMessage(m: AgentMessage): string {\n const raw = readAgentMessageContent(m);\n if (typeof raw === 'string') return raw.trim();\n if (Array.isArray(raw)) {\n const parts: string[] = [];\n for (const c of raw) {\n if (c && typeof c === 'object') {\n const o = c as unknown as Record<string, unknown>;\n const type = typeof o.type === 'string' ? o.type : '';\n if (type === 'toolCall' || type === 'tool_use' || type === 'tool_result') continue;\n if (typeof o.text === 'string' && o.text.trim()) {\n parts.push(o.text.trim());\n }\n }\n }\n return parts.join(' ').trim();\n }\n return '';\n}\n\nfunction firstUserText(messages: AgentMessage[]): string {\n const u = messages.find((m) => m.role === 'user');\n if (!u) return '';\n const raw = extractTextFromMessage(u);\n return normalizeUserTextForTitle(raw);\n}\n\nfunction stripMediaClaimChecks(text: string): string {\n if (!text.includes('[media attached:') && !text.includes('xopc-media-uri:')) {\n return text;\n }\n return text\n .replace(\n /\\s*\\[media attached:[^\\]]+\\]\\s*\\r?\\nxopc-media-uri:[^\\r\\n]+\\r?\\n\\s*xopc-media-path:[^\\r\\n]+(?:\\r?\\n\\s*Use the read_media tool[^\\r\\n]*)?/g,\n '',\n )\n .replace(/\\s*\\[media attached:[^\\]]+\\]\\s*/g, ' ')\n .replace(/\\s*xopc-media-uri:[^\\r\\n]+/g, '')\n .replace(/\\s*xopc-media-path:[^\\r\\n]+/g, '')\n .replace(/\\s*Use the read_media tool[^\\r\\n]*/g, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n\nfunction stripExpandedSkillBlockForTitle(text: string): string {\n const trimmed = text.trimStart();\n if (!/^##\\s+Skill:\\s*\\S+/i.test(trimmed)) {\n return text;\n }\n\n const markerRe = /(?:^|\\r?\\n)\\*\\*Arguments\\*\\*:\\s*/gi;\n let argsStart = -1;\n while (markerRe.exec(trimmed) !== null) {\n argsStart = markerRe.lastIndex;\n }\n\n if (argsStart !== -1) {\n const args = trimmed.slice(argsStart).trim();\n if (args) return args;\n }\n\n const firstLine = trimmed.split(/\\r?\\n/)[0]?.trim() ?? '';\n return firstLine.replace(/^##\\s+Skill:\\s*/i, '').trim();\n}\n\nfunction stripSkillCommandForTitle(text: string): string {\n const trimmed = text.trimStart();\n const match = trimmed.match(/^\\/skill:([^\\s]+)(?:\\s+([\\s\\S]*))?$/i);\n if (!match) {\n return text;\n }\n const args = match[2]?.trim();\n return args || match[1]?.trim() || text;\n}\n\nfunction normalizeUserTextForTitle(raw: string): string {\n return stripSkillCommandForTitle(\n stripExpandedSkillBlockForTitle(\n stripMediaClaimChecks(\n stripEnvelopeTimestampPrefix(stripSessionStartupContextFromUserText(raw)),\n ),\n ),\n ).trim();\n}\n\n/** First assistant message that has visible text (skips tool-only assistant rows). */\nfunction firstAssistantText(messages: AgentMessage[]): string {\n for (const m of messages) {\n if (m.role === 'assistant') {\n const t = extractTextFromMessage(m);\n if (t.length > 0) return t;\n }\n }\n return '';\n}\n\nexport function isWebchatSessionKey(sessionKey: string): boolean {\n const p = parseSessionKey(sessionKey);\n if (p?.source === 'webchat') return true;\n return sessionKey.includes(':webchat:');\n}\n\n/** Whether to run LLM/fallback session naming for this key (excludes cron, heartbeat). */\nexport function shouldAutoTitleSessionKey(sessionKey: string): boolean {\n const raw = (sessionKey ?? '').trim();\n if (!raw) return false;\n if (isCronSessionKey(raw)) return false;\n if (raw.toLowerCase().startsWith('heartbeat:')) return false;\n return true;\n}\n\nexport function sanitizeSessionTitle(raw: string): string {\n let s = raw.trim();\n if ((s.startsWith('\"') && s.endsWith('\"')) || (s.startsWith(\"'\") && s.endsWith(\"'\"))) {\n s = s.slice(1, -1).trim();\n }\n const lineBreak = s.indexOf('\\n');\n if (lineBreak !== -1) s = s.slice(0, lineBreak).trim();\n if (s.length > MAX_TITLE_LEN) s = s.slice(0, MAX_TITLE_LEN - 1).trimEnd() + '…';\n return s;\n}\n\nexport type SessionTitleSource = 'provisional' | 'llm' | 'user';\n\nexport function getSessionTitleSource(\n meta: { customData?: Record<string, unknown> } | null | undefined,\n): SessionTitleSource | null {\n const raw = meta?.customData?.titleSource;\n if (raw === 'provisional' || raw === 'llm' || raw === 'user') return raw;\n return null;\n}\n\n/** Title from a single user message (first line), for immediate sidebar labels. */\nexport function provisionalTitleFromUserText(raw: string): string | null {\n const text = normalizeUserTextForTitle((raw ?? '').trim());\n if (!text) return null;\n const line = text.split(/\\n/)[0]?.trim();\n if (!line) return null;\n return sanitizeSessionTitle(line);\n}\n\n/** Non-LLM title: first line of first user text, else first assistant line. */\nexport function fallbackTitleFromMessages(messages: AgentMessage[]): string | null {\n const u = firstUserText(messages);\n if (u) {\n const line = u.split(/\\n/)[0]?.trim();\n if (line) return sanitizeSessionTitle(line);\n }\n const a = firstAssistantText(messages);\n if (a) {\n const line = a.split(/\\n/)[0]?.trim();\n if (line) return sanitizeSessionTitle(line);\n }\n return null;\n}\n\n/**\n * Returns a title string, or null if generation should be skipped or failed.\n */\nexport async function generateSessionTitleFromMessages(\n modelRef: string,\n messages: AgentMessage[],\n signal?: AbortSignal,\n): Promise<string | null> {\n const userText = firstUserText(messages);\n const assistantText = firstAssistantText(messages);\n if (!userText && !assistantText) return null;\n\n let model: ReturnType<typeof resolveModel>;\n try {\n model = resolveModel(modelRef);\n } catch (err) {\n log.warn({ err, modelRef }, 'Cannot resolve model for session title');\n return null;\n }\n\n const prompt =\n userText && assistantText\n ? `You label chat sessions. Given the first user message and the start of the assistant reply, output ONE short title (max 8 words). No quotes. No punctuation at the end. Use the same language as the user when possible.\n\nUser: ${userText.slice(0, 2000)}\n\nAssistant: ${assistantText.slice(0, 2000)}\n\nTitle:`\n : userText\n ? `The assistant reply only used tools (no visible text yet). Output ONE short title (max 8 words) based only on the user's first message. No quotes. No punctuation at the end. Use the same language as the user.\n\nUser: ${userText.slice(0, 2000)}\n\nTitle:`\n : `Output ONE short title (max 8 words) for this assistant reply. No quotes. No punctuation at the end.\n\nAssistant: ${assistantText!.slice(0, 2000)}\n\nTitle:`;\n\n const userMsg: UserMessage = { role: 'user', content: prompt, timestamp: Date.now() };\n\n try {\n const result = await completeWithResolvedCredentials(\n model,\n { messages: [userMsg] },\n {\n maxTokens: 64,\n temperature: 0.35,\n signal: signal as AbortSignal,\n },\n );\n\n let text = '';\n if (Array.isArray(result.content)) {\n for (const c of result.content) {\n if (c && typeof c === 'object' && (c as { type?: string }).type === 'text') {\n text += String((c as { text?: string }).text || '');\n }\n }\n }\n\n const cleaned = sanitizeSessionTitle(text);\n return cleaned.length > 0 ? cleaned : null;\n } catch (err) {\n log.warn({ err }, 'Session title LLM call failed');\n return null;\n }\n}\n\nexport type SessionTitleUpdatedHook = (sessionKey: string, name: string) => void | Promise<void>;\n\n/** Whether post-turn LLM refine may run (provisional or still unnamed; not user-locked). */\nexport function shouldRefineSessionTitleWithLlm(\n meta: { name?: string; customData?: Record<string, unknown> } | null | undefined,\n): boolean {\n if (!meta) return false;\n const source = getSessionTitleSource(meta);\n if (source === 'user') return false;\n if (meta.name?.trim()) return source === 'provisional';\n return true;\n}\n\nfunction canAutoWriteTitle(meta: { name?: string; customData?: Record<string, unknown> } | null): boolean {\n if (!meta) return false;\n const source = getSessionTitleSource(meta);\n if (source === 'user') return false;\n return !meta.name?.trim();\n}\n\n/**\n * Set provisional title from first user text when session is still unnamed.\n * Skips cron/heartbeat keys and user-locked titles.\n */\nexport async function maybeSetProvisionalSessionTitle(\n sessionStore: SessionStore,\n sessionKey: string,\n userText?: string,\n onUpdated?: SessionTitleUpdatedHook,\n): Promise<void> {\n if (!shouldAutoTitleSessionKey(sessionKey)) return;\n\n let meta = await sessionStore.getMetadata(sessionKey);\n if (!meta) return;\n if (!canAutoWriteTitle(meta)) return;\n\n let title: string | null = null;\n if (userText?.trim()) {\n title = provisionalTitleFromUserText(userText);\n }\n if (!title) {\n const messages = await sessionStore.load(sessionKey);\n if (!messages.length) return;\n title = fallbackTitleFromMessages(messages);\n }\n if (!title) return;\n\n try {\n await sessionStore.updateMetadata(sessionKey, {\n name: title,\n customData: { ...(meta.customData ?? {}), titleSource: 'provisional' },\n });\n await onUpdated?.(sessionKey, title);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Session title: provisional updateMetadata failed');\n }\n}\n\n/**\n * LLM refine when title is empty or still provisional (not user-locked).\n */\nexport async function maybeRefineSessionTitleWithLlm(\n sessionStore: SessionStore,\n sessionKey: string,\n modelRef: string | undefined,\n onUpdated?: SessionTitleUpdatedHook,\n): Promise<void> {\n if (!shouldAutoTitleSessionKey(sessionKey)) return;\n\n let messages = await sessionStore.load(sessionKey);\n if (!messages.length) return;\n\n let meta = await sessionStore.getMetadata(sessionKey);\n if (!meta) {\n await sessionStore.saveMessages(sessionKey, messages);\n meta = await sessionStore.getMetadata(sessionKey);\n }\n if (!meta) {\n log.warn({ sessionKey }, 'Session title: metadata missing after save');\n return;\n }\n if (!shouldRefineSessionTitleWithLlm(meta)) return;\n\n const source = getSessionTitleSource(meta);\n let title: string | null = null;\n const ref = modelRef?.trim();\n if (ref) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 25_000);\n try {\n title = await generateSessionTitleFromMessages(ref, messages, controller.signal);\n } finally {\n clearTimeout(timeout);\n }\n }\n if (!title) {\n title = fallbackTitleFromMessages(messages);\n }\n if (!title) return;\n\n const existing = meta.name?.trim();\n if (existing === title) {\n if (source !== 'llm') {\n try {\n await sessionStore.updateMetadata(sessionKey, {\n customData: { ...(meta.customData ?? {}), titleSource: 'llm' },\n });\n } catch {\n /* ignore */\n }\n }\n return;\n }\n\n try {\n await sessionStore.updateMetadata(sessionKey, {\n name: title,\n customData: { ...(meta.customData ?? {}), titleSource: 'llm' },\n });\n await onUpdated?.(sessionKey, title);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Session title: refine updateMetadata failed');\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
/** Quote a user query for FTS5 phrase/prefix-safe matching. */
|
|
2
2
|
export declare function escapeFts5Query(raw: string): string;
|
|
3
|
+
/** Build a syntax-safe, recall-oriented query for natural-language memory search. */
|
|
4
|
+
export declare function buildFts5SearchQuery(raw: string): string;
|
|
5
|
+
/** Normalize FTS5 BM25 ranks, where a more-negative rank is a better match. */
|
|
6
|
+
export declare function fts5RankToScore(rank: number, bestRank: number, worstRank: number): number;
|
|
7
|
+
/** Bounded lexical fallback for reformulations unsupported by unicode61 tokenization. */
|
|
8
|
+
export declare function memoryLexicalSimilarity(query: string, content: string): number;
|
|
@@ -5,7 +5,76 @@ function escapeFts5Query(raw) {
|
|
|
5
5
|
if (!trimmed) return "";
|
|
6
6
|
return `"${trimmed.replace(/"/g, "\"\"")}"`;
|
|
7
7
|
}
|
|
8
|
+
const FTS_SEARCH_STOP_WORDS = new Set([
|
|
9
|
+
"a",
|
|
10
|
+
"an",
|
|
11
|
+
"and",
|
|
12
|
+
"are",
|
|
13
|
+
"can",
|
|
14
|
+
"did",
|
|
15
|
+
"do",
|
|
16
|
+
"does",
|
|
17
|
+
"for",
|
|
18
|
+
"how",
|
|
19
|
+
"i",
|
|
20
|
+
"in",
|
|
21
|
+
"is",
|
|
22
|
+
"it",
|
|
23
|
+
"me",
|
|
24
|
+
"my",
|
|
25
|
+
"of",
|
|
26
|
+
"on",
|
|
27
|
+
"or",
|
|
28
|
+
"the",
|
|
29
|
+
"to",
|
|
30
|
+
"was",
|
|
31
|
+
"what",
|
|
32
|
+
"when",
|
|
33
|
+
"where",
|
|
34
|
+
"which",
|
|
35
|
+
"who",
|
|
36
|
+
"why",
|
|
37
|
+
"with",
|
|
38
|
+
"you",
|
|
39
|
+
"your"
|
|
40
|
+
]);
|
|
41
|
+
/** Build a syntax-safe, recall-oriented query for natural-language memory search. */
|
|
42
|
+
function buildFts5SearchQuery(raw) {
|
|
43
|
+
const terms = raw.normalize("NFKC").match(/[\p{L}\p{N}_]+/gu)?.map((term) => term.toLocaleLowerCase()).filter((term) => term.length > 1 && !FTS_SEARCH_STOP_WORDS.has(term));
|
|
44
|
+
if (!terms?.length) return escapeFts5Query(raw);
|
|
45
|
+
return [...new Set(terms)].slice(0, 12).map((term) => escapeFts5Query(term)).join(" OR ");
|
|
46
|
+
}
|
|
47
|
+
/** Normalize FTS5 BM25 ranks, where a more-negative rank is a better match. */
|
|
48
|
+
function fts5RankToScore(rank, bestRank, worstRank) {
|
|
49
|
+
if (![
|
|
50
|
+
rank,
|
|
51
|
+
bestRank,
|
|
52
|
+
worstRank
|
|
53
|
+
].every(Number.isFinite)) return 0;
|
|
54
|
+
if (bestRank === worstRank) return .75;
|
|
55
|
+
const normalized = (worstRank - rank) / (worstRank - bestRank);
|
|
56
|
+
return .5 + .5 * Math.max(0, Math.min(1, normalized));
|
|
57
|
+
}
|
|
58
|
+
function lexicalFeatures(raw) {
|
|
59
|
+
const normalized = raw.normalize("NFKC").toLocaleLowerCase();
|
|
60
|
+
const features = /* @__PURE__ */ new Set();
|
|
61
|
+
for (const term of normalized.match(/[\p{L}\p{N}_]+/gu) ?? []) if (term.length > 1 && !FTS_SEARCH_STOP_WORDS.has(term)) features.add(`word:${term}`);
|
|
62
|
+
for (const sequence of normalized.match(/\p{Script=Han}+/gu) ?? []) {
|
|
63
|
+
const chars = [...sequence];
|
|
64
|
+
for (let index = 0; index < chars.length - 1; index += 1) features.add(`han:${chars[index]}${chars[index + 1]}`);
|
|
65
|
+
}
|
|
66
|
+
return features;
|
|
67
|
+
}
|
|
68
|
+
/** Bounded lexical fallback for reformulations unsupported by unicode61 tokenization. */
|
|
69
|
+
function memoryLexicalSimilarity(query, content) {
|
|
70
|
+
const queryFeatures = lexicalFeatures(query);
|
|
71
|
+
const contentFeatures = lexicalFeatures(content);
|
|
72
|
+
if (queryFeatures.size === 0 || contentFeatures.size === 0) return 0;
|
|
73
|
+
let shared = 0;
|
|
74
|
+
for (const feature of queryFeatures) if (contentFeatures.has(feature)) shared += 1;
|
|
75
|
+
return 2 * shared / (queryFeatures.size + contentFeatures.size);
|
|
76
|
+
}
|
|
8
77
|
//#endregion
|
|
9
|
-
export { escapeFts5Query };
|
|
78
|
+
export { buildFts5SearchQuery, escapeFts5Query, fts5RankToScore, memoryLexicalSimilarity };
|
|
10
79
|
|
|
11
80
|
//# sourceMappingURL=fts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fts.js","names":[],"sources":["../../../../src/storage/sqlite/fts.ts"],"sourcesContent":["/** Quote a user query for FTS5 phrase/prefix-safe matching. */\nexport function escapeFts5Query(raw: string): string {\n const trimmed = raw.trim();\n if (!trimmed) {\n return '';\n }\n return `\"${trimmed.replace(/\"/g, '\"\"')}\"`;\n}\n"],"mappings":";;AACA,SAAgB,gBAAgB,KAAqB;CACnD,MAAM,UAAU,IAAI,MAAM;AAC1B,KAAI,CAAC,QACH,QAAO;AAET,QAAO,IAAI,QAAQ,QAAQ,MAAM,OAAK,CAAC"}
|
|
1
|
+
{"version":3,"file":"fts.js","names":[],"sources":["../../../../src/storage/sqlite/fts.ts"],"sourcesContent":["/** Quote a user query for FTS5 phrase/prefix-safe matching. */\nexport function escapeFts5Query(raw: string): string {\n const trimmed = raw.trim();\n if (!trimmed) {\n return '';\n }\n return `\"${trimmed.replace(/\"/g, '\"\"')}\"`;\n}\n\nconst FTS_SEARCH_STOP_WORDS = new Set([\n 'a', 'an', 'and', 'are', 'can', 'did', 'do', 'does', 'for', 'how', 'i', 'in', 'is',\n 'it', 'me', 'my', 'of', 'on', 'or', 'the', 'to', 'was', 'what', 'when', 'where',\n 'which', 'who', 'why', 'with', 'you', 'your',\n]);\n\n/** Build a syntax-safe, recall-oriented query for natural-language memory search. */\nexport function buildFts5SearchQuery(raw: string): string {\n const terms = raw\n .normalize('NFKC')\n .match(/[\\p{L}\\p{N}_]+/gu)\n ?.map((term) => term.toLocaleLowerCase())\n .filter((term) => term.length > 1 && !FTS_SEARCH_STOP_WORDS.has(term));\n if (!terms?.length) return escapeFts5Query(raw);\n return [...new Set(terms)]\n .slice(0, 12)\n .map((term) => escapeFts5Query(term))\n .join(' OR ');\n}\n\n/** Normalize FTS5 BM25 ranks, where a more-negative rank is a better match. */\nexport function fts5RankToScore(rank: number, bestRank: number, worstRank: number): number {\n if (![rank, bestRank, worstRank].every(Number.isFinite)) return 0;\n if (bestRank === worstRank) return 0.75;\n const normalized = (worstRank - rank) / (worstRank - bestRank);\n return 0.5 + 0.5 * Math.max(0, Math.min(1, normalized));\n}\n\nfunction lexicalFeatures(raw: string): Set<string> {\n const normalized = raw.normalize('NFKC').toLocaleLowerCase();\n const features = new Set<string>();\n for (const term of normalized.match(/[\\p{L}\\p{N}_]+/gu) ?? []) {\n if (term.length > 1 && !FTS_SEARCH_STOP_WORDS.has(term)) features.add(`word:${term}`);\n }\n for (const sequence of normalized.match(/\\p{Script=Han}+/gu) ?? []) {\n const chars = [...sequence];\n for (let index = 0; index < chars.length - 1; index += 1) {\n features.add(`han:${chars[index]}${chars[index + 1]}`);\n }\n }\n return features;\n}\n\n/** Bounded lexical fallback for reformulations unsupported by unicode61 tokenization. */\nexport function memoryLexicalSimilarity(query: string, content: string): number {\n const queryFeatures = lexicalFeatures(query);\n const contentFeatures = lexicalFeatures(content);\n if (queryFeatures.size === 0 || contentFeatures.size === 0) return 0;\n let shared = 0;\n for (const feature of queryFeatures) {\n if (contentFeatures.has(feature)) shared += 1;\n }\n return (2 * shared) / (queryFeatures.size + contentFeatures.size);\n}\n"],"mappings":";;AACA,SAAgB,gBAAgB,KAAqB;CACnD,MAAM,UAAU,IAAI,MAAM;AAC1B,KAAI,CAAC,QACH,QAAO;AAET,QAAO,IAAI,QAAQ,QAAQ,MAAM,OAAK,CAAC;;AAGzC,MAAM,wBAAwB,IAAI,IAAI;CACpC;CAAK;CAAM;CAAO;CAAO;CAAO;CAAO;CAAM;CAAQ;CAAO;CAAO;CAAK;CAAM;CAC9E;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAO;CAAM;CAAO;CAAQ;CAAQ;CACxE;CAAS;CAAO;CAAO;CAAQ;CAAO;CACvC,CAAC;;AAGF,SAAgB,qBAAqB,KAAqB;CACxD,MAAM,QAAQ,IACX,UAAU,OAAO,CACjB,MAAM,mBAAmB,EACxB,KAAK,SAAS,KAAK,mBAAmB,CAAC,CACxC,QAAQ,SAAS,KAAK,SAAS,KAAK,CAAC,sBAAsB,IAAI,KAAK,CAAC;AACxE,KAAI,CAAC,OAAO,OAAQ,QAAO,gBAAgB,IAAI;AAC/C,QAAO,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC,CACvB,MAAM,GAAG,GAAG,CACZ,KAAK,SAAS,gBAAgB,KAAK,CAAC,CACpC,KAAK,OAAO;;;AAIjB,SAAgB,gBAAgB,MAAc,UAAkB,WAA2B;AACzF,KAAI,CAAC;EAAC;EAAM;EAAU;EAAU,CAAC,MAAM,OAAO,SAAS,CAAE,QAAO;AAChE,KAAI,aAAa,UAAW,QAAO;CACnC,MAAM,cAAc,YAAY,SAAS,YAAY;AACrD,QAAO,KAAM,KAAM,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,WAAW,CAAC;;AAGzD,SAAS,gBAAgB,KAA0B;CACjD,MAAM,aAAa,IAAI,UAAU,OAAO,CAAC,mBAAmB;CAC5D,MAAM,2BAAW,IAAI,KAAa;AAClC,MAAK,MAAM,QAAQ,WAAW,MAAM,mBAAmB,IAAI,EAAE,CAC3D,KAAI,KAAK,SAAS,KAAK,CAAC,sBAAsB,IAAI,KAAK,CAAE,UAAS,IAAI,QAAQ,OAAO;AAEvF,MAAK,MAAM,YAAY,WAAW,MAAM,oBAAoB,IAAI,EAAE,EAAE;EAClE,MAAM,QAAQ,CAAC,GAAG,SAAS;AAC3B,OAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,SAAS,GAAG,SAAS,EACrD,UAAS,IAAI,OAAO,MAAM,SAAS,MAAM,QAAQ,KAAK;;AAG1D,QAAO;;;AAIT,SAAgB,wBAAwB,OAAe,SAAyB;CAC9E,MAAM,gBAAgB,gBAAgB,MAAM;CAC5C,MAAM,kBAAkB,gBAAgB,QAAQ;AAChD,KAAI,cAAc,SAAS,KAAK,gBAAgB,SAAS,EAAG,QAAO;CACnE,IAAI,SAAS;AACb,MAAK,MAAM,WAAW,cACpB,KAAI,gBAAgB,IAAI,QAAQ,CAAE,WAAU;AAE9C,QAAQ,IAAI,UAAW,cAAc,OAAO,gBAAgB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getSqliteDatabase, runSqliteWriteTransaction } from "./transaction.js";
|
|
2
|
-
import {
|
|
2
|
+
import { buildFts5SearchQuery, fts5RankToScore, memoryLexicalSimilarity } from "./fts.js";
|
|
3
3
|
import { join, relative } from "node:path";
|
|
4
4
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
@@ -50,9 +50,6 @@ function displayPath(baseDir, memoriesDir, userMemoryPath, absPath) {
|
|
|
50
50
|
function contentHash(content) {
|
|
51
51
|
return createHash("sha256").update(content).digest("hex");
|
|
52
52
|
}
|
|
53
|
-
function bm25ToScore(rank) {
|
|
54
|
-
return 1 / (1 + Math.max(0, -rank));
|
|
55
|
-
}
|
|
56
53
|
function indexFile(db, agentId, baseDir, memoriesDir, userMemoryPath, absPath) {
|
|
57
54
|
const content = readFileSync(absPath, "utf-8");
|
|
58
55
|
const mtimeMs = statSync(absPath).mtimeMs;
|
|
@@ -106,16 +103,28 @@ function syncMemoryIndex(params) {
|
|
|
106
103
|
}
|
|
107
104
|
function searchMemoryIndex(params) {
|
|
108
105
|
const { agentId, query, maxResults = 5, minScore = .3 } = params;
|
|
109
|
-
const ftsQuery =
|
|
106
|
+
const ftsQuery = buildFts5SearchQuery(query);
|
|
110
107
|
if (!ftsQuery) return [];
|
|
111
|
-
const
|
|
108
|
+
const db = getSqliteDatabase();
|
|
109
|
+
const rows = db.prepare(`SELECT chunk_id, path, start_line, end_line, content, bm25(memory_fts) AS rank
|
|
112
110
|
FROM memory_fts
|
|
113
111
|
WHERE memory_fts MATCH ? AND agent_id = ?
|
|
114
112
|
ORDER BY rank
|
|
115
113
|
LIMIT ?`).all(ftsQuery, agentId, maxResults * 3);
|
|
114
|
+
if (rows.length === 0) return db.prepare(`SELECT chunk_id, path, start_line, end_line, content
|
|
115
|
+
FROM memory_fts
|
|
116
|
+
WHERE agent_id = ?
|
|
117
|
+
LIMIT 500`).all(agentId).map((row) => ({
|
|
118
|
+
path: row.path,
|
|
119
|
+
lines: row.content,
|
|
120
|
+
score: memoryLexicalSimilarity(query, row.content),
|
|
121
|
+
lineNumbers: [row.start_line]
|
|
122
|
+
})).filter((result) => result.score >= minScore).sort((a, b) => b.score - a.score).slice(0, maxResults);
|
|
123
|
+
const bestRank = Math.min(...rows.map((row) => row.rank));
|
|
124
|
+
const worstRank = Math.max(...rows.map((row) => row.rank));
|
|
116
125
|
const results = [];
|
|
117
126
|
for (const row of rows) {
|
|
118
|
-
const score =
|
|
127
|
+
const score = fts5RankToScore(row.rank, bestRank, worstRank);
|
|
119
128
|
if (score < minScore) continue;
|
|
120
129
|
results.push({
|
|
121
130
|
path: row.path,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-index-repository.js","names":[],"sources":["../../../../src/storage/sqlite/memory-index-repository.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { existsSync, readFileSync, statSync } from 'node:fs';\nimport { join, relative } from 'node:path';\n\nimport { escapeFts5Query } from './fts.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\nexport type MemorySearchHit = {\n path: string;\n lines: string;\n score: number;\n lineNumbers: number[];\n};\n\nconst AGENT_MEMORY_FILENAME = 'MEMORY.md';\nconst USER_MEMORY_DISPLAY_PATH = 'user/MEMORY.md';\nconst MEMORY_SYNC_CACHE_TTL_MS = 30_000;\n\ntype MemorySyncCache = {\n timestamp: number;\n paths: string[];\n};\n\nconst memorySyncCache = new Map<string, MemorySyncCache>();\n\nexport function resolveAgentIdFromMemoriesDir(memoriesDir: string | undefined): string {\n if (!memoriesDir) return 'main';\n const normalized = memoriesDir.replace(/\\\\/g, '/');\n const match = normalized.match(/\\/agents\\/([^/]+)\\/memories\\/?$/);\n return match?.[1] ?? 'main';\n}\n\nfunction getDailyMemoryPath(baseDir: string, date?: Date): string {\n const d = date || new Date();\n const year = d.getFullYear();\n const month = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return join(baseDir, 'memory', `${year}-${month}-${day}.md`);\n}\n\nfunction getCuratedMemoryPaths(memoriesDir: string | undefined, userMemoryPath: string | undefined): string[] {\n return [\n ...(memoriesDir ? [join(memoriesDir, AGENT_MEMORY_FILENAME)] : []),\n ...(userMemoryPath ? [userMemoryPath] : []),\n ].filter((p) => existsSync(p));\n}\n\nfunction collectMemoryPaths(baseDir: string, memoriesDir?: string, userMemoryPath?: string): string[] {\n const paths: string[] = [];\n paths.push(...getCuratedMemoryPaths(memoriesDir, userMemoryPath));\n\n const longTermPath = join(baseDir, 'MEMORY.md');\n if (existsSync(longTermPath)) {\n paths.push(longTermPath);\n }\n\n const memoryDir = join(baseDir, 'memory');\n if (existsSync(memoryDir)) {\n const today = new Date();\n for (let i = 0; i < 30; i++) {\n const date = new Date(today);\n date.setDate(date.getDate() - i);\n const path = getDailyMemoryPath(baseDir, date);\n if (existsSync(path)) {\n paths.push(path);\n }\n }\n }\n\n return paths;\n}\n\nfunction displayPath(\n baseDir: string,\n memoriesDir: string | undefined,\n userMemoryPath: string | undefined,\n absPath: string,\n): string {\n const normalized = absPath.replace(/\\\\/g, '/');\n if (userMemoryPath && normalized === userMemoryPath.replace(/\\\\/g, '/')) {\n return USER_MEMORY_DISPLAY_PATH;\n }\n if (memoriesDir) {\n const memoriesRoot = memoriesDir.replace(/\\\\/g, '/');\n if (normalized.startsWith(memoriesRoot)) {\n const filename = normalized.slice(memoriesRoot.length).replace(/^\\//, '');\n if (filename === AGENT_MEMORY_FILENAME) {\n return filename;\n }\n }\n }\n return relative(baseDir, absPath).replace(/\\\\/g, '/');\n}\n\nfunction contentHash(content: string): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\nfunction bm25ToScore(rank: number): number {\n return 1 / (1 + Math.max(0, -rank));\n}\n\nfunction indexFile(\n db: ReturnType<typeof getSqliteDatabase>,\n agentId: string,\n baseDir: string,\n memoriesDir: string | undefined,\n userMemoryPath: string | undefined,\n absPath: string,\n): void {\n const content = readFileSync(absPath, 'utf-8');\n const mtimeMs = statSync(absPath).mtimeMs;\n const hash = contentHash(content);\n const relPath = displayPath(baseDir, memoriesDir, userMemoryPath, absPath);\n const fileId = `${agentId}:${relPath}`;\n\n const existing = db\n .prepare(`SELECT content_hash, mtime_ms FROM memory_files WHERE file_id = ?`)\n .get(fileId) as { content_hash: string; mtime_ms: number } | undefined;\n if (existing && existing.content_hash === hash && existing.mtime_ms === mtimeMs) {\n return;\n }\n\n db.prepare(`DELETE FROM memory_fts WHERE chunk_id IN (SELECT chunk_id FROM memory_chunks WHERE file_id = ?)`).run(\n fileId,\n );\n db.prepare(`DELETE FROM memory_chunks WHERE file_id = ?`).run(fileId);\n db.prepare(`DELETE FROM memory_files WHERE file_id = ?`).run(fileId);\n\n db.prepare(\n `INSERT INTO memory_files (file_id, agent_id, path, mtime_ms, content_hash)\n VALUES (?, ?, ?, ?, ?)`,\n ).run(fileId, agentId, relPath, mtimeMs, hash);\n\n const lines = content.split('\\n');\n const insertChunk = db.prepare(\n `INSERT INTO memory_chunks (chunk_id, file_id, start_line, end_line, content)\n VALUES (?, ?, ?, ?, ?)`,\n );\n const insertFts = db.prepare(\n `INSERT INTO memory_fts (content, chunk_id, agent_id, path, start_line, end_line)\n VALUES (?, ?, ?, ?, ?, ?)`,\n );\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (!line.trim()) continue;\n const lineNo = i + 1;\n const chunkId = `${fileId}:${lineNo}`;\n insertChunk.run(chunkId, fileId, lineNo, lineNo, line);\n insertFts.run(line, chunkId, agentId, relPath, lineNo, lineNo);\n }\n}\n\nexport function syncMemoryIndex(params: {\n agentId: string;\n workspaceDir: string;\n memoriesDir?: string;\n userMemoryPath?: string;\n}): void {\n const cacheKey = `${params.agentId}:${params.workspaceDir}:${params.memoriesDir ?? ''}:${params.userMemoryPath ?? ''}`;\n const now = Date.now();\n const cached = memorySyncCache.get(cacheKey);\n if (cached && now - cached.timestamp < MEMORY_SYNC_CACHE_TTL_MS) {\n return;\n }\n\n const paths = collectMemoryPaths(params.workspaceDir, params.memoriesDir, params.userMemoryPath);\n memorySyncCache.set(cacheKey, { timestamp: now, paths });\n runSqliteWriteTransaction((db) => {\n const seen = new Set<string>();\n for (const absPath of paths) {\n seen.add(`${params.agentId}:${displayPath(params.workspaceDir, params.memoriesDir, params.userMemoryPath, absPath)}`);\n indexFile(db, params.agentId, params.workspaceDir, params.memoriesDir, params.userMemoryPath, absPath);\n }\n\n const stale = db\n .prepare(`SELECT file_id FROM memory_files WHERE agent_id = ?`)\n .all(params.agentId) as Array<{ file_id: string }>;\n for (const row of stale) {\n if (!seen.has(row.file_id)) {\n db.prepare(\n `DELETE FROM memory_fts WHERE chunk_id IN (SELECT chunk_id FROM memory_chunks WHERE file_id = ?)`,\n ).run(row.file_id);\n db.prepare(`DELETE FROM memory_chunks WHERE file_id = ?`).run(row.file_id);\n db.prepare(`DELETE FROM memory_files WHERE file_id = ?`).run(row.file_id);\n }\n }\n });\n}\n\nexport function searchMemoryIndex(params: {\n agentId: string;\n query: string;\n maxResults?: number;\n minScore?: number;\n}): MemorySearchHit[] {\n const { agentId, query, maxResults = 5, minScore = 0.3 } = params;\n const ftsQuery = escapeFts5Query(query);\n if (!ftsQuery) {\n return [];\n }\n\n const db = getSqliteDatabase();\n const rows = db\n .prepare(\n `SELECT chunk_id, path, start_line, end_line, content, bm25(memory_fts) AS rank\n FROM memory_fts\n WHERE memory_fts MATCH ? AND agent_id = ?\n ORDER BY rank\n LIMIT ?`,\n )\n .all(ftsQuery, agentId, maxResults * 3) as Array<{\n chunk_id: string;\n path: string;\n start_line: number;\n end_line: number;\n content: string;\n rank: number;\n }>;\n\n const results: MemorySearchHit[] = [];\n for (const row of rows) {\n const score = bm25ToScore(row.rank);\n if (score < minScore) continue;\n results.push({\n path: row.path,\n lines: row.content,\n score,\n lineNumbers: [row.start_line],\n });\n }\n\n results.sort((a, b) => b.score - a.score);\n return results.slice(0, maxResults);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"memory-index-repository.js","names":[],"sources":["../../../../src/storage/sqlite/memory-index-repository.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { existsSync, readFileSync, statSync } from 'node:fs';\nimport { join, relative } from 'node:path';\n\nimport { buildFts5SearchQuery, fts5RankToScore, memoryLexicalSimilarity } from './fts.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\nexport type MemorySearchHit = {\n path: string;\n lines: string;\n score: number;\n lineNumbers: number[];\n};\n\nconst AGENT_MEMORY_FILENAME = 'MEMORY.md';\nconst USER_MEMORY_DISPLAY_PATH = 'user/MEMORY.md';\nconst MEMORY_SYNC_CACHE_TTL_MS = 30_000;\n\ntype MemorySyncCache = {\n timestamp: number;\n paths: string[];\n};\n\nconst memorySyncCache = new Map<string, MemorySyncCache>();\n\nexport function resolveAgentIdFromMemoriesDir(memoriesDir: string | undefined): string {\n if (!memoriesDir) return 'main';\n const normalized = memoriesDir.replace(/\\\\/g, '/');\n const match = normalized.match(/\\/agents\\/([^/]+)\\/memories\\/?$/);\n return match?.[1] ?? 'main';\n}\n\nfunction getDailyMemoryPath(baseDir: string, date?: Date): string {\n const d = date || new Date();\n const year = d.getFullYear();\n const month = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return join(baseDir, 'memory', `${year}-${month}-${day}.md`);\n}\n\nfunction getCuratedMemoryPaths(memoriesDir: string | undefined, userMemoryPath: string | undefined): string[] {\n return [\n ...(memoriesDir ? [join(memoriesDir, AGENT_MEMORY_FILENAME)] : []),\n ...(userMemoryPath ? [userMemoryPath] : []),\n ].filter((p) => existsSync(p));\n}\n\nfunction collectMemoryPaths(baseDir: string, memoriesDir?: string, userMemoryPath?: string): string[] {\n const paths: string[] = [];\n paths.push(...getCuratedMemoryPaths(memoriesDir, userMemoryPath));\n\n const longTermPath = join(baseDir, 'MEMORY.md');\n if (existsSync(longTermPath)) {\n paths.push(longTermPath);\n }\n\n const memoryDir = join(baseDir, 'memory');\n if (existsSync(memoryDir)) {\n const today = new Date();\n for (let i = 0; i < 30; i++) {\n const date = new Date(today);\n date.setDate(date.getDate() - i);\n const path = getDailyMemoryPath(baseDir, date);\n if (existsSync(path)) {\n paths.push(path);\n }\n }\n }\n\n return paths;\n}\n\nfunction displayPath(\n baseDir: string,\n memoriesDir: string | undefined,\n userMemoryPath: string | undefined,\n absPath: string,\n): string {\n const normalized = absPath.replace(/\\\\/g, '/');\n if (userMemoryPath && normalized === userMemoryPath.replace(/\\\\/g, '/')) {\n return USER_MEMORY_DISPLAY_PATH;\n }\n if (memoriesDir) {\n const memoriesRoot = memoriesDir.replace(/\\\\/g, '/');\n if (normalized.startsWith(memoriesRoot)) {\n const filename = normalized.slice(memoriesRoot.length).replace(/^\\//, '');\n if (filename === AGENT_MEMORY_FILENAME) {\n return filename;\n }\n }\n }\n return relative(baseDir, absPath).replace(/\\\\/g, '/');\n}\n\nfunction contentHash(content: string): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\nfunction indexFile(\n db: ReturnType<typeof getSqliteDatabase>,\n agentId: string,\n baseDir: string,\n memoriesDir: string | undefined,\n userMemoryPath: string | undefined,\n absPath: string,\n): void {\n const content = readFileSync(absPath, 'utf-8');\n const mtimeMs = statSync(absPath).mtimeMs;\n const hash = contentHash(content);\n const relPath = displayPath(baseDir, memoriesDir, userMemoryPath, absPath);\n const fileId = `${agentId}:${relPath}`;\n\n const existing = db\n .prepare(`SELECT content_hash, mtime_ms FROM memory_files WHERE file_id = ?`)\n .get(fileId) as { content_hash: string; mtime_ms: number } | undefined;\n if (existing && existing.content_hash === hash && existing.mtime_ms === mtimeMs) {\n return;\n }\n\n db.prepare(`DELETE FROM memory_fts WHERE chunk_id IN (SELECT chunk_id FROM memory_chunks WHERE file_id = ?)`).run(\n fileId,\n );\n db.prepare(`DELETE FROM memory_chunks WHERE file_id = ?`).run(fileId);\n db.prepare(`DELETE FROM memory_files WHERE file_id = ?`).run(fileId);\n\n db.prepare(\n `INSERT INTO memory_files (file_id, agent_id, path, mtime_ms, content_hash)\n VALUES (?, ?, ?, ?, ?)`,\n ).run(fileId, agentId, relPath, mtimeMs, hash);\n\n const lines = content.split('\\n');\n const insertChunk = db.prepare(\n `INSERT INTO memory_chunks (chunk_id, file_id, start_line, end_line, content)\n VALUES (?, ?, ?, ?, ?)`,\n );\n const insertFts = db.prepare(\n `INSERT INTO memory_fts (content, chunk_id, agent_id, path, start_line, end_line)\n VALUES (?, ?, ?, ?, ?, ?)`,\n );\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (!line.trim()) continue;\n const lineNo = i + 1;\n const chunkId = `${fileId}:${lineNo}`;\n insertChunk.run(chunkId, fileId, lineNo, lineNo, line);\n insertFts.run(line, chunkId, agentId, relPath, lineNo, lineNo);\n }\n}\n\nexport function syncMemoryIndex(params: {\n agentId: string;\n workspaceDir: string;\n memoriesDir?: string;\n userMemoryPath?: string;\n}): void {\n const cacheKey = `${params.agentId}:${params.workspaceDir}:${params.memoriesDir ?? ''}:${params.userMemoryPath ?? ''}`;\n const now = Date.now();\n const cached = memorySyncCache.get(cacheKey);\n if (cached && now - cached.timestamp < MEMORY_SYNC_CACHE_TTL_MS) {\n return;\n }\n\n const paths = collectMemoryPaths(params.workspaceDir, params.memoriesDir, params.userMemoryPath);\n memorySyncCache.set(cacheKey, { timestamp: now, paths });\n runSqliteWriteTransaction((db) => {\n const seen = new Set<string>();\n for (const absPath of paths) {\n seen.add(`${params.agentId}:${displayPath(params.workspaceDir, params.memoriesDir, params.userMemoryPath, absPath)}`);\n indexFile(db, params.agentId, params.workspaceDir, params.memoriesDir, params.userMemoryPath, absPath);\n }\n\n const stale = db\n .prepare(`SELECT file_id FROM memory_files WHERE agent_id = ?`)\n .all(params.agentId) as Array<{ file_id: string }>;\n for (const row of stale) {\n if (!seen.has(row.file_id)) {\n db.prepare(\n `DELETE FROM memory_fts WHERE chunk_id IN (SELECT chunk_id FROM memory_chunks WHERE file_id = ?)`,\n ).run(row.file_id);\n db.prepare(`DELETE FROM memory_chunks WHERE file_id = ?`).run(row.file_id);\n db.prepare(`DELETE FROM memory_files WHERE file_id = ?`).run(row.file_id);\n }\n }\n });\n}\n\nexport function searchMemoryIndex(params: {\n agentId: string;\n query: string;\n maxResults?: number;\n minScore?: number;\n}): MemorySearchHit[] {\n const { agentId, query, maxResults = 5, minScore = 0.3 } = params;\n const ftsQuery = buildFts5SearchQuery(query);\n if (!ftsQuery) {\n return [];\n }\n\n const db = getSqliteDatabase();\n const rows = db\n .prepare(\n `SELECT chunk_id, path, start_line, end_line, content, bm25(memory_fts) AS rank\n FROM memory_fts\n WHERE memory_fts MATCH ? AND agent_id = ?\n ORDER BY rank\n LIMIT ?`,\n )\n .all(ftsQuery, agentId, maxResults * 3) as Array<{\n chunk_id: string;\n path: string;\n start_line: number;\n end_line: number;\n content: string;\n rank: number;\n }>;\n\n if (rows.length === 0) {\n const fallbackRows = db.prepare(\n `SELECT chunk_id, path, start_line, end_line, content\n FROM memory_fts\n WHERE agent_id = ?\n LIMIT 500`,\n ).all(agentId) as Array<{\n chunk_id: string;\n path: string;\n start_line: number;\n end_line: number;\n content: string;\n }>;\n return fallbackRows\n .map((row) => ({\n path: row.path,\n lines: row.content,\n score: memoryLexicalSimilarity(query, row.content),\n lineNumbers: [row.start_line],\n }))\n .filter((result) => result.score >= minScore)\n .sort((a, b) => b.score - a.score)\n .slice(0, maxResults);\n }\n\n const bestRank = Math.min(...rows.map((row) => row.rank));\n const worstRank = Math.max(...rows.map((row) => row.rank));\n const results: MemorySearchHit[] = [];\n for (const row of rows) {\n const score = fts5RankToScore(row.rank, bestRank, worstRank);\n if (score < minScore) continue;\n results.push({\n path: row.path,\n lines: row.content,\n score,\n lineNumbers: [row.start_line],\n });\n }\n\n results.sort((a, b) => b.score - a.score);\n return results.slice(0, maxResults);\n}\n"],"mappings":"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"}
|
|
@@ -32,6 +32,14 @@ export interface ListMemoryRecordsOptions {
|
|
|
32
32
|
agentId?: string;
|
|
33
33
|
workspaceId?: string;
|
|
34
34
|
projectId?: string;
|
|
35
|
+
/** Include unscoped records plus records visible to this session. */
|
|
36
|
+
visibleToSessionKey?: string;
|
|
37
|
+
/** Return only records that are not scoped to a session. */
|
|
38
|
+
unscopedSessionOnly?: boolean;
|
|
39
|
+
/** Include unscoped records plus records visible to this project. */
|
|
40
|
+
visibleToProjectId?: string;
|
|
41
|
+
/** Return only records that are not scoped to a project. */
|
|
42
|
+
unscopedProjectOnly?: boolean;
|
|
35
43
|
kind?: MemoryKind;
|
|
36
44
|
status?: MemoryStatus;
|
|
37
45
|
canonicalKey?: string;
|
|
@@ -43,6 +51,14 @@ export interface SearchMemoryRecordsOptions {
|
|
|
43
51
|
agentId?: string;
|
|
44
52
|
workspaceId?: string;
|
|
45
53
|
projectId?: string;
|
|
54
|
+
/** Include unscoped records plus records visible to this session. */
|
|
55
|
+
visibleToSessionKey?: string;
|
|
56
|
+
/** Return only records that are not scoped to a session. */
|
|
57
|
+
unscopedSessionOnly?: boolean;
|
|
58
|
+
/** Include unscoped records plus records visible to this project. */
|
|
59
|
+
visibleToProjectId?: string;
|
|
60
|
+
/** Return only records that are not scoped to a project. */
|
|
61
|
+
unscopedProjectOnly?: boolean;
|
|
46
62
|
providerId?: string;
|
|
47
63
|
kinds?: MemoryKind[];
|
|
48
64
|
statuses?: MemoryStatus[];
|
|
@@ -204,6 +220,7 @@ export declare function setMemoryProviderState(providerId: string, scopeKey: str
|
|
|
204
220
|
export declare function appendMemoryTraceEvent(input: AppendMemoryTraceEventInput): string;
|
|
205
221
|
export declare function listMemoryTraceEvents(options?: {
|
|
206
222
|
providerId?: string;
|
|
223
|
+
agentId?: string;
|
|
207
224
|
sessionKey?: string;
|
|
208
225
|
phase?: string;
|
|
209
226
|
limit?: number;
|
|
@@ -214,6 +231,7 @@ export declare function setLatestMemoryInjectFeedback(input: SetLatestMemoryInje
|
|
|
214
231
|
export declare function summarizeMemoryRecallFeedback(options?: {
|
|
215
232
|
recordId?: string;
|
|
216
233
|
providerId?: string;
|
|
234
|
+
agentId?: string;
|
|
217
235
|
sessionKey?: string;
|
|
218
236
|
limit?: number;
|
|
219
237
|
}): MemoryRecallFeedbackSummary[];
|