@soederpop/luca 0.1.2 → 0.2.1
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/.github/workflows/release.yaml +167 -0
- package/CLAUDE.md +2 -0
- package/README.md +3 -0
- package/assistants/codingAssistant/ABOUT.md +3 -0
- package/assistants/codingAssistant/CORE.md +22 -17
- package/assistants/codingAssistant/hooks.ts +17 -4
- package/assistants/codingAssistant/tools.ts +1 -106
- package/assistants/inkbot/ABOUT.md +5 -0
- package/assistants/inkbot/CORE.md +71 -0
- package/assistants/inkbot/hooks.ts +14 -0
- package/assistants/inkbot/tools.ts +47 -0
- package/bun.lock +20 -4
- package/commands/inkbot.ts +353 -0
- package/commands/release.ts +75 -181
- package/dist/agi/container.server.d.ts +63 -0
- package/dist/agi/container.server.d.ts.map +1 -0
- package/dist/agi/endpoints/ask.d.ts +20 -0
- package/dist/agi/endpoints/ask.d.ts.map +1 -0
- package/dist/agi/endpoints/conversations/[id].d.ts +27 -0
- package/dist/agi/endpoints/conversations/[id].d.ts.map +1 -0
- package/dist/agi/endpoints/conversations.d.ts +18 -0
- package/dist/agi/endpoints/conversations.d.ts.map +1 -0
- package/dist/agi/endpoints/experts.d.ts +8 -0
- package/dist/agi/endpoints/experts.d.ts.map +1 -0
- package/dist/agi/feature.d.ts +9 -0
- package/dist/agi/feature.d.ts.map +1 -0
- package/dist/agi/features/assistant.d.ts +509 -0
- package/dist/agi/features/assistant.d.ts.map +1 -0
- package/dist/agi/features/assistants-manager.d.ts +236 -0
- package/dist/agi/features/assistants-manager.d.ts.map +1 -0
- package/dist/agi/features/autonomous-assistant.d.ts +281 -0
- package/dist/agi/features/autonomous-assistant.d.ts.map +1 -0
- package/dist/agi/features/browser-use.d.ts +479 -0
- package/dist/agi/features/browser-use.d.ts.map +1 -0
- package/dist/agi/features/claude-code.d.ts +824 -0
- package/dist/agi/features/claude-code.d.ts.map +1 -0
- package/dist/agi/features/conversation-history.d.ts +245 -0
- package/dist/agi/features/conversation-history.d.ts.map +1 -0
- package/dist/agi/features/conversation.d.ts +464 -0
- package/dist/agi/features/conversation.d.ts.map +1 -0
- package/dist/agi/features/docs-reader.d.ts +72 -0
- package/dist/agi/features/docs-reader.d.ts.map +1 -0
- package/dist/agi/features/file-tools.d.ts +110 -0
- package/dist/agi/features/file-tools.d.ts.map +1 -0
- package/dist/agi/features/luca-coder.d.ts +323 -0
- package/dist/agi/features/luca-coder.d.ts.map +1 -0
- package/dist/agi/features/openai-codex.d.ts +381 -0
- package/dist/agi/features/openai-codex.d.ts.map +1 -0
- package/dist/agi/features/openapi.d.ts +200 -0
- package/dist/agi/features/openapi.d.ts.map +1 -0
- package/dist/agi/features/skills-library.d.ts +167 -0
- package/dist/agi/features/skills-library.d.ts.map +1 -0
- package/dist/agi/index.d.ts +5 -0
- package/dist/agi/index.d.ts.map +1 -0
- package/dist/agi/lib/interceptor-chain.d.ts +44 -0
- package/dist/agi/lib/interceptor-chain.d.ts.map +1 -0
- package/dist/agi/lib/token-counter.d.ts +13 -0
- package/dist/agi/lib/token-counter.d.ts.map +1 -0
- package/dist/bootstrap/generated.d.ts +5 -0
- package/dist/bootstrap/generated.d.ts.map +1 -0
- package/dist/browser.d.ts +12 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/bus.d.ts +29 -0
- package/dist/bus.d.ts.map +1 -0
- package/dist/cli/build-info.d.ts +4 -0
- package/dist/cli/build-info.d.ts.map +1 -0
- package/dist/cli/cli.d.ts +3 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/client.d.ts +60 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/clients/civitai/index.d.ts +472 -0
- package/dist/clients/civitai/index.d.ts.map +1 -0
- package/dist/clients/client-template.d.ts +30 -0
- package/dist/clients/client-template.d.ts.map +1 -0
- package/dist/clients/comfyui/index.d.ts +281 -0
- package/dist/clients/comfyui/index.d.ts.map +1 -0
- package/dist/clients/elevenlabs/index.d.ts +197 -0
- package/dist/clients/elevenlabs/index.d.ts.map +1 -0
- package/dist/clients/graph.d.ts +64 -0
- package/dist/clients/graph.d.ts.map +1 -0
- package/dist/clients/openai/index.d.ts +247 -0
- package/dist/clients/openai/index.d.ts.map +1 -0
- package/dist/clients/rest.d.ts +92 -0
- package/dist/clients/rest.d.ts.map +1 -0
- package/dist/clients/supabase/index.d.ts +176 -0
- package/dist/clients/supabase/index.d.ts.map +1 -0
- package/dist/clients/websocket.d.ts +127 -0
- package/dist/clients/websocket.d.ts.map +1 -0
- package/dist/command.d.ts +163 -0
- package/dist/command.d.ts.map +1 -0
- package/dist/commands/bootstrap.d.ts +20 -0
- package/dist/commands/bootstrap.d.ts.map +1 -0
- package/dist/commands/chat.d.ts +37 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/code.d.ts +28 -0
- package/dist/commands/code.d.ts.map +1 -0
- package/dist/commands/console.d.ts +22 -0
- package/dist/commands/console.d.ts.map +1 -0
- package/dist/commands/describe.d.ts +50 -0
- package/dist/commands/describe.d.ts.map +1 -0
- package/dist/commands/eval.d.ts +23 -0
- package/dist/commands/eval.d.ts.map +1 -0
- package/dist/commands/help.d.ts +25 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/index.d.ts +18 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/introspect.d.ts +24 -0
- package/dist/commands/introspect.d.ts.map +1 -0
- package/dist/commands/mcp.d.ts +35 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/prompt.d.ts +38 -0
- package/dist/commands/prompt.d.ts.map +1 -0
- package/dist/commands/run.d.ts +24 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/sandbox-mcp.d.ts +34 -0
- package/dist/commands/sandbox-mcp.d.ts.map +1 -0
- package/dist/commands/save-api-docs.d.ts +21 -0
- package/dist/commands/save-api-docs.d.ts.map +1 -0
- package/dist/commands/scaffold.d.ts +24 -0
- package/dist/commands/scaffold.d.ts.map +1 -0
- package/dist/commands/select.d.ts +22 -0
- package/dist/commands/select.d.ts.map +1 -0
- package/dist/commands/serve.d.ts +29 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/container-describer.d.ts +144 -0
- package/dist/container-describer.d.ts.map +1 -0
- package/dist/container.d.ts +451 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/endpoint.d.ts +113 -0
- package/dist/endpoint.d.ts.map +1 -0
- package/dist/feature.d.ts +47 -0
- package/dist/feature.d.ts.map +1 -0
- package/dist/graft.d.ts +29 -0
- package/dist/graft.d.ts.map +1 -0
- package/dist/hash-object.d.ts +8 -0
- package/dist/hash-object.d.ts.map +1 -0
- package/dist/helper.d.ts +209 -0
- package/dist/helper.d.ts.map +1 -0
- package/dist/introspection/generated.node.d.ts +44623 -0
- package/dist/introspection/generated.node.d.ts.map +1 -0
- package/dist/introspection/generated.web.d.ts +1412 -0
- package/dist/introspection/generated.web.d.ts.map +1 -0
- package/dist/introspection/index.d.ts +156 -0
- package/dist/introspection/index.d.ts.map +1 -0
- package/dist/introspection/scan.d.ts +147 -0
- package/dist/introspection/scan.d.ts.map +1 -0
- package/dist/node/container.d.ts +256 -0
- package/dist/node/container.d.ts.map +1 -0
- package/dist/node/feature.d.ts +9 -0
- package/dist/node/feature.d.ts.map +1 -0
- package/dist/node/features/container-link.d.ts +213 -0
- package/dist/node/features/container-link.d.ts.map +1 -0
- package/dist/node/features/content-db.d.ts +354 -0
- package/dist/node/features/content-db.d.ts.map +1 -0
- package/dist/node/features/disk-cache.d.ts +236 -0
- package/dist/node/features/disk-cache.d.ts.map +1 -0
- package/dist/node/features/dns.d.ts +511 -0
- package/dist/node/features/dns.d.ts.map +1 -0
- package/dist/node/features/docker.d.ts +485 -0
- package/dist/node/features/docker.d.ts.map +1 -0
- package/dist/node/features/downloader.d.ts +73 -0
- package/dist/node/features/downloader.d.ts.map +1 -0
- package/dist/node/features/figlet-fonts.d.ts +4 -0
- package/dist/node/features/figlet-fonts.d.ts.map +1 -0
- package/dist/node/features/file-manager.d.ts +177 -0
- package/dist/node/features/file-manager.d.ts.map +1 -0
- package/dist/node/features/fs.d.ts +635 -0
- package/dist/node/features/fs.d.ts.map +1 -0
- package/dist/node/features/git.d.ts +329 -0
- package/dist/node/features/git.d.ts.map +1 -0
- package/dist/node/features/google-auth.d.ts +200 -0
- package/dist/node/features/google-auth.d.ts.map +1 -0
- package/dist/node/features/google-calendar.d.ts +194 -0
- package/dist/node/features/google-calendar.d.ts.map +1 -0
- package/dist/node/features/google-docs.d.ts +138 -0
- package/dist/node/features/google-docs.d.ts.map +1 -0
- package/dist/node/features/google-drive.d.ts +202 -0
- package/dist/node/features/google-drive.d.ts.map +1 -0
- package/dist/node/features/google-mail.d.ts +221 -0
- package/dist/node/features/google-mail.d.ts.map +1 -0
- package/dist/node/features/google-sheets.d.ts +157 -0
- package/dist/node/features/google-sheets.d.ts.map +1 -0
- package/dist/node/features/grep.d.ts +207 -0
- package/dist/node/features/grep.d.ts.map +1 -0
- package/dist/node/features/helpers.d.ts +236 -0
- package/dist/node/features/helpers.d.ts.map +1 -0
- package/dist/node/features/ink.d.ts +332 -0
- package/dist/node/features/ink.d.ts.map +1 -0
- package/dist/node/features/ipc-socket.d.ts +298 -0
- package/dist/node/features/ipc-socket.d.ts.map +1 -0
- package/dist/node/features/json-tree.d.ts +140 -0
- package/dist/node/features/json-tree.d.ts.map +1 -0
- package/dist/node/features/networking.d.ts +373 -0
- package/dist/node/features/networking.d.ts.map +1 -0
- package/dist/node/features/nlp.d.ts +125 -0
- package/dist/node/features/nlp.d.ts.map +1 -0
- package/dist/node/features/opener.d.ts +93 -0
- package/dist/node/features/opener.d.ts.map +1 -0
- package/dist/node/features/os.d.ts +168 -0
- package/dist/node/features/os.d.ts.map +1 -0
- package/dist/node/features/package-finder.d.ts +419 -0
- package/dist/node/features/package-finder.d.ts.map +1 -0
- package/dist/node/features/postgres.d.ts +173 -0
- package/dist/node/features/postgres.d.ts.map +1 -0
- package/dist/node/features/proc.d.ts +285 -0
- package/dist/node/features/proc.d.ts.map +1 -0
- package/dist/node/features/process-manager.d.ts +427 -0
- package/dist/node/features/process-manager.d.ts.map +1 -0
- package/dist/node/features/python.d.ts +477 -0
- package/dist/node/features/python.d.ts.map +1 -0
- package/dist/node/features/redis.d.ts +247 -0
- package/dist/node/features/redis.d.ts.map +1 -0
- package/dist/node/features/repl.d.ts +84 -0
- package/dist/node/features/repl.d.ts.map +1 -0
- package/dist/node/features/runpod.d.ts +527 -0
- package/dist/node/features/runpod.d.ts.map +1 -0
- package/dist/node/features/secure-shell.d.ts +145 -0
- package/dist/node/features/secure-shell.d.ts.map +1 -0
- package/dist/node/features/semantic-search.d.ts +207 -0
- package/dist/node/features/semantic-search.d.ts.map +1 -0
- package/dist/node/features/sqlite.d.ts +180 -0
- package/dist/node/features/sqlite.d.ts.map +1 -0
- package/dist/node/features/telegram.d.ts +173 -0
- package/dist/node/features/telegram.d.ts.map +1 -0
- package/dist/node/features/transpiler.d.ts +51 -0
- package/dist/node/features/transpiler.d.ts.map +1 -0
- package/dist/node/features/tts.d.ts +108 -0
- package/dist/node/features/tts.d.ts.map +1 -0
- package/dist/node/features/ui.d.ts +562 -0
- package/dist/node/features/ui.d.ts.map +1 -0
- package/dist/node/features/vault.d.ts +90 -0
- package/dist/node/features/vault.d.ts.map +1 -0
- package/dist/node/features/vm.d.ts +285 -0
- package/dist/node/features/vm.d.ts.map +1 -0
- package/dist/node/features/yaml-tree.d.ts +118 -0
- package/dist/node/features/yaml-tree.d.ts.map +1 -0
- package/dist/node/features/yaml.d.ts +127 -0
- package/dist/node/features/yaml.d.ts.map +1 -0
- package/dist/node.d.ts +67 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/python/generated.d.ts +2 -0
- package/dist/python/generated.d.ts.map +1 -0
- package/dist/react/index.d.ts +36 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/registry.d.ts +97 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/scaffolds/generated.d.ts +13 -0
- package/dist/scaffolds/generated.d.ts.map +1 -0
- package/dist/scaffolds/template.d.ts +11 -0
- package/dist/scaffolds/template.d.ts.map +1 -0
- package/dist/schemas/base.d.ts +254 -0
- package/dist/schemas/base.d.ts.map +1 -0
- package/dist/selector.d.ts +130 -0
- package/dist/selector.d.ts.map +1 -0
- package/dist/server.d.ts +89 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/servers/express.d.ts +104 -0
- package/dist/servers/express.d.ts.map +1 -0
- package/dist/servers/mcp.d.ts +201 -0
- package/dist/servers/mcp.d.ts.map +1 -0
- package/dist/servers/socket.d.ts +121 -0
- package/dist/servers/socket.d.ts.map +1 -0
- package/dist/state.d.ts +24 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/web/clients/socket.d.ts +37 -0
- package/dist/web/clients/socket.d.ts.map +1 -0
- package/dist/web/container.d.ts +55 -0
- package/dist/web/container.d.ts.map +1 -0
- package/dist/web/extension.d.ts +4 -0
- package/dist/web/extension.d.ts.map +1 -0
- package/dist/web/feature.d.ts +8 -0
- package/dist/web/feature.d.ts.map +1 -0
- package/dist/web/features/asset-loader.d.ts +35 -0
- package/dist/web/features/asset-loader.d.ts.map +1 -0
- package/dist/web/features/container-link.d.ts +167 -0
- package/dist/web/features/container-link.d.ts.map +1 -0
- package/dist/web/features/esbuild.d.ts +51 -0
- package/dist/web/features/esbuild.d.ts.map +1 -0
- package/dist/web/features/helpers.d.ts +140 -0
- package/dist/web/features/helpers.d.ts.map +1 -0
- package/dist/web/features/network.d.ts +69 -0
- package/dist/web/features/network.d.ts.map +1 -0
- package/dist/web/features/speech.d.ts +71 -0
- package/dist/web/features/speech.d.ts.map +1 -0
- package/dist/web/features/vault.d.ts +62 -0
- package/dist/web/features/vault.d.ts.map +1 -0
- package/dist/web/features/vm.d.ts +48 -0
- package/dist/web/features/vm.d.ts.map +1 -0
- package/dist/web/features/voice-recognition.d.ts +96 -0
- package/dist/web/features/voice-recognition.d.ts.map +1 -0
- package/dist/web/shims/isomorphic-vm.d.ts +22 -0
- package/dist/web/shims/isomorphic-vm.d.ts.map +1 -0
- package/docs/apis/features/agi/assistant.md +1 -0
- package/docs/apis/features/agi/assistants-manager.md +62 -2
- package/docs/apis/features/agi/auto-assistant.md +11 -109
- package/docs/apis/features/agi/claude-code.md +138 -0
- package/docs/apis/features/agi/conversation.md +60 -31
- package/docs/apis/features/agi/luca-coder.md +407 -0
- package/docs/apis/features/agi/openapi.md +2 -2
- package/docs/apis/features/agi/skills-library.md +12 -0
- package/docs/apis/features/node/python.md +81 -11
- package/docs/apis/features/node/transpiler.md +74 -0
- package/docs/apis/features/web/esbuild.md +0 -6
- package/docs/apis/servers/mcp.md +2 -2
- package/docs/examples/entity.md +124 -0
- package/docs/ideas/assistant-factory-pattern.md +142 -0
- package/package.json +74 -21
- package/src/agi/container.server.ts +10 -0
- package/src/agi/feature.ts +13 -0
- package/src/agi/features/agent-memory.ts +694 -0
- package/src/agi/features/assistant.ts +37 -26
- package/src/agi/features/assistants-manager.ts +95 -5
- package/src/agi/features/autonomous-assistant.ts +1 -5
- package/src/agi/features/browser-use.ts +32 -2
- package/src/agi/features/claude-code.ts +165 -1
- package/src/agi/features/coding-tools.ts +175 -0
- package/src/agi/features/conversation-history.ts +2 -6
- package/src/agi/features/conversation.ts +95 -3
- package/src/agi/features/docs-reader.ts +2 -1
- package/src/agi/features/file-tools.ts +35 -28
- package/src/agi/features/luca-coder.ts +1 -5
- package/src/agi/features/openai-codex.ts +1 -1
- package/src/agi/features/openapi.ts +3 -3
- package/src/agi/features/skills-library.ts +111 -13
- package/src/agi/lib/interceptor-chain.ts +10 -0
- package/src/agi/lib/token-counter.ts +1 -1
- package/src/bootstrap/generated.ts +126 -1
- package/src/bus.ts +27 -5
- package/src/cli/build-info.ts +2 -2
- package/src/client.ts +2 -2
- package/src/clients/elevenlabs/index.ts +5 -0
- package/src/clients/voicebox/index.ts +300 -0
- package/src/commands/bootstrap.ts +2 -1
- package/src/commands/chat.ts +1 -0
- package/src/commands/code.ts +4 -2
- package/src/commands/prompt.ts +34 -34
- package/src/commands/sandbox-mcp.ts +69 -163
- package/src/commands/save-api-docs.ts +10 -8
- package/src/commands/select.ts +8 -3
- package/src/container-describer.ts +70 -84
- package/src/container.ts +93 -3
- package/src/endpoint.ts +1 -1
- package/src/entity.ts +173 -0
- package/src/feature.ts +3 -3
- package/src/helper.ts +8 -4
- package/src/introspection/generated.agi.ts +3012 -1356
- package/src/introspection/generated.node.ts +179 -33
- package/src/introspection/generated.web.ts +95 -3
- package/src/introspection/scan.ts +1 -1
- package/src/node/container.ts +1 -1
- package/src/node/features/content-db.ts +57 -30
- package/src/node/features/file-manager.ts +10 -9
- package/src/node/features/git.ts +5 -5
- package/src/node/features/helpers.ts +1 -1
- package/src/node/features/json-tree.ts +1 -1
- package/src/node/features/os.ts +3 -3
- package/src/node/features/package-finder.ts +1 -1
- package/src/node/features/process-manager.ts +51 -18
- package/src/node/features/python.ts +3 -3
- package/src/node/features/redis.ts +1 -1
- package/src/node/features/repl.ts +2 -2
- package/src/node/features/transpiler.ts +2 -2
- package/src/node/features/ui.ts +1 -1
- package/src/node/features/vm.ts +3 -3
- package/src/node/features/yaml-tree.ts +1 -1
- package/src/node.ts +1 -0
- package/src/python/generated.ts +1 -1
- package/src/scaffolds/generated.ts +1 -1
- package/src/selector.ts +74 -4
- package/src/server.ts +2 -2
- package/src/servers/mcp.ts +6 -6
- package/src/web/features/helpers.ts +1 -1
- package/src/web/features/network.ts +1 -0
- package/test/assistant.test.ts +14 -5
- package/test/conversation.test.ts +220 -0
- package/test-integration/memory.test.ts +204 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +1 -1
- package/scripts/examples/telegram-ink-ui.ts +0 -302
- package/scripts/examples/using-openai-codex.ts +0 -23
- package/scripts/examples/vm-loading-esm-modules.ts +0 -16
package/src/selector.ts
CHANGED
|
@@ -135,17 +135,17 @@ export class Selector<
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
// Run the selector
|
|
138
|
-
this.state.set('running' as any, true)
|
|
138
|
+
this.state.set('running' as any, true as any)
|
|
139
139
|
this.emit('started' as any)
|
|
140
140
|
|
|
141
141
|
let data: any
|
|
142
142
|
try {
|
|
143
143
|
data = await this.run(parsed, this.context)
|
|
144
|
-
this.state.set('running' as any, false)
|
|
145
|
-
this.state.set('lastRanAt' as any, Date.now())
|
|
144
|
+
this.state.set('running' as any, false as any)
|
|
145
|
+
this.state.set('lastRanAt' as any, Date.now() as any)
|
|
146
146
|
this.emit('completed' as any, data)
|
|
147
147
|
} catch (err: any) {
|
|
148
|
-
this.state.set('running' as any, false)
|
|
148
|
+
this.state.set('running' as any, false as any)
|
|
149
149
|
this.emit('failed' as any, err)
|
|
150
150
|
throw err
|
|
151
151
|
}
|
|
@@ -197,6 +197,76 @@ export class SelectorsRegistry extends Registry<Selector<any>> {
|
|
|
197
197
|
override scope = 'selectors'
|
|
198
198
|
override baseClass = Selector as any
|
|
199
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Convert all registered selectors into a `{ schemas, handlers }` object
|
|
202
|
+
* compatible with `assistant.use()`.
|
|
203
|
+
*
|
|
204
|
+
* Each selector becomes a tool whose parameters come from the selector's
|
|
205
|
+
* `argsSchema` (with internal fields stripped) and whose handler dispatches
|
|
206
|
+
* the selector and returns the data directly (cache metadata is not exposed).
|
|
207
|
+
*
|
|
208
|
+
* @param container - The container used to instantiate and run selectors
|
|
209
|
+
* @param options - Optional filter/transform options
|
|
210
|
+
* @param options.include - Only include these selector names (default: all)
|
|
211
|
+
* @param options.exclude - Exclude these selector names (default: none)
|
|
212
|
+
* @param options.prefix - Prefix tool names (e.g. 'sel_' → 'sel_packageInfo')
|
|
213
|
+
*/
|
|
214
|
+
toTools(
|
|
215
|
+
container: Container<any> & SelectorsInterface,
|
|
216
|
+
options?: { include?: string[], exclude?: string[], prefix?: string },
|
|
217
|
+
): { schemas: Record<string, z.ZodType>, handlers: Record<string, Function> } {
|
|
218
|
+
const schemas: Record<string, z.ZodType> = {}
|
|
219
|
+
const handlers: Record<string, Function> = {}
|
|
220
|
+
const prefix = options?.prefix ?? ''
|
|
221
|
+
const includeSet = options?.include ? new Set(options.include) : null
|
|
222
|
+
const excludeSet = new Set(options?.exclude ?? [])
|
|
223
|
+
|
|
224
|
+
// Internal fields from HelperOptionsSchema and SelectorOptionsSchema
|
|
225
|
+
const internalFields = ['name', '_cacheKey', 'dispatchSource']
|
|
226
|
+
|
|
227
|
+
for (const name of this.available) {
|
|
228
|
+
if (excludeSet.has(name)) continue
|
|
229
|
+
if (includeSet && !includeSet.has(name)) continue
|
|
230
|
+
|
|
231
|
+
const Sel = this.lookup(name) as typeof Selector
|
|
232
|
+
const rawSchema = Sel.argsSchema
|
|
233
|
+
const description = Sel.selectorDescription || Sel.description || name
|
|
234
|
+
|
|
235
|
+
let toolSchema: z.ZodType
|
|
236
|
+
try {
|
|
237
|
+
const shape = typeof (rawSchema as any)?._def?.shape === 'function'
|
|
238
|
+
? (rawSchema as any)._def.shape()
|
|
239
|
+
: (rawSchema as any)?._def?.shape
|
|
240
|
+
|
|
241
|
+
if (shape) {
|
|
242
|
+
const cleanShape: Record<string, z.ZodType> = {}
|
|
243
|
+
for (const [key, val] of Object.entries(shape)) {
|
|
244
|
+
if (internalFields.includes(key)) continue
|
|
245
|
+
cleanShape[key] = val as z.ZodType
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
toolSchema = Object.keys(cleanShape).length > 0
|
|
249
|
+
? z.object(cleanShape).describe(description)
|
|
250
|
+
: z.object({}).describe(description)
|
|
251
|
+
} else {
|
|
252
|
+
toolSchema = z.object({}).describe(description)
|
|
253
|
+
}
|
|
254
|
+
} catch {
|
|
255
|
+
toolSchema = z.object({}).describe(description)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const toolName = `${prefix}${name}`
|
|
259
|
+
schemas[toolName] = toolSchema
|
|
260
|
+
handlers[toolName] = async (args: Record<string, any>) => {
|
|
261
|
+
const sel = (container.select as any)(name)
|
|
262
|
+
const result = await sel.select(args ?? {})
|
|
263
|
+
return result.data
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return { schemas, handlers }
|
|
268
|
+
}
|
|
269
|
+
|
|
200
270
|
/**
|
|
201
271
|
* Discover and register selectors from a directory.
|
|
202
272
|
* Detection order:
|
package/src/server.ts
CHANGED
|
@@ -30,7 +30,7 @@ export class Server<T extends ServerState = ServerState, K extends ServerOptions
|
|
|
30
30
|
static override eventsSchema = ServerEventsSchema
|
|
31
31
|
|
|
32
32
|
/** Self-register a Server subclass from a static initialization block. */
|
|
33
|
-
static register: (SubClass:
|
|
33
|
+
static register: (SubClass: abstract new (options: any, context: any) => Server, id?: string) => abstract new (options: any, context: any) => Server
|
|
34
34
|
|
|
35
35
|
override get initialState() : T {
|
|
36
36
|
return ({
|
|
@@ -203,7 +203,7 @@ export const helperCache = new Map()
|
|
|
203
203
|
* ```
|
|
204
204
|
*/
|
|
205
205
|
Server.register = function registerServer(
|
|
206
|
-
SubClass:
|
|
206
|
+
SubClass: abstract new (options: any, context: any) => Server,
|
|
207
207
|
id?: string,
|
|
208
208
|
) {
|
|
209
209
|
const registryId = id ?? SubClass.name[0]!.toLowerCase() + SubClass.name.slice(1)
|
package/src/servers/mcp.ts
CHANGED
|
@@ -33,9 +33,9 @@ export type MCPContext = {
|
|
|
33
33
|
export interface RegisteredTool {
|
|
34
34
|
name: string
|
|
35
35
|
description?: string
|
|
36
|
-
schema?: z.
|
|
36
|
+
schema?: z.ZodType
|
|
37
37
|
jsonSchema?: Record<string, any>
|
|
38
|
-
handler:
|
|
38
|
+
handler: Function
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/** A registered MCP resource with its URI, metadata, and handler. */
|
|
@@ -61,9 +61,9 @@ export type PromptMessage = {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
type ToolRegistrationOptions = {
|
|
64
|
-
schema?: z.
|
|
64
|
+
schema?: z.ZodType
|
|
65
65
|
description?: string
|
|
66
|
-
handler
|
|
66
|
+
handler?: Function | ((args: any, ctx: any) => any)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
type ResourceRegistrationOptions = {
|
|
@@ -340,7 +340,7 @@ export class MCPServer extends Server<MCPServerState, MCPServerOptions> {
|
|
|
340
340
|
* @param name - Unique tool name
|
|
341
341
|
* @param options - Tool schema, description, and handler
|
|
342
342
|
*/
|
|
343
|
-
tool(name: string, options: ToolRegistrationOptions): this {
|
|
343
|
+
override tool(name: string, options: ToolRegistrationOptions): this {
|
|
344
344
|
let jsonSchema: Record<string, any> | undefined
|
|
345
345
|
|
|
346
346
|
if (options.schema) {
|
|
@@ -357,7 +357,7 @@ export class MCPServer extends Server<MCPServerState, MCPServerOptions> {
|
|
|
357
357
|
description: options.description,
|
|
358
358
|
schema: options.schema,
|
|
359
359
|
jsonSchema,
|
|
360
|
-
handler: options.handler,
|
|
360
|
+
handler: options.handler ?? (() => {}),
|
|
361
361
|
}
|
|
362
362
|
|
|
363
363
|
this._tools.set(name, registered)
|
|
@@ -136,7 +136,7 @@ export class Helpers extends Feature<HelpersState, HelpersOptions> {
|
|
|
136
136
|
return {}
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
const manifest
|
|
139
|
+
const manifest = await response.json() as Manifest
|
|
140
140
|
this._manifest = manifest
|
|
141
141
|
this.state.set('manifestLoaded', true)
|
|
142
142
|
this.emit('manifestLoaded' as any, manifest)
|
package/test/assistant.test.ts
CHANGED
|
@@ -15,18 +15,27 @@ describe('Assistant', () => {
|
|
|
15
15
|
expect(assistant.systemPrompt).toContain('coding assistant')
|
|
16
16
|
})
|
|
17
17
|
|
|
18
|
-
it('loads tools from
|
|
19
|
-
const assistant = container.feature('assistant', {
|
|
18
|
+
it('loads tools from codingTools feature after start', async () => {
|
|
19
|
+
const assistant = container.feature('assistant', {
|
|
20
|
+
folder: 'assistants/codingAssistant',
|
|
21
|
+
local: true,
|
|
22
|
+
model: 'qwen/qwen3-8b',
|
|
23
|
+
})
|
|
24
|
+
await assistant.start()
|
|
20
25
|
const tools = assistant.availableTools
|
|
21
26
|
expect(tools).toContain('rg')
|
|
22
27
|
expect(tools).toContain('ls')
|
|
23
28
|
expect(tools).toContain('cat')
|
|
24
|
-
expect(tools).toContain('pwd')
|
|
25
29
|
expect(tools.length).toBeGreaterThan(0)
|
|
26
30
|
})
|
|
27
31
|
|
|
28
|
-
it('tools have descriptions and parameter schemas', () => {
|
|
29
|
-
const assistant = container.feature('assistant', {
|
|
32
|
+
it('tools have descriptions and parameter schemas', async () => {
|
|
33
|
+
const assistant = container.feature('assistant', {
|
|
34
|
+
folder: 'assistants/codingAssistant',
|
|
35
|
+
local: true,
|
|
36
|
+
model: 'qwen/qwen3-8b',
|
|
37
|
+
})
|
|
38
|
+
await assistant.start()
|
|
30
39
|
const { rg, ls, cat } = assistant.tools
|
|
31
40
|
expect(rg.description.length).toBeGreaterThan(0)
|
|
32
41
|
expect(rg.parameters.type).toBe('object')
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'bun:test'
|
|
2
|
+
import { AGIContainer } from '../src/agi/container.server'
|
|
3
|
+
import type { Conversation } from '../src/agi/features/conversation'
|
|
4
|
+
|
|
5
|
+
function makeConversation(opts: Record<string, any> = {}): Conversation {
|
|
6
|
+
const container = new AGIContainer()
|
|
7
|
+
return container.feature('conversation', { model: 'gpt-5', ...opts }) as Conversation
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe('Conversation', () => {
|
|
11
|
+
describe('state', () => {
|
|
12
|
+
it('initializes with empty messages when no history provided', () => {
|
|
13
|
+
const conv = makeConversation()
|
|
14
|
+
expect(conv.messages).toEqual([])
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('seeds message history from options', () => {
|
|
18
|
+
const history = [{ role: 'system', content: 'You are helpful.' }]
|
|
19
|
+
const conv = makeConversation({ history })
|
|
20
|
+
expect(conv.messages).toHaveLength(1)
|
|
21
|
+
expect(conv.messages[0]).toEqual(history[0])
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('uses provided model', () => {
|
|
25
|
+
const conv = makeConversation({ model: 'gpt-4.1' })
|
|
26
|
+
expect(conv.model).toBe('gpt-4.1')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('isStreaming is false initially', () => {
|
|
30
|
+
const conv = makeConversation()
|
|
31
|
+
expect(conv.isStreaming).toBe(false)
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe('pushMessage', () => {
|
|
36
|
+
it('appends to messages', () => {
|
|
37
|
+
const conv = makeConversation()
|
|
38
|
+
conv.pushMessage({ role: 'user', content: 'hello' })
|
|
39
|
+
expect(conv.messages).toHaveLength(1)
|
|
40
|
+
expect(conv.messages[0]!.role).toBe('user')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('does not mutate prior messages array', () => {
|
|
44
|
+
const conv = makeConversation()
|
|
45
|
+
const before = conv.messages
|
|
46
|
+
conv.pushMessage({ role: 'user', content: 'hi' })
|
|
47
|
+
expect(conv.messages).not.toBe(before)
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
describe('tools', () => {
|
|
52
|
+
it('starts with no tools', () => {
|
|
53
|
+
const conv = makeConversation()
|
|
54
|
+
expect(conv.availableTools).toHaveLength(0)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('addTool registers a tool', () => {
|
|
58
|
+
const conv = makeConversation()
|
|
59
|
+
conv.addTool('greet', {
|
|
60
|
+
description: 'Says hello',
|
|
61
|
+
parameters: { type: 'object', properties: {} },
|
|
62
|
+
handler: async () => 'hello',
|
|
63
|
+
})
|
|
64
|
+
expect(conv.availableTools).toContain('greet')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('removeTool deregisters a tool', () => {
|
|
68
|
+
const conv = makeConversation()
|
|
69
|
+
conv.addTool('greet', {
|
|
70
|
+
description: 'Says hello',
|
|
71
|
+
parameters: { type: 'object', properties: {} },
|
|
72
|
+
handler: async () => 'hello',
|
|
73
|
+
})
|
|
74
|
+
conv.removeTool('greet')
|
|
75
|
+
expect(conv.availableTools).not.toContain('greet')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('updateTools merges without replacing unrelated tools', () => {
|
|
79
|
+
const conv = makeConversation()
|
|
80
|
+
conv.addTool('a', { description: 'A', parameters: { type: 'object', properties: {} }, handler: async () => 'a' })
|
|
81
|
+
conv.updateTools({ b: { description: 'B', parameters: { type: 'object', properties: {} }, handler: async () => 'b' } })
|
|
82
|
+
expect(conv.availableTools).toContain('a')
|
|
83
|
+
expect(conv.availableTools).toContain('b')
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('construction-time tools are available immediately', () => {
|
|
87
|
+
const conv = makeConversation({
|
|
88
|
+
tools: {
|
|
89
|
+
ping: { description: 'Ping', parameters: { type: 'object', properties: {} }, handler: async () => 'pong' }
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
expect(conv.availableTools).toContain('ping')
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
describe('estimateTokens', () => {
|
|
97
|
+
it('returns a near-zero baseline for empty messages', () => {
|
|
98
|
+
const conv = makeConversation()
|
|
99
|
+
expect(conv.estimateTokens()).toBeLessThan(10)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('returns a positive number when messages exist', () => {
|
|
103
|
+
const conv = makeConversation({
|
|
104
|
+
history: [{ role: 'user', content: 'What is the capital of France?' }]
|
|
105
|
+
})
|
|
106
|
+
expect(conv.estimateTokens()).toBeGreaterThan(0)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('increases as more messages are added', () => {
|
|
110
|
+
const conv = makeConversation()
|
|
111
|
+
conv.pushMessage({ role: 'user', content: 'Hello' })
|
|
112
|
+
const first = conv.estimateTokens()
|
|
113
|
+
conv.pushMessage({ role: 'assistant', content: 'Hi there, how can I help you today?' })
|
|
114
|
+
const second = conv.estimateTokens()
|
|
115
|
+
expect(second).toBeGreaterThan(first)
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
describe('stub()', () => {
|
|
120
|
+
it('exact string match returns the stub response', async () => {
|
|
121
|
+
const conv = makeConversation()
|
|
122
|
+
conv.stub('ping', 'pong')
|
|
123
|
+
const result = await conv.ask('ping')
|
|
124
|
+
expect(result).toBe('pong')
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('substring match triggers the stub', async () => {
|
|
128
|
+
const conv = makeConversation()
|
|
129
|
+
conv.stub('weather', 'Sunny and 72°F.')
|
|
130
|
+
const result = await conv.ask('what is the weather today?')
|
|
131
|
+
expect(result).toBe('Sunny and 72°F.')
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('regex match triggers the stub', async () => {
|
|
135
|
+
const conv = makeConversation()
|
|
136
|
+
conv.stub(/hello/i, 'Hey there!')
|
|
137
|
+
const result = await conv.ask('HELLO')
|
|
138
|
+
expect(result).toBe('Hey there!')
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('function response is called on each match', async () => {
|
|
142
|
+
const conv = makeConversation()
|
|
143
|
+
let callCount = 0
|
|
144
|
+
conv.stub('count', () => `call ${++callCount}`)
|
|
145
|
+
expect(await conv.ask('count')).toBe('call 1')
|
|
146
|
+
expect(await conv.ask('count')).toBe('call 2')
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('first matching stub wins', async () => {
|
|
150
|
+
const conv = makeConversation()
|
|
151
|
+
conv.stub('hello', 'first')
|
|
152
|
+
conv.stub('hello', 'second')
|
|
153
|
+
expect(await conv.ask('hello')).toBe('first')
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('appends user and assistant messages to history', async () => {
|
|
157
|
+
const conv = makeConversation()
|
|
158
|
+
conv.stub('hi', 'hello back')
|
|
159
|
+
await conv.ask('hi')
|
|
160
|
+
expect(conv.messages).toHaveLength(2)
|
|
161
|
+
expect(conv.messages[0]).toMatchObject({ role: 'user', content: 'hi' })
|
|
162
|
+
expect(conv.messages[1]).toMatchObject({ role: 'assistant', content: 'hello back' })
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('isStreaming is false after the call resolves', async () => {
|
|
166
|
+
const conv = makeConversation()
|
|
167
|
+
conv.stub('test', 'response')
|
|
168
|
+
await conv.ask('test')
|
|
169
|
+
expect(conv.isStreaming).toBe(false)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('emits chunk events for each word', async () => {
|
|
173
|
+
const conv = makeConversation()
|
|
174
|
+
conv.stub('go', 'one two three')
|
|
175
|
+
const chunks: string[] = []
|
|
176
|
+
conv.on('chunk', (delta: string) => chunks.push(delta))
|
|
177
|
+
await conv.ask('go')
|
|
178
|
+
expect(chunks.join('')).toBe('one two three')
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('emits preview events with accumulating text', async () => {
|
|
182
|
+
const conv = makeConversation()
|
|
183
|
+
conv.stub('go', 'alpha beta')
|
|
184
|
+
const previews: string[] = []
|
|
185
|
+
conv.on('preview', (text: string) => previews.push(text))
|
|
186
|
+
await conv.ask('go')
|
|
187
|
+
expect(previews.at(-1)).toBe('alpha beta')
|
|
188
|
+
// Each preview should be longer than or equal to the previous
|
|
189
|
+
for (let i = 1; i < previews.length; i++) {
|
|
190
|
+
expect(previews[i]!.length).toBeGreaterThanOrEqual(previews[i - 1]!.length)
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it('emits turnStart and turnEnd events', async () => {
|
|
195
|
+
const conv = makeConversation()
|
|
196
|
+
conv.stub('x', 'y')
|
|
197
|
+
const events: string[] = []
|
|
198
|
+
conv.on('turnStart', () => events.push('turnStart'))
|
|
199
|
+
conv.on('turnEnd', () => events.push('turnEnd'))
|
|
200
|
+
await conv.ask('x')
|
|
201
|
+
expect(events).toEqual(['turnStart', 'turnEnd'])
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('emits response event with the full text', async () => {
|
|
205
|
+
const conv = makeConversation()
|
|
206
|
+
conv.stub('question', 'the answer')
|
|
207
|
+
let emitted = ''
|
|
208
|
+
conv.on('response', (text: string) => { emitted = text })
|
|
209
|
+
await conv.ask('question')
|
|
210
|
+
expect(emitted).toBe('the answer')
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('stub is chainable', () => {
|
|
214
|
+
const conv = makeConversation()
|
|
215
|
+
const result = conv.stub('a', 'A').stub('b', 'B')
|
|
216
|
+
expect(result).toBe(conv)
|
|
217
|
+
expect(conv.availableTools).toHaveLength(0) // stubs don't affect tools
|
|
218
|
+
})
|
|
219
|
+
})
|
|
220
|
+
})
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import {
|
|
2
|
+
requireEnv,
|
|
3
|
+
describeWithRequirements,
|
|
4
|
+
createAGIContainer,
|
|
5
|
+
API_TIMEOUT,
|
|
6
|
+
} from './helpers'
|
|
7
|
+
import type { AGIContainer } from '../src/agi/container.server'
|
|
8
|
+
import type { Memory } from '../src/agi/features/agent-memory'
|
|
9
|
+
|
|
10
|
+
const openaiKey = requireEnv('OPENAI_API_KEY')
|
|
11
|
+
|
|
12
|
+
describeWithRequirements('Memory Integration', [openaiKey], () => {
|
|
13
|
+
let container: AGIContainer
|
|
14
|
+
let mem: Memory
|
|
15
|
+
|
|
16
|
+
beforeAll(async () => {
|
|
17
|
+
container = createAGIContainer()
|
|
18
|
+
mem = container.feature('memory', { namespace: 'test-integration' }) as unknown as Memory
|
|
19
|
+
await mem.initDb()
|
|
20
|
+
await mem.wipeAll()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
afterAll(async () => {
|
|
24
|
+
await mem.wipeAll()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
describe('Feature registration', () => {
|
|
28
|
+
it('registers in the container', () => {
|
|
29
|
+
expect(container.features.available).toContain('memory')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('has correct shortcut', () => {
|
|
33
|
+
const { Memory: MemClass } = require('../src/agi/features/agent-memory')
|
|
34
|
+
expect(MemClass.shortcut).toBe('features.memory')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('initializes database', () => {
|
|
38
|
+
expect(mem.state.get('dbReady')).toBe(true)
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
describe('CRUD operations', () => {
|
|
43
|
+
it('creates and retrieves a memory', async () => {
|
|
44
|
+
const m = await mem.create('facts', 'Jonathan is a software engineer based in Austin')
|
|
45
|
+
expect(m.id).toBeGreaterThan(0)
|
|
46
|
+
expect(m.category).toBe('facts')
|
|
47
|
+
expect(m.document).toBe('Jonathan is a software engineer based in Austin')
|
|
48
|
+
|
|
49
|
+
const fetched = await mem.get('facts', m.id)
|
|
50
|
+
expect(fetched).not.toBeNull()
|
|
51
|
+
expect(fetched!.document).toBe(m.document)
|
|
52
|
+
}, API_TIMEOUT)
|
|
53
|
+
|
|
54
|
+
it('updates a memory', async () => {
|
|
55
|
+
const m = await mem.create('facts', 'Prefers dark mode')
|
|
56
|
+
const updated = await mem.update('facts', m.id, {
|
|
57
|
+
text: 'Prefers dark mode, especially Dracula theme',
|
|
58
|
+
metadata: { updated: 'true' },
|
|
59
|
+
})
|
|
60
|
+
expect(updated!.document).toBe('Prefers dark mode, especially Dracula theme')
|
|
61
|
+
expect(updated!.metadata.updated).toBe('true')
|
|
62
|
+
}, API_TIMEOUT)
|
|
63
|
+
|
|
64
|
+
it('deletes a memory', async () => {
|
|
65
|
+
const m = await mem.create('scratch', 'temporary memory')
|
|
66
|
+
expect(await mem.delete('scratch', m.id)).toBe(true)
|
|
67
|
+
expect(await mem.get('scratch', m.id)).toBeNull()
|
|
68
|
+
}, API_TIMEOUT)
|
|
69
|
+
|
|
70
|
+
it('lists categories', async () => {
|
|
71
|
+
const cats = await mem.categories()
|
|
72
|
+
expect(cats).toContain('facts')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('counts memories', async () => {
|
|
76
|
+
const total = await mem.count()
|
|
77
|
+
expect(total).toBeGreaterThan(0)
|
|
78
|
+
const factCount = await mem.count('facts')
|
|
79
|
+
expect(factCount).toBeGreaterThan(0)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('gets all memories with filtering', async () => {
|
|
83
|
+
await mem.create('prefs', 'Likes iterative delivery', { source: 'onboarding' })
|
|
84
|
+
await mem.create('prefs', 'Prefers concise responses')
|
|
85
|
+
|
|
86
|
+
const onboarding = await mem.getAll('prefs', { filterMetadata: { source: 'onboarding' } })
|
|
87
|
+
expect(onboarding.length).toBeGreaterThan(0)
|
|
88
|
+
expect(onboarding.every(m => m.metadata.source === 'onboarding')).toBe(true)
|
|
89
|
+
}, API_TIMEOUT)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
describe('Semantic search', () => {
|
|
93
|
+
it('finds relevant memories by query', async () => {
|
|
94
|
+
const results = await mem.search('facts', 'Where does the user live?', 3)
|
|
95
|
+
expect(results.length).toBeGreaterThan(0)
|
|
96
|
+
expect(results[0].distance).toBeLessThan(1)
|
|
97
|
+
}, API_TIMEOUT)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
describe('Deduplication', () => {
|
|
101
|
+
it('blocks near-duplicate memories', async () => {
|
|
102
|
+
const before = await mem.count('facts')
|
|
103
|
+
const dup = await mem.createUnique('facts', 'Jonathan is a software engineer living in Austin, TX')
|
|
104
|
+
const after = await mem.count('facts')
|
|
105
|
+
expect(dup).toBeNull()
|
|
106
|
+
expect(after).toBe(before)
|
|
107
|
+
}, API_TIMEOUT)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
describe('Epochs and events', () => {
|
|
111
|
+
it('tracks epoch state', () => {
|
|
112
|
+
expect(mem.getEpoch()).toBe(1)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('creates events and increments epoch', async () => {
|
|
116
|
+
await mem.createEvent('User started a new project')
|
|
117
|
+
await mem.incrementEpoch()
|
|
118
|
+
expect(mem.getEpoch()).toBe(2)
|
|
119
|
+
|
|
120
|
+
await mem.createEvent('User requested a test script')
|
|
121
|
+
|
|
122
|
+
const e1 = await mem.getEvents({ epoch: 1 })
|
|
123
|
+
const e2 = await mem.getEvents({ epoch: 2 })
|
|
124
|
+
expect(e1.length).toBeGreaterThan(0)
|
|
125
|
+
expect(e2.length).toBeGreaterThan(0)
|
|
126
|
+
}, API_TIMEOUT)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
describe('Export and import', () => {
|
|
130
|
+
it('roundtrips through export/import', async () => {
|
|
131
|
+
const exported = await mem.exportToJson()
|
|
132
|
+
expect(exported.memories.length).toBeGreaterThan(0)
|
|
133
|
+
|
|
134
|
+
await mem.wipeAll()
|
|
135
|
+
expect(await mem.count()).toBe(0)
|
|
136
|
+
|
|
137
|
+
const imported = await mem.importFromJson(exported)
|
|
138
|
+
expect(imported).toBe(exported.memories.length)
|
|
139
|
+
expect(await mem.count()).toBe(exported.memories.length)
|
|
140
|
+
}, API_TIMEOUT * 3)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
describe('Tool interface (toTools)', () => {
|
|
144
|
+
it('exposes tools via standard toTools() pattern', () => {
|
|
145
|
+
const { schemas, handlers } = mem.toTools()
|
|
146
|
+
expect(Object.keys(schemas)).toContain('remember')
|
|
147
|
+
expect(Object.keys(schemas)).toContain('recall')
|
|
148
|
+
expect(Object.keys(schemas)).toContain('forgetCategory')
|
|
149
|
+
expect(Object.keys(schemas)).toContain('listCategories')
|
|
150
|
+
expect(typeof handlers.remember).toBe('function')
|
|
151
|
+
expect(typeof handlers.recall).toBe('function')
|
|
152
|
+
expect(typeof handlers.forgetCategory).toBe('function')
|
|
153
|
+
expect(typeof handlers.listCategories).toBe('function')
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('toTools only/except filtering works', () => {
|
|
157
|
+
const { schemas: onlySchemas } = mem.toTools({ only: ['remember', 'recall'] })
|
|
158
|
+
expect(Object.keys(onlySchemas)).toEqual(['remember', 'recall'])
|
|
159
|
+
|
|
160
|
+
const { schemas: exceptSchemas } = mem.toTools({ except: ['forgetCategory'] })
|
|
161
|
+
expect(Object.keys(exceptSchemas)).not.toContain('forgetCategory')
|
|
162
|
+
expect(Object.keys(exceptSchemas)).toContain('remember')
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('remember tool stores and deduplicates', async () => {
|
|
166
|
+
const { handlers } = mem.toTools()
|
|
167
|
+
const result = await handlers.remember({ category: 'facts', text: 'Has a golden retriever named Max' })
|
|
168
|
+
expect(result.stored).toBe(true)
|
|
169
|
+
|
|
170
|
+
const dup = await handlers.remember({ category: 'facts', text: 'Has a golden retriever named Max' })
|
|
171
|
+
expect(dup.stored).toBe(false)
|
|
172
|
+
}, API_TIMEOUT)
|
|
173
|
+
|
|
174
|
+
it('recall tool searches memories', async () => {
|
|
175
|
+
const { handlers } = mem.toTools()
|
|
176
|
+
const results = await handlers.recall({ category: 'facts', query: 'pets and animals', n_results: 2 })
|
|
177
|
+
expect(results.length).toBeGreaterThan(0)
|
|
178
|
+
expect(results[0]).toHaveProperty('document')
|
|
179
|
+
expect(results[0]).toHaveProperty('distance')
|
|
180
|
+
}, API_TIMEOUT)
|
|
181
|
+
|
|
182
|
+
it('listCategories tool returns counts', async () => {
|
|
183
|
+
const { handlers } = mem.toTools()
|
|
184
|
+
const result = await handlers.listCategories()
|
|
185
|
+
expect(result.categories).toBeDefined()
|
|
186
|
+
expect(typeof result.categories.facts).toBe('number')
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
describe('Wipe operations', () => {
|
|
191
|
+
it('wipeCategory removes only that category', async () => {
|
|
192
|
+
const before = await mem.count()
|
|
193
|
+
const deleted = await mem.wipeCategory('scratch')
|
|
194
|
+
const after = await mem.count()
|
|
195
|
+
expect(after).toBeLessThanOrEqual(before)
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('wipeAll removes everything and resets epoch', async () => {
|
|
199
|
+
await mem.wipeAll()
|
|
200
|
+
expect(await mem.count()).toBe(0)
|
|
201
|
+
expect(mem.getEpoch()).toBe(1)
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
})
|