luca 2.0.0 → 3.0.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/.github/workflows/release.yaml +169 -0
- package/AGENTS.md +99 -0
- package/CLAUDE.md +115 -0
- package/CNAME +1 -0
- package/README.md +257 -9
- package/RUNME.md +56 -0
- package/assistants/codingAssistant/ABOUT.md +5 -0
- package/assistants/codingAssistant/CORE.md +28 -0
- package/assistants/codingAssistant/hooks.ts +21 -0
- package/assistants/codingAssistant/tools.ts +12 -0
- package/assistants/inkbot/ABOUT.md +16 -0
- package/assistants/inkbot/CORE.md +330 -0
- package/assistants/inkbot/hooks.ts +6 -0
- package/assistants/inkbot/tools.ts +53 -0
- package/assistants/researcher/ABOUT.md +5 -0
- package/assistants/researcher/CORE.md +46 -0
- package/assistants/researcher/hooks.ts +16 -0
- package/assistants/researcher/tools.ts +237 -0
- package/bun.lock +2769 -0
- package/bunfig.toml +3 -0
- package/commands/audit-docs.ts +740 -0
- package/commands/build-bootstrap.ts +118 -0
- package/commands/build-python-bridge.ts +43 -0
- package/commands/build-scaffolds.ts +176 -0
- package/commands/generate-api-docs.ts +114 -0
- package/commands/inkbot.ts +874 -0
- package/commands/release.ts +80 -0
- package/commands/try-all-challenges.ts +543 -0
- package/commands/try-challenge.ts +100 -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 -12
- 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/CLI.md +335 -0
- package/docs/CNAME +1 -0
- package/docs/README.md +60 -0
- package/docs/TABLE-OF-CONTENTS.md +183 -0
- package/docs/apis/clients/elevenlabs.md +308 -0
- package/docs/apis/clients/graph.md +107 -0
- package/docs/apis/clients/openai.md +429 -0
- package/docs/apis/clients/rest.md +161 -0
- package/docs/apis/clients/websocket.md +174 -0
- package/docs/apis/features/agi/assistant.md +625 -0
- package/docs/apis/features/agi/assistants-manager.md +282 -0
- package/docs/apis/features/agi/auto-assistant.md +279 -0
- package/docs/apis/features/agi/browser-use.md +802 -0
- package/docs/apis/features/agi/claude-code.md +884 -0
- package/docs/apis/features/agi/conversation-history.md +364 -0
- package/docs/apis/features/agi/conversation.md +548 -0
- package/docs/apis/features/agi/docs-reader.md +99 -0
- package/docs/apis/features/agi/file-tools.md +163 -0
- package/docs/apis/features/agi/luca-coder.md +407 -0
- package/docs/apis/features/agi/openai-codex.md +396 -0
- package/docs/apis/features/agi/openapi.md +138 -0
- package/docs/apis/features/agi/semantic-search.md +387 -0
- package/docs/apis/features/agi/skills-library.md +239 -0
- package/docs/apis/features/node/container-link.md +192 -0
- package/docs/apis/features/node/content-db.md +450 -0
- package/docs/apis/features/node/disk-cache.md +379 -0
- package/docs/apis/features/node/dns.md +652 -0
- package/docs/apis/features/node/docker.md +706 -0
- package/docs/apis/features/node/downloader.md +81 -0
- package/docs/apis/features/node/esbuild.md +60 -0
- package/docs/apis/features/node/file-manager.md +191 -0
- package/docs/apis/features/node/fs.md +1217 -0
- package/docs/apis/features/node/git.md +371 -0
- package/docs/apis/features/node/google-auth.md +193 -0
- package/docs/apis/features/node/google-calendar.md +202 -0
- package/docs/apis/features/node/google-docs.md +173 -0
- package/docs/apis/features/node/google-drive.md +246 -0
- package/docs/apis/features/node/google-mail.md +214 -0
- package/docs/apis/features/node/google-sheets.md +194 -0
- package/docs/apis/features/node/grep.md +292 -0
- package/docs/apis/features/node/helpers.md +164 -0
- package/docs/apis/features/node/ink.md +334 -0
- package/docs/apis/features/node/ipc-socket.md +249 -0
- package/docs/apis/features/node/json-tree.md +86 -0
- package/docs/apis/features/node/networking.md +316 -0
- package/docs/apis/features/node/nlp.md +133 -0
- package/docs/apis/features/node/opener.md +97 -0
- package/docs/apis/features/node/os.md +146 -0
- package/docs/apis/features/node/package-finder.md +392 -0
- package/docs/apis/features/node/postgres.md +234 -0
- package/docs/apis/features/node/proc.md +399 -0
- package/docs/apis/features/node/process-manager.md +305 -0
- package/docs/apis/features/node/python.md +604 -0
- package/docs/apis/features/node/redis.md +380 -0
- package/docs/apis/features/node/repl.md +88 -0
- package/docs/apis/features/node/runpod.md +674 -0
- package/docs/apis/features/node/secure-shell.md +176 -0
- package/docs/apis/features/node/semantic-search.md +408 -0
- package/docs/apis/features/node/sqlite.md +233 -0
- package/docs/apis/features/node/telegram.md +279 -0
- package/docs/apis/features/node/transpiler.md +74 -0
- package/docs/apis/features/node/tts.md +133 -0
- package/docs/apis/features/node/ui.md +701 -0
- package/docs/apis/features/node/vault.md +59 -0
- package/docs/apis/features/node/vm.md +75 -0
- package/docs/apis/features/node/yaml-tree.md +85 -0
- package/docs/apis/features/node/yaml.md +176 -0
- package/docs/apis/features/web/asset-loader.md +59 -0
- package/docs/apis/features/web/container-link.md +192 -0
- package/docs/apis/features/web/esbuild.md +54 -0
- package/docs/apis/features/web/helpers.md +164 -0
- package/docs/apis/features/web/network.md +44 -0
- package/docs/apis/features/web/speech.md +69 -0
- package/docs/apis/features/web/vault.md +59 -0
- package/docs/apis/features/web/vm.md +75 -0
- package/docs/apis/features/web/voice.md +84 -0
- package/docs/apis/servers/express.md +171 -0
- package/docs/apis/servers/mcp.md +238 -0
- package/docs/apis/servers/websocket.md +170 -0
- package/docs/bootstrap/CLAUDE.md +101 -0
- package/docs/bootstrap/SKILL.md +341 -0
- package/docs/bootstrap/templates/about-command.ts +41 -0
- package/docs/bootstrap/templates/docs-models.ts +22 -0
- package/docs/bootstrap/templates/docs-readme.md +43 -0
- package/docs/bootstrap/templates/example-feature.ts +53 -0
- package/docs/bootstrap/templates/health-endpoint.ts +15 -0
- package/docs/bootstrap/templates/luca-cli.ts +30 -0
- package/docs/bootstrap/templates/runme.md +54 -0
- package/docs/challenges/caching-proxy.md +16 -0
- package/docs/challenges/content-db-round-trip.md +14 -0
- package/docs/challenges/custom-command.md +9 -0
- package/docs/challenges/file-watcher-pipeline.md +11 -0
- package/docs/challenges/grep-audit-report.md +15 -0
- package/docs/challenges/multi-feature-dashboard.md +14 -0
- package/docs/challenges/process-orchestrator.md +17 -0
- package/docs/challenges/rest-api-server-with-client.md +12 -0
- package/docs/challenges/script-runner-with-vm.md +11 -0
- package/docs/challenges/simple-rest-api.md +15 -0
- package/docs/challenges/websocket-serve-and-client.md +11 -0
- package/docs/challenges/yaml-config-system.md +14 -0
- package/docs/command-system-overhaul.md +94 -0
- package/docs/documentation-audit.md +134 -0
- package/docs/examples/assistant/CORE.md +18 -0
- package/docs/examples/assistant/hooks.ts +3 -0
- package/docs/examples/assistant/tools.ts +10 -0
- package/docs/examples/assistant-hooks-reference.ts +171 -0
- package/docs/examples/assistant-with-process-manager.md +84 -0
- package/docs/examples/content-db.md +77 -0
- package/docs/examples/disk-cache.md +83 -0
- package/docs/examples/docker.md +101 -0
- package/docs/examples/downloader.md +70 -0
- package/docs/examples/entity.md +124 -0
- package/docs/examples/esbuild.md +80 -0
- package/docs/examples/feature-as-tool-provider.md +143 -0
- package/docs/examples/file-manager.md +82 -0
- package/docs/examples/fs.md +83 -0
- package/docs/examples/git.md +85 -0
- package/docs/examples/google-auth.md +88 -0
- package/docs/examples/google-calendar.md +94 -0
- package/docs/examples/google-docs.md +82 -0
- package/docs/examples/google-drive.md +96 -0
- package/docs/examples/google-sheets.md +95 -0
- package/docs/examples/grep.md +85 -0
- package/docs/examples/ink-blocks.md +75 -0
- package/docs/examples/ink-renderer.md +41 -0
- package/docs/examples/ink.md +103 -0
- package/docs/examples/ipc-socket.md +103 -0
- package/docs/examples/json-tree.md +91 -0
- package/docs/examples/networking.md +58 -0
- package/docs/examples/nlp.md +91 -0
- package/docs/examples/opener.md +78 -0
- package/docs/examples/os.md +72 -0
- package/docs/examples/package-finder.md +89 -0
- package/docs/examples/postgres.md +91 -0
- package/docs/examples/proc.md +81 -0
- package/docs/examples/process-manager.md +79 -0
- package/docs/examples/python.md +132 -0
- package/docs/examples/repl.md +93 -0
- package/docs/examples/runpod.md +119 -0
- package/docs/examples/secure-shell.md +92 -0
- package/docs/examples/sqlite.md +86 -0
- package/docs/examples/structured-output-with-assistants.md +144 -0
- package/docs/examples/telegram.md +77 -0
- package/docs/examples/tts.md +86 -0
- package/docs/examples/ui.md +80 -0
- package/docs/examples/vault.md +70 -0
- package/docs/examples/vm.md +86 -0
- package/docs/examples/websocket-ask-and-reply-example.md +128 -0
- package/docs/examples/yaml-tree.md +93 -0
- package/docs/examples/yaml.md +104 -0
- package/docs/ideas/assistant-factory-pattern.md +142 -0
- package/docs/in-memory-fs.md +4 -0
- package/docs/introspection-audit.md +49 -0
- package/docs/introspection.md +164 -0
- package/docs/mcp/readme.md +162 -0
- package/docs/models.ts +41 -0
- package/docs/philosophy.md +86 -0
- package/docs/principles.md +7 -0
- package/docs/prompts/audit-codebase-for-failures-to-use-the-container.md +34 -0
- package/docs/prompts/check-for-undocumented-features.md +27 -0
- package/docs/prompts/mcp-test-easy-command.md +27 -0
- package/docs/scaffolds/client.md +149 -0
- package/docs/scaffolds/command.md +120 -0
- package/docs/scaffolds/endpoint.md +171 -0
- package/docs/scaffolds/feature.md +158 -0
- package/docs/scaffolds/selector.md +91 -0
- package/docs/scaffolds/server.md +196 -0
- package/docs/selectors.md +115 -0
- package/docs/sessions/custom-command/attempt-log-2.md +195 -0
- package/docs/sessions/file-watcher-pipeline/attempt-log-1.md +728 -0
- package/docs/sessions/file-watcher-pipeline/attempt-log-2.md +555 -0
- package/docs/sessions/grep-audit-report/attempt-log-1.md +289 -0
- package/docs/sessions/multi-feature-dashboard/attempt-log-2.md +679 -0
- package/docs/sessions/rest-api-server-with-client/attempt-log-1.md +1 -0
- package/docs/sessions/rest-api-server-with-client/attempt-log-3.md +920 -0
- package/docs/sessions/simple-rest-api/attempt-log-1.md +593 -0
- package/docs/sessions/websocket-serve-and-client/attempt-log-2.md +995 -0
- package/docs/tutorials/00-bootstrap.md +166 -0
- package/docs/tutorials/01-getting-started.md +106 -0
- package/docs/tutorials/02-container.md +210 -0
- package/docs/tutorials/03-scripts.md +194 -0
- package/docs/tutorials/04-features-overview.md +196 -0
- package/docs/tutorials/05-state-and-events.md +171 -0
- package/docs/tutorials/06-servers.md +157 -0
- package/docs/tutorials/07-endpoints.md +198 -0
- package/docs/tutorials/08-commands.md +252 -0
- package/docs/tutorials/09-clients.md +162 -0
- package/docs/tutorials/10-creating-features.md +203 -0
- package/docs/tutorials/11-contentbase.md +191 -0
- package/docs/tutorials/12-assistants.md +215 -0
- package/docs/tutorials/13-introspection.md +157 -0
- package/docs/tutorials/14-type-system.md +174 -0
- package/docs/tutorials/15-project-patterns.md +222 -0
- package/docs/tutorials/16-google-features.md +534 -0
- package/docs/tutorials/17-tui-blocks.md +530 -0
- package/docs/tutorials/18-semantic-search.md +334 -0
- package/docs/tutorials/19-python-sessions.md +401 -0
- package/docs/tutorials/20-browser-esm.md +234 -0
- package/index.html +1430 -0
- package/index.ts +1 -0
- package/install.sh +84 -0
- package/luca.cli.ts +16 -0
- package/luca.console.ts +9 -0
- package/main.py +6 -0
- package/package.json +219 -58
- package/public/index.html +1430 -0
- package/public/slides-ai-native.html +902 -0
- package/public/slides-intro.html +974 -0
- package/pyproject.toml +7 -0
- package/scripts/build-web.ts +28 -0
- package/scripts/examples/ask-luca-expert.ts +42 -0
- package/scripts/examples/assistant-questions.ts +12 -0
- package/scripts/examples/excalidraw-expert.ts +75 -0
- package/scripts/examples/expert-chat.ts +0 -0
- package/scripts/examples/file-manager.ts +14 -0
- package/scripts/examples/ideas.ts +12 -0
- package/scripts/examples/interactive-chat.ts +20 -0
- package/scripts/examples/openai-tool-calls.ts +113 -0
- package/scripts/examples/opening-a-web-browser.ts +5 -0
- package/scripts/examples/telegram-bot.ts +79 -0
- package/scripts/examples/using-assistant-with-mcp.ts +555 -0
- package/scripts/examples/using-claude-code.ts +10 -0
- package/scripts/examples/using-contentdb.ts +35 -0
- package/scripts/examples/using-conversations.ts +35 -0
- package/scripts/examples/using-disk-cache.ts +10 -0
- package/scripts/examples/using-docker-shell.ts +75 -0
- package/scripts/examples/using-elevenlabs.ts +25 -0
- package/scripts/examples/using-google-calendar.ts +57 -0
- package/scripts/examples/using-google-docs.ts +74 -0
- package/scripts/examples/using-google-drive.ts +74 -0
- package/scripts/examples/using-google-sheets.ts +89 -0
- package/scripts/examples/using-nlp.ts +55 -0
- package/scripts/examples/using-ollama.ts +11 -0
- package/scripts/examples/using-postgres.ts +55 -0
- package/scripts/examples/using-runpod.ts +32 -0
- package/scripts/examples/using-tts.ts +40 -0
- package/scripts/scaffold.ts +391 -0
- package/scripts/scratch.ts +15 -0
- package/scripts/stamp-build.sh +12 -0
- package/scripts/test-assistant-hooks.ts +13 -0
- package/scripts/test-docs-reader.ts +10 -0
- package/scripts/test-linux-binary.sh +80 -0
- package/scripts/update-introspection-data.ts +58 -0
- package/src/agi/README.md +14 -0
- package/src/agi/container.server.ts +152 -0
- package/src/agi/endpoints/ask.ts +60 -0
- package/src/agi/endpoints/conversations/[id].ts +45 -0
- package/src/agi/endpoints/conversations.ts +31 -0
- package/src/agi/endpoints/experts.ts +37 -0
- package/src/agi/feature.ts +13 -0
- package/src/agi/features/agent-memory.ts +694 -0
- package/src/agi/features/assistant.ts +1624 -0
- package/src/agi/features/assistants-manager.ts +418 -0
- package/src/agi/features/autonomous-assistant.ts +431 -0
- package/src/agi/features/browser-use.ts +653 -0
- package/src/agi/features/claude-code.ts +1538 -0
- package/src/agi/features/coding-tools.ts +175 -0
- package/src/agi/features/conversation-history.ts +495 -0
- package/src/agi/features/conversation.ts +1323 -0
- package/src/agi/features/docs-reader.ts +167 -0
- package/src/agi/features/file-tools.ts +293 -0
- package/src/agi/features/luca-coder.ts +639 -0
- package/src/agi/features/openai-codex.ts +651 -0
- package/src/agi/features/openapi.ts +445 -0
- package/src/agi/features/skills-library.ts +478 -0
- package/src/agi/index.ts +6 -0
- package/src/agi/lib/interceptor-chain.ts +89 -0
- package/src/agi/lib/token-counter.ts +122 -0
- package/src/bootstrap/generated.ts +9792 -0
- package/src/browser.ts +25 -0
- package/src/bus.ts +122 -0
- package/src/cli/build-info.ts +4 -0
- package/src/cli/cli.ts +355 -0
- package/src/client.ts +170 -0
- package/src/clients/civitai/index.ts +537 -0
- package/src/clients/client-template.ts +41 -0
- package/src/clients/comfyui/index.ts +604 -0
- package/src/clients/elevenlabs/index.ts +317 -0
- package/src/clients/graph.ts +87 -0
- package/src/clients/openai/index.ts +456 -0
- package/src/clients/rest.ts +207 -0
- package/src/clients/supabase/index.ts +357 -0
- package/src/clients/voicebox/index.ts +300 -0
- package/src/clients/websocket.ts +251 -0
- package/src/command.ts +505 -0
- package/src/commands/bootstrap.ts +244 -0
- package/src/commands/chat.ts +308 -0
- package/src/commands/code.ts +371 -0
- package/src/commands/console.ts +189 -0
- package/src/commands/describe.ts +243 -0
- package/src/commands/eval.ts +121 -0
- package/src/commands/help.ts +240 -0
- package/src/commands/index.ts +19 -0
- package/src/commands/introspect.ts +218 -0
- package/src/commands/mcp.ts +64 -0
- package/src/commands/prompt.ts +982 -0
- package/src/commands/run.ts +278 -0
- package/src/commands/sandbox-mcp.ts +343 -0
- package/src/commands/save-api-docs.ts +51 -0
- package/src/commands/scaffold.ts +225 -0
- package/src/commands/select.ts +99 -0
- package/src/commands/serve.ts +208 -0
- package/src/container-describer.ts +1084 -0
- package/src/container.ts +1186 -0
- package/src/endpoint.ts +365 -0
- package/src/entity.ts +173 -0
- package/src/feature.ts +118 -0
- package/src/graft.ts +181 -0
- package/src/hash-object.ts +97 -0
- package/src/helper.ts +849 -0
- package/src/introspection/generated.agi.ts +40208 -0
- package/src/introspection/generated.node.ts +28686 -0
- package/src/introspection/generated.web.ts +2251 -0
- package/src/introspection/index.ts +296 -0
- package/src/introspection/scan.ts +1131 -0
- package/src/node/container.ts +409 -0
- package/src/node/feature.ts +13 -0
- package/src/node/features/container-link.ts +559 -0
- package/src/node/features/content-db.ts +812 -0
- package/src/node/features/disk-cache.ts +388 -0
- package/src/node/features/dns.ts +669 -0
- package/src/node/features/docker.ts +921 -0
- package/src/node/features/downloader.ts +79 -0
- package/src/node/features/figlet-fonts.ts +600 -0
- package/src/node/features/file-manager.ts +535 -0
- package/src/node/features/fs.ts +1050 -0
- package/src/node/features/git.ts +592 -0
- package/src/node/features/google-auth.ts +504 -0
- package/src/node/features/google-calendar.ts +306 -0
- package/src/node/features/google-docs.ts +412 -0
- package/src/node/features/google-drive.ts +346 -0
- package/src/node/features/google-mail.ts +540 -0
- package/src/node/features/google-sheets.ts +286 -0
- package/src/node/features/grep.ts +427 -0
- package/src/node/features/helpers.ts +735 -0
- package/src/node/features/ink.ts +490 -0
- package/src/node/features/ipc-socket.ts +649 -0
- package/src/node/features/json-tree.ts +170 -0
- package/src/node/features/networking.ts +961 -0
- package/src/node/features/nlp.ts +212 -0
- package/src/node/features/opener.ts +180 -0
- package/src/node/features/os.ts +403 -0
- package/src/node/features/package-finder.ts +540 -0
- package/src/node/features/postgres.ts +289 -0
- package/src/node/features/proc.ts +503 -0
- package/src/node/features/process-manager.ts +844 -0
- package/src/node/features/python.ts +906 -0
- package/src/node/features/redis.ts +446 -0
- package/src/node/features/repl.ts +212 -0
- package/src/node/features/runpod.ts +811 -0
- package/src/node/features/secure-shell.ts +267 -0
- package/src/node/features/semantic-search.ts +935 -0
- package/src/node/features/sqlite.ts +289 -0
- package/src/node/features/telegram.ts +343 -0
- package/src/node/features/transpiler.ts +161 -0
- package/src/node/features/tts.ts +185 -0
- package/src/node/features/ui.ts +786 -0
- package/src/node/features/vault.ts +153 -0
- package/src/node/features/vm.ts +462 -0
- package/src/node/features/yaml-tree.ts +148 -0
- package/src/node/features/yaml.ts +133 -0
- package/src/node.ts +76 -0
- package/src/python/bridge.py +220 -0
- package/src/python/generated.ts +227 -0
- package/src/react/index.ts +175 -0
- package/src/registry.ts +210 -0
- package/src/scaffolds/generated.ts +1815 -0
- package/src/scaffolds/template.ts +46 -0
- package/src/schemas/base.ts +296 -0
- package/src/selector.ts +352 -0
- package/src/server.ts +229 -0
- package/src/servers/express.ts +283 -0
- package/src/servers/mcp.ts +802 -0
- package/src/servers/socket.ts +258 -0
- package/src/state.ts +101 -0
- package/src/web/clients/socket.ts +99 -0
- package/src/web/container.ts +75 -0
- package/src/web/extension.ts +30 -0
- package/src/web/feature.ts +12 -0
- package/src/web/features/asset-loader.ts +72 -0
- package/src/web/features/container-link.ts +382 -0
- package/src/web/features/esbuild.ts +93 -0
- package/src/web/features/helpers.ts +269 -0
- package/src/web/features/network.ts +85 -0
- package/src/web/features/speech.ts +104 -0
- package/src/web/features/vault.ts +207 -0
- package/src/web/features/vm.ts +85 -0
- package/src/web/features/voice-recognition.ts +161 -0
- package/src/web/shims/isomorphic-vm.ts +149 -0
- package/test/assistant-hooks.test.ts +306 -0
- package/test/assistant.test.ts +81 -0
- package/test/bus.test.ts +134 -0
- package/test/clients-servers.test.ts +217 -0
- package/test/command.test.ts +267 -0
- package/test/container-link.test.ts +274 -0
- package/test/conversation.test.ts +220 -0
- package/test/features.test.ts +160 -0
- package/test/fork-and-research.test.ts +450 -0
- package/test/integration.test.ts +787 -0
- package/test/interceptor-chain.test.ts +61 -0
- package/test/node-container.test.ts +121 -0
- package/test/python-session.test.ts +105 -0
- package/test/rate-limit.test.ts +272 -0
- package/test/semantic-search.test.ts +550 -0
- package/test/state.test.ts +121 -0
- package/test/vm-context.test.ts +146 -0
- package/test/vm-loadmodule.test.ts +213 -0
- package/test/websocket-ask.test.ts +101 -0
- package/test-integration/assistant.test.ts +138 -0
- package/test-integration/assistants-manager.test.ts +113 -0
- package/test-integration/claude-code.test.ts +98 -0
- package/test-integration/conversation-history.test.ts +205 -0
- package/test-integration/conversation.test.ts +137 -0
- package/test-integration/elevenlabs.test.ts +55 -0
- package/test-integration/google-services.test.ts +80 -0
- package/test-integration/helpers.ts +89 -0
- package/test-integration/memory.test.ts +204 -0
- package/test-integration/openai-codex.test.ts +93 -0
- package/test-integration/runpod.test.ts +58 -0
- package/test-integration/server-endpoints.test.ts +97 -0
- package/test-integration/telegram.test.ts +46 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +58 -0
- package/uv.lock +8 -0
- package/LICENSE +0 -21
- package/dist/cli/cli.js +0 -48
- package/dist/cli/common.d.ts +0 -2
- package/dist/cli/common.js +0 -6
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.js +0 -5
- package/dist/cli/run.d.ts +0 -1
- package/dist/cli/run.js +0 -38
- package/dist/core/index.d.ts +0 -4
- package/dist/core/index.js +0 -32
- package/dist/core/read.d.ts +0 -2
- package/dist/core/read.js +0 -29
- package/dist/core/request.d.ts +0 -1
- package/dist/core/request.js +0 -2
- package/dist/core/write.d.ts +0 -2
- package/dist/core/write.js +0 -21
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -5
- package/dist/utils/common.d.ts +0 -9
- package/dist/utils/common.js +0 -57
- package/dist/utils/consts.d.ts +0 -3
- package/dist/utils/consts.js +0 -11
- package/dist/utils/dict.d.ts +0 -1
- package/dist/utils/dict.js +0 -7
- package/dist/utils/index.d.ts +0 -5
- package/dist/utils/index.js +0 -21
- package/dist/utils/log.d.ts +0 -1
- package/dist/utils/log.js +0 -5
- package/dist/utils/types.d.ts +0 -1
- package/dist/utils/types.js +0 -2
- package/dist/utils/utils.test.d.ts +0 -1
- package/dist/utils/utils.test.js +0 -7
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
|
|
3
|
+
import { type AvailableFeatures } from '@soederpop/luca/feature'
|
|
4
|
+
import { Feature } from '../feature.js'
|
|
5
|
+
import { parse } from 'contentbase'
|
|
6
|
+
import type { DocsReader } from './docs-reader.js'
|
|
7
|
+
import Assistant from './assistant.js'
|
|
8
|
+
|
|
9
|
+
declare module '@soederpop/luca/feature' {
|
|
10
|
+
interface AvailableFeatures {
|
|
11
|
+
skillsLibrary: typeof SkillsLibrary
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface SkillInfo {
|
|
16
|
+
/** Skill name derived from folder name or frontmatter */
|
|
17
|
+
name: string
|
|
18
|
+
/** Description from frontmatter */
|
|
19
|
+
description: string
|
|
20
|
+
/** Absolute path to the skill folder (dirname of SKILL.md) */
|
|
21
|
+
path: string
|
|
22
|
+
/** Absolute path to SKILL.md */
|
|
23
|
+
skillFilePath: string
|
|
24
|
+
/** Which location this skill was found in */
|
|
25
|
+
locationPath: string
|
|
26
|
+
/** All frontmatter metadata */
|
|
27
|
+
meta: Record<string, unknown>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const SkillsLibraryStateSchema = FeatureStateSchema.extend({
|
|
31
|
+
loaded: z.boolean().describe('Whether skill locations have been scanned'),
|
|
32
|
+
locations: z.array(z.string()).describe('Tracked skill location folder paths'),
|
|
33
|
+
skillCount: z.number().describe('Total number of discovered skills'),
|
|
34
|
+
skills: z.record(z.string(), z.any()).describe('Discovered skills keyed by name'),
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
export const SkillsLibraryOptionsSchema = FeatureOptionsSchema.extend({
|
|
38
|
+
configPath: z.string().optional().describe('Override path for skills.json (defaults to ~/.luca/skills.json)'),
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
export const SkillsLibraryEventsSchema = FeatureEventsSchema.extend({
|
|
42
|
+
started: z.tuple([]).describe('Fired after all skill locations have been scanned'),
|
|
43
|
+
locationAdded: z.tuple([z.string().describe('The absolute path of the added location')]).describe('Fired when a new skill location is registered'),
|
|
44
|
+
skillDiscovered: z.tuple([z.any().describe('The SkillInfo object')]).describe('Fired when a skill is discovered during scanning'),
|
|
45
|
+
}).describe('SkillsLibrary events')
|
|
46
|
+
|
|
47
|
+
export type SkillsLibraryState = z.infer<typeof SkillsLibraryStateSchema>
|
|
48
|
+
export type SkillsLibraryOptions = z.infer<typeof SkillsLibraryOptionsSchema>
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Manages a registry of skill locations — folders containing SKILL.md files.
|
|
52
|
+
*
|
|
53
|
+
* Persists known locations to ~/.luca/skills.json and scans them on start.
|
|
54
|
+
* Each skill folder can be opened as a DocsReader for AI-assisted Q&A.
|
|
55
|
+
* Exposes tools for assistant integration via assistant.use(skillsLibrary).
|
|
56
|
+
*
|
|
57
|
+
* @extends Feature
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const lib = container.feature('skillsLibrary')
|
|
61
|
+
* await lib.start()
|
|
62
|
+
* await lib.addLocation('~/.claude/skills')
|
|
63
|
+
* lib.list() // => SkillInfo[]
|
|
64
|
+
* const reader = lib.createSkillReader('my-skill')
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export class SkillsLibrary extends Feature<SkillsLibraryState, SkillsLibraryOptions> {
|
|
68
|
+
static override stateSchema = SkillsLibraryStateSchema
|
|
69
|
+
static override optionsSchema = SkillsLibraryOptionsSchema
|
|
70
|
+
static override eventsSchema = SkillsLibraryEventsSchema
|
|
71
|
+
static override shortcut = 'features.skillsLibrary' as const
|
|
72
|
+
|
|
73
|
+
static { Feature.register(this, 'skillsLibrary') }
|
|
74
|
+
|
|
75
|
+
/** Tools for assistant integration via assistant.use(skillsLibrary). */
|
|
76
|
+
static override tools: Record<string, { schema: z.ZodType; handler?: Function }> = {
|
|
77
|
+
searchAvailableSkills: {
|
|
78
|
+
schema: z.object({
|
|
79
|
+
query: z.string().optional().describe('A keyword or phrase to filter skills by name or description. Omit to list all available skills.'),
|
|
80
|
+
}).describe('Discover what skills are available. Call this first when you need specialized knowledge — skills are curated guides and reference material for specific domains (frameworks, tools, patterns). Returns skill names and descriptions so you can decide which to load.'),
|
|
81
|
+
},
|
|
82
|
+
loadSkill: {
|
|
83
|
+
schema: z.object({
|
|
84
|
+
skillName: z.string().describe('The exact skill name as returned by searchAvailableSkills'),
|
|
85
|
+
}).describe('Load a skill\'s full reference content (SKILL.md). This gives you detailed guidance, examples, and best practices for that domain. Load a skill before attempting work in an unfamiliar area — the content is curated to prevent common mistakes.'),
|
|
86
|
+
},
|
|
87
|
+
askSkillBasedQuestion: {
|
|
88
|
+
schema: z.object({
|
|
89
|
+
skillName: z.string().describe('The exact skill name to query'),
|
|
90
|
+
question: z.string().describe('A specific question about the skill\'s domain. Be precise — "how do I add a new feature to the container?" is better than "tell me about features".'),
|
|
91
|
+
}).describe('Ask a focused question about a skill\'s domain using AI-assisted document reading. Use this when you need a specific answer from a skill rather than reading the whole thing. More efficient than loadSkill for targeted lookups.'),
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** @returns Default state. */
|
|
96
|
+
override get initialState(): SkillsLibraryState {
|
|
97
|
+
return {
|
|
98
|
+
...super.initialState,
|
|
99
|
+
started: false,
|
|
100
|
+
locations: [],
|
|
101
|
+
skillCount: 0,
|
|
102
|
+
skills: {},
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
override setupToolsConsumer(assistant: Feature) {
|
|
107
|
+
if (!(assistant instanceof Assistant)) {
|
|
108
|
+
throw new Error('Skills library tools require an Assistant instance (including subclasses).')
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const a : Assistant = assistant as Assistant
|
|
112
|
+
|
|
113
|
+
a.addSystemPromptExtension('skillsLibrary', [
|
|
114
|
+
'## Skills Library',
|
|
115
|
+
'',
|
|
116
|
+
'You have access to a library of curated skills — domain-specific reference guides with examples, patterns, and best practices.',
|
|
117
|
+
'',
|
|
118
|
+
'**When to use skills:**',
|
|
119
|
+
'- When working in an unfamiliar domain or framework — load the skill before writing code',
|
|
120
|
+
'- When the user asks about a topic that might have a matching skill — search first',
|
|
121
|
+
'- When you see "Required Skills" in a message — load those skills immediately with `loadSkill` before answering',
|
|
122
|
+
'',
|
|
123
|
+
'**Workflow:** `searchAvailableSkills` → find relevant skill → `loadSkill` to get the full guide → follow its patterns. Use `askSkillBasedQuestion` for targeted lookups when you don\'t need the whole guide.',
|
|
124
|
+
'',
|
|
125
|
+
'**Skills are authoritative.** When a loaded skill contradicts your general knowledge, follow the skill — it reflects project-specific conventions and decisions.',
|
|
126
|
+
].join('\n'))
|
|
127
|
+
|
|
128
|
+
const { container } = a
|
|
129
|
+
|
|
130
|
+
const skillsLibrary = this
|
|
131
|
+
|
|
132
|
+
const preloadSkills : string[] = []
|
|
133
|
+
if (a.meta.skills) {
|
|
134
|
+
if (Array.isArray(a.meta.skills)) {
|
|
135
|
+
preloadSkills.push(...a.meta.skills)
|
|
136
|
+
} else {
|
|
137
|
+
preloadSkills.push(a.meta.skills)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function beforeAskCheckIfWeNeedSkills(ctx: any, next: any) {
|
|
142
|
+
const { question } = ctx
|
|
143
|
+
const skills = await skillsLibrary.findRelevantSkillsForAssistant(a, question as string)
|
|
144
|
+
|
|
145
|
+
const allSkillsToLoad : string[] = container.utils.lodash.uniq([
|
|
146
|
+
...skills,
|
|
147
|
+
...preloadSkills,
|
|
148
|
+
])
|
|
149
|
+
|
|
150
|
+
if (allSkillsToLoad.length) {
|
|
151
|
+
ctx.question = `${ctx.question} \n\n## Required Skills\nYou will need to load the following skills to answer this question: ${skills.join(', ')}`
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
a.interceptors.beforeAsk.remove(beforeAskCheckIfWeNeedSkills)
|
|
155
|
+
|
|
156
|
+
await next()
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
assistant.intercept('beforeAsk', beforeAskCheckIfWeNeedSkills as any)
|
|
160
|
+
|
|
161
|
+
return assistant
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Discovered skills keyed by name. */
|
|
165
|
+
get skills(): Record<string, SkillInfo> {
|
|
166
|
+
return (this.state.get('skills') || {}) as Record<string, SkillInfo>
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
get availableSkills() {
|
|
170
|
+
return Object.keys(this.skills)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
get skillsTable() : Record<string, string> {
|
|
174
|
+
const skills = this.skills
|
|
175
|
+
|
|
176
|
+
return Object.fromEntries(
|
|
177
|
+
Object.keys(skills).map((name) => [name, this.skills[name]!.description])
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Resolved path to the skills.json config file. */
|
|
182
|
+
get configPath(): string {
|
|
183
|
+
if (this.options.configPath) return this.options.configPath
|
|
184
|
+
const { os, paths } = this.container
|
|
185
|
+
return paths.resolve(os.homedir, '.luca', 'skills.json')
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Whether the library has been loaded. */
|
|
189
|
+
get isStarted(): boolean {
|
|
190
|
+
return !!this.state.get('started')
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Expand ~ to home directory in a path. */
|
|
194
|
+
private expandHome(p: string): string {
|
|
195
|
+
return p.replace(/^\~/, this.container.os.homedir)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Read the persisted config, creating it if it doesn't exist. */
|
|
199
|
+
private readConfig(): { locations: string[] } {
|
|
200
|
+
const { fs } = this.container
|
|
201
|
+
|
|
202
|
+
if (!fs.exists(this.configPath)) {
|
|
203
|
+
const defaultConfig = { locations: [] as string[] }
|
|
204
|
+
this.writeConfig(defaultConfig)
|
|
205
|
+
return defaultConfig
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return fs.readJson(this.configPath)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Write the config back to disk. */
|
|
212
|
+
private writeConfig(config: { locations: string[] }): void {
|
|
213
|
+
const { fs, os, paths } = this.container
|
|
214
|
+
fs.mkdirp(paths.resolve(os.homedir, '.luca'))
|
|
215
|
+
fs.writeJson(this.configPath, config, 2)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Start the skills library: read config, scan all locations.
|
|
220
|
+
*
|
|
221
|
+
* @returns This instance for chaining
|
|
222
|
+
*/
|
|
223
|
+
async start(): Promise<SkillsLibrary> {
|
|
224
|
+
if (this.isStarted) return this
|
|
225
|
+
|
|
226
|
+
const { uniq } = this.container.utils.lodash
|
|
227
|
+
const config = this.readConfig()
|
|
228
|
+
const configLocations = config.locations.map(l => this.expandHome(l))
|
|
229
|
+
const allLocations = uniq([
|
|
230
|
+
...configLocations,
|
|
231
|
+
(this.container as any).paths.resolve((this.container as any).os.homedir, '.claude', 'skills'),
|
|
232
|
+
(this.container as any).paths.resolve((this.container as any).cwd, '.claude', 'skills')
|
|
233
|
+
]).filter(Boolean).filter(l => (this.container as any).fs.exists(l))
|
|
234
|
+
this.state.set('locations', allLocations)
|
|
235
|
+
|
|
236
|
+
for (const loc of allLocations) {
|
|
237
|
+
await this.scanLocation(loc)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
this.state.set('started', true)
|
|
241
|
+
this.state.set('skillCount', Object.keys(this.skills).length)
|
|
242
|
+
this.emit('started')
|
|
243
|
+
|
|
244
|
+
return this
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Add a new skill location folder and scan it for skills.
|
|
249
|
+
*
|
|
250
|
+
* @param locationPath - Path to a directory containing skill subfolders with SKILL.md
|
|
251
|
+
*/
|
|
252
|
+
async addLocation(locationPath: string): Promise<void> {
|
|
253
|
+
const resolved = this.expandHome(locationPath)
|
|
254
|
+
const current = this.state.get('locations') as string[]
|
|
255
|
+
|
|
256
|
+
if (current.includes(resolved)) return
|
|
257
|
+
|
|
258
|
+
const updated = [...current, resolved]
|
|
259
|
+
this.state.set('locations', updated)
|
|
260
|
+
|
|
261
|
+
// Persist — store the original (unexpanded) path for portability
|
|
262
|
+
const config = this.readConfig()
|
|
263
|
+
if (!config.locations.includes(locationPath)) {
|
|
264
|
+
config.locations.push(locationPath)
|
|
265
|
+
this.writeConfig(config)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
await this.scanLocation(resolved)
|
|
269
|
+
this.state.set('skillCount', Object.keys(this.skills).length)
|
|
270
|
+
this.emit('locationAdded', resolved)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Remove a skill location and its skills from the library.
|
|
275
|
+
*
|
|
276
|
+
* @param locationPath - The location path to remove
|
|
277
|
+
*/
|
|
278
|
+
async removeLocation(locationPath: string): Promise<void> {
|
|
279
|
+
const resolved = this.expandHome(locationPath)
|
|
280
|
+
const current = this.state.get('locations') as string[]
|
|
281
|
+
this.state.set('locations', current.filter(l => l !== resolved))
|
|
282
|
+
|
|
283
|
+
// Remove skills from this location
|
|
284
|
+
const remaining: Record<string, SkillInfo> = {}
|
|
285
|
+
for (const [name, info] of Object.entries(this.skills)) {
|
|
286
|
+
if (info.locationPath !== resolved) {
|
|
287
|
+
remaining[name] = info
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
this.state.set('skills', remaining)
|
|
291
|
+
this.state.set('skillCount', Object.keys(remaining).length)
|
|
292
|
+
|
|
293
|
+
// Persist
|
|
294
|
+
const config = this.readConfig()
|
|
295
|
+
config.locations = config.locations.filter(l => this.expandHome(l) !== resolved)
|
|
296
|
+
this.writeConfig(config)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Scan a location folder for skill subfolders containing SKILL.md.
|
|
301
|
+
*
|
|
302
|
+
* @param locationPath - Absolute path to scan
|
|
303
|
+
*/
|
|
304
|
+
async scanLocation(locationPath: string): Promise<void> {
|
|
305
|
+
const { fs, paths } = this.container
|
|
306
|
+
if (!fs.exists(locationPath)) return
|
|
307
|
+
|
|
308
|
+
const entries = fs.readdirSync(locationPath)
|
|
309
|
+
|
|
310
|
+
for (const entry of entries) {
|
|
311
|
+
const skillDir = paths.resolve(locationPath, entry)
|
|
312
|
+
const skillFile = paths.resolve(skillDir, 'SKILL.md')
|
|
313
|
+
|
|
314
|
+
if (!fs.exists(skillFile)) continue
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
const parsed = await parse(skillFile)
|
|
318
|
+
const meta = (parsed.meta || {}) as Record<string, unknown>
|
|
319
|
+
const name = entry
|
|
320
|
+
|
|
321
|
+
const info: SkillInfo = {
|
|
322
|
+
name,
|
|
323
|
+
description: (meta.description as string) || '',
|
|
324
|
+
path: skillDir,
|
|
325
|
+
skillFilePath: skillFile,
|
|
326
|
+
locationPath,
|
|
327
|
+
meta,
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
this.state.set('skills', { ...this.skills, [name]: info })
|
|
331
|
+
this.emit('skillDiscovered', info)
|
|
332
|
+
} catch {
|
|
333
|
+
// Skip unparseable skill files
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Return all discovered skills. */
|
|
339
|
+
list(): SkillInfo[] {
|
|
340
|
+
return Object.values(this.skills)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** Find a skill by name. */
|
|
344
|
+
find(skillName: string): SkillInfo | undefined {
|
|
345
|
+
return this.skills[skillName]
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Create a DocsReader for a skill's folder, enabling AI-assisted Q&A.
|
|
350
|
+
*
|
|
351
|
+
* @param skillName - Name of the skill to create a reader for
|
|
352
|
+
* @returns A DocsReader instance rooted at the skill's folder
|
|
353
|
+
*/
|
|
354
|
+
createSkillReader(skillName: string): DocsReader {
|
|
355
|
+
const skill = this.find(skillName)
|
|
356
|
+
if (!skill) throw new Error(`Skill "${skillName}" not found in the library`)
|
|
357
|
+
|
|
358
|
+
return this.container.feature('docsReader', { contentDb: skill.path })
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Create a tmp directory containing symlinked/copied skill folders by name,
|
|
363
|
+
* suitable for passing to claude --add-dir.
|
|
364
|
+
*
|
|
365
|
+
* @param skillNames - Array of skill names to include
|
|
366
|
+
* @returns Absolute path to the created directory
|
|
367
|
+
*/
|
|
368
|
+
ensureFolderCreatedWithSkillsByName(skillNames: string[]): string {
|
|
369
|
+
const { fs, paths, os } = this.container
|
|
370
|
+
const hash = this.container.utils.hashObject(skillNames.sort())
|
|
371
|
+
const dir = paths.resolve(os.tmpdir, 'luca-skills', hash)
|
|
372
|
+
|
|
373
|
+
if (fs.exists(dir)) return dir
|
|
374
|
+
|
|
375
|
+
fs.mkdirp(dir)
|
|
376
|
+
|
|
377
|
+
for (const name of skillNames) {
|
|
378
|
+
const skill = this.find(name)
|
|
379
|
+
if (!skill) throw new Error(`Skill "${name}" not found in the library`)
|
|
380
|
+
|
|
381
|
+
const dest = paths.resolve(dir, name)
|
|
382
|
+
if (!fs.exists(dest)) {
|
|
383
|
+
fs.copy(skill.path, dest)
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return dir
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// --- Tool handlers for assistant.use(skillsLibrary) ---
|
|
391
|
+
|
|
392
|
+
/** Search available skills, optionally filtered by a query string. */
|
|
393
|
+
async searchAvailableSkills({ query }: { query?: string } = {}): Promise<string> {
|
|
394
|
+
if (!this.isStarted) await this.start()
|
|
395
|
+
|
|
396
|
+
let skills = this.list()
|
|
397
|
+
|
|
398
|
+
if (query) {
|
|
399
|
+
const q = query.toLowerCase()
|
|
400
|
+
skills = skills.filter(s =>
|
|
401
|
+
s.name.toLowerCase().includes(q) ||
|
|
402
|
+
s.description.toLowerCase().includes(q)
|
|
403
|
+
)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (skills.length === 0) return 'No skills found.'
|
|
407
|
+
|
|
408
|
+
return skills.map(s => `- **${s.name}**: ${s.description || '(no description)'}\n Path: ${s.path}`).join('\n')
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/** Load a skill's full SKILL.md content and metadata. */
|
|
412
|
+
async loadSkill({ skillName }: { skillName: string }): Promise<string> {
|
|
413
|
+
if (!this.isStarted) await this.start()
|
|
414
|
+
|
|
415
|
+
const skill = this.find(skillName)
|
|
416
|
+
if (!skill) return `Skill "${skillName}" not found.`
|
|
417
|
+
|
|
418
|
+
const content = this.container.fs.readFile(skill.skillFilePath)
|
|
419
|
+
|
|
420
|
+
return `# Skill: ${skill.name}\n\n**Description:** ${skill.description}\n**Path:** ${skill.path}\n\n---\n\n${content}`
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/** Ask a question about a specific skill using a DocsReader. */
|
|
424
|
+
async askSkillBasedQuestion({ skillName, question }: { skillName: string; question: string }): Promise<string> {
|
|
425
|
+
if (!this.isStarted) await this.start()
|
|
426
|
+
|
|
427
|
+
const reader = this.createSkillReader(skillName)
|
|
428
|
+
const answer = await reader.ask(question)
|
|
429
|
+
return answer
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Fork the given assistant and ask it which skills (if any) are relevant
|
|
434
|
+
* to the user's query. Returns an array of skill names that should be loaded
|
|
435
|
+
* before the real question is answered.
|
|
436
|
+
*
|
|
437
|
+
* The fork is ephemeral (historyMode: 'none') and uses structured output so
|
|
438
|
+
* the result is always a clean string array — never free text.
|
|
439
|
+
*
|
|
440
|
+
* @param assistant - The assistant instance to fork
|
|
441
|
+
* @param userQuery - The user's original question
|
|
442
|
+
* @returns Array of skill names relevant to the query (may be empty)
|
|
443
|
+
*/
|
|
444
|
+
async findRelevantSkillsForAssistant(assistant: Assistant, userQuery: string): Promise<string[]> {
|
|
445
|
+
if (!this.isStarted) await this.start()
|
|
446
|
+
|
|
447
|
+
const skills = this.list()
|
|
448
|
+
if (skills.length === 0) return []
|
|
449
|
+
|
|
450
|
+
const responseSchema = z.object({
|
|
451
|
+
skills: z.array(z.string()).describe('Names of skills relevant to the query. Empty array if none apply.'),
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
const skillsDescription = Object.entries(this.skillsTable)
|
|
455
|
+
.map(([title,description]) => `- **${title}**: ${description}`)
|
|
456
|
+
.join("\n")
|
|
457
|
+
|
|
458
|
+
const prompt = this.container.ui.endent(`You are a routing assistant. Given a user query and a list of available skills, determine which skills (if any) should be loaded to help answer the query.
|
|
459
|
+
Available skills:
|
|
460
|
+
-------
|
|
461
|
+
${skillsDescription}
|
|
462
|
+
|
|
463
|
+
User query: ${userQuery}
|
|
464
|
+
|
|
465
|
+
Return only the skill names that are directly relevant. Return an empty array if none apply. Do not load skills speculatively — only include ones that would materially help answer this specific query.`)
|
|
466
|
+
|
|
467
|
+
const fork = assistant.conversation.fork()
|
|
468
|
+
const result = await fork.ask(prompt, { schema: responseSchema }) as unknown as { skills: string[] }
|
|
469
|
+
|
|
470
|
+
const found = result.skills.filter(name => this.find(name) !== undefined)
|
|
471
|
+
|
|
472
|
+
this.emit('foundSkills', found, assistant, userQuery)
|
|
473
|
+
|
|
474
|
+
return found
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
export default SkillsLibrary
|
package/src/agi/index.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A composable middleware chain. Each interceptor receives a mutable
|
|
3
|
+
* context and a `next` function. Calling `next()` continues the chain;
|
|
4
|
+
* skipping it short-circuits.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type InterceptorFn<T> = (ctx: T, next: () => Promise<void>) => Promise<void>
|
|
8
|
+
|
|
9
|
+
export class InterceptorChain<T> {
|
|
10
|
+
private fns: InterceptorFn<T>[] = []
|
|
11
|
+
|
|
12
|
+
add(fn: InterceptorFn<T>): void {
|
|
13
|
+
this.fns.push(fn)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
remove(fn: InterceptorFn<T>): void {
|
|
17
|
+
const idx = this.fns.indexOf(fn)
|
|
18
|
+
if (idx !== -1) this.fns.splice(idx, 1)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get hasInterceptors(): boolean {
|
|
22
|
+
return this.fns.length > 0
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get size(): number {
|
|
26
|
+
return this.fns.length
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
clear(): void {
|
|
30
|
+
this.fns = []
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
clone(): InterceptorChain<T> {
|
|
34
|
+
const copy = new InterceptorChain<T>()
|
|
35
|
+
for (const fn of this.fns) copy.add(fn)
|
|
36
|
+
return copy
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async run(ctx: T, final: () => Promise<void>): Promise<void> {
|
|
40
|
+
let index = 0
|
|
41
|
+
const fns = this.fns
|
|
42
|
+
|
|
43
|
+
const next = async (): Promise<void> => {
|
|
44
|
+
if (index < fns.length) {
|
|
45
|
+
const fn = fns[index++]!
|
|
46
|
+
await fn(ctx, next)
|
|
47
|
+
} else {
|
|
48
|
+
await final()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
await next()
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface BeforeAskCtx {
|
|
57
|
+
question: string | any[]
|
|
58
|
+
options?: any
|
|
59
|
+
result?: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ToolCallCtx {
|
|
63
|
+
name: string
|
|
64
|
+
args: Record<string, any>
|
|
65
|
+
result?: string
|
|
66
|
+
error?: any
|
|
67
|
+
skip?: boolean
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface BeforeResponseCtx {
|
|
71
|
+
text: string
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface BeforeTurnCtx {
|
|
75
|
+
turn: number
|
|
76
|
+
isFollowUp: boolean
|
|
77
|
+
messages: any[]
|
|
78
|
+
skip?: boolean
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface InterceptorPoints {
|
|
82
|
+
beforeAsk: BeforeAskCtx
|
|
83
|
+
beforeTurn: BeforeTurnCtx
|
|
84
|
+
beforeToolCall: ToolCallCtx
|
|
85
|
+
afterToolCall: ToolCallCtx
|
|
86
|
+
beforeResponse: BeforeResponseCtx
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export type InterceptorPoint = keyof InterceptorPoints
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { encodingForModel, getEncoding } from 'js-tiktoken'
|
|
2
|
+
import type { Tiktoken } from 'js-tiktoken'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Known model context window sizes. Prefix-matched for dated variants
|
|
6
|
+
* (e.g. "gpt-4o-2024-08-06" matches "gpt-4o").
|
|
7
|
+
*/
|
|
8
|
+
const MODEL_CONTEXT_WINDOWS: Record<string, number> = {
|
|
9
|
+
'gpt-4.1': 1_000_000,
|
|
10
|
+
'gpt-4.1-mini': 1_000_000,
|
|
11
|
+
'gpt-4.1-nano': 1_000_000,
|
|
12
|
+
'gpt-4o': 128_000,
|
|
13
|
+
'gpt-4o-mini': 128_000,
|
|
14
|
+
'gpt-4-turbo': 128_000,
|
|
15
|
+
'gpt-4': 8_192,
|
|
16
|
+
'gpt-3.5-turbo': 16_385,
|
|
17
|
+
'o1': 200_000,
|
|
18
|
+
'o1-mini': 128_000,
|
|
19
|
+
'o1-pro': 200_000,
|
|
20
|
+
'o3': 200_000,
|
|
21
|
+
'o3-mini': 200_000,
|
|
22
|
+
'o4-mini': 200_000,
|
|
23
|
+
'gpt-5': 1_000_000,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const DEFAULT_CONTEXT_WINDOW = 128_000
|
|
27
|
+
|
|
28
|
+
const encoderCache = new Map<string, Tiktoken>()
|
|
29
|
+
|
|
30
|
+
/** Look up the context window size for a model name (exact then prefix match). */
|
|
31
|
+
export function getContextWindow(model: string): number {
|
|
32
|
+
if (MODEL_CONTEXT_WINDOWS[model]) return MODEL_CONTEXT_WINDOWS[model]
|
|
33
|
+
|
|
34
|
+
// Prefix match — longest prefix wins (e.g. "gpt-4o-mini" before "gpt-4o")
|
|
35
|
+
let best = ''
|
|
36
|
+
for (const key of Object.keys(MODEL_CONTEXT_WINDOWS)) {
|
|
37
|
+
if (model.startsWith(key) && key.length > best.length) {
|
|
38
|
+
best = key
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return best ? MODEL_CONTEXT_WINDOWS[best] ?? DEFAULT_CONTEXT_WINDOW : DEFAULT_CONTEXT_WINDOW
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Get a cached tiktoken encoder for a model (falls back to o200k_base). */
|
|
46
|
+
export function getEncoder(model: string): Tiktoken {
|
|
47
|
+
if (encoderCache.has(model)) return encoderCache.get(model)!
|
|
48
|
+
|
|
49
|
+
let enc: Tiktoken
|
|
50
|
+
try {
|
|
51
|
+
enc = encodingForModel(model as any)
|
|
52
|
+
} catch {
|
|
53
|
+
enc = getEncoding('o200k_base')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
encoderCache.set(model, enc)
|
|
57
|
+
return enc
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Count tokens in a plain string. */
|
|
61
|
+
export function countTokens(text: string, model: string): number {
|
|
62
|
+
return getEncoder(model).encode(text).length
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Estimate the total input token count for a messages array.
|
|
67
|
+
* Follows the OpenAI token counting recipe with per-message overhead.
|
|
68
|
+
*/
|
|
69
|
+
export function countMessageTokens(messages: any[], model: string): number {
|
|
70
|
+
const enc = getEncoder(model)
|
|
71
|
+
const TOKENS_PER_MESSAGE = 3 // <|start|>role\ncontent<|end|>
|
|
72
|
+
const REPLY_PRIMING = 3
|
|
73
|
+
|
|
74
|
+
let total = 0
|
|
75
|
+
|
|
76
|
+
for (const msg of messages) {
|
|
77
|
+
total += TOKENS_PER_MESSAGE
|
|
78
|
+
|
|
79
|
+
// Role
|
|
80
|
+
if (msg.role) {
|
|
81
|
+
total += enc.encode(msg.role).length
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Content
|
|
85
|
+
if (typeof msg.content === 'string') {
|
|
86
|
+
total += enc.encode(msg.content).length
|
|
87
|
+
} else if (Array.isArray(msg.content)) {
|
|
88
|
+
for (const part of msg.content) {
|
|
89
|
+
if (part.type === 'text' && part.text) {
|
|
90
|
+
total += enc.encode(part.text).length
|
|
91
|
+
} else if (part.type === 'image_url') {
|
|
92
|
+
// Rough image token estimates
|
|
93
|
+
const detail = part.image_url?.detail || 'auto'
|
|
94
|
+
total += detail === 'low' ? 85 : 170
|
|
95
|
+
} else if (part.type === 'input_audio' || part.type === 'input_file') {
|
|
96
|
+
total += 50 // rough placeholder for non-text parts
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Tool calls on assistant messages
|
|
102
|
+
if (msg.tool_calls && Array.isArray(msg.tool_calls)) {
|
|
103
|
+
for (const tc of msg.tool_calls) {
|
|
104
|
+
total += 3 // tool call overhead
|
|
105
|
+
if (tc.function?.name) {
|
|
106
|
+
total += enc.encode(tc.function.name).length
|
|
107
|
+
}
|
|
108
|
+
if (tc.function?.arguments) {
|
|
109
|
+
total += enc.encode(tc.function.arguments).length
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Name field (used on some message types)
|
|
115
|
+
if (msg.name) {
|
|
116
|
+
total += enc.encode(msg.name).length + 1
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
total += REPLY_PRIMING
|
|
121
|
+
return total
|
|
122
|
+
}
|