@soederpop/luca 0.1.2 → 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 +1 -5
- 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/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 +87 -6
- 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 +1246 -798
- package/src/introspection/generated.node.ts +892 -798
- 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 +2 -2
- package/src/node/features/ui.ts +1 -1
- package/src/node/features/vm.ts +3 -3
- package/src/node/features/yaml-tree.ts +1 -1
- package/src/node.ts +1 -0
- package/src/python/generated.ts +1 -1
- package/src/scaffolds/generated.ts +1 -1
- package/src/selector.ts +74 -4
- package/src/server.ts +2 -2
- package/src/servers/mcp.ts +6 -6
- package/src/web/features/helpers.ts +1 -1
- package/src/web/features/network.ts +1 -0
- package/test/conversation.test.ts +220 -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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
|
|
4
4
|
import { type AvailableFeatures } from '@soederpop/luca/feature'
|
|
5
|
-
import { Feature } from '
|
|
5
|
+
import { Feature } from '../feature.js'
|
|
6
6
|
|
|
7
7
|
declare module '@soederpop/luca/feature' {
|
|
8
8
|
interface AvailableFeatures {
|
|
@@ -1349,6 +1349,170 @@ export class ClaudeCode extends Feature<ClaudeCodeState, ClaudeCodeOptions> {
|
|
|
1349
1349
|
}
|
|
1350
1350
|
}
|
|
1351
1351
|
|
|
1352
|
+
/**
|
|
1353
|
+
* List all Claude Code processes currently registered in ~/.claude/sessions/.
|
|
1354
|
+
* Returns each session's metadata along with whether the process is still alive.
|
|
1355
|
+
*
|
|
1356
|
+
* @returns {Promise<Array<{ pid: number; sessionId: string; cwd: string; startedAt: number; kind: string; entrypoint: string; alive: boolean }>>}
|
|
1357
|
+
*
|
|
1358
|
+
* @example
|
|
1359
|
+
* const sessions = await cc.listProcessSessions()
|
|
1360
|
+
* for (const s of sessions) {
|
|
1361
|
+
* console.log(`[${s.alive ? 'LIVE' : 'dead'}] PID ${s.pid} in ${s.cwd}`)
|
|
1362
|
+
* }
|
|
1363
|
+
*/
|
|
1364
|
+
async listProcessSessions(): Promise<Array<{
|
|
1365
|
+
pid: number
|
|
1366
|
+
sessionId: string
|
|
1367
|
+
cwd: string
|
|
1368
|
+
startedAt: number
|
|
1369
|
+
kind: string
|
|
1370
|
+
entrypoint: string
|
|
1371
|
+
alive: boolean
|
|
1372
|
+
}>> {
|
|
1373
|
+
const fs = this.container.feature('fs')
|
|
1374
|
+
const proc = this.container.feature('proc')
|
|
1375
|
+
const home = process.env.HOME ?? '/tmp'
|
|
1376
|
+
const sessionsDir = `${home}/.claude/sessions`
|
|
1377
|
+
|
|
1378
|
+
let files: string[]
|
|
1379
|
+
try {
|
|
1380
|
+
files = await fs.readdir(sessionsDir)
|
|
1381
|
+
} catch {
|
|
1382
|
+
return []
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
const jsonFiles = files.filter((f: string) => f.endsWith('.json'))
|
|
1386
|
+
|
|
1387
|
+
const results = await Promise.all(jsonFiles.map(async (file: string) => {
|
|
1388
|
+
try {
|
|
1389
|
+
const raw = await fs.readFile(`${sessionsDir}/${file}`, 'utf8')
|
|
1390
|
+
const data = JSON.parse(raw)
|
|
1391
|
+
let alive = false
|
|
1392
|
+
try {
|
|
1393
|
+
await proc.exec(`kill -0 ${data.pid}`)
|
|
1394
|
+
alive = true
|
|
1395
|
+
} catch {
|
|
1396
|
+
alive = false
|
|
1397
|
+
}
|
|
1398
|
+
return { ...data, alive }
|
|
1399
|
+
} catch {
|
|
1400
|
+
return null
|
|
1401
|
+
}
|
|
1402
|
+
}))
|
|
1403
|
+
|
|
1404
|
+
return results.filter(Boolean)
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
/**
|
|
1408
|
+
* Read a single process session by PID from ~/.claude/sessions/<pid>.json.
|
|
1409
|
+
*
|
|
1410
|
+
* @param {number} pid - The process ID
|
|
1411
|
+
* @returns {Promise<{ pid: number; sessionId: string; cwd: string; startedAt: number; kind: string; entrypoint: string } | null>}
|
|
1412
|
+
*
|
|
1413
|
+
* @example
|
|
1414
|
+
* const session = await cc.getProcessSession(12345)
|
|
1415
|
+
* console.log(session?.cwd)
|
|
1416
|
+
*/
|
|
1417
|
+
async getProcessSession(pid: number): Promise<{
|
|
1418
|
+
pid: number
|
|
1419
|
+
sessionId: string
|
|
1420
|
+
cwd: string
|
|
1421
|
+
startedAt: number
|
|
1422
|
+
kind: string
|
|
1423
|
+
entrypoint: string
|
|
1424
|
+
} | null> {
|
|
1425
|
+
const fs = this.container.feature('fs')
|
|
1426
|
+
const home = process.env.HOME ?? '/tmp'
|
|
1427
|
+
try {
|
|
1428
|
+
const raw = await fs.readFile(`${home}/.claude/sessions/${pid}.json`, 'utf8')
|
|
1429
|
+
return JSON.parse(raw)
|
|
1430
|
+
} catch {
|
|
1431
|
+
return null
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
/**
|
|
1436
|
+
* Read the conversation history for a Claude Code session from its JSONL file in
|
|
1437
|
+
* ~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl.
|
|
1438
|
+
*
|
|
1439
|
+
* Returns an array of parsed message objects (user, assistant, tool_use, tool_result).
|
|
1440
|
+
*
|
|
1441
|
+
* @param {string} sessionId - The Claude CLI session ID (from listProcessSessions or getProcessSession)
|
|
1442
|
+
* @param {string} cwd - The working directory of the session (used to locate the project folder)
|
|
1443
|
+
* @returns {Promise<any[]>} Array of parsed JSONL records
|
|
1444
|
+
*
|
|
1445
|
+
* @example
|
|
1446
|
+
* const sessions = await cc.listProcessSessions()
|
|
1447
|
+
* const s = sessions[0]
|
|
1448
|
+
* const history = await cc.getConversationHistory(s.sessionId, s.cwd)
|
|
1449
|
+
* console.log(history.length, 'turns')
|
|
1450
|
+
*/
|
|
1451
|
+
async getConversationHistory(sessionId: string, cwd?: string): Promise<any[]> {
|
|
1452
|
+
const fs = this.container.feature('fs')
|
|
1453
|
+
const home = process.env.HOME ?? '/tmp'
|
|
1454
|
+
const resolvedCwd = cwd ?? this.options.cwd ?? (this.container as any).cwd
|
|
1455
|
+
const encodedCwd = resolvedCwd.replace(/\//g, '-').replace(/@/g, '-')
|
|
1456
|
+
const filePath = `${home}/.claude/projects/${encodedCwd}/${sessionId}.jsonl`
|
|
1457
|
+
try {
|
|
1458
|
+
const raw = await fs.readFile(filePath, 'utf8')
|
|
1459
|
+
return raw
|
|
1460
|
+
.split('\n')
|
|
1461
|
+
.filter((line: string) => line.trim())
|
|
1462
|
+
.map((line: string) => JSON.parse(line))
|
|
1463
|
+
} catch {
|
|
1464
|
+
return []
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
/**
|
|
1469
|
+
* List all conversation sessions stored for a given working directory.
|
|
1470
|
+
* Reads ~/.claude/projects/<encoded-cwd>/ and returns metadata for each .jsonl file.
|
|
1471
|
+
*
|
|
1472
|
+
* @param {string} cwd - The working directory path to look up
|
|
1473
|
+
* @returns {Promise<Array<{ sessionId: string; filePath: string; messageCount: number }>>}
|
|
1474
|
+
*
|
|
1475
|
+
* @example
|
|
1476
|
+
* const sessions = await cc.listSessionsForCwd('/Users/me/my-project')
|
|
1477
|
+
* for (const s of sessions) {
|
|
1478
|
+
* console.log(s.sessionId, s.messageCount, 'messages')
|
|
1479
|
+
* }
|
|
1480
|
+
*/
|
|
1481
|
+
async listSessionsForCwd(cwd?: string): Promise<Array<{
|
|
1482
|
+
sessionId: string
|
|
1483
|
+
filePath: string
|
|
1484
|
+
messageCount: number
|
|
1485
|
+
}>> {
|
|
1486
|
+
const fs = this.container.feature('fs')
|
|
1487
|
+
const home = process.env.HOME ?? '/tmp'
|
|
1488
|
+
const resolvedCwd = cwd ?? this.options.cwd ?? (this.container as any).cwd
|
|
1489
|
+
const encodedCwd = resolvedCwd.replace(/\//g, '-').replace(/@/g, '-')
|
|
1490
|
+
const projectDir = `${home}/.claude/projects/${encodedCwd}`
|
|
1491
|
+
|
|
1492
|
+
let files: string[]
|
|
1493
|
+
try {
|
|
1494
|
+
files = await fs.readdir(projectDir)
|
|
1495
|
+
} catch {
|
|
1496
|
+
return []
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
const jsonlFiles = files.filter((f: string) => f.endsWith('.jsonl'))
|
|
1500
|
+
|
|
1501
|
+
const results = await Promise.all(jsonlFiles.map(async (file: string) => {
|
|
1502
|
+
const sessionId = file.replace(/\.jsonl$/, '')
|
|
1503
|
+
const filePath = `${projectDir}/${file}`
|
|
1504
|
+
try {
|
|
1505
|
+
const raw = await fs.readFile(filePath, 'utf8')
|
|
1506
|
+
const messageCount = raw.split('\n').filter((l: string) => l.trim()).length
|
|
1507
|
+
return { sessionId, filePath, messageCount }
|
|
1508
|
+
} catch {
|
|
1509
|
+
return { sessionId, filePath, messageCount: 0 }
|
|
1510
|
+
}
|
|
1511
|
+
}))
|
|
1512
|
+
|
|
1513
|
+
return results
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1352
1516
|
/**
|
|
1353
1517
|
* Clean up any temp MCP config files created during sessions.
|
|
1354
1518
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
|
|
3
3
|
import { type AvailableFeatures } from '@soederpop/luca/feature'
|
|
4
|
-
import { Feature } from '
|
|
5
|
-
import {
|
|
4
|
+
import { Feature } from '../feature.js'
|
|
5
|
+
import type { DiskCache } from '@soederpop/luca/node/container'
|
|
6
6
|
import type { Message } from './conversation'
|
|
7
7
|
|
|
8
8
|
declare module '@soederpop/luca/feature' {
|
|
@@ -95,10 +95,6 @@ export class ConversationHistory extends Feature<ConversationHistoryState, Conve
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
/** @returns The parent NodeContainer, narrowed from the base Container type. */
|
|
99
|
-
override get container() {
|
|
100
|
-
return super.container as NodeContainer<NodeFeatures, any>
|
|
101
|
-
}
|
|
102
98
|
|
|
103
99
|
/** @returns The diskCache feature instance used for persistence, configured with the optional cachePath. */
|
|
104
100
|
get diskCache(): DiskCache {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
|
|
3
3
|
import { type AvailableFeatures } from '@soederpop/luca/feature'
|
|
4
|
-
import { Feature } from '
|
|
4
|
+
import { Feature } from '../feature.js'
|
|
5
5
|
import type { OpenAIClient } from '../../clients/openai';
|
|
6
6
|
import type OpenAI from 'openai';
|
|
7
7
|
import type { ConversationHistory } from './conversation-history';
|
|
@@ -212,9 +212,12 @@ export class Conversation extends Feature<ConversationState, ConversationOptions
|
|
|
212
212
|
/** The active structured output schema for the current ask() call, if any. */
|
|
213
213
|
private _activeSchema: z.ZodType | null = null
|
|
214
214
|
|
|
215
|
-
/**
|
|
215
|
+
/** Registered stubs: matched against user input to short-circuit the API with a canned response. */
|
|
216
|
+
private _stubs: Array<{ matcher: string | RegExp; response: string | (() => string) }> = []
|
|
217
|
+
|
|
218
|
+
/** Resolved max tokens: per-call override > options-level. Undefined means no limit (model default). */
|
|
216
219
|
private get maxTokens(): number | undefined {
|
|
217
|
-
return (this.state.get('callMaxTokens') as number | null) ?? this.options.maxTokens ??
|
|
220
|
+
return (this.state.get('callMaxTokens') as number | null) ?? this.options.maxTokens ?? undefined
|
|
218
221
|
}
|
|
219
222
|
|
|
220
223
|
/** @returns Default state seeded from options: id, thread, model, initial history, and zero token usage. */
|
|
@@ -276,6 +279,22 @@ export class Conversation extends Feature<ConversationState, ConversationOptions
|
|
|
276
279
|
return this
|
|
277
280
|
}
|
|
278
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Register a hardcoded stub response that bypasses the API when the user's message matches.
|
|
284
|
+
* Streaming is still simulated — chunk/preview events fire word-by-word.
|
|
285
|
+
*
|
|
286
|
+
* @param matcher - Exact string match, substring, or RegExp tested against user input
|
|
287
|
+
* @param response - The text to stream back, or a zero-arg function that returns it
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* conversation.stub('hello', 'Hi there!')
|
|
291
|
+
* conversation.stub(/weather/i, () => 'Sunny and 72°F.')
|
|
292
|
+
*/
|
|
293
|
+
stub(matcher: string | RegExp, response: string | (() => string)): this {
|
|
294
|
+
this._stubs.push({ matcher, response })
|
|
295
|
+
return this
|
|
296
|
+
}
|
|
297
|
+
|
|
279
298
|
/** Returns configured remote MCP servers keyed by server label. */
|
|
280
299
|
get mcpServers(): Record<string, ConversationMCPServer> {
|
|
281
300
|
return (this.options.mcpServers || {}) as Record<string, ConversationMCPServer>
|
|
@@ -286,6 +305,30 @@ export class Conversation extends Feature<ConversationState, ConversationOptions
|
|
|
286
305
|
return this.state.get('messages') || []
|
|
287
306
|
}
|
|
288
307
|
|
|
308
|
+
/**
|
|
309
|
+
* Fork the conversation into a new independent instance.
|
|
310
|
+
* The fork inherits the same system prompt, tools, and full message history,
|
|
311
|
+
* but has its own identity and state — changes in either direction do not affect the other.
|
|
312
|
+
*
|
|
313
|
+
* @param overrides - Optional option overrides for the forked conversation (e.g. different model or title)
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```typescript
|
|
317
|
+
* const fork = conversation.fork()
|
|
318
|
+
* await fork.ask('What if we took a different approach?')
|
|
319
|
+
* // original conversation is unchanged
|
|
320
|
+
* ```
|
|
321
|
+
*/
|
|
322
|
+
fork(overrides: Partial<ConversationOptions> = {}): Conversation {
|
|
323
|
+
return this.container.feature('conversation', {
|
|
324
|
+
...this.options,
|
|
325
|
+
id: undefined,
|
|
326
|
+
history: JSON.parse(JSON.stringify(this.messages)),
|
|
327
|
+
tools: { ...this.tools },
|
|
328
|
+
...overrides,
|
|
329
|
+
})
|
|
330
|
+
}
|
|
331
|
+
|
|
289
332
|
/** Returns the OpenAI model name being used for completions. */
|
|
290
333
|
get model(): string {
|
|
291
334
|
return this.state.get('model')!
|
|
@@ -518,6 +561,11 @@ export class Conversation extends Feature<ConversationState, ConversationOptions
|
|
|
518
561
|
this.emit('userMessage', content)
|
|
519
562
|
|
|
520
563
|
try {
|
|
564
|
+
const stubText = this._matchStub(typeof content === 'string' ? content : '')
|
|
565
|
+
if (stubText !== null) {
|
|
566
|
+
return await this._streamStub(stubText)
|
|
567
|
+
}
|
|
568
|
+
|
|
521
569
|
let raw: string
|
|
522
570
|
|
|
523
571
|
if (this.apiMode === 'responses') {
|
|
@@ -764,6 +812,50 @@ export class Conversation extends Feature<ConversationState, ConversationOptions
|
|
|
764
812
|
}
|
|
765
813
|
}
|
|
766
814
|
|
|
815
|
+
/** Check registered stubs against user input. Returns the response text, or null if no match. */
|
|
816
|
+
private _matchStub(input: string): string | null {
|
|
817
|
+
for (const { matcher, response } of this._stubs) {
|
|
818
|
+
const matched = typeof matcher === 'string'
|
|
819
|
+
? input === matcher || input.includes(matcher)
|
|
820
|
+
: matcher.test(input)
|
|
821
|
+
if (matched) {
|
|
822
|
+
return typeof response === 'function' ? response() : response
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return null
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Simulate a streaming response for a hardcoded stub text.
|
|
830
|
+
* Emits chunk/preview events word-by-word, yielding between each to keep the event loop alive.
|
|
831
|
+
*/
|
|
832
|
+
private async _streamStub(text: string): Promise<string> {
|
|
833
|
+
this.state.set('streaming', true)
|
|
834
|
+
this.emit('turnStart', { turn: 1, isFollowUp: false })
|
|
835
|
+
|
|
836
|
+
let accumulated = ''
|
|
837
|
+
const chunks = text.match(/\S+\s*/g) ?? [text]
|
|
838
|
+
|
|
839
|
+
try {
|
|
840
|
+
for (const chunk of chunks) {
|
|
841
|
+
accumulated += chunk
|
|
842
|
+
this.emit('chunk', chunk)
|
|
843
|
+
this.emit('preview', accumulated)
|
|
844
|
+
await Promise.resolve()
|
|
845
|
+
}
|
|
846
|
+
} finally {
|
|
847
|
+
this.state.set('streaming', false)
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
const trimmed = text
|
|
851
|
+
this.pushMessage({ role: 'assistant', content: trimmed })
|
|
852
|
+
this.state.set('lastResponse', trimmed)
|
|
853
|
+
this.emit('turnEnd', { turn: 1, hasToolCalls: false })
|
|
854
|
+
this.emit('response', trimmed)
|
|
855
|
+
|
|
856
|
+
return trimmed
|
|
857
|
+
}
|
|
858
|
+
|
|
767
859
|
/**
|
|
768
860
|
* Runs the streaming Responses API loop. Handles local function calls by
|
|
769
861
|
* executing handlers and submitting `function_call_output` items until
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
|
|
3
|
-
import { type AvailableFeatures
|
|
3
|
+
import { type AvailableFeatures } from '@soederpop/luca/feature'
|
|
4
|
+
import { Feature } from '../feature.js'
|
|
4
5
|
import type { ContentDb } from '@/node.js'
|
|
5
6
|
import type Assistant from './assistant.js'
|
|
6
7
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
|
|
3
|
-
import { Feature } from '
|
|
3
|
+
import { Feature } from '../feature.js'
|
|
4
4
|
import type { FS } from '../../node/features/fs.js'
|
|
5
5
|
import type { Grep, GrepMatch } from '../../node/features/grep.js'
|
|
6
6
|
import type { Helper } from '../../helper.js'
|
|
@@ -38,7 +38,7 @@ export class FileTools extends Feature {
|
|
|
38
38
|
|
|
39
39
|
static { Feature.register(this, 'fileTools') }
|
|
40
40
|
|
|
41
|
-
static tools: Record<string, { schema: z.ZodType; description?: string }> = {
|
|
41
|
+
static override tools: Record<string, { schema: z.ZodType; description?: string }> = {
|
|
42
42
|
readFile: {
|
|
43
43
|
description: 'Read the contents of a file. Returns the text content. Use offset/limit to read portions of large files.',
|
|
44
44
|
schema: z.object({
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
|
|
3
|
-
import { Feature } from '
|
|
4
|
-
import type { AGIContainer } from '../container.server.js'
|
|
3
|
+
import { Feature } from '../feature.js'
|
|
5
4
|
import type { Assistant } from './assistant.js'
|
|
6
5
|
import type { ToolCallCtx } from '../lib/interceptor-chain.js'
|
|
7
6
|
|
|
@@ -214,9 +213,6 @@ export class LucaCoder extends Feature<LucaCoderState, LucaCoderOptions> {
|
|
|
214
213
|
}
|
|
215
214
|
}
|
|
216
215
|
|
|
217
|
-
override get container(): AGIContainer {
|
|
218
|
-
return super.container as AGIContainer
|
|
219
|
-
}
|
|
220
216
|
|
|
221
217
|
/** The inner assistant. Throws if not started. */
|
|
222
218
|
get assistant(): Assistant {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
|
|
4
4
|
import { type AvailableFeatures } from '@soederpop/luca/feature'
|
|
5
|
-
import { Feature } from '
|
|
5
|
+
import { Feature } from '../feature.js'
|
|
6
6
|
|
|
7
7
|
declare module '@soederpop/luca/feature' {
|
|
8
8
|
interface AvailableFeatures {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Feature } from '
|
|
1
|
+
import { Feature } from '../feature.js'
|
|
2
2
|
import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
|
|
3
3
|
import { z } from 'zod'
|
|
4
4
|
import { camelCase } from 'lodash-es'
|
|
@@ -95,7 +95,7 @@ export interface OpenAIToolDef {
|
|
|
95
95
|
* api.endpoint('getPetById')
|
|
96
96
|
*
|
|
97
97
|
* // Convert to OpenAI tool definitions
|
|
98
|
-
* api.
|
|
98
|
+
* api.toOpenAITools()
|
|
99
99
|
*
|
|
100
100
|
* // Convert a single endpoint to a function definition
|
|
101
101
|
* api.toFunction('getPetById')
|
|
@@ -205,7 +205,7 @@ export class OpenAPI extends Feature<OpenAPIState, OpenAPIOptions> {
|
|
|
205
205
|
* @param {Function} [filter] - Optional predicate to select which endpoints to include
|
|
206
206
|
* @returns {OpenAIToolDef[]} Array of tool definitions ready for the OpenAI tools parameter
|
|
207
207
|
*/
|
|
208
|
-
|
|
208
|
+
toOpenAITools(filter?: (ep: EndpointInfo) => boolean): OpenAIToolDef[] {
|
|
209
209
|
const eps = filter ? this.endpoints.filter(filter) : this.endpoints
|
|
210
210
|
return eps.map((ep) => ({
|
|
211
211
|
type: 'function' as const,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
|
|
3
|
-
import { type AvailableFeatures
|
|
3
|
+
import { type AvailableFeatures } from '@soederpop/luca/feature'
|
|
4
|
+
import { Feature } from '../feature.js'
|
|
4
5
|
import { parse } from 'contentbase'
|
|
5
6
|
import type { DocsReader } from './docs-reader.js'
|
|
6
|
-
import
|
|
7
|
+
import Assistant from './assistant.js'
|
|
7
8
|
|
|
8
9
|
declare module '@soederpop/luca/feature' {
|
|
9
10
|
interface AvailableFeatures {
|
|
@@ -72,7 +73,7 @@ export class SkillsLibrary extends Feature<SkillsLibraryState, SkillsLibraryOpti
|
|
|
72
73
|
static { Feature.register(this, 'skillsLibrary') }
|
|
73
74
|
|
|
74
75
|
/** Tools for assistant integration via assistant.use(skillsLibrary). */
|
|
75
|
-
static tools: Record<string, { schema: z.ZodType; handler?: Function }> = {
|
|
76
|
+
static override tools: Record<string, { schema: z.ZodType; handler?: Function }> = {
|
|
76
77
|
searchAvailableSkills: {
|
|
77
78
|
schema: z.object({
|
|
78
79
|
query: z.string().optional().describe('Optional search term to filter skills by name or description'),
|
|
@@ -102,9 +103,46 @@ export class SkillsLibrary extends Feature<SkillsLibraryState, SkillsLibraryOpti
|
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
override setupToolsConsumer(assistant:
|
|
106
|
-
|
|
107
|
-
|
|
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
|
+
const { container } = a
|
|
114
|
+
|
|
115
|
+
const skillsLibrary = this
|
|
116
|
+
|
|
117
|
+
const preloadSkills : string[] = []
|
|
118
|
+
if (a.meta.skills) {
|
|
119
|
+
if (Array.isArray(a.meta.skills)) {
|
|
120
|
+
preloadSkills.push(...a.meta.skills)
|
|
121
|
+
} else {
|
|
122
|
+
preloadSkills.push(a.meta.skills)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function beforeAskCheckIfWeNeedSkills(ctx: any, next: any) {
|
|
127
|
+
const { question } = ctx
|
|
128
|
+
const skills = await skillsLibrary.findRelevantSkillsForAssistant(a, question as string)
|
|
129
|
+
|
|
130
|
+
const allSkillsToLoad : string[] = container.utils.lodash.uniq([
|
|
131
|
+
...skills,
|
|
132
|
+
...preloadSkills,
|
|
133
|
+
])
|
|
134
|
+
|
|
135
|
+
if (allSkillsToLoad.length) {
|
|
136
|
+
ctx.question = `${ctx.question} \n\n## Required Skills\nYou will need to load the following skills to answer this question: ${skills.join(', ')}`
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
a.interceptors.beforeAsk.remove(beforeAskCheckIfWeNeedSkills)
|
|
140
|
+
|
|
141
|
+
await next()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
assistant.intercept('beforeAsk', beforeAskCheckIfWeNeedSkills as any)
|
|
145
|
+
|
|
108
146
|
return assistant
|
|
109
147
|
}
|
|
110
148
|
|
|
@@ -375,6 +413,49 @@ export class SkillsLibrary extends Feature<SkillsLibraryState, SkillsLibraryOpti
|
|
|
375
413
|
const answer = await reader.ask(question)
|
|
376
414
|
return answer
|
|
377
415
|
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Fork the given assistant and ask it which skills (if any) are relevant
|
|
419
|
+
* to the user's query. Returns an array of skill names that should be loaded
|
|
420
|
+
* before the real question is answered.
|
|
421
|
+
*
|
|
422
|
+
* The fork is ephemeral (historyMode: 'none') and uses structured output so
|
|
423
|
+
* the result is always a clean string array — never free text.
|
|
424
|
+
*
|
|
425
|
+
* @param assistant - The assistant instance to fork
|
|
426
|
+
* @param userQuery - The user's original question
|
|
427
|
+
* @returns Array of skill names relevant to the query (may be empty)
|
|
428
|
+
*/
|
|
429
|
+
async findRelevantSkillsForAssistant(assistant: Assistant, userQuery: string): Promise<string[]> {
|
|
430
|
+
if (!this.isStarted) await this.start()
|
|
431
|
+
|
|
432
|
+
const skills = this.list()
|
|
433
|
+
if (skills.length === 0) return []
|
|
434
|
+
|
|
435
|
+
const responseSchema = z.object({
|
|
436
|
+
skills: z.array(z.string()).describe('Names of skills relevant to the query. Empty array if none apply.'),
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
const skillsDescription = Object.entries(this.skillsTable)
|
|
440
|
+
.map(([title,description]) => `- **${title}**: ${description}`)
|
|
441
|
+
.join("\n")
|
|
442
|
+
|
|
443
|
+
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.
|
|
444
|
+
Available skills:
|
|
445
|
+
-------
|
|
446
|
+
${skillsDescription}
|
|
447
|
+
|
|
448
|
+
User query: ${userQuery}
|
|
449
|
+
|
|
450
|
+
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.`)
|
|
451
|
+
|
|
452
|
+
const fork = assistant.conversation.fork()
|
|
453
|
+
const result = await fork.ask(prompt, { schema: responseSchema }) as unknown as { skills: string[] }
|
|
454
|
+
|
|
455
|
+
console.log('Got a result', result)
|
|
456
|
+
|
|
457
|
+
return result.skills.filter(name => this.find(name) !== undefined)
|
|
458
|
+
}
|
|
378
459
|
}
|
|
379
460
|
|
|
380
461
|
export default SkillsLibrary
|
|
@@ -26,6 +26,16 @@ export class InterceptorChain<T> {
|
|
|
26
26
|
return this.fns.length
|
|
27
27
|
}
|
|
28
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
|
+
|
|
29
39
|
async run(ctx: T, final: () => Promise<void>): Promise<void> {
|
|
30
40
|
let index = 0
|
|
31
41
|
const fns = this.fns
|
|
@@ -39,7 +39,7 @@ export function getContextWindow(model: string): number {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
return best ? MODEL_CONTEXT_WINDOWS[best] : DEFAULT_CONTEXT_WINDOW
|
|
42
|
+
return best ? MODEL_CONTEXT_WINDOWS[best] ?? DEFAULT_CONTEXT_WINDOW : DEFAULT_CONTEXT_WINDOW
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/** Get a cached tiktoken encoder for a model (falls back to o200k_base). */
|