tomo-ai 0.4.2 → 0.5.1
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/CHANGELOG.md +26 -0
- package/README.md +20 -3
- package/defaults/AGENT.md +9 -0
- package/defaults/skills/system/CONFIG.md +14 -2
- package/defaults/skills/system/SKILL.md +20 -2
- package/dist/agent.d.ts +77 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +421 -47
- package/dist/agent.js.map +1 -1
- package/dist/channels/imessage.d.ts.map +1 -1
- package/dist/channels/imessage.js +16 -4
- package/dist/channels/imessage.js.map +1 -1
- package/dist/channels/telegram.d.ts.map +1 -1
- package/dist/channels/telegram.js +38 -5
- package/dist/channels/telegram.js.map +1 -1
- package/dist/channels/types.d.ts +6 -0
- package/dist/channels/types.d.ts.map +1 -1
- package/dist/cli.js +7 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +26 -0
- package/dist/config.js.map +1 -1
- package/dist/mcp/internal-server.d.ts +12 -0
- package/dist/mcp/internal-server.d.ts.map +1 -0
- package/dist/mcp/internal-server.js +76 -0
- package/dist/mcp/internal-server.js.map +1 -0
- package/dist/sessions/store.d.ts +4 -0
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +19 -0
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/types.d.ts +4 -0
- package/dist/sessions/types.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.1 (2026-04-26)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- **Coalesce queued messages into one turn** (#67). When multiple DMs (or messages in a passive group) arrive while a turn is in flight, they merge into a single follow-up SDK turn instead of firing one turn per message. Lets the agent see `"do X"` → `"wait"` → `"actually nevermind"` together and skip wasted work on retracted requests. Mention-required groups bypass coalescing because per-message mention filtering would be lost. Passive group batches use sender-prefixed lines (`Alice: ...` / `Bob: ...`) plus a header noting the messages came from a group. Each user message still appends individually to the on-disk transcript; only the SDK prompt is merged.
|
|
8
|
+
|
|
9
|
+
### Bug fixes
|
|
10
|
+
|
|
11
|
+
- **Telegram NO_REPLY no longer leaks via the streaming first frame** (#66). The streaming placeholder was sent before the model finished, so a NO_REPLY response left an empty/partial message in the chat. `StreamingMessage` now exposes `cancel()`, called in the NO_REPLY path (and the new batched path) after the SDK turn completes — cancels the placeholder before any visible content lands.
|
|
12
|
+
- **Channel ingress was serializing against the SDK turn** (#67). grammy's `bot.on(...)` awaits its handler body before reading the next update, and the iMessage dispatch did the same — both then awaited `enqueueMessage`'s task-completion promise, so the channel layer waited a full SDK turn before delivering the next message. Result: messages never piled up in the queue, and the new coalescing was effectively dead in production. Three layers of fix (defense-in-depth): `telegram.ts` and `imessage.ts` dispatch fire handlers without awaiting; `agent.enqueueMessage` returns once queued, not when the turn completes. Regression test models the serial channel-loop pattern.
|
|
13
|
+
|
|
14
|
+
## 0.5.0 (2026-04-26)
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
- **Proactive messaging via `send_message` MCP tool** (#64). New in-process MCP server (`tomo-internal`) exposes `send_message(target, message, mode)` and `list_sessions()`. Two modes: `delegate` (default) hands the request to the recipient session's Claude as a system message — that Claude composes the actual message in its own voice with full local context (participant names, recent conversation, group tone); fire-and-forget via the existing `handleCronMessage` primitive, the user observes the outcome directly in the recipient channel. `direct` posts verbatim text via `Channel.send()` without triggering a recipient Claude turn — best for factual broadcasts and self-targeted mid-loop progress updates. `list_sessions` returns identities and active groups with `chatTitle` + `participants` metadata, both now persisted on `SessionEntry` in `_sessions.json` (existing group entries populate on next group activity).
|
|
19
|
+
- **Configurable `maxTurns`, default raised to 50** (#64). The Agent SDK `maxTurns` ceiling (one turn ≈ one tool-use round) is now read from `config.maxTurns` instead of being hardcoded to 30. Override via `maxTurns` in `~/.tomo/config.json` or `TOMO_MAX_TURNS` env.
|
|
20
|
+
- **`canUseTool` callback grants writes under `<workspaceDir>/.claude/skills/`** (#64). The SDK's `bypassPermissions` mode does not actually exempt `.claude/` writes despite the docs implying it does, so creating/editing skills via Edit/Write hung on a permission prompt with no UI to approve. A narrow callback now auto-approves writes under the workspace's `.claude/skills/**` (Write/Edit/MultiEdit/NotebookEdit, plus Bash commands targeting that path); everything else that reaches the callback is denied with a descriptive message.
|
|
21
|
+
- **Per-Telegram-group passive listen mode** (#64). New `channels.telegram.passiveGroups: string[]` config field accepts a list of group chatIds. In those groups, Tomo sees every message (no `@mention` required) and decides via `NO_REPLY` whether to respond — same shape as iMessage groups have always behaved. The typing-indicator skip and error-message suppression in `handleMessage` are generalized via a single `isPassiveListenGroup(channel, chatId)` helper. iMessage groups remain implicitly passive (no config needed).
|
|
22
|
+
- **Group context moved into the system prompt** (#64). The "you are in <title>, participants are X, listen mode is passive, NO_REPLY for noise" instructions previously injected as a one-time runtime turn via `updateGroupContext` are now part of the per-session system prompt block (under a new `## Group Chat Context` heading). Survives LCM compaction — earlier the rules could be summarized away, after which Tomo would start replying to passive-group chatter. `updateGroupContext` is now pure persistence (participants + title to `_sessions.json`); no more per-new-participant Claude turn cost. Snapshot of participants in the prompt is from session-creation time; new joiners are still cued by the `<sender>: <text>` message format.
|
|
23
|
+
- **Tool result events logged with originating tool name** (#64). Previously `consumeEvents` handled assistant `tool_use` blocks but silently dropped user `tool_result` blocks, making it impossible to tell from the log whether a failed tool call was harness-rejected vs the model misreading. Adds a `pendingToolNames` map (tool_use_id → name) on `LiveSession` so result lines can be labelled, plus `summarizeToolResult` truncating to a 500-char readable line. `is_error` is surfaced at INFO level so failures stand out.
|
|
24
|
+
|
|
25
|
+
### Other
|
|
26
|
+
|
|
27
|
+
- **`cli --version` now reads from `package.json` at runtime** (#64). Resolves the long-standing drift risk flagged in `0.4.1`: `src/cli.ts` previously hardcoded the version string and required a parallel update on every release bump. Now derived from `import.meta.url` → `../package.json`, so the package.json bump is the single source of truth.
|
|
28
|
+
|
|
3
29
|
## 0.4.2 (2026-04-24)
|
|
4
30
|
|
|
5
31
|
### Bug fixes
|
package/README.md
CHANGED
|
@@ -59,6 +59,7 @@ tomo sessions clear # Reset all sessions
|
|
|
59
59
|
|---------|-------------|
|
|
60
60
|
| `/new` | Start a new conversation (resets session) |
|
|
61
61
|
| `/model` | Switch model (sonnet/opus/haiku) |
|
|
62
|
+
| `/status` | Show session info (model, channel, message count) |
|
|
62
63
|
|
|
63
64
|
## Features
|
|
64
65
|
|
|
@@ -83,7 +84,7 @@ File-based persistent memory at `~/.tomo/workspace/memory/`. The `MEMORY.md` ind
|
|
|
83
84
|
- **Telegram** — DM and group chat support
|
|
84
85
|
- Typing indicators with keepalive and error backoff
|
|
85
86
|
- Image/photo support (sends to Claude as vision input)
|
|
86
|
-
- Group chat: only responds when @mentioned or replied to
|
|
87
|
+
- Group chat: defaults to mention-required (only responds when @mentioned or replied to); per-group passive listen mode opt-in via `channels.telegram.passiveGroups`
|
|
87
88
|
- Markdown rendering with plain-text fallback
|
|
88
89
|
- **iMessage** — via [BlueBubbles](https://bluebubbles.app)
|
|
89
90
|
- DM and group chat support
|
|
@@ -112,6 +113,10 @@ Tomo has access to Claude's built-in tools:
|
|
|
112
113
|
| WebSearch, WebFetch | Web access |
|
|
113
114
|
| Agent | Subagents for complex tasks |
|
|
114
115
|
| Skill | Specialized workflows |
|
|
116
|
+
| TodoWrite | Task tracking within a turn |
|
|
117
|
+
| NotebookEdit | Edit Jupyter notebooks |
|
|
118
|
+
| `send_message` (MCP) | Proactively send a message to another session/identity |
|
|
119
|
+
| `list_sessions` (MCP) | List active identities and group sessions |
|
|
115
120
|
|
|
116
121
|
### Scheduled Tasks
|
|
117
122
|
|
|
@@ -157,7 +162,11 @@ Run `tomo config` for interactive setup, or edit `~/.tomo/config.json` directly:
|
|
|
157
162
|
```json
|
|
158
163
|
{
|
|
159
164
|
"channels": {
|
|
160
|
-
"telegram": {
|
|
165
|
+
"telegram": {
|
|
166
|
+
"token": "your-bot-token",
|
|
167
|
+
"allowlist": ["123456789"],
|
|
168
|
+
"passiveGroups": ["-1001234567890"]
|
|
169
|
+
},
|
|
161
170
|
"imessage": { "url": "http://localhost:1234", "password": "...", "allowlist": ["+15551234567"] }
|
|
162
171
|
},
|
|
163
172
|
"identities": [
|
|
@@ -168,7 +177,14 @@ Run `tomo config` for interactive setup, or edit `~/.tomo/config.json` directly:
|
|
|
168
177
|
}
|
|
169
178
|
],
|
|
170
179
|
"model": "claude-sonnet-4-6",
|
|
171
|
-
"
|
|
180
|
+
"maxTurns": 50,
|
|
181
|
+
"saveInboundImages": true,
|
|
182
|
+
"groupSecret": "tomo-xxxxxxxx",
|
|
183
|
+
"lcm": {
|
|
184
|
+
"nudgeAtPct": 70,
|
|
185
|
+
"nudgeResetPct": 60,
|
|
186
|
+
"groupCompactStyle": "sdk"
|
|
187
|
+
}
|
|
172
188
|
}
|
|
173
189
|
```
|
|
174
190
|
|
|
@@ -180,6 +196,7 @@ Environment variables override config file values:
|
|
|
180
196
|
| `IMESSAGE_URL` | Override BlueBubbles URL |
|
|
181
197
|
| `CLAUDE_MODEL` | Override model |
|
|
182
198
|
| `TOMO_WORKSPACE` | Override workspace directory |
|
|
199
|
+
| `TOMO_MAX_TURNS` | Override per-turn tool-use ceiling (default: `50`) |
|
|
183
200
|
| `LOG_LEVEL` | Log level (default: `debug`) |
|
|
184
201
|
|
|
185
202
|
## Development
|
package/defaults/AGENT.md
CHANGED
|
@@ -11,3 +11,12 @@ Don't narrate your process. Don't explain what you're "about to do." Just do it
|
|
|
11
11
|
## Mistakes
|
|
12
12
|
|
|
13
13
|
You'll get things wrong. When you do: say so plainly, correct it, move on. Don't over-apologize.
|
|
14
|
+
|
|
15
|
+
## Reaching Out
|
|
16
|
+
|
|
17
|
+
You have `list_sessions` and `send_message` tools for proactively posting to another conversation — most often a group chat the user is in. Two modes:
|
|
18
|
+
|
|
19
|
+
- **`delegate` (default)**: describe the intent ("follow up with Alice about her recent trip"). The recipient session's Claude composes the actual message in its own voice and context. Use for social or contextual messages.
|
|
20
|
+
- **`direct`**: send verbatim text. Use for factual broadcasts ("meeting moved to 3pm"), pasted content, or self-targeted mid-loop progress updates.
|
|
21
|
+
|
|
22
|
+
Call `list_sessions` first if you're unsure which group to address. For normal in-conversation responses, just reply with text — don't reach for these tools.
|
|
@@ -13,7 +13,8 @@ Prefer editing via `tomo config` (interactive TUI). This reference is for readin
|
|
|
13
13
|
"channels": {
|
|
14
14
|
"telegram": {
|
|
15
15
|
"token": "123456:ABC-DEF1234ghIkl-zyx57W2v...",
|
|
16
|
-
"allowlist": ["123456789"]
|
|
16
|
+
"allowlist": ["123456789"],
|
|
17
|
+
"passiveGroups": ["-1001234567"]
|
|
17
18
|
},
|
|
18
19
|
"imessage": {
|
|
19
20
|
"url": "https://your-bluebubbles.example.com",
|
|
@@ -34,6 +35,12 @@ Prefer editing via `tomo config` (interactive TUI). This reference is for readin
|
|
|
34
35
|
],
|
|
35
36
|
"sessionModelOverrides": {
|
|
36
37
|
"dm:alice": "claude-opus-4-7"
|
|
38
|
+
},
|
|
39
|
+
"maxTurns": 50,
|
|
40
|
+
"lcm": {
|
|
41
|
+
"nudgeAtPct": 70,
|
|
42
|
+
"nudgeResetPct": 60,
|
|
43
|
+
"groupCompactStyle": "sdk"
|
|
37
44
|
}
|
|
38
45
|
}
|
|
39
46
|
```
|
|
@@ -48,6 +55,7 @@ Prefer editing via `tomo config` (interactive TUI). This reference is for readin
|
|
|
48
55
|
| `groupSecret` | string \| null | Passphrase users send in a group chat to activate Tomo there. `null` disables group chats entirely. |
|
|
49
56
|
| `channels.telegram.token` | string | BotFather token (`123456:...`). Required to enable the Telegram channel. |
|
|
50
57
|
| `channels.telegram.allowlist` | string[] | Telegram user IDs (as strings) permitted to DM the bot. Identity-bound chatIds are auto-allowed even if missing here. |
|
|
58
|
+
| `channels.telegram.passiveGroups` | string[] | Telegram group chatIds (negative IDs as strings) where Tomo should listen to every message — no `@mention` required. Tomo decides via `NO_REPLY` whether to respond. iMessage groups are always passive regardless of this list. |
|
|
51
59
|
| `channels.imessage.url` | string | BlueBubbles server URL. Required to enable the iMessage channel. |
|
|
52
60
|
| `channels.imessage.password` | string | BlueBubbles server password. |
|
|
53
61
|
| `channels.imessage.webhookPort` | number | Port Tomo listens on for BlueBubbles webhooks. Default `3100`. |
|
|
@@ -56,9 +64,13 @@ Prefer editing via `tomo config` (interactive TUI). This reference is for readin
|
|
|
56
64
|
| `identities[].channels` | object | `{ channelName: chatId }` — maps each channel the identity uses to its chatId. |
|
|
57
65
|
| `identities[].replyPolicy` | string | `"last-active"` (reply on whichever channel the identity last messaged from) or a fixed channel name like `"telegram"` / `"imessage"` (always reply there). |
|
|
58
66
|
| `sessionModelOverrides` | object | `{ sessionKey: modelId }` — per-session model override, takes precedence over top-level `model`. Keys are session keys (`dm:alice`, `telegram:12345`, etc.). |
|
|
67
|
+
| `maxTurns` | number | Max agent turns per single user message (one turn ≈ one tool-use round). Default `50`. Raise if you see "max turns exceeded" on long tool chains. |
|
|
68
|
+
| `lcm.nudgeAtPct` | number | Context-usage % at which the harness nudges the agent to run `tomo lcm daily`. Default `70`. Lower = compact earlier and more often. |
|
|
69
|
+
| `lcm.nudgeResetPct` | number | Hysteresis reset threshold — the "already nudged" flag clears once usage drops below this %. Default `60`. Must be less than `nudgeAtPct`; invalid values fall back to defaults. |
|
|
70
|
+
| `lcm.groupCompactStyle` | string | `"sdk"` (default) or `"lcm"`. `"sdk"` lets the SDK auto-compact group sessions; `"lcm"` opts groups into the same hierarchical LCM flow as DMs (disables SDK auto-compact and fires the daily/80% nudges). DMs always use LCM regardless. |
|
|
59
71
|
|
|
60
72
|
## Requirements and overrides
|
|
61
73
|
|
|
62
74
|
- **At least one channel must be configured** — either `channels.telegram.token` or `channels.imessage.url`. Startup fails otherwise.
|
|
63
|
-
- **Env vars override file values** where they exist: `TELEGRAM_BOT_TOKEN`, `IMESSAGE_URL`, `IMESSAGE_PASSWORD`, `IMESSAGE_WEBHOOK_PORT`, `CLAUDE_MODEL`, `TOMO_CITY`, `TOMO_CONTINUITY`, `TOMO_WORKSPACE`, `SESSIONS_DIR`, `HISTORY_LIMIT`.
|
|
75
|
+
- **Env vars override file values** where they exist: `TELEGRAM_BOT_TOKEN`, `IMESSAGE_URL`, `IMESSAGE_PASSWORD`, `IMESSAGE_WEBHOOK_PORT`, `CLAUDE_MODEL`, `TOMO_CITY`, `TOMO_CONTINUITY`, `TOMO_WORKSPACE`, `SESSIONS_DIR`, `HISTORY_LIMIT`, `TOMO_MAX_TURNS`.
|
|
64
76
|
- `workspaceDir`, `sessionsDir`, `historyLimit` are env-only — they're not read from the JSON file.
|
|
@@ -29,7 +29,7 @@ Session stats show:
|
|
|
29
29
|
|
|
30
30
|
**Note:** Context stats come from the SDK API and reflect the state at the end of the *previous* query. After compacting or other changes, you need to wait for a new query to complete before the numbers update.
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
When context crosses the nudge threshold (default 70%, set via `lcm.nudgeAtPct` in config.json), the harness sends a system message asking you to run `tomo lcm daily` — see the `tomo-lcm` skill. A second nudge at 80% asks you to use the `lcm compact` skill before the next user message. Group sessions default to SDK auto-compact instead (override with `lcm.groupCompactStyle: "lcm"`).
|
|
33
33
|
|
|
34
34
|
## Cron Jobs
|
|
35
35
|
|
|
@@ -76,6 +76,24 @@ Every message includes a timestamp prefix like `[Mon 04/07 14:30 PDT]` so you al
|
|
|
76
76
|
### System messages
|
|
77
77
|
Messages prefixed with `System:` are from the harness (cron triggers, group context), not from a human.
|
|
78
78
|
|
|
79
|
+
### Group chats
|
|
80
|
+
Two listen modes per group:
|
|
81
|
+
|
|
82
|
+
- **Mention-required** (default for Telegram): you only receive messages that explicitly tag you. Respond as you would in a DM.
|
|
83
|
+
- **Passive** (default for iMessage; opt-in for Telegram via `channels.telegram.passiveGroups: ["<chatId>"]` in config.json): you see every message in the group without `@mention`. Reply only when genuinely useful — `NO_REPLY` to stay silent on chatter, greetings, or messages not directed at you.
|
|
84
|
+
|
|
85
|
+
The active mode plus group title and known participants are part of your per-session system prompt under `## Group Chat Context` whenever you're in a group session — survives compaction. Each incoming group message is prefixed with the sender name (`Alice: ...`) so you can attribute it.
|
|
86
|
+
|
|
87
|
+
### Proactive messaging (MCP tools)
|
|
88
|
+
Two in-process MCP tools let you message outside the current conversation:
|
|
89
|
+
|
|
90
|
+
- `mcp__tomo-internal__list_sessions` — discover valid targets. Returns `{identities: [{name}], groups: [{key, title?, participants?}]}`. Group titles and participants populate as messages arrive in the group; an entry without them just hasn't seen activity since the schema landed.
|
|
91
|
+
- `mcp__tomo-internal__send_message(target, message, mode?)` — send to a target. Two modes:
|
|
92
|
+
- `delegate` (default): describe the *intent* (e.g. "follow up with Alice about her recent trip"). The recipient session's Claude composes the actual message in its own voice/context. Best for social or contextual relays. Fire-and-forget.
|
|
93
|
+
- `direct`: send the verbatim text. Recipient is not triggered into a Claude turn. Best for factual broadcasts ("meeting moved to 3pm"), pasted content, or self-targeted mid-loop progress updates.
|
|
94
|
+
|
|
95
|
+
Pass identity name (`"alice"`) or session key (`"telegram:-1001234567"`) as `target`. Call `list_sessions` first if unsure. Tool result lines (with `is_error` flag) appear in `tomo logs` immediately after the corresponding tool call.
|
|
96
|
+
|
|
79
97
|
## Skills
|
|
80
98
|
|
|
81
99
|
### Built-in skills (`tomo-*`)
|
|
@@ -138,7 +156,7 @@ tomo logs | grep Cron
|
|
|
138
156
|
Jobs are checked every 30 seconds. Jobs created via CLI are picked up on the next tick.
|
|
139
157
|
|
|
140
158
|
### Context window full
|
|
141
|
-
Check with `tomo sessions list`.
|
|
159
|
+
Check with `tomo sessions list`. DM sessions and any group with `lcm.groupCompactStyle: "lcm"` rely on the harness nudging you to run `tomo lcm daily` / the `lcm compact` skill — SDK auto-compact is off for them. Other groups fall back to SDK auto-compact near 100%. If stuck, `/new` starts a fresh session.
|
|
142
160
|
|
|
143
161
|
### Memory not loading
|
|
144
162
|
Memory is read from `~/.tomo/workspace/memory/MEMORY.md` at the start of every query. Check the file exists and has content:
|
package/dist/agent.d.ts
CHANGED
|
@@ -1,17 +1,45 @@
|
|
|
1
1
|
import type { Channel } from "./channels/types.js";
|
|
2
|
+
export type SendResult = {
|
|
3
|
+
ok: true;
|
|
4
|
+
} | {
|
|
5
|
+
ok: false;
|
|
6
|
+
error: string;
|
|
7
|
+
};
|
|
8
|
+
export interface SessionCatalog {
|
|
9
|
+
identities: Array<{
|
|
10
|
+
name: string;
|
|
11
|
+
}>;
|
|
12
|
+
groups: Array<{
|
|
13
|
+
key: string;
|
|
14
|
+
title?: string;
|
|
15
|
+
participants?: string[];
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
2
18
|
export declare class Agent {
|
|
3
19
|
private channels;
|
|
4
20
|
private sessions;
|
|
5
21
|
private router;
|
|
6
22
|
private liveSessions;
|
|
7
23
|
private messageQueues;
|
|
24
|
+
private pendingBatches;
|
|
8
25
|
private groupParticipants;
|
|
9
26
|
private modelOverrides;
|
|
10
27
|
private lastPromptHash;
|
|
11
28
|
private contextNudged;
|
|
29
|
+
private pendingNotes;
|
|
30
|
+
private readonly internalMcpServer;
|
|
12
31
|
constructor();
|
|
13
32
|
/** Look up a channel by name */
|
|
14
33
|
private getChannel;
|
|
34
|
+
/**
|
|
35
|
+
* Is this group a "passive listen" group? Tomo sees every message (no
|
|
36
|
+
* @mention required) and decides via NO_REPLY whether to respond.
|
|
37
|
+
* iMessage groups are always passive (the channel can't reliably detect
|
|
38
|
+
* mentions). Telegram (and others) opt in via config.passiveGroups.
|
|
39
|
+
*/
|
|
40
|
+
private isPassiveListenGroup;
|
|
41
|
+
/** Snapshot of group metadata for the system prompt — null for non-group sessions. */
|
|
42
|
+
private buildGroupContext;
|
|
15
43
|
/** Activate a group chat by adding it to the channel's allowlist */
|
|
16
44
|
private activateGroup;
|
|
17
45
|
addChannel(channel: Channel): void;
|
|
@@ -24,7 +52,15 @@ export declare class Agent {
|
|
|
24
52
|
* from being stomped by overlapping callers.
|
|
25
53
|
*/
|
|
26
54
|
private enqueueForSession;
|
|
27
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Queue messages per session key so they process sequentially. For DMs, also
|
|
57
|
+
* coalesce: messages that pile up behind an in-flight turn are merged into a
|
|
58
|
+
* single follow-up turn so the agent sees them together (e.g. "do X" → "wait"
|
|
59
|
+
* → "nevermind" all become one prompt). Passive groups (iMessage, opt-in
|
|
60
|
+
* Telegram) coalesce too — every message reaches Tomo there anyway, so
|
|
61
|
+
* batching just reduces turn count. Mention-required groups bypass
|
|
62
|
+
* coalescing because per-message mention filtering would be lost.
|
|
63
|
+
*/
|
|
28
64
|
private enqueueMessage;
|
|
29
65
|
private static readonly AVAILABLE_MODELS;
|
|
30
66
|
private handleCommand;
|
|
@@ -32,7 +68,18 @@ export declare class Agent {
|
|
|
32
68
|
private closeLiveSession;
|
|
33
69
|
private hashString;
|
|
34
70
|
private handleMessage;
|
|
71
|
+
/**
|
|
72
|
+
* Process 2+ messages that piled up behind an in-flight turn as a single
|
|
73
|
+
* follow-up turn. Handles DMs and passive groups; mention-required groups
|
|
74
|
+
* never reach this path.
|
|
75
|
+
*/
|
|
76
|
+
private handleBatchedMessages;
|
|
35
77
|
private runWithRetry;
|
|
78
|
+
/** Track participants and chat title for a group session. The actual rules
|
|
79
|
+
* (passive listen, NO_REPLY guidance, participant snapshot) are now part of
|
|
80
|
+
* the system prompt — see SessionContext.group in sdkOptions — so they
|
|
81
|
+
* survive compaction. This stays as pure persistence + in-memory tracking;
|
|
82
|
+
* no LLM injection. */
|
|
36
83
|
private updateGroupContext;
|
|
37
84
|
private injectTimestamp;
|
|
38
85
|
/** Handle a cron-triggered message (queued per session key) */
|
|
@@ -45,6 +92,35 @@ export declare class Agent {
|
|
|
45
92
|
* Skips group chats (Telegram negative IDs, iMessage group GUIDs). */
|
|
46
93
|
private parseChannelKey;
|
|
47
94
|
private findLastChatId;
|
|
95
|
+
/**
|
|
96
|
+
* Direct mode: post a verbatim message to a target session via Channel.send().
|
|
97
|
+
* No Claude query is invoked for the recipient — the message arrives as-is.
|
|
98
|
+
* A pending note is queued so the recipient's next Claude turn knows context.
|
|
99
|
+
*/
|
|
100
|
+
sendToSession(target: string, text: string): Promise<SendResult>;
|
|
101
|
+
/**
|
|
102
|
+
* Delegate mode: queue a system request for the target session's Claude to
|
|
103
|
+
* compose and send a message in its own voice/context. Fire-and-forget — the
|
|
104
|
+
* caller's tool result returns as soon as the request is dispatched, not when
|
|
105
|
+
* the recipient's Claude finishes. The user observes the actual outcome in
|
|
106
|
+
* the recipient channel directly (since they're a participant).
|
|
107
|
+
*
|
|
108
|
+
* Note: delegate-to-self isn't blocked here. If it happens, the system
|
|
109
|
+
* request is just queued behind the current turn via enqueueForSession —
|
|
110
|
+
* one extra Claude turn fires, no infinite loop. For mid-loop self-progress
|
|
111
|
+
* updates, prefer direct mode (no extra turn).
|
|
112
|
+
*/
|
|
113
|
+
delegateToSession(target: string, request: string): Promise<SendResult>;
|
|
114
|
+
/** Resolve a send_message `target` (identity name or session key) to (sessionKey, replyTarget). */
|
|
115
|
+
private resolveSendTarget;
|
|
116
|
+
/** Parse a "<channel>:<chatId>" key into a ReplyTarget. Group-friendly; for
|
|
117
|
+
* explicit-target paths only. Use parseChannelKey for notification fallbacks. */
|
|
118
|
+
private parseRawChannelKey;
|
|
119
|
+
/** Catalog of valid send_message targets, with friendly metadata for groups. Backs the `list_sessions` tool. */
|
|
120
|
+
listSessionCatalog(): SessionCatalog;
|
|
121
|
+
private queuePendingNote;
|
|
122
|
+
/** Drain notes queued for this session (e.g. by sendToSession) and return them as a prefix. */
|
|
123
|
+
private drainPendingNotes;
|
|
48
124
|
/** Send a direct notification to the user's DM channel (no agent query) */
|
|
49
125
|
sendNotification(text: string): Promise<void>;
|
|
50
126
|
start(): Promise<void>;
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAmB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|