Skip to content

fix(history, prose): one table of the tools that carry an agent's words, read by silencing, search, extract and the semantic index - #914

Open
nissa-seru wants to merge 1 commit into
anima-research:mainfrom
nissa-seru:nell/524-speech-tools
Open

nissa-seru wants to merge 1 commit into
anima-research:mainfrom
nissa-seru:nell/524-speech-tools

Conversation

@nissa-seru

Copy link
Copy Markdown
Contributor

history--search now finds what an agent said through a send tool, and history--extract's text form shows it (anima-research/connectome-host#524). Both read the words from one new table in src/send-tools.ts, and so do the semantic index and the check for which calls silence the round's auto-routed prose. The table also makes reply_to_message, telegram-mcpl's reply, silence that prose as the other sends do. Each new test fails on 84e0408977's src except the five that pin what stays as it was, and each of 32 changes that take one part out fails at least one test (Tests).

@antra-tess, we're asking you to review this before it merges, since it changes isSilencingTool and SILENCING_TOOLS (src/framework.ts:291–296), which you wrote. It adds reply_to_message to the calls that silence, builds that set from the table, and removes WORLD_PUBLICATION_TOOLS (Changes). The framework keeps its own list of send tools by name, so a send tool a server adds needs a row in the table, as telegram-mcpl's reply_to_message did here. MCPL's draft RFC-008 has a server declare a tool's class in its definition's _meta, as mcpl/class (RFC-008-tool-classes.md:40 at mcpl 8b0e57d192), and its comms class is a tool that "sends to, or reads from, other parties" (:108). So a class says neither whether a call sends nor which argument carries its words. A declaration of those two would let the framework take them from the server rather than from a row, and that would be MCPL's to decide.

Problem

@The-Glassy reported that history--search and history--extract don't see what an agent said through send tools, as an agent does with proseRouting: "disabled". This change's tests, run on 84e0408977's src, show what that src does:

  • extract's text form shows each send as its name alone, such as [tool_use: telegram--send_message], and plain search finds none of them by their words.
  • The semantic index reads nothing from telegram-mcpl's tools under their own argument names, or from Eidoverse's say and whisper. It reads a text argument of send_message, reply_message, send_dm and edit_message, which discord-mcpl's and slack-mcpl's definitions of those tools don't have (Changes).
  • A reply_to_message whose answer names no channel doesn't silence the prose beside it: the test's server publishes "Here it is." and "Sent it." beside the reply.
  • A code_execution script whose code names telegram--reply_to_message doesn't hold its round: the round is published while the script runs.

Changes

src/send-tools.ts holds one table (:12). Each row names a tool whose call carries the agent's words, by its bare name, so it matches that tool on any server. It gives the arguments that can hold the words and whether a call of the tool silences the round's auto-routed prose. The first of those arguments that is present holds the words, as content ?? text did, and they count only when they're a string that isn't blank (:42–43). SILENCING_TOOLS (src/framework.ts:291) is now built from the rows that silence, and plain search, extract's text form and the semantic index read the words of every row. Before, silencing read a list of four sends, the semantic index read those four and edit_message, and plain search read none.

