Skip to content

v0.5.0

Choose a tag to compare

@shuaiyuan17 shuaiyuan17 released this 26 Apr 18:45
· 264 commits to main since this release
08ded0c

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.