talon-agent 1.12.0 → 1.34.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/README.md +111 -52
- package/package.json +39 -12
- package/prompts/README.md +112 -0
- package/prompts/base.md +12 -12
- package/prompts/discord.md +10 -47
- package/prompts/heartbeat.md +23 -8
- package/prompts/identity.md +12 -22
- package/prompts/native.md +23 -0
- package/prompts/system/contract-text-or-tools.md +8 -0
- package/prompts/system/contract-text-preferred.md +5 -0
- package/prompts/system/contract-tool-only.md +10 -0
- package/prompts/system/cron.md +3 -0
- package/prompts/system/daily-memory.md +3 -0
- package/prompts/system/goals.md +7 -0
- package/prompts/system/heartbeat-agent.md +44 -0
- package/prompts/system/persistent-memory.md +8 -0
- package/prompts/system/skills.md +9 -0
- package/prompts/system/triggers.md +5 -0
- package/prompts/system/workspace.md +9 -0
- package/prompts/teams.md +4 -14
- package/prompts/telegram.md +17 -89
- package/src/app.ts +278 -0
- package/src/backend/claude-sdk/constants.ts +22 -10
- package/src/backend/claude-sdk/factory.ts +79 -22
- package/src/backend/claude-sdk/handler.ts +431 -160
- package/src/backend/claude-sdk/index.ts +6 -2
- package/src/backend/claude-sdk/mcp-ready.ts +54 -0
- package/src/backend/claude-sdk/model-drift.ts +65 -0
- package/src/backend/claude-sdk/model-provider.ts +12 -6
- package/src/backend/claude-sdk/models/convert.ts +169 -0
- package/src/backend/claude-sdk/models/discovery.ts +203 -0
- package/src/backend/claude-sdk/models/index.ts +18 -0
- package/src/backend/claude-sdk/models/parsing.ts +264 -0
- package/src/backend/claude-sdk/models/static.ts +45 -0
- package/src/backend/claude-sdk/one-shot.ts +19 -13
- package/src/backend/claude-sdk/options.ts +228 -104
- package/src/backend/claude-sdk/state.ts +1 -1
- package/src/backend/claude-sdk/stream.ts +153 -45
- package/src/backend/claude-sdk/warm.ts +12 -5
- package/src/backend/codex/auth.ts +216 -40
- package/src/backend/codex/constants.ts +63 -22
- package/src/backend/codex/discovery.ts +427 -0
- package/src/backend/codex/factory.ts +63 -33
- package/src/backend/codex/handler/events.ts +239 -0
- package/src/backend/codex/handler/index.ts +15 -0
- package/src/backend/codex/handler/message.ts +669 -0
- package/src/backend/codex/handler/state.ts +14 -0
- package/src/backend/codex/handler/usage.ts +68 -0
- package/src/backend/codex/index.ts +2 -2
- package/src/backend/codex/init.ts +92 -11
- package/src/backend/codex/mcp-config.ts +89 -62
- package/src/backend/codex/models.ts +305 -54
- package/src/backend/codex/oauth-incompat.ts +302 -0
- package/src/backend/codex/one-shot.ts +91 -8
- package/src/backend/codex/state.ts +54 -3
- package/src/backend/codex/token-usage.ts +434 -0
- package/src/backend/kilo/factory.ts +60 -35
- package/src/backend/kilo/handler/index.ts +13 -0
- package/src/backend/kilo/handler/message.ts +317 -0
- package/src/backend/kilo/handler/state.ts +14 -0
- package/src/backend/kilo/handler/turn.ts +321 -0
- package/src/backend/kilo/index.ts +5 -5
- package/src/backend/kilo/model-provider.ts +26 -168
- package/src/backend/kilo/models/index.ts +52 -0
- package/src/backend/kilo/one-shot.ts +20 -142
- package/src/backend/kilo/server.ts +14 -21
- package/src/backend/openai-agents/builtins.ts +215 -114
- package/src/backend/openai-agents/constants.ts +41 -15
- package/src/backend/openai-agents/discovery.ts +332 -0
- package/src/backend/openai-agents/factory.ts +66 -28
- package/src/backend/openai-agents/handler/events.ts +170 -0
- package/src/backend/openai-agents/handler/index.ts +13 -0
- package/src/backend/openai-agents/handler/message.ts +527 -0
- package/src/backend/openai-agents/handler/state.ts +13 -0
- package/src/backend/openai-agents/index.ts +22 -4
- package/src/backend/openai-agents/init.ts +51 -88
- package/src/backend/openai-agents/mcp-pool.ts +252 -0
- package/src/backend/openai-agents/models.ts +118 -11
- package/src/backend/openai-agents/session.ts +277 -0
- package/src/backend/openai-agents/state.ts +82 -11
- package/src/backend/opencode/factory.ts +54 -25
- package/src/backend/opencode/handler/index.ts +13 -0
- package/src/backend/opencode/handler/message.ts +325 -0
- package/src/backend/opencode/handler/state.ts +14 -0
- package/src/backend/opencode/handler/turn.ts +304 -0
- package/src/backend/opencode/index.ts +2 -2
- package/src/backend/opencode/model-provider.ts +26 -168
- package/src/backend/opencode/models/index.ts +51 -0
- package/src/backend/opencode/one-shot.ts +20 -137
- package/src/backend/opencode/server.ts +12 -11
- package/src/backend/remote-server/client.ts +13 -6
- package/src/backend/remote-server/events.ts +59 -9
- package/src/backend/remote-server/lifecycle.ts +3 -1
- package/src/backend/remote-server/mcp.ts +52 -19
- package/src/backend/remote-server/model-catalog/catalog.ts +270 -0
- package/src/backend/remote-server/model-catalog/index.ts +100 -0
- package/src/backend/remote-server/model-catalog/presentation.ts +298 -0
- package/src/backend/remote-server/model-catalog/provider.ts +193 -0
- package/src/backend/remote-server/model-catalog/resolve.ts +225 -0
- package/src/backend/remote-server/model-catalog/types.ts +136 -0
- package/src/backend/remote-server/one-shot.ts +225 -0
- package/src/backend/remote-server/session-helpers.ts +3 -4
- package/src/backend/remote-server/state.ts +1 -1
- package/src/backend/shared/delivery-contract.ts +161 -0
- package/src/backend/shared/delivery.ts +49 -0
- package/src/backend/shared/flow-violation.ts +38 -7
- package/src/backend/shared/frontends.ts +66 -0
- package/src/backend/shared/handle-retry.ts +244 -0
- package/src/backend/shared/handler-to-events.ts +186 -0
- package/src/backend/shared/handler-types.ts +57 -0
- package/src/backend/shared/index.ts +51 -5
- package/src/backend/shared/metrics.ts +199 -0
- package/src/backend/shared/model-retry.ts +1 -1
- package/src/backend/shared/prompt-format.ts +69 -0
- package/src/backend/shared/session-name.ts +1 -1
- package/src/backend/shared/stream-state.ts +36 -2
- package/src/backend/shared/system-prompt.ts +200 -23
- package/src/bootstrap.ts +338 -38
- package/src/cli/chat.ts +49 -0
- package/src/cli/config-view.ts +100 -0
- package/src/cli/config.ts +105 -0
- package/src/cli/context.ts +23 -0
- package/src/cli/daemon.ts +95 -0
- package/src/cli/doctor.ts +53 -0
- package/src/cli/index.ts +139 -0
- package/src/cli/logs.ts +60 -0
- package/src/cli/menu.ts +104 -0
- package/src/cli/setup.ts +455 -0
- package/src/cli/status.ts +75 -0
- package/src/cli.ts +20 -1211
- package/src/core/agent-runtime/README.md +173 -0
- package/src/{backend/registry.ts → core/agent-runtime/backend-registry.ts} +15 -6
- package/src/core/agent-runtime/capabilities.ts +330 -0
- package/src/core/agent-runtime/contract-tests.ts +378 -0
- package/src/core/agent-runtime/events.ts +291 -0
- package/src/core/agent-runtime/index.ts +62 -0
- package/src/core/agent-runtime/model-ref.ts +130 -0
- package/src/core/background/cron.ts +469 -0
- package/src/core/{dream.ts → background/dream.ts} +118 -53
- package/src/core/background/heartbeat/agent.ts +333 -0
- package/src/core/background/heartbeat/index.ts +25 -0
- package/src/core/background/heartbeat/scheduler.ts +211 -0
- package/src/core/background/heartbeat/state.ts +153 -0
- package/src/core/background/index.ts +21 -0
- package/src/core/background/isolated-agent.ts +115 -0
- package/src/core/background/job-health.ts +140 -0
- package/src/core/background/job-oneshot.ts +165 -0
- package/src/core/background/job-prompt.ts +54 -0
- package/src/core/{pulse.ts → background/pulse.ts} +6 -5
- package/src/core/background/triggers/command.ts +60 -0
- package/src/core/background/triggers/exit.ts +202 -0
- package/src/core/background/triggers/index.ts +38 -0
- package/src/core/background/triggers/output.ts +142 -0
- package/src/core/background/triggers/resume.ts +127 -0
- package/src/core/background/triggers/spawn.ts +282 -0
- package/src/core/background/triggers/state.ts +52 -0
- package/src/core/constants.ts +57 -28
- package/src/core/daemon/control.ts +229 -0
- package/src/core/daemon/discovery.ts +169 -0
- package/src/core/daemon/pidfile.ts +98 -0
- package/src/core/doctor.ts +459 -0
- package/src/core/engine/backend-controller/holders.ts +17 -0
- package/src/core/engine/backend-controller/index.ts +71 -0
- package/src/core/engine/backend-controller/legacy.ts +111 -0
- package/src/core/engine/backend-controller/pool.ts +296 -0
- package/src/core/engine/backend-controller/rebind.ts +205 -0
- package/src/core/engine/backend-controller/state.ts +150 -0
- package/src/core/engine/backend-controller/types.ts +51 -0
- package/src/core/engine/dispatcher.ts +68 -0
- package/src/core/engine/gateway-actions/cron.ts +518 -0
- package/src/core/engine/gateway-actions/fetch-url.ts +120 -0
- package/src/core/engine/gateway-actions/goals.ts +178 -0
- package/src/core/engine/gateway-actions/history.ts +53 -0
- package/src/core/engine/gateway-actions/index.ts +60 -0
- package/src/core/engine/gateway-actions/models.ts +120 -0
- package/src/core/engine/gateway-actions/plugins.ts +83 -0
- package/src/core/engine/gateway-actions/scripts.ts +127 -0
- package/src/core/engine/gateway-actions/shared.ts +66 -0
- package/src/core/engine/gateway-actions/skills.ts +120 -0
- package/src/core/engine/gateway-actions/triggers.ts +237 -0
- package/src/core/engine/gateway-actions/types.ts +18 -0
- package/src/core/{gateway.ts → engine/gateway.ts} +223 -60
- package/src/core/engine/index.ts +20 -0
- package/src/core/engine/model-audit.ts +121 -0
- package/src/core/errors.ts +68 -6
- package/src/core/mcp-hub/children.ts +254 -0
- package/src/core/mcp-hub/index.ts +335 -0
- package/src/core/mcp-hub/proxy-server.ts +45 -0
- package/src/core/mcp-hub/talon-server.ts +71 -0
- package/src/core/memory/retrieval.ts +90 -0
- package/src/core/models/active-model.ts +441 -0
- package/src/core/{models.ts → models/catalog.ts} +6 -0
- package/src/core/models/index.ts +17 -0
- package/src/core/models/reasoning-levels.ts +57 -0
- package/src/core/plugin/actions.ts +32 -0
- package/src/core/plugin/builtins.ts +162 -0
- package/src/core/plugin/index.ts +37 -0
- package/src/core/plugin/loader.ts +289 -0
- package/src/core/plugin/mcp.ts +86 -0
- package/src/core/plugin/registry.ts +124 -0
- package/src/core/plugin/types.ts +126 -0
- package/src/core/prompt/assemble.ts +273 -0
- package/src/core/prompt/disk-prompts.ts +41 -0
- package/src/core/prompt/embed.ts +107 -0
- package/src/core/prompt/embedded-prompts.ts +85 -0
- package/src/core/prompt/index.ts +30 -0
- package/src/core/prompt/templates.ts +124 -0
- package/src/core/prompt/workspace-listing.ts +91 -0
- package/src/core/scripting/lua-runner.ts +210 -0
- package/src/core/scripts/runner.ts +125 -0
- package/src/core/soul/README.md +110 -0
- package/src/core/soul/RESEARCH.md +98 -0
- package/src/core/soul/associative.ts +98 -0
- package/src/core/soul/centrality.ts +98 -0
- package/src/core/soul/cluster.ts +83 -0
- package/src/core/soul/compiler.ts +207 -0
- package/src/core/soul/consolidate.ts +179 -0
- package/src/core/soul/critic.ts +159 -0
- package/src/core/soul/dag.ts +265 -0
- package/src/core/soul/delta.ts +123 -0
- package/src/core/soul/drift.ts +99 -0
- package/src/core/soul/embedder.ts +129 -0
- package/src/core/soul/emergent-critic.ts +96 -0
- package/src/core/soul/forgetting.ts +131 -0
- package/src/core/soul/governance.ts +93 -0
- package/src/core/soul/hash.ts +97 -0
- package/src/core/soul/hdc.ts +154 -0
- package/src/core/soul/index.ts +140 -0
- package/src/core/soul/kernel.ts +540 -0
- package/src/core/soul/lattice.ts +103 -0
- package/src/core/soul/lens.ts +110 -0
- package/src/core/soul/projector.ts +240 -0
- package/src/core/soul/reflect.ts +170 -0
- package/src/core/soul/reflex.ts +164 -0
- package/src/core/soul/retrieve.ts +146 -0
- package/src/core/soul/salience.ts +142 -0
- package/src/core/soul/service.ts +204 -0
- package/src/core/soul/settings.ts +47 -0
- package/src/core/soul/signals.ts +117 -0
- package/src/core/soul/talon-embedder.ts +80 -0
- package/src/core/soul/taps.ts +199 -0
- package/src/core/soul/types.ts +298 -0
- package/src/core/soul/valence.ts +83 -0
- package/src/core/tools/goals.ts +117 -0
- package/src/core/tools/index.ts +10 -0
- package/src/core/tools/mcp-env.ts +94 -0
- package/src/core/tools/mcp-server.ts +27 -4
- package/src/core/tools/messaging.ts +122 -29
- package/src/core/tools/models.ts +39 -0
- package/src/core/tools/scheduling.ts +133 -14
- package/src/core/tools/schemas.ts +16 -4
- package/src/core/tools/scripts.ts +78 -0
- package/src/core/tools/skills.ts +90 -0
- package/src/core/tools/stickers.ts +1 -1
- package/src/core/tools/triggers.ts +19 -3
- package/src/core/tools/types.ts +6 -6
- package/src/core/types.ts +62 -112
- package/src/core/update/self-update.ts +269 -0
- package/src/core/weaver/index.ts +10 -0
- package/src/core/weaver/loom.ts +125 -0
- package/src/core/weaver/thread-session.ts +51 -0
- package/src/core/weaver/thread.ts +196 -0
- package/src/core/weaver/weaver.ts +357 -0
- package/src/frontend/discord/actions/chat-info.ts +268 -0
- package/src/frontend/discord/actions/index.ts +77 -0
- package/src/frontend/discord/actions/media.ts +190 -0
- package/src/frontend/discord/actions/messaging.ts +277 -0
- package/src/frontend/discord/actions/shared.ts +100 -0
- package/src/frontend/discord/actions/types.ts +33 -0
- package/src/frontend/discord/admin.ts +13 -23
- package/src/frontend/discord/callbacks/autocomplete.ts +55 -0
- package/src/frontend/discord/{callbacks.ts → callbacks/components.ts} +150 -190
- package/src/frontend/discord/callbacks/index.ts +14 -0
- package/src/frontend/discord/callbacks/modals.ts +76 -0
- package/src/frontend/discord/callbacks/shared.ts +22 -0
- package/src/frontend/discord/commands/admin.ts +88 -0
- package/src/frontend/discord/commands/definitions.ts +200 -0
- package/src/frontend/discord/commands/index.ts +19 -0
- package/src/frontend/discord/commands/info.ts +106 -0
- package/src/frontend/discord/commands/router.ts +149 -0
- package/src/frontend/discord/commands/session.ts +89 -0
- package/src/frontend/discord/commands/settings.ts +405 -0
- package/src/frontend/discord/commands/shared.ts +50 -0
- package/src/frontend/discord/formatting.ts +8 -34
- package/src/frontend/discord/handlers/access.ts +248 -0
- package/src/frontend/discord/handlers/context.ts +161 -0
- package/src/frontend/discord/handlers/delivery.ts +94 -0
- package/src/frontend/discord/handlers/index.ts +34 -0
- package/src/frontend/discord/handlers/messages.ts +120 -0
- package/src/frontend/discord/handlers/queue.ts +135 -0
- package/src/frontend/discord/handlers/registry.ts +29 -0
- package/src/frontend/discord/handlers/state.ts +94 -0
- package/src/frontend/discord/helpers.ts +46 -57
- package/src/frontend/discord/index.ts +10 -7
- package/src/frontend/discord/middleware.ts +2 -2
- package/src/frontend/native/actions.ts +133 -0
- package/src/frontend/native/chats.ts +146 -0
- package/src/frontend/native/discovery.ts +50 -0
- package/src/frontend/native/index.ts +1227 -0
- package/src/frontend/native/logs.ts +95 -0
- package/src/frontend/native/protocol.ts +253 -0
- package/src/frontend/native/server.ts +612 -0
- package/src/frontend/native/settings.ts +205 -0
- package/src/frontend/native/turn-meta.ts +43 -0
- package/src/frontend/shared/format.ts +51 -0
- package/src/frontend/shared/reasoning-levels.ts +46 -0
- package/src/frontend/shared/session-status.ts +189 -0
- package/src/frontend/shared/status-context.ts +125 -0
- package/src/frontend/teams/actions.ts +7 -5
- package/src/frontend/teams/formatting.ts +7 -18
- package/src/frontend/teams/graph.ts +2 -1
- package/src/frontend/teams/index.ts +76 -54
- package/src/frontend/telegram/actions/chat-info.ts +323 -0
- package/src/frontend/telegram/actions/index.ts +68 -0
- package/src/frontend/telegram/actions/media.ts +224 -0
- package/src/frontend/telegram/actions/messaging.ts +295 -0
- package/src/frontend/telegram/actions/shared.ts +58 -0
- package/src/frontend/telegram/actions/types.ts +31 -0
- package/src/frontend/telegram/admin.ts +12 -22
- package/src/frontend/telegram/callbacks/effort.ts +68 -0
- package/src/frontend/telegram/callbacks/index.ts +68 -0
- package/src/frontend/telegram/callbacks/model.ts +311 -0
- package/src/frontend/telegram/callbacks/pulse.ts +50 -0
- package/src/frontend/telegram/callbacks/settings.ts +177 -0
- package/src/frontend/telegram/callbacks/shared.ts +71 -0
- package/src/frontend/telegram/commands/admin.ts +226 -0
- package/src/frontend/telegram/commands/definitions.ts +60 -0
- package/src/frontend/telegram/commands/index.ts +39 -0
- package/src/frontend/telegram/commands/info.ts +125 -0
- package/src/frontend/telegram/commands/session.ts +77 -0
- package/src/frontend/telegram/commands/settings.ts +345 -0
- package/src/frontend/telegram/commands/state.ts +34 -0
- package/src/frontend/telegram/formatting.ts +14 -25
- package/src/frontend/telegram/handlers/access.ts +287 -0
- package/src/frontend/telegram/handlers/context.ts +210 -0
- package/src/frontend/telegram/handlers/delivery.ts +236 -0
- package/src/frontend/telegram/handlers/index.ts +39 -0
- package/src/frontend/telegram/handlers/messages.ts +477 -0
- package/src/frontend/telegram/handlers/queue.ts +241 -0
- package/src/frontend/telegram/handlers/state.ts +75 -0
- package/src/frontend/telegram/helpers/diagnostics.ts +188 -0
- package/src/frontend/telegram/helpers/format.ts +41 -0
- package/src/frontend/telegram/helpers/index.ts +13 -0
- package/src/frontend/telegram/{helpers.ts → helpers/menu.ts} +209 -210
- package/src/frontend/telegram/index.ts +24 -27
- package/src/frontend/telegram/middleware.ts +16 -3
- package/src/frontend/telegram/model-callbacks.ts +14 -0
- package/src/frontend/telegram/model-menu.ts +317 -0
- package/src/frontend/telegram/sticker-library.ts +178 -0
- package/src/frontend/telegram/userbot.ts +2 -44
- package/src/frontend/terminal/commands.ts +47 -41
- package/src/frontend/terminal/index.ts +46 -31
- package/src/frontend/terminal/input.ts +5 -0
- package/src/frontend/terminal/renderer.ts +4 -2
- package/src/index.ts +13 -171
- package/src/login.ts +3 -2
- package/src/native/blake3-wasm-bytes.ts +7 -0
- package/src/native/blake3.ts +234 -0
- package/src/native/htmlents-wasm-bytes.ts +7 -0
- package/src/native/htmlents.ts +56 -0
- package/src/native/prelude.d.mts +168 -0
- package/src/native/prelude.mjs +1561 -0
- package/src/native/registry.ts +142 -0
- package/src/native/runtime.ts +164 -0
- package/src/native/scheduler-core/gleam.d.mts +2 -0
- package/src/native/scheduler-core/gleam.mjs +1 -0
- package/src/native/scheduler-core/scheduler_core.d.mts +76 -0
- package/src/native/scheduler-core/scheduler_core.mjs +252 -0
- package/src/native/scheduler-core.ts +124 -0
- package/src/native/sqlguard-wasm-bytes.ts +7 -0
- package/src/native/sqlguard.ts +107 -0
- package/src/native/strsim-wasm-bytes.ts +7 -0
- package/src/native/strsim.ts +98 -0
- package/src/native/textops-wasm-bytes.ts +7 -0
- package/src/native/textops.ts +83 -0
- package/src/native/warden.ts +287 -0
- package/src/plugins/github/index.ts +1 -1
- package/src/plugins/mempalace/index.ts +1 -1
- package/src/plugins/playwright/index.ts +1 -1
- package/src/storage/chat-settings.ts +335 -85
- package/src/storage/cron-store.ts +266 -109
- package/src/storage/daily-log.ts +15 -3
- package/src/storage/db.ts +206 -0
- package/src/storage/goal-store.ts +186 -0
- package/src/storage/history.ts +137 -188
- package/src/storage/kv.ts +52 -0
- package/src/storage/legacy-import.ts +47 -0
- package/src/storage/media-index.ts +118 -61
- package/src/storage/repositories/chat-settings-repo.ts +60 -0
- package/src/storage/repositories/cron-repo.ts +128 -0
- package/src/storage/repositories/goals-repo.ts +122 -0
- package/src/storage/repositories/history-repo.ts +207 -0
- package/src/storage/repositories/kv-repo.ts +25 -0
- package/src/storage/repositories/media-index-repo.ts +147 -0
- package/src/storage/repositories/scripts-repo.ts +81 -0
- package/src/storage/repositories/sessions-repo.ts +129 -0
- package/src/storage/repositories/triggers-repo.ts +158 -0
- package/src/storage/repositories/turn-meta-repo.ts +32 -0
- package/src/storage/scheduled-store.ts +84 -0
- package/src/storage/script-store.ts +198 -0
- package/src/storage/sessions.ts +227 -102
- package/src/storage/skill-store.ts +325 -0
- package/src/storage/sql/README.md +18 -0
- package/src/storage/sql/chat-settings.sql +11 -0
- package/src/storage/sql/cron.sql +40 -0
- package/src/storage/sql/db.sql +11 -0
- package/src/storage/sql/embed.ts +118 -0
- package/src/storage/sql/goals.sql +41 -0
- package/src/storage/sql/history.sql +83 -0
- package/src/storage/sql/kv.sql +11 -0
- package/src/storage/sql/media-index.sql +45 -0
- package/src/storage/sql/schema.sql +233 -0
- package/src/storage/sql/scripts.sql +28 -0
- package/src/storage/sql/sessions.sql +22 -0
- package/src/storage/sql/statements.generated.ts +500 -0
- package/src/storage/sql/triggers.sql +69 -0
- package/src/storage/sql/turn-meta.sql +24 -0
- package/src/storage/sticker-store.ts +141 -0
- package/src/storage/trigger-store.ts +104 -126
- package/src/storage/turn-meta.ts +118 -0
- package/src/util/chat-id.ts +30 -0
- package/src/util/config.ts +313 -195
- package/src/util/fs-path.ts +32 -0
- package/src/util/log.ts +53 -31
- package/src/util/mcp-launcher.ts +351 -33
- package/src/util/paths.ts +45 -17
- package/src/util/tail-file.ts +24 -0
- package/src/util/workspace.ts +161 -10
- package/tsconfig.json +1 -0
- package/src/backend/claude-sdk/models.ts +0 -501
- package/src/backend/codex/handler.ts +0 -595
- package/src/backend/kilo/handler.ts +0 -707
- package/src/backend/kilo/models.ts +0 -762
- package/src/backend/openai-agents/handler.ts +0 -539
- package/src/backend/openai-agents/mcp.ts +0 -139
- package/src/backend/opencode/handler.ts +0 -663
- package/src/backend/opencode/models.ts +0 -741
- package/src/core/cron.ts +0 -184
- package/src/core/dispatcher.ts +0 -144
- package/src/core/gateway-actions.ts +0 -603
- package/src/core/heartbeat.ts +0 -575
- package/src/core/plugin.ts +0 -776
- package/src/core/triggers.ts +0 -640
- package/src/frontend/discord/actions.ts +0 -729
- package/src/frontend/discord/commands.ts +0 -1036
- package/src/frontend/discord/handlers.ts +0 -798
- package/src/frontend/telegram/actions.ts +0 -731
- package/src/frontend/telegram/callbacks.ts +0 -419
- package/src/frontend/telegram/commands.ts +0 -642
- package/src/frontend/telegram/handlers.ts +0 -1352
- package/src/util/mcp-launcher.mjs +0 -140
|
@@ -7,8 +7,12 @@
|
|
|
7
7
|
|
|
8
8
|
export { initAgent, updateSystemPrompt } from "./state.js";
|
|
9
9
|
export { warmSession } from "./warm.js";
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
10
|
+
export { getActiveQuery } from "./handler.js";
|
|
11
|
+
export {
|
|
12
|
+
buildMcpServers,
|
|
13
|
+
buildPluginMcpServers,
|
|
14
|
+
getActiveFrontends,
|
|
15
|
+
} from "./options.js";
|
|
12
16
|
export { getBridgePort } from "./state.js";
|
|
13
17
|
export {
|
|
14
18
|
runOneShotAgent,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper to bridge the gap between MCP server *registration* and *connection*.
|
|
3
|
+
*
|
|
4
|
+
* `Query.setMcpServers` resolves as soon as servers are registered — MCP
|
|
5
|
+
* startup is non-blocking by design. A stdio server that dials a slow remote
|
|
6
|
+
* endpoint (e.g. the playwright plugin connecting to the Camoufox websocket)
|
|
7
|
+
* finishes its `initialize` handshake seconds later. During that window the
|
|
8
|
+
* server is reported as `pending` and its tools are absent from the live
|
|
9
|
+
* registry, so a turn that proceeds immediately after `refreshTools` sees
|
|
10
|
+
* `mcp__playwright-tools__*` stuck "connecting" until the next refresh.
|
|
11
|
+
*
|
|
12
|
+
* `waitForMcpServersReady` polls `mcpServerStatus()` until the named servers
|
|
13
|
+
* leave the transient `pending` state (or a bounded timeout elapses), so the
|
|
14
|
+
* caller can return only once the freshly-added tools are actually usable.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { Query } from "@anthropic-ai/claude-agent-sdk";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolve once every server in `names` has left the `pending` state, or after
|
|
21
|
+
* `timeoutMs`. Best-effort and non-throwing: any error from `mcpServerStatus()`
|
|
22
|
+
* ends the wait so the turn is never blocked. Servers that settle into a
|
|
23
|
+
* terminal state (`connected`/`failed`/`needs-auth`/`disabled`) also resolve
|
|
24
|
+
* the wait — only the transient `pending` window is blocked on.
|
|
25
|
+
*/
|
|
26
|
+
export async function waitForMcpServersReady(
|
|
27
|
+
qi: Pick<Query, "mcpServerStatus">,
|
|
28
|
+
names: string[],
|
|
29
|
+
timeoutMs = 60_000,
|
|
30
|
+
pollMs = 250,
|
|
31
|
+
): Promise<void> {
|
|
32
|
+
if (names.length === 0) return;
|
|
33
|
+
const pending = new Set(names);
|
|
34
|
+
const deadline = Date.now() + timeoutMs;
|
|
35
|
+
while (Date.now() < deadline) {
|
|
36
|
+
let statuses;
|
|
37
|
+
try {
|
|
38
|
+
statuses = await qi.mcpServerStatus();
|
|
39
|
+
} catch {
|
|
40
|
+
return; // status query unsupported/failed — don't block the turn
|
|
41
|
+
}
|
|
42
|
+
// Best-effort: a backend (or stub) may report status in an unexpected
|
|
43
|
+
// shape (undefined / non-array). Treat anything non-iterable as
|
|
44
|
+
// "unsupported" and return rather than throwing into the turn.
|
|
45
|
+
if (!Array.isArray(statuses)) return;
|
|
46
|
+
for (const s of statuses) {
|
|
47
|
+
if (pending.has(s.name) && s.status !== "pending") {
|
|
48
|
+
pending.delete(s.name);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (pending.size === 0) return;
|
|
52
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn-time model-drift detection.
|
|
3
|
+
*
|
|
4
|
+
* The SDK result's `modelUsage` is keyed by the model that ACTUALLY
|
|
5
|
+
* served the turn. Nothing compared it to the model we asked for — so
|
|
6
|
+
* when the API substitutes (requested model withdrawn from the
|
|
7
|
+
* catalog, rate-limit downshift, silent server-side fallback), the
|
|
8
|
+
* only evidence was an INFO accounting line nobody reads. This module
|
|
9
|
+
* turns that into a WARN with the fix in it, once per
|
|
10
|
+
* (requested → actual) pair per process.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { logWarn } from "../../util/log.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Alias-tolerant model identity: "opus" is the same model as
|
|
17
|
+
* "claude-opus-4-8", "sonnet[1m]" the same as "sonnet". Substring
|
|
18
|
+
* containment after normalisation is deliberately loose — a false
|
|
19
|
+
* "same" (missed warning) is better than crying wolf on every alias
|
|
20
|
+
* expansion.
|
|
21
|
+
*/
|
|
22
|
+
export function isSameModel(requested: string, actual: string): boolean {
|
|
23
|
+
const norm = (s: string) =>
|
|
24
|
+
s
|
|
25
|
+
.toLowerCase()
|
|
26
|
+
.replace(/\[1m\]/g, "")
|
|
27
|
+
.trim();
|
|
28
|
+
const r = norm(requested);
|
|
29
|
+
const a = norm(actual);
|
|
30
|
+
if (!r || !a) return true; // nothing to compare — don't warn
|
|
31
|
+
return a === r || a.includes(r) || r.includes(a);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** (requested→actual) pairs already warned about this process. */
|
|
35
|
+
const warned = new Set<string>();
|
|
36
|
+
|
|
37
|
+
/** Test seam. */
|
|
38
|
+
export function resetModelDriftWarnings(): void {
|
|
39
|
+
warned.clear();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Compare the requested model against the models that actually served
|
|
44
|
+
* the turn (the `modelUsage` keys). Warns once per distinct pair.
|
|
45
|
+
* "default" requests are skipped — no pin, nothing to drift from.
|
|
46
|
+
*/
|
|
47
|
+
export function checkModelDrift(
|
|
48
|
+
requested: string,
|
|
49
|
+
actualModels: readonly string[],
|
|
50
|
+
): void {
|
|
51
|
+
if (!requested || requested === "default") return;
|
|
52
|
+
if (actualModels.length === 0) return;
|
|
53
|
+
if (actualModels.some((actual) => isSameModel(requested, actual))) return;
|
|
54
|
+
|
|
55
|
+
const actual = actualModels.join(", ");
|
|
56
|
+
const key = `${requested}→${actual}`;
|
|
57
|
+
if (warned.has(key)) return;
|
|
58
|
+
warned.add(key);
|
|
59
|
+
logWarn(
|
|
60
|
+
"agent",
|
|
61
|
+
`[MODEL DRIFT] requested "${requested}" but the API served "${actual}" — ` +
|
|
62
|
+
`the pinned model may no longer exist or was substituted upstream. ` +
|
|
63
|
+
`Check "model" in config.json (or this chat's /model override).`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Model provider methods for the Claude SDK backend.
|
|
3
3
|
*
|
|
4
|
-
* Implements the optional model methods from
|
|
4
|
+
* Implements the optional model methods from Backend by delegating
|
|
5
5
|
* to the core model registry. The Claude SDK exposes a single provider
|
|
6
6
|
* ("anthropic") with models discovered from the SDK at startup.
|
|
7
7
|
*/
|
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
getModels,
|
|
12
12
|
resolveModel as coreResolveModel,
|
|
13
13
|
resolveModelId,
|
|
14
|
-
} from "../../core/models.js";
|
|
15
|
-
import type { ModelInfo } from "../../core/models.js";
|
|
14
|
+
} from "../../core/models/catalog.js";
|
|
15
|
+
import type { ModelInfo } from "../../core/models/catalog.js";
|
|
16
16
|
import type {
|
|
17
17
|
UnifiedModelInfo,
|
|
18
18
|
UnifiedModelResolution,
|
|
@@ -34,10 +34,16 @@ function toUnified(model: ModelInfo): UnifiedModelInfo {
|
|
|
34
34
|
provider: PROVIDER_ID,
|
|
35
35
|
providerName: PROVIDER_NAME,
|
|
36
36
|
selectable: true,
|
|
37
|
+
supportedReasoningLevels: model.supportedReasoningLevels,
|
|
38
|
+
defaultReasoningLevel: model.defaultReasoningLevel,
|
|
37
39
|
};
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* De-duplicate models by displayName. Base and 1M variants carry distinct
|
|
44
|
+
* labels ("Sonnet 4.6" vs "Sonnet 4.6 (1M context)"), so both survive here;
|
|
45
|
+
* this only guards against accidental collisions.
|
|
46
|
+
*/
|
|
41
47
|
function getUniqueModels(): ModelInfo[] {
|
|
42
48
|
const options: ModelInfo[] = [];
|
|
43
49
|
const seenKeys = new Set<string>();
|
|
@@ -156,7 +162,7 @@ export async function getProviders(): Promise<UnifiedProviderInfo[]> {
|
|
|
156
162
|
|
|
157
163
|
export async function getProviderModels(
|
|
158
164
|
providerId: string,
|
|
159
|
-
page =
|
|
165
|
+
page = 1,
|
|
160
166
|
pageSize = 20,
|
|
161
167
|
): Promise<{ models: UnifiedModelInfo[]; total: number }> {
|
|
162
168
|
if (providerId !== PROVIDER_ID) {
|
|
@@ -164,7 +170,7 @@ export async function getProviderModels(
|
|
|
164
170
|
}
|
|
165
171
|
|
|
166
172
|
const all = getModels(PROVIDER_ID).map(toUnified);
|
|
167
|
-
const start = page * pageSize;
|
|
173
|
+
const start = (page - 1) * pageSize;
|
|
168
174
|
return {
|
|
169
175
|
models: all.slice(start, start + pageSize),
|
|
170
176
|
total: all.length,
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK → registry conversion. Groups SDK models into variant buckets, picks a
|
|
3
|
+
* canonical per bucket, derives display names + aliases + the fallback chain,
|
|
4
|
+
* and emits the registry `ModelInfo[]`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ModelInfo } from "../../../core/models/catalog.js";
|
|
8
|
+
import { normalizeReasoningLevels } from "../../../core/models/reasoning-levels.js";
|
|
9
|
+
import {
|
|
10
|
+
buildSdkModelRecords,
|
|
11
|
+
buildGeneratedAliases,
|
|
12
|
+
deriveDisplayName,
|
|
13
|
+
getPreferredModelPriority,
|
|
14
|
+
mergeAliases,
|
|
15
|
+
type AliasFormOptions,
|
|
16
|
+
type SdkModelInfo,
|
|
17
|
+
type SdkModelRecord,
|
|
18
|
+
} from "./parsing.js";
|
|
19
|
+
|
|
20
|
+
function extractSdkReasoningLevels(model: SdkModelInfo) {
|
|
21
|
+
if (model.supportsEffort === false) return [];
|
|
22
|
+
|
|
23
|
+
const sdkLevels = normalizeReasoningLevels(model.supportedEffortLevels);
|
|
24
|
+
if (sdkLevels.length > 0) return sdkLevels;
|
|
25
|
+
|
|
26
|
+
const effort = model.capabilities?.effort;
|
|
27
|
+
if (!effort?.supported) return [];
|
|
28
|
+
|
|
29
|
+
return normalizeReasoningLevels(
|
|
30
|
+
(["low", "medium", "high", "max", "xhigh"] as const).filter(
|
|
31
|
+
(level) => effort[level]?.supported === true,
|
|
32
|
+
),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Group records into variant buckets and pick the canonical record for each.
|
|
38
|
+
* Records without a parseable identity become their own singleton bucket so
|
|
39
|
+
* they still surface (keyed by value to stay unique).
|
|
40
|
+
*/
|
|
41
|
+
function groupVariants(
|
|
42
|
+
records: readonly SdkModelRecord[],
|
|
43
|
+
): Map<string, SdkModelRecord[]> {
|
|
44
|
+
const groups = new Map<string, SdkModelRecord[]>();
|
|
45
|
+
for (const record of records) {
|
|
46
|
+
const key = record.variantKey ?? `raw:${record.value}`;
|
|
47
|
+
const bucket = groups.get(key) ?? [];
|
|
48
|
+
bucket.push(record);
|
|
49
|
+
groups.set(key, bucket);
|
|
50
|
+
}
|
|
51
|
+
return groups;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function pickCanonical(bucket: readonly SdkModelRecord[]): SdkModelRecord {
|
|
55
|
+
return [...bucket].sort((left, right) => {
|
|
56
|
+
const priorityDelta =
|
|
57
|
+
getPreferredModelPriority(left) - getPreferredModelPriority(right);
|
|
58
|
+
if (priorityDelta !== 0) return priorityDelta;
|
|
59
|
+
return left.index - right.index;
|
|
60
|
+
})[0]!;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Assign a best-effort fallback (used on overload/timeout) to every model
|
|
65
|
+
* except the last:
|
|
66
|
+
* - A 1M variant prefers its base sibling of the same family+version.
|
|
67
|
+
* - Otherwise a model falls back to the next model in SDK order.
|
|
68
|
+
*/
|
|
69
|
+
function assignFallbacks(
|
|
70
|
+
models: ModelInfo[],
|
|
71
|
+
recordByValue: ReadonlyMap<string, SdkModelRecord>,
|
|
72
|
+
): void {
|
|
73
|
+
const baseByFamily = new Map<string, string>();
|
|
74
|
+
for (const model of models) {
|
|
75
|
+
const rec = recordByValue.get(model.id);
|
|
76
|
+
if (rec && !rec.identity.isOneMillion && rec.familyKey) {
|
|
77
|
+
baseByFamily.set(rec.familyKey, model.id);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
models.forEach((model, index) => {
|
|
82
|
+
const rec = recordByValue.get(model.id);
|
|
83
|
+
if (rec?.identity.isOneMillion && rec.familyKey) {
|
|
84
|
+
const baseSibling = baseByFamily.get(rec.familyKey);
|
|
85
|
+
if (baseSibling && baseSibling !== model.id) {
|
|
86
|
+
model.fallback = baseSibling;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (index < models.length - 1) {
|
|
91
|
+
model.fallback = models[index + 1]!.id;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Convert SDK ModelInfo to our registry format.
|
|
98
|
+
*
|
|
99
|
+
* Base and 1M variants of the same family+version each surface as their own
|
|
100
|
+
* selectable entry (so the picker shows both), while true duplicates — the
|
|
101
|
+
* same model exposed under multiple ids — collapse into one canonical entry
|
|
102
|
+
* that absorbs the others' aliases. Display names, aliases, and the fallback
|
|
103
|
+
* chain are all derived from SDK metadata, never hardcoded versions.
|
|
104
|
+
*/
|
|
105
|
+
export function convertSdkModels(sdkModels: SdkModelInfo[]): ModelInfo[] {
|
|
106
|
+
const records = buildSdkModelRecords(sdkModels);
|
|
107
|
+
const groups = groupVariants(records);
|
|
108
|
+
|
|
109
|
+
// One canonical per variant bucket, ordered by SDK position so the registry
|
|
110
|
+
// preserves the SDK's ordering.
|
|
111
|
+
const canonicals = [...groups.values()]
|
|
112
|
+
.map(pickCanonical)
|
|
113
|
+
.sort((a, b) => a.index - b.index);
|
|
114
|
+
|
|
115
|
+
// Which family+version pairs have a base (non-1M) canonical? A 1M entry only
|
|
116
|
+
// claims the bare family aliases when there is no base sibling to own them.
|
|
117
|
+
const baseFamilyVersions = new Set<string>();
|
|
118
|
+
for (const canonical of canonicals) {
|
|
119
|
+
if (!canonical.identity.isOneMillion && canonical.familyKey) {
|
|
120
|
+
baseFamilyVersions.add(canonical.familyKey);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const usedKeys = new Set<string>();
|
|
125
|
+
const recordByValue = new Map<string, SdkModelRecord>();
|
|
126
|
+
const models: ModelInfo[] = [];
|
|
127
|
+
|
|
128
|
+
for (const canonical of canonicals) {
|
|
129
|
+
const groupKey = canonical.variantKey ?? `raw:${canonical.value}`;
|
|
130
|
+
const bucket = groups.get(groupKey)!;
|
|
131
|
+
const { identity } = canonical;
|
|
132
|
+
|
|
133
|
+
const hasBaseSibling = identity.isOneMillion
|
|
134
|
+
? !!canonical.familyKey && baseFamilyVersions.has(canonical.familyKey)
|
|
135
|
+
: true;
|
|
136
|
+
const aliasForms: AliasFormOptions = {
|
|
137
|
+
includeBare: !identity.isOneMillion || !hasBaseSibling,
|
|
138
|
+
include1m: identity.isOneMillion,
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// Aliases come from every record the canonical absorbs (its own value plus
|
|
142
|
+
// each duplicate's value and generated forms), so legacy ids keep resolving.
|
|
143
|
+
const canonicalKey = canonical.value.toLowerCase();
|
|
144
|
+
const aliases = mergeAliases(
|
|
145
|
+
...bucket.map((record) => [
|
|
146
|
+
record.value,
|
|
147
|
+
...buildGeneratedAliases(record.identity, aliasForms),
|
|
148
|
+
]),
|
|
149
|
+
)
|
|
150
|
+
.filter((alias) => alias.toLowerCase() !== canonicalKey)
|
|
151
|
+
.filter((alias) => !usedKeys.has(alias.toLowerCase()));
|
|
152
|
+
|
|
153
|
+
usedKeys.add(canonicalKey);
|
|
154
|
+
for (const alias of aliases) usedKeys.add(alias.toLowerCase());
|
|
155
|
+
|
|
156
|
+
recordByValue.set(canonical.value, canonical);
|
|
157
|
+
models.push({
|
|
158
|
+
id: canonical.value,
|
|
159
|
+
displayName: deriveDisplayName(identity, canonical.displayName),
|
|
160
|
+
description: canonical.description,
|
|
161
|
+
aliases,
|
|
162
|
+
provider: "anthropic",
|
|
163
|
+
supportedReasoningLevels: extractSdkReasoningLevels(canonical),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
assignFallbacks(models, recordByValue);
|
|
168
|
+
return models;
|
|
169
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude model discovery — spawns throwaway SDK subprocesses, calls
|
|
3
|
+
* `supportedModels()`, unions the results, and registers them in the global
|
|
4
|
+
* model registry. Also the static-registration path for tests / CLI setup.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
8
|
+
import {
|
|
9
|
+
registerModels,
|
|
10
|
+
clearModelsByProvider,
|
|
11
|
+
registerProviderPrefix,
|
|
12
|
+
} from "../../../core/models/catalog.js";
|
|
13
|
+
import type { ModelInfo } from "../../../core/models/catalog.js";
|
|
14
|
+
import { log, logError } from "../../../util/log.js";
|
|
15
|
+
import { describeSdkModel, type SdkModelInfo } from "./parsing.js";
|
|
16
|
+
import { convertSdkModels } from "./convert.js";
|
|
17
|
+
|
|
18
|
+
type ProbeOptions = {
|
|
19
|
+
cwd?: string;
|
|
20
|
+
permissionMode?: string;
|
|
21
|
+
allowDangerouslySkipPermissions?: boolean;
|
|
22
|
+
pathToClaudeCodeExecutable?: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const DISCOVERY_TIMEOUT_MS = 15_000;
|
|
26
|
+
const MAX_DISCOVERY_SEEDS = 8;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The `claude` binary returns "Custom model" for a `model` it doesn't
|
|
30
|
+
* recognise — it echoes the requested id straight back as a passthrough entry
|
|
31
|
+
* with no real metadata. We drop those (except the user's own configured
|
|
32
|
+
* model) so seed probing doesn't pollute the registry with junk families.
|
|
33
|
+
*/
|
|
34
|
+
const CUSTOM_MODEL_DESCRIPTION = "custom model";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Spawn a throwaway SDK subprocess seeded with `seedModel` and return its
|
|
38
|
+
* `supportedModels()` list. The binary always echoes the requested model into
|
|
39
|
+
* the list, so the seed controls which models surface beyond the base set.
|
|
40
|
+
*/
|
|
41
|
+
async function probeSupportedModels(
|
|
42
|
+
seedModel: string,
|
|
43
|
+
probeOptions: ProbeOptions,
|
|
44
|
+
): Promise<SdkModelInfo[]> {
|
|
45
|
+
const abort = new AbortController();
|
|
46
|
+
let drainPromise: Promise<void> | undefined;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const neverYield = async function* (): AsyncGenerator<never> {
|
|
50
|
+
await new Promise<never>((_, reject) => {
|
|
51
|
+
abort.signal.addEventListener("abort", () =>
|
|
52
|
+
reject(new Error("aborted")),
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const q = query({
|
|
58
|
+
prompt: neverYield(),
|
|
59
|
+
options: {
|
|
60
|
+
...probeOptions,
|
|
61
|
+
model: seedModel,
|
|
62
|
+
abortController: abort,
|
|
63
|
+
} as Parameters<typeof query>[0]["options"],
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
drainPromise = (async () => {
|
|
67
|
+
try {
|
|
68
|
+
for await (const _ of q) {
|
|
69
|
+
/* discard */
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
/* expected on abort */
|
|
73
|
+
}
|
|
74
|
+
})();
|
|
75
|
+
|
|
76
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
77
|
+
const timeout = new Promise<never>((_, reject) => {
|
|
78
|
+
timeoutId = setTimeout(
|
|
79
|
+
() =>
|
|
80
|
+
reject(
|
|
81
|
+
new Error(
|
|
82
|
+
`model discovery timed out after 15s (seed "${seedModel}")`,
|
|
83
|
+
),
|
|
84
|
+
),
|
|
85
|
+
DISCOVERY_TIMEOUT_MS,
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
return await Promise.race([q.supportedModels(), timeout]);
|
|
91
|
+
} finally {
|
|
92
|
+
if (timeoutId !== undefined) clearTimeout(timeoutId);
|
|
93
|
+
abort.abort();
|
|
94
|
+
await drainPromise.catch(() => {});
|
|
95
|
+
}
|
|
96
|
+
} catch (err) {
|
|
97
|
+
abort.abort();
|
|
98
|
+
if (drainPromise) await drainPromise.catch(() => {});
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Merge probe results into one list, first-occurrence-wins by value, dropping
|
|
105
|
+
* unrecognised "Custom model" passthrough echoes (but always keeping the user's
|
|
106
|
+
* configured model even if the binary treats it as custom).
|
|
107
|
+
*/
|
|
108
|
+
function unionSdkModels(
|
|
109
|
+
lists: readonly SdkModelInfo[][],
|
|
110
|
+
configuredModel: string,
|
|
111
|
+
): SdkModelInfo[] {
|
|
112
|
+
const byValue = new Map<string, SdkModelInfo>();
|
|
113
|
+
for (const list of lists) {
|
|
114
|
+
for (const model of list) {
|
|
115
|
+
if (byValue.has(model.value)) continue;
|
|
116
|
+
const isPassthroughJunk =
|
|
117
|
+
(model.description ?? "").trim().toLowerCase() ===
|
|
118
|
+
CUSTOM_MODEL_DESCRIPTION && model.value !== configuredModel;
|
|
119
|
+
if (isPassthroughJunk) continue;
|
|
120
|
+
byValue.set(model.value, model);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return [...byValue.values()];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Discover available models from the Claude Agent SDK and register them.
|
|
128
|
+
*
|
|
129
|
+
* Discovery is a union of probes. The binary only echoes the *requested* model
|
|
130
|
+
* (and a small base set) from `supportedModels()`, so a single probe misses
|
|
131
|
+
* the base-vs-1M counterpart of most families. We therefore probe the
|
|
132
|
+
* configured model first (mandatory), then best-effort re-probe each real
|
|
133
|
+
* family alias it revealed, and union the results. Throws only if the
|
|
134
|
+
* mandatory first probe fails — if the SDK can't provide models, Talon cannot
|
|
135
|
+
* function.
|
|
136
|
+
*/
|
|
137
|
+
export async function registerClaudeModels(sdkOptions: {
|
|
138
|
+
model: string;
|
|
139
|
+
cwd?: string;
|
|
140
|
+
permissionMode?: string;
|
|
141
|
+
allowDangerouslySkipPermissions?: boolean;
|
|
142
|
+
pathToClaudeCodeExecutable?: string;
|
|
143
|
+
}): Promise<void> {
|
|
144
|
+
const { model: configuredModel, ...probeOptions } = sdkOptions;
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
// Pass 1 (mandatory): the configured model is always echoed back.
|
|
148
|
+
const primary = await probeSupportedModels(configuredModel, probeOptions);
|
|
149
|
+
if (primary.length === 0) {
|
|
150
|
+
throw new Error("SDK returned empty model list");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Pass 2 (best-effort): re-probe each real family alias from pass 1 to coax
|
|
154
|
+
// out the context variants the first probe didn't echo. Bare family aliases
|
|
155
|
+
// (e.g. "opus") return real metadata; unrecognised ones come back as
|
|
156
|
+
// "Custom model" and are filtered by unionSdkModels.
|
|
157
|
+
const seeds = new Set<string>();
|
|
158
|
+
for (const model of primary) {
|
|
159
|
+
const { family } = describeSdkModel(model);
|
|
160
|
+
if (family && family !== "default") seeds.add(family);
|
|
161
|
+
}
|
|
162
|
+
seeds.delete(configuredModel);
|
|
163
|
+
|
|
164
|
+
const seedList = [...seeds].slice(0, MAX_DISCOVERY_SEEDS);
|
|
165
|
+
const secondary = await Promise.allSettled(
|
|
166
|
+
seedList.map((seed) => probeSupportedModels(seed, probeOptions)),
|
|
167
|
+
);
|
|
168
|
+
const extraLists = secondary
|
|
169
|
+
.filter(
|
|
170
|
+
(result): result is PromiseFulfilledResult<SdkModelInfo[]> =>
|
|
171
|
+
result.status === "fulfilled",
|
|
172
|
+
)
|
|
173
|
+
.map((result) => result.value);
|
|
174
|
+
|
|
175
|
+
const union = unionSdkModels([primary, ...extraLists], configuredModel);
|
|
176
|
+
const models = convertSdkModels(union);
|
|
177
|
+
clearModelsByProvider("anthropic");
|
|
178
|
+
registerProviderPrefix("claude-");
|
|
179
|
+
registerModels(models);
|
|
180
|
+
log(
|
|
181
|
+
"agent",
|
|
182
|
+
`Discovered ${models.length} models from SDK ` +
|
|
183
|
+
`(${1 + extraLists.length}/${1 + seedList.length} probes): ` +
|
|
184
|
+
models.map((m) => m.id).join(", "),
|
|
185
|
+
);
|
|
186
|
+
} catch (err) {
|
|
187
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
188
|
+
logError("agent", `Fatal: model discovery failed — ${msg}`);
|
|
189
|
+
throw new Error(
|
|
190
|
+
`Claude SDK model discovery failed: ${msg}. ` +
|
|
191
|
+
`Check that Claude Code is installed and your API key is valid.`,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Register models from a static list. For use in tests and the CLI setup
|
|
198
|
+
* wizard where the SDK subprocess is not available.
|
|
199
|
+
*/
|
|
200
|
+
export function registerClaudeModelsStatic(models: ModelInfo[]): void {
|
|
201
|
+
registerProviderPrefix("claude-");
|
|
202
|
+
registerModels(models);
|
|
203
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude model discovery — queries the SDK for available models.
|
|
3
|
+
*
|
|
4
|
+
* Split by responsibility:
|
|
5
|
+
* - `parsing` — model-identity parsing, display names, alias generation
|
|
6
|
+
* - `convert` — SDK ModelInfo → registry ModelInfo[] (variants, fallbacks)
|
|
7
|
+
* - `discovery` — SDK subprocess probing + registration (registerClaudeModels,
|
|
8
|
+
* registerClaudeModelsStatic)
|
|
9
|
+
* - `static` — CLAUDE_MODELS_STATIC fallback list
|
|
10
|
+
*
|
|
11
|
+
* Re-exports the same public surface the old single-file module exposed.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
registerClaudeModels,
|
|
16
|
+
registerClaudeModelsStatic,
|
|
17
|
+
} from "./discovery.js";
|
|
18
|
+
export { CLAUDE_MODELS_STATIC } from "./static.js";
|