@raviolelabs/engram-mcp 0.2.0
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/CLAUDE.md +232 -0
- package/LICENSE +21 -0
- package/README.md +222 -0
- package/SKILL.md +299 -0
- package/dist/cloud/auth.d.ts +29 -0
- package/dist/cloud/auth.d.ts.map +1 -0
- package/dist/cloud/auth.js +132 -0
- package/dist/cloud/auth.js.map +1 -0
- package/dist/cloud/bridge-client.d.ts +10 -0
- package/dist/cloud/bridge-client.d.ts.map +1 -0
- package/dist/cloud/bridge-client.js +167 -0
- package/dist/cloud/bridge-client.js.map +1 -0
- package/dist/cloud/crypto.d.ts +42 -0
- package/dist/cloud/crypto.d.ts.map +1 -0
- package/dist/cloud/crypto.js +146 -0
- package/dist/cloud/crypto.js.map +1 -0
- package/dist/cloud/endpoints.d.ts +26 -0
- package/dist/cloud/endpoints.d.ts.map +1 -0
- package/dist/cloud/endpoints.js +26 -0
- package/dist/cloud/endpoints.js.map +1 -0
- package/dist/cloud/pairing.d.ts +30 -0
- package/dist/cloud/pairing.d.ts.map +1 -0
- package/dist/cloud/pairing.js +157 -0
- package/dist/cloud/pairing.js.map +1 -0
- package/dist/cloud/transit-poller.d.ts +35 -0
- package/dist/cloud/transit-poller.d.ts.map +1 -0
- package/dist/cloud/transit-poller.js +281 -0
- package/dist/cloud/transit-poller.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +24 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/schema.d.ts +466 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +171 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/core/db/index.d.ts +7 -0
- package/dist/core/db/index.d.ts.map +1 -0
- package/dist/core/db/index.js +273 -0
- package/dist/core/db/index.js.map +1 -0
- package/dist/core/logger.d.ts +19 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +223 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/server/http.d.ts +15 -0
- package/dist/core/server/http.d.ts.map +1 -0
- package/dist/core/server/http.js +76 -0
- package/dist/core/server/http.js.map +1 -0
- package/dist/core/server/instructions.d.ts +2 -0
- package/dist/core/server/instructions.d.ts.map +1 -0
- package/dist/core/server/instructions.js +36 -0
- package/dist/core/server/instructions.js.map +1 -0
- package/dist/core/server/mcp-handler.d.ts +39 -0
- package/dist/core/server/mcp-handler.d.ts.map +1 -0
- package/dist/core/server/mcp-handler.js +204 -0
- package/dist/core/server/mcp-handler.js.map +1 -0
- package/dist/core/server/mcp-http.d.ts +4 -0
- package/dist/core/server/mcp-http.d.ts.map +1 -0
- package/dist/core/server/mcp-http.js +56 -0
- package/dist/core/server/mcp-http.js.map +1 -0
- package/dist/core/server/tool-router.d.ts +9 -0
- package/dist/core/server/tool-router.d.ts.map +1 -0
- package/dist/core/server/tool-router.js +25 -0
- package/dist/core/server/tool-router.js.map +1 -0
- package/dist/core/server/websocket.d.ts +4 -0
- package/dist/core/server/websocket.d.ts.map +1 -0
- package/dist/core/server/websocket.js +25 -0
- package/dist/core/server/websocket.js.map +1 -0
- package/dist/db/index.d.ts +2 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +3 -0
- package/dist/db/index.js.map +1 -0
- package/dist/embeddings/index.d.ts +24 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +86 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/embeddings/providers/engram.d.ts +7 -0
- package/dist/embeddings/providers/engram.d.ts.map +1 -0
- package/dist/embeddings/providers/engram.js +67 -0
- package/dist/embeddings/providers/engram.js.map +1 -0
- package/dist/embeddings/providers/ollama.d.ts +3 -0
- package/dist/embeddings/providers/ollama.d.ts.map +1 -0
- package/dist/embeddings/providers/ollama.js +9 -0
- package/dist/embeddings/providers/ollama.js.map +1 -0
- package/dist/embeddings/providers/openai-compat.d.ts +7 -0
- package/dist/embeddings/providers/openai-compat.d.ts.map +1 -0
- package/dist/embeddings/providers/openai-compat.js +27 -0
- package/dist/embeddings/providers/openai-compat.js.map +1 -0
- package/dist/embeddings/providers/openai.d.ts +3 -0
- package/dist/embeddings/providers/openai.d.ts.map +1 -0
- package/dist/embeddings/providers/openai.js +12 -0
- package/dist/embeddings/providers/openai.js.map +1 -0
- package/dist/embeddings/providers/voyage.d.ts +3 -0
- package/dist/embeddings/providers/voyage.d.ts.map +1 -0
- package/dist/embeddings/providers/voyage.js +12 -0
- package/dist/embeddings/providers/voyage.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest/jobs.d.ts +29 -0
- package/dist/ingest/jobs.d.ts.map +1 -0
- package/dist/ingest/jobs.js +131 -0
- package/dist/ingest/jobs.js.map +1 -0
- package/dist/logger.d.ts +2 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +3 -0
- package/dist/logger.js.map +1 -0
- package/dist/mcp-server/server.d.ts +2 -0
- package/dist/mcp-server/server.d.ts.map +1 -0
- package/dist/mcp-server/server.js +3 -0
- package/dist/mcp-server/server.js.map +1 -0
- package/dist/mcp-server/tests/mcp-e2e.test.d.ts +2 -0
- package/dist/mcp-server/tests/mcp-e2e.test.d.ts.map +1 -0
- package/dist/mcp-server/tests/mcp-e2e.test.js +157 -0
- package/dist/mcp-server/tests/mcp-e2e.test.js.map +1 -0
- package/dist/mcp-server/tool-router.d.ts +2 -0
- package/dist/mcp-server/tool-router.d.ts.map +1 -0
- package/dist/mcp-server/tool-router.js +3 -0
- package/dist/mcp-server/tool-router.js.map +1 -0
- package/dist/memory/admin/tools.d.ts +6 -0
- package/dist/memory/admin/tools.d.ts.map +1 -0
- package/dist/memory/admin/tools.js +134 -0
- package/dist/memory/admin/tools.js.map +1 -0
- package/dist/memory/core/chunker.d.ts +6 -0
- package/dist/memory/core/chunker.d.ts.map +1 -0
- package/dist/memory/core/chunker.js +49 -0
- package/dist/memory/core/chunker.js.map +1 -0
- package/dist/memory/core/module-interface.d.ts +23 -0
- package/dist/memory/core/module-interface.d.ts.map +1 -0
- package/dist/memory/core/module-interface.js +2 -0
- package/dist/memory/core/module-interface.js.map +1 -0
- package/dist/memory/core/module-registry.d.ts +14 -0
- package/dist/memory/core/module-registry.d.ts.map +1 -0
- package/dist/memory/core/module-registry.js +45 -0
- package/dist/memory/core/module-registry.js.map +1 -0
- package/dist/memory/core/property-extractor.d.ts +6 -0
- package/dist/memory/core/property-extractor.d.ts.map +1 -0
- package/dist/memory/core/property-extractor.js +90 -0
- package/dist/memory/core/property-extractor.js.map +1 -0
- package/dist/memory/core/reindex.d.ts +11 -0
- package/dist/memory/core/reindex.d.ts.map +1 -0
- package/dist/memory/core/reindex.js +55 -0
- package/dist/memory/core/reindex.js.map +1 -0
- package/dist/memory/core/source-registry.d.ts +42 -0
- package/dist/memory/core/source-registry.d.ts.map +1 -0
- package/dist/memory/core/source-registry.js +86 -0
- package/dist/memory/core/source-registry.js.map +1 -0
- package/dist/memory/core/store.d.ts +40 -0
- package/dist/memory/core/store.d.ts.map +1 -0
- package/dist/memory/core/store.js +257 -0
- package/dist/memory/core/store.js.map +1 -0
- package/dist/memory/core/wikilinks.d.ts +13 -0
- package/dist/memory/core/wikilinks.d.ts.map +1 -0
- package/dist/memory/core/wikilinks.js +25 -0
- package/dist/memory/core/wikilinks.js.map +1 -0
- package/dist/memory/modules/_custom/generic-module.d.ts +7 -0
- package/dist/memory/modules/_custom/generic-module.d.ts.map +1 -0
- package/dist/memory/modules/_custom/generic-module.js +108 -0
- package/dist/memory/modules/_custom/generic-module.js.map +1 -0
- package/dist/memory/modules/_custom/persistence.d.ts +15 -0
- package/dist/memory/modules/_custom/persistence.d.ts.map +1 -0
- package/dist/memory/modules/_custom/persistence.js +47 -0
- package/dist/memory/modules/_custom/persistence.js.map +1 -0
- package/dist/memory/modules/_custom/tests/custom-types.test.d.ts +2 -0
- package/dist/memory/modules/_custom/tests/custom-types.test.d.ts.map +1 -0
- package/dist/memory/modules/_custom/tests/custom-types.test.js +89 -0
- package/dist/memory/modules/_custom/tests/custom-types.test.js.map +1 -0
- package/dist/memory/modules/_custom/tools.d.ts +7 -0
- package/dist/memory/modules/_custom/tools.d.ts.map +1 -0
- package/dist/memory/modules/_custom/tools.js +72 -0
- package/dist/memory/modules/_custom/tools.js.map +1 -0
- package/dist/memory/modules/audio/ingest.d.ts +9 -0
- package/dist/memory/modules/audio/ingest.d.ts.map +1 -0
- package/dist/memory/modules/audio/ingest.js +32 -0
- package/dist/memory/modules/audio/ingest.js.map +1 -0
- package/dist/memory/modules/audio/module.d.ts +6 -0
- package/dist/memory/modules/audio/module.d.ts.map +1 -0
- package/dist/memory/modules/audio/module.js +18 -0
- package/dist/memory/modules/audio/module.js.map +1 -0
- package/dist/memory/modules/audio/tests/audio.test.d.ts +2 -0
- package/dist/memory/modules/audio/tests/audio.test.d.ts.map +1 -0
- package/dist/memory/modules/audio/tests/audio.test.js +57 -0
- package/dist/memory/modules/audio/tests/audio.test.js.map +1 -0
- package/dist/memory/modules/audio/tests/transcriber.test.d.ts +2 -0
- package/dist/memory/modules/audio/tests/transcriber.test.d.ts.map +1 -0
- package/dist/memory/modules/audio/tests/transcriber.test.js +27 -0
- package/dist/memory/modules/audio/tests/transcriber.test.js.map +1 -0
- package/dist/memory/modules/audio/tools.d.ts +5 -0
- package/dist/memory/modules/audio/tools.d.ts.map +1 -0
- package/dist/memory/modules/audio/tools.js +60 -0
- package/dist/memory/modules/audio/tools.js.map +1 -0
- package/dist/memory/modules/audio/transcriber.d.ts +15 -0
- package/dist/memory/modules/audio/transcriber.d.ts.map +1 -0
- package/dist/memory/modules/audio/transcriber.js +177 -0
- package/dist/memory/modules/audio/transcriber.js.map +1 -0
- package/dist/memory/modules/conversations/ingest.d.ts +10 -0
- package/dist/memory/modules/conversations/ingest.d.ts.map +1 -0
- package/dist/memory/modules/conversations/ingest.js +38 -0
- package/dist/memory/modules/conversations/ingest.js.map +1 -0
- package/dist/memory/modules/conversations/module.d.ts +6 -0
- package/dist/memory/modules/conversations/module.d.ts.map +1 -0
- package/dist/memory/modules/conversations/module.js +43 -0
- package/dist/memory/modules/conversations/module.js.map +1 -0
- package/dist/memory/modules/conversations/tests/conversations.test.d.ts +2 -0
- package/dist/memory/modules/conversations/tests/conversations.test.d.ts.map +1 -0
- package/dist/memory/modules/conversations/tests/conversations.test.js +70 -0
- package/dist/memory/modules/conversations/tests/conversations.test.js.map +1 -0
- package/dist/memory/modules/conversations/tools.d.ts +5 -0
- package/dist/memory/modules/conversations/tools.d.ts.map +1 -0
- package/dist/memory/modules/conversations/tools.js +75 -0
- package/dist/memory/modules/conversations/tools.js.map +1 -0
- package/dist/memory/modules/drive/connector.d.ts +19 -0
- package/dist/memory/modules/drive/connector.d.ts.map +1 -0
- package/dist/memory/modules/drive/connector.js +52 -0
- package/dist/memory/modules/drive/connector.js.map +1 -0
- package/dist/memory/modules/drive/ingest.d.ts +9 -0
- package/dist/memory/modules/drive/ingest.d.ts.map +1 -0
- package/dist/memory/modules/drive/ingest.js +27 -0
- package/dist/memory/modules/drive/ingest.js.map +1 -0
- package/dist/memory/modules/drive/module.d.ts +6 -0
- package/dist/memory/modules/drive/module.d.ts.map +1 -0
- package/dist/memory/modules/drive/module.js +31 -0
- package/dist/memory/modules/drive/module.js.map +1 -0
- package/dist/memory/modules/drive/oauth.d.ts +14 -0
- package/dist/memory/modules/drive/oauth.d.ts.map +1 -0
- package/dist/memory/modules/drive/oauth.js +130 -0
- package/dist/memory/modules/drive/oauth.js.map +1 -0
- package/dist/memory/modules/drive/tests/drive.test.d.ts +2 -0
- package/dist/memory/modules/drive/tests/drive.test.d.ts.map +1 -0
- package/dist/memory/modules/drive/tests/drive.test.js +66 -0
- package/dist/memory/modules/drive/tests/drive.test.js.map +1 -0
- package/dist/memory/modules/drive/tools.d.ts +5 -0
- package/dist/memory/modules/drive/tools.d.ts.map +1 -0
- package/dist/memory/modules/drive/tools.js +131 -0
- package/dist/memory/modules/drive/tools.js.map +1 -0
- package/dist/memory/modules/drive/watcher.d.ts +5 -0
- package/dist/memory/modules/drive/watcher.d.ts.map +1 -0
- package/dist/memory/modules/drive/watcher.js +46 -0
- package/dist/memory/modules/drive/watcher.js.map +1 -0
- package/dist/memory/modules/notes/ingest.d.ts +3 -0
- package/dist/memory/modules/notes/ingest.d.ts.map +1 -0
- package/dist/memory/modules/notes/ingest.js +30 -0
- package/dist/memory/modules/notes/ingest.js.map +1 -0
- package/dist/memory/modules/notes/module.d.ts +5 -0
- package/dist/memory/modules/notes/module.d.ts.map +1 -0
- package/dist/memory/modules/notes/module.js +28 -0
- package/dist/memory/modules/notes/module.js.map +1 -0
- package/dist/memory/modules/notes/tests/notes.test.d.ts +2 -0
- package/dist/memory/modules/notes/tests/notes.test.d.ts.map +1 -0
- package/dist/memory/modules/notes/tests/notes.test.js +59 -0
- package/dist/memory/modules/notes/tests/notes.test.js.map +1 -0
- package/dist/memory/modules/notes/tools.d.ts +5 -0
- package/dist/memory/modules/notes/tools.d.ts.map +1 -0
- package/dist/memory/modules/notes/tools.js +69 -0
- package/dist/memory/modules/notes/tools.js.map +1 -0
- package/dist/memory/modules/notion/connector.d.ts +10 -0
- package/dist/memory/modules/notion/connector.d.ts.map +1 -0
- package/dist/memory/modules/notion/connector.js +112 -0
- package/dist/memory/modules/notion/connector.js.map +1 -0
- package/dist/memory/modules/notion/ingest.d.ts +9 -0
- package/dist/memory/modules/notion/ingest.d.ts.map +1 -0
- package/dist/memory/modules/notion/ingest.js +24 -0
- package/dist/memory/modules/notion/ingest.js.map +1 -0
- package/dist/memory/modules/notion/module.d.ts +6 -0
- package/dist/memory/modules/notion/module.d.ts.map +1 -0
- package/dist/memory/modules/notion/module.js +31 -0
- package/dist/memory/modules/notion/module.js.map +1 -0
- package/dist/memory/modules/notion/oauth.d.ts +19 -0
- package/dist/memory/modules/notion/oauth.d.ts.map +1 -0
- package/dist/memory/modules/notion/oauth.js +117 -0
- package/dist/memory/modules/notion/oauth.js.map +1 -0
- package/dist/memory/modules/notion/tests/notion.test.d.ts +2 -0
- package/dist/memory/modules/notion/tests/notion.test.d.ts.map +1 -0
- package/dist/memory/modules/notion/tests/notion.test.js +53 -0
- package/dist/memory/modules/notion/tests/notion.test.js.map +1 -0
- package/dist/memory/modules/notion/tools.d.ts +5 -0
- package/dist/memory/modules/notion/tools.d.ts.map +1 -0
- package/dist/memory/modules/notion/tools.js +116 -0
- package/dist/memory/modules/notion/tools.js.map +1 -0
- package/dist/memory/modules/notion/watcher.d.ts +5 -0
- package/dist/memory/modules/notion/watcher.d.ts.map +1 -0
- package/dist/memory/modules/notion/watcher.js +41 -0
- package/dist/memory/modules/notion/watcher.js.map +1 -0
- package/dist/memory/modules/obsidian/ingest.d.ts +9 -0
- package/dist/memory/modules/obsidian/ingest.d.ts.map +1 -0
- package/dist/memory/modules/obsidian/ingest.js +80 -0
- package/dist/memory/modules/obsidian/ingest.js.map +1 -0
- package/dist/memory/modules/obsidian/module.d.ts +6 -0
- package/dist/memory/modules/obsidian/module.d.ts.map +1 -0
- package/dist/memory/modules/obsidian/module.js +31 -0
- package/dist/memory/modules/obsidian/module.js.map +1 -0
- package/dist/memory/modules/obsidian/tests/obsidian.test.d.ts +2 -0
- package/dist/memory/modules/obsidian/tests/obsidian.test.d.ts.map +1 -0
- package/dist/memory/modules/obsidian/tests/obsidian.test.js +65 -0
- package/dist/memory/modules/obsidian/tests/obsidian.test.js.map +1 -0
- package/dist/memory/modules/obsidian/tests/vault-reader.test.d.ts +2 -0
- package/dist/memory/modules/obsidian/tests/vault-reader.test.d.ts.map +1 -0
- package/dist/memory/modules/obsidian/tests/vault-reader.test.js +37 -0
- package/dist/memory/modules/obsidian/tests/vault-reader.test.js.map +1 -0
- package/dist/memory/modules/obsidian/tools.d.ts +5 -0
- package/dist/memory/modules/obsidian/tools.d.ts.map +1 -0
- package/dist/memory/modules/obsidian/tools.js +101 -0
- package/dist/memory/modules/obsidian/tools.js.map +1 -0
- package/dist/memory/modules/obsidian/vault-reader.d.ts +8 -0
- package/dist/memory/modules/obsidian/vault-reader.d.ts.map +1 -0
- package/dist/memory/modules/obsidian/vault-reader.js +82 -0
- package/dist/memory/modules/obsidian/vault-reader.js.map +1 -0
- package/dist/memory/modules/obsidian/watcher.d.ts +5 -0
- package/dist/memory/modules/obsidian/watcher.d.ts.map +1 -0
- package/dist/memory/modules/obsidian/watcher.js +83 -0
- package/dist/memory/modules/obsidian/watcher.js.map +1 -0
- package/dist/memory/modules/youtube/ingest.d.ts +20 -0
- package/dist/memory/modules/youtube/ingest.d.ts.map +1 -0
- package/dist/memory/modules/youtube/ingest.js +49 -0
- package/dist/memory/modules/youtube/ingest.js.map +1 -0
- package/dist/memory/modules/youtube/module.d.ts +11 -0
- package/dist/memory/modules/youtube/module.d.ts.map +1 -0
- package/dist/memory/modules/youtube/module.js +26 -0
- package/dist/memory/modules/youtube/module.js.map +1 -0
- package/dist/memory/modules/youtube/tests/channel.test.d.ts +2 -0
- package/dist/memory/modules/youtube/tests/channel.test.d.ts.map +1 -0
- package/dist/memory/modules/youtube/tests/channel.test.js +61 -0
- package/dist/memory/modules/youtube/tests/channel.test.js.map +1 -0
- package/dist/memory/modules/youtube/tests/transcript-fetcher.test.d.ts +2 -0
- package/dist/memory/modules/youtube/tests/transcript-fetcher.test.d.ts.map +1 -0
- package/dist/memory/modules/youtube/tests/transcript-fetcher.test.js +23 -0
- package/dist/memory/modules/youtube/tests/transcript-fetcher.test.js.map +1 -0
- package/dist/memory/modules/youtube/tests/youtube.test.d.ts +2 -0
- package/dist/memory/modules/youtube/tests/youtube.test.d.ts.map +1 -0
- package/dist/memory/modules/youtube/tests/youtube.test.js +52 -0
- package/dist/memory/modules/youtube/tests/youtube.test.js.map +1 -0
- package/dist/memory/modules/youtube/tools.d.ts +5 -0
- package/dist/memory/modules/youtube/tools.d.ts.map +1 -0
- package/dist/memory/modules/youtube/tools.js +182 -0
- package/dist/memory/modules/youtube/tools.js.map +1 -0
- package/dist/memory/modules/youtube/transcript-fetcher.d.ts +17 -0
- package/dist/memory/modules/youtube/transcript-fetcher.d.ts.map +1 -0
- package/dist/memory/modules/youtube/transcript-fetcher.js +178 -0
- package/dist/memory/modules/youtube/transcript-fetcher.js.map +1 -0
- package/dist/memory/modules/youtube/watcher.d.ts +30 -0
- package/dist/memory/modules/youtube/watcher.d.ts.map +1 -0
- package/dist/memory/modules/youtube/watcher.js +198 -0
- package/dist/memory/modules/youtube/watcher.js.map +1 -0
- package/dist/memory/public/tools.d.ts +5 -0
- package/dist/memory/public/tools.d.ts.map +1 -0
- package/dist/memory/public/tools.js +1761 -0
- package/dist/memory/public/tools.js.map +1 -0
- package/dist/private/algorithms/chunker-semantic.d.ts +3 -0
- package/dist/private/algorithms/chunker-semantic.d.ts.map +1 -0
- package/dist/private/algorithms/chunker-semantic.js +70 -0
- package/dist/private/algorithms/chunker-semantic.js.map +1 -0
- package/dist/private/algorithms/find-related-smart.d.ts +4 -0
- package/dist/private/algorithms/find-related-smart.d.ts.map +1 -0
- package/dist/private/algorithms/find-related-smart.js +52 -0
- package/dist/private/algorithms/find-related-smart.js.map +1 -0
- package/dist/private/algorithms/graph-semantic-edges.d.ts +4 -0
- package/dist/private/algorithms/graph-semantic-edges.d.ts.map +1 -0
- package/dist/private/algorithms/graph-semantic-edges.js +38 -0
- package/dist/private/algorithms/graph-semantic-edges.js.map +1 -0
- package/dist/private/algorithms/search-all-smart.d.ts +9 -0
- package/dist/private/algorithms/search-all-smart.d.ts.map +1 -0
- package/dist/private/algorithms/search-all-smart.js +62 -0
- package/dist/private/algorithms/search-all-smart.js.map +1 -0
- package/dist/private/index.d.ts +7 -0
- package/dist/private/index.d.ts.map +1 -0
- package/dist/private/index.js +39 -0
- package/dist/private/index.js.map +1 -0
- package/dist/private/prompts/extraction-system.d.ts +2 -0
- package/dist/private/prompts/extraction-system.d.ts.map +1 -0
- package/dist/private/prompts/extraction-system.js +15 -0
- package/dist/private/prompts/extraction-system.js.map +1 -0
- package/dist/private/prompts/suggest-properties.d.ts +2 -0
- package/dist/private/prompts/suggest-properties.d.ts.map +1 -0
- package/dist/private/prompts/suggest-properties.js +18 -0
- package/dist/private/prompts/suggest-properties.js.map +1 -0
- package/dist/private/tests/find-related-smart.test.d.ts +2 -0
- package/dist/private/tests/find-related-smart.test.d.ts.map +1 -0
- package/dist/private/tests/find-related-smart.test.js +86 -0
- package/dist/private/tests/find-related-smart.test.js.map +1 -0
- package/dist/private/tests/property-extractor-smart.test.d.ts +2 -0
- package/dist/private/tests/property-extractor-smart.test.d.ts.map +1 -0
- package/dist/private/tests/property-extractor-smart.test.js +26 -0
- package/dist/private/tests/property-extractor-smart.test.js.map +1 -0
- package/dist/scripts/install-ollama.d.ts +3 -0
- package/dist/scripts/install-ollama.d.ts.map +1 -0
- package/dist/scripts/install-ollama.js +78 -0
- package/dist/scripts/install-ollama.js.map +1 -0
- package/dist/scripts/install.d.ts +3 -0
- package/dist/scripts/install.d.ts.map +1 -0
- package/dist/scripts/install.js +191 -0
- package/dist/scripts/install.js.map +1 -0
- package/dist/scripts/pair.d.ts +3 -0
- package/dist/scripts/pair.d.ts.map +1 -0
- package/dist/scripts/pair.js +78 -0
- package/dist/scripts/pair.js.map +1 -0
- package/dist/scripts/rebuild.d.ts +20 -0
- package/dist/scripts/rebuild.d.ts.map +1 -0
- package/dist/scripts/rebuild.js +171 -0
- package/dist/scripts/rebuild.js.map +1 -0
- package/dist/scripts/reindex.d.ts +3 -0
- package/dist/scripts/reindex.d.ts.map +1 -0
- package/dist/scripts/reindex.js +23 -0
- package/dist/scripts/reindex.js.map +1 -0
- package/dist/scripts/serve.d.ts +3 -0
- package/dist/scripts/serve.d.ts.map +1 -0
- package/dist/scripts/serve.js +57 -0
- package/dist/scripts/serve.js.map +1 -0
- package/dist/scripts/service.d.ts +19 -0
- package/dist/scripts/service.d.ts.map +1 -0
- package/dist/scripts/service.js +257 -0
- package/dist/scripts/service.js.map +1 -0
- package/dist/server/api/daily.d.ts +3 -0
- package/dist/server/api/daily.d.ts.map +1 -0
- package/dist/server/api/daily.js +44 -0
- package/dist/server/api/daily.js.map +1 -0
- package/dist/server/api/graph.d.ts +26 -0
- package/dist/server/api/graph.d.ts.map +1 -0
- package/dist/server/api/graph.js +80 -0
- package/dist/server/api/graph.js.map +1 -0
- package/dist/server/api/integrations.d.ts +4 -0
- package/dist/server/api/integrations.d.ts.map +1 -0
- package/dist/server/api/integrations.js +228 -0
- package/dist/server/api/integrations.js.map +1 -0
- package/dist/server/api/memories.d.ts +4 -0
- package/dist/server/api/memories.d.ts.map +1 -0
- package/dist/server/api/memories.js +267 -0
- package/dist/server/api/memories.js.map +1 -0
- package/dist/server/api/reindex.d.ts +3 -0
- package/dist/server/api/reindex.d.ts.map +1 -0
- package/dist/server/api/reindex.js +18 -0
- package/dist/server/api/reindex.js.map +1 -0
- package/dist/server/api/settings.d.ts +3 -0
- package/dist/server/api/settings.d.ts.map +1 -0
- package/dist/server/api/settings.js +24 -0
- package/dist/server/api/settings.js.map +1 -0
- package/dist/server/api/sources.d.ts +4 -0
- package/dist/server/api/sources.d.ts.map +1 -0
- package/dist/server/api/sources.js +45 -0
- package/dist/server/api/sources.js.map +1 -0
- package/dist/server/api/sync-status.d.ts +3 -0
- package/dist/server/api/sync-status.d.ts.map +1 -0
- package/dist/server/api/sync-status.js +43 -0
- package/dist/server/api/sync-status.js.map +1 -0
- package/dist/server/api/types.d.ts +3 -0
- package/dist/server/api/types.d.ts.map +1 -0
- package/dist/server/api/types.js +20 -0
- package/dist/server/api/types.js.map +1 -0
- package/dist/server/api/views.d.ts +25 -0
- package/dist/server/api/views.d.ts.map +1 -0
- package/dist/server/api/views.js +54 -0
- package/dist/server/api/views.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +3 -0
- package/dist/server/index.js.map +1 -0
- package/dist/sync/apply.d.ts +55 -0
- package/dist/sync/apply.d.ts.map +1 -0
- package/dist/sync/apply.js +277 -0
- package/dist/sync/apply.js.map +1 -0
- package/dist/sync/channel-client.d.ts +27 -0
- package/dist/sync/channel-client.d.ts.map +1 -0
- package/dist/sync/channel-client.js +154 -0
- package/dist/sync/channel-client.js.map +1 -0
- package/dist/sync/cloud-saves.d.ts +49 -0
- package/dist/sync/cloud-saves.d.ts.map +1 -0
- package/dist/sync/cloud-saves.js +182 -0
- package/dist/sync/cloud-saves.js.map +1 -0
- package/dist/sync/ed25519.d.ts +54 -0
- package/dist/sync/ed25519.d.ts.map +1 -0
- package/dist/sync/ed25519.js +136 -0
- package/dist/sync/ed25519.js.map +1 -0
- package/dist/sync/ops-log.d.ts +43 -0
- package/dist/sync/ops-log.d.ts.map +1 -0
- package/dist/sync/ops-log.js +153 -0
- package/dist/sync/ops-log.js.map +1 -0
- package/dist/sync/recovery-setup.d.ts +26 -0
- package/dist/sync/recovery-setup.d.ts.map +1 -0
- package/dist/sync/recovery-setup.js +113 -0
- package/dist/sync/recovery-setup.js.map +1 -0
- package/dist/sync/replay.d.ts +19 -0
- package/dist/sync/replay.d.ts.map +1 -0
- package/dist/sync/replay.js +59 -0
- package/dist/sync/replay.js.map +1 -0
- package/dist/sync/shamir.d.ts +22 -0
- package/dist/sync/shamir.d.ts.map +1 -0
- package/dist/sync/shamir.js +109 -0
- package/dist/sync/shamir.js.map +1 -0
- package/dist/sync/tests/apply.test.d.ts +4 -0
- package/dist/sync/tests/apply.test.d.ts.map +1 -0
- package/dist/sync/tests/apply.test.js +119 -0
- package/dist/sync/tests/apply.test.js.map +1 -0
- package/dist/sync/tests/ops-log.test.d.ts +2 -0
- package/dist/sync/tests/ops-log.test.d.ts.map +1 -0
- package/dist/sync/tests/ops-log.test.js +105 -0
- package/dist/sync/tests/ops-log.test.js.map +1 -0
- package/dist/sync/tests/two-device-sync.test.d.ts +2 -0
- package/dist/sync/tests/two-device-sync.test.d.ts.map +1 -0
- package/dist/sync/tests/two-device-sync.test.js +250 -0
- package/dist/sync/tests/two-device-sync.test.js.map +1 -0
- package/dist/sync/types.d.ts +87 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/sync/types.js +37 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/tests/chunker.test.d.ts +2 -0
- package/dist/tests/chunker.test.d.ts.map +1 -0
- package/dist/tests/chunker.test.js +24 -0
- package/dist/tests/chunker.test.js.map +1 -0
- package/dist/tests/cloud-auth.test.d.ts +2 -0
- package/dist/tests/cloud-auth.test.d.ts.map +1 -0
- package/dist/tests/cloud-auth.test.js +75 -0
- package/dist/tests/cloud-auth.test.js.map +1 -0
- package/dist/tests/cloud-crypto.test.d.ts +2 -0
- package/dist/tests/cloud-crypto.test.d.ts.map +1 -0
- package/dist/tests/cloud-crypto.test.js +58 -0
- package/dist/tests/cloud-crypto.test.js.map +1 -0
- package/dist/tests/cloud-integration.test.d.ts +2 -0
- package/dist/tests/cloud-integration.test.d.ts.map +1 -0
- package/dist/tests/cloud-integration.test.js +193 -0
- package/dist/tests/cloud-integration.test.js.map +1 -0
- package/dist/tests/cloud-pairing.test.d.ts +2 -0
- package/dist/tests/cloud-pairing.test.d.ts.map +1 -0
- package/dist/tests/cloud-pairing.test.js +86 -0
- package/dist/tests/cloud-pairing.test.js.map +1 -0
- package/dist/tests/cloud-saves-integration.test.d.ts +2 -0
- package/dist/tests/cloud-saves-integration.test.d.ts.map +1 -0
- package/dist/tests/cloud-saves-integration.test.js +92 -0
- package/dist/tests/cloud-saves-integration.test.js.map +1 -0
- package/dist/tests/cloud-transit.test.d.ts +2 -0
- package/dist/tests/cloud-transit.test.d.ts.map +1 -0
- package/dist/tests/cloud-transit.test.js +263 -0
- package/dist/tests/cloud-transit.test.js.map +1 -0
- package/dist/tests/config.test.d.ts +2 -0
- package/dist/tests/config.test.d.ts.map +1 -0
- package/dist/tests/config.test.js +25 -0
- package/dist/tests/config.test.js.map +1 -0
- package/dist/tests/db.test.d.ts +2 -0
- package/dist/tests/db.test.d.ts.map +1 -0
- package/dist/tests/db.test.js +75 -0
- package/dist/tests/db.test.js.map +1 -0
- package/dist/tests/embeddings-providers.test.d.ts +2 -0
- package/dist/tests/embeddings-providers.test.d.ts.map +1 -0
- package/dist/tests/embeddings-providers.test.js +62 -0
- package/dist/tests/embeddings-providers.test.js.map +1 -0
- package/dist/tests/embeddings.test.d.ts +2 -0
- package/dist/tests/embeddings.test.d.ts.map +1 -0
- package/dist/tests/embeddings.test.js +22 -0
- package/dist/tests/embeddings.test.js.map +1 -0
- package/dist/tests/integrations-api.test.d.ts +2 -0
- package/dist/tests/integrations-api.test.d.ts.map +1 -0
- package/dist/tests/integrations-api.test.js +129 -0
- package/dist/tests/integrations-api.test.js.map +1 -0
- package/dist/tests/memory-store.test.d.ts +2 -0
- package/dist/tests/memory-store.test.d.ts.map +1 -0
- package/dist/tests/memory-store.test.js +129 -0
- package/dist/tests/memory-store.test.js.map +1 -0
- package/dist/tests/module-registry.test.d.ts +2 -0
- package/dist/tests/module-registry.test.d.ts.map +1 -0
- package/dist/tests/module-registry.test.js +44 -0
- package/dist/tests/module-registry.test.js.map +1 -0
- package/dist/tests/property-extractor.test.d.ts +2 -0
- package/dist/tests/property-extractor.test.d.ts.map +1 -0
- package/dist/tests/property-extractor.test.js +24 -0
- package/dist/tests/property-extractor.test.js.map +1 -0
- package/dist/tests/public-tools.test.d.ts +2 -0
- package/dist/tests/public-tools.test.d.ts.map +1 -0
- package/dist/tests/public-tools.test.js +270 -0
- package/dist/tests/public-tools.test.js.map +1 -0
- package/dist/tests/reindex.test.d.ts +2 -0
- package/dist/tests/reindex.test.d.ts.map +1 -0
- package/dist/tests/reindex.test.js +58 -0
- package/dist/tests/reindex.test.js.map +1 -0
- package/dist/tests/shamir.test.d.ts +2 -0
- package/dist/tests/shamir.test.d.ts.map +1 -0
- package/dist/tests/shamir.test.js +57 -0
- package/dist/tests/shamir.test.js.map +1 -0
- package/dist/tests/source-registry.test.d.ts +2 -0
- package/dist/tests/source-registry.test.d.ts.map +1 -0
- package/dist/tests/source-registry.test.js +58 -0
- package/dist/tests/source-registry.test.js.map +1 -0
- package/dist/tests/types.test.d.ts +2 -0
- package/dist/tests/types.test.d.ts.map +1 -0
- package/dist/tests/types.test.js +26 -0
- package/dist/tests/types.test.js.map +1 -0
- package/dist/tests/vector.test.d.ts +2 -0
- package/dist/tests/vector.test.d.ts.map +1 -0
- package/dist/tests/vector.test.js +61 -0
- package/dist/tests/vector.test.js.map +1 -0
- package/dist/tests/wikilinks.test.d.ts +2 -0
- package/dist/tests/wikilinks.test.d.ts.map +1 -0
- package/dist/tests/wikilinks.test.js +20 -0
- package/dist/tests/wikilinks.test.js.map +1 -0
- package/dist/tools/index.d.ts +22 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +38 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types.d.ts +134 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +25 -0
- package/dist/types.js.map +1 -0
- package/dist/vector/store.d.ts +28 -0
- package/dist/vector/store.d.ts.map +1 -0
- package/dist/vector/store.js +132 -0
- package/dist/vector/store.js.map +1 -0
- package/dist/webapp/api/daily.d.ts +3 -0
- package/dist/webapp/api/daily.d.ts.map +1 -0
- package/dist/webapp/api/daily.js +44 -0
- package/dist/webapp/api/daily.js.map +1 -0
- package/dist/webapp/api/graph.d.ts +26 -0
- package/dist/webapp/api/graph.d.ts.map +1 -0
- package/dist/webapp/api/graph.js +80 -0
- package/dist/webapp/api/graph.js.map +1 -0
- package/dist/webapp/api/memories.d.ts +4 -0
- package/dist/webapp/api/memories.d.ts.map +1 -0
- package/dist/webapp/api/memories.js +70 -0
- package/dist/webapp/api/memories.js.map +1 -0
- package/dist/webapp/api/reindex.d.ts +3 -0
- package/dist/webapp/api/reindex.d.ts.map +1 -0
- package/dist/webapp/api/reindex.js +18 -0
- package/dist/webapp/api/reindex.js.map +1 -0
- package/dist/webapp/api/settings.d.ts +3 -0
- package/dist/webapp/api/settings.d.ts.map +1 -0
- package/dist/webapp/api/settings.js +24 -0
- package/dist/webapp/api/settings.js.map +1 -0
- package/dist/webapp/api/sources.d.ts +4 -0
- package/dist/webapp/api/sources.d.ts.map +1 -0
- package/dist/webapp/api/sources.js +45 -0
- package/dist/webapp/api/sources.js.map +1 -0
- package/dist/webapp/api/sync-status.d.ts +3 -0
- package/dist/webapp/api/sync-status.d.ts.map +1 -0
- package/dist/webapp/api/sync-status.js +43 -0
- package/dist/webapp/api/sync-status.js.map +1 -0
- package/dist/webapp/api/types.d.ts +3 -0
- package/dist/webapp/api/types.d.ts.map +1 -0
- package/dist/webapp/api/types.js +20 -0
- package/dist/webapp/api/types.js.map +1 -0
- package/dist/webapp/api/views.d.ts +25 -0
- package/dist/webapp/api/views.d.ts.map +1 -0
- package/dist/webapp/api/views.js +54 -0
- package/dist/webapp/api/views.js.map +1 -0
- package/dist/webapp/mcp-http.d.ts +2 -0
- package/dist/webapp/mcp-http.d.ts.map +1 -0
- package/dist/webapp/mcp-http.js +3 -0
- package/dist/webapp/mcp-http.js.map +1 -0
- package/dist/webapp/server.d.ts +2 -0
- package/dist/webapp/server.d.ts.map +1 -0
- package/dist/webapp/server.js +3 -0
- package/dist/webapp/server.js.map +1 -0
- package/dist/webapp/tests/api.test.d.ts +2 -0
- package/dist/webapp/tests/api.test.d.ts.map +1 -0
- package/dist/webapp/tests/api.test.js +125 -0
- package/dist/webapp/tests/api.test.js.map +1 -0
- package/dist/webapp/tests/mcp-http.test.d.ts +2 -0
- package/dist/webapp/tests/mcp-http.test.d.ts.map +1 -0
- package/dist/webapp/tests/mcp-http.test.js +47 -0
- package/dist/webapp/tests/mcp-http.test.js.map +1 -0
- package/dist/webapp/websocket.d.ts +2 -0
- package/dist/webapp/websocket.d.ts.map +1 -0
- package/dist/webapp/websocket.js +3 -0
- package/dist/webapp/websocket.js.map +1 -0
- package/package.json +128 -0
- package/src/private/README.md +49 -0
package/SKILL.md
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: engram-semantic-memory
|
|
3
|
+
description: Use EngramMCP to give yourself persistent semantic memory — write what matters with remember(), retrieve later with recall(), build connections with relate(). Works locally via 768-dim embeddings + FTS5.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# EngramMCP — Calling Agent Guide
|
|
7
|
+
|
|
8
|
+
> **Tool naming note:** Tool names in this doc are written WITHOUT a client prefix. Your runtime may expose them as `mcp__engram__<name>` (Claude Code) or bare `<name>` (Anthropic API, OpenAI, generic MCP client). Use whichever form your runtime exposes.
|
|
9
|
+
|
|
10
|
+
## Avoiding tool loops
|
|
11
|
+
|
|
12
|
+
The agent MUST NOT call any single tool more than 3 times in a single turn on the same input.
|
|
13
|
+
|
|
14
|
+
If a tool returns:
|
|
15
|
+
- **success** → done, move on
|
|
16
|
+
- **empty result** → the answer is genuinely 'no' or the input is wrong; do not retry with the same input
|
|
17
|
+
- **error** → retry once with adjusted input; if still fails, surface to user
|
|
18
|
+
- **'pending' (jobs)** → respect `retry_after_ms` hint, stop after `should_give_up: true` (10 polls)
|
|
19
|
+
|
|
20
|
+
### Idempotent tools (safe to call multiple times — will not create duplicates)
|
|
21
|
+
|
|
22
|
+
| Tool | Idempotency key |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `remember` | content_hash + type |
|
|
25
|
+
| `update` | returns `{updated: false}` if no-op |
|
|
26
|
+
| `forget` | returns success even if already deleted |
|
|
27
|
+
| `watch` | returns `{already_watching: true}` if already registered |
|
|
28
|
+
| `create_type` | returns `{created: false}` if type already exists |
|
|
29
|
+
|
|
30
|
+
### Non-retryable tools (require user action — calling again creates orphan state)
|
|
31
|
+
|
|
32
|
+
| Tool | Reason |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `connect_drive` | Starts OAuth flow — wait for user browser action |
|
|
35
|
+
| `connect_notion` | Starts OAuth flow — wait for user browser action |
|
|
36
|
+
|
|
37
|
+
### Cache within turn (call once, reuse result)
|
|
38
|
+
|
|
39
|
+
- `list_types` — cheap but stable within a conversation
|
|
40
|
+
- `list_sources` — slow on large workspaces
|
|
41
|
+
|
|
42
|
+
### If you find yourself about to call the same tool with the same args 2+ times: STOP, surface to user.
|
|
43
|
+
|
|
44
|
+
## When to use this skill
|
|
45
|
+
|
|
46
|
+
Use Engram whenever you want information to outlast the current context window.
|
|
47
|
+
|
|
48
|
+
- User shares preferences, facts, decisions, contact info, project context
|
|
49
|
+
- User describes their workflow, what they're building, what they care about
|
|
50
|
+
- User mentions documents or URLs that should be ingested for later context
|
|
51
|
+
- Start of every conversation: call `recent({limit: 10})` to refresh stale context
|
|
52
|
+
- User explicitly asks to "remember" or "save" something
|
|
53
|
+
|
|
54
|
+
## Anti-patterns
|
|
55
|
+
|
|
56
|
+
- **DON'T** `remember()` ephemeral chitchat. Memory should be high-signal.
|
|
57
|
+
- **DON'T** `remember()` your own outputs unless the user explicitly says "remember this".
|
|
58
|
+
- **DON'T** skip tags — untagged memories rarely surface in `recall`.
|
|
59
|
+
- **DON'T** call `recall()` with the literal user question. Extract the *topic*. "What did I say about Alice?" → `recall({query: "alice"})`.
|
|
60
|
+
- **DON'T** call `recall()` when you already have an id — use `get(id)`.
|
|
61
|
+
- **DON'T** call `get()` when searching by concept — use `recall()`.
|
|
62
|
+
|
|
63
|
+
## The 24 tools — quick reference
|
|
64
|
+
|
|
65
|
+
All tools are public — no `--admin` flag needed.
|
|
66
|
+
|
|
67
|
+
| Tool | Use when |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `remember(content, title, tags, type?)` | User shares anything worth keeping. Idempotent on content_hash. |
|
|
70
|
+
| `recall(query, types?, limit?)` | Surface past memories on a topic. Don't retry with same query if empty. |
|
|
71
|
+
| `get(id)` | You already have an id and need the full record. Don't retry on not_found. |
|
|
72
|
+
| `update(id, ...)` | User corrects a fact or wants to retag/rename. No-op if unchanged. |
|
|
73
|
+
| `forget(id)` | User explicitly asks to erase something. Idempotent. |
|
|
74
|
+
| `relate(id, limit?)` | "What else is connected to this?" Don't retry if empty. |
|
|
75
|
+
| `list_types()` | First call when joining an existing Engram. Cache result for turn. |
|
|
76
|
+
| `recent(limit?, types?)` | Start of conversation — fetch fresh context. |
|
|
77
|
+
| `ingest(uri, type?)` | User mentions a file path, YouTube URL, Drive doc, etc. |
|
|
78
|
+
| `get_ingest_status(job_id)` | Check status of async audio/video ingest. Respect retry_after_ms. |
|
|
79
|
+
| `suggest_properties(id)` | A memory came in without title/tags. Call once, then update(). |
|
|
80
|
+
| `watch(source_type, target_id, opts?)` | Start continuous sync. Idempotent. |
|
|
81
|
+
| `unwatch(source_type?, target_id?, source_id?)` | Stop syncing a source (memories kept). Idempotent. |
|
|
82
|
+
| `list_sources(source_type?)` | See what sources are being auto-synced. Cache for turn. |
|
|
83
|
+
| `create_type(name, display_name?, schema?)` | Create a user-defined memory type. Idempotent on name. |
|
|
84
|
+
| `delete_type(name, confirm: true)` | Remove a custom type definition. Requires confirm; shows summary first. |
|
|
85
|
+
| `connect_drive` | Initiate Google Drive OAuth. Requires user browser action — DO NOT retry. |
|
|
86
|
+
| `list_drive_files(query?, limit?)` | List Drive files. Cache result per query per turn. |
|
|
87
|
+
| `connect_notion` | Initiate Notion OAuth. Requires user browser action — DO NOT retry. |
|
|
88
|
+
| `list_notion_pages(query?, limit?)` | Search Notion workspace. Cache result per query per turn. |
|
|
89
|
+
| `import_watch_later(playlistUrl, limit?)` | Bulk import YouTube playlist. Slow — never call twice. |
|
|
90
|
+
| `analyze_patterns(topic, types?, limit?, lookback_days?)` | Cross-memory pattern synthesis on a topic. Returns bundle + instruction. Idempotent. |
|
|
91
|
+
| `summarize_recent(days?, types?, limit?)` | Recent memory digest. Returns bundle + summarization instruction. Idempotent. |
|
|
92
|
+
| `find_gaps(topic, lookback_days?)` | Gap analysis on a topic. Returns bundle + gap-finding instruction. Idempotent. |
|
|
93
|
+
|
|
94
|
+
## Wikilinks
|
|
95
|
+
|
|
96
|
+
Mention related memories by `[[id]]` or `[[title]]` in your `remember` content. The system auto-extracts these as graph edges.
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
remember({
|
|
100
|
+
content: "Met with [[Alice]] today about [[Project Atlas]]. She prefers async over standups.",
|
|
101
|
+
title: "Alice 1:1 May 17",
|
|
102
|
+
tags: ["alice", "atlas", "preferences"]
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
When you recall a memory that mentions Atlas, `relate({id: atlasId})` will surface Alice's 1:1 as a related node.
|
|
107
|
+
|
|
108
|
+
## Tagging strategy
|
|
109
|
+
|
|
110
|
+
Tags are how memories cluster and surface. Good tags:
|
|
111
|
+
|
|
112
|
+
- **Entities**: people (`alice`, `bob`), companies (`anthropic`), projects (`atlas`)
|
|
113
|
+
- **Domains**: `programming`, `finance`, `health`, `family`
|
|
114
|
+
- **Event types**: `decision`, `blocker`, `todo`, `idea`
|
|
115
|
+
|
|
116
|
+
Bad tags:
|
|
117
|
+
- Vague: `stuff`, `misc`, `thing`
|
|
118
|
+
- Singletons that will never recur
|
|
119
|
+
- Sentence fragments
|
|
120
|
+
|
|
121
|
+
Aim for 2-5 tags per memory. A memory with zero tags is retrievable only by exact semantic match — it will miss topical queries.
|
|
122
|
+
|
|
123
|
+
## Recall strategy
|
|
124
|
+
|
|
125
|
+
`recall` does parallel fan-out across all types, then scores by semantic similarity + recency boost + MMR diversity (if private algorithms loaded). Tips:
|
|
126
|
+
|
|
127
|
+
- Restrict `types` if you know the type — it's significantly faster.
|
|
128
|
+
- Use short, topic-y queries. `"alice preferences"` not `"what does alice prefer about how we work together"`.
|
|
129
|
+
- Default limit 10 is enough for most lookups; bump to 20-30 for exhaustive sweeps.
|
|
130
|
+
- After recall, if a snippet looks relevant but incomplete, call `get(id)` for full content.
|
|
131
|
+
|
|
132
|
+
## Conversation pattern
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
[user opens chat]
|
|
136
|
+
→ recent({limit: 10}) -- see what's fresh, prime context
|
|
137
|
+
|
|
138
|
+
[user mentions a topic]
|
|
139
|
+
→ recall({query: "<noun>"}) -- retrieve relevant past memories
|
|
140
|
+
|
|
141
|
+
[user shares new info]
|
|
142
|
+
→ remember({content, title, tags})
|
|
143
|
+
|
|
144
|
+
[user mentions a file/URL]
|
|
145
|
+
→ ingest({uri: "<path or url>"})
|
|
146
|
+
|
|
147
|
+
[end of conversation, milestone reached]
|
|
148
|
+
→ remember({ -- optional: capture session summary
|
|
149
|
+
content: "Summary: ...",
|
|
150
|
+
title: "Session YYYY-MM-DD",
|
|
151
|
+
tags: ["summary", "<project>"]
|
|
152
|
+
})
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Ingest URI routing + async behavior
|
|
156
|
+
|
|
157
|
+
`ingest()` auto-routes based on the URI. No need to pick the module manually.
|
|
158
|
+
|
|
159
|
+
| URI pattern | Routed to | Sync? |
|
|
160
|
+
|---|---|---|
|
|
161
|
+
| `file://*.md`, `*.txt` | notes (read file content) | Sync |
|
|
162
|
+
| `file://*.mp3`, `*.wav`, `*.m4a`, `*.ogg`, `*.webm` | audio (Whisper transcription) | **Async** |
|
|
163
|
+
| `file://*.pdf` | notes (pdf-parse text extraction) | Sync |
|
|
164
|
+
| `file://*.png`, `*.jpg`, `*.gif` | images type | Sync |
|
|
165
|
+
| `https://www.youtube.com/watch?*`, `https://youtu.be/*` | youtube (transcript fetch) | Sync if <5min, **Async** otherwise |
|
|
166
|
+
| `https://docs.google.com/document/d/*` | drive | Sync |
|
|
167
|
+
| `https://*.notion.so/*` | notion | Sync |
|
|
168
|
+
| `obsidian://vault/<vault>/<path>` | obsidian | Sync |
|
|
169
|
+
| Any other `https://` URL | fetch + store as note | Sync |
|
|
170
|
+
|
|
171
|
+
You can override routing with `type: "audio"` etc.
|
|
172
|
+
|
|
173
|
+
### Async ingest pattern
|
|
174
|
+
|
|
175
|
+
`ingest()` returns one of two shapes:
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
// Fast paths (markdown, small pages, Drive, Notion) — synchronous:
|
|
179
|
+
{ id: "01JXY...", type: "notes", title: "My Note", status: "completed" }
|
|
180
|
+
|
|
181
|
+
// Slow paths (audio, YouTube) — async job:
|
|
182
|
+
{ job_id: "job_01JXY...", status: "pending", estimated_ms: 30000 }
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
For async jobs, poll `get_ingest_status(job_id)` every few seconds:
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
get_ingest_status("job_01JXY...")
|
|
189
|
+
// → { job_id, status: "processing", progress: 45 }
|
|
190
|
+
// → { job_id, status: "completed", memory_id: "01JXZ..." }
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Once `status === "completed"`, the `memory_id` is searchable via `recall()`.
|
|
194
|
+
|
|
195
|
+
**Tip:** You can also just fire `ingest()` and move on — the job completes in the background and becomes searchable automatically. Only poll if you need the result immediately.
|
|
196
|
+
|
|
197
|
+
## Watching sources
|
|
198
|
+
|
|
199
|
+
`watch()` is now on the public surface — no `--admin` flag needed. It requires that the source is already authenticated (run `connect_drive` / `connect_notion` once via admin or the install wizard).
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
// Watch a YouTube channel for new videos
|
|
203
|
+
watch({ source_type: "youtube", target_id: "UCxxxxxx", opts: { channelName: "My Channel" } })
|
|
204
|
+
|
|
205
|
+
// Watch an Obsidian vault
|
|
206
|
+
watch({ source_type: "obsidian", target_id: "/path/to/vault" })
|
|
207
|
+
|
|
208
|
+
// List all watched sources
|
|
209
|
+
list_sources()
|
|
210
|
+
list_sources({ source_type: "youtube" })
|
|
211
|
+
|
|
212
|
+
// Stop watching
|
|
213
|
+
unwatch({ source_id: "<id from watch()>" })
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Custom types
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
// Create a new memory type
|
|
220
|
+
create_type({ name: "books", display_name: "Books" })
|
|
221
|
+
|
|
222
|
+
// Now use it:
|
|
223
|
+
remember({ content: "...", type: "books", title: "Dune", tags: ["sci-fi"] })
|
|
224
|
+
recall({ query: "space opera", types: ["books"] })
|
|
225
|
+
|
|
226
|
+
// Remove type schema (memories kept):
|
|
227
|
+
delete_type({ name: "books", confirm: true })
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Pattern analysis tools
|
|
231
|
+
|
|
232
|
+
For high-level synthesis beyond single recall:
|
|
233
|
+
- `analyze_patterns(topic, types?, limit?, lookback_days?)` — "what patterns in my notes about X?" Returns matching memories + pre-computed aggregations (tag freq, timeline, type distribution) + a structured instruction. You (the agent) then perform the actual analysis using the bundled data. DON'T re-call `recall()` — `analyze_patterns` already includes the matching memories.
|
|
234
|
+
- `summarize_recent(days?, types?, limit?)` — "summarize my last week". Returns recent memories + summarization instruction for a digest.
|
|
235
|
+
- `find_gaps(topic, lookback_days?)` — "what's missing or unanswered about X?" Returns memories on a topic + gap-analysis instruction.
|
|
236
|
+
|
|
237
|
+
All three tools are **IDEMPOTENT** on their inputs — calling twice in the same turn returns the same bundle. DO NOT re-call with the same args.
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
// Analyze patterns across memories on a topic
|
|
241
|
+
analyze_patterns({ topic: "project atlas" })
|
|
242
|
+
// → { topic, memories_found, date_range, memories, aggregations, instruction }
|
|
243
|
+
// You then synthesize the analysis from the bundled data.
|
|
244
|
+
|
|
245
|
+
// Summarize recent activity
|
|
246
|
+
summarize_recent({ days: 7 })
|
|
247
|
+
// → { period, memories_count, memories, instruction }
|
|
248
|
+
|
|
249
|
+
// Find gaps in documentation on a topic
|
|
250
|
+
find_gaps({ topic: "alice", lookback_days: 90 })
|
|
251
|
+
// → { topic, memories_found, date_range, memories, aggregations, instruction }
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## suggest_properties workflow
|
|
255
|
+
|
|
256
|
+
For memories ingested without metadata (audio drops, Drive files named `Untitled`):
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
1. suggest_properties({id}) -- returns content + extraction instruction
|
|
260
|
+
2. Extract title, tags, sentiment from the returned content (you are the LLM)
|
|
261
|
+
3. update({id, title, tags, sentiment})
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Type semantics
|
|
265
|
+
|
|
266
|
+
| Type | Content |
|
|
267
|
+
|---|---|
|
|
268
|
+
| `notes` | Free-form notes, text files, web pages |
|
|
269
|
+
| `conversations` | Full exchanges (`"user: X\nassistant: Y"`) |
|
|
270
|
+
| `audio` | Whisper transcript from audio files |
|
|
271
|
+
| `youtube` | YouTube video transcript |
|
|
272
|
+
| `drive` | Google Drive documents |
|
|
273
|
+
| `notion` | Notion pages |
|
|
274
|
+
| `obsidian` | Obsidian markdown notes |
|
|
275
|
+
| `images` | Image metadata (no OCR yet) |
|
|
276
|
+
| custom | User-defined via admin `create_custom_type` — use `remember({type: "books", ...})` once created |
|
|
277
|
+
|
|
278
|
+
## Error handling
|
|
279
|
+
|
|
280
|
+
| Error | Cause | Fix |
|
|
281
|
+
|---|---|---|
|
|
282
|
+
| `not_found` from `get`/`update`/`forget` | Stale id | Re-recall to get fresh id |
|
|
283
|
+
| `embedding_failed` | Ollama is offline | Inform user, don't retry in a loop |
|
|
284
|
+
| `ingest` returns `error` key | URI routing failed or module error | Check URI format, confirm OAuth if Drive/Notion |
|
|
285
|
+
|
|
286
|
+
## OAuth tools — browser action required
|
|
287
|
+
|
|
288
|
+
`connect_drive` and `connect_notion` initiate OAuth flows. The agent CANNOT complete these autonomously — the user must open the returned `auth_url` in a browser.
|
|
289
|
+
|
|
290
|
+
**Correct flow:**
|
|
291
|
+
1. Agent calls `connect_drive()` → gets `{auth_url, instructions}`
|
|
292
|
+
2. Agent tells user: "Please open this URL in your browser: [auth_url]"
|
|
293
|
+
3. User completes authorization
|
|
294
|
+
4. User tells agent "Done" (or drive tools start working)
|
|
295
|
+
5. Agent verifies by calling `list_drive_files()`
|
|
296
|
+
|
|
297
|
+
The install wizard (`engram-mcp-install`) handles OAuth interactively. The dashboard at `localhost:7777` also has OAuth buttons.
|
|
298
|
+
|
|
299
|
+
**Total tool count: 24 (all public — no --admin flag needed).**
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface EngramTokens {
|
|
2
|
+
jwt: string;
|
|
3
|
+
refreshToken: string;
|
|
4
|
+
apiKey: string;
|
|
5
|
+
/** Unix ms — when the JWT expires */
|
|
6
|
+
expiresAt: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function saveTokens(tokens: EngramTokens): void;
|
|
9
|
+
export declare function loadTokens(): EngramTokens | null;
|
|
10
|
+
export declare function clearTokens(): void;
|
|
11
|
+
/**
|
|
12
|
+
* Parse expiry from a JWT payload without verifying the signature.
|
|
13
|
+
* The server will reject an expired JWT anyway; we just need the timing.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseJwtExpiry(jwt: string): number;
|
|
16
|
+
/**
|
|
17
|
+
* Return a valid (non-expired) JWT. Refreshes automatically if needed.
|
|
18
|
+
* Throws if tokens are not present or refresh fails.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getValidJwt(baseUrl: string): Promise<string>;
|
|
21
|
+
/**
|
|
22
|
+
* Return the stored API key. Throws if not paired.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getApiKey(): string;
|
|
25
|
+
/**
|
|
26
|
+
* Check whether the user has paired (tokens present in DB).
|
|
27
|
+
*/
|
|
28
|
+
export declare function isPaired(): boolean;
|
|
29
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/cloud/auth.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAoBrD;AAED,wBAAgB,UAAU,IAAI,YAAY,GAAG,IAAI,CAyBhD;AAED,wBAAgB,WAAW,IAAI,IAAI,CAGlC;AAMD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQlD;AA+BD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBlE;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAKlC;AAED;;GAEG;AACH,wBAAgB,QAAQ,IAAI,OAAO,CAElC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/cloud/auth.ts
|
|
3
|
+
*
|
|
4
|
+
* Manages the local Engram cloud account token lifecycle:
|
|
5
|
+
* - Persists JWT + refresh token in the existing oauth_tokens table
|
|
6
|
+
* (provider = 'engram_account').
|
|
7
|
+
* - Refreshes the JWT when it is within 2 minutes of expiry.
|
|
8
|
+
* - Exposes getValidJwt() and getApiKey() for use by transit-poller and
|
|
9
|
+
* bridge-client.
|
|
10
|
+
*
|
|
11
|
+
* The API key is stored in `extra_json` alongside the JWT so both are in one
|
|
12
|
+
* table row. The refresh token is stored in the `refresh_token` column.
|
|
13
|
+
*/
|
|
14
|
+
import { getDb } from '../db/index.js';
|
|
15
|
+
import { createLogger } from '../logger.js';
|
|
16
|
+
const log = createLogger('cloud:auth');
|
|
17
|
+
const PROVIDER = 'engram_account';
|
|
18
|
+
const REFRESH_BUFFER_MS = 2 * 60 * 1000; // refresh when < 2 min remaining
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Persistence (oauth_tokens table)
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
export function saveTokens(tokens) {
|
|
23
|
+
const db = getDb();
|
|
24
|
+
db.prepare(`INSERT INTO oauth_tokens (provider, access_token, refresh_token, expires_at, extra_json, updated_at)
|
|
25
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
26
|
+
ON CONFLICT(provider) DO UPDATE SET
|
|
27
|
+
access_token = excluded.access_token,
|
|
28
|
+
refresh_token = excluded.refresh_token,
|
|
29
|
+
expires_at = excluded.expires_at,
|
|
30
|
+
extra_json = excluded.extra_json,
|
|
31
|
+
updated_at = excluded.updated_at`).run(PROVIDER, tokens.jwt, tokens.refreshToken, tokens.expiresAt, JSON.stringify({ apiKey: tokens.apiKey }), Date.now());
|
|
32
|
+
log.debug('Tokens saved to oauth_tokens');
|
|
33
|
+
}
|
|
34
|
+
export function loadTokens() {
|
|
35
|
+
const db = getDb();
|
|
36
|
+
const row = db
|
|
37
|
+
.prepare(`SELECT access_token, refresh_token, expires_at, extra_json
|
|
38
|
+
FROM oauth_tokens WHERE provider = ?`)
|
|
39
|
+
.get(PROVIDER);
|
|
40
|
+
if (!row)
|
|
41
|
+
return null;
|
|
42
|
+
const extra = row.extra_json ? JSON.parse(row.extra_json) : {};
|
|
43
|
+
return {
|
|
44
|
+
jwt: row.access_token,
|
|
45
|
+
refreshToken: row.refresh_token ?? '',
|
|
46
|
+
apiKey: extra.apiKey ?? '',
|
|
47
|
+
expiresAt: row.expires_at ?? 0,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function clearTokens() {
|
|
51
|
+
getDb().prepare('DELETE FROM oauth_tokens WHERE provider = ?').run(PROVIDER);
|
|
52
|
+
log.info('Engram account tokens cleared');
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// JWT refresh
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
/**
|
|
58
|
+
* Parse expiry from a JWT payload without verifying the signature.
|
|
59
|
+
* The server will reject an expired JWT anyway; we just need the timing.
|
|
60
|
+
*/
|
|
61
|
+
export function parseJwtExpiry(jwt) {
|
|
62
|
+
const parts = jwt.split('.');
|
|
63
|
+
if (parts.length !== 3)
|
|
64
|
+
throw new Error('Malformed JWT');
|
|
65
|
+
const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'));
|
|
66
|
+
if (!payload.exp)
|
|
67
|
+
throw new Error('JWT has no exp claim');
|
|
68
|
+
return payload.exp * 1000; // convert to Unix ms
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* POST /auth/refresh to get a new JWT using the refresh token.
|
|
72
|
+
* Plan I exposes this endpoint.
|
|
73
|
+
*/
|
|
74
|
+
async function refreshJwt(refreshToken, baseUrl) {
|
|
75
|
+
const url = `${baseUrl.replace(/\/$/, '')}/auth/refresh`;
|
|
76
|
+
const res = await fetch(url, {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
headers: { 'Content-Type': 'application/json' },
|
|
79
|
+
body: JSON.stringify({ refreshToken }),
|
|
80
|
+
signal: AbortSignal.timeout(15_000),
|
|
81
|
+
});
|
|
82
|
+
if (!res.ok) {
|
|
83
|
+
const body = await res.text();
|
|
84
|
+
throw new Error(`JWT refresh failed ${res.status}: ${body.slice(0, 200)}`);
|
|
85
|
+
}
|
|
86
|
+
const data = (await res.json());
|
|
87
|
+
const newJwt = data.jwt ?? data.token ?? '';
|
|
88
|
+
if (!newJwt)
|
|
89
|
+
throw new Error('Refresh response missing jwt field');
|
|
90
|
+
return { jwt: newJwt, expiresAt: parseJwtExpiry(newJwt) };
|
|
91
|
+
}
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Public API
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
/**
|
|
96
|
+
* Return a valid (non-expired) JWT. Refreshes automatically if needed.
|
|
97
|
+
* Throws if tokens are not present or refresh fails.
|
|
98
|
+
*/
|
|
99
|
+
export async function getValidJwt(baseUrl) {
|
|
100
|
+
const tokens = loadTokens();
|
|
101
|
+
if (!tokens)
|
|
102
|
+
throw new Error('Not paired — run `engram-mcp-pair` to connect your account');
|
|
103
|
+
const now = Date.now();
|
|
104
|
+
if (tokens.expiresAt - now > REFRESH_BUFFER_MS) {
|
|
105
|
+
// Still valid
|
|
106
|
+
return tokens.jwt;
|
|
107
|
+
}
|
|
108
|
+
// Needs refresh
|
|
109
|
+
log.info('JWT near expiry — refreshing');
|
|
110
|
+
const { jwt, expiresAt } = await refreshJwt(tokens.refreshToken, baseUrl);
|
|
111
|
+
saveTokens({ ...tokens, jwt, expiresAt });
|
|
112
|
+
log.info('JWT refreshed successfully');
|
|
113
|
+
return jwt;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Return the stored API key. Throws if not paired.
|
|
117
|
+
*/
|
|
118
|
+
export function getApiKey() {
|
|
119
|
+
const tokens = loadTokens();
|
|
120
|
+
if (!tokens)
|
|
121
|
+
throw new Error('Not paired — run `engram-mcp-pair` to connect your account');
|
|
122
|
+
if (!tokens.apiKey)
|
|
123
|
+
throw new Error('Paired but API key missing — re-pair to fix');
|
|
124
|
+
return tokens.apiKey;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Check whether the user has paired (tokens present in DB).
|
|
128
|
+
*/
|
|
129
|
+
export function isPaired() {
|
|
130
|
+
return loadTokens() !== null;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/cloud/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface BridgeClientOptions {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
localPort: number;
|
|
4
|
+
}
|
|
5
|
+
export interface BridgeClient {
|
|
6
|
+
/** Stop the client and close the WebSocket. */
|
|
7
|
+
stop: () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function startBridgeClient(opts: BridgeClientOptions): BridgeClient;
|
|
10
|
+
//# sourceMappingURL=bridge-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-client.d.ts","sourceRoot":"","sources":["../../src/cloud/bridge-client.ts"],"names":[],"mappings":"AA0HA,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,mBAAmB,GAAG,YAAY,CA8FzE"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/cloud/bridge-client.ts
|
|
3
|
+
*
|
|
4
|
+
* Bridge Relay WSS client.
|
|
5
|
+
*
|
|
6
|
+
* Maintains a persistent WebSocket connection to the Plan J Bridge Relay DO
|
|
7
|
+
* so that engram-mcp.com (Plan L) can proxy REST API requests to the user's local
|
|
8
|
+
* Express server without the PC needing a public IP or port-forward.
|
|
9
|
+
*
|
|
10
|
+
* Security constraints enforced on the PC side:
|
|
11
|
+
* - Only /api/* paths are forwarded (all other paths → 403)
|
|
12
|
+
* - Body size limit: 4MB (same as Express limit)
|
|
13
|
+
* - Read-only paths that mutate state (/api/reindex, DELETE /api/memories/*)
|
|
14
|
+
* are still allowed because they require the user to be authenticated to
|
|
15
|
+
* engram-mcp.com — the bridge is an authenticated channel, not a public one.
|
|
16
|
+
*
|
|
17
|
+
* Reconnect: exponential backoff 1s → 2s → 4s → … capped at 60s.
|
|
18
|
+
* The bridge client does NOT start if engramAccount is absent.
|
|
19
|
+
*/
|
|
20
|
+
import { WebSocket } from 'ws';
|
|
21
|
+
import { createLogger } from '../logger.js';
|
|
22
|
+
import { getValidJwt } from './auth.js';
|
|
23
|
+
const log = createLogger('cloud:bridge');
|
|
24
|
+
const RELAY_PATH = '/relay/pc';
|
|
25
|
+
const LOCAL_API_BASE = 'http://127.0.0.1';
|
|
26
|
+
const MAX_BODY_BYTES = 4 * 1024 * 1024; // 4MB
|
|
27
|
+
const RECONNECT_BASE_MS = 1_000;
|
|
28
|
+
const RECONNECT_MAX_MS = 60_000;
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Local HTTP forwarder
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
async function forwardToLocal(req, localPort) {
|
|
33
|
+
// Security: only /api/* paths
|
|
34
|
+
if (!req.path.startsWith('/api/')) {
|
|
35
|
+
return {
|
|
36
|
+
requestId: req.requestId,
|
|
37
|
+
status: 403,
|
|
38
|
+
headers: { 'Content-Type': 'application/json' },
|
|
39
|
+
body: JSON.stringify({ error: 'Bridge relay only forwards /api/* paths' }),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const url = `${LOCAL_API_BASE}:${localPort}${req.path}`;
|
|
43
|
+
const fetchInit = {
|
|
44
|
+
method: req.method,
|
|
45
|
+
headers: {
|
|
46
|
+
'Content-Type': 'application/json',
|
|
47
|
+
...req.headers,
|
|
48
|
+
},
|
|
49
|
+
signal: AbortSignal.timeout(30_000),
|
|
50
|
+
};
|
|
51
|
+
if (req.body && !['GET', 'HEAD', 'DELETE'].includes(req.method.toUpperCase())) {
|
|
52
|
+
if (req.body.length > MAX_BODY_BYTES) {
|
|
53
|
+
return {
|
|
54
|
+
requestId: req.requestId,
|
|
55
|
+
status: 413,
|
|
56
|
+
headers: { 'Content-Type': 'application/json' },
|
|
57
|
+
body: JSON.stringify({ error: 'Request body too large (max 4MB)' }),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
fetchInit.body = req.body;
|
|
61
|
+
}
|
|
62
|
+
let res;
|
|
63
|
+
try {
|
|
64
|
+
res = await fetch(url, fetchInit);
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
return {
|
|
68
|
+
requestId: req.requestId,
|
|
69
|
+
status: 502,
|
|
70
|
+
headers: { 'Content-Type': 'application/json' },
|
|
71
|
+
body: JSON.stringify({
|
|
72
|
+
error: `Local API unreachable: ${e instanceof Error ? e.message : String(e)}`,
|
|
73
|
+
}),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const responseBody = await res.text();
|
|
77
|
+
const responseHeaders = {};
|
|
78
|
+
res.headers.forEach((value, key) => {
|
|
79
|
+
responseHeaders[key] = value;
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
requestId: req.requestId,
|
|
83
|
+
status: res.status,
|
|
84
|
+
headers: responseHeaders,
|
|
85
|
+
body: responseBody,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export function startBridgeClient(opts) {
|
|
89
|
+
const wsUrl = opts.baseUrl
|
|
90
|
+
.replace(/^https:\/\//, 'wss://')
|
|
91
|
+
.replace(/^http:\/\//, 'ws://')
|
|
92
|
+
.replace(/\/$/, '') + RELAY_PATH;
|
|
93
|
+
let stopped = false;
|
|
94
|
+
let ws = null;
|
|
95
|
+
let reconnectDelay = RECONNECT_BASE_MS;
|
|
96
|
+
let reconnectTimer = null;
|
|
97
|
+
function connect() {
|
|
98
|
+
if (stopped)
|
|
99
|
+
return;
|
|
100
|
+
log.info(`Bridge relay: connecting to ${wsUrl}`);
|
|
101
|
+
// Resolve JWT asynchronously — if it fails, we retry later
|
|
102
|
+
getValidJwt(opts.baseUrl)
|
|
103
|
+
.then((jwt) => {
|
|
104
|
+
if (stopped)
|
|
105
|
+
return;
|
|
106
|
+
ws = new WebSocket(wsUrl, {
|
|
107
|
+
headers: { Authorization: `Bearer ${jwt}` },
|
|
108
|
+
});
|
|
109
|
+
ws.on('open', () => {
|
|
110
|
+
log.info('Bridge relay: connected');
|
|
111
|
+
reconnectDelay = RECONNECT_BASE_MS; // reset backoff on success
|
|
112
|
+
ws.send(JSON.stringify({ type: 'pc_ready' }));
|
|
113
|
+
});
|
|
114
|
+
ws.on('message', async (data) => {
|
|
115
|
+
let req;
|
|
116
|
+
try {
|
|
117
|
+
req = JSON.parse(data.toString());
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
log.warn('Bridge relay: received non-JSON message, ignoring');
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
log.debug(`Bridge relay: tunnel request ${req.requestId} ${req.method} ${req.path}`);
|
|
124
|
+
const response = await forwardToLocal(req, opts.localPort);
|
|
125
|
+
if (ws?.readyState === WebSocket.OPEN) {
|
|
126
|
+
ws.send(JSON.stringify(response));
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
ws.on('close', (code, reason) => {
|
|
130
|
+
log.warn(`Bridge relay: disconnected (code=${code}, reason=${reason.toString().slice(0, 80)})`);
|
|
131
|
+
scheduleReconnect();
|
|
132
|
+
});
|
|
133
|
+
ws.on('error', (err) => {
|
|
134
|
+
log.error(`Bridge relay: WS error: ${err.message}`);
|
|
135
|
+
// 'close' event fires after 'error', so reconnect is scheduled there
|
|
136
|
+
});
|
|
137
|
+
})
|
|
138
|
+
.catch((e) => {
|
|
139
|
+
log.error(`Bridge relay: could not get JWT — ${e instanceof Error ? e.message : String(e)}`);
|
|
140
|
+
scheduleReconnect();
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function scheduleReconnect() {
|
|
144
|
+
if (stopped)
|
|
145
|
+
return;
|
|
146
|
+
log.info(`Bridge relay: reconnecting in ${reconnectDelay}ms`);
|
|
147
|
+
reconnectTimer = setTimeout(() => {
|
|
148
|
+
reconnectDelay = Math.min(reconnectDelay * 2, RECONNECT_MAX_MS);
|
|
149
|
+
connect();
|
|
150
|
+
}, reconnectDelay);
|
|
151
|
+
}
|
|
152
|
+
// Start first connection
|
|
153
|
+
connect();
|
|
154
|
+
return {
|
|
155
|
+
stop: () => {
|
|
156
|
+
stopped = true;
|
|
157
|
+
if (reconnectTimer)
|
|
158
|
+
clearTimeout(reconnectTimer);
|
|
159
|
+
if (ws) {
|
|
160
|
+
ws.close(1000, 'Bridge client stopped');
|
|
161
|
+
ws = null;
|
|
162
|
+
}
|
|
163
|
+
log.info('Bridge relay: stopped');
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=bridge-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-client.js","sourceRoot":"","sources":["../../src/cloud/bridge-client.ts"],"names":[],"mappings":"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"}
|