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
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message queue β debounces rapid-fire messages per chat (concatenating them
|
|
3
|
+
* into a single agent turn) with one transient retry, then best-effort error
|
|
4
|
+
* notification.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { classify, friendlyMessage } from "../../../core/errors.js";
|
|
8
|
+
import { recordMessageProcessed, recordError } from "../../../util/watchdog.js";
|
|
9
|
+
import { appendDailyLog } from "../../../storage/daily-log.js";
|
|
10
|
+
import { log, logError } from "../../../util/log.js";
|
|
11
|
+
import { processAndReply } from "./delivery.js";
|
|
12
|
+
import { sendChunked } from "./context.js";
|
|
13
|
+
import {
|
|
14
|
+
messageQueues,
|
|
15
|
+
DEBOUNCE_MS,
|
|
16
|
+
MAX_QUEUED_PER_CHAT,
|
|
17
|
+
type QueuedMessage,
|
|
18
|
+
} from "./state.js";
|
|
19
|
+
import type { TalonConfig } from "../../../util/config.js";
|
|
20
|
+
|
|
21
|
+
export function enqueueMessage(
|
|
22
|
+
config: TalonConfig,
|
|
23
|
+
chatId: string,
|
|
24
|
+
numericChatId: number,
|
|
25
|
+
msg: QueuedMessage,
|
|
26
|
+
): void {
|
|
27
|
+
const existing = messageQueues.get(chatId);
|
|
28
|
+
if (existing) {
|
|
29
|
+
if (existing.messages.length >= MAX_QUEUED_PER_CHAT) return;
|
|
30
|
+
existing.messages.push(msg);
|
|
31
|
+
clearTimeout(existing.timer);
|
|
32
|
+
existing.timer = setTimeout(() => flushQueue(chatId), DEBOUNCE_MS);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
messageQueues.set(chatId, {
|
|
36
|
+
messages: [msg],
|
|
37
|
+
timer: setTimeout(() => flushQueue(chatId), DEBOUNCE_MS),
|
|
38
|
+
config,
|
|
39
|
+
chatId,
|
|
40
|
+
numericChatId,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function flushQueue(chatId: string): Promise<void> {
|
|
45
|
+
const entry = messageQueues.get(chatId);
|
|
46
|
+
if (!entry) return;
|
|
47
|
+
messageQueues.delete(chatId);
|
|
48
|
+
|
|
49
|
+
const { messages, config, numericChatId } = entry;
|
|
50
|
+
const last = messages[messages.length - 1];
|
|
51
|
+
const combinedPrompt =
|
|
52
|
+
messages.length === 1
|
|
53
|
+
? messages[0].prompt
|
|
54
|
+
: messages.map((m) => m.prompt).join("\n\n");
|
|
55
|
+
|
|
56
|
+
appendDailyLog(last.senderName, combinedPrompt, {
|
|
57
|
+
chatTitle: last.chatTitle,
|
|
58
|
+
username: last.senderUsername,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const runTurn = () =>
|
|
62
|
+
processAndReply({
|
|
63
|
+
config,
|
|
64
|
+
chatId,
|
|
65
|
+
numericChatId,
|
|
66
|
+
replyToId: last.replyToId,
|
|
67
|
+
messageId: last.messageId,
|
|
68
|
+
numericMessageId: last.numericMessageId,
|
|
69
|
+
prompt: combinedPrompt,
|
|
70
|
+
senderName: last.senderName,
|
|
71
|
+
isGroup: last.isGroup,
|
|
72
|
+
senderUsername: last.senderUsername,
|
|
73
|
+
senderId: last.senderId,
|
|
74
|
+
channel: last.channel,
|
|
75
|
+
chatTitle: last.chatTitle,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
await runTurn();
|
|
80
|
+
recordMessageProcessed();
|
|
81
|
+
} catch (err) {
|
|
82
|
+
const classified = classify(err);
|
|
83
|
+
const promptPreview = combinedPrompt.slice(0, 100).replace(/\n/g, " ");
|
|
84
|
+
logError(
|
|
85
|
+
"bot",
|
|
86
|
+
`[${chatId}] [${last.isGroup ? "guild" : "DM"}] [${last.senderName}] ${classified.reason}: ${classified.message} | prompt: "${promptPreview}"`,
|
|
87
|
+
);
|
|
88
|
+
recordError(classified.message);
|
|
89
|
+
|
|
90
|
+
if (classified.retryable) {
|
|
91
|
+
const delayMs = classified.retryAfterMs ?? 2000;
|
|
92
|
+
log(
|
|
93
|
+
"bot",
|
|
94
|
+
`[${chatId}] Retrying after ${classified.reason} (${delayMs}ms)...`,
|
|
95
|
+
);
|
|
96
|
+
try {
|
|
97
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
98
|
+
await runTurn();
|
|
99
|
+
recordMessageProcessed();
|
|
100
|
+
return;
|
|
101
|
+
} catch (retryErr) {
|
|
102
|
+
const retryClassified = classify(retryErr);
|
|
103
|
+
logError("bot", `[${chatId}] Retry failed: ${retryClassified.message}`);
|
|
104
|
+
// Error-recovery send must never throw β if the network is fully down
|
|
105
|
+
// the queue handler would otherwise propagate up and stall future
|
|
106
|
+
// messages. Best-effort: notify if we can, log + move on otherwise.
|
|
107
|
+
try {
|
|
108
|
+
await sendChunked(
|
|
109
|
+
last.channel,
|
|
110
|
+
friendlyMessage(retryClassified),
|
|
111
|
+
last.replyToId,
|
|
112
|
+
);
|
|
113
|
+
} catch (notifyErr) {
|
|
114
|
+
logError(
|
|
115
|
+
"bot",
|
|
116
|
+
`[${chatId}] Could not notify user about retry failure: ${notifyErr instanceof Error ? notifyErr.message : notifyErr}`,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
await sendChunked(
|
|
124
|
+
last.channel,
|
|
125
|
+
friendlyMessage(classified),
|
|
126
|
+
last.replyToId,
|
|
127
|
+
);
|
|
128
|
+
} catch (notifyErr) {
|
|
129
|
+
logError(
|
|
130
|
+
"bot",
|
|
131
|
+
`[${chatId}] Could not notify user about error: ${notifyErr instanceof Error ? notifyErr.message : notifyErr}`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat-registry accessors β map between numeric/string chat ids and the
|
|
3
|
+
* Discord channel info the action handler needs to post back.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
chatRegistry,
|
|
8
|
+
chatRegistryByString,
|
|
9
|
+
type DiscordChatInfo,
|
|
10
|
+
} from "./state.js";
|
|
11
|
+
|
|
12
|
+
export type { DiscordChatInfo } from "./state.js";
|
|
13
|
+
|
|
14
|
+
export function registerDiscordChat(info: DiscordChatInfo): void {
|
|
15
|
+
chatRegistry.set(info.numericChatId, info);
|
|
16
|
+
chatRegistryByString.set(info.chatId, info);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function lookupDiscordChat(
|
|
20
|
+
numericChatId: number,
|
|
21
|
+
): DiscordChatInfo | undefined {
|
|
22
|
+
return chatRegistry.get(numericChatId);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function lookupDiscordChatByString(
|
|
26
|
+
chatId: string,
|
|
27
|
+
): DiscordChatInfo | undefined {
|
|
28
|
+
return chatRegistryByString.get(chatId);
|
|
29
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared module-level state for the Discord message-handling pipeline:
|
|
3
|
+
* the chat registry, access config, DM-user tracking, unauthorized cooldown,
|
|
4
|
+
* per-user rate-limit windows, and the debounce queue. Created once here and
|
|
5
|
+
* imported by the handler submodules so all state stays coherent across files.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { TextBasedChannel } from "discord.js";
|
|
9
|
+
import type { TalonConfig } from "../../../util/config.js";
|
|
10
|
+
|
|
11
|
+
// ββ Chat registry: numericChatId β Discord channel info βββββββββββββββββββββ
|
|
12
|
+
// The gateway/dispatcher uses numeric chatIds. Action handlers need to map
|
|
13
|
+
// back to the Discord channel they should post to.
|
|
14
|
+
|
|
15
|
+
export type DiscordChatInfo = {
|
|
16
|
+
channelId: string;
|
|
17
|
+
guildId: string | null;
|
|
18
|
+
userId: string | null; // for DM contexts
|
|
19
|
+
numericChatId: number;
|
|
20
|
+
chatId: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const chatRegistry = new Map<number, DiscordChatInfo>();
|
|
24
|
+
export const chatRegistryByString = new Map<string, DiscordChatInfo>();
|
|
25
|
+
|
|
26
|
+
// ββ Access control state βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
27
|
+
|
|
28
|
+
export type AccessConfig = {
|
|
29
|
+
allowedUsers: Set<string>;
|
|
30
|
+
allowedGuilds: Set<string>;
|
|
31
|
+
allowedChannels: Set<string>; // empty = all channels in allowedGuilds
|
|
32
|
+
adminUserIds: Set<string>;
|
|
33
|
+
respondMode: "mention" | "channel";
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Reassignable access config on a holder object so `setAccessControl` can
|
|
38
|
+
* swap it and every reader sees the update through the import.
|
|
39
|
+
*/
|
|
40
|
+
export const accessState: { access: AccessConfig } = {
|
|
41
|
+
access: {
|
|
42
|
+
allowedUsers: new Set(),
|
|
43
|
+
allowedGuilds: new Set(),
|
|
44
|
+
allowedChannels: new Set(),
|
|
45
|
+
adminUserIds: new Set(),
|
|
46
|
+
respondMode: "mention",
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// ββ First-time DM user tracking ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
51
|
+
|
|
52
|
+
export const knownDmUsers = new Set<string>();
|
|
53
|
+
export const KNOWN_DM_USERS_CAP = 10_000;
|
|
54
|
+
|
|
55
|
+
// ββ Unauthorized notification cooldown (10 min) ββββββββββββββββββββββββββββββ
|
|
56
|
+
|
|
57
|
+
export const unauthorizedCooldown = new Map<string, number>();
|
|
58
|
+
export const UNAUTHORIZED_COOLDOWN_MS = 10 * 60 * 1000;
|
|
59
|
+
export const MAX_UNAUTHORIZED_COOLDOWNS = 5000;
|
|
60
|
+
|
|
61
|
+
// ββ Per-user rate limiting βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
62
|
+
|
|
63
|
+
export const userMessageTimestamps = new Map<string, number[]>();
|
|
64
|
+
export const RATE_LIMIT_WINDOW_MS = 60_000;
|
|
65
|
+
export const RATE_LIMIT_MAX_MESSAGES = 15;
|
|
66
|
+
|
|
67
|
+
// ββ Message queue (debounce rapid-fire messages per chat) ββββββββββββββββββββ
|
|
68
|
+
|
|
69
|
+
export type QueuedMessage = {
|
|
70
|
+
prompt: string;
|
|
71
|
+
replyToId: string;
|
|
72
|
+
messageId: string;
|
|
73
|
+
numericMessageId: number;
|
|
74
|
+
senderName: string;
|
|
75
|
+
senderUsername?: string;
|
|
76
|
+
senderId: string;
|
|
77
|
+
isGroup: boolean;
|
|
78
|
+
channel: TextBasedChannel;
|
|
79
|
+
chatTitle?: string;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const messageQueues = new Map<
|
|
83
|
+
string,
|
|
84
|
+
{
|
|
85
|
+
messages: QueuedMessage[];
|
|
86
|
+
timer: ReturnType<typeof setTimeout>;
|
|
87
|
+
config: TalonConfig;
|
|
88
|
+
chatId: string;
|
|
89
|
+
numericChatId: number;
|
|
90
|
+
}
|
|
91
|
+
>();
|
|
92
|
+
|
|
93
|
+
export const DEBOUNCE_MS = 500;
|
|
94
|
+
export const MAX_QUEUED_PER_CHAT = 20;
|
|
@@ -7,21 +7,28 @@
|
|
|
7
7
|
* - chat IDs are Discord snowflakes (strings), not numbers.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { REASONING_LEVEL_DESCRIPTIONS } from "../../core/models/reasoning-levels.js";
|
|
11
11
|
import { DISCORD_MAX_TEXT, DISCORD_SAFE_RESERVE } from "./formatting.js";
|
|
12
|
+
import {
|
|
13
|
+
DEFAULT_PULSE_INTERVAL_MS,
|
|
14
|
+
formatDuration,
|
|
15
|
+
formatModelLabel,
|
|
16
|
+
} from "../shared/format.js";
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
parseInterval,
|
|
20
|
+
formatDuration,
|
|
21
|
+
formatTokenCount,
|
|
22
|
+
formatBytes,
|
|
23
|
+
formatModelLabel,
|
|
24
|
+
} from "../shared/format.js";
|
|
12
25
|
|
|
13
|
-
const DEFAULT_PULSE_INTERVAL_MS = 5 * 60 * 1000;
|
|
14
26
|
/** Per-message length budget for metrics output. */
|
|
15
27
|
const DEFAULT_METRICS_MESSAGE_MAX = DISCORD_MAX_TEXT - DISCORD_SAFE_RESERVE;
|
|
16
28
|
|
|
17
29
|
/** Effort level descriptions shown next to each option in select menus. */
|
|
18
30
|
export const EFFORT_DESCRIPTIONS: Record<string, string> = {
|
|
19
|
-
|
|
20
|
-
low: "short reasoning pass",
|
|
21
|
-
medium: "balanced reasoning",
|
|
22
|
-
high: "deeper reasoning, slower",
|
|
23
|
-
max: "most thorough β slowest",
|
|
24
|
-
adaptive: "model decides when to think",
|
|
31
|
+
...REASONING_LEVEL_DESCRIPTIONS,
|
|
25
32
|
};
|
|
26
33
|
|
|
27
34
|
type MetricsSnapshot = {
|
|
@@ -32,45 +39,6 @@ type MetricsSnapshot = {
|
|
|
32
39
|
>;
|
|
33
40
|
};
|
|
34
41
|
|
|
35
|
-
/** Parse a duration string like "30m", "2h", "1d", "1h30m", "1d6h" into ms. */
|
|
36
|
-
export function parseInterval(input: string): number | null {
|
|
37
|
-
const match = input.match(/^(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?$/);
|
|
38
|
-
if (!match || (!match[1] && !match[2] && !match[3])) return null;
|
|
39
|
-
const days = parseInt(match[1] || "0", 10);
|
|
40
|
-
const hours = parseInt(match[2] || "0", 10);
|
|
41
|
-
const minutes = parseInt(match[3] || "0", 10);
|
|
42
|
-
const ms = (days * 24 * 60 + hours * 60 + minutes) * 60 * 1000;
|
|
43
|
-
return ms > 0 ? ms : null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function formatDuration(ms: number): string {
|
|
47
|
-
const safeMs = Math.max(0, Math.round(ms));
|
|
48
|
-
if (safeMs < 1000) return `${safeMs}ms`;
|
|
49
|
-
const s = Math.floor(safeMs / 1000);
|
|
50
|
-
if (s < 60) return `${s}s`;
|
|
51
|
-
const m = Math.floor(s / 60);
|
|
52
|
-
if (m < 60) return `${m}m ${s % 60}s`;
|
|
53
|
-
const h = Math.floor(m / 60);
|
|
54
|
-
return `${h}h ${m % 60}m`;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function formatTokenCount(n: number): string {
|
|
58
|
-
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
|
|
59
|
-
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}k`;
|
|
60
|
-
return String(n);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function formatBytes(bytes: number): string {
|
|
64
|
-
if (bytes >= 1_073_741_824) return `${(bytes / 1_073_741_824).toFixed(1)} GB`;
|
|
65
|
-
if (bytes >= 1_048_576) return `${(bytes / 1_048_576).toFixed(1)} MB`;
|
|
66
|
-
if (bytes >= 1_024) return `${(bytes / 1_024).toFixed(1)} KB`;
|
|
67
|
-
return `${bytes} B`;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function formatModelLabel(modelId: string): string {
|
|
71
|
-
return resolveModel(modelId)?.displayName ?? modelId;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
42
|
function truncateMetricLabel(label: string, max = 60): string {
|
|
75
43
|
return label.length <= max ? label : `${label.slice(0, max - 3)}...`;
|
|
76
44
|
}
|
|
@@ -87,18 +55,30 @@ export function renderMetricsMessages(
|
|
|
87
55
|
const continuationHeader = "**π Metrics (cont.)**";
|
|
88
56
|
const sections: string[][] = [];
|
|
89
57
|
|
|
58
|
+
// Histograms come in two flavours: durations (keys ending in `_ms`,
|
|
59
|
+
// rendered as human times) and plain counts like `tool_calls_per_turn`
|
|
60
|
+
// (rendered as bare numbers β "p50=1ms" for a count is nonsense).
|
|
90
61
|
const histKeys = Object.keys(metrics.histograms).sort();
|
|
91
|
-
|
|
62
|
+
const durationKeys = histKeys.filter((key) => key.endsWith("_ms"));
|
|
63
|
+
const countKeys = histKeys.filter((key) => !key.endsWith("_ms"));
|
|
64
|
+
const histLine = (key: string, fmt: (v: number) => string): string => {
|
|
65
|
+
const h = metrics.histograms[key];
|
|
66
|
+
return (
|
|
67
|
+
` \`${truncateMetricLabel(key)}\` n=${h.count} ` +
|
|
68
|
+
`p50=${fmt(h.p50)} p95=${fmt(h.p95)} ` +
|
|
69
|
+
`p99=${fmt(h.p99)} avg=${fmt(h.avg)}`
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
if (durationKeys.length > 0) {
|
|
92
73
|
sections.push([
|
|
93
74
|
"**Latency**",
|
|
94
|
-
...
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}),
|
|
75
|
+
...durationKeys.map((key) => histLine(key, formatDuration)),
|
|
76
|
+
]);
|
|
77
|
+
}
|
|
78
|
+
if (countKeys.length > 0) {
|
|
79
|
+
sections.push([
|
|
80
|
+
"**Distributions**",
|
|
81
|
+
...countKeys.map((key) => histLine(key, String)),
|
|
102
82
|
]);
|
|
103
83
|
}
|
|
104
84
|
|
|
@@ -111,7 +91,16 @@ export function renderMetricsMessages(
|
|
|
111
91
|
groups.get(prefix)!.push(key);
|
|
112
92
|
}
|
|
113
93
|
for (const prefix of [...groups.keys()].sort()) {
|
|
114
|
-
|
|
94
|
+
// tool_calls reads best as a leaderboard β busiest tools first.
|
|
95
|
+
// Other groups keep alphabetical order (stable lookup by name).
|
|
96
|
+
const keys =
|
|
97
|
+
prefix === "tool_calls"
|
|
98
|
+
? [...groups.get(prefix)!].sort(
|
|
99
|
+
(a, b) =>
|
|
100
|
+
metrics.counters[b]! - metrics.counters[a]! ||
|
|
101
|
+
a.localeCompare(b),
|
|
102
|
+
)
|
|
103
|
+
: groups.get(prefix)!;
|
|
115
104
|
sections.push([
|
|
116
105
|
`**${prefix}**`,
|
|
117
106
|
...keys.map((key) => {
|
|
@@ -23,20 +23,20 @@ import {
|
|
|
23
23
|
import { once } from "node:events";
|
|
24
24
|
import type { TalonConfig } from "../../util/config.js";
|
|
25
25
|
import type { ContextManager } from "../../core/types.js";
|
|
26
|
-
import type { Gateway } from "../../core/gateway.js";
|
|
26
|
+
import type { Gateway } from "../../core/engine/gateway.js";
|
|
27
27
|
import { log, logError, logWarn } from "../../util/log.js";
|
|
28
|
-
import { createDiscordActionHandler } from "./actions.js";
|
|
28
|
+
import { createDiscordActionHandler } from "./actions/index.js";
|
|
29
29
|
import {
|
|
30
30
|
registerCommandsForGuilds,
|
|
31
31
|
registerInteractionRouter,
|
|
32
|
-
} from "./commands.js";
|
|
32
|
+
} from "./commands/index.js";
|
|
33
33
|
import { registerMiddleware } from "./middleware.js";
|
|
34
34
|
import {
|
|
35
35
|
setAccessControl,
|
|
36
36
|
lookupDiscordChat,
|
|
37
37
|
registerDiscordChat,
|
|
38
|
-
} from "./handlers.js";
|
|
39
|
-
import { sendChunked } from "./handlers.js";
|
|
38
|
+
} from "./handlers/index.js";
|
|
39
|
+
import { sendChunked } from "./handlers/index.js";
|
|
40
40
|
import { deriveNumericChatId } from "../../util/chat-id.js";
|
|
41
41
|
|
|
42
42
|
// ββ Frontend type βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -89,7 +89,7 @@ export function createDiscordFrontend(
|
|
|
89
89
|
|
|
90
90
|
const context: ContextManager = {
|
|
91
91
|
acquire: (chatId: number, stringId?: string) =>
|
|
92
|
-
gateway.setContext(chatId, stringId),
|
|
92
|
+
gateway.setContext(chatId, stringId, "discord"),
|
|
93
93
|
release: (chatId: number) => gateway.clearContext(chatId),
|
|
94
94
|
getMessageCount: (chatId: number) => gateway.getMessageCount(chatId),
|
|
95
95
|
};
|
|
@@ -147,7 +147,10 @@ export function createDiscordFrontend(
|
|
|
147
147
|
|
|
148
148
|
async init() {
|
|
149
149
|
// Register Discord action handler with the gateway
|
|
150
|
-
gateway.
|
|
150
|
+
gateway.registerFrontendHandler(
|
|
151
|
+
"discord",
|
|
152
|
+
createDiscordActionHandler(client, gateway),
|
|
153
|
+
);
|
|
151
154
|
|
|
152
155
|
const port = await gateway.start(19876);
|
|
153
156
|
log("discord", `Gateway started on port ${port}`);
|
|
@@ -18,9 +18,9 @@ import type { Client, Message } from "discord.js";
|
|
|
18
18
|
import { ChannelType } from "discord.js";
|
|
19
19
|
import type { TalonConfig } from "../../util/config.js";
|
|
20
20
|
import { pushMessage } from "../../storage/history.js";
|
|
21
|
-
import { registerChat } from "../../core/pulse.js";
|
|
21
|
+
import { registerChat } from "../../core/background/pulse.js";
|
|
22
22
|
import { deriveNumericChatId } from "../../util/chat-id.js";
|
|
23
|
-
import { handleMessage, getSenderName } from "./handlers.js";
|
|
23
|
+
import { handleMessage, getSenderName } from "./handlers/index.js";
|
|
24
24
|
|
|
25
25
|
export function registerMiddleware(client: Client, config: TalonConfig): void {
|
|
26
26
|
client.on("messageCreate", (msg: Message) => {
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native action handler β the gateway-side bridge for the agent's delivery
|
|
3
|
+
* tools. When the model calls `end_turn` / `send_message` / `react` / edit /
|
|
4
|
+
* delete, the MCP tool POSTs an action to the gateway, which routes it here
|
|
5
|
+
* by the active chat's numeric id. We translate each action into a Bridge
|
|
6
|
+
* event the connected clients render, persist assistant messages to history,
|
|
7
|
+
* and count deliveries on the gateway (so the dispatcher's trailing-prose
|
|
8
|
+
* fallback knows the turn already produced output).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { FrontendActionHandler, ActionResult } from "../../core/types.js";
|
|
12
|
+
import type { Gateway } from "../../core/engine/gateway.js";
|
|
13
|
+
import type { NativeChats, ChatEntry } from "./chats.js";
|
|
14
|
+
import type { BridgeEvent, ClientButton } from "./protocol.js";
|
|
15
|
+
|
|
16
|
+
export type NativeActionDeps = {
|
|
17
|
+
chats: NativeChats;
|
|
18
|
+
gateway: Gateway;
|
|
19
|
+
/** Persist + broadcast an assistant message; returns its numeric id. */
|
|
20
|
+
emitAssistant: (
|
|
21
|
+
entry: ChatEntry,
|
|
22
|
+
text: string,
|
|
23
|
+
buttons?: ClientButton[][],
|
|
24
|
+
) => number;
|
|
25
|
+
/** Persist + broadcast an assistant photo message; returns its numeric id. */
|
|
26
|
+
emitPhoto: (entry: ChatEntry, filePath: string, caption?: string) => number;
|
|
27
|
+
broadcast: (event: BridgeEvent) => void;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Coerce gateway button rows (`[[{text,url,callback_data}]]`) to wire buttons. */
|
|
31
|
+
function toButtons(rows: unknown): ClientButton[][] | undefined {
|
|
32
|
+
if (!Array.isArray(rows)) return undefined;
|
|
33
|
+
const mapped: ClientButton[][] = [];
|
|
34
|
+
for (const row of rows) {
|
|
35
|
+
if (!Array.isArray(row)) continue;
|
|
36
|
+
const cells: ClientButton[] = [];
|
|
37
|
+
for (const cell of row) {
|
|
38
|
+
if (cell && typeof cell === "object") {
|
|
39
|
+
const c = cell as Record<string, unknown>;
|
|
40
|
+
const text = typeof c.text === "string" ? c.text : "";
|
|
41
|
+
if (!text) continue;
|
|
42
|
+
cells.push({
|
|
43
|
+
text,
|
|
44
|
+
url: typeof c.url === "string" ? c.url : undefined,
|
|
45
|
+
data:
|
|
46
|
+
typeof c.callback_data === "string" ? c.callback_data : undefined,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (cells.length) mapped.push(cells);
|
|
51
|
+
}
|
|
52
|
+
return mapped.length ? mapped : undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createNativeActionHandler(
|
|
56
|
+
deps: NativeActionDeps,
|
|
57
|
+
): FrontendActionHandler {
|
|
58
|
+
const { chats, gateway, emitAssistant, emitPhoto, broadcast } = deps;
|
|
59
|
+
|
|
60
|
+
return async (body, chatId): Promise<ActionResult | null> => {
|
|
61
|
+
const action = typeof body.action === "string" ? body.action : "";
|
|
62
|
+
const entry = chats.byNumeric(chatId);
|
|
63
|
+
if (!entry) return { ok: false, error: "No active native chat" };
|
|
64
|
+
|
|
65
|
+
switch (action) {
|
|
66
|
+
case "send_message": {
|
|
67
|
+
const text = String(body.text ?? "");
|
|
68
|
+
const id = emitAssistant(entry, text);
|
|
69
|
+
gateway.incrementMessages(chatId);
|
|
70
|
+
return { ok: true, message_id: id };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
case "send_message_with_buttons": {
|
|
74
|
+
const text = String(body.text ?? "");
|
|
75
|
+
const id = emitAssistant(entry, text, toButtons(body.rows));
|
|
76
|
+
gateway.incrementMessages(chatId);
|
|
77
|
+
return { ok: true, message_id: id };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
case "send_photo": {
|
|
81
|
+
const filePath = String(body.file_path ?? "");
|
|
82
|
+
if (!filePath) return { ok: false, error: "file_path required" };
|
|
83
|
+
const caption =
|
|
84
|
+
typeof body.caption === "string" ? body.caption : undefined;
|
|
85
|
+
const id = emitPhoto(entry, filePath, caption);
|
|
86
|
+
gateway.incrementMessages(chatId);
|
|
87
|
+
return { ok: true, message_id: id };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
case "react": {
|
|
91
|
+
const messageId = String(body.message_id ?? "");
|
|
92
|
+
const emoji = String(body.emoji ?? "π");
|
|
93
|
+
broadcast({ kind: "reaction", chatId: entry.id, messageId, emoji });
|
|
94
|
+
gateway.incrementMessages(chatId);
|
|
95
|
+
return { ok: true };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
case "edit_message": {
|
|
99
|
+
const messageId = String(body.message_id ?? "");
|
|
100
|
+
const text = String(body.text ?? "");
|
|
101
|
+
broadcast({
|
|
102
|
+
kind: "message_edited",
|
|
103
|
+
chatId: entry.id,
|
|
104
|
+
messageId,
|
|
105
|
+
text,
|
|
106
|
+
});
|
|
107
|
+
return { ok: true };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
case "delete_message": {
|
|
111
|
+
const messageId = String(body.message_id ?? "");
|
|
112
|
+
broadcast({ kind: "message_deleted", chatId: entry.id, messageId });
|
|
113
|
+
return { ok: true };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
case "get_chat_info":
|
|
117
|
+
return {
|
|
118
|
+
ok: true,
|
|
119
|
+
id: entry.numericId,
|
|
120
|
+
type: "private",
|
|
121
|
+
title: entry.title,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Typing is driven by the dispatcher via sendTyping β no-op ack here.
|
|
125
|
+
case "send_chat_action":
|
|
126
|
+
return { ok: true };
|
|
127
|
+
|
|
128
|
+
default:
|
|
129
|
+
// Let the gateway try plugin + shared actions (history, web, cronβ¦).
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|