@soederpop/luca 0.1.1 → 0.1.3
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 +2 -0
- package/assistants/codingAssistant/hooks.ts +3 -0
- package/assistants/inkbot/CORE.md +69 -0
- package/assistants/inkbot/hooks.ts +14 -0
- package/assistants/inkbot/tools.ts +47 -0
- package/commands/inkbot.ts +353 -0
- 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/package.json +73 -21
- package/scripts/test-assistant-hooks.ts +13 -0
- package/src/agi/feature.ts +13 -0
- package/src/agi/features/assistant.ts +36 -25
- package/src/agi/features/assistants-manager.ts +70 -5
- package/src/agi/features/autonomous-assistant.ts +1 -5
- package/src/agi/features/browser-use.ts +2 -2
- package/src/agi/features/claude-code.ts +165 -1
- 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 +2 -2
- 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 +90 -2
- 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/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 +1403 -929
- package/src/introspection/generated.node.ts +127 -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 +3 -3
- 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 +1 -1
- 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 +34 -9
- package/src/node/features/ui.ts +1 -1
- package/src/node/features/vm.ts +6 -5
- 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 +72 -0
- package/test/conversation.test.ts +220 -0
- package/test/vm-loadmodule.test.ts +213 -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
|
@@ -101,7 +101,7 @@ export class Repl<
|
|
|
101
101
|
|
|
102
102
|
// Load existing history
|
|
103
103
|
try {
|
|
104
|
-
const content = fs.readFile(this._historyPath, 'utf-8')
|
|
104
|
+
const content = this.container.fs.readFile(this._historyPath, 'utf-8') as string
|
|
105
105
|
this._history = content.split('\n').filter(Boolean).reverse()
|
|
106
106
|
} catch {}
|
|
107
107
|
|
|
@@ -204,7 +204,7 @@ export class Repl<
|
|
|
204
204
|
private _saveHistory(line: string) {
|
|
205
205
|
if (!this._historyPath || !line.trim()) return
|
|
206
206
|
try {
|
|
207
|
-
fs.
|
|
207
|
+
this.container.fs.appendFile(this._historyPath, line + '\n')
|
|
208
208
|
} catch {}
|
|
209
209
|
}
|
|
210
210
|
}
|
|
@@ -18,7 +18,9 @@ export interface TransformResult {
|
|
|
18
18
|
* so the code can run in a vm context that provides `require`.
|
|
19
19
|
*/
|
|
20
20
|
function esmToCjs(code: string): string {
|
|
21
|
-
|
|
21
|
+
const exportedNames: string[] = []
|
|
22
|
+
|
|
23
|
+
let result = code
|
|
22
24
|
// import Foo, { bar, baz } from 'x' → const Foo = require('x').default ?? require('x'); const { bar, baz } = require('x')
|
|
23
25
|
.replace(/^import\s+(\w+)\s*,\s*\{([^}]+)\}\s+from\s+(['"][^'"]+['"])\s*;?$/gm,
|
|
24
26
|
'const $1 = require($3).default ?? require($3); const {$2} = require($3);')
|
|
@@ -39,14 +41,37 @@ function esmToCjs(code: string): string {
|
|
|
39
41
|
// export { a, b } from 'x' → Object.assign(module.exports, require('x')) (re-exports)
|
|
40
42
|
.replace(/^export\s+\{[^}]*\}\s+from\s+(['"][^'"]+['"])\s*;?$/gm,
|
|
41
43
|
'Object.assign(module.exports, require($1));')
|
|
42
|
-
// export {
|
|
43
|
-
.replace(/^export\s+\{[^}]
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
// export { a, b as c } → exports.a = a; exports.c = b;
|
|
45
|
+
.replace(/^export\s+\{([^}]*)\}\s*;?$/gm, (_match, body: string) => {
|
|
46
|
+
return body.split(',').map(s => {
|
|
47
|
+
const parts = s.trim().split(/\s+as\s+/)
|
|
48
|
+
const local = (parts[0] ?? '').trim()
|
|
49
|
+
const exported = (parts[1] ?? parts[0] ?? '').trim()
|
|
50
|
+
return local ? `exports['${exported}'] = ${local};` : ''
|
|
51
|
+
}).filter(Boolean).join(' ')
|
|
52
|
+
})
|
|
53
|
+
// export const/let/var NAME → const/let/var NAME (track for deferred export)
|
|
54
|
+
.replace(/^export\s+(const|let|var)\s+(\w+)/gm, (_match, decl: string, name: string) => {
|
|
55
|
+
exportedNames.push(name)
|
|
56
|
+
return `${decl} ${name}`
|
|
57
|
+
})
|
|
58
|
+
// export function NAME / export class NAME → function/class NAME (track for deferred export)
|
|
59
|
+
.replace(/^export\s+(function|class)\s+(\w+)/gm, (_match, type: string, name: string) => {
|
|
60
|
+
exportedNames.push(name)
|
|
61
|
+
return `${type} ${name}`
|
|
62
|
+
})
|
|
63
|
+
// export async function NAME → async function NAME (track for deferred export)
|
|
64
|
+
.replace(/^export\s+(async\s+function)\s+(\w+)/gm, (_match, type: string, name: string) => {
|
|
65
|
+
exportedNames.push(name)
|
|
66
|
+
return `${type} ${name}`
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// Append exports for all tracked named exports
|
|
70
|
+
if (exportedNames.length > 0) {
|
|
71
|
+
result += '\n' + exportedNames.map(n => `exports['${n}'] = ${n};`).join('\n')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return result
|
|
50
75
|
}
|
|
51
76
|
|
|
52
77
|
/**
|
package/src/node/features/ui.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from 'zod'
|
|
|
2
2
|
import { FeatureStateSchema } from '../../schemas/base.js'
|
|
3
3
|
import { Feature } from "../feature.js";
|
|
4
4
|
import colors from "chalk";
|
|
5
|
-
import type { Fonts } from "figlet";
|
|
5
|
+
import type { FontName as Fonts } from "figlet";
|
|
6
6
|
import { figlet, fontNames } from "./figlet-fonts.js";
|
|
7
7
|
import inquirer from "inquirer";
|
|
8
8
|
import { marked } from 'marked';
|
package/src/node/features/vm.ts
CHANGED
|
@@ -219,9 +219,9 @@ export class VM<
|
|
|
219
219
|
|
|
220
220
|
// Find the last non-empty line
|
|
221
221
|
let lastIdx = lines.length - 1
|
|
222
|
-
while (lastIdx > 0 && !lines[lastIdx].trim()) lastIdx--
|
|
222
|
+
while (lastIdx > 0 && !(lines[lastIdx] ?? '').trim()) lastIdx--
|
|
223
223
|
|
|
224
|
-
let lastLine = lines[lastIdx]
|
|
224
|
+
let lastLine = lines[lastIdx] ?? ''
|
|
225
225
|
|
|
226
226
|
// For single-line code with semicolons (e.g. CLI eval), split the last line
|
|
227
227
|
// into statements and only try to return the final statement.
|
|
@@ -390,12 +390,13 @@ export class VM<
|
|
|
390
390
|
if (!fs.exists(filePath)) return {}
|
|
391
391
|
|
|
392
392
|
const raw = fs.readFile(filePath)
|
|
393
|
-
const { code } = this.container.feature('transpiler').transformSync(raw, { format: 'cjs' })
|
|
393
|
+
const { code } = this.container.feature('transpiler').transformSync(String(raw), { format: 'cjs' })
|
|
394
394
|
|
|
395
|
+
const sharedExports = {}
|
|
395
396
|
const { context } = this.performSync(code, {
|
|
396
397
|
require: this.createRequireFor(filePath),
|
|
397
|
-
exports:
|
|
398
|
-
module: { exports:
|
|
398
|
+
exports: sharedExports,
|
|
399
|
+
module: { exports: sharedExports },
|
|
399
400
|
console,
|
|
400
401
|
setTimeout,
|
|
401
402
|
setInterval,
|
|
@@ -113,7 +113,7 @@ export class YamlTree<T extends YamlTreeState = YamlTreeState> extends Feature<T
|
|
|
113
113
|
for (const file of yamlFiles.filter(Boolean)) {
|
|
114
114
|
if(file?.relativePath) {
|
|
115
115
|
const fileContent = fileSystem.readFile(file.relativePath);
|
|
116
|
-
const fileData = yamlFeature.parse(fileContent);
|
|
116
|
+
const fileData = yamlFeature.parse(String(fileContent));
|
|
117
117
|
const path = file.relativePath.replace(/\.ya?ml$/, "").replace(basePath + "/", "").split("/").filter(v => v?.length).map(p => camelCase(p));
|
|
118
118
|
set(tree, path, fileData)
|
|
119
119
|
}
|
package/src/node.ts
CHANGED
|
@@ -65,6 +65,7 @@ export type { AvailableFeatures, FeatureOptions, FeatureState } from './feature'
|
|
|
65
65
|
export type { NodeContainer, NodeFeatures } from './node/container'
|
|
66
66
|
export type { AvailableServers, StartOptions, ServersInterface } from './server'
|
|
67
67
|
export type { HelperState, HelperOptions } from './helper'
|
|
68
|
+
export type { Entity } from './entity'
|
|
68
69
|
export type { EventMap } from './bus'
|
|
69
70
|
export type { SetStateValue, StateChangeType } from './state'
|
|
70
71
|
|
package/src/python/generated.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Auto-generated scaffold and MCP readme content
|
|
2
|
-
// Generated at: 2026-
|
|
2
|
+
// Generated at: 2026-04-03T01:24:53.146Z
|
|
3
3
|
// Source: docs/scaffolds/*.md, docs/examples/assistant/, and docs/mcp/readme.md
|
|
4
4
|
//
|
|
5
5
|
// Do not edit manually. Run: luca build-scaffolds
|
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)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'bun:test'
|
|
2
|
+
import { AGIContainer } from '../src/agi/container.server'
|
|
3
|
+
|
|
4
|
+
describe('Assistant', () => {
|
|
5
|
+
let container: AGIContainer
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
container = new AGIContainer()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
describe('codingAssistant', () => {
|
|
12
|
+
it('loads a non-empty system prompt from CORE.md', () => {
|
|
13
|
+
const assistant = container.feature('assistant', { folder: 'assistants/codingAssistant' })
|
|
14
|
+
expect(assistant.systemPrompt.length).toBeGreaterThan(0)
|
|
15
|
+
expect(assistant.systemPrompt).toContain('coding assistant')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('loads tools from tools.ts via the VM', () => {
|
|
19
|
+
const assistant = container.feature('assistant', { folder: 'assistants/codingAssistant' })
|
|
20
|
+
const tools = assistant.availableTools
|
|
21
|
+
expect(tools).toContain('rg')
|
|
22
|
+
expect(tools).toContain('ls')
|
|
23
|
+
expect(tools).toContain('cat')
|
|
24
|
+
expect(tools).toContain('pwd')
|
|
25
|
+
expect(tools.length).toBeGreaterThan(0)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('tools have descriptions and parameter schemas', () => {
|
|
29
|
+
const assistant = container.feature('assistant', { folder: 'assistants/codingAssistant' })
|
|
30
|
+
const { rg, ls, cat } = assistant.tools
|
|
31
|
+
expect(rg.description.length).toBeGreaterThan(0)
|
|
32
|
+
expect(rg.parameters.type).toBe('object')
|
|
33
|
+
expect(rg.parameters.properties).toHaveProperty('args')
|
|
34
|
+
expect(ls.parameters.properties).toHaveProperty('args')
|
|
35
|
+
expect(cat.parameters.properties).toHaveProperty('args')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('loads hooks from hooks.ts via the VM', () => {
|
|
39
|
+
const assistant = container.feature('assistant', { folder: 'assistants/codingAssistant' })
|
|
40
|
+
const hooks = assistant.state.get('hooks') as Record<string, Function>
|
|
41
|
+
expect(hooks).toBeDefined()
|
|
42
|
+
expect(typeof hooks.started).toBe('function')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('hooks fire when the assistant starts', async () => {
|
|
46
|
+
const assistant = container.feature('assistant', {
|
|
47
|
+
folder: 'assistants/codingAssistant',
|
|
48
|
+
local: true,
|
|
49
|
+
model: 'qwen/qwen3-8b',
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// bindHooksToEvents emits 'hookFired' with the event name each time a hook runs
|
|
53
|
+
const fired: string[] = []
|
|
54
|
+
assistant.on('hookFired', (eventName: string) => { fired.push(eventName) })
|
|
55
|
+
|
|
56
|
+
await assistant.start()
|
|
57
|
+
expect(fired).toContain('started')
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('tools are wired into the conversation after start', async () => {
|
|
61
|
+
const assistant = container.feature('assistant', {
|
|
62
|
+
folder: 'assistants/codingAssistant',
|
|
63
|
+
local: true,
|
|
64
|
+
model: 'qwen/qwen3-8b',
|
|
65
|
+
})
|
|
66
|
+
await assistant.start()
|
|
67
|
+
const convTools = assistant.conversation.tools
|
|
68
|
+
expect(Object.keys(convTools)).toContain('rg')
|
|
69
|
+
expect(Object.keys(convTools)).toContain('ls')
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
})
|
|
@@ -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
|
+
})
|