talon-agent 1.11.0 → 1.12.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 +69 -40
- package/package.json +9 -1
- package/prompts/base.md +12 -8
- package/prompts/discord.md +84 -0
- package/prompts/identity.md +6 -6
- package/src/backend/claude-sdk/factory.ts +79 -0
- package/src/backend/claude-sdk/handler.ts +106 -129
- package/src/backend/claude-sdk/index.ts +7 -1
- package/src/backend/claude-sdk/model-provider.ts +21 -5
- package/src/backend/claude-sdk/one-shot.ts +277 -0
- package/src/backend/claude-sdk/options.ts +94 -6
- package/src/backend/claude-sdk/warm.ts +4 -3
- package/src/backend/codex/auth.ts +165 -0
- package/src/backend/codex/constants.ts +73 -0
- package/src/backend/codex/factory.ts +67 -0
- package/src/backend/codex/handler.ts +595 -0
- package/src/backend/codex/index.ts +33 -0
- package/src/backend/codex/init.ts +166 -0
- package/src/backend/codex/mcp-config.ts +137 -0
- package/src/backend/codex/models.ts +241 -0
- package/src/backend/codex/one-shot.ts +239 -0
- package/src/backend/codex/state.ts +41 -0
- package/src/backend/kilo/events.ts +60 -0
- package/src/backend/kilo/factory.ts +97 -0
- package/src/backend/kilo/handler.ts +707 -0
- package/src/backend/kilo/index.ts +87 -0
- package/src/backend/kilo/model-provider.ts +178 -0
- package/src/backend/kilo/models.ts +762 -0
- package/src/backend/kilo/one-shot.ts +162 -0
- package/src/backend/kilo/server.ts +332 -0
- package/src/backend/kilo/sessions.ts +101 -0
- package/src/backend/openai-agents/builtins.ts +338 -0
- package/src/backend/openai-agents/constants.ts +54 -0
- package/src/backend/openai-agents/factory.ts +63 -0
- package/src/backend/openai-agents/handler.ts +539 -0
- package/src/backend/openai-agents/index.ts +30 -0
- package/src/backend/openai-agents/init.ts +273 -0
- package/src/backend/openai-agents/mcp.ts +139 -0
- package/src/backend/openai-agents/models.ts +388 -0
- package/src/backend/openai-agents/state.ts +61 -0
- package/src/backend/opencode/factory.ts +86 -0
- package/src/backend/opencode/handler.ts +582 -117
- package/src/backend/opencode/index.ts +3 -0
- package/src/backend/opencode/model-provider.ts +0 -1
- package/src/backend/opencode/models.ts +1 -2
- package/src/backend/opencode/one-shot.ts +157 -0
- package/src/backend/opencode/server.ts +119 -278
- package/src/backend/opencode/sessions.ts +67 -460
- package/src/backend/registry.ts +117 -0
- package/src/backend/remote-server/client.ts +63 -0
- package/src/backend/remote-server/events.ts +410 -0
- package/src/backend/remote-server/index.ts +65 -0
- package/src/backend/remote-server/lifecycle.ts +197 -0
- package/src/backend/remote-server/mcp.ts +312 -0
- package/src/backend/remote-server/messages.ts +52 -0
- package/src/backend/remote-server/providers.ts +118 -0
- package/src/backend/remote-server/session-helpers.ts +543 -0
- package/src/backend/remote-server/sessions.ts +108 -0
- package/src/backend/remote-server/sse-stream.ts +80 -0
- package/src/backend/remote-server/state.ts +93 -0
- package/src/backend/shared/delivered-text.ts +103 -0
- package/src/backend/shared/delivery.ts +184 -0
- package/src/backend/shared/flow-violation.ts +92 -0
- package/src/backend/shared/index.ts +75 -0
- package/src/backend/shared/model-retry.ts +72 -0
- package/src/backend/shared/prompt-format.ts +71 -0
- package/src/backend/shared/session-name.ts +41 -0
- package/src/backend/shared/sleep.ts +26 -0
- package/src/backend/shared/stream-state.ts +247 -0
- package/src/backend/shared/system-prompt.ts +71 -0
- package/src/backend/shared/usage.ts +51 -0
- package/src/bootstrap.ts +61 -84
- package/src/cli.ts +313 -29
- package/src/core/dream.ts +53 -108
- package/src/core/errors.ts +7 -4
- package/src/core/gateway-actions.ts +219 -0
- package/src/core/gateway.ts +28 -7
- package/src/core/heartbeat.ts +184 -116
- package/src/core/tools/bridge.ts +21 -2
- package/src/core/tools/chat.ts +4 -4
- package/src/core/tools/history.ts +11 -9
- package/src/core/tools/index.ts +70 -12
- package/src/core/tools/mcp-server.ts +2 -1
- package/src/core/tools/media.ts +1 -1
- package/src/core/tools/members.ts +6 -6
- package/src/core/tools/messaging.ts +85 -21
- package/src/core/tools/scheduling.ts +1 -1
- package/src/core/tools/schemas.ts +62 -0
- package/src/core/tools/triggers.ts +147 -0
- package/src/core/tools/types.ts +7 -1
- package/src/core/triggers.ts +640 -0
- package/src/core/types.ts +135 -10
- package/src/frontend/discord/actions.ts +729 -0
- package/src/frontend/discord/admin.ts +208 -0
- package/src/frontend/discord/callbacks.ts +589 -0
- package/src/frontend/discord/commands.ts +1036 -0
- package/src/frontend/discord/errors.ts +145 -0
- package/src/frontend/discord/formatting.ts +101 -0
- package/src/frontend/discord/handlers.ts +798 -0
- package/src/frontend/discord/helpers.ts +188 -0
- package/src/frontend/discord/index.ts +360 -0
- package/src/frontend/discord/middleware.ts +83 -0
- package/src/frontend/telegram/callbacks.ts +215 -81
- package/src/frontend/telegram/commands.ts +34 -53
- package/src/frontend/telegram/formatting.ts +5 -2
- package/src/frontend/telegram/handlers.ts +15 -2
- package/src/frontend/telegram/helpers.ts +224 -2
- package/src/frontend/telegram/model-callbacks.ts +98 -0
- package/src/index.ts +9 -0
- package/src/storage/chat-settings.ts +19 -0
- package/src/storage/cron-store.ts +1 -0
- package/src/storage/history.ts +2 -5
- package/src/storage/trigger-store.ts +373 -0
- package/src/util/config.ts +102 -3
- package/src/util/log.ts +2 -0
- package/src/util/mcp-launcher.mjs +70 -2
- package/src/util/mcp-launcher.ts +14 -2
- package/src/util/paths.ts +4 -0
- package/src/util/respawn.ts +74 -0
package/README.md
CHANGED
|
@@ -2,25 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://nodejs.org)
|
|
4
4
|
[](https://www.typescriptlang.org/)
|
|
5
|
-
[](#backends)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](https://github.com/dylanneve1/talon/actions/workflows/ci.yml)
|
|
8
8
|
|
|
9
|
-
Multi-platform agentic AI harness
|
|
9
|
+
Multi-platform agentic AI harness. Runs on **Telegram**, **Discord**, **Microsoft Teams**, and the **Terminal**, with a pluggable backend (**Claude Agent SDK**, **Kilo**, **OpenCode**, or **Codex**) and full tool access through MCP.
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
14
|
|
|
15
|
-
| |
|
|
16
|
-
| --------------------- |
|
|
17
|
-
| **Multi-frontend** | Telegram (Grammy + GramJS userbot), Microsoft Teams (Bot Framework), Terminal with live tool visibility
|
|
18
|
-
| **Claude Agent SDK
|
|
19
|
-
| **MCP tools** | Messaging, media, history, search, web fetch, cron jobs, stickers, file system, admin controls
|
|
20
|
-
| **Plugins** | Hot-reloadable plugin system. Built-in: GitHub, MemPalace, Playwright, Brave Search
|
|
21
|
-
| **Background agents** | Heartbeat (periodic maintenance) and Dream (memory consolidation + diary)
|
|
22
|
-
| **
|
|
23
|
-
| **
|
|
15
|
+
| | |
|
|
16
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| **Multi-frontend** | Telegram (Grammy + GramJS userbot), Discord (discord.js), Microsoft Teams (Bot Framework), Terminal with live tool visibility |
|
|
18
|
+
| **Pluggable backend** | Claude Agent SDK, Kilo, OpenCode, Codex — selectable per-process via `backend` config. Streaming, model fallback, context-overflow recovery. |
|
|
19
|
+
| **MCP tools** | Messaging, media, history, search, web fetch, cron jobs, triggers, stickers, file system, admin controls |
|
|
20
|
+
| **Plugins** | Hot-reloadable plugin system. Built-in: GitHub, MemPalace, Playwright, Brave Search |
|
|
21
|
+
| **Background agents** | Heartbeat (periodic maintenance) and Dream (memory consolidation + diary) — backend-agnostic |
|
|
22
|
+
| **Triggers** | Self-authored watcher scripts (bash/python/node) that wake the bot when conditions are met |
|
|
23
|
+
| **Per-chat settings** | Model, effort level, and pulse toggle per conversation via inline keyboard |
|
|
24
|
+
| **Model registry** | Models discovered from the active backend at startup — new models appear in all pickers automatically |
|
|
24
25
|
|
|
25
26
|
---
|
|
26
27
|
|
|
@@ -41,7 +42,11 @@ npx talon chat # terminal chat mode
|
|
|
41
42
|
**Prerequisites:**
|
|
42
43
|
|
|
43
44
|
- [Node.js 22+](https://nodejs.org/)
|
|
44
|
-
-
|
|
45
|
+
- Backend-specific:
|
|
46
|
+
- `claude` backend: [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed and authenticated (`claude` CLI on PATH).
|
|
47
|
+
- `kilo` backend: nothing extra — `@kilocode/sdk` spawns a local server. Free models are accessible without auth; routed models use Kilo's own credentials.
|
|
48
|
+
- `opencode` backend: nothing extra — `@opencode-ai/sdk` spawns a local server.
|
|
49
|
+
- `codex` backend: install the `codex` CLI (`npm i -g @openai/codex`) and authenticate with `codex login` (ChatGPT auth or `OPENAI_API_KEY`).
|
|
45
50
|
- Talon runs from a normal source or package install; standalone compiled binaries are not supported.
|
|
46
51
|
|
|
47
52
|
---
|
|
@@ -52,7 +57,7 @@ npx talon chat # terminal chat mode
|
|
|
52
57
|
index.ts Composition root
|
|
53
58
|
|
|
|
54
59
|
+-- core/ Platform-agnostic engine
|
|
55
|
-
| +-- models.ts Model registry (dynamic
|
|
60
|
+
| +-- models.ts Model registry (dynamic backend discovery)
|
|
56
61
|
| +-- gateway.ts HTTP bridge for MCP tool calls
|
|
57
62
|
| +-- dispatcher.ts Per-chat serial, cross-chat parallel execution
|
|
58
63
|
| +-- plugin.ts Plugin loader, registry, hot-reload
|
|
@@ -60,15 +65,23 @@ index.ts Composition root
|
|
|
60
65
|
| +-- dream.ts Memory consolidation agent
|
|
61
66
|
| +-- pulse.ts Conversation-aware group engagement
|
|
62
67
|
| +-- cron.ts Persistent scheduled jobs
|
|
63
|
-
| +--
|
|
68
|
+
| +-- triggers.ts Self-authored watcher scripts
|
|
69
|
+
| +-- tools/ MCP tool definitions
|
|
64
70
|
|
|
|
65
71
|
+-- backend/
|
|
66
|
-
| +--
|
|
67
|
-
|
|
|
68
|
-
|
|
|
72
|
+
| +-- registry.ts Bootstrap-decoupled backend lookup
|
|
73
|
+
| +-- shared/ Cross-backend helpers (stream state, flow violation,
|
|
74
|
+
| | prompt format, model retry, system prompt, usage)
|
|
75
|
+
| +-- remote-server/ Shared infrastructure for agent-server backends
|
|
76
|
+
| | (MCP registration, sessions, providers, lifecycle)
|
|
77
|
+
| +-- claude-sdk/ Claude Agent SDK (in-process MCP, hooks)
|
|
78
|
+
| +-- kilo/ Kilo HTTP server backend (streaming via SSE)
|
|
79
|
+
| +-- opencode/ OpenCode HTTP server backend
|
|
80
|
+
| +-- codex/ Codex CLI backend (`@openai/codex-sdk`)
|
|
69
81
|
|
|
|
70
82
|
+-- frontend/
|
|
71
|
-
| +-- telegram/ Grammy bot + GramJS userbot
|
|
83
|
+
| +-- telegram/ Grammy bot + GramJS userbot
|
|
84
|
+
| +-- discord/ discord.js v14
|
|
72
85
|
| +-- teams/ Bot Framework + Graph API
|
|
73
86
|
| +-- terminal/ Readline CLI with tool call visibility
|
|
74
87
|
|
|
|
@@ -77,7 +90,22 @@ index.ts Composition root
|
|
|
77
90
|
+-- util/ Config, logging, workspace, paths, time
|
|
78
91
|
```
|
|
79
92
|
|
|
80
|
-
**Dependency rule:** `core/` imports nothing from `frontend/` or `backend/`. Frontends and backends depend on core types, never on each other.
|
|
93
|
+
**Dependency rule:** `core/` imports nothing from `frontend/` or `backend/`. Frontends and backends depend on core types, never on each other. All four backends (Claude SDK, Kilo, OpenCode, Codex) implement the same `QueryBackend` interface in `core/types.ts`. Kilo and OpenCode additionally share the `remote-server/` infrastructure because they wrap forks of the same upstream HTTP agent server.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Backends
|
|
98
|
+
|
|
99
|
+
Select via the `backend` field in `~/.talon/config.json`. All backends implement the same `QueryBackend` interface — heartbeat, dream, and chat handlers are backend-agnostic.
|
|
100
|
+
|
|
101
|
+
| Backend | `backend` value | Transport | Notes |
|
|
102
|
+
| ---------- | --------------- | ------------------------------------------------------ | ----------------------------------------------------------------------- |
|
|
103
|
+
| Claude SDK | `"claude"` | In-process via `@anthropic-ai/claude-agent-sdk` | Requires the `claude` CLI on `PATH`. Hook-based turn termination. |
|
|
104
|
+
| Kilo | `"kilo"` | Local HTTP server via `@kilocode/sdk` | SSE-streamed turns. Routes to many model providers via Kilo's auth. |
|
|
105
|
+
| OpenCode | `"opencode"` | Local HTTP server via `@opencode-ai/sdk` | SSE-streamed turns; same MCP and session shape as Kilo (upstream fork). |
|
|
106
|
+
| Codex | `"codex"` | Per-turn subprocess via `@openai/codex-sdk` | Requires the `codex` CLI from `@openai/codex` and an OpenAI API key (or ChatGPT auth). MCP servers configured via TOML overrides at thread start. |
|
|
107
|
+
|
|
108
|
+
The Kilo and OpenCode backends share infrastructure (`backend/remote-server/`) since the upstream HTTP API is the same; each backend supplies its own SDK client, port, and delivery suffix. Codex is its own integration on top of the Codex CLI's JSONL event stream.
|
|
81
109
|
|
|
82
110
|
---
|
|
83
111
|
|
|
@@ -216,25 +244,25 @@ talon doctor Validate environment and dependencies
|
|
|
216
244
|
|
|
217
245
|
Config file: `~/.talon/config.json`
|
|
218
246
|
|
|
219
|
-
| Field | Default | Description
|
|
220
|
-
| -------------------------- | ------------ |
|
|
221
|
-
| `frontend` | `"telegram"` | `"telegram"`, `"
|
|
222
|
-
| `backend` | `"claude"` | `"claude"` or `"
|
|
223
|
-
| `botToken` | --- | Telegram bot token
|
|
224
|
-
| `model` | `"default"` | Default
|
|
225
|
-
| `concurrency` | `1` | Max concurrent AI queries (1--20)
|
|
226
|
-
| `pulse` | `true` | Periodic group engagement
|
|
227
|
-
| `heartbeat` | `false` | Background maintenance agent
|
|
228
|
-
| `heartbeatIntervalMinutes` | `60` | Heartbeat interval
|
|
229
|
-
| `braveApiKey` | --- | Brave Search API key
|
|
230
|
-
| `timezone` | --- | IANA timezone (e.g. `"Europe/London"`)
|
|
231
|
-
| `plugins` | `[]` | External plugin packages
|
|
232
|
-
| `adminUserId` | --- | Telegram user ID for `/admin` commands
|
|
233
|
-
| `allowedUsers` | --- | Whitelist of Telegram user IDs
|
|
234
|
-
| `apiId` / `apiHash` | --- | Telegram API credentials for full message history
|
|
235
|
-
| `github` | --- | GitHub plugin config (see above)
|
|
236
|
-
| `mempalace` | --- | MemPalace plugin config (see above)
|
|
237
|
-
| `playwright` | --- | Playwright plugin config (see above)
|
|
247
|
+
| Field | Default | Description |
|
|
248
|
+
| -------------------------- | ------------ | --------------------------------------------------------------- |
|
|
249
|
+
| `frontend` | `"telegram"` | `"telegram"`, `"discord"`, `"teams"`, `"terminal"`, or an array |
|
|
250
|
+
| `backend` | `"claude"` | `"claude"`, `"kilo"`, `"opencode"`, or `"codex"` |
|
|
251
|
+
| `botToken` | --- | Telegram bot token |
|
|
252
|
+
| `model` | `"default"` | Default model. Interpretation depends on the active backend. |
|
|
253
|
+
| `concurrency` | `1` | Max concurrent AI queries (1--20) |
|
|
254
|
+
| `pulse` | `true` | Periodic group engagement |
|
|
255
|
+
| `heartbeat` | `false` | Background maintenance agent |
|
|
256
|
+
| `heartbeatIntervalMinutes` | `60` | Heartbeat interval |
|
|
257
|
+
| `braveApiKey` | --- | Brave Search API key |
|
|
258
|
+
| `timezone` | --- | IANA timezone (e.g. `"Europe/London"`) |
|
|
259
|
+
| `plugins` | `[]` | External plugin packages |
|
|
260
|
+
| `adminUserId` | --- | Telegram user ID for `/admin` commands |
|
|
261
|
+
| `allowedUsers` | --- | Whitelist of Telegram user IDs |
|
|
262
|
+
| `apiId` / `apiHash` | --- | Telegram API credentials for full message history |
|
|
263
|
+
| `github` | --- | GitHub plugin config (see above) |
|
|
264
|
+
| `mempalace` | --- | MemPalace plugin config (see above) |
|
|
265
|
+
| `playwright` | --- | Playwright plugin config (see above) |
|
|
238
266
|
|
|
239
267
|
---
|
|
240
268
|
|
|
@@ -258,7 +286,7 @@ Commands: `/model`, `/effort`, `/reset`, `/status`, `/help`
|
|
|
258
286
|
docker compose up -d
|
|
259
287
|
```
|
|
260
288
|
|
|
261
|
-
**Systemd:** `talon.service`
|
|
289
|
+
**Systemd:** unit file at `packaging/systemd/talon.service` — copy to `/etc/systemd/system/`, set `User=` and `WorkingDirectory=`, then `systemctl enable --now talon`.
|
|
262
290
|
|
|
263
291
|
**Health endpoint:** `GET http://localhost:19876/health` returns JSON with uptime, memory, queue depth, active sessions, and last activity timestamp.
|
|
264
292
|
|
|
@@ -272,10 +300,11 @@ docker compose up -d
|
|
|
272
300
|
|
|
273
301
|
```bash
|
|
274
302
|
npm run dev # watch mode
|
|
275
|
-
npm test #
|
|
303
|
+
npm test # 2300+ tests across unit / SDK-stub / MCP-functional / integration tiers
|
|
276
304
|
npm run test:coverage # with coverage report
|
|
277
305
|
npm run typecheck # tsc --noEmit
|
|
278
306
|
npm run lint # oxlint
|
|
307
|
+
npm run format # prettier --write
|
|
279
308
|
```
|
|
280
309
|
|
|
281
310
|
---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "talon-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "Multi-frontend AI agent with full tool access, streaming, cron jobs, and plugin system",
|
|
5
5
|
"author": "Dylan Neve",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,6 +54,10 @@
|
|
|
54
54
|
"test:functional": "vitest run --reporter=verbose --reporter=json --outputFile=functional-results.json src/__tests__/package.functional.test.ts src/__tests__/tool-functional.test.ts src/__tests__/mcp-launcher.test.ts src/__tests__/mcp-launcher-functional.test.ts src/__tests__/integration/sdk-stub.test.ts src/__tests__/integration/talon-functional.test.ts",
|
|
55
55
|
"test:integration": "vitest run --reporter=verbose --reporter=json --outputFile=integration-results.json src/__tests__/integration/talon-mcp-functional.test.ts",
|
|
56
56
|
"test:integration:all": "vitest run --reporter=verbose src/__tests__/integration/",
|
|
57
|
+
"test:claude:backend": "vitest run --reporter=verbose --reporter=json --outputFile=claude-backend-results.json src/__tests__/integration/claude-live-discovery.test.ts",
|
|
58
|
+
"test:kilo:backend": "vitest run --reporter=verbose --reporter=json --outputFile=kilo-backend-results.json src/__tests__/integration/kilo-live-discovery.test.ts",
|
|
59
|
+
"test:opencode:backend": "vitest run --reporter=verbose --reporter=json --outputFile=opencode-backend-results.json src/__tests__/integration/opencode-live-discovery.test.ts",
|
|
60
|
+
"test:codex:backend": "vitest run --reporter=verbose --reporter=json --outputFile=codex-backend-results.json src/__tests__/integration/codex-live-discovery.test.ts",
|
|
57
61
|
"tarball:check": "node .github/scripts/tarball-check.mjs",
|
|
58
62
|
"build:stub-sea": "node src/__tests__/integration/stub-claude/build-sea.mjs",
|
|
59
63
|
"test:watch": "vitest",
|
|
@@ -72,12 +76,16 @@
|
|
|
72
76
|
"@clack/prompts": "^1.2.0",
|
|
73
77
|
"@grammyjs/auto-retry": "^2.0.2",
|
|
74
78
|
"@grammyjs/transformer-throttler": "^1.2.1",
|
|
79
|
+
"@kilocode/sdk": "^7.2.22",
|
|
75
80
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
81
|
+
"@openai/agents": "^0.11.4",
|
|
82
|
+
"@openai/codex-sdk": "^0.130.0",
|
|
76
83
|
"@opencode-ai/sdk": "^1.4.0",
|
|
77
84
|
"@playwright/mcp": "^0.0.75",
|
|
78
85
|
"big-integer": "^1.6.52",
|
|
79
86
|
"cheerio": "^1.2.0",
|
|
80
87
|
"croner": "^10.0.1",
|
|
88
|
+
"discord.js": "^14.16.3",
|
|
81
89
|
"grammy": "^1.42.0",
|
|
82
90
|
"marked": "^18.0.0",
|
|
83
91
|
"p-retry": "^8.0.0",
|
package/prompts/base.md
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
Be concise and direct. No filler. Answer directly.
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Tools
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
Only the tools the runtime registers for this turn are usable — the
|
|
6
|
+
list is attached to this prompt by the backend. Do not invent or
|
|
7
|
+
guess tool names from prior Talon configurations, other agents, or
|
|
8
|
+
typical AI tooling vocabularies; if a name isn't in the registered
|
|
9
|
+
list, calling it will fail the turn.
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
When a tool that does what you need isn't present, fall back to
|
|
12
|
+
plain conversation. Don't pretend to perform actions (reading a
|
|
13
|
+
file, running a command, browsing the web) you have no tool for —
|
|
14
|
+
say so plainly instead, and ask the user if you're unsure.
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
Workspace artifacts, when persistable for this backend, live under
|
|
17
|
+
`~/.talon/workspace/`.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
## Discord Mode
|
|
2
|
+
|
|
3
|
+
In servers (guilds), you'll see messages prefixed with [Name]: — use their name naturally. In DMs, just one user.
|
|
4
|
+
|
|
5
|
+
### CRITICAL: Message delivery
|
|
6
|
+
|
|
7
|
+
ALL messages to the user MUST be sent using the `send` tool. Your plain text output is **private** — the user never sees it, only you. Think of it as an internal scratchpad: jot a brief note to yourself if useful (a sentence or two — what you did, what you noticed, a reminder), but keep it short since nobody reads it. The only way to reach the user is the `send` tool.
|
|
8
|
+
|
|
9
|
+
### The `send` tool
|
|
10
|
+
|
|
11
|
+
One tool for everything. Set `type` to choose what to send:
|
|
12
|
+
|
|
13
|
+
- `send(type="text", text="Hello!")` — send a message
|
|
14
|
+
- `send(type="text", text="Hey", reply_to="123456789012345678")` — reply to a specific message (Discord IDs are strings)
|
|
15
|
+
- `send(type="text", text="Pick", buttons=[[{"text":"A","callback_data":"a","style":"primary"}]])` — with buttons
|
|
16
|
+
- `send(type="text", text="Reminder", delay_seconds=60)` — schedule for later
|
|
17
|
+
- `send(type="photo", file_path="img.jpg", caption="Look!")` — send an image
|
|
18
|
+
- `send(type="file", file_path="report.pdf")` — send a document
|
|
19
|
+
- `send(type="video", file_path="clip.mp4")` — send a video
|
|
20
|
+
- `send(type="voice", file_path="audio.ogg")` — send an audio attachment
|
|
21
|
+
- `send(type="poll", question="Best?", options=["A","B","C"])` — create a poll
|
|
22
|
+
- `send(type="dice")` — roll dice
|
|
23
|
+
- `send(type="location", latitude=37.77, longitude=-122.42)` — share a Google Maps location link
|
|
24
|
+
- `send(type="contact", phone_number="+1234", first_name="John")` — share a contact card
|
|
25
|
+
|
|
26
|
+
ALL types support `reply_to` to reply to a specific message.
|
|
27
|
+
|
|
28
|
+
### Discord-specific
|
|
29
|
+
|
|
30
|
+
- **IDs are strings** — Discord uses snowflakes (17–20 digits). Treat them as opaque strings, not numbers.
|
|
31
|
+
- **Buttons:** the `style` field accepts `"primary"`, `"secondary"`, `"success"`, `"danger"`. URL buttons use `url` instead of `callback_data`.
|
|
32
|
+
- **Markdown is native:** `**bold**`, `*italic*`, `` `code` ``, ` ```fenced``` `, `# headings`, `> quotes`, `||spoilers||`, `[links](url)`. Discord renders these without translation.
|
|
33
|
+
- **Mentions:** the bot is configured to suppress all mentions (`@everyone`, `@here`, role/user pings) so you can't accidentally ping anyone. Don't worry about escaping.
|
|
34
|
+
- **Message limit:** 2000 chars per message. Long messages are auto-chunked at paragraph breaks.
|
|
35
|
+
|
|
36
|
+
### Other tools
|
|
37
|
+
|
|
38
|
+
- `react(message_id, emoji)` — react to a message (unicode emoji only on Discord; custom emojis need `<:name:id>` format)
|
|
39
|
+
- `edit_message(message_id, text)` — edit a sent message (max 2000 chars)
|
|
40
|
+
- `delete_message(message_id)` — delete a message
|
|
41
|
+
- `pin_message(message_id)` / `unpin_message()` — pin/unpin
|
|
42
|
+
- `read_chat_history(limit)` — read past messages from this channel
|
|
43
|
+
- `search_chat_history(query)` — search recent messages by keyword
|
|
44
|
+
- `list_chat_members()` — list members in this server (guild only)
|
|
45
|
+
- `get_member_info(user_id)` — detailed user info
|
|
46
|
+
- `online_count()` — approximate online member count
|
|
47
|
+
|
|
48
|
+
### Message IDs
|
|
49
|
+
|
|
50
|
+
The user's message ID is in the prompt as msg_id:N (Discord snowflake string). Use with `reply_to` and `react`.
|
|
51
|
+
|
|
52
|
+
### Choosing not to respond
|
|
53
|
+
|
|
54
|
+
You don't HAVE to respond to every message. If a message doesn't need a response:
|
|
55
|
+
|
|
56
|
+
- React with an emoji using the `react` tool — preferred way to acknowledge without replying.
|
|
57
|
+
- Or simply don't call `send` and skip it entirely.
|
|
58
|
+
- In servers, prefer reactions over replies for simple acknowledgements.
|
|
59
|
+
|
|
60
|
+
### Reactions
|
|
61
|
+
|
|
62
|
+
Use naturally: 👍 ❤️ 🔥 😂 🎉 👀 💯. React AND reply when both feel right.
|
|
63
|
+
|
|
64
|
+
### Buttons & Components
|
|
65
|
+
|
|
66
|
+
When a user presses a button, you'll receive "[Button pressed]" with the custom_id. Buttons can also be a select menu — those come through with the chosen value in the same format.
|
|
67
|
+
|
|
68
|
+
### File sending
|
|
69
|
+
|
|
70
|
+
- Files users send are saved to `~/.talon/workspace/uploads/`.
|
|
71
|
+
- To send files: write the file, then use `send(type="file", file_path="...")`.
|
|
72
|
+
- File limit depends on the server's boost tier: 10 MB (default), 25 MB (tier 1), 50 MB (tier 2), 100 MB (tier 3). DMs use 10 MB. Larger files get rejected with a clear error — split or upload externally.
|
|
73
|
+
- You CAN send files. NEVER say you can't.
|
|
74
|
+
|
|
75
|
+
### Servers vs DMs
|
|
76
|
+
|
|
77
|
+
- In servers, you only see messages where you're @mentioned or replied to (default), or any message in a configured channel (alt mode). Outside that, the conversation is happening without you.
|
|
78
|
+
- In DMs, you see everything — but only allowed users can DM you in the first place.
|
|
79
|
+
|
|
80
|
+
### Style
|
|
81
|
+
|
|
82
|
+
- Concise. No filler.
|
|
83
|
+
- Discord markdown renders natively — use it.
|
|
84
|
+
- In servers, use names naturally.
|
package/prompts/identity.md
CHANGED
|
@@ -9,20 +9,20 @@
|
|
|
9
9
|
|
|
10
10
|
## Core
|
|
11
11
|
|
|
12
|
-
- You're
|
|
13
|
-
- You have tools to interact with your current platform directly (send messages, react, etc.)
|
|
12
|
+
- You're a Talon agent. The model and tools available to you depend on the active backend — only the tools listed below this prompt actually exist for this run.
|
|
13
|
+
- You have tools to interact with your current platform directly (send messages, react, etc.) — those are always provided by the frontend.
|
|
14
14
|
|
|
15
15
|
## Identity Bootstrap
|
|
16
16
|
|
|
17
|
-
Your identity is
|
|
17
|
+
Your identity is stored at `~/.talon/workspace/identity.md`. If a filesystem-capable tool is listed below, open that file to see who you are; if not, treat the identity content already inlined into this prompt (or absent) as authoritative and proceed.
|
|
18
18
|
|
|
19
|
-
If the identity file is empty or only contains
|
|
19
|
+
If the identity file is empty or only contains template comments, you MUST ask the user during your first interaction:
|
|
20
20
|
|
|
21
21
|
- What should I be called?
|
|
22
22
|
- Who are you / who created me?
|
|
23
23
|
- What will I be used for?
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
When a filesystem-capable tool is available, persist the answers to `~/.talon/workspace/identity.md`. When it isn't, just remember the answers within the conversation and apply them. Keep identity content concise — key facts only.
|
|
26
26
|
|
|
27
27
|
## Guidelines
|
|
28
28
|
|
|
@@ -36,7 +36,7 @@ Write the answers to `~/.talon/workspace/identity.md` using the Write tool. Keep
|
|
|
36
36
|
|
|
37
37
|
## Memory Management
|
|
38
38
|
|
|
39
|
-
When you learn important new information during a conversation,
|
|
39
|
+
When you learn important new information during a conversation, persist it to your memory file at `~/.talon/workspace/memory/memory.md` — only when a filesystem-capable tool is available for this backend. When no such tool is available, keep the information in working memory for the current conversation and don't pretend to save anything you can't actually save. Things worth remembering:
|
|
40
40
|
|
|
41
41
|
- **User preferences**: communication style, interests, timezone, language, how they like to be addressed
|
|
42
42
|
- **Important facts**: names, roles, relationships between users, projects they're working on
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude SDK backend factory — wires the Anthropic Claude Agent SDK
|
|
3
|
+
* into the registry.
|
|
4
|
+
*
|
|
5
|
+
* Unlike Kilo/OpenCode (which run a local HTTP server), the Claude SDK
|
|
6
|
+
* spawns a per-query subprocess. So this factory also wires the
|
|
7
|
+
* `refreshMcpServers` hot-swap path used by plugin reload + the
|
|
8
|
+
* `evictOrphanSubprocesses` cleanup helper.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { registerBackend } from "../registry.js";
|
|
12
|
+
import type { BackendFactory } from "../registry.js";
|
|
13
|
+
import type { QueryBackend } from "../../core/types.js";
|
|
14
|
+
import { log } from "../../util/log.js";
|
|
15
|
+
import { getPluginMcpServers } from "../../core/plugin.js";
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
initAgent as initClaudeAgent,
|
|
19
|
+
updateSystemPrompt as claudeUpdateSystemPrompt,
|
|
20
|
+
handleMessage as claudeHandleMessage,
|
|
21
|
+
warmSession as claudeWarmSession,
|
|
22
|
+
getActiveQuery,
|
|
23
|
+
buildMcpServers,
|
|
24
|
+
runOneShotAgent as claudeRunOneShotAgent,
|
|
25
|
+
evictOrphanSubprocesses as claudeEvictOrphanSubprocesses,
|
|
26
|
+
} from "./index.js";
|
|
27
|
+
|
|
28
|
+
import * as modelProvider from "./model-provider.js";
|
|
29
|
+
|
|
30
|
+
// ── Factory ────────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
const claudeSdkFactory: BackendFactory = {
|
|
33
|
+
// The config schema uses `"claude"` for backward compatibility with
|
|
34
|
+
// talon.json files predating the registry. Matching the id here means
|
|
35
|
+
// no migration is needed.
|
|
36
|
+
id: "claude",
|
|
37
|
+
label: "Anthropic",
|
|
38
|
+
|
|
39
|
+
async init(config, ctx) {
|
|
40
|
+
await initClaudeAgent(config, ctx.getBridgePort);
|
|
41
|
+
log("bot", "Backend: Claude SDK (@anthropic-ai/claude-agent-sdk)");
|
|
42
|
+
|
|
43
|
+
const backend: QueryBackend = {
|
|
44
|
+
query: (params) => claudeHandleMessage(params),
|
|
45
|
+
warmSession: (chatId) => claudeWarmSession(chatId),
|
|
46
|
+
updateSystemPrompt: (prompt) => claudeUpdateSystemPrompt(prompt),
|
|
47
|
+
resolveModel: (q) => modelProvider.resolveModel(q),
|
|
48
|
+
getModelInfo: (id) => modelProvider.getModelInfo(id),
|
|
49
|
+
getSettingsPresentation: (m, options) =>
|
|
50
|
+
modelProvider.getSettingsPresentation(m, options),
|
|
51
|
+
getProviders: () => modelProvider.getProviders(),
|
|
52
|
+
getProviderModels: (p, pg, ps) =>
|
|
53
|
+
modelProvider.getProviderModels(p, pg, ps),
|
|
54
|
+
formatModelError: (q, r) => modelProvider.formatModelError(q, r),
|
|
55
|
+
listModels: (f) => modelProvider.listModels(f),
|
|
56
|
+
backendLabel: "Anthropic",
|
|
57
|
+
refreshMcpServers: async (chatId) => {
|
|
58
|
+
const qi = getActiveQuery(chatId);
|
|
59
|
+
if (!qi) return null;
|
|
60
|
+
// Two-phase teardown: remove all MCP servers first so each
|
|
61
|
+
// subprocess receives an OS-agnostic shutdown via stdio, then
|
|
62
|
+
// install the fresh set.
|
|
63
|
+
await qi.setMcpServers({});
|
|
64
|
+
const bridgeUrl = `http://127.0.0.1:${ctx.getBridgePort()}`;
|
|
65
|
+
const freshServers = {
|
|
66
|
+
...buildMcpServers(chatId),
|
|
67
|
+
...getPluginMcpServers(bridgeUrl, chatId),
|
|
68
|
+
};
|
|
69
|
+
return qi.setMcpServers(freshServers);
|
|
70
|
+
},
|
|
71
|
+
runOneShotAgent: (p) => claudeRunOneShotAgent(p),
|
|
72
|
+
evictOrphanSubprocesses: (label) => claudeEvictOrphanSubprocesses(label),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return { backend };
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
registerBackend(claudeSdkFactory);
|