Tool Words read from Silences
channel_publish content, text yes, as before
send_message content, and now message yes, as before
reply_message, send_dm content yes, as before
reply_to_message text (new row) yes (new)
send_file caption (new row) no, as before
send_scheduled_message message (new row) no, as before
create_poll question (new row) no, as before
edit_message content, and now new_text no, as before
say, whisper text (new rows) yes, as before, now through the table
  • telegram-mcpl's rows take the argument names its source gives at 4f00f6f918: send_message(message) (telegram_mcp/tools/messages.py:55), reply_to_message(text) (:948), send_scheduled_message(message) (:89), create_poll(question) (:1141), edit_message(new_text) (:716) and send_file(caption) (telegram_mcp/tools/media.py:12). A poll's options aren't read.
  • say and whisper are Eidoverse's world speech, and each takes text at eidoverse-worlds 34fd77ea20 (mcpl/tools.ts:97, :100). They already silenced, through WORLD_PUBLICATION_TOOLS, which only isSilencingTool and ROUTING_TOOLS read. Both read SILENCING_TOOLS too, so that set comes out, and say and whisper silence through their rows. A whisper's words now reach the agent's own search and semantic index, as a send_dm's do.
  • text on four sends. main's semantic index read content ?? text for send_message, reply_message, send_dm and edit_message. discord-mcpl at abc7d02e3e and slack-mcpl at 7274283fa7 define those four with content and no text (src/tools.ts, discord-mcpl's :72, :87, :100 and :142, slack-mcpl's :38, :55, :88 and :114). So the table reads content for them, and text only for channel_publish, from its older callers. A server that sent those four tools text would no longer have those words in the semantic index.
  • What doesn't silence. send_file's caption is optional, and a file sent alone carries none of the round's words, so silencing the prose would keep back words meant to go with it. A scheduled message and a poll aren't the round's speech either.
  • How a call shows. In plain search and extract, a call of a table tool that carries words reads [NAME] words, with the name as stored, a server's prefix included (src/modules/history/index.ts:2471). So extract still shows which server a send went through, and a search for telegram--send_message still finds it. Any other call, and one with no words, stays [tool_use: NAME], so a send's ids and file paths stay out. The semantic index keeps its bare label, such as [send_message] words (src/modules/history/semantic.ts:311), so the sends it has already embedded embed the same.
  • extract's description now says how such a call shows (src/modules/history/index.ts:699), so an agent with the history tools sees its tool list change once.
  • Script routing. ROUTING_TOOLS is built from SILENCING_TOOLS (src/framework.ts:354). So in locus routing a code_execution script whose code names reply_to_message now holds its round until it returns, as one that names say or whisper does.
  • Overlap with open pull requests, by git merge-tree against each one's head at 18:56 UTC on October 29, with main 612f6fd3bd and with main merged with this change: this change adds conflicts to three of them. feat(agent): declare later tool and system-text changes in the context #690 at 598f8cc58c conflicts in src/modules/history/index.ts, and fix(routing): a turn from a non-channel surface pins no inferred locus #190 at 191fc24821 in test/prose-drafts.test.ts, where neither conflicts with main. feat(mcpl): every channel item names its conversation in a stored source header #257 at 665957dd5f already conflicts with main in 33 files, and in one of them, src/modules/history/index.ts, this change takes it from one conflict block to two.

What each part is for

  • The table, and speechOf reading it (src/send-tools.ts:36): one place names each tool and its arguments, and silencing, search, extract and the semantic index read it, so they can't name different tools.
  • The first argument present holds the words: this keeps content ?? text's answer, so a channel_publish whose content is empty or blank never shows its text.
  • text off four rows: discord-mcpl and slack-mcpl send those tools content, so a text there was never sent, and the table names what each tool takes.
  • The guards on a call's name and input: a stored call with no name or no input still renders in search and extract, as it did before.
  • Rows for send_scheduled_message, create_poll, say and whisper: each carries the agent's own words, which is what the report asks search to find. They change nothing a turn does.
  • WORLD_PUBLICATION_TOOLS removed: with say and whisper as silencing rows, its two readers already read them through SILENCING_TOOLS.
  • The stored name in plain search and extract (src/modules/history/index.ts:2471): a call reads [telegram--send_message] words, with the server's prefix, so extract still shows which server a send went through, and a search for the stored name still finds it, which "search finds a send by its server's name for the tool" checks. The semantic index keeps the bare name, so what it has already embedded embeds the same.
  • extract's description (:699): an agent reads from it how a send's call shows in the text form. Its cost is that the history tools' text changes once.
  • The new tests:
    • the history-module search and extract tests pin each row's arguments, the first argument present, a null or blank one, a call with no name or no input, the stored name, and that a file's path doesn't show;
    • the semantic tests pin telegram-mcpl's rows, say and whisper, and that the index doesn't read text on the four sends;
    • the prose-drafts tests pin reply_to_message's silencing and the four rows that don't silence;
    • the code-execution test pins reply_to_message among the tools a script's round waits on.
  • SEND_TOOL_NAME in the speech-route test server (test/fixtures/speech-route-mcpl-server.mjs): it lets the existing server stand in for telegram-mcpl's tools rather than adding a second server.

What this leaves

  • Engagement: this change doesn't touch SEND_ENGAGEMENT_TOOLS (src/framework.ts:576), the sends that open a closed channel and note their publish. It finds a send's channel from a channelId in the send's answer, or else in its arguments (:35806–35807). telegram-mcpl's reply_to_message takes chat_id and answers in plain text at 4f00f6f918 (telegram_mcp/tools/messages.py:948, :966).
  • Where a telegram reply's prose is held: in the prose-drafts test, a reply whose answer names no channel holds the prose beside it and after it. There the turn's message comes from one room, and the reply names that room. No run has the prose bound for another channel.
  • The framework's own DiscordModule: its send, dm, reply and edit tools aren't in the table. As bare names they're generic, and a file tool named edit with a content argument would read as speech.
  • The semantic index's private-prose setting: unchanged. With includePrivateTools on, its default, a think call's words are indexed as before.

Tests

Each run in the table used a clean copy of the files its row names. Each copy was made for an earlier run of the same files but for one test, and installed then with npm install, with no lockfile. These runs started from what that install left there, which npm install reported up to date. Each was built with npm run build, and each test file was run from dist as the test script runs it. Each ran on Linux with Node 24.22.1, chronicle 0.5.0, context-manager 0.24.1, membrane 0.8.0 and mcpl-core 0.4.0.

history-module history-module-semantic prose-drafts explicit-prose-routing present-while-acting code-execution
This head, 5d9af7beec 53 of 53 16 of 16 258 of 258 120 of 120 95 of 95 100 of 100
This head's tests on 84e0408977's src 50 of 53 13 of 16 257 of 258 120 of 120 95 of 95 99 of 100
This head merged with main 612f6fd3bd 53 of 53 16 of 16 258 of 258 120 of 120 95 of 95 100 of 100
  • On 84e0408977's src, the eight tests that fail are this change's new tests:
    • in history-module, "search finds what was said through each one, under its own argument name", "search finds them under the agent as author" and "extract shows each one as its name and its words, and a call with no words, no input or no name as before";
    • in history-module-semantic, the telegram-mcpl test, the say and whisper test, and "messageIndexText doesn't read a text argument of send_message, reply_message, send_dm or edit_message, which servers send content (#​524)";
    • in prose-drafts, "a send through reply_to_message, telegram-mcpl's reply, whose answer names no channel, holds the prose beside it and after it (#​524)";
    • in code-execution, "a script whose code names reply_to_message, telegram-mcpl's reply, holds its round until it returns (#​524)".
  • Passing on 84e0408977's src too: the five new tests that pin what stays as it was. Those are the four that check edit_message, send_file, send_scheduled_message and create_poll don't hold the round's prose, and "search finds a send by its server's name for the tool".
  • The merge with main: this head merges with 612f6fd3bd cleanly by git merge-tree, which gives tree 9a1c2fad9f, the tree the run used.
  • Taking each part out: on an earlier version of this change, whose src differs from this head's only in two doc comments, and whose tests lacked the code-execution test and one extract case and gave one semantic test an earlier title, each of 32 changes takes one part out or puts text back on one of the four rows. Each fails at least one test. This run used tsx rather than dist, on Linux with Node 24.22.1, chronicle 0.5.0, context-manager 0.24.1 and membrane 0.8.0, and printed no mcpl-core version. Under tsx, five prose-drafts script tests fail with no change made, so a change there is counted only when another test fails. For each of the five prose-drafts changes, that's the test of the row it changes. The existing tests of say and whisper silencing prose, in explicit-prose-routing and present-while-acting, fail when their row is made not to silence.

Not verified

  • telegram-mcpl and Eidoverse themselves: no run used either. The tool names and arguments are their sources' at the commits named in Changes. The prose-drafts tests call tools of telegram-mcpl's names on the test server, and the history tests read stored calls of those names and of say and whisper.
  • An established semantic index: no run shows whether it embeds a send stored before this change. src/modules/history/semantic.ts:828 sets the start of its walk to its watermark less overlapMs, 600,000 ms by default. Plain search reads the stored calls when it runs, as its tests do with calls already in the store.
  • Hybrid routing for a script: the code-execution tests of a script holding its round run in locus routing, so no test in this change shows it under hybrid routing. A probe outside this change ran the same check in hybrid routing, built from dist in the copies the table's runs used, where npm install reported them up to date. On an earlier version of this change, whose src differs from this head's only in two doc comments, a script whose code names telegram--reply_to_message, discord--send_message or world--say held its round until it returned. On 84e0408977's src, the send_message and say scripts held it and the reply_to_message one didn't.
  • The whole suite: these runs are of the six files named above. The repository's whole suite runs in CI once this pull request opens.

  • Changelog fragments added: changelog.d/524-speech-tools.changed.md and changelog.d/524-speech-tools.fixed.md.

Nell-1783 made the change, and Ada-1017 made its design calls. @theaspirational's #638 began reading a send's words into the semantic index. Agnes-984 read the change and ran it, and Abigail-2676 weighed what each part pays for. Our internal review read earlier versions of it three times. Fiona-2757 wrote this description.

…ch, extract and the semantic index

An agent that speaks through send tools couldn't find its own words with
history--search: plain search and extract's text form render a tool call
as `[tool_use: NAME]` with no arguments, so the words a send carries were
never matched (anima-research/connectome-host#524). The semantic index
read a send's `content` or `text`, but not telegram-mcpl's argument names,
and the framework's list of sends didn't name telegram-mcpl's reply, so a
reply didn't hold the prose beside it as a send does.

src/send-tools.ts now holds one table: each tool whose call carries the
resident's words, by bare name, the arguments that can hold them (the
first one present holds the words, as `content ?? text` did), and whether
a call of it silences the round's auto-routed prose. Silencing, plain
search, extract's text form and the semantic index all read it:
- channel_publish reads `content`, or `text` from older callers;
  send_message, reply_message and send_dm read `content`, as
  discord-mcpl and slack-mcpl send it, and send_message also `message`,
  as telegram-mcpl's takes it. main's semantic index also read `text`
  for these three and edit_message, which no server we know sends them;
  it no longer does;
- reply_to_message (`text`) is a send that silences;
- send_file (`caption`), send_scheduled_message (`message`) and
  create_poll (`question`) are read for their words and don't silence, as
  before: a file can go without a caption, and a scheduled message or a
  poll isn't the round's speech;
- edit_message reads `content`, or `new_text` as telegram-mcpl's takes
  it, and still doesn't silence;
- Eidoverse's say and whisper read `text`, and silence as before.

WORLD_PUBLICATION_TOOLS goes. It held say and whisper so that an
explicit world utterance, the resident's chosen public speech for its
round, silences the adjacent auto-routed prose in every routing mode:
until 2026-09-01 it was consulted only in hybrid mode, and in locus mode
a round of ordinary text and a say published twice (Cairn world seq
15146 was the say's text, 15147 the adjacent prose, byte for byte). Its
only readers were isSilencingTool and ROUTING_TOOLS, which both read
SILENCING_TOOLS too, and say and whisper are silencing rows of the table
now, so they still silence, outrank a same-round hybrid envelope, and
hold a script's round, through the table alone.

In plain search and extract's text form, a table tool's call that
carries words flattens to `[NAME] words`, its name as stored, server
prefix included, so extract still shows which server a send went through
and a search by that name still finds it; any other call stays
`[tool_use: NAME]`, so a send's ids and paths stay out. The semantic
index keeps its bare label, `[send_message] words`, so what it has
already embedded embeds the same. extract's description says how such a
call shows, which changes the history tools' text once.

Tests:
- history-module: a send is found by its server's name for the tool, as
  on main, and a stored call of each of telegram-mcpl's six tools, of
  say and whisper, and of channel_publish, send_message, reply_message,
  send_dm and edit_message with `content`, is found by search; two of
  them by regular expression, and three under the agent as author; a
  send's file path, with a caption or without one, and a think call's
  words aren't. extract shows each as its name and its words, a null
  first argument falling through to the next, and a think call, a call
  whose first argument is empty or blank, one with no input, one with no
  name and a file sent without a caption as before.
- history-module-semantic: messageIndexText reads telegram-mcpl's six
  tools under their own argument names, and say and whisper, and doesn't
  read a `text` argument of send_message, reply_message, send_dm or
  edit_message.
- prose-drafts: through the speech-route fixture, whose send tool's name
  now comes from SEND_TOOL_NAME, a send through reply_to_message whose
  answer names no channel holds the prose beside it and after it as
  drafts, and calls of edit_message, send_file, send_scheduled_message
  and create_poll reach the server and don't.
- code-execution: a script whose code names reply_to_message holds its
  round until it returns, as one naming send_message does.
The existing tests of say and whisper silencing prose, in
explicit-prose-routing and present-while-acting, now pin their rows.
Each test fails on this change's base's src (84e0408), except the
five that pin what stays as it was, which pass there: the four calls
that don't silence, each failing when its row is made to silence, and
the search by a server's name.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant