You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Features
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).
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.
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.
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).
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.
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.
Other
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.