@xopcai/xopc 0.0.117 → 0.0.118
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/README.md +24 -23
- package/README.zh-CN.md +45 -42
- 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-BBLhJ2C6.js → agent-avatar-dicebear-dzUrQoeZ.js} +1 -1
- package/dist/gateway/static/root/assets/agents-BCxZbkOw.js +2 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-CBpLONvG.js → app-management-settings-panel-bxYJVJpx.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-CGcc54yj.js → appearance-settings-BCIykXlI.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-DdDx0V4z.js → apps-page--wMEL00f.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-DZsVqMhU.js → archive-plugin-Cki4SfU2.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-CIol6JWX.js → attachment-load-D-KnS2Do.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-B3UauMfp.js → binary-plugins-D2sgvJja.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-2HJoFchH.js → block-editor-DIq-4Gvb.js} +27 -27
- package/dist/gateway/static/root/assets/browser-settings-page-DRhKdn81.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-XQoMTI0A.js → capability-presets-api-CorhiHlg.js} +1 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-qFx9Jtce.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-BAwAkqIA.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-BQLcfitM.js +2 -0
- package/dist/gateway/static/root/assets/{cron-api-R41aQCX9.js → cron-api-qGwmL-24.js} +1 -1
- package/dist/gateway/static/root/assets/cron-page-BBWMAj8-.js +3 -0
- package/dist/gateway/static/root/assets/{dreaming-settings-Bceaw1PG.js → dreaming-settings-Dw89Obml.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-DX5WRE_t.js → extension-debug-page-DYu1KaFQ.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-Buah7Bw-.js → extension-page-B3W6QwlZ.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DuroLVnf.js → extension-settings-page-D7VQ-cxE.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-B5zdl27C.js → fetch-B7xDPu-R.js} +1 -1
- package/dist/gateway/static/root/assets/{format-interval-label-Dn_QKIqb.js → format-interval-label-BzxafKxR.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-gT4ZhE7h.js → gateway-config-api-BTBLSeUu.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-iISWAZRm.js → gateway-settings-Cmwy58rS.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-B3kL8KGY.js → gateway-startup-retry-CYj9FWCp.js} +1 -1
- package/dist/gateway/static/root/assets/goal-detail-page-l7mu5GWX.js +1 -0
- package/dist/gateway/static/root/assets/{goals-config-api-AR-fVh0P.js → goals-config-api-CQQgzPr3.js} +1 -1
- package/dist/gateway/static/root/assets/{goals-page-Cpb21ezg.js → goals-page-BHitvJAv.js} +2 -2
- package/dist/gateway/static/root/assets/{goals-settings-CPAuFwoD.js → goals-settings-CYVY4NGp.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-D7bKFEeO.js → heartbeat-config-api-DriO_CN9.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-CUQTKxg7.js +1 -0
- package/dist/gateway/static/root/assets/index-C17Tsd_b.css +1 -0
- package/dist/gateway/static/root/assets/{index-CwhK8qCh.js → index-Cc41r16l.js} +120 -51
- package/dist/gateway/static/root/assets/{interval-presets-0DtsnxE0.js → interval-presets-DFPCKW_W.js} +1 -1
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BPREtN8B.js → keyboard-shortcuts-settings-CcVcqstx.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-DefCgF4o.js → logs-page-C2Uqvae9.js} +2 -2
- package/dist/gateway/static/root/assets/{markdown-split-ChbR9LGO.js → markdown-split-BwOFrJ2E.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-B82oP80n.js → media-plugins-Cnz6Q5G6.js} +1 -1
- package/dist/gateway/static/root/assets/messages-B1iSKjiw.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-BfLfcta8.js +2 -0
- package/dist/gateway/static/root/assets/note-detail-page-BjwOQBfT.js +2 -0
- package/dist/gateway/static/root/assets/{note-image-lightbox-CDknITM3.js → note-image-lightbox-B-Svpmza.js} +1 -1
- package/dist/gateway/static/root/assets/notes-page-DktdBiwB.js +1 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-DV3xQsWB.js → preview-open-alternatives-Bss4TiwP.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-rfVx96fq.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-BODCXnvY.js → save-bar-controls-BRNjdgKq.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-CgJSCD3B.js → schedule-field-CUCKODde.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-qtfEI6QM.js → schema-form-Dub9sy8r.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-DbtMFuuH.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-5re5gAnP.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-BHZKjVcS.js → settings-collapsible-section-BeFLavGi.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-5i35D-J1.js → settings-form-section-dmyaPOTG.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-D-WMyLlj.js +1 -0
- package/dist/gateway/static/root/assets/setup-status-panel-Cl-DEJAU.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-B0H63tDv.js → share-preview-page-C8glrdqe.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-CyPVvbLt.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-CleTH1oz.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-Ck8hdXqR.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-DXs2VmBt.js → system-settings-panel-DI-LN_8G.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-4gPuydz_.js → typed-models-lib-kX9duch4.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-BCjP_VHb.js → use-channel-catalog-DqlWTsDb.js} +1 -1
- package/dist/gateway/static/root/assets/user-profile-settings-panel-B-UFPMXy.js +1 -0
- package/dist/gateway/static/root/assets/utils-Dm0hKalR.js +1 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-C73IVZeC.js +1 -0
- package/dist/gateway/static/root/assets/{voice-player-store-BlF9V05n.js → voice-player-store-C_ArLJPM.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-BtFGp2y_.js +18 -0
- package/dist/gateway/static/root/index.html +5 -5
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +2 -1
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/agent-scope.js +0 -2
- package/dist/src/agent/agent-scope.js.map +1 -1
- package/dist/src/agent/background-review/prompts.d.ts +2 -2
- package/dist/src/agent/background-review/prompts.js +2 -2
- package/dist/src/agent/background-review/prompts.js.map +1 -1
- package/dist/src/agent/background-review/settings.d.ts +1 -1
- package/dist/src/agent/background-review/settings.js +7 -8
- package/dist/src/agent/background-review/settings.js.map +1 -1
- package/dist/src/agent/bootstrap/bootstrap-cache.d.ts +1 -0
- package/dist/src/agent/bootstrap/bootstrap-cache.js +4 -3
- package/dist/src/agent/bootstrap/bootstrap-cache.js.map +1 -1
- package/dist/src/agent/bootstrap/bootstrap-files.d.ts +4 -0
- package/dist/src/agent/bootstrap/bootstrap-files.js +4 -3
- package/dist/src/agent/bootstrap/bootstrap-files.js.map +1 -1
- package/dist/src/agent/bootstrap/filter-bootstrap-files.js +4 -2
- package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
- package/dist/src/agent/bootstrap/load-bootstrap-files.d.ts +2 -1
- package/dist/src/agent/bootstrap/load-bootstrap-files.js +17 -16
- package/dist/src/agent/bootstrap/load-bootstrap-files.js.map +1 -1
- package/dist/src/agent/bootstrap/types.d.ts +1 -1
- package/dist/src/agent/context/workspace-templates/AGENTS.md +1 -1
- package/dist/src/agent/context/workspace.d.ts +4 -2
- package/dist/src/agent/context/workspace.js +5 -4
- package/dist/src/agent/context/workspace.js.map +1 -1
- package/dist/src/agent/embedded/run-for-session.js +4 -1
- package/dist/src/agent/embedded/run-for-session.js.map +1 -1
- package/dist/src/agent/fallback/candidates.js +10 -11
- package/dist/src/agent/fallback/candidates.js.map +1 -1
- package/dist/src/agent/memory/builtin-memory-store.d.ts +4 -2
- package/dist/src/agent/memory/builtin-memory-store.js +11 -6
- package/dist/src/agent/memory/builtin-memory-store.js.map +1 -1
- package/dist/src/agent/memory/builtin-provider.js +36 -7
- package/dist/src/agent/memory/builtin-provider.js.map +1 -1
- package/dist/src/agent/memory/candidate-extractor.d.ts +6 -0
- package/dist/src/agent/memory/candidate-extractor.js +60 -0
- package/dist/src/agent/memory/candidate-extractor.js.map +1 -0
- package/dist/src/agent/memory/manager.d.ts +2 -1
- package/dist/src/agent/memory/manager.js +27 -3
- package/dist/src/agent/memory/manager.js.map +1 -1
- package/dist/src/agent/memory/memory-config.js +3 -0
- package/dist/src/agent/memory/memory-config.js.map +1 -1
- package/dist/src/agent/memory/prefetch-coordinator.js.map +1 -1
- package/dist/src/agent/memory/types.d.ts +27 -4
- package/dist/src/agent/prompt/memory/index.d.ts +4 -2
- package/dist/src/agent/prompt/memory/index.js +18 -12
- package/dist/src/agent/prompt/memory/index.js.map +1 -1
- package/dist/src/agent/prompt/sections/memory-skills.js +1 -1
- package/dist/src/agent/prompt/sections/memory-skills.js.map +1 -1
- package/dist/src/agent/prompt/service-prompt-builder.js +4 -19
- package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
- package/dist/src/agent/reply/post-compaction-context.js +1 -3
- package/dist/src/agent/reply/post-compaction-context.js.map +1 -1
- package/dist/src/agent/reply/startup-context.js +1 -5
- package/dist/src/agent/reply/startup-context.js.map +1 -1
- package/dist/src/agent/service.d.ts +3 -3
- package/dist/src/agent/service.js +7 -8
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/tools/curated-memory-tool.js +57 -2
- package/dist/src/agent/tools/curated-memory-tool.js.map +1 -1
- package/dist/src/agent/tools/web-extract.js +6 -8
- package/dist/src/agent/tools/web-extract.js.map +1 -1
- package/dist/src/agent-manifest/index.js +2 -2
- package/dist/src/agent-manifest/resolver.d.ts +3 -2
- package/dist/src/agent-manifest/resolver.js +5 -3
- package/dist/src/agent-manifest/resolver.js.map +1 -1
- package/dist/src/agent-manifest/schema.d.ts +232 -0
- package/dist/src/agent-manifest/schema.js +4 -2
- package/dist/src/agent-manifest/schema.js.map +1 -1
- package/dist/src/agent-manifest/validator.d.ts +3 -2
- package/dist/src/agent-manifest/validator.js +2 -1
- package/dist/src/agent-manifest/validator.js.map +1 -1
- package/dist/src/channels/pipeline.d.ts +1 -1
- package/dist/src/channels/pipeline.js.map +1 -1
- package/dist/src/chat-commands/agent-edit.js +0 -1
- package/dist/src/chat-commands/agent-edit.js.map +1 -1
- package/dist/src/chat-commands/builtins/model.js +2 -2
- package/dist/src/chat-commands/builtins/model.js.map +1 -1
- package/dist/src/cli/command-manifest.d.ts +1 -1
- package/dist/src/cli/command-manifest.js +1 -1
- package/dist/src/cli/command-manifest.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -0
- package/dist/src/cli/commands/doctor/checks/provider-auth.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/workspace-status.js +0 -1
- package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -1
- package/dist/src/cli/commands/init.js +7 -6
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/models.js +13 -31
- package/dist/src/cli/commands/models.js.map +1 -1
- package/dist/src/cli/commands/onboard/model.js +17 -10
- package/dist/src/cli/commands/onboard/model.js.map +1 -1
- package/dist/src/cli/commands/onboard.js +2 -2
- package/dist/src/cli/commands/onboard.js.map +1 -1
- package/dist/src/cli/commands/tui.js +2 -2
- package/dist/src/cli/commands/tui.js.map +1 -1
- package/dist/src/cli/index.d.ts +1 -0
- package/dist/src/cli/index.js +10 -5
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/registry.js +1 -1
- package/dist/src/cli/registry.js.map +1 -1
- package/dist/src/cli/templates.d.ts +1 -1
- package/dist/src/cli/templates.js +1 -11
- package/dist/src/cli/templates.js.map +1 -1
- package/dist/src/commands/agents.config.js +2 -10
- package/dist/src/commands/agents.config.js.map +1 -1
- package/dist/src/config/agent-profile.js +3 -2
- package/dist/src/config/agent-profile.js.map +1 -1
- package/dist/src/config/agent-typed-models.js +3 -5
- package/dist/src/config/agent-typed-models.js.map +1 -1
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/paths.d.ts +8 -1
- package/dist/src/config/paths.js +16 -3
- package/dist/src/config/paths.js.map +1 -1
- package/dist/src/config/schema.d.ts +32 -32
- package/dist/src/config/schema.js +46 -26
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/gateway/agents-admin.d.ts +7 -0
- package/dist/src/gateway/agents-admin.js +51 -20
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/chat-stream/mapper.js +9 -2
- package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
- package/dist/src/gateway/global-defaults-admin.d.ts +36 -0
- package/dist/src/gateway/global-defaults-admin.js +146 -0
- package/dist/src/gateway/global-defaults-admin.js.map +1 -0
- package/dist/src/gateway/hono/routes/agents.js +33 -1
- package/dist/src/gateway/hono/routes/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/commands-skills.js +4 -5
- package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
- package/dist/src/gateway/hono/routes/global-defaults.d.ts +3 -0
- package/dist/src/gateway/hono/routes/global-defaults.js +61 -0
- package/dist/src/gateway/hono/routes/global-defaults.js.map +1 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js +11 -3
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/memory.js +75 -4
- package/dist/src/gateway/hono/routes/memory.js.map +1 -1
- package/dist/src/gateway/hono/routes/models.js +1 -1
- package/dist/src/gateway/service/marketplace-service.d.ts +4 -4
- package/dist/src/gateway/service/marketplace-service.js +11 -7
- package/dist/src/gateway/service/marketplace-service.js.map +1 -1
- package/dist/src/notes/service.js +3 -1
- package/dist/src/notes/service.js.map +1 -1
- package/dist/src/providers/index.d.ts +4 -5
- package/dist/src/providers/index.js +16 -16
- package/dist/src/providers/index.js.map +1 -1
- package/dist/src/session/session-workspace.js +2 -7
- package/dist/src/session/session-workspace.js.map +1 -1
- package/dist/src/session/store.js +0 -5
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/storage/sqlite/index.d.ts +1 -1
- package/dist/src/storage/sqlite/index.js +2 -2
- package/dist/src/storage/sqlite/memory-index-repository.d.ts +1 -0
- package/dist/src/storage/sqlite/memory-index-repository.js +15 -14
- package/dist/src/storage/sqlite/memory-index-repository.js.map +1 -1
- package/dist/src/storage/sqlite/memory-records-repository.d.ts +44 -1
- package/dist/src/storage/sqlite/memory-records-repository.js +164 -20
- package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/014_memory_record_governance.sql +10 -0
- package/dist/src/storage/sqlite/migrations/015_memory_trace_feedback.sql +4 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/tui/backends/embedded-backend.d.ts +3 -0
- package/dist/src/tui/backends/embedded-backend.js +37 -20
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.d.ts +1 -0
- package/dist/src/tui/backends/gateway-sse-backend.js +16 -3
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
- package/dist/src/tui/components/tui-header.d.ts +0 -1
- package/dist/src/tui/components/tui-header.js +3 -8
- package/dist/src/tui/components/tui-header.js.map +1 -1
- package/dist/src/tui/tui-startup-resources.js +2 -1
- package/dist/src/tui/tui-startup-resources.js.map +1 -1
- package/dist/src/tui/tui-startup-text.js +12 -12
- package/dist/src/tui/tui-startup-text.js.map +1 -1
- package/dist/src/tui/tui.js +4 -1
- package/dist/src/tui/tui.js.map +1 -1
- package/package.json +1 -1
- package/dist/gateway/static/root/assets/agents-C6xgagZd.js +0 -71
- package/dist/gateway/static/root/assets/browser-settings-page-CUOO-PQk.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-CfqF-kGr.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-BYyRzsex.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-DIvEz4NX.js +0 -2
- package/dist/gateway/static/root/assets/cron-page-D97nFqQD.js +0 -3
- package/dist/gateway/static/root/assets/goal-detail-page-DCUROLMQ.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-CC69vNx0.js +0 -1
- package/dist/gateway/static/root/assets/index-CyfhARL-.css +0 -1
- package/dist/gateway/static/root/assets/messages-Cf1O9JB0.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-DmKiIihT.js +0 -2
- package/dist/gateway/static/root/assets/note-detail-page-CxxfLRJZ.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-X2slSx7V.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BvmUWqgu.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-B0yESuuL.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-q2M8RKOo.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-CGozabLD.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-FoOSe6nU.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-Cy5zWyYQ.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DaZNFro9.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-CnwtUzvw.js +0 -2
- package/dist/gateway/static/root/assets/voice-api-key-field-DvKiOglO.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-CAVZcONQ.js +0 -18
- package/dist/src/agent/context/workspace-templates/USER.md +0 -24
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtin-memory-store.js","names":[],"sources":["../../../../src/agent/memory/builtin-memory-store.ts"],"sourcesContent":["/**\n * Bounded curated memory: agent home `memories/MEMORY.md` + `USER.md`, §-delimited entries.\n * Snapshot for system prompt is captured at load time and not mutated until next load.\n */\n\nimport { existsSync, mkdirSync, readFileSync } from 'node:fs';\nimport { mkdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport lockfile from 'proper-lockfile';\n\nimport type { MemorySnapshot, MemoryStoreConfig } from './types.js';\n\nexport const MEMORY_ENTRY_DELIMITER = '\\n§\\n';\n\nconst DEFAULT_MEMORY_LIMIT = 2200;\nconst DEFAULT_USER_LIMIT = 1375;\n\nexport class BuiltinMemoryStore {\n private memoryEntries: string[] = [];\n private userEntries: string[] = [];\n private snapshot: MemorySnapshot = { memory: '', user: '' };\n\n constructor(private readonly config: MemoryStoreConfig) {}\n\n /** When false, USER.md is omitted from the snapshot and should not be edited via `curated_memory`. */\n isUserProfileEnabled(): boolean {\n return this.config.userProfileEnabled !== false;\n }\n\n /** Agent-scoped curated memory directory (`~/.xopc/agents/<id>/memories/`). */\n get memoriesDir(): string {\n return this.config.memoriesDir;\n }\n\n /** Workspace whose MEMORY.md and daily notes participate in local memory search. */\n get workspaceDir(): string {\n return this.config.workspaceDir;\n }\n\n pathFor(target: 'memory' | 'user'): string {\n return join(this.memoriesDir, target === 'memory' ? 'MEMORY.md' : 'USER.md');\n }\n\n /**\n * Load from disk and freeze {@link getSnapshot}. Sync for agent creation (prefix cache stability).\n */\n loadFromDiskSync(): void {\n mkdirSync(this.memoriesDir, { recursive: true });\n this.memoryEntries = this.parseFileContent(\n this.readPathSync(join(this.memoriesDir, 'MEMORY.md')),\n );\n this.userEntries =\n this.config.userProfileEnabled === false\n ? []\n : this.parseFileContent(this.readPathSync(join(this.memoriesDir, 'USER.md')));\n this.memoryEntries = dedupePreserveOrder(this.memoryEntries);\n this.userEntries = dedupePreserveOrder(this.userEntries);\n this.snapshot = {\n memory: this.renderBlock('memory', this.memoryEntries),\n user: this.renderBlock('user', this.userEntries),\n };\n }\n\n private readPathSync(path: string): string {\n try {\n if (!existsSync(path)) return '';\n return readFileSync(path, { encoding: 'utf-8' });\n } catch {\n return '';\n }\n }\n\n private parseFileContent(raw: string): string[] {\n if (!raw.trim()) return [];\n return raw\n .split(MEMORY_ENTRY_DELIMITER)\n .map((e) => e.trim())\n .filter(Boolean);\n }\n\n getSnapshot(): MemorySnapshot {\n return this.snapshot;\n }\n\n /**\n * Live entries for read/debug (may differ from snapshot after mutations).\n */\n getLiveEntries(target: 'memory' | 'user'): string[] {\n return target === 'memory' ? [...this.memoryEntries] : [...this.userEntries];\n }\n\n async add(\n target: 'memory' | 'user',\n content: string,\n ): Promise<{ success: boolean; error?: string; message?: string }> {\n const trimmed = content.trim();\n if (!trimmed) {\n return { success: false, error: 'Content cannot be empty.' };\n }\n const scanError = scanForThreats(trimmed);\n if (scanError) {\n return { success: false, error: scanError };\n }\n\n return this.withFileLock(target, async () => {\n await this.reloadTargetFromDisk(target);\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const limit = this.charLimit(target);\n\n if (entries.includes(trimmed)) {\n return { success: false, error: 'Entry already exists' };\n }\n\n const newTotal = calculateTotal([...entries, trimmed]);\n if (newTotal > limit) {\n return {\n success: false,\n error: `Memory at ${calculateTotal(entries)}/${limit} chars. Adding would exceed limit.`,\n };\n }\n\n entries.push(trimmed);\n await this.flushTargetToDisk(target);\n return { success: true, message: 'Entry added.' };\n });\n }\n\n async replace(\n target: 'memory' | 'user',\n oldText: string,\n newContent: string,\n ): Promise<{ success: boolean; error?: string; message?: string }> {\n const ot = oldText.trim();\n const nc = newContent.trim();\n if (!ot) {\n return { success: false, error: 'old_text cannot be empty.' };\n }\n if (!nc) {\n return { success: false, error: 'new_content cannot be empty. Use remove to delete entries.' };\n }\n const scanError = scanForThreats(nc);\n if (scanError) {\n return { success: false, error: scanError };\n }\n\n return this.withFileLock(target, async () => {\n await this.reloadTargetFromDisk(target);\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const matches: Array<{ index: number; entry: string }> = [];\n for (let i = 0; i < entries.length; i++) {\n if (entries[i].includes(ot)) {\n matches.push({ index: i, entry: entries[i] });\n }\n }\n\n if (matches.length === 0) {\n return { success: false, error: `No entry matched '${ot}'.` };\n }\n\n if (matches.length > 1) {\n const uniqueTexts = new Set(matches.map((m) => m.entry));\n if (uniqueTexts.size > 1) {\n const previews = matches.map((m) =>\n m.entry.length > 80 ? `${m.entry.slice(0, 80)}...` : m.entry,\n );\n return {\n success: false,\n error: `Multiple entries matched '${ot}'. Be more specific. Previews: ${previews.join(' | ')}`,\n };\n }\n }\n\n const idx = matches[0].index;\n const limit = this.charLimit(target);\n const testEntries = [...entries];\n testEntries[idx] = nc;\n\n if (calculateTotal(testEntries) > limit) {\n return {\n success: false,\n error: `Replacement would put memory at ${calculateTotal(testEntries)}/${limit} chars.`,\n };\n }\n\n entries[idx] = nc;\n await this.flushTargetToDisk(target);\n return { success: true, message: 'Entry replaced.' };\n });\n }\n\n async remove(\n target: 'memory' | 'user',\n oldText: string,\n ): Promise<{ success: boolean; error?: string; message?: string }> {\n const ot = oldText.trim();\n if (!ot) {\n return { success: false, error: 'old_text cannot be empty.' };\n }\n\n return this.withFileLock(target, async () => {\n await this.reloadTargetFromDisk(target);\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const matches: number[] = [];\n for (let i = 0; i < entries.length; i++) {\n if (entries[i].includes(ot)) {\n matches.push(i);\n }\n }\n\n if (matches.length === 0) {\n return { success: false, error: `No entry matched '${ot}'.` };\n }\n\n if (matches.length > 1) {\n const uniqueTexts = new Set(matches.map((i) => entries[i]));\n if (uniqueTexts.size > 1) {\n const previews = matches.map((i) => {\n const e = entries[i];\n return e.length > 80 ? `${e.slice(0, 80)}...` : e;\n });\n return {\n success: false,\n error: `Multiple entries matched '${ot}'. Be more specific. Previews: ${previews.join(' | ')}`,\n };\n }\n }\n\n entries.splice(matches[0], 1);\n await this.flushTargetToDisk(target);\n return { success: true, message: 'Entry removed.' };\n });\n }\n\n private async withFileLock<T>(\n target: 'memory' | 'user',\n fn: () => Promise<T>,\n ): Promise<T> {\n const filePath = this.pathFor(target);\n await mkdir(this.memoriesDir, { recursive: true });\n if (!existsSync(filePath)) {\n await writeFile(filePath, '', 'utf-8');\n }\n await lockfile.lock(filePath, { retries: 3, stale: 10_000 });\n try {\n return await fn();\n } finally {\n await lockfile.unlock(filePath);\n }\n }\n\n private async reloadTargetFromDisk(target: 'memory' | 'user'): Promise<void> {\n const path = this.pathFor(target);\n let raw = '';\n try {\n raw = await readFile(path, 'utf-8');\n } catch {\n raw = '';\n }\n const parsed = dedupePreserveOrder(this.parseFileContent(raw));\n if (target === 'memory') {\n this.memoryEntries = parsed;\n } else {\n this.userEntries = parsed;\n }\n }\n\n private async flushTargetToDisk(target: 'memory' | 'user'): Promise<void> {\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const filePath = this.pathFor(target);\n const content = entries.join(MEMORY_ENTRY_DELIMITER);\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await writeFile(tmpPath, content, 'utf-8');\n await rename(tmpPath, filePath);\n }\n\n private charLimit(target: 'memory' | 'user'): number {\n return target === 'memory'\n ? (this.config.memoryCharLimit ?? DEFAULT_MEMORY_LIMIT)\n : (this.config.userCharLimit ?? DEFAULT_USER_LIMIT);\n }\n\n private renderBlock(target: 'memory' | 'user', entries: string[]): string {\n if (entries.length === 0) {\n return '';\n }\n\n const limit = this.charLimit(target);\n const content = entries.join(MEMORY_ENTRY_DELIMITER);\n const current = content.length;\n const pct = Math.min(100, Math.round((current / limit) * 100));\n\n const header =\n target === 'user'\n ? `USER PROFILE (who the user is) [${pct}% — ${current.toLocaleString()}/${limit.toLocaleString()} chars]`\n : `MEMORY (your personal notes) [${pct}% — ${current.toLocaleString()}/${limit.toLocaleString()} chars]`;\n\n const separator = '═'.repeat(46);\n return `${separator}\\n${header}\\n${separator}\\n${content}`;\n }\n}\n\nfunction dedupePreserveOrder(entries: string[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const e of entries) {\n if (seen.has(e)) {\n continue;\n }\n seen.add(e);\n out.push(e);\n }\n return out;\n}\n\nfunction calculateTotal(entries: string[]): number {\n if (entries.length === 0) {\n return 0;\n }\n return entries.join(MEMORY_ENTRY_DELIMITER).length;\n}\n\nexport function scanForThreats(content: string): string | null {\n const patterns: Array<{ regex: RegExp; id: string }> = [\n { regex: /ignore\\s+(previous|all|above|prior)\\s+instructions/i, id: 'prompt_injection' },\n { regex: /you\\s+are\\s+now\\s+/i, id: 'role_hijack' },\n { regex: /do\\s+not\\s+tell\\s+the\\s+user/i, id: 'deception_hide' },\n { regex: /system\\s+prompt\\s+override/i, id: 'sys_prompt_override' },\n {\n regex: /disregard\\s+(your|all|any)\\s+(instructions|rules|guidelines)/i,\n id: 'disregard_rules',\n },\n {\n regex: /act\\s+as\\s+(if|though)\\s+you\\s+(have\\s+no|don't\\s+have)\\s+(restrictions|limits|rules)/i,\n id: 'bypass_restrictions',\n },\n { regex: /curl\\s+[^\\n]*\\$\\{?\\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)/i, id: 'exfil_curl' },\n { regex: /wget\\s+[^\\n]*\\$\\{?\\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)/i, id: 'exfil_wget' },\n {\n regex: /cat\\s+[^\\n]*(\\.env|credentials|\\.netrc|\\.pgpass|\\.npmrc|\\.pypirc)/i,\n id: 'read_secrets',\n },\n { regex: /authorized_keys/i, id: 'ssh_backdoor' },\n { regex: /\\$HOME\\/\\.ssh|\\~\\/\\.ssh/i, id: 'ssh_access' },\n ];\n\n for (const { regex, id } of patterns) {\n if (regex.test(content)) {\n return `Blocked: content matches threat pattern '${id}'. Memory entries are injected into the system prompt and must not contain injection payloads.`;\n }\n }\n\n const invisibleChars = [\n '\\u200b',\n '\\u200c',\n '\\u200d',\n '\\u2060',\n '\\ufeff',\n '\\u202a',\n '\\u202b',\n '\\u202c',\n '\\u202d',\n '\\u202e',\n ];\n for (const char of invisibleChars) {\n if (content.includes(char)) {\n const code = char.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0');\n return `Blocked: content contains invisible unicode character U+${code} (possible injection).`;\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"builtin-memory-store.js","names":[],"sources":["../../../../src/agent/memory/builtin-memory-store.ts"],"sourcesContent":["/**\n * Bounded curated memory: agent home `memories/MEMORY.md` + global `user/MEMORY.md`, §-delimited entries.\n * Snapshot for system prompt is captured at load time and not mutated until next load.\n */\n\nimport { existsSync, mkdirSync, readFileSync } from 'node:fs';\nimport { mkdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport lockfile from 'proper-lockfile';\n\nimport type { MemorySnapshot, MemoryStoreConfig } from './types.js';\n\nexport const MEMORY_ENTRY_DELIMITER = '\\n§\\n';\n\nconst DEFAULT_MEMORY_LIMIT = 2200;\nconst DEFAULT_USER_LIMIT = 1375;\n\nexport class BuiltinMemoryStore {\n private memoryEntries: string[] = [];\n private userEntries: string[] = [];\n private snapshot: MemorySnapshot = { memory: '', user: '' };\n\n constructor(private readonly config: MemoryStoreConfig) {}\n\n /** When false, global user memory is omitted from the snapshot and should not be edited via `curated_memory`. */\n isUserProfileEnabled(): boolean {\n return this.config.userProfileEnabled !== false;\n }\n\n /** Agent-scoped curated memory directory (`~/.xopc/agents/<id>/memories/`). */\n get memoriesDir(): string {\n return this.config.memoriesDir;\n }\n\n /** Global user memory file (`~/.xopc/user/MEMORY.md`). */\n get userMemoryPath(): string {\n return this.config.userMemoryPath;\n }\n\n /** Workspace whose MEMORY.md and daily notes participate in local memory search. */\n get workspaceDir(): string {\n return this.config.workspaceDir;\n }\n\n pathFor(target: 'memory' | 'user'): string {\n return target === 'memory' ? join(this.memoriesDir, 'MEMORY.md') : this.userMemoryPath;\n }\n\n /**\n * Load from disk and freeze {@link getSnapshot}. Sync for agent creation (prefix cache stability).\n */\n loadFromDiskSync(): void {\n mkdirSync(this.memoriesDir, { recursive: true });\n this.memoryEntries = this.parseFileContent(\n this.readPathSync(join(this.memoriesDir, 'MEMORY.md')),\n );\n this.userEntries =\n this.config.userProfileEnabled === false\n ? []\n : this.parseFileContent(this.readPathSync(this.userMemoryPath));\n this.memoryEntries = dedupePreserveOrder(this.memoryEntries);\n this.userEntries = dedupePreserveOrder(this.userEntries);\n this.snapshot = {\n memory: this.renderBlock('memory', this.memoryEntries),\n user: this.renderBlock('user', this.userEntries),\n };\n }\n\n private readPathSync(path: string): string {\n try {\n if (!existsSync(path)) return '';\n return readFileSync(path, { encoding: 'utf-8' });\n } catch {\n return '';\n }\n }\n\n private parseFileContent(raw: string): string[] {\n if (!raw.trim()) return [];\n return raw\n .split(MEMORY_ENTRY_DELIMITER)\n .map((e) => e.trim())\n .filter(Boolean);\n }\n\n getSnapshot(): MemorySnapshot {\n return this.snapshot;\n }\n\n /**\n * Live entries for read/debug (may differ from snapshot after mutations).\n */\n getLiveEntries(target: 'memory' | 'user'): string[] {\n return target === 'memory' ? [...this.memoryEntries] : [...this.userEntries];\n }\n\n async add(\n target: 'memory' | 'user',\n content: string,\n ): Promise<{ success: boolean; error?: string; message?: string }> {\n const trimmed = content.trim();\n if (!trimmed) {\n return { success: false, error: 'Content cannot be empty.' };\n }\n const scanError = scanForThreats(trimmed);\n if (scanError) {\n return { success: false, error: scanError };\n }\n\n return this.withFileLock(target, async () => {\n await this.reloadTargetFromDisk(target);\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const limit = this.charLimit(target);\n\n if (entries.includes(trimmed)) {\n return { success: false, error: 'Entry already exists' };\n }\n\n const newTotal = calculateTotal([...entries, trimmed]);\n if (newTotal > limit) {\n return {\n success: false,\n error: `Memory at ${calculateTotal(entries)}/${limit} chars. Adding would exceed limit.`,\n };\n }\n\n entries.push(trimmed);\n await this.flushTargetToDisk(target);\n return { success: true, message: 'Entry added.' };\n });\n }\n\n async replace(\n target: 'memory' | 'user',\n oldText: string,\n newContent: string,\n ): Promise<{ success: boolean; error?: string; message?: string }> {\n const ot = oldText.trim();\n const nc = newContent.trim();\n if (!ot) {\n return { success: false, error: 'old_text cannot be empty.' };\n }\n if (!nc) {\n return { success: false, error: 'new_content cannot be empty. Use remove to delete entries.' };\n }\n const scanError = scanForThreats(nc);\n if (scanError) {\n return { success: false, error: scanError };\n }\n\n return this.withFileLock(target, async () => {\n await this.reloadTargetFromDisk(target);\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const matches: Array<{ index: number; entry: string }> = [];\n for (let i = 0; i < entries.length; i++) {\n if (entries[i].includes(ot)) {\n matches.push({ index: i, entry: entries[i] });\n }\n }\n\n if (matches.length === 0) {\n return { success: false, error: `No entry matched '${ot}'.` };\n }\n\n if (matches.length > 1) {\n const uniqueTexts = new Set(matches.map((m) => m.entry));\n if (uniqueTexts.size > 1) {\n const previews = matches.map((m) =>\n m.entry.length > 80 ? `${m.entry.slice(0, 80)}...` : m.entry,\n );\n return {\n success: false,\n error: `Multiple entries matched '${ot}'. Be more specific. Previews: ${previews.join(' | ')}`,\n };\n }\n }\n\n const idx = matches[0].index;\n const limit = this.charLimit(target);\n const testEntries = [...entries];\n testEntries[idx] = nc;\n\n if (calculateTotal(testEntries) > limit) {\n return {\n success: false,\n error: `Replacement would put memory at ${calculateTotal(testEntries)}/${limit} chars.`,\n };\n }\n\n entries[idx] = nc;\n await this.flushTargetToDisk(target);\n return { success: true, message: 'Entry replaced.' };\n });\n }\n\n async remove(\n target: 'memory' | 'user',\n oldText: string,\n ): Promise<{ success: boolean; error?: string; message?: string }> {\n const ot = oldText.trim();\n if (!ot) {\n return { success: false, error: 'old_text cannot be empty.' };\n }\n\n return this.withFileLock(target, async () => {\n await this.reloadTargetFromDisk(target);\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const matches: number[] = [];\n for (let i = 0; i < entries.length; i++) {\n if (entries[i].includes(ot)) {\n matches.push(i);\n }\n }\n\n if (matches.length === 0) {\n return { success: false, error: `No entry matched '${ot}'.` };\n }\n\n if (matches.length > 1) {\n const uniqueTexts = new Set(matches.map((i) => entries[i]));\n if (uniqueTexts.size > 1) {\n const previews = matches.map((i) => {\n const e = entries[i];\n return e.length > 80 ? `${e.slice(0, 80)}...` : e;\n });\n return {\n success: false,\n error: `Multiple entries matched '${ot}'. Be more specific. Previews: ${previews.join(' | ')}`,\n };\n }\n }\n\n entries.splice(matches[0], 1);\n await this.flushTargetToDisk(target);\n return { success: true, message: 'Entry removed.' };\n });\n }\n\n private async withFileLock<T>(\n target: 'memory' | 'user',\n fn: () => Promise<T>,\n ): Promise<T> {\n const filePath = this.pathFor(target);\n await mkdir(dirname(filePath), { recursive: true });\n if (!existsSync(filePath)) {\n await writeFile(filePath, '', 'utf-8');\n }\n await lockfile.lock(filePath, { retries: 3, stale: 10_000 });\n try {\n return await fn();\n } finally {\n await lockfile.unlock(filePath);\n }\n }\n\n private async reloadTargetFromDisk(target: 'memory' | 'user'): Promise<void> {\n const path = this.pathFor(target);\n let raw = '';\n try {\n raw = await readFile(path, 'utf-8');\n } catch {\n raw = '';\n }\n const parsed = dedupePreserveOrder(this.parseFileContent(raw));\n if (target === 'memory') {\n this.memoryEntries = parsed;\n } else {\n this.userEntries = parsed;\n }\n }\n\n private async flushTargetToDisk(target: 'memory' | 'user'): Promise<void> {\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const filePath = this.pathFor(target);\n const content = entries.join(MEMORY_ENTRY_DELIMITER);\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(tmpPath, content, 'utf-8');\n await rename(tmpPath, filePath);\n }\n\n private charLimit(target: 'memory' | 'user'): number {\n return target === 'memory'\n ? (this.config.memoryCharLimit ?? DEFAULT_MEMORY_LIMIT)\n : (this.config.userCharLimit ?? DEFAULT_USER_LIMIT);\n }\n\n private renderBlock(target: 'memory' | 'user', entries: string[]): string {\n if (entries.length === 0) {\n return '';\n }\n\n const limit = this.charLimit(target);\n const content = entries.join(MEMORY_ENTRY_DELIMITER);\n const current = content.length;\n const pct = Math.min(100, Math.round((current / limit) * 100));\n\n const header =\n target === 'user'\n ? `USER PROFILE (who the user is) [${pct}% — ${current.toLocaleString()}/${limit.toLocaleString()} chars]`\n : `MEMORY (your personal notes) [${pct}% — ${current.toLocaleString()}/${limit.toLocaleString()} chars]`;\n\n const separator = '═'.repeat(46);\n return `${separator}\\n${header}\\n${separator}\\n${content}`;\n }\n}\n\nfunction dedupePreserveOrder(entries: string[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const e of entries) {\n if (seen.has(e)) {\n continue;\n }\n seen.add(e);\n out.push(e);\n }\n return out;\n}\n\nfunction calculateTotal(entries: string[]): number {\n if (entries.length === 0) {\n return 0;\n }\n return entries.join(MEMORY_ENTRY_DELIMITER).length;\n}\n\nexport function scanForThreats(content: string): string | null {\n const patterns: Array<{ regex: RegExp; id: string }> = [\n { regex: /ignore\\s+(previous|all|above|prior)\\s+instructions/i, id: 'prompt_injection' },\n { regex: /you\\s+are\\s+now\\s+/i, id: 'role_hijack' },\n { regex: /do\\s+not\\s+tell\\s+the\\s+user/i, id: 'deception_hide' },\n { regex: /system\\s+prompt\\s+override/i, id: 'sys_prompt_override' },\n {\n regex: /disregard\\s+(your|all|any)\\s+(instructions|rules|guidelines)/i,\n id: 'disregard_rules',\n },\n {\n regex: /act\\s+as\\s+(if|though)\\s+you\\s+(have\\s+no|don't\\s+have)\\s+(restrictions|limits|rules)/i,\n id: 'bypass_restrictions',\n },\n { regex: /curl\\s+[^\\n]*\\$\\{?\\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)/i, id: 'exfil_curl' },\n { regex: /wget\\s+[^\\n]*\\$\\{?\\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)/i, id: 'exfil_wget' },\n {\n regex: /cat\\s+[^\\n]*(\\.env|credentials|\\.netrc|\\.pgpass|\\.npmrc|\\.pypirc)/i,\n id: 'read_secrets',\n },\n { regex: /authorized_keys/i, id: 'ssh_backdoor' },\n { regex: /\\$HOME\\/\\.ssh|\\~\\/\\.ssh/i, id: 'ssh_access' },\n ];\n\n for (const { regex, id } of patterns) {\n if (regex.test(content)) {\n return `Blocked: content matches threat pattern '${id}'. Memory entries are injected into the system prompt and must not contain injection payloads.`;\n }\n }\n\n const invisibleChars = [\n '\\u200b',\n '\\u200c',\n '\\u200d',\n '\\u2060',\n '\\ufeff',\n '\\u202a',\n '\\u202b',\n '\\u202c',\n '\\u202d',\n '\\u202e',\n ];\n for (const char of invisibleChars) {\n if (content.includes(char)) {\n const code = char.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0');\n return `Blocked: content contains invisible unicode character U+${code} (possible injection).`;\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
|
|
@@ -61,6 +61,7 @@ var BuiltinMemoryProvider = class {
|
|
|
61
61
|
maxResults: request.maxResults,
|
|
62
62
|
minScore: request.minScore,
|
|
63
63
|
memoriesDir: this.store.memoriesDir,
|
|
64
|
+
userMemoryPath: this.store.userMemoryPath,
|
|
64
65
|
agentId
|
|
65
66
|
})).map((entry) => {
|
|
66
67
|
const start = entry.lineNumbers[0] ?? 1;
|
|
@@ -100,7 +101,7 @@ var BuiltinMemoryProvider = class {
|
|
|
100
101
|
const existing = getMemoryRecord(request.id);
|
|
101
102
|
if (existing) return { record: existing };
|
|
102
103
|
}
|
|
103
|
-
const result = memoryGet(this.workspaceDir, request.path ?? request.id ?? "", request.from, request.lines, this.store.memoriesDir);
|
|
104
|
+
const result = memoryGet(this.workspaceDir, request.path ?? request.id ?? "", request.from, request.lines, this.store.memoriesDir, this.store.userMemoryPath);
|
|
104
105
|
if (!result) return null;
|
|
105
106
|
const path = request.path ?? request.id ?? "";
|
|
106
107
|
return {
|
|
@@ -148,12 +149,36 @@ var BuiltinMemoryProvider = class {
|
|
|
148
149
|
content,
|
|
149
150
|
source: {
|
|
150
151
|
provider: this.id,
|
|
151
|
-
path: target
|
|
152
|
+
path: sourcePathForTarget(target)
|
|
152
153
|
}
|
|
153
154
|
}));
|
|
154
155
|
}
|
|
155
156
|
async write(request) {
|
|
156
157
|
const target = request.target ?? (request.kind === "user_profile" ? "user" : "memory");
|
|
158
|
+
if (request.status === "candidate") return {
|
|
159
|
+
success: true,
|
|
160
|
+
message: "Memory candidate added to inbox",
|
|
161
|
+
record: upsertMemoryRecord({
|
|
162
|
+
providerId: this.id,
|
|
163
|
+
kind: target === "user" ? "user_profile" : request.kind,
|
|
164
|
+
agentId: request.scope?.agentId ?? this.agentId,
|
|
165
|
+
workspaceId: request.scope?.workspaceId ?? this.workspaceDir,
|
|
166
|
+
sessionKey: request.scope?.sessionKey,
|
|
167
|
+
content: request.content,
|
|
168
|
+
source: {
|
|
169
|
+
...request.source ?? {},
|
|
170
|
+
provider: this.id,
|
|
171
|
+
path: sourcePathForTarget(target)
|
|
172
|
+
},
|
|
173
|
+
confidence: request.confidence,
|
|
174
|
+
tags: request.tags,
|
|
175
|
+
status: "candidate",
|
|
176
|
+
sensitivity: request.sensitivity,
|
|
177
|
+
evidence: request.evidence,
|
|
178
|
+
reviewAfter: request.reviewAfter,
|
|
179
|
+
expiresAt: request.expiresAt
|
|
180
|
+
})
|
|
181
|
+
};
|
|
157
182
|
const result = await this.store.add(target, request.content);
|
|
158
183
|
if (!result.success) return {
|
|
159
184
|
success: false,
|
|
@@ -173,7 +198,7 @@ var BuiltinMemoryProvider = class {
|
|
|
173
198
|
source: {
|
|
174
199
|
...request.source ?? {},
|
|
175
200
|
provider: this.id,
|
|
176
|
-
path: target
|
|
201
|
+
path: sourcePathForTarget(target)
|
|
177
202
|
},
|
|
178
203
|
tags: request.tags
|
|
179
204
|
})
|
|
@@ -198,7 +223,7 @@ var BuiltinMemoryProvider = class {
|
|
|
198
223
|
content: request.content,
|
|
199
224
|
source: existing?.source ?? {
|
|
200
225
|
provider: this.id,
|
|
201
|
-
path: target
|
|
226
|
+
path: sourcePathForTarget(target)
|
|
202
227
|
},
|
|
203
228
|
tags: existing?.tags
|
|
204
229
|
});
|
|
@@ -262,14 +287,18 @@ var BuiltinMemoryProvider = class {
|
|
|
262
287
|
async shutdown() {}
|
|
263
288
|
};
|
|
264
289
|
function inferKindFromPath(path) {
|
|
265
|
-
|
|
266
|
-
if (
|
|
267
|
-
if (/^memory\/\d{4}-\d{2}-\d{2}\.md$/.test(
|
|
290
|
+
const normalized = path.replace(/\\/g, "/");
|
|
291
|
+
if (normalized === "user/MEMORY.md" || normalized.endsWith("/user/MEMORY.md")) return "user_profile";
|
|
292
|
+
if (/^memory\/\d{4}-\d{2}-\d{2}\.md$/.test(normalized)) return "daily_note";
|
|
293
|
+
if (normalized.endsWith("MEMORY.md")) return "agent_note";
|
|
268
294
|
return "workspace_fact";
|
|
269
295
|
}
|
|
270
296
|
function targetForKind(kind) {
|
|
271
297
|
return kind === "user_profile" ? "user" : "memory";
|
|
272
298
|
}
|
|
299
|
+
function sourcePathForTarget(target) {
|
|
300
|
+
return target === "user" ? "user/MEMORY.md" : "MEMORY.md";
|
|
301
|
+
}
|
|
273
302
|
function parseLineAddress(id) {
|
|
274
303
|
const match = /^(.*)#L(\d+)(?:-L(\d+))?$/.exec(id);
|
|
275
304
|
if (!match) return { path: id };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtin-provider.js","names":[],"sources":["../../../../src/agent/memory/builtin-provider.ts"],"sourcesContent":["import type { BuiltinMemoryStore } from './builtin-memory-store.js';\nimport type { MemoryProvider, MemoryProviderInitOptions } from './provider.js';\nimport { memoryGet, memorySearch } from '../prompt/memory/index.js';\nimport {\n appendMemorySignal,\n deleteMemoryRecord,\n getMemoryRecord,\n listMemoryRecords,\n searchMemoryRecords,\n upsertMemoryRecord,\n} from '../../storage/sqlite/index.js';\nimport type {\n MemoryDeleteRequest,\n MemoryListRequest,\n MemoryReadRequest,\n MemoryReadResult,\n MemoryRecord,\n MemorySearchRequest,\n MemorySearchResult,\n MemoryUpdateRequest,\n MemoryWriteRequest,\n MemoryWriteResult,\n MemorySyncEvent,\n} from './types.js';\n\n/**\n * Builtin local memory provider backed by agent-home curated files, workspace MEMORY.md,\n * daily notes, and the local SQLite FTS index.\n */\nexport class BuiltinMemoryProvider implements MemoryProvider {\n readonly id = 'local';\n readonly displayName = 'Local Markdown Memory';\n readonly capabilities = {\n search: true,\n read: true,\n write: true,\n update: true,\n delete: true,\n keywordSearch: true,\n semanticSearch: false,\n hybridSearch: false,\n citations: true,\n sync: false,\n local: true,\n };\n\n private workspaceDir = '';\n private agentId = 'main';\n\n constructor(private readonly store: BuiltinMemoryStore) {}\n\n isAvailable(): boolean {\n return true;\n }\n\n async initialize(_sessionId: string, options?: MemoryProviderInitOptions): Promise<void> {\n this.workspaceDir = options?.workspace ?? options?.agentWorkspace ?? this.store.workspaceDir;\n this.agentId = String(options?.agentId ?? this.agentId);\n }\n\n systemPromptBlock(): string {\n return '';\n }\n\n async prefetch(): Promise<string> {\n return '';\n }\n\n queuePrefetch(): void {}\n\n getToolSchemas() {\n return [];\n }\n\n async search(request: MemorySearchRequest): Promise<MemorySearchResult[]> {\n const agentId = request.scope?.agentId ?? this.agentId;\n const workspaceId = request.scope?.workspaceId ?? this.workspaceDir;\n const recordHits = searchMemoryRecords({\n query: request.query,\n agentId,\n workspaceId,\n providerId: this.id,\n kinds: request.kinds,\n maxResults: request.maxResults,\n minScore: request.minScore,\n });\n if (recordHits.length > 0) {\n return recordHits;\n }\n\n const results = await memorySearch(this.workspaceDir, request.query, {\n maxResults: request.maxResults,\n minScore: request.minScore,\n memoriesDir: this.store.memoriesDir,\n agentId,\n });\n return results.map((entry) => {\n const start = entry.lineNumbers[0] ?? 1;\n const end = entry.lineNumbers[entry.lineNumbers.length - 1] ?? start;\n const id = `${entry.file}#L${start}-L${end}`;\n const kind = inferKindFromPath(entry.file);\n const record = upsertMemoryRecord({\n id,\n providerId: this.id,\n kind,\n agentId,\n workspaceId,\n content: entry.lines,\n source: {\n provider: this.id,\n path: entry.file,\n lineStart: start,\n lineEnd: end,\n },\n confidence: entry.score,\n });\n return {\n record,\n score: entry.score,\n snippet: entry.lines,\n citation: {\n providerId: this.id,\n recordId: id,\n path: entry.file,\n lineStart: start,\n lineEnd: end,\n },\n };\n });\n }\n\n async read(request: MemoryReadRequest): Promise<MemoryReadResult | null> {\n if (request.id && !request.path) {\n const existing = getMemoryRecord(request.id);\n if (existing) {\n return { record: existing };\n }\n }\n const result = memoryGet(\n this.workspaceDir,\n request.path ?? request.id ?? '',\n request.from,\n request.lines,\n this.store.memoriesDir,\n );\n if (!result) return null;\n const path = request.path ?? request.id ?? '';\n const record = upsertMemoryRecord({\n id: `${path}#L${result.lineNumbers.start}-L${result.lineNumbers.end}`,\n providerId: this.id,\n kind: inferKindFromPath(path),\n agentId: request.scope?.agentId ?? this.agentId,\n workspaceId: request.scope?.workspaceId ?? this.workspaceDir,\n content: result.content,\n source: {\n provider: this.id,\n path,\n lineStart: result.lineNumbers.start,\n lineEnd: result.lineNumbers.end,\n },\n });\n return {\n record,\n lineNumbers: result.lineNumbers,\n };\n }\n\n async get(id: string): Promise<MemoryRecord | null> {\n const parsed = parseLineAddress(id);\n const result = await this.read({\n path: parsed.path,\n from: parsed.lineStart,\n lines: parsed.lineEnd != null ? parsed.lineEnd - parsed.lineStart + 1 : undefined,\n });\n return result?.record ?? null;\n }\n\n async list(request: MemoryListRequest): Promise<MemoryRecord[]> {\n const records = listMemoryRecords({\n providerId: this.id,\n agentId: request.scope?.agentId ?? this.agentId,\n workspaceId: request.scope?.workspaceId ?? this.workspaceDir,\n kind: request.kind,\n limit: 200,\n });\n if (records.length > 0 || request.kind) {\n return request.target\n ? records.filter((record) => targetForKind(record.kind) === request.target)\n : records;\n }\n\n const target = request.target ?? (request.kind === 'user_profile' ? 'user' : 'memory');\n return this.store.getLiveEntries(target).map((content, index) =>\n upsertMemoryRecord({\n id: `curated:${target}:${index + 1}`,\n providerId: this.id,\n kind: target === 'user' ? 'user_profile' : 'agent_note',\n agentId: request.scope?.agentId ?? this.agentId,\n workspaceId: request.scope?.workspaceId ?? this.workspaceDir,\n content,\n source: {\n provider: this.id,\n path: target === 'user' ? 'USER.md' : 'MEMORY.md',\n },\n }),\n );\n }\n\n async write(request: MemoryWriteRequest): Promise<MemoryWriteResult> {\n const target = request.target ?? (request.kind === 'user_profile' ? 'user' : 'memory');\n const result = await this.store.add(target, request.content);\n if (!result.success) return { success: false, error: result.error };\n return {\n success: true,\n message: result.message,\n record: upsertMemoryRecord({\n id: `curated:${target}:${Date.now()}`,\n providerId: this.id,\n kind: target === 'user' ? 'user_profile' : request.kind,\n agentId: request.scope?.agentId ?? this.agentId,\n workspaceId: request.scope?.workspaceId ?? this.workspaceDir,\n sessionKey: request.scope?.sessionKey,\n content: request.content,\n source: {\n ...(request.source ?? {}),\n provider: this.id,\n path: target === 'user' ? 'USER.md' : 'MEMORY.md',\n },\n tags: request.tags,\n }),\n };\n }\n\n async update(request: MemoryUpdateRequest): Promise<MemoryWriteResult> {\n const target = request.target ?? 'memory';\n const result = await this.store.replace(target, request.matchText ?? request.id ?? '', request.content);\n if (!result.success) return { success: false, error: result.error };\n if (request.id) {\n const existing = getMemoryRecord(request.id);\n upsertMemoryRecord({\n id: request.id,\n providerId: this.id,\n kind: existing?.kind ?? (target === 'user' ? 'user_profile' : 'agent_note'),\n agentId: existing?.scope.agentId ?? request.scope?.agentId ?? this.agentId,\n workspaceId: existing?.scope.workspaceId ?? request.scope?.workspaceId ?? this.workspaceDir,\n sessionKey: existing?.scope.sessionKey ?? request.scope?.sessionKey,\n content: request.content,\n source: existing?.source ?? { provider: this.id, path: target === 'user' ? 'USER.md' : 'MEMORY.md' },\n tags: existing?.tags,\n });\n } else if (request.matchText) {\n for (const existing of listMemoryRecords({\n providerId: this.id,\n agentId: request.scope?.agentId ?? this.agentId,\n workspaceId: request.scope?.workspaceId ?? this.workspaceDir,\n kind: target === 'user' ? 'user_profile' : 'agent_note',\n limit: 200,\n })) {\n if (!existing.content.includes(request.matchText)) continue;\n upsertMemoryRecord({\n id: existing.id,\n providerId: this.id,\n kind: existing.kind,\n agentId: existing.scope.agentId,\n workspaceId: existing.scope.workspaceId,\n sessionKey: existing.scope.sessionKey,\n content: request.content,\n source: existing.source,\n tags: existing.tags,\n });\n }\n }\n return { success: true, message: result.message };\n }\n\n async delete(request: MemoryDeleteRequest): Promise<MemoryWriteResult> {\n const target = request.target ?? 'memory';\n const result = await this.store.remove(target, request.matchText ?? request.id ?? '');\n if (!result.success) return { success: false, error: result.error };\n if (request.id) {\n deleteMemoryRecord(request.id);\n } else if (request.matchText) {\n for (const existing of listMemoryRecords({\n providerId: this.id,\n agentId: request.scope?.agentId ?? this.agentId,\n workspaceId: request.scope?.workspaceId ?? this.workspaceDir,\n kind: target === 'user' ? 'user_profile' : 'agent_note',\n limit: 200,\n })) {\n if (existing.content.includes(request.matchText)) {\n deleteMemoryRecord(existing.id);\n }\n }\n }\n return { success: true, message: result.message };\n }\n\n sync(event: MemorySyncEvent): void {\n if (event.type !== 'signal') return;\n appendMemorySignal({\n signal: event.signal,\n providerId: this.id,\n agentId: this.agentId,\n workspaceId: this.workspaceDir,\n sessionKey:\n typeof event.signal.metadata?.sessionKey === 'string'\n ? event.signal.metadata.sessionKey\n : undefined,\n });\n }\n\n async shutdown(): Promise<void> {}\n}\n\nfunction inferKindFromPath(path: string): MemoryRecord['kind'] {\n if (path.endsWith('USER.md')) return 'user_profile';\n if (path.endsWith('MEMORY.md')) return 'agent_note';\n if (/^memory\\/\\d{4}-\\d{2}-\\d{2}\\.md$/.test(path)) return 'daily_note';\n return 'workspace_fact';\n}\n\nfunction targetForKind(kind: MemoryRecord['kind']): 'memory' | 'user' {\n return kind === 'user_profile' ? 'user' : 'memory';\n}\n\nfunction parseLineAddress(id: string): { path: string; lineStart?: number; lineEnd?: number } {\n const match = /^(.*)#L(\\d+)(?:-L(\\d+))?$/.exec(id);\n if (!match) return { path: id };\n const lineStart = Number.parseInt(match[2] ?? '1', 10);\n const lineEnd = Number.parseInt(match[3] ?? match[2] ?? '1', 10);\n return {\n path: match[1] ?? id,\n lineStart: Number.isFinite(lineStart) ? lineStart : undefined,\n lineEnd: Number.isFinite(lineEnd) ? lineEnd : undefined,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"builtin-provider.js","names":[],"sources":["../../../../src/agent/memory/builtin-provider.ts"],"sourcesContent":["import type { BuiltinMemoryStore } from './builtin-memory-store.js';\nimport type { MemoryProvider, MemoryProviderInitOptions } from './provider.js';\nimport { memoryGet, memorySearch } from '../prompt/memory/index.js';\nimport {\n appendMemorySignal,\n deleteMemoryRecord,\n getMemoryRecord,\n listMemoryRecords,\n searchMemoryRecords,\n upsertMemoryRecord,\n} from '../../storage/sqlite/index.js';\nimport type {\n MemoryDeleteRequest,\n MemoryListRequest,\n MemoryReadRequest,\n MemoryReadResult,\n MemoryRecord,\n MemorySearchRequest,\n MemorySearchResult,\n MemoryUpdateRequest,\n MemoryWriteRequest,\n MemoryWriteResult,\n MemorySyncEvent,\n} from './types.js';\n\n/**\n * Builtin local memory provider backed by agent-home curated files, workspace MEMORY.md,\n * daily notes, and the local SQLite FTS index.\n */\nexport class BuiltinMemoryProvider implements MemoryProvider {\n readonly id = 'local';\n readonly displayName = 'Local Markdown Memory';\n readonly capabilities = {\n search: true,\n read: true,\n write: true,\n update: true,\n delete: true,\n keywordSearch: true,\n semanticSearch: false,\n hybridSearch: false,\n citations: true,\n sync: false,\n local: true,\n };\n\n private workspaceDir = '';\n private agentId = 'main';\n\n constructor(private readonly store: BuiltinMemoryStore) {}\n\n isAvailable(): boolean {\n return true;\n }\n\n async initialize(_sessionId: string, options?: MemoryProviderInitOptions): Promise<void> {\n this.workspaceDir = options?.workspace ?? options?.agentWorkspace ?? this.store.workspaceDir;\n this.agentId = String(options?.agentId ?? this.agentId);\n }\n\n systemPromptBlock(): string {\n return '';\n }\n\n async prefetch(): Promise<string> {\n return '';\n }\n\n queuePrefetch(): void {}\n\n getToolSchemas() {\n return [];\n }\n\n async search(request: MemorySearchRequest): Promise<MemorySearchResult[]> {\n const agentId = request.scope?.agentId ?? this.agentId;\n const workspaceId = request.scope?.workspaceId ?? this.workspaceDir;\n const recordHits = searchMemoryRecords({\n query: request.query,\n agentId,\n workspaceId,\n providerId: this.id,\n kinds: request.kinds,\n maxResults: request.maxResults,\n minScore: request.minScore,\n });\n if (recordHits.length > 0) {\n return recordHits;\n }\n\n const results = await memorySearch(this.workspaceDir, request.query, {\n maxResults: request.maxResults,\n minScore: request.minScore,\n memoriesDir: this.store.memoriesDir,\n userMemoryPath: this.store.userMemoryPath,\n agentId,\n });\n return results.map((entry) => {\n const start = entry.lineNumbers[0] ?? 1;\n const end = entry.lineNumbers[entry.lineNumbers.length - 1] ?? start;\n const id = `${entry.file}#L${start}-L${end}`;\n const kind = inferKindFromPath(entry.file);\n const record = upsertMemoryRecord({\n id,\n providerId: this.id,\n kind,\n agentId,\n workspaceId,\n content: entry.lines,\n source: {\n provider: this.id,\n path: entry.file,\n lineStart: start,\n lineEnd: end,\n },\n confidence: entry.score,\n });\n return {\n record,\n score: entry.score,\n snippet: entry.lines,\n citation: {\n providerId: this.id,\n recordId: id,\n path: entry.file,\n lineStart: start,\n lineEnd: end,\n },\n };\n });\n }\n\n async read(request: MemoryReadRequest): Promise<MemoryReadResult | null> {\n if (request.id && !request.path) {\n const existing = getMemoryRecord(request.id);\n if (existing) {\n return { record: existing };\n }\n }\n const result = memoryGet(\n this.workspaceDir,\n request.path ?? request.id ?? '',\n request.from,\n request.lines,\n this.store.memoriesDir,\n this.store.userMemoryPath,\n );\n if (!result) return null;\n const path = request.path ?? request.id ?? '';\n const record = upsertMemoryRecord({\n id: `${path}#L${result.lineNumbers.start}-L${result.lineNumbers.end}`,\n providerId: this.id,\n kind: inferKindFromPath(path),\n agentId: request.scope?.agentId ?? this.agentId,\n workspaceId: request.scope?.workspaceId ?? this.workspaceDir,\n content: result.content,\n source: {\n provider: this.id,\n path,\n lineStart: result.lineNumbers.start,\n lineEnd: result.lineNumbers.end,\n },\n });\n return {\n record,\n lineNumbers: result.lineNumbers,\n };\n }\n\n async get(id: string): Promise<MemoryRecord | null> {\n const parsed = parseLineAddress(id);\n const result = await this.read({\n path: parsed.path,\n from: parsed.lineStart,\n lines: parsed.lineEnd != null ? parsed.lineEnd - parsed.lineStart + 1 : undefined,\n });\n return result?.record ?? null;\n }\n\n async list(request: MemoryListRequest): Promise<MemoryRecord[]> {\n const records = listMemoryRecords({\n providerId: this.id,\n agentId: request.scope?.agentId ?? this.agentId,\n workspaceId: request.scope?.workspaceId ?? this.workspaceDir,\n kind: request.kind,\n limit: 200,\n });\n if (records.length > 0 || request.kind) {\n return request.target\n ? records.filter((record) => targetForKind(record.kind) === request.target)\n : records;\n }\n\n const target = request.target ?? (request.kind === 'user_profile' ? 'user' : 'memory');\n return this.store.getLiveEntries(target).map((content, index) =>\n upsertMemoryRecord({\n id: `curated:${target}:${index + 1}`,\n providerId: this.id,\n kind: target === 'user' ? 'user_profile' : 'agent_note',\n agentId: request.scope?.agentId ?? this.agentId,\n workspaceId: request.scope?.workspaceId ?? this.workspaceDir,\n content,\n source: {\n provider: this.id,\n path: sourcePathForTarget(target),\n },\n }),\n );\n }\n\n async write(request: MemoryWriteRequest): Promise<MemoryWriteResult> {\n const target = request.target ?? (request.kind === 'user_profile' ? 'user' : 'memory');\n if (request.status === 'candidate') {\n const record = upsertMemoryRecord({\n providerId: this.id,\n kind: target === 'user' ? 'user_profile' : request.kind,\n agentId: request.scope?.agentId ?? this.agentId,\n workspaceId: request.scope?.workspaceId ?? this.workspaceDir,\n sessionKey: request.scope?.sessionKey,\n content: request.content,\n source: {\n ...(request.source ?? {}),\n provider: this.id,\n path: sourcePathForTarget(target),\n },\n confidence: request.confidence,\n tags: request.tags,\n status: 'candidate',\n sensitivity: request.sensitivity,\n evidence: request.evidence,\n reviewAfter: request.reviewAfter,\n expiresAt: request.expiresAt,\n });\n return {\n success: true,\n message: 'Memory candidate added to inbox',\n record,\n };\n }\n const result = await this.store.add(target, request.content);\n if (!result.success) return { success: false, error: result.error };\n return {\n success: true,\n message: result.message,\n record: upsertMemoryRecord({\n id: `curated:${target}:${Date.now()}`,\n providerId: this.id,\n kind: target === 'user' ? 'user_profile' : request.kind,\n agentId: request.scope?.agentId ?? this.agentId,\n workspaceId: request.scope?.workspaceId ?? this.workspaceDir,\n sessionKey: request.scope?.sessionKey,\n content: request.content,\n source: {\n ...(request.source ?? {}),\n provider: this.id,\n path: sourcePathForTarget(target),\n },\n tags: request.tags,\n }),\n };\n }\n\n async update(request: MemoryUpdateRequest): Promise<MemoryWriteResult> {\n const target = request.target ?? 'memory';\n const result = await this.store.replace(target, request.matchText ?? request.id ?? '', request.content);\n if (!result.success) return { success: false, error: result.error };\n if (request.id) {\n const existing = getMemoryRecord(request.id);\n upsertMemoryRecord({\n id: request.id,\n providerId: this.id,\n kind: existing?.kind ?? (target === 'user' ? 'user_profile' : 'agent_note'),\n agentId: existing?.scope.agentId ?? request.scope?.agentId ?? this.agentId,\n workspaceId: existing?.scope.workspaceId ?? request.scope?.workspaceId ?? this.workspaceDir,\n sessionKey: existing?.scope.sessionKey ?? request.scope?.sessionKey,\n content: request.content,\n source: existing?.source ?? { provider: this.id, path: sourcePathForTarget(target) },\n tags: existing?.tags,\n });\n } else if (request.matchText) {\n for (const existing of listMemoryRecords({\n providerId: this.id,\n agentId: request.scope?.agentId ?? this.agentId,\n workspaceId: request.scope?.workspaceId ?? this.workspaceDir,\n kind: target === 'user' ? 'user_profile' : 'agent_note',\n limit: 200,\n })) {\n if (!existing.content.includes(request.matchText)) continue;\n upsertMemoryRecord({\n id: existing.id,\n providerId: this.id,\n kind: existing.kind,\n agentId: existing.scope.agentId,\n workspaceId: existing.scope.workspaceId,\n sessionKey: existing.scope.sessionKey,\n content: request.content,\n source: existing.source,\n tags: existing.tags,\n });\n }\n }\n return { success: true, message: result.message };\n }\n\n async delete(request: MemoryDeleteRequest): Promise<MemoryWriteResult> {\n const target = request.target ?? 'memory';\n const result = await this.store.remove(target, request.matchText ?? request.id ?? '');\n if (!result.success) return { success: false, error: result.error };\n if (request.id) {\n deleteMemoryRecord(request.id);\n } else if (request.matchText) {\n for (const existing of listMemoryRecords({\n providerId: this.id,\n agentId: request.scope?.agentId ?? this.agentId,\n workspaceId: request.scope?.workspaceId ?? this.workspaceDir,\n kind: target === 'user' ? 'user_profile' : 'agent_note',\n limit: 200,\n })) {\n if (existing.content.includes(request.matchText)) {\n deleteMemoryRecord(existing.id);\n }\n }\n }\n return { success: true, message: result.message };\n }\n\n sync(event: MemorySyncEvent): void {\n if (event.type !== 'signal') return;\n appendMemorySignal({\n signal: event.signal,\n providerId: this.id,\n agentId: this.agentId,\n workspaceId: this.workspaceDir,\n sessionKey:\n typeof event.signal.metadata?.sessionKey === 'string'\n ? event.signal.metadata.sessionKey\n : undefined,\n });\n }\n\n async shutdown(): Promise<void> {}\n}\n\nfunction inferKindFromPath(path: string): MemoryRecord['kind'] {\n const normalized = path.replace(/\\\\/g, '/');\n if (normalized === 'user/MEMORY.md' || normalized.endsWith('/user/MEMORY.md')) return 'user_profile';\n if (/^memory\\/\\d{4}-\\d{2}-\\d{2}\\.md$/.test(normalized)) return 'daily_note';\n if (normalized.endsWith('MEMORY.md')) return 'agent_note';\n return 'workspace_fact';\n}\n\nfunction targetForKind(kind: MemoryRecord['kind']): 'memory' | 'user' {\n return kind === 'user_profile' ? 'user' : 'memory';\n}\n\nfunction sourcePathForTarget(target: 'memory' | 'user'): string {\n return target === 'user' ? 'user/MEMORY.md' : 'MEMORY.md';\n}\n\nfunction parseLineAddress(id: string): { path: string; lineStart?: number; lineEnd?: number } {\n const match = /^(.*)#L(\\d+)(?:-L(\\d+))?$/.exec(id);\n if (!match) return { path: id };\n const lineStart = Number.parseInt(match[2] ?? '1', 10);\n const lineEnd = Number.parseInt(match[3] ?? match[2] ?? '1', 10);\n return {\n path: match[1] ?? id,\n lineStart: Number.isFinite(lineStart) ? lineStart : undefined,\n lineEnd: Number.isFinite(lineEnd) ? lineEnd : undefined,\n };\n}\n"],"mappings":";;;;;;;;AA6BA,IAAa,wBAAb,MAA6D;CAC3D,KAAc;CACd,cAAuB;CACvB,eAAwB;EACtB,QAAQ;EACR,MAAM;EACN,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,eAAe;EACf,gBAAgB;EAChB,cAAc;EACd,WAAW;EACX,MAAM;EACN,OAAO;EACR;CAED,eAAuB;CACvB,UAAkB;CAElB,YAAY,OAA4C;AAA3B,OAAA,QAAA;;CAE7B,cAAuB;AACrB,SAAO;;CAGT,MAAM,WAAW,YAAoB,SAAoD;AACvF,OAAK,eAAe,SAAS,aAAa,SAAS,kBAAkB,KAAK,MAAM;AAChF,OAAK,UAAU,OAAO,SAAS,WAAW,KAAK,QAAQ;;CAGzD,oBAA4B;AAC1B,SAAO;;CAGT,MAAM,WAA4B;AAChC,SAAO;;CAGT,gBAAsB;CAEtB,iBAAiB;AACf,SAAO,EAAE;;CAGX,MAAM,OAAO,SAA6D;EACxE,MAAM,UAAU,QAAQ,OAAO,WAAW,KAAK;EAC/C,MAAM,cAAc,QAAQ,OAAO,eAAe,KAAK;EACvD,MAAM,aAAa,oBAAoB;GACrC,OAAO,QAAQ;GACf;GACA;GACA,YAAY,KAAK;GACjB,OAAO,QAAQ;GACf,YAAY,QAAQ;GACpB,UAAU,QAAQ;GACnB,CAAC;AACF,MAAI,WAAW,SAAS,EACtB,QAAO;AAUT,UAAO,MAPe,aAAa,KAAK,cAAc,QAAQ,OAAO;GACnE,YAAY,QAAQ;GACpB,UAAU,QAAQ;GAClB,aAAa,KAAK,MAAM;GACxB,gBAAgB,KAAK,MAAM;GAC3B;GACD,CAAC,EACa,KAAK,UAAU;GAC5B,MAAM,QAAQ,MAAM,YAAY,MAAM;GACtC,MAAM,MAAM,MAAM,YAAY,MAAM,YAAY,SAAS,MAAM;GAC/D,MAAM,KAAK,GAAG,MAAM,KAAK,IAAI,MAAM,IAAI;GACvC,MAAM,OAAO,kBAAkB,MAAM,KAAK;AAgB1C,UAAO;IACL,QAhBa,mBAAmB;KAChC;KACA,YAAY,KAAK;KACjB;KACA;KACA;KACA,SAAS,MAAM;KACf,QAAQ;MACN,UAAU,KAAK;MACf,MAAM,MAAM;MACZ,WAAW;MACX,SAAS;MACV;KACD,YAAY,MAAM;KACnB,CAEO;IACN,OAAO,MAAM;IACb,SAAS,MAAM;IACf,UAAU;KACR,YAAY,KAAK;KACjB,UAAU;KACV,MAAM,MAAM;KACZ,WAAW;KACX,SAAS;KACV;IACF;IACD;;CAGJ,MAAM,KAAK,SAA8D;AACvE,MAAI,QAAQ,MAAM,CAAC,QAAQ,MAAM;GAC/B,MAAM,WAAW,gBAAgB,QAAQ,GAAG;AAC5C,OAAI,SACF,QAAO,EAAE,QAAQ,UAAU;;EAG/B,MAAM,SAAS,UACb,KAAK,cACL,QAAQ,QAAQ,QAAQ,MAAM,IAC9B,QAAQ,MACR,QAAQ,OACR,KAAK,MAAM,aACX,KAAK,MAAM,eACZ;AACD,MAAI,CAAC,OAAQ,QAAO;EACpB,MAAM,OAAO,QAAQ,QAAQ,QAAQ,MAAM;AAe3C,SAAO;GACL,QAfa,mBAAmB;IAChC,IAAI,GAAG,KAAK,IAAI,OAAO,YAAY,MAAM,IAAI,OAAO,YAAY;IAChE,YAAY,KAAK;IACjB,MAAM,kBAAkB,KAAK;IAC7B,SAAS,QAAQ,OAAO,WAAW,KAAK;IACxC,aAAa,QAAQ,OAAO,eAAe,KAAK;IAChD,SAAS,OAAO;IAChB,QAAQ;KACN,UAAU,KAAK;KACf;KACA,WAAW,OAAO,YAAY;KAC9B,SAAS,OAAO,YAAY;KAC7B;IACF,CAEO;GACN,aAAa,OAAO;GACrB;;CAGH,MAAM,IAAI,IAA0C;EAClD,MAAM,SAAS,iBAAiB,GAAG;AAMnC,UAAO,MALc,KAAK,KAAK;GAC7B,MAAM,OAAO;GACb,MAAM,OAAO;GACb,OAAO,OAAO,WAAW,OAAO,OAAO,UAAU,OAAO,YAAY,IAAI,KAAA;GACzE,CAAC,GACa,UAAU;;CAG3B,MAAM,KAAK,SAAqD;EAC9D,MAAM,UAAU,kBAAkB;GAChC,YAAY,KAAK;GACjB,SAAS,QAAQ,OAAO,WAAW,KAAK;GACxC,aAAa,QAAQ,OAAO,eAAe,KAAK;GAChD,MAAM,QAAQ;GACd,OAAO;GACR,CAAC;AACF,MAAI,QAAQ,SAAS,KAAK,QAAQ,KAChC,QAAO,QAAQ,SACX,QAAQ,QAAQ,WAAW,cAAc,OAAO,KAAK,KAAK,QAAQ,OAAO,GACzE;EAGN,MAAM,SAAS,QAAQ,WAAW,QAAQ,SAAS,iBAAiB,SAAS;AAC7E,SAAO,KAAK,MAAM,eAAe,OAAO,CAAC,KAAK,SAAS,UACrD,mBAAmB;GACjB,IAAI,WAAW,OAAO,GAAG,QAAQ;GACjC,YAAY,KAAK;GACjB,MAAM,WAAW,SAAS,iBAAiB;GAC3C,SAAS,QAAQ,OAAO,WAAW,KAAK;GACxC,aAAa,QAAQ,OAAO,eAAe,KAAK;GAChD;GACA,QAAQ;IACN,UAAU,KAAK;IACf,MAAM,oBAAoB,OAAO;IAClC;GACF,CAAC,CACH;;CAGH,MAAM,MAAM,SAAyD;EACnE,MAAM,SAAS,QAAQ,WAAW,QAAQ,SAAS,iBAAiB,SAAS;AAC7E,MAAI,QAAQ,WAAW,YAqBrB,QAAO;GACL,SAAS;GACT,SAAS;GACT,QAvBa,mBAAmB;IAChC,YAAY,KAAK;IACjB,MAAM,WAAW,SAAS,iBAAiB,QAAQ;IACnD,SAAS,QAAQ,OAAO,WAAW,KAAK;IACxC,aAAa,QAAQ,OAAO,eAAe,KAAK;IAChD,YAAY,QAAQ,OAAO;IAC3B,SAAS,QAAQ;IACjB,QAAQ;KACN,GAAI,QAAQ,UAAU,EAAE;KACxB,UAAU,KAAK;KACf,MAAM,oBAAoB,OAAO;KAClC;IACD,YAAY,QAAQ;IACpB,MAAM,QAAQ;IACd,QAAQ;IACR,aAAa,QAAQ;IACrB,UAAU,QAAQ;IAClB,aAAa,QAAQ;IACrB,WAAW,QAAQ;IACpB,CAIO;GACP;EAEH,MAAM,SAAS,MAAM,KAAK,MAAM,IAAI,QAAQ,QAAQ,QAAQ;AAC5D,MAAI,CAAC,OAAO,QAAS,QAAO;GAAE,SAAS;GAAO,OAAO,OAAO;GAAO;AACnE,SAAO;GACL,SAAS;GACT,SAAS,OAAO;GAChB,QAAQ,mBAAmB;IACzB,IAAI,WAAW,OAAO,GAAG,KAAK,KAAK;IACnC,YAAY,KAAK;IACjB,MAAM,WAAW,SAAS,iBAAiB,QAAQ;IACnD,SAAS,QAAQ,OAAO,WAAW,KAAK;IACxC,aAAa,QAAQ,OAAO,eAAe,KAAK;IAChD,YAAY,QAAQ,OAAO;IAC3B,SAAS,QAAQ;IACjB,QAAQ;KACN,GAAI,QAAQ,UAAU,EAAE;KACxB,UAAU,KAAK;KACf,MAAM,oBAAoB,OAAO;KAClC;IACD,MAAM,QAAQ;IACf,CAAC;GACH;;CAGH,MAAM,OAAO,SAA0D;EACrE,MAAM,SAAS,QAAQ,UAAU;EACjC,MAAM,SAAS,MAAM,KAAK,MAAM,QAAQ,QAAQ,QAAQ,aAAa,QAAQ,MAAM,IAAI,QAAQ,QAAQ;AACvG,MAAI,CAAC,OAAO,QAAS,QAAO;GAAE,SAAS;GAAO,OAAO,OAAO;GAAO;AACnE,MAAI,QAAQ,IAAI;GACd,MAAM,WAAW,gBAAgB,QAAQ,GAAG;AAC5C,sBAAmB;IACjB,IAAI,QAAQ;IACZ,YAAY,KAAK;IACjB,MAAM,UAAU,SAAS,WAAW,SAAS,iBAAiB;IAC9D,SAAS,UAAU,MAAM,WAAW,QAAQ,OAAO,WAAW,KAAK;IACnE,aAAa,UAAU,MAAM,eAAe,QAAQ,OAAO,eAAe,KAAK;IAC/E,YAAY,UAAU,MAAM,cAAc,QAAQ,OAAO;IACzD,SAAS,QAAQ;IACjB,QAAQ,UAAU,UAAU;KAAE,UAAU,KAAK;KAAI,MAAM,oBAAoB,OAAO;KAAE;IACpF,MAAM,UAAU;IACjB,CAAC;aACO,QAAQ,UACjB,MAAK,MAAM,YAAY,kBAAkB;GACvC,YAAY,KAAK;GACjB,SAAS,QAAQ,OAAO,WAAW,KAAK;GACxC,aAAa,QAAQ,OAAO,eAAe,KAAK;GAChD,MAAM,WAAW,SAAS,iBAAiB;GAC3C,OAAO;GACR,CAAC,EAAE;AACF,OAAI,CAAC,SAAS,QAAQ,SAAS,QAAQ,UAAU,CAAE;AACnD,sBAAmB;IACjB,IAAI,SAAS;IACb,YAAY,KAAK;IACjB,MAAM,SAAS;IACf,SAAS,SAAS,MAAM;IACxB,aAAa,SAAS,MAAM;IAC5B,YAAY,SAAS,MAAM;IAC3B,SAAS,QAAQ;IACjB,QAAQ,SAAS;IACjB,MAAM,SAAS;IAChB,CAAC;;AAGN,SAAO;GAAE,SAAS;GAAM,SAAS,OAAO;GAAS;;CAGnD,MAAM,OAAO,SAA0D;EACrE,MAAM,SAAS,QAAQ,UAAU;EACjC,MAAM,SAAS,MAAM,KAAK,MAAM,OAAO,QAAQ,QAAQ,aAAa,QAAQ,MAAM,GAAG;AACrF,MAAI,CAAC,OAAO,QAAS,QAAO;GAAE,SAAS;GAAO,OAAO,OAAO;GAAO;AACnE,MAAI,QAAQ,GACV,oBAAmB,QAAQ,GAAG;WACrB,QAAQ;QACZ,MAAM,YAAY,kBAAkB;IACvC,YAAY,KAAK;IACjB,SAAS,QAAQ,OAAO,WAAW,KAAK;IACxC,aAAa,QAAQ,OAAO,eAAe,KAAK;IAChD,MAAM,WAAW,SAAS,iBAAiB;IAC3C,OAAO;IACR,CAAC,CACA,KAAI,SAAS,QAAQ,SAAS,QAAQ,UAAU,CAC9C,oBAAmB,SAAS,GAAG;;AAIrC,SAAO;GAAE,SAAS;GAAM,SAAS,OAAO;GAAS;;CAGnD,KAAK,OAA8B;AACjC,MAAI,MAAM,SAAS,SAAU;AAC7B,qBAAmB;GACjB,QAAQ,MAAM;GACd,YAAY,KAAK;GACjB,SAAS,KAAK;GACd,aAAa,KAAK;GAClB,YACE,OAAO,MAAM,OAAO,UAAU,eAAe,WACzC,MAAM,OAAO,SAAS,aACtB,KAAA;GACP,CAAC;;CAGJ,MAAM,WAA0B;;AAGlC,SAAS,kBAAkB,MAAoC;CAC7D,MAAM,aAAa,KAAK,QAAQ,OAAO,IAAI;AAC3C,KAAI,eAAe,oBAAoB,WAAW,SAAS,kBAAkB,CAAE,QAAO;AACtF,KAAI,kCAAkC,KAAK,WAAW,CAAE,QAAO;AAC/D,KAAI,WAAW,SAAS,YAAY,CAAE,QAAO;AAC7C,QAAO;;AAGT,SAAS,cAAc,MAA+C;AACpE,QAAO,SAAS,iBAAiB,SAAS;;AAG5C,SAAS,oBAAoB,QAAmC;AAC9D,QAAO,WAAW,SAAS,mBAAmB;;AAGhD,SAAS,iBAAiB,IAAoE;CAC5F,MAAM,QAAQ,4BAA4B,KAAK,GAAG;AAClD,KAAI,CAAC,MAAO,QAAO,EAAE,MAAM,IAAI;CAC/B,MAAM,YAAY,OAAO,SAAS,MAAM,MAAM,KAAK,GAAG;CACtD,MAAM,UAAU,OAAO,SAAS,MAAM,MAAM,MAAM,MAAM,KAAK,GAAG;AAChE,QAAO;EACL,MAAM,MAAM,MAAM;EAClB,WAAW,OAAO,SAAS,UAAU,GAAG,YAAY,KAAA;EACpD,SAAS,OAAO,SAAS,QAAQ,GAAG,UAAU,KAAA;EAC/C"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
//#region src/agent/memory/candidate-extractor.ts
|
|
2
|
+
const MAX_CANDIDATE_CHARS = 600;
|
|
3
|
+
const EXPLICIT_PATTERNS = [
|
|
4
|
+
/\b(?:please\s+)?remember(?:\s+that)?\s+(.+)/i,
|
|
5
|
+
/\bkeep\s+in\s+mind(?:\s+that)?\s+(.+)/i,
|
|
6
|
+
/(?:请)?记住[::\s]*(.+)/,
|
|
7
|
+
/以后(?:都)?(?:请)?[::\s]*(.+)/,
|
|
8
|
+
/下次(?:请)?[::\s]*(.+)/
|
|
9
|
+
];
|
|
10
|
+
function cleanCandidateContent(raw) {
|
|
11
|
+
return raw.replace(/^["'“”‘’\s]+|["'“”‘’\s]+$/g, "").replace(/\s+/g, " ").trim().slice(0, MAX_CANDIDATE_CHARS);
|
|
12
|
+
}
|
|
13
|
+
function inferKind(content) {
|
|
14
|
+
const lower = content.toLowerCase();
|
|
15
|
+
if (/\b(prefer|preference|like)\b/i.test(content) || /喜欢|偏好|习惯/.test(content)) return "user_profile";
|
|
16
|
+
if (/\b(tool|command|pnpm|npm|git|ripgrep|rg)\b/i.test(content) || /工具|命令/.test(content)) return "tool_preference";
|
|
17
|
+
if (/\b(project|repo|codebase|workspace)\b/i.test(content) || /项目|仓库|代码库/.test(content)) return "workspace_fact";
|
|
18
|
+
if (/\b(next time|lesson|avoid|failed|error)\b/i.test(lower) || /下次|教训|失败|错误|不要/.test(content)) return "task_lesson";
|
|
19
|
+
return "agent_note";
|
|
20
|
+
}
|
|
21
|
+
function inferSensitivity(kind, content) {
|
|
22
|
+
if (/\b(api[_-]?key|token|password|secret|credential)\b/i.test(content) || /密钥|密码|令牌/.test(content)) return "secret";
|
|
23
|
+
if (kind === "user_profile") return "personal";
|
|
24
|
+
return "normal";
|
|
25
|
+
}
|
|
26
|
+
function extractExplicitContent(userContent) {
|
|
27
|
+
const lines = userContent.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
28
|
+
for (const line of lines.length > 0 ? lines : [userContent.trim()]) for (const pattern of EXPLICIT_PATTERNS) {
|
|
29
|
+
const content = cleanCandidateContent(pattern.exec(line)?.[1] ?? "");
|
|
30
|
+
if (content.length >= 8) return content;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
function proposeMemoryCandidatesFromTurn(params) {
|
|
35
|
+
const content = extractExplicitContent(params.userContent);
|
|
36
|
+
if (!content) return [];
|
|
37
|
+
const kind = inferKind(content);
|
|
38
|
+
const evidence = [{
|
|
39
|
+
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
|
|
40
|
+
sourceText: params.userContent.trim().slice(0, MAX_CANDIDATE_CHARS)
|
|
41
|
+
}];
|
|
42
|
+
return [{
|
|
43
|
+
kind,
|
|
44
|
+
target: kind === "user_profile" ? "user" : "memory",
|
|
45
|
+
content,
|
|
46
|
+
status: "candidate",
|
|
47
|
+
sensitivity: inferSensitivity(kind, content),
|
|
48
|
+
confidence: .72,
|
|
49
|
+
evidence,
|
|
50
|
+
tags: ["auto-proposed", "explicit-user-request"],
|
|
51
|
+
source: {
|
|
52
|
+
provider: "turn-sync",
|
|
53
|
+
...params.sessionKey ? { sessionEntryId: params.sessionKey } : {}
|
|
54
|
+
}
|
|
55
|
+
}];
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { proposeMemoryCandidatesFromTurn };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=candidate-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"candidate-extractor.js","names":[],"sources":["../../../../src/agent/memory/candidate-extractor.ts"],"sourcesContent":["import type { MemoryEvidence, MemoryKind, MemorySensitivity, MemoryWriteRequest } from './types.js';\n\nconst MAX_CANDIDATE_CHARS = 600;\n\nconst EXPLICIT_PATTERNS: RegExp[] = [\n /\\b(?:please\\s+)?remember(?:\\s+that)?\\s+(.+)/i,\n /\\bkeep\\s+in\\s+mind(?:\\s+that)?\\s+(.+)/i,\n /(?:请)?记住[::\\s]*(.+)/,\n /以后(?:都)?(?:请)?[::\\s]*(.+)/,\n /下次(?:请)?[::\\s]*(.+)/,\n];\n\nfunction cleanCandidateContent(raw: string): string {\n return raw\n .replace(/^[\"'“”‘’\\s]+|[\"'“”‘’\\s]+$/g, '')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, MAX_CANDIDATE_CHARS);\n}\n\nfunction inferKind(content: string): MemoryKind {\n const lower = content.toLowerCase();\n if (/\\b(prefer|preference|like)\\b/i.test(content) || /喜欢|偏好|习惯/.test(content)) {\n return 'user_profile';\n }\n if (/\\b(tool|command|pnpm|npm|git|ripgrep|rg)\\b/i.test(content) || /工具|命令/.test(content)) {\n return 'tool_preference';\n }\n if (/\\b(project|repo|codebase|workspace)\\b/i.test(content) || /项目|仓库|代码库/.test(content)) {\n return 'workspace_fact';\n }\n if (/\\b(next time|lesson|avoid|failed|error)\\b/i.test(lower) || /下次|教训|失败|错误|不要/.test(content)) {\n return 'task_lesson';\n }\n return 'agent_note';\n}\n\nfunction inferSensitivity(kind: MemoryKind, content: string): MemorySensitivity {\n if (/\\b(api[_-]?key|token|password|secret|credential)\\b/i.test(content) || /密钥|密码|令牌/.test(content)) {\n return 'secret';\n }\n if (kind === 'user_profile') {\n return 'personal';\n }\n return 'normal';\n}\n\nfunction extractExplicitContent(userContent: string): string | null {\n const lines = userContent\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter(Boolean);\n for (const line of lines.length > 0 ? lines : [userContent.trim()]) {\n for (const pattern of EXPLICIT_PATTERNS) {\n const match = pattern.exec(line);\n const content = cleanCandidateContent(match?.[1] ?? '');\n if (content.length >= 8) {\n return content;\n }\n }\n }\n return null;\n}\n\nexport function proposeMemoryCandidatesFromTurn(params: {\n userContent: string;\n assistantContent?: string;\n sessionKey?: string;\n}): MemoryWriteRequest[] {\n const content = extractExplicitContent(params.userContent);\n if (!content) {\n return [];\n }\n const kind = inferKind(content);\n const evidence: MemoryEvidence[] = [\n {\n ...(params.sessionKey ? { sessionKey: params.sessionKey } : {}),\n sourceText: params.userContent.trim().slice(0, MAX_CANDIDATE_CHARS),\n },\n ];\n return [\n {\n kind,\n target: kind === 'user_profile' ? 'user' : 'memory',\n content,\n status: 'candidate',\n sensitivity: inferSensitivity(kind, content),\n confidence: 0.72,\n evidence,\n tags: ['auto-proposed', 'explicit-user-request'],\n source: {\n provider: 'turn-sync',\n ...(params.sessionKey ? { sessionEntryId: params.sessionKey } : {}),\n },\n },\n ];\n}\n"],"mappings":";AAEA,MAAM,sBAAsB;AAE5B,MAAM,oBAA8B;CAClC;CACA;CACA;CACA;CACA;CACD;AAED,SAAS,sBAAsB,KAAqB;AAClD,QAAO,IACJ,QAAQ,8BAA8B,GAAG,CACzC,QAAQ,QAAQ,IAAI,CACpB,MAAM,CACN,MAAM,GAAG,oBAAoB;;AAGlC,SAAS,UAAU,SAA6B;CAC9C,MAAM,QAAQ,QAAQ,aAAa;AACnC,KAAI,gCAAgC,KAAK,QAAQ,IAAI,WAAW,KAAK,QAAQ,CAC3E,QAAO;AAET,KAAI,8CAA8C,KAAK,QAAQ,IAAI,QAAQ,KAAK,QAAQ,CACtF,QAAO;AAET,KAAI,yCAAyC,KAAK,QAAQ,IAAI,YAAY,KAAK,QAAQ,CACrF,QAAO;AAET,KAAI,6CAA6C,KAAK,MAAM,IAAI,iBAAiB,KAAK,QAAQ,CAC5F,QAAO;AAET,QAAO;;AAGT,SAAS,iBAAiB,MAAkB,SAAoC;AAC9E,KAAI,sDAAsD,KAAK,QAAQ,IAAI,WAAW,KAAK,QAAQ,CACjG,QAAO;AAET,KAAI,SAAS,eACX,QAAO;AAET,QAAO;;AAGT,SAAS,uBAAuB,aAAoC;CAClE,MAAM,QAAQ,YACX,MAAM,QAAQ,CACd,KAAK,SAAS,KAAK,MAAM,CAAC,CAC1B,OAAO,QAAQ;AAClB,MAAK,MAAM,QAAQ,MAAM,SAAS,IAAI,QAAQ,CAAC,YAAY,MAAM,CAAC,CAChE,MAAK,MAAM,WAAW,mBAAmB;EAEvC,MAAM,UAAU,sBADF,QAAQ,KAAK,KACgB,GAAG,MAAM,GAAG;AACvD,MAAI,QAAQ,UAAU,EACpB,QAAO;;AAIb,QAAO;;AAGT,SAAgB,gCAAgC,QAIvB;CACvB,MAAM,UAAU,uBAAuB,OAAO,YAAY;AAC1D,KAAI,CAAC,QACH,QAAO,EAAE;CAEX,MAAM,OAAO,UAAU,QAAQ;CAC/B,MAAM,WAA6B,CACjC;EACE,GAAI,OAAO,aAAa,EAAE,YAAY,OAAO,YAAY,GAAG,EAAE;EAC9D,YAAY,OAAO,YAAY,MAAM,CAAC,MAAM,GAAG,oBAAoB;EACpE,CACF;AACD,QAAO,CACL;EACE;EACA,QAAQ,SAAS,iBAAiB,SAAS;EAC3C;EACA,QAAQ;EACR,aAAa,iBAAiB,MAAM,QAAQ;EAC5C,YAAY;EACZ;EACA,MAAM,CAAC,iBAAiB,wBAAwB;EAChD,QAAQ;GACN,UAAU;GACV,GAAI,OAAO,aAAa,EAAE,gBAAgB,OAAO,YAAY,GAAG,EAAE;GACnE;EACF,CACF"}
|
|
@@ -40,7 +40,8 @@ export declare class MemoryManager {
|
|
|
40
40
|
}): void;
|
|
41
41
|
syncAll(userContent: string, assistantContent: string, options?: {
|
|
42
42
|
sessionId?: string;
|
|
43
|
-
}): void
|
|
43
|
+
}): Promise<void>;
|
|
44
|
+
private proposeCandidatesFromTurn;
|
|
44
45
|
getAdditionalTools(): AgentTool[];
|
|
45
46
|
handleToolCall(toolName: string, args: Record<string, unknown>): Promise<string>;
|
|
46
47
|
search(request: MemorySearchRequest): Promise<MemorySearchResult[]>;
|
|
@@ -2,6 +2,7 @@ import { createLogger } from "../../utils/logger/index.js";
|
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
3
|
import { appendMemoryTraceEvent } from "../../storage/sqlite/memory-records-repository.js";
|
|
4
4
|
import "../../storage/sqlite/index.js";
|
|
5
|
+
import { proposeMemoryCandidatesFromTurn } from "./candidate-extractor.js";
|
|
5
6
|
//#region src/agent/memory/manager.ts
|
|
6
7
|
init_logger();
|
|
7
8
|
const log = createLogger("MemoryManager");
|
|
@@ -89,17 +90,18 @@ var MemoryManager = class {
|
|
|
89
90
|
}, "queuePrefetch failed (non-fatal)");
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
|
-
syncAll(userContent, assistantContent, options) {
|
|
93
|
+
async syncAll(userContent, assistantContent, options) {
|
|
93
94
|
const event = {
|
|
94
95
|
type: "turn",
|
|
95
96
|
userContent,
|
|
96
97
|
assistantContent,
|
|
97
98
|
sessionId: options?.sessionId
|
|
98
99
|
};
|
|
100
|
+
await this.proposeCandidatesFromTurn(event);
|
|
99
101
|
for (const p of this.providers) {
|
|
100
102
|
const started = Date.now();
|
|
101
103
|
try {
|
|
102
|
-
p.sync?.(event);
|
|
104
|
+
await p.sync?.(event);
|
|
103
105
|
this.trace("sync", p.id, event, {
|
|
104
106
|
resultCount: 1,
|
|
105
107
|
durationMs: Date.now() - started,
|
|
@@ -118,6 +120,27 @@ var MemoryManager = class {
|
|
|
118
120
|
}
|
|
119
121
|
}
|
|
120
122
|
}
|
|
123
|
+
async proposeCandidatesFromTurn(event) {
|
|
124
|
+
const candidates = proposeMemoryCandidatesFromTurn({
|
|
125
|
+
userContent: event.userContent,
|
|
126
|
+
assistantContent: event.assistantContent,
|
|
127
|
+
sessionKey: event.sessionId
|
|
128
|
+
});
|
|
129
|
+
for (const candidate of candidates) try {
|
|
130
|
+
await this.write({
|
|
131
|
+
...candidate,
|
|
132
|
+
scope: {
|
|
133
|
+
...candidate.scope ?? {},
|
|
134
|
+
...event.sessionId ? { sessionKey: event.sessionId } : {}
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
} catch (err) {
|
|
138
|
+
log.debug({
|
|
139
|
+
err,
|
|
140
|
+
sessionKey: event.sessionId
|
|
141
|
+
}, "memory candidate proposal failed");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
121
144
|
getAdditionalTools() {
|
|
122
145
|
const out = [];
|
|
123
146
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -355,7 +378,7 @@ var MemoryManager = class {
|
|
|
355
378
|
}
|
|
356
379
|
}
|
|
357
380
|
async writeWithStrategy(operation, request) {
|
|
358
|
-
const providers = this.providersForWrite();
|
|
381
|
+
const providers = operation === "write" && request.status === "candidate" ? this.providers.filter((provider) => provider.capabilities.local) : this.providersForWrite();
|
|
359
382
|
const errors = [];
|
|
360
383
|
let firstSuccess = null;
|
|
361
384
|
for (const p of providers) {
|
|
@@ -408,6 +431,7 @@ var MemoryManager = class {
|
|
|
408
431
|
if (!provider.capabilities.local && this.writePolicy.allowedProviderIds && this.writePolicy.allowedProviderIds.length > 0 && !this.writePolicy.allowedProviderIds.includes(provider.id)) return false;
|
|
409
432
|
if (operation === "write") {
|
|
410
433
|
const writeRequest = request;
|
|
434
|
+
if (writeRequest.status === "candidate" && !provider.capabilities.local) return false;
|
|
411
435
|
if (this.writePolicy.autoWriteKinds && this.writePolicy.autoWriteKinds.length > 0 && !this.writePolicy.autoWriteKinds.includes(writeRequest.kind)) return false;
|
|
412
436
|
if (this.writePolicy.requireUserProfileApproval && writeRequest.kind === "user_profile" && !writeRequest.approved) return false;
|
|
413
437
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.js","names":[],"sources":["../../../../src/agent/memory/manager.ts"],"sourcesContent":["import type { AgentTool } from '@earendil-works/pi-agent-core';\n\nimport { appendMemoryTraceEvent } from '../../storage/sqlite/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { MemoryProvider, MemoryProviderInitOptions } from './provider.js';\nimport type {\n MemoryDeleteRequest,\n MemoryListRequest,\n MemoryReadRequest,\n MemoryReadResult,\n MemorySearchRequest,\n MemorySearchResult,\n MemorySyncEvent,\n MemoryUpdateRequest,\n MemoryWriteRequest,\n MemoryWriteResult,\n MemoryRecord,\n MemorySignal,\n MemoryKind,\n} from './types.js';\n\nconst log = createLogger('MemoryManager');\n\nexport type MemorySearchStrategy = 'local-first' | 'external-first' | 'fanout' | 'local-only' | 'external-only';\nexport type MemoryWriteStrategy = 'local-first' | 'external-first' | 'write-through' | 'local-only' | 'external-only';\n\nexport interface MemoryRoutingOptions {\n searchStrategy?: MemorySearchStrategy;\n writeStrategy?: MemoryWriteStrategy;\n replicateTo?: string[];\n}\n\nexport interface MemoryManagerOptions extends MemoryRoutingOptions {\n loadProviders?: () => Promise<MemoryProvider[]>;\n writePolicy?: MemoryWritePolicy;\n}\n\nexport interface MemoryWritePolicy {\n allowExternalWrites?: boolean;\n allowedProviderIds?: string[];\n autoWriteKinds?: MemoryKind[];\n requireUserProfileApproval?: boolean;\n}\n\nexport class MemoryManager {\n private providers: MemoryProvider[] = [];\n private toolToProvider = new Map<string, MemoryProvider>();\n private readonly routing: Required<MemoryRoutingOptions>;\n private readonly loadProviders?: () => Promise<MemoryProvider[]>;\n private readonly writePolicy: Required<Pick<MemoryWritePolicy, 'allowExternalWrites' | 'requireUserProfileApproval'>> &\n Omit<MemoryWritePolicy, 'allowExternalWrites' | 'requireUserProfileApproval'>;\n private pluginProvidersLoaded = false;\n\n constructor(options: MemoryManagerOptions = {}) {\n this.routing = {\n searchStrategy: options.searchStrategy ?? 'fanout',\n writeStrategy: options.writeStrategy ?? 'local-first',\n replicateTo: options.replicateTo ?? [],\n };\n this.loadProviders = options.loadProviders;\n this.writePolicy = {\n allowExternalWrites: options.writePolicy?.allowExternalWrites ?? false,\n requireUserProfileApproval: options.writePolicy?.requireUserProfileApproval ?? false,\n allowedProviderIds: options.writePolicy?.allowedProviderIds,\n autoWriteKinds: options.writePolicy?.autoWriteKinds,\n };\n }\n\n addProvider(provider: MemoryProvider): void {\n this.providers.push(provider);\n\n for (const schema of provider.getToolSchemas?.() ?? []) {\n const toolName = schema.name;\n if (!toolName) {\n continue;\n }\n if (!this.toolToProvider.has(toolName)) {\n this.toolToProvider.set(toolName, provider);\n } else {\n log.warn(\n { toolName, existing: this.toolToProvider.get(toolName)?.id, ignored: provider.id },\n 'Memory tool name conflict',\n );\n }\n }\n\n log.info(\n { id: provider.id, toolCount: provider.getToolSchemas?.().length ?? 0, capabilities: provider.capabilities },\n 'Memory provider registered',\n );\n }\n\n get providersList(): MemoryProvider[] {\n return [...this.providers];\n }\n\n /**\n * Non-builtin static instructions (builtin uses curated snapshot in system prompt builder).\n */\n buildExternalSystemPrompt(): string {\n const blocks: string[] = [];\n for (const p of this.providers) {\n if (p.capabilities.local) {\n continue;\n }\n try {\n const block = p.systemPromptBlock?.();\n if (block?.trim()) {\n blocks.push(block.trim());\n }\n } catch (err) {\n log.warn({ err, id: p.id }, 'systemPromptBlock failed');\n }\n }\n return blocks.join('\\n\\n');\n }\n\n async prefetchAll(query: string, options?: { sessionId?: string }): Promise<string> {\n const parts: string[] = [];\n for (const p of this.providers) {\n try {\n const v = await p.prefetch?.(query, options);\n if (v?.trim()) {\n parts.push(v.trim());\n }\n } catch (err) {\n log.debug({ err, id: p.id }, 'prefetch failed (non-fatal)');\n }\n }\n return parts.join('\\n\\n');\n }\n\n queuePrefetchAll(query: string, options?: { sessionId?: string }): void {\n for (const p of this.providers) {\n try {\n p.queuePrefetch?.(query, options);\n } catch (err) {\n log.debug({ err, id: p.id }, 'queuePrefetch failed (non-fatal)');\n }\n }\n }\n\n syncAll(userContent: string, assistantContent: string, options?: { sessionId?: string }): void {\n const event: MemorySyncEvent = {\n type: 'turn',\n userContent,\n assistantContent,\n sessionId: options?.sessionId,\n };\n for (const p of this.providers) {\n const started = Date.now();\n try {\n p.sync?.(event);\n this.trace('sync', p.id, event, {\n resultCount: 1,\n durationMs: Date.now() - started,\n sessionKey: options?.sessionId,\n });\n } catch (err) {\n this.trace('sync', p.id, event, {\n error: err instanceof Error ? err.message : String(err),\n durationMs: Date.now() - started,\n sessionKey: options?.sessionId,\n });\n log.warn({ err, id: p.id }, 'memory sync failed');\n }\n }\n }\n\n getAdditionalTools(): AgentTool[] {\n const out: AgentTool[] = [];\n const seen = new Set<string>();\n for (const p of this.providers) {\n if (p.capabilities.local) {\n continue;\n }\n try {\n for (const t of p.getToolSchemas?.() ?? []) {\n if (t.name && !seen.has(t.name)) {\n seen.add(t.name);\n out.push(t);\n }\n }\n } catch (err) {\n log.warn({ err, id: p.id }, 'getToolSchemas failed');\n }\n }\n return out;\n }\n\n async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<string> {\n const provider = this.toolToProvider.get(toolName);\n if (!provider) {\n throw new Error(`No memory provider handles tool '${toolName}'`);\n }\n if (!provider.handleToolCall) {\n throw new Error(`Memory provider '${provider.id}' does not handle tool calls`);\n }\n return provider.handleToolCall(toolName, args);\n }\n\n async search(request: MemorySearchRequest): Promise<MemorySearchResult[]> {\n const providers = this.providersForSearch();\n const results: MemorySearchResult[] = [];\n\n for (const p of providers) {\n if (!p.capabilities.search || !p.search) continue;\n const started = Date.now();\n try {\n const providerResults = await p.search(request);\n this.trace('search', p.id, request, {\n resultCount: providerResults.length,\n selectedRecordIds: providerResults.map((result) => result.record.id),\n durationMs: Date.now() - started,\n sessionKey: request.scope?.sessionKey,\n });\n results.push(...providerResults);\n if (providerResults.length > 0 && this.routing.searchStrategy !== 'fanout') {\n break;\n }\n } catch (err) {\n this.trace('search', p.id, request, {\n error: err instanceof Error ? err.message : String(err),\n durationMs: Date.now() - started,\n sessionKey: request.scope?.sessionKey,\n });\n log.warn({ err, id: p.id }, 'memory search failed');\n }\n }\n\n const seen = new Set<string>();\n return results\n .sort((a, b) => b.score - a.score)\n .filter((result) => {\n const key = `${result.citation.providerId}:${result.citation.recordId}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n })\n .slice(0, request.maxResults ?? 5);\n }\n\n async read(request: MemoryReadRequest): Promise<MemoryReadResult | null> {\n for (const p of this.providersForSearch()) {\n if (!p.capabilities.read || !p.read) continue;\n const started = Date.now();\n try {\n const result = await p.read(request);\n this.trace('read', p.id, request, {\n resultCount: result ? 1 : 0,\n selectedRecordIds: result ? [result.record.id] : [],\n durationMs: Date.now() - started,\n sessionKey: request.scope?.sessionKey,\n });\n if (result) return result;\n } catch (err) {\n this.trace('read', p.id, request, {\n error: err instanceof Error ? err.message : String(err),\n durationMs: Date.now() - started,\n sessionKey: request.scope?.sessionKey,\n });\n log.warn({ err, id: p.id }, 'memory read failed');\n }\n }\n return null;\n }\n\n async get(id: string): Promise<MemoryRecord | null> {\n for (const p of this.providers) {\n if (!p.capabilities.read) continue;\n try {\n const direct = await p.get?.(id);\n if (direct) return direct;\n } catch (err) {\n log.warn({ err, id: p.id, recordId: id }, 'memory get failed');\n }\n }\n return null;\n }\n\n async list(request: MemoryListRequest): Promise<MemoryRecord[]> {\n const out: MemoryRecord[] = [];\n for (const p of this.providersForSearch()) {\n if (!p.capabilities.read || !p.list) continue;\n try {\n out.push(...await p.list(request));\n } catch (err) {\n log.warn({ err, id: p.id }, 'memory list failed');\n }\n }\n return out;\n }\n\n async write(request: MemoryWriteRequest): Promise<MemoryWriteResult> {\n return this.writeWithStrategy('write', request);\n }\n\n async update(request: MemoryUpdateRequest): Promise<MemoryWriteResult> {\n return this.writeWithStrategy('update', request);\n }\n\n async delete(request: MemoryDeleteRequest): Promise<MemoryWriteResult> {\n return this.writeWithStrategy('delete', request);\n }\n\n onMemoryWrite(action: 'add' | 'replace' | 'remove', target: 'memory' | 'user', content: string): void {\n for (const p of this.providers) {\n if (p.capabilities.local) {\n continue;\n }\n try {\n p.sync?.({ type: 'write', action, target, content });\n } catch (err) {\n log.debug({ err, id: p.id }, 'memory write sync failed');\n }\n }\n }\n\n recordSignal(signal: MemorySignal): void {\n for (const p of this.providers) {\n const started = Date.now();\n try {\n p.sync?.({ type: 'signal', signal });\n this.trace('sync', p.id, { type: 'signal', signal }, {\n resultCount: 1,\n durationMs: Date.now() - started,\n });\n } catch (err) {\n this.trace('sync', p.id, { type: 'signal', signal }, {\n error: err instanceof Error ? err.message : String(err),\n durationMs: Date.now() - started,\n });\n log.debug({ err, id: p.id, source: signal.source }, 'memory signal sync failed');\n }\n }\n }\n\n async initializeAll(sessionId: string, options?: MemoryProviderInitOptions): Promise<void> {\n await this.loadPluginProvidersOnce();\n for (const p of this.providers) {\n try {\n await p.initialize(sessionId, options);\n } catch (err) {\n log.warn({ err, id: p.id }, 'initialize failed');\n }\n }\n }\n\n private async loadPluginProvidersOnce(): Promise<void> {\n if (this.pluginProvidersLoaded || !this.loadProviders) return;\n this.pluginProvidersLoaded = true;\n try {\n const providers = await this.loadProviders();\n const existingIds = new Set(this.providers.map((p) => p.id));\n for (const provider of providers) {\n if (existingIds.has(provider.id)) {\n log.warn({ id: provider.id }, 'Skipped duplicate memory provider');\n continue;\n }\n this.addProvider(provider);\n existingIds.add(provider.id);\n }\n } catch (err) {\n log.warn({ err }, 'Memory provider plugin loading failed');\n }\n }\n\n async shutdownAll(): Promise<void> {\n for (const p of [...this.providers].reverse()) {\n try {\n await p.shutdown();\n } catch (err) {\n log.warn({ err, id: p.id }, 'shutdown failed');\n }\n }\n }\n\n private providersForSearch(): MemoryProvider[] {\n const locals = this.providers.filter((p) => p.capabilities.local);\n const externals = this.providers.filter((p) => !p.capabilities.local);\n switch (this.routing.searchStrategy) {\n case 'local-only':\n return locals;\n case 'external-only':\n return externals;\n case 'external-first':\n return [...externals, ...locals];\n case 'local-first':\n case 'fanout':\n default:\n return [...locals, ...externals];\n }\n }\n\n private providersForWrite(): MemoryProvider[] {\n const locals = this.providers.filter((p) => p.capabilities.local);\n const externals = this.providers.filter((p) => !p.capabilities.local);\n switch (this.routing.writeStrategy) {\n case 'local-only':\n return locals;\n case 'external-only':\n return externals;\n case 'external-first':\n return [...externals, ...locals];\n case 'write-through':\n case 'local-first':\n default:\n return [...locals, ...externals];\n }\n }\n\n private async writeWithStrategy(\n operation: 'write' | 'update' | 'delete',\n request: MemoryWriteRequest | MemoryUpdateRequest | MemoryDeleteRequest,\n ): Promise<MemoryWriteResult> {\n const providers = this.providersForWrite();\n const errors: string[] = [];\n let firstSuccess: MemoryWriteResult | null = null;\n\n for (const p of providers) {\n const fn = p[operation];\n if (!this.providerAllowedForWrite(p, operation, request)) {\n this.trace(operation, p.id, request, {\n skippedReason: 'policy',\n sessionKey: request.scope?.sessionKey,\n });\n continue;\n }\n const canWrite =\n (operation === 'write' && p.capabilities.write) ||\n (operation === 'update' && p.capabilities.update) ||\n (operation === 'delete' && p.capabilities.delete);\n if (!canWrite || !fn) continue;\n const started = Date.now();\n try {\n const result = await fn.call(p, request as never);\n this.trace(operation, p.id, request, {\n resultCount: result.record ? 1 : 0,\n selectedRecordIds: result.record ? [result.record.id] : [],\n error: result.success ? undefined : result.error,\n durationMs: Date.now() - started,\n sessionKey: request.scope?.sessionKey,\n });\n if (result.success) {\n firstSuccess ??= result;\n if (this.routing.writeStrategy !== 'write-through') {\n return result;\n }\n } else if (result.error) {\n errors.push(`${p.id}: ${result.error}`);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n this.trace(operation, p.id, request, {\n error: message,\n durationMs: Date.now() - started,\n sessionKey: request.scope?.sessionKey,\n });\n errors.push(`${p.id}: ${message}`);\n log.warn({ err, id: p.id, operation }, 'memory write operation failed');\n }\n }\n\n if (firstSuccess) return firstSuccess;\n return { success: false, error: errors.join('; ') || `No provider supports memory ${operation}` };\n }\n\n private providerAllowedForWrite(\n provider: MemoryProvider,\n operation: 'write' | 'update' | 'delete',\n request: MemoryWriteRequest | MemoryUpdateRequest | MemoryDeleteRequest,\n ): boolean {\n if (!provider.capabilities.local && !this.writePolicy.allowExternalWrites) {\n return false;\n }\n if (\n !provider.capabilities.local &&\n this.writePolicy.allowedProviderIds &&\n this.writePolicy.allowedProviderIds.length > 0 &&\n !this.writePolicy.allowedProviderIds.includes(provider.id)\n ) {\n return false;\n }\n if (operation === 'write') {\n const writeRequest = request as MemoryWriteRequest;\n if (\n this.writePolicy.autoWriteKinds &&\n this.writePolicy.autoWriteKinds.length > 0 &&\n !this.writePolicy.autoWriteKinds.includes(writeRequest.kind)\n ) {\n return false;\n }\n if (\n this.writePolicy.requireUserProfileApproval &&\n writeRequest.kind === 'user_profile' &&\n !writeRequest.approved\n ) {\n return false;\n }\n }\n if (operation === 'update' && this.writePolicy.requireUserProfileApproval) {\n const updateRequest = request as MemoryUpdateRequest;\n if (updateRequest.target === 'user' && !updateRequest.approved) {\n return false;\n }\n }\n return true;\n }\n\n private trace(\n phase: 'search' | 'read' | 'write' | 'update' | 'delete' | 'sync' | 'inject' | 'test',\n providerId: string,\n request: unknown,\n meta: {\n sessionKey?: string;\n resultCount?: number;\n selectedRecordIds?: string[];\n skippedReason?: string;\n error?: string;\n durationMs?: number;\n } = {},\n ): void {\n try {\n appendMemoryTraceEvent({\n phase,\n providerId,\n request: sanitizeTraceRequest(request),\n ...meta,\n });\n } catch (err) {\n log.debug({ err, providerId, phase }, 'memory trace append failed');\n }\n }\n}\n\nfunction sanitizeTraceRequest(request: unknown): unknown {\n if (!request || typeof request !== 'object') return request;\n const copy = { ...(request as Record<string, unknown>) };\n if (typeof copy.content === 'string' && copy.content.length > 500) {\n copy.content = `${copy.content.slice(0, 500)}…`;\n }\n return copy;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"manager.js","names":[],"sources":["../../../../src/agent/memory/manager.ts"],"sourcesContent":["import type { AgentTool } from '@earendil-works/pi-agent-core';\n\nimport { appendMemoryTraceEvent } from '../../storage/sqlite/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { proposeMemoryCandidatesFromTurn } from './candidate-extractor.js';\nimport type { MemoryProvider, MemoryProviderInitOptions } from './provider.js';\nimport type {\n MemoryDeleteRequest,\n MemoryListRequest,\n MemoryReadRequest,\n MemoryReadResult,\n MemorySearchRequest,\n MemorySearchResult,\n MemorySyncEvent,\n MemoryUpdateRequest,\n MemoryWriteRequest,\n MemoryWriteResult,\n MemoryRecord,\n MemorySignal,\n MemoryKind,\n} from './types.js';\n\nconst log = createLogger('MemoryManager');\n\nexport type MemorySearchStrategy = 'local-first' | 'external-first' | 'fanout' | 'local-only' | 'external-only';\nexport type MemoryWriteStrategy = 'local-first' | 'external-first' | 'write-through' | 'local-only' | 'external-only';\n\nexport interface MemoryRoutingOptions {\n searchStrategy?: MemorySearchStrategy;\n writeStrategy?: MemoryWriteStrategy;\n replicateTo?: string[];\n}\n\nexport interface MemoryManagerOptions extends MemoryRoutingOptions {\n loadProviders?: () => Promise<MemoryProvider[]>;\n writePolicy?: MemoryWritePolicy;\n}\n\nexport interface MemoryWritePolicy {\n allowExternalWrites?: boolean;\n allowedProviderIds?: string[];\n autoWriteKinds?: MemoryKind[];\n requireUserProfileApproval?: boolean;\n}\n\nexport class MemoryManager {\n private providers: MemoryProvider[] = [];\n private toolToProvider = new Map<string, MemoryProvider>();\n private readonly routing: Required<MemoryRoutingOptions>;\n private readonly loadProviders?: () => Promise<MemoryProvider[]>;\n private readonly writePolicy: Required<Pick<MemoryWritePolicy, 'allowExternalWrites' | 'requireUserProfileApproval'>> &\n Omit<MemoryWritePolicy, 'allowExternalWrites' | 'requireUserProfileApproval'>;\n private pluginProvidersLoaded = false;\n\n constructor(options: MemoryManagerOptions = {}) {\n this.routing = {\n searchStrategy: options.searchStrategy ?? 'fanout',\n writeStrategy: options.writeStrategy ?? 'local-first',\n replicateTo: options.replicateTo ?? [],\n };\n this.loadProviders = options.loadProviders;\n this.writePolicy = {\n allowExternalWrites: options.writePolicy?.allowExternalWrites ?? false,\n requireUserProfileApproval: options.writePolicy?.requireUserProfileApproval ?? false,\n allowedProviderIds: options.writePolicy?.allowedProviderIds,\n autoWriteKinds: options.writePolicy?.autoWriteKinds,\n };\n }\n\n addProvider(provider: MemoryProvider): void {\n this.providers.push(provider);\n\n for (const schema of provider.getToolSchemas?.() ?? []) {\n const toolName = schema.name;\n if (!toolName) {\n continue;\n }\n if (!this.toolToProvider.has(toolName)) {\n this.toolToProvider.set(toolName, provider);\n } else {\n log.warn(\n { toolName, existing: this.toolToProvider.get(toolName)?.id, ignored: provider.id },\n 'Memory tool name conflict',\n );\n }\n }\n\n log.info(\n { id: provider.id, toolCount: provider.getToolSchemas?.().length ?? 0, capabilities: provider.capabilities },\n 'Memory provider registered',\n );\n }\n\n get providersList(): MemoryProvider[] {\n return [...this.providers];\n }\n\n /**\n * Non-builtin static instructions (builtin uses curated snapshot in system prompt builder).\n */\n buildExternalSystemPrompt(): string {\n const blocks: string[] = [];\n for (const p of this.providers) {\n if (p.capabilities.local) {\n continue;\n }\n try {\n const block = p.systemPromptBlock?.();\n if (block?.trim()) {\n blocks.push(block.trim());\n }\n } catch (err) {\n log.warn({ err, id: p.id }, 'systemPromptBlock failed');\n }\n }\n return blocks.join('\\n\\n');\n }\n\n async prefetchAll(query: string, options?: { sessionId?: string }): Promise<string> {\n const parts: string[] = [];\n for (const p of this.providers) {\n try {\n const v = await p.prefetch?.(query, options);\n if (v?.trim()) {\n parts.push(v.trim());\n }\n } catch (err) {\n log.debug({ err, id: p.id }, 'prefetch failed (non-fatal)');\n }\n }\n return parts.join('\\n\\n');\n }\n\n queuePrefetchAll(query: string, options?: { sessionId?: string }): void {\n for (const p of this.providers) {\n try {\n p.queuePrefetch?.(query, options);\n } catch (err) {\n log.debug({ err, id: p.id }, 'queuePrefetch failed (non-fatal)');\n }\n }\n }\n\n async syncAll(userContent: string, assistantContent: string, options?: { sessionId?: string }): Promise<void> {\n const event: MemorySyncEvent = {\n type: 'turn',\n userContent,\n assistantContent,\n sessionId: options?.sessionId,\n };\n await this.proposeCandidatesFromTurn(event);\n for (const p of this.providers) {\n const started = Date.now();\n try {\n await p.sync?.(event);\n this.trace('sync', p.id, event, {\n resultCount: 1,\n durationMs: Date.now() - started,\n sessionKey: options?.sessionId,\n });\n } catch (err) {\n this.trace('sync', p.id, event, {\n error: err instanceof Error ? err.message : String(err),\n durationMs: Date.now() - started,\n sessionKey: options?.sessionId,\n });\n log.warn({ err, id: p.id }, 'memory sync failed');\n }\n }\n }\n\n private async proposeCandidatesFromTurn(event: Extract<MemorySyncEvent, { type: 'turn' }>): Promise<void> {\n const candidates = proposeMemoryCandidatesFromTurn({\n userContent: event.userContent,\n assistantContent: event.assistantContent,\n sessionKey: event.sessionId,\n });\n for (const candidate of candidates) {\n try {\n await this.write({\n ...candidate,\n scope: {\n ...(candidate.scope ?? {}),\n ...(event.sessionId ? { sessionKey: event.sessionId } : {}),\n },\n });\n } catch (err) {\n log.debug({ err, sessionKey: event.sessionId }, 'memory candidate proposal failed');\n }\n }\n }\n\n getAdditionalTools(): AgentTool[] {\n const out: AgentTool[] = [];\n const seen = new Set<string>();\n for (const p of this.providers) {\n if (p.capabilities.local) {\n continue;\n }\n try {\n for (const t of p.getToolSchemas?.() ?? []) {\n if (t.name && !seen.has(t.name)) {\n seen.add(t.name);\n out.push(t);\n }\n }\n } catch (err) {\n log.warn({ err, id: p.id }, 'getToolSchemas failed');\n }\n }\n return out;\n }\n\n async handleToolCall(toolName: string, args: Record<string, unknown>): Promise<string> {\n const provider = this.toolToProvider.get(toolName);\n if (!provider) {\n throw new Error(`No memory provider handles tool '${toolName}'`);\n }\n if (!provider.handleToolCall) {\n throw new Error(`Memory provider '${provider.id}' does not handle tool calls`);\n }\n return provider.handleToolCall(toolName, args);\n }\n\n async search(request: MemorySearchRequest): Promise<MemorySearchResult[]> {\n const providers = this.providersForSearch();\n const results: MemorySearchResult[] = [];\n\n for (const p of providers) {\n if (!p.capabilities.search || !p.search) continue;\n const started = Date.now();\n try {\n const providerResults = await p.search(request);\n this.trace('search', p.id, request, {\n resultCount: providerResults.length,\n selectedRecordIds: providerResults.map((result) => result.record.id),\n durationMs: Date.now() - started,\n sessionKey: request.scope?.sessionKey,\n });\n results.push(...providerResults);\n if (providerResults.length > 0 && this.routing.searchStrategy !== 'fanout') {\n break;\n }\n } catch (err) {\n this.trace('search', p.id, request, {\n error: err instanceof Error ? err.message : String(err),\n durationMs: Date.now() - started,\n sessionKey: request.scope?.sessionKey,\n });\n log.warn({ err, id: p.id }, 'memory search failed');\n }\n }\n\n const seen = new Set<string>();\n return results\n .sort((a, b) => b.score - a.score)\n .filter((result) => {\n const key = `${result.citation.providerId}:${result.citation.recordId}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n })\n .slice(0, request.maxResults ?? 5);\n }\n\n async read(request: MemoryReadRequest): Promise<MemoryReadResult | null> {\n for (const p of this.providersForSearch()) {\n if (!p.capabilities.read || !p.read) continue;\n const started = Date.now();\n try {\n const result = await p.read(request);\n this.trace('read', p.id, request, {\n resultCount: result ? 1 : 0,\n selectedRecordIds: result ? [result.record.id] : [],\n durationMs: Date.now() - started,\n sessionKey: request.scope?.sessionKey,\n });\n if (result) return result;\n } catch (err) {\n this.trace('read', p.id, request, {\n error: err instanceof Error ? err.message : String(err),\n durationMs: Date.now() - started,\n sessionKey: request.scope?.sessionKey,\n });\n log.warn({ err, id: p.id }, 'memory read failed');\n }\n }\n return null;\n }\n\n async get(id: string): Promise<MemoryRecord | null> {\n for (const p of this.providers) {\n if (!p.capabilities.read) continue;\n try {\n const direct = await p.get?.(id);\n if (direct) return direct;\n } catch (err) {\n log.warn({ err, id: p.id, recordId: id }, 'memory get failed');\n }\n }\n return null;\n }\n\n async list(request: MemoryListRequest): Promise<MemoryRecord[]> {\n const out: MemoryRecord[] = [];\n for (const p of this.providersForSearch()) {\n if (!p.capabilities.read || !p.list) continue;\n try {\n out.push(...await p.list(request));\n } catch (err) {\n log.warn({ err, id: p.id }, 'memory list failed');\n }\n }\n return out;\n }\n\n async write(request: MemoryWriteRequest): Promise<MemoryWriteResult> {\n return this.writeWithStrategy('write', request);\n }\n\n async update(request: MemoryUpdateRequest): Promise<MemoryWriteResult> {\n return this.writeWithStrategy('update', request);\n }\n\n async delete(request: MemoryDeleteRequest): Promise<MemoryWriteResult> {\n return this.writeWithStrategy('delete', request);\n }\n\n onMemoryWrite(action: 'add' | 'replace' | 'remove', target: 'memory' | 'user', content: string): void {\n for (const p of this.providers) {\n if (p.capabilities.local) {\n continue;\n }\n try {\n p.sync?.({ type: 'write', action, target, content });\n } catch (err) {\n log.debug({ err, id: p.id }, 'memory write sync failed');\n }\n }\n }\n\n recordSignal(signal: MemorySignal): void {\n for (const p of this.providers) {\n const started = Date.now();\n try {\n p.sync?.({ type: 'signal', signal });\n this.trace('sync', p.id, { type: 'signal', signal }, {\n resultCount: 1,\n durationMs: Date.now() - started,\n });\n } catch (err) {\n this.trace('sync', p.id, { type: 'signal', signal }, {\n error: err instanceof Error ? err.message : String(err),\n durationMs: Date.now() - started,\n });\n log.debug({ err, id: p.id, source: signal.source }, 'memory signal sync failed');\n }\n }\n }\n\n async initializeAll(sessionId: string, options?: MemoryProviderInitOptions): Promise<void> {\n await this.loadPluginProvidersOnce();\n for (const p of this.providers) {\n try {\n await p.initialize(sessionId, options);\n } catch (err) {\n log.warn({ err, id: p.id }, 'initialize failed');\n }\n }\n }\n\n private async loadPluginProvidersOnce(): Promise<void> {\n if (this.pluginProvidersLoaded || !this.loadProviders) return;\n this.pluginProvidersLoaded = true;\n try {\n const providers = await this.loadProviders();\n const existingIds = new Set(this.providers.map((p) => p.id));\n for (const provider of providers) {\n if (existingIds.has(provider.id)) {\n log.warn({ id: provider.id }, 'Skipped duplicate memory provider');\n continue;\n }\n this.addProvider(provider);\n existingIds.add(provider.id);\n }\n } catch (err) {\n log.warn({ err }, 'Memory provider plugin loading failed');\n }\n }\n\n async shutdownAll(): Promise<void> {\n for (const p of [...this.providers].reverse()) {\n try {\n await p.shutdown();\n } catch (err) {\n log.warn({ err, id: p.id }, 'shutdown failed');\n }\n }\n }\n\n private providersForSearch(): MemoryProvider[] {\n const locals = this.providers.filter((p) => p.capabilities.local);\n const externals = this.providers.filter((p) => !p.capabilities.local);\n switch (this.routing.searchStrategy) {\n case 'local-only':\n return locals;\n case 'external-only':\n return externals;\n case 'external-first':\n return [...externals, ...locals];\n case 'local-first':\n case 'fanout':\n default:\n return [...locals, ...externals];\n }\n }\n\n private providersForWrite(): MemoryProvider[] {\n const locals = this.providers.filter((p) => p.capabilities.local);\n const externals = this.providers.filter((p) => !p.capabilities.local);\n switch (this.routing.writeStrategy) {\n case 'local-only':\n return locals;\n case 'external-only':\n return externals;\n case 'external-first':\n return [...externals, ...locals];\n case 'write-through':\n case 'local-first':\n default:\n return [...locals, ...externals];\n }\n }\n\n private async writeWithStrategy(\n operation: 'write' | 'update' | 'delete',\n request: MemoryWriteRequest | MemoryUpdateRequest | MemoryDeleteRequest,\n ): Promise<MemoryWriteResult> {\n const providers =\n operation === 'write' && (request as MemoryWriteRequest).status === 'candidate'\n ? this.providers.filter((provider) => provider.capabilities.local)\n : this.providersForWrite();\n const errors: string[] = [];\n let firstSuccess: MemoryWriteResult | null = null;\n\n for (const p of providers) {\n const fn = p[operation];\n if (!this.providerAllowedForWrite(p, operation, request)) {\n this.trace(operation, p.id, request, {\n skippedReason: 'policy',\n sessionKey: request.scope?.sessionKey,\n });\n continue;\n }\n const canWrite =\n (operation === 'write' && p.capabilities.write) ||\n (operation === 'update' && p.capabilities.update) ||\n (operation === 'delete' && p.capabilities.delete);\n if (!canWrite || !fn) continue;\n const started = Date.now();\n try {\n const result = await fn.call(p, request as never);\n this.trace(operation, p.id, request, {\n resultCount: result.record ? 1 : 0,\n selectedRecordIds: result.record ? [result.record.id] : [],\n error: result.success ? undefined : result.error,\n durationMs: Date.now() - started,\n sessionKey: request.scope?.sessionKey,\n });\n if (result.success) {\n firstSuccess ??= result;\n if (this.routing.writeStrategy !== 'write-through') {\n return result;\n }\n } else if (result.error) {\n errors.push(`${p.id}: ${result.error}`);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n this.trace(operation, p.id, request, {\n error: message,\n durationMs: Date.now() - started,\n sessionKey: request.scope?.sessionKey,\n });\n errors.push(`${p.id}: ${message}`);\n log.warn({ err, id: p.id, operation }, 'memory write operation failed');\n }\n }\n\n if (firstSuccess) return firstSuccess;\n return { success: false, error: errors.join('; ') || `No provider supports memory ${operation}` };\n }\n\n private providerAllowedForWrite(\n provider: MemoryProvider,\n operation: 'write' | 'update' | 'delete',\n request: MemoryWriteRequest | MemoryUpdateRequest | MemoryDeleteRequest,\n ): boolean {\n if (!provider.capabilities.local && !this.writePolicy.allowExternalWrites) {\n return false;\n }\n if (\n !provider.capabilities.local &&\n this.writePolicy.allowedProviderIds &&\n this.writePolicy.allowedProviderIds.length > 0 &&\n !this.writePolicy.allowedProviderIds.includes(provider.id)\n ) {\n return false;\n }\n if (operation === 'write') {\n const writeRequest = request as MemoryWriteRequest;\n if (writeRequest.status === 'candidate' && !provider.capabilities.local) {\n return false;\n }\n if (\n this.writePolicy.autoWriteKinds &&\n this.writePolicy.autoWriteKinds.length > 0 &&\n !this.writePolicy.autoWriteKinds.includes(writeRequest.kind)\n ) {\n return false;\n }\n if (\n this.writePolicy.requireUserProfileApproval &&\n writeRequest.kind === 'user_profile' &&\n !writeRequest.approved\n ) {\n return false;\n }\n }\n if (operation === 'update' && this.writePolicy.requireUserProfileApproval) {\n const updateRequest = request as MemoryUpdateRequest;\n if (updateRequest.target === 'user' && !updateRequest.approved) {\n return false;\n }\n }\n return true;\n }\n\n private trace(\n phase: 'search' | 'read' | 'write' | 'update' | 'delete' | 'sync' | 'inject' | 'test',\n providerId: string,\n request: unknown,\n meta: {\n sessionKey?: string;\n resultCount?: number;\n selectedRecordIds?: string[];\n skippedReason?: string;\n error?: string;\n durationMs?: number;\n } = {},\n ): void {\n try {\n appendMemoryTraceEvent({\n phase,\n providerId,\n request: sanitizeTraceRequest(request),\n ...meta,\n });\n } catch (err) {\n log.debug({ err, providerId, phase }, 'memory trace append failed');\n }\n }\n}\n\nfunction sanitizeTraceRequest(request: unknown): unknown {\n if (!request || typeof request !== 'object') return request;\n const copy = { ...(request as Record<string, unknown>) };\n if (typeof copy.content === 'string' && copy.content.length > 500) {\n copy.content = `${copy.content.slice(0, 500)}…`;\n }\n return copy;\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { init_agent_scope, resolveAgentHomeDir, resolveAgentIdForWorkspacePath } from "../agent-scope.js";
|
|
2
2
|
import { resolveEffectiveAgentManifestForAgent } from "../../config/agent-profile.js";
|
|
3
|
+
import { init_paths, resolveUserMemoryPath } from "../../config/paths.js";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
//#region src/agent/memory/memory-config.ts
|
|
6
|
+
init_paths();
|
|
5
7
|
init_agent_scope();
|
|
6
8
|
/** When false, curated `memories/` (under agent home) + external memory providers are off. */
|
|
7
9
|
function isMemorySubsystemEnabled(config) {
|
|
@@ -19,6 +21,7 @@ function resolveBuiltinMemoryStoreConfig(workspaceDir, config) {
|
|
|
19
21
|
return {
|
|
20
22
|
workspaceDir,
|
|
21
23
|
memoriesDir: config != null ? join(resolveAgentHomeDir(config, agentId ?? "main"), "memories") : join(workspaceDir, "memories"),
|
|
24
|
+
userMemoryPath: config != null ? resolveUserMemoryPath() : join(workspaceDir, "user", "MEMORY.md"),
|
|
22
25
|
memoryCharLimit: manifest?.memory.retention?.maxItems ?? 2200,
|
|
23
26
|
userCharLimit: 1375,
|
|
24
27
|
userProfileEnabled: manifest?.memory.sources.includes("userProfile") ?? true
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-config.js","names":[],"sources":["../../../../src/agent/memory/memory-config.ts"],"sourcesContent":["import { join } from 'node:path';\n\nimport type { Config } from '../../config/schema.js';\nimport { resolveEffectiveAgentManifestForAgent } from '../../config/agent-profile.js';\nimport { resolveAgentHomeDir, resolveAgentIdForWorkspacePath } from '../agent-scope.js';\n\nimport type { MemoryStoreConfig } from './types.js';\n\n/** When false, curated `memories/` (under agent home) + external memory providers are off. */\nexport function isMemorySubsystemEnabled(config: Config | undefined): boolean {\n if (!config) return true;\n return config.agents.list.some((agent) => agent.enabled !== false && agent.memory.mode !== 'off');\n}\n\n/** Curated memory tool + agent-home `memories/` store (not injected into system prompt). */\nexport function isCuratedMemoryInPrompt(config: Config | undefined): boolean {\n if (!config) return true;\n return config.agents.list.some((agent) => agent.enabled !== false && agent.memory.sources.includes('curated'));\n}\n\nexport function resolveBuiltinMemoryStoreConfig(\n workspaceDir: string,\n config: Config | undefined,\n): MemoryStoreConfig {\n const agentId = config != null ? resolveAgentIdForWorkspacePath(config, workspaceDir) : undefined;\n const manifest = config && agentId ? resolveEffectiveAgentManifestForAgent(config, agentId) : undefined;\n const memoriesDir =\n config != null\n ? join(\n resolveAgentHomeDir(config, agentId ?? 'main'),\n 'memories',\n )\n : join(workspaceDir, 'memories');\n return {\n workspaceDir,\n memoriesDir,\n memoryCharLimit: manifest?.memory.retention?.maxItems ?? 2200,\n userCharLimit: 1375,\n userProfileEnabled: manifest?.memory.sources.includes('userProfile') ?? true,\n };\n}\n\n/**\n * Whether to prefix the user message with prefetched external memory this turn.\n * `first-turn` only injects on turn 1. `contextCadence` N injects on turns 1, N+1, 2N+1, …\n */\nexport function shouldInjectMemoryPrefetchThisTurn(\n config: Config | undefined,\n turnNumber: number,\n): boolean {\n return turnNumber >= 1;\n}\n\nexport function shouldRegisterCuratedMemoryTool(config: Config | undefined): boolean {\n return isCuratedMemoryInPrompt(config);\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"memory-config.js","names":[],"sources":["../../../../src/agent/memory/memory-config.ts"],"sourcesContent":["import { join } from 'node:path';\n\nimport type { Config } from '../../config/schema.js';\nimport { resolveUserMemoryPath } from '../../config/paths.js';\nimport { resolveEffectiveAgentManifestForAgent } from '../../config/agent-profile.js';\nimport { resolveAgentHomeDir, resolveAgentIdForWorkspacePath } from '../agent-scope.js';\n\nimport type { MemoryStoreConfig } from './types.js';\n\n/** When false, curated `memories/` (under agent home) + external memory providers are off. */\nexport function isMemorySubsystemEnabled(config: Config | undefined): boolean {\n if (!config) return true;\n return config.agents.list.some((agent) => agent.enabled !== false && agent.memory.mode !== 'off');\n}\n\n/** Curated memory tool + agent-home `memories/` store (not injected into system prompt). */\nexport function isCuratedMemoryInPrompt(config: Config | undefined): boolean {\n if (!config) return true;\n return config.agents.list.some((agent) => agent.enabled !== false && agent.memory.sources.includes('curated'));\n}\n\nexport function resolveBuiltinMemoryStoreConfig(\n workspaceDir: string,\n config: Config | undefined,\n): MemoryStoreConfig {\n const agentId = config != null ? resolveAgentIdForWorkspacePath(config, workspaceDir) : undefined;\n const manifest = config && agentId ? resolveEffectiveAgentManifestForAgent(config, agentId) : undefined;\n const memoriesDir =\n config != null\n ? join(\n resolveAgentHomeDir(config, agentId ?? 'main'),\n 'memories',\n )\n : join(workspaceDir, 'memories');\n return {\n workspaceDir,\n memoriesDir,\n userMemoryPath: config != null ? resolveUserMemoryPath() : join(workspaceDir, 'user', 'MEMORY.md'),\n memoryCharLimit: manifest?.memory.retention?.maxItems ?? 2200,\n userCharLimit: 1375,\n userProfileEnabled: manifest?.memory.sources.includes('userProfile') ?? true,\n };\n}\n\n/**\n * Whether to prefix the user message with prefetched external memory this turn.\n * `first-turn` only injects on turn 1. `contextCadence` N injects on turns 1, N+1, 2N+1, …\n */\nexport function shouldInjectMemoryPrefetchThisTurn(\n config: Config | undefined,\n turnNumber: number,\n): boolean {\n return turnNumber >= 1;\n}\n\nexport function shouldRegisterCuratedMemoryTool(config: Config | undefined): boolean {\n return isCuratedMemoryInPrompt(config);\n}\n"],"mappings":";;;;;YAG8D;kBAE0B;;AAKxF,SAAgB,yBAAyB,QAAqC;AAC5E,KAAI,CAAC,OAAQ,QAAO;AACpB,QAAO,OAAO,OAAO,KAAK,MAAM,UAAU,MAAM,YAAY,SAAS,MAAM,OAAO,SAAS,MAAM;;;AAInG,SAAgB,wBAAwB,QAAqC;AAC3E,KAAI,CAAC,OAAQ,QAAO;AACpB,QAAO,OAAO,OAAO,KAAK,MAAM,UAAU,MAAM,YAAY,SAAS,MAAM,OAAO,QAAQ,SAAS,UAAU,CAAC;;AAGhH,SAAgB,gCACd,cACA,QACmB;CACnB,MAAM,UAAU,UAAU,OAAO,+BAA+B,QAAQ,aAAa,GAAG,KAAA;CACxF,MAAM,WAAW,UAAU,UAAU,sCAAsC,QAAQ,QAAQ,GAAG,KAAA;AAQ9F,QAAO;EACL;EACA,aARA,UAAU,OACN,KACE,oBAAoB,QAAQ,WAAW,OAAO,EAC9C,WACD,GACD,KAAK,cAAc,WAAW;EAIlC,gBAAgB,UAAU,OAAO,uBAAuB,GAAG,KAAK,cAAc,QAAQ,YAAY;EAClG,iBAAiB,UAAU,OAAO,WAAW,YAAY;EACzD,eAAe;EACf,oBAAoB,UAAU,OAAO,QAAQ,SAAS,cAAc,IAAI;EACzE;;;;;;AAOH,SAAgB,mCACd,QACA,YACS;AACT,QAAO,cAAc;;AAGvB,SAAgB,gCAAgC,QAAqC;AACnF,QAAO,wBAAwB,OAAO"}
|