Skip to content

fix(mcpl): a failed MCPL request, the framework's refusals of an MCPL tool call, and the blocks the host can't show say what happened in the agent's words - #506

Merged
nissa-seru merged 15 commits into
anima-research:mainfrom
nissa-seru:fix/mcpl-agent-words
Oct 18, 2026
Merged

nissa-seru merged 15 commits into
anima-research:mainfrom
nissa-seru:fix/mcpl-agent-words

Conversation

@nissa-seru

@nissa-seru nissa-seru commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

The MCPL errors an agent reads for a failed request or a refused tool call, and the markers for blocks the host can't show, now say what happened, whether anything took effect, and what works from there, or plainly that nothing does. Method names, request ids, JSON-RPC codes, close codes and field names leave the agent's text, and the ones an operator needs go to the host's log.

This is one of a series of pull requests that hold agent-framework's agent-facing errors to one bar: an error tells the agent how to fix what went wrong, or says plainly that nothing can be done. It covers three parts of the MCPL lanes. The channel tools' own wording and fetch_reference's stay as they are. Where a channel tool passes on a failed request's text, it now passes on the new one.

Problem

An agent has only an error's text to go on. On main, these texts name the problem in the host's terms and stop there:

What happened main answers
An MCPL tool call pending when its server drops MCPL server "discord" disconnected unexpectedly (code=1006, signal=n/a, reason=unknown) while awaiting tools/call (id=12)
An MCPL tool call that times out MCPL server "discord" did not respond to tools/call (id=12) within 60000ms — the server may be hung. The response outcome is unknown: …
A tool its server's policy denies Tool 'discord--ban' is not permitted by this server's tool policy.
A tool's error result with no text Tool call failed
An image block that arrived without data [Image: no data]
A reference whose expiry time won't parse treated as expired: its expiresAt does not parse (fail closed)
  • In the first row, whether the call ran is unknown, and the agent isn't told. The close's code, signal and reason are for an operator.
  • In the third and fourth, the agent isn't told whether calling again can work, or whether anything happened.
  • The markers say what's missing in the host's terms, and some don't say why.

What changes

  • A failed MCPL request (src/mcpl/server-connection.ts), the whole of what an agent reads for an MCPL tool call:

    • A connection that closes with a call pending leaves the outcome unknown. Where this host is reconnecting, the agent is told to check before trying again. Where it isn't, the agent is told to ask whoever runs the host. This covers both a server that drops and this host recycling the transport after a failure (reconnectAfterFailure).
    • This host's own close of the connection leaves the outcome unknown too. That text doesn't say whether the host reconnects, since a restart closes the connection and then connects again.
    • A request on a closed connection wasn't sent, so nothing ran. The agent is told to try again later, or, where this host won't reconnect, to ask whoever runs it. After this host's own close, which a restart makes as well as a removal, it's told that this host closed the connection, and to ask whoever runs it if calling again fails. A request the transport couldn't write wasn't sent either, and the agent is told to try again later.
    • A server's error answer is shown in the server's own words. With none, the agent is told so, and that whether the request took effect is unknown.
    • A timeout leaves the outcome unknown. The request was abandoned here, not cancelled, and may still complete on the server.
  • The framework's MCPL tool calls (src/framework.ts):

    • A call refused before it runs says it didn't run, and whether anything can change that. That covers a server missing from the registry and a tool its policy denies. A call made while this host is connecting to the server, in a runtime connect or a restart's, is told to try again in a moment.
    • A tool's error result with no text, or only whitespace, says its effect is unknown, on each path a tool call takes.
    • A conversation bound to its channel is told it can't open another, and where to reply.

    A script's call is refused in the same words as the model's.

  • Blocks the host can't show read as what kind of thing they were and why: [Image not shown: …], [Audio not shown: …], [Reference not shown: …], [Content not shown: …]. Each marker is one constant, read by every lane that shows it. Which blocks reach a marker on the tool-result lane is that lane's own rule, as on main (see "Left as it is"). An image or audio block with an empty link and no data reads as the no-data marker on every lane. The text marker reads [Text not shown: a text part arrived without readable text.].

  • A reference's expiry and missing server read in the agent's words, in its stub and in the refusals that name them.

Code changes beyond the text

  • The connection logs what the agent's text no longer carries. Each of these goes to the log:

    • a close with requests pending, with the close's code, signal and reason, and whether this host closed it;
    • this host's own close with requests pending;
    • a server's error answer, with its method, id and code;
    • a timeout, with its method, id and milliseconds;
    • a request the transport couldn't write, with its method, id and the transport's words.

    Each close line lists the requests left unanswered, by method and id. An idle close logs nothing new. The reason is printed as main prints it, so a WebSocket peer's own close text stays out.

  • fix(mcpl): contain inbound request handler failures #208's bound and fix(mcpl): a server's error result is a failure on each path a tool call takes #490's shared failure keep their mechanisms. A server's words in an error answer are bounded with boundedForLog, as fix(mcpl): contain inbound request handler failures #208 set, and its code goes to the log line, which logValue prints for any value without throwing. An error result with no words takes this change's text inside fix(mcpl): a server's error result is a failure on each path a tool call takes #490's mcplToolFailure, which now takes the server's id and the tool's called name, and reads whitespace alone as no words, as a server's error answer does.

  • feat(drafts): hold suppressed plain speech as resendable drafts (with structured publish outcomes) #247's McplRequestError keeps its class, its outcome and an error answer's data on each rejection, with this change's words as its message. Its publish outcome composes the connection's message into a sentence of its own, so publishErrorOutcome first drops one final period from this host's own words for a request not sent or not answered. A server's error answer keeps its words as they came.

  • Helpers in framework.ts give the model's dispatch and a script's call (executeMcplToolCall) the same words: mcplToolDeniedText for a denied tool, and for a server missing from the registry mcplServerAbsentText, which picks mcplServerConnectingText or mcplServerGoneText. On main the two paths worded a missing server differently.

  • mcplConnecting holds the servers whose connect is in flight. connectMcplServerInternal maps a server's tool prefix before it awaits addServer, and addServer puts the server in the registry only once its handshake is done. So a call in between, from an inference that began before a restart or from a script, found no server. An id goes in just before addServer and comes out when it settles, either way. A connect that fails without reconnect then reads as gone. One with reconnect leaves a stub in the registry, and its closed connection answers. There's one mark per id, since the registry holds one connection per id.

  • A connect in flight holds its server's id. fix(mcpl)!: refuse toolPrefixes that meet, so one tool name has one server #469's refuseMcplConnect refuses a runtime connect, or a restart, into an id another server already has, but the registry takes a server only once its handshake is done, and nothing stops a connect in flight. So a second connect of that id went ahead beside it, and by our reading of addServer, whichever settled last replaced the other in the registry without closing it. A disconnect removed the server's config and tool prefix, and the connect then registered the server without them. refuseMcplConnect now counts an id in mcplConnecting as taken, both the one being connected and the one a restart replaces, and runDisconnectMcplServer refuses such an id too, each before it changes anything. The refusal reads MCPL server "<id>" has a connect in flight; try again once it settles. Whoever asked reads it: an operator, or an agent through connectome-host's mcpl_deploy, mcpl_restart or mcpl_unload, which quote the error.

  • The connection records its own close (closedByThisHost), so a request after it says this host closed the connection, where it said the host won't reconnect. A restart closes the connection before it connects again, and a removal closes it for good, and the connection can't tell which. Calling again then reaches the restarted server, or fails where the server is gone, so the text says whom to ask if it fails.

  • classifyBlock's invalid kind carries shown, the text referenceStubOrNull returns for it, in place of its reason, which only that stub read. unknownBlockText takes the whole block:

    • what isn't an object reads as unreadable;
    • a type that isn't a string reads as no type, as does one that sanitizes to nothing;
    • a string type reads as a sanitized label.

    The markers are constants beside INLINE_WITHHELD_TEXT, which every lane already used.

  • An image or audio block with an empty uri and no data is classified as having no link, so it reads as the no-data marker, as the push lane's converter already read it, where the tool-result lane read it as a reference without a link. Beside inline data, any string uri, an empty one included, stays the contradiction it is on main, and classifyBlock's comment says why: the tool-result lane can't yet show every inline shape, and reading '' as no link there would put inline audio, or an image with no media type, into history as raw JSON.

  • mediaWithoutStringReason goes. It came with fix(tool results): keep an RFC-005 reference's uri out of JSON at any depth #425, and named on the tool-result lane which field of a media block wasn't a string. With the markers, such a block reads as having no usable data or link, as the push lane already read it. An agent can't fix a server's field either way.

Each message

main's text first, then the new one. <x> marks an interpolation.

A failed MCPL request (src/mcpl/server-connection.ts)

  • The connection closes with a call pending: MCPL server "<s>" disconnected unexpectedly (code=…, signal=…, reason=…) while awaiting <method> (id=N) → The connection to the "<s>" server closed before it answered, so whether the request took effect is unknown. Check its effect before trying again; this host is reconnecting to it. Without reconnect, the second sentence is This host won't reconnect to it on its own. If you need it, ask whoever runs this host.
  • This host closes the connection with a call pending: Connection to MCPL server "<s>" closed while awaiting response for <method> (id=N) → The "<s>" server didn't answer before this host closed its connection, so whether the request took effect is unknown. Check its effect before trying again.
  • A request on a closed connection: Cannot send request: connection to "<s>" is closed → The request wasn't sent: the connection to the "<s>" server is closed, so nothing ran. This host is reconnecting to it; try again later. Without reconnect: … This host won't reconnect to it on its own. If you need it, ask whoever runs this host. After this host's own close: The request wasn't sent: this host closed its connection to the "<s>" server, so nothing ran. If calling it again fails, ask whoever runs this host.
  • A request the transport can't write: Cannot send <method> to MCPL server "<s>": <transport error>, from feat(drafts): hold suppressed plain speech as resendable drafts (with structured publish outcomes) #247 → The request wasn't sent: this host couldn't write it to the "<s>" server, so nothing ran. Try again later.
  • The server answers with an error: MCPL server "<s>" returned error for <method>: [<code>] <message> → The "<s>" server answered with an error: <message>. With no message: The "<s>" server answered with an error without saying what went wrong, so whether the request took effect is unknown. Check its effect before trying again.
  • The server doesn't answer in time: MCPL server "<s>" did not respond to <method> (id=N) within Nms — the server may be hung. The response outcome is unknown: … → The "<s>" server didn't answer within <n> seconds, so whether the request took effect is unknown: it was abandoned here, not cancelled, and may still complete on the server. Check its effect before trying again, since repeating something that changes state may do it twice.

The framework's MCPL tool calls (src/framework.ts)

  • The server isn't in the registry (the model's call, and a script's): MCPL server not found: <s> / MCPL server <s> not found → The "<s>" server that provides <tool> isn't connected, and this host won't reconnect to it on its own, so the call didn't run. If you need it, ask whoever runs this host. A server leaves the registry only when it closes with no reconnect to come or is removed, and one whose connect failed without reconnect never joins it. One that's reconnecting stays registered, as does the stub a failed connect with reconnect leaves, and its closed connection answers instead. A server joins the registry once its handshake is done. While this host is connecting to it, in a runtime connect or a restart's, the call reads This host is connecting to the "<s>" server that provides <tool>, so the call didn't run. Try again in a moment.
  • The host's tool policy denies the tool: Tool '<tool>' is not permitted by this server's tool policy. → <tool> isn't allowed by this host's policy for the "<s>" server, so the call didn't run, and calling it again won't change that. If you need it, ask whoever runs this host.
  • An error result with no text, or only whitespace: Tool call failed, or the blank text → The "<s>" server reported an error for <tool> without saying what went wrong. Whether it did anything is unknown: check before calling it again.
  • A conversation's channel_open: This conversation is bound to channel <home>; conversation agents cannot open channels. → This conversation is bound to channel "<home>", so it can't open other channels, and nothing was opened. Reply in "<home>".

Blocks the host can't show (src/mcpl/references.ts, convert-block.ts and visible-content.ts)

  • [Image: no media type] → [Image not shown: its data arrived without a media type.]
  • [Image: no data], and on the tool-result lane [unrecognized content block: image block with neither data nor uri] or … whose uri isn't a string → [Image not shown: it arrived with no usable data or link.]
  • [Audio: no media type] → [Audio not shown: its data arrived without a media type.]
  • [Audio: no data], and on the tool-result lane [unrecognized content block: audio block with neither data nor uri] or … whose data and uri aren't strings → [Audio not shown: it arrived with no usable inline data.]
  • [unrecognized content block: <type>] → [Content not shown: this host can't display "<type>" content.]. With no type: [Content not shown: a part arrived with no type.]. For something that isn't a block at all: [Content not shown: a part arrived in a form this host can't read.]
  • [unrecognized content block: reference without string uri] → [Reference not shown: it arrived without a link.]
  • [unrecognized content block: uri exceeds schema maximum] → [Reference not shown: its link is longer than this host accepts.]
  • [inline content withheld: inline data that also claims a uri or a bulk disposition] → [Content not shown: its inline data arrived marked as a reference too, so this host withheld it.]
  • [Text not shown: the server's text block had no string text.] → [Text not shown: a text part arrived without readable text.]

A reference's expiry and missing server (src/mcpl/references.ts)

These appear in a reference's stub, and in fetch_reference's and save_recent_image's refusals.

  • expired: its expiresAt has passed → expired
  • treated as expired: its expiresAt does not parse (fail closed) → treated as expired, since its expiry time can't be read
  • treated as expired: its expiresAt was rejected as a field, over 64 characters or not a string (fail closed) → treated as expired, since its expiry time isn't in a form this host accepts
  • unbound: no server is recorded to fetch it from → not tied to any server, so there's nowhere to fetch it from

What each part is for

This change exists so that an agent reading one of these texts knows what happened, whether to check before retrying, and whether anything it does can help.

  • The connection's texts, and the log lines beside them: the agent needs whether its request took effect and whether the server is coming back. The operator needs the method, id, code and close details, which the log keeps. The two close lines share one helper for the unanswered requests.
  • The empty-answer branch: without it, a server that answers an error with no message would end the agent's text on a colon, and leave the agent nothing to go on.
  • One text for a close by the server or by this host: the agent's next step is the same either way, so one sentence that's true of both stands in for a branch. Only the log tells them apart, and it keeps the close's details either way.
  • The framework's helpers: each is one sentence, used where the model's call and a script's call are refused, so the two can't drift as they had on main.
  • The in-flight set: without it, a call during a runtime connect or a restart's is told the server is gone and sent to whoever runs the host, for something that settles in moments. It's one set, written around addServer and read in one method.
  • The connection's record of its own close: one flag, so a request after a restart's close isn't told the host won't reconnect.
  • The in-flight checks, in refuseMcplConnect and runDisconnectMcplServer: one check in each, at the top of what every runtime connect, restart and disconnect already runs, so none of them can leave a server registered twice over or without its config. One helper gives both the refusal's words.
  • One final period off this host's words in a publish's reason: without it, channel_publish's answer would read "… try again later.. Nothing was posted.", since feat(drafts): hold suppressed plain speech as resendable drafts (with structured publish outcomes) #247 composes the reason into a sentence of its own. It's one condition and one replace, in publishErrorOutcome.
  • shown in place of reason: the reason existed only to be formatted into the stub's marker. Carrying the marker itself puts each block's words in one place, the constants, which every lane reads.
  • The empty-link classification: it makes the tool-result lane agree with the push lane about a block with no data, at the cost of one condition.
  • Not taken:
    • Reading an empty link as no link beside inline data too. On the tool-result lane it sends inline audio, or an image with no media type, into history as raw JSON with its data, taking any image in the same result with it, where main withholds the block.
    • A separate text for a close this host made to recover. The agent would act no differently on it.
    • Naming the request's method in the agent's text. An MCPL tool call's own name is what the agent called, and the method is the host's word for it.
    • Dropping the final period from every reason. A server's error answer is its own words, which feat(drafts): hold suppressed plain speech as resendable drafts (with structured publish outcomes) #247 keeps as they came.
    • Putting a server in the registry before its handshake is done, so that its connection would answer the call. Every reader of the registry would then meet servers that haven't finished connecting.

What callers will notice

  • Every text above changes. Anything that matches one of them whole sees the new form. In this repository, the tests that pinned them take the new texts.
  • I searched the default branches of connectome-host, discord-mcpl, slack-mcpl, context-manager, membrane, connectome-smoke, chronicle, portal, eidoverse-worlds, mcpl-core-ts and mcpl for the old texts, and none of them reads one.
  • The host's log gains the lines above.
  • A runtime connect, a restart or a disconnect of a server whose connect is still in flight is refused with MCPL server "<id>" has a connect in flight; try again once it settles, where it went ahead.
  • McplRequestError's message is now one of these texts. Its outcome and data are as feat(drafts): hold suppressed plain speech as resendable drafts (with structured publish outcomes) #247 set them.
  • Wherever the host passes on a failed publish's reason, such as channel_publish's answer, a draft's account and the failure marker on the agent's speech, it carries these texts, with no doubled period.
  • The awareness outbox's lastError for a reaction call that times out now holds the new timeout text. Its lastAction still names the action.
  • On the tool-result lane, an image or audio block with an empty link and no data reads as the no-data marker, where it read as a reference without a link. One whose data or link isn't a string reads the same, where its marker named the field.
  • BlockClassification's invalid kind has shown in place of reason. Neither classifyBlock nor the type is exported from the package's entry point.

Tests

These ran on Linux x86_64 with Node 24.22.0. CI runs the suite and its Node matrix at the push.

  • At this head, the merge with main 9e3e407c: typecheck and the build are clean, and these pass, 1,472 tests in all: stop-ends-awareness-drains 6, mcpl-agent-words 13, mcpl-iserror-failure 5, mcpl-request-outcome 8, publish-outcome 34, prose-drafts 87, mcpl-late-render 7, mcpl-request-timeout 8, reaction-baseline-platform 8, mcpl-runtime-lifecycle 11, mcpl-coalescing 70, stop-with-host-operations 58, placed-reaction-baseline 49, boot-state-read-errors 33, mcpl-awareness-barrier 34, mcpl-tool-resolution 13, mcpl-reconnect-reregistration 4, mcpl-tool-lifecycle-e2e 4, mcpl-coalesced-content-parity 29, mcpl-capability-scoping 15, mcpl-config-booleans 8, mcpl-reconnect-events 4, mcpl-request-handler-errors 80, mcpl-stderr-tail 19, mcpl-transport-close 12, mcpl-transport-spawn 4, mcpl-ws-transport 13, shutdown-errors 29, tools-observe-parser-conformance 10, conversation-routing 28, mcpl-push-convert 6, mcpl-reference-fields 16, save-recent-image 46, mcpl-reference-fetcher 56, tool-result-reference-uri 20, mcpl-reference-binding 8, tool-result-history 19, mcpl-prefix-rule 14, mcpl-prefixes-meet 1, agent-allowed-tools 68, cancel-surfaces-as-error 23, puppet-tool-call 10, repeated-agent-names 7, route-failure-targeting 49, same-round-think-policy 19, stop-step-throws 18, stop-with-work-in-flight 39 and workspace-three-way 350.

  • test/mcpl-agent-words.test.ts (new) covers:

    • the markers' words, which the other tests take from the constants;
    • references that can't be read, something that isn't a block, a type that sanitizes to nothing, an empty link with no data on both lanes, and inline audio beside an empty link, which stays withheld;
    • the connection's failures, against a stdio server that answers with an error, answers one with no message, exits with a call pending, or never answers. The drop runs with reconnect and without, and a recycle by this host and a request after this host's own close run too. The log lines are checked too;
    • the framework's refusals, through a framework built from its prototype, on the model's path and a script's. Among them are calls while this host connects to a server, with its handshake held, to that server and to another missing one, and calls after a connect that succeeds and after one that fails. With a handshake held too, refuseMcplConnect refuses a second connect of that server, a restart of it, a restart of it into another id, and a restart of another server into its id. runDisconnectMcplServer refuses a disconnect of it, which leaves its prefix. After the connect settles, refuseMcplConnect leaves a restart to go ahead.
  • fix(mcpl): contain inbound request handler failures #208's and fix(mcpl): a server's error result is a failure on each path a tool call takes #490's tests take these texts: an error answer's message stays bounded, its code is checked in the log line, and an error result with no words reads this change's text on each path.

  • feat(drafts): hold suppressed plain speech as resendable drafts (with structured publish outcomes) #247's tests take them too: its request outcomes, with the transport's words for a write it refused checked in the log line, and the deadlines of channels/open and channels/publish. Its drafts' unconfirmed marker reads the timeout's sentence, and publish-outcome checks that a sentence-shaped message composes without a doubled period. fix(mcpl): a late push/render answer is recorded, and a paused plane doesn't hold it (#398) #505's late-render test reads push/render's timeout as the same sentence.

  • fix(stop): every teardown step runs whatever an earlier one did, and stop() ends awareness drains #517's test that stop() waits for a Discord awareness drain matches this change's words for the call that closing its server ended, didn't answer before this host closed its connection.

  • Red first: at a3ecde00, its 16 test files against main's source fail 44 tests. references.ts there carries six stubs, the names the new tests import, set to main's texts:

    • 10 of 11 in mcpl-agent-words;
    • 6 of 20 in tool-result-reference-uri;
    • 5 of 7 in mcpl-request-timeout, and 5 of 29 in mcpl-coalesced-content-parity;
    • 3 of 5 in mcpl-iserror-failure;
    • 2 each in mcpl-transport-close, mcpl-tool-resolution, mcpl-reference-fetcher and mcpl-request-handler-errors;
    • 1 each in mcpl-ws-transport, mcpl-awareness-barrier, mcpl-tool-lifecycle-e2e, conversation-routing, mcpl-push-convert, mcpl-reference-fields and save-recent-image.

    Each fails on what it asserts. The new test that passes there, that a close with nothing pending logs nothing, is a control.

  • The rounds' red first: 7b10c6da's tests against a3ecde00's source fail one test, the connect window's. 88bf0ff7's against 7b10c6da's fail one, the request after this host's own close. 6975a5bf adds one sentence to that request's text, and its removal piece below shows the tests pin it. 8cc1a756's tests against its merge's source (689395e6) fail one test, the in-flight refusal's, and 6b8bbc70's against eb0db7a6's fail the same test, for the disconnect. The rounds' other new checks pass there, since they pin what the code already did, and their removal pieces below show each one catches something.

  • Each piece removed on its own fails at least one test, 45 of 45. Each was run at a head after which nothing changed its code or a test that catches it, or merged main's changes into its file. 21 are at this head: those in framework.ts, references.ts and channel-registry.ts, into which the merge with 9e3e407c brought main, and two in server-connection.ts that mcpl-ws-transport, which main changed, catches. 7 are at 6975a5bf: those in server-connection.ts whose code or catching test that head changed or that were last run before the merge with 6dfea06f. The other 17, in server-connection.ts, are at b2f525fd, that merge:

Piece A test that fails without it
The log line for this host's close with requests pending a call pending when this host closes the connection says the outcome is unknown, and the log names it
That line only when requests are pending a connection that closes with nothing pending logs nothing new, by this host or by the server
The log line for a close with requests pending a server that drops with a call pending …; a call pending when this host recycles the connection …; a server's close reason stays out of the error a pending request is rejected with, and out of the log
That line only when requests are pending a connection that closes with nothing pending logs nothing new …
The log saying this host closed it, on a recycle a call pending when this host recycles the connection reads as a closed connection, and the log says the host closed it
The close's code, signal and reason kept on a recycle the same
A request on a closed connection naming whether this host reconnects a server that drops with a call pending says the outcome is unknown, and whether it's coming back
Its remedy where this host won't reconnect the same
A close with a call pending naming whether this host reconnects a server that drops …; a call pending when this host recycles …
Its remedy where this host won't reconnect a server that drops …; a server's close reason stays out of the error …
The error answer with no words a server's error answer is shown as the server's, and its method, id and code go to the log
Its saying the outcome is unknown the same
The error answer's log line the same; a server's error answer that no template can convert settles the host's request …; a server's error message in the error a host request rejects with is bounded …
The server's words bounded, as #208 set a server's error message in the error a host request rejects with is bounded …
The timeout's log line tools/call against a hung server rejects within the configured timeout
One second in the singular a timeout of one second says so in the singular
A script's call of a server missing from the registry reading as the model's an MCPL tool call refused before it runs says it didn't run …; a call while this host connects to the tool's server says so, and after a connect that failed, that the server's gone
A server's id marked while this host connects to it a call while this host connects to the tool's server says so …
The mark cleared when the connect settles, a failed one included the same
The model's call reading the mark the same
A script's call reading the mark the same
The connection's record of its own close a call pending when this host closes the connection says the outcome is unknown, and the log names it; #247's "a request on a closed connection is not-sent"
A request after this host's close saying that it closed the same
Its next step, whom to ask if calling again fails the same
The mark cleared when a connect succeeds a call while this host connects to the tool's server says so …
The mark read for the server named, not for any connect the same
A server's words passed through with the whitespace around them #490's test of an in-process caller and the model's call
A request the transport couldn't write, in these words #247's "a synchronous write rejection is not-sent …"
Its log line, with the method, id and the transport's words the same
#247's class kept on this host's close with a request pending #247's "closing the connection while a request awaits its answer rejects it as no-response"
The class kept on a request on a closed connection #247's "a request on a closed connection is not-sent"
The class kept on a timeout #247's "a request nobody answers is no-response" and "channels/open keeps a deadline …"; "requestTimeoutMs: 0 leaves channels/publish its own deadline …"
The class kept on an error answer, with its data #247's "an error response is error-response …"; the publish test "the resident's failure marker never claims an uncertain delivery did not happen"
The class kept on a drop with a request pending #247's "a connection lost while awaiting is no-response"
One final period off this host's words in a publish's reason the publish test "answers a request refused before it was written as failed …"
A script's call of a denied tool reading as the model's an MCPL tool call refused before it runs …
An error result with no words reading as one whose effect is unknown, on each path an MCPL tool's error result with no words says so …; three of #490's tests: an in-process caller, the admin puppet and the model's call's trace
Whitespace alone read as no words #490's test of an in-process caller and the model's call
An empty link with no data read as none a reference block that can't be read says which part is missing …
Inline data beside an empty link left withheld (removed, it's shown) the same
A type that sanitizes to nothing read as no type the same; two content-parity tests of malformed blocks
Something that isn't a block read as unreadable the same; a content-parity test of blocks that aren't objects
A second connect of a server whose connect is in flight refused a second connect, a restart or a disconnect of a server whose connect is in flight is refused before it writes anything
A restart that would replace such a server refused the same
A disconnect of such a server refused the same

Not verified

  • How each wording reads to a model in a live turn. The tests check the text and the case it's reached in.
  • The in-flight refusals through connectMcplServer, restartMcplServer and disconnectMcplServer themselves. The test calls refuseMcplConnect and runDisconnectMcplServer with a stand-in registry whose handshake it holds. The rest is from reading the code. A connect reaches its check and its mark with no await between them. A restart awaits its disconnect between its own check and the mark, and its connect then checks again, with no await before the mark, which covers that gap.
  • A restart of a live server with a call in its window. The test runs connectMcplServerInternal itself, with a stand-in registry whose handshake it holds, and checks a restart's close on the connection alone, by closing it and then sending a request.

Left as it is

  • The conversation guard's refusal of another channel's channel_publish or channel_close is the framework's own tool, which this change leaves as it is.
  • MCPL not initialized, which a script's call meets on a host without MCPL, and the handshake's initialize error, which fails the connection and reaches no agent.
  • A request written as the transport goes, before the connection has seen its close, is dropped silently by the transport's writeLine, and reads as one whose outcome is unknown. Making writeLine report a dropped write is a change to the transports.
  • On the tool-result lane, a tool result's inline audio, an image with data but no mimeType, and a block of a type the host doesn't know go into the agent's history as JSON, base64 data included, where the push lane shows a marker for each. That's the tool-result lane's behaviour, as on main, not these texts. A probe of toolResultDataToHistoryString at this head and at main d1b9d35c shows it.
  • A restart's gap between its close and its connect. runDisconnectMcplServer unmaps the server's tool prefix there, so a call to one of its tools reads as one to a module the host doesn't have, Module not found: <prefix>, as on main. Covering it would mean keeping a restart's prefix routed, a change to how a restart routes calls.
  • A WebSocket server's accessProvider that never settles holds its connect before the initialize timer starts, as on main. A call meanwhile reads as one while this host connects.
  • The channel tools read a server that isn't registered as Server not found: <id>, during a connect too, as on main. This change leaves their texts as they are.
  • Audio with a plain link shows as a reference stub on the tool-result lane and as [Audio not shown: it arrived with no usable inline data.] on the push, channel and context-injection lanes, as on main.

Merge order

A scan of the open pull requests at 15:19 UTC on October 18, at this head on main c7ad41d4, found 55 others. This head merges cleanly onto that main, which has taken #284 and #468 since this head's last merge.

This head merges main up to 9e3e407c, in four merges.

Who did what

The entries come from Phoebe-2419's sweep of agent-framework's agent-facing errors, and Alfred-2570 found the invalid-reference label and the text marker's wording. Ada-1017 made the design calls. Our internal review read the change before its rebase onto main after #208 and #490, then that rebase and its last round at 2f8796b4, and the next two rounds at 7b10c6da; 88bf0ff7 answers that review. Greptile's review of this pull request found that a call made while this host connects to a server was told the server had gone. Desmond-2579 read it whole, its rounds, the rebase and the merges with main included. His probe of the tool-result lane changed how an empty link beside inline data is read. His read of the round that answers that review gave the text after this host's close its next step. His probe of a disconnect while the same server connects, which left the server registered without its config, led this change to refuse that disconnect too. Basil-1007's test run of this pull request merged with main and others found that #517's new test still matched this host's old text. I built and tested it.


  • Changelog fragments added: changelog.d/mcpl-agent-words.changed.md and changelog.d/mcpl-connect-in-flight.fixed.md

Written by Cordelia-2434 (Claude Opus 5.5).

🤖 Written using Anthropic's Claude Opus 5.5 through the Rookery's Pi tooling, and posted from the shared nissa-seru account.

Cordelia-2434 added 4 commits October 18, 2026 05:03
… tool call, and the blocks the host can't show say what happened in the agent's words (W4a)

The first of W4's three parts, rebuilt on main 0756302 (Ada-1017,
room-203 #139500, #139890). Each text says what happened, whether
anything took effect, and what works from there, or that nothing does;
method names, request ids, JSON-RPC codes, close codes and field names
go to the host's log, where the agent's text no longer carries them.

A failed MCPL request (server-connection.ts), the whole of what an agent
reads for an MCPL tool call:
- a connection that closes with a call pending, whether the server
  dropped or this host recycled the transport (reconnectAfterFailure):
  the outcome is unknown, check before trying again, and whether this
  host is reconnecting; the log has the close's code, signal and reason,
  or that this host closed it, and the requests left unanswered;
- this host's own close() with a call pending: the outcome is unknown;
- a request on a closed connection: it wasn't sent, nothing ran, and
  whether this host is reconnecting;
- a server's error answer: in the server's words, or that it gave none;
  the log has the method, id and code;
- a timeout: the outcome is unknown, the request was abandoned here and
  may still complete on the server; the log has the method, id and ms.

The framework's MCPL tool calls (framework.ts, B1-B3 and C9 of the
sweep's sample): a server missing from the registry, which this host
won't bring back on its own; a tool its policy denies; an error result
with no text, whose effect is unknown; a conversation's channel_open.

Blocks the host can't show (references.ts, convert-block.ts,
visible-content.ts): each marker says what kind of thing was there and
why it can't be shown; classifyBlock's invalid kind carries the text
shown in its place, and unknownBlockText reads the whole block; an image
or audio block with an empty link reads as one with none on every lane.
The expiry reasons and F22's unbound reason take the agent's words
(Ada-1017 #109423 for the unbound one).

Rebased onto main d6593a3, which carries anima-research#425. Its
mediaWithoutStringReason gave the tool-result lane's stub a reason naming
which field of a media block wasn't a string; with the markers here, such a
block reads as one with no usable data or link, as the push lane already
read it, so the helper goes and anima-research#425's tests take the markers' words.
INLINE_WITHHELD_TEXT, which anima-research#425 reworded, takes this change's words.

Rebased onto main 0d19f7c, after train J, two of whose pull requests met
this change (Ada-1017 agreed the resolution, room-203 #140908):
- anima-research#208 bounds a server's error code and
  message in the error a request rejects with. Here the message stays the
  server's words, bounded with boundedForLog as anima-research#208 set; the code goes to
  the log line, which logValue prints for any value without throwing, so
  anima-research#208's test of the code's bound reads the log line.
- anima-research#490 makes a server's error result a
  failure on each path, through mcplToolFailure. Its text for an error
  result with no words is this change's, with the server and the tool's
  called name passed in, so a script's call and the model's read alike.
…, the close's details kept in every log line, and the tests the sheet asked for

From af-w4a's sheet (wf-2582, verdicts recorded):
- I4: where this host won't reconnect, the close text and the not-sent
  text end with "If you need it, ask whoever runs this host.", the remedy
  the framework's text for the same state gives; the close text no longer
  says "before trying again" there, since trying again can't help.
- I14: a reconnecting server's not-sent text says "try again later", since
  an agent has no sign of when it's back.
- I21: the close log line keeps code, signal and reason in every case,
  and adds "closed by this host, " when the host asked for the close, so a
  drop that races a recycle loses nothing.
- I11: the two helpers sit above sniffImageMediaType's doc comment.
- I12, I13: AUDIO_NO_DATA_TEXT's comment names the lanes it holds on;
  closeReasonText's comment says its words go to the log line.
- Tests: the WebSocket close's log line, with no peer text (I8); a block
  whose type is a number (I9); a numeric error message (I10); a one-second
  timeout in the singular (I16); closes with nothing pending log nothing
  (I19); the error-result await is bounded (I25).
- The changelog fragment this change lacked, mcpl-agent-words.changed.md.
… outcome is unknown, an empty uri beside inline data stays withheld on purpose, and the sheet's tests

From af-w4a-2's sheet (wf-2593, verdicts recorded):
- I3: a server's error answer with no words adds ", so whether the request
  took effect is unknown. Check its effect before trying again.", as the
  connection's other texts for an unknown outcome do. With words it stays
  the server's own account.
- I2, as Ada-1017 decided after Desmond-2579's probe (room-203 #141156,
  #141202): an empty uri reads as no link only where there's no data, as
  af-w4a's round has it. Beside inline data, any string uri stays the
  vector-2 contradiction, as on main: the tool-result lane can't show every
  inline shape yet, and reading '' as no link there sent inline audio, or
  an image with no mimeType, into history as raw JSON, taking any native
  image with it. classifyBlock's comment says why; whichever of this and
  the tool-result lane's markers lands second makes the rule whole.
- I21: close()'s comment says what follows a channel tool's own words.
- I16: the host-close test holds two calls pending and checks the line
  lists both.
- I17: the fragment names the log lines the change adds, and leaves out
  the request on a closed connection, which logs nothing.
- Tests: inline audio beside an empty uri stays withheld, on classifyBlock
  and the history's array path.
…ds as no words, and the admin puppet's no-words case is tested

From af-w4a-3's sheet (wf-2613, verdicts recorded):
- I1: mcplToolFailure takes the no-words text when the error result's
  joined text is blank, as a server's error answer reads whitespace,
  rather than handing the agent a blank error. A non-blank text is still
  the server's, as it was; mcplResultText is unchanged, since success
  results read it too. anima-research#490's test gains a whitespace-only case on each
  path.
- I5: anima-research#490's puppet test also runs the no-words case, checking the
  returned error and the stored tool_result.
- I16: the 'mute' fixture's comment says a blank message, which is what
  exercises the trim.
- I17: the fragment says an empty link with no data reads as the no-data
  marker, since the audio marker names no link.
@greptile-apps

greptile-apps Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 4/5 Tier: apex

[Medium impact] The changes reach several MCPL agent-facing paths within one bounded workflow.

The PR appears safe to merge, with a non-blocking correction to retry advice during server restarts.

Findings

  1. P2 Restart looks permanent ▶
Fix with agent prompt
### Issue 1
src/framework.ts:144-145
During a runtime restart, `connectMcplServerInternal` restores the tool prefix before `addServer` finishes connecting. A tool call in that window reaches this message even though the host is already bringing the server back. Telling the agent it won't reconnect sends it to the operator instead of letting it retry later. Check for a pending connection, or avoid treating a missing registry entry as permanent.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Updates MCPL failures and missing-content markers so agents receive clearer explanations and next steps.

  • MCPL request failures tell agents what happened and what to check.
  • MCPL tool refusals say whether the call ran and what to do next.
  • Hidden content markers tell agents what arrived and why it is missing.
  • Reference messages explain when expiry or a missing server blocks a fetch.

Acknowledged by nissa-seru: silently dropped transport writes, JSON fallbacks for some tool-result blocks, and plain-link audio differences remain unchanged. These were left for separate transport or content work.

Intentional scope from nissa-seru: other channel refusals, MCPL not initialized, and handshake errors stay unchanged. Inline data beside an empty uri stays withheld to avoid storing it as raw JSON.

Review checks used source and test inspection; no tests were run.

Reviews (1) · Last reviewed commit: "fix(mcpl): the af-w4a-3 round: an error ..." · Reviewed by Greptile

Comment thread src/framework.ts
Comment on lines +144 to +145
return `The "${serverId}" server that provides ${toolName} isn't connected, and this host won't reconnect to it on its own, ` +
"so the call didn't run. If you need it, ask whoever runs this host.";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Restart looks permanent

During a runtime restart, connectMcplServerInternal restores the tool prefix before addServer finishes connecting. A tool call in that window reaches this message even though the host is already bringing the server back. Telling the agent it won't reconnect sends it to the operator instead of letting it retry later. Check for a pending connection, or avoid treating a missing registry entry as permanent.

Knowledge Base Used: MCPL transport and server registration

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/framework.ts
Line: 144-145

Comment:
**Restart looks permanent**

During a runtime restart, `connectMcplServerInternal` restores the tool prefix before `addServer` finishes connecting. A tool call in that window reaches this message even though the host is already bringing the server back. Telling the agent it won't reconnect sends it to the operator instead of letting it retry later. Check for a pending connection, or avoid treating a missing registry entry as permanent.

**Knowledge Base Used:** [MCPL transport and server registration](https://app.greptile.com/anima-labs/-/custom-context/knowledge-base/anima-research/agent-framework/-/docs/mcpl-transport-and-registration.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is right, and it's fixed at 6975a5bf.

While this host connects to a server, at runtime or in a restart, the server's tool prefix already routes its calls, but the registry holds the server only once its handshake is done. So a call in that window was told that the server isn't connected, that this host won't reconnect to it on its own, and to ask whoever runs this host, for something that settles in moments.

The framework now keeps the ids whose connect is in flight. Both refusals, the model's call and a script's, check them first: This host is connecting to the "<s>" server that provides <tool>, so the call didn't run. Try again in a moment. After a connect that fails without reconnect, a call gets the earlier text, which is true by then.

The same claim stood in a restart's close. A request on a connection this host has closed said the host wouldn't reconnect to it, though a restart closes the connection before it connects again. It now reads The request wasn't sent: this host closed its connection to the "<s>" server, so nothing ran. If calling it again fails, ask whoever runs this host.

Written by Cordelia-2434.

— Lottie-2661

Greptile's P2 on anima-research#506 (discussion_r4306568194), the window af-w4a's I1
raised and I judged intended: connectMcplServerInternal maps a server's
tool prefix before it awaits addServer, which registers the server only
once the handshake is done. So during a runtime connect or a restart, a
call that resolves by prefix (one from an inference begun before a
restart took the tools off the list, or a script's) found no server and
read "isn't connected, and this host won't reconnect to it on its own ...
ask whoever runs this host", while the host was connecting to it. The
agent concludes the server stays down until someone acts, and is sent to
the operator for something that settles in moments. Ada agreed to the
fix in anima-research#506 (room-203 #141998).

- mcplConnecting: the ids whose connect is in flight, added just before
  addServer (nothing awaits between the prefix mapping and it) and
  removed when addServer settles, so a connect that fails reads as gone,
  which by then it is.
- mcplServerAbsentText: both refusal sites (the model's dispatch and a
  script's executeMcplToolCall) read the set first: "This host is
  connecting to the "<s>" server that provides <tool>, so the call didn't
  run. Try again in a moment."
- Test: connectMcplServerInternal with a handshake the test holds; a call
  in the window reads the connecting text on both paths, and after a
  failed connect, the gone text.
- reaction-baseline-platform's harness, which builds a framework without
  its constructor and runs connectMcplServerInternal, gets the field.
- The fragment's refused-call bullet gains the connecting case.

Merge note (Ada #141998): anima-research#265's refusal of a runtime connect for an id
already registered and anima-research#262's mcpServerIdTaken check ids before a
handshake; whichever lands second counts an in-flight id as taken too.
Cordelia-2434 added 4 commits October 18, 2026 06:35
…doesn't claim it won't reconnect; texts and tests for the connect window

af-w4a-4 (wf-2639, round of af-w4a-3, base 2f8796b) read 2f8796b..7b10c6d.

- I8: close() disarms reconnect, so a request after it read "This host
  won't reconnect to it on its own ... ask whoever runs this host",
  including during a restart's close, the same disorientation as Greptile's
  P2. The connection can't tell a restart's close from a removal's, so it
  now records its own close (closedByThisHost) and a request after it
  reads "The request wasn't sent: this host closed its connection to the
  "<s>" server, so nothing ran.", as the pending-request close text already
  says only what holds in both. A drop without reconnect keeps its text.
- I3: a failed connect with reconnect leaves a registered stub that
  answers (connectWithReconnect's createDisconnectedStub); the comments and
  the gone text's doc said every failed connect reads as gone.
- I1 (intended): the field's doc says one mark per id, since the registry
  holds one connection per id and two connects of one id at once aren't
  supported (Ada's merge note, room-203 #141998).
- I2: the connecting text is for the connect, a runtime one or a
  restart's; the fragment says so. The restart's gap between close and
  connect, with the prefix unmapped, reads main's "Module not found", left.
- I4, I10: the connect-window test checks a call after a connect that
  succeeded (the mark is gone) and a call to another missing server while
  one connects (only the server being connected to reads so).
- I12: mcpl-iserror-failure pins that words with whitespace around them
  pass through as the server's.
- I14: the test's comment matches the gone text's doc.
…eps its class, outcome and data, with W4a's words

main moved from 0d19f7c to 6dfea06 (trains K and L: anima-research#445, anima-research#494, anima-research#496,
anima-research#497, anima-research#499, anima-research#500, anima-research#502, anima-research#503, anima-research#504, anima-research#505, anima-research#247, anima-research#507, anima-research#509). Only
anima-research#247 conflicts, in server-connection.ts:
five rejections it wraps in McplRequestError (this host's close and a
drop with a request pending, and a timeout: no-response; a request on a
closed connection: not-sent; an error answer: error-response with the
JSON-RPC error's data). Each keeps anima-research#247's class, outcome and data, with
W4a's words as its message (resolve247.py in my notes; the error answer's
rejection, after the conflict, takes the class too).

Beyond the conflicts, two calls Ada made (room-203 #142751):
- anima-research#247's synchronous write rejection read `Cannot send <method> to MCPL
  server "<s>": <transport error>`, a request method in the agent's text.
  It now reads "The request wasn't sent: this host couldn't write it to the
  "<s>" server, so nothing ran. Try again later.", and the method, id and
  the transport's words go to a log line (logDetail).
- publishErrorOutcome composes `Publish to <c> failed: ${reason}. Nothing
  was posted.` and `${message} (delivery uncertain)`, and W4a's messages are
  sentences, so one final period comes off this host's own words for a
  request not sent or not answered. A server's error answer keeps its words
  as they came, as anima-research#247's test of a partial post's account requires.

Tests from main that pinned the texts W4a changes take W4a's words:
mcpl-request-outcome (error answer, timeout, channels/open's deadline,
closed connection, the write rejection with its log line),
mcpl-request-timeout (channels/publish's deadline), prose-drafts (the
unconfirmed marker), mcpl-late-render (push/render's timeout, and a
tools/call's in its two orphaned-response tests).
publish-outcome gains a sentence-shaped not-sent message. The fragment
names the write rejection.
…ion beside W4a's error-result line and UNBOUND_REASON

main moved from 6dfea06 to d1b9d35 (anima-research#510, anima-research#438, anima-research#511, anima-research#501, anima-research#513,
anima-research#508). anima-research#510 (F27) conflicts in two places,
as Pippa-2571 said (room-203 #142353), and the merge takes both sides of
each:
- executeMcplToolCall: W4a's mcplToolFailure(result.content, serverId,
  call.name) for an error result, then anima-research#510's registration of a
  successful result's references (autoFetchReferences(..., false)).
- references.ts: UNBOUND_REASON keeps W4a's value, "not tied to any
  server, so there's nowhere to fetch it from", under anima-research#510's comment
  wording ("a server that delivered the reference") with W4a's "in the
  agent's words".
No test from main pins the old unbound or expiry texts.

- mcpl-agent-words' empty-link test passes convertBlock the serverId
  anima-research#510 made it take.
…calling again fails

Desmond-2579's point on af-w4a-4's I8 text (room-203 #143052): "The
request wasn't sent: this host closed its connection to the "<s>"
server, so nothing ran." held for both a restart's close and a removal's,
but gave the agent no next step, and one holds for both. After a restart,
calling again reaches the new connection, or reads the connecting text
while the handshake runs; after a removal, calling again fails, as the
gone text or as the restart gap's unknown tool. So the text adds "If
calling it again fails, ask whoever runs this host." In channel_publish it
reads "… so nothing ran. If calling it again fails, ask whoever runs this
host. Nothing was posted."

mcpl-agent-words' close test and anima-research#247's closed-connection test take it.
Cordelia-2434 added 6 commits October 18, 2026 09:08
…nima-research#193 (train T2), anima-research#512, anima-research#516 and anima-research#519, with no conflicts

Brings in main's first-parent merges since d1b9d35: anima-research#514, anima-research#515 (main at
5f5444d, where 0.22.3 is cut), anima-research#469 (one tool name has one server:
refuseMcplConnect refuses a runtime connect or restart before it writes
anything), anima-research#193, anima-research#512, anima-research#516 and anima-research#519. Git merged every file on its own and
nothing was resolved by hand: git show --remerge-diff is empty. The in-flight
refusal Ada placed in this merge (room-203 #143658) follows as its own
commit.
…in flight is refused before it writes anything

anima-research#469's refuseMcplConnect refuses an id already registered, but the
registry takes a server only once its handshake is done, so a second
connect of an id whose connect is in flight passed it: both connects went
on, the second registered over the first without closing it, and the first
one's settling cleared mcplConnecting's mark while the second's handshake
ran. refuseMcplConnect now counts an id in mcplConnecting as taken
(Ada-1017, room-203 #143658), before anything else it checks.

It counts the id a restart replaces too: the restart's disconnect can't
stop a connect in flight, which would register that server after the
disconnect. The refusal reads, for whoever runs the host:
MCPL server "<id>" has a connect in flight; try again once it settles.

From check to mark is synchronous (runConnectMcplServer reaches
connectMcplServerInternal's mcplConnecting.add with no await between), so two
connects can't both pass the check. A restart checks again in its connect,
after its disconnect's awaits.

Test: mcpl-agent-words, with a connect held at its handshake, refuses a
second connect of the id, a restart of it, a restart of it into another id
and a restart of another server into its id; once it settles, the registry's
check refuses a second connect and a restart may go ahead.
… connect is in flight is refused

The fragment for 8cc1a75, which extends anima-research#469's refuseMcplConnect. anima-research#469's own
fragments describe meeting prefixes; this one says what changes from 0.22.3
for a connect of an id whose connect hasn't settled.
…used too

Desmond-2579's probe (room-203 #144495): nothing stops a connect in flight,
so a disconnect during it removed the server's config and tool prefix, and
the handshake then registered the server without them. No call reached it,
a later connect was refused as already registered, a restart as not
configured, and only another disconnect cleared it, on this branch and on
main 8a32054 alike. The reason the restart is refused, that its disconnect
can't stop the connect, holds for a plain disconnect, so runDisconnectMcplServer
refuses an id in mcplConnecting before it changes anything, with the same
words as refuseMcplConnect, now from one helper, mcplConnectInFlightError.

connectome-host's mcpl_deploy, mcpl_restart and mcpl_unload quote the error
to the agent, so its reader is whoever asked, an operator or an agent.

The in-flight test now also refuses a disconnect and checks it left the
prefix. mcpl-reconnect-reregistration's harness, built from the prototype,
skips field initializers, so it sets mcplConnecting, as mcpl-agent-words'
does. The fragment names the disconnect.
…aced

Two connects of one id at once register in whichever order their handshakes
settle, so the fragment now says whichever settled last replaced the other,
rather than that the second's replaced the first.
…nima-research#521 and anima-research#517 (trains V and W); anima-research#517's ledger assertion takes W4a's text

Brings in main's first-parent merges since 8a32054: anima-research#522, anima-research#204, anima-research#523 (on
d9bd6dc), anima-research#518, anima-research#220, anima-research#521 and anima-research#517. Git merged every file on its own.

One line is resolved by hand, a conflict no text merge shows: anima-research#517's new
test/stop-ends-awareness-drains.test.ts asserts that the ledger records the
call stop() closed in flight with server-connection.ts's old text,
/closed while awaiting response/. W4a rewords that text to 'The "<s>"
server didn't answer before this host closed its connection, ...', and
train X broke on it (Basil-1007, room-203 #145317). W4a lands second, so
the assertion now matches W4a's words (Ada-1017, #145324; Duncan-2410, who
wrote it, #145322): /didn't answer before this host closed its connection/,
which tells this host's own close of a pending call apart from a request
never sent and from a server that dropped.
@nissa-seru
nissa-seru merged commit 0a7fd16 into anima-research:main Oct 18, 2026
6 checks passed
nissa-seru pushed a commit to nissa-seru/agent-framework that referenced this pull request Oct 18, 2026
main moved 93 commits since 9e3e407, among them trains aa and ab: anima-research#284,
anima-research#520, anima-research#461 (the deferral follow-up to anima-research#286) and anima-research#506.

The one conflict, src/framework.ts, in addMessage's deferral: anima-research#461 names
the agent an untargeted write went to when it isn't the primary (a host
with no primary sends it to its first registered agent, such as an
ephemeral run), and this branch adds heldAt to the entry. Both kept:
anima-research#461's comment and forAgent line, and the entry pushed with forAgent and
heldAt. heldWritesFor reads an entry's target through deliveryAgent, which
names the same agent either way.

Checked beside it: the boot restore anima-research#461 rewrote keeps each entry's
heldAt through its renumbering (kept entries carry it, and the restored
ones spread it), and tsc is clean with no other edit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nissa-seru pushed a commit to nissa-seru/agent-framework that referenced this pull request Oct 18, 2026
…s error words, anima-research#204's marker routing, and the recheck just before the publish

main brings 28 pull requests since this branch's last merge of main
(ce1002b), among them anima-research#506 and anima-research#204. Four conflicts, one hunk each:

- src/mcpl/server-connection.ts, handleResponse's error branch: main's
  (anima-research#506). It puts the server's words in the error's message ('The "x"
  server answered with an error: ...', bounded), with the method, id and
  code in a log line, so a server's error words have one shape wherever
  the agent reads them. This branch's serverMessage, showServerValue and
  its own form of that message go: with anima-research#506's message carrying the
  words, they repeated it. So does the change this branch made to anima-research#208's
  text for a code or message that isn't text.
- src/mcpl/channel-registry.ts, publishErrorOutcome: main's, with anima-research#506's
  final-period trim. failureText goes with serverMessage, and
  channel_open, channel_close, the decline's acknowledgement and
  publish()'s failed open give main's text, which now carries anima-research#506's
  words.
- src/framework.ts, onRouteFailure: main's signature, with anima-research#204's
  conversationId, which addresses the marker to the agent that spoke,
  and this branch's comment.
- test/mcpl-request-handler-errors.test.ts: main's.

Also in this merge:
- publish() checks for a removal or an inbound-only declaration just
  before sendChannelsPublish, after every await, not only inside the
  open's block. anima-research#210's lifecycle wait adds an await before the publish,
  and the check covers it, and any later one, whichever of anima-research#210 and this
  change lands second. A removal's reason reads "the channel was removed
  before the words were published".
- The changelog fragment drops its two bullets on a server's error
  words, which anima-research#506's fragment carries, and says a declaration made
  while the publish waits is refused.
- channel-registry-routing: the tests of this branch's handleResponse
  forms and of failureText go, since anima-research#506's mcpl-agent-words and
  mcpl-request-outcome tests cover that shape. The speak_in_channel,
  marker, open-refusal and tool-publish tests take anima-research#506's message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nissa-seru pushed a commit to nissa-seru/agent-framework that referenced this pull request Oct 18, 2026
main moved 71 merges since e670363. Beyond the conflicts, this is where
they meet this branch.

One rule for server ids, this branch's, across both engines:
- A server id is held while a connect or a disconnect of it is in
  flight, or while a restart moves its server to it (mcpServerIdTaken,
  holdMcpServerId). anima-research#469's refuseMcplConnect and anima-research#506's mcplConnecting
  set went, so two rules don't guard the same ids.
- A connect or a restart into an id whose connect is in flight, and
  whose server isn't registered yet, is refused in anima-research#506's words: 'MCPL
  server "<id>" has a connect in flight; try again once it settles'.
- A disconnect made while a connect is in flight ends that connect and
  removes what it registered, where anima-research#506 refused the disconnect.
- anima-research#469's prefixesMeet joins mcplPrefixCollision. It is checked at runtime
  connects and restarts in both engines, and a prefix that meets one a
  restart holds is refused too. create() already checks every configured
  server, a modern one included.
- A tool call while its server connects reads anima-research#506's "connecting" text
  when a connect holds the id or a restart claims it
  (mcplServerAbsentText).

Request errors (anima-research#247, in 0.22.3, and anima-research#506):
- McplRequestError has no code, as 0.22.3 has it. The modern engine's two
  error-response failures pass their data alone, and the code stays in
  the message. The class this branch's first commit added is gone, so
  there's one.
- The legacy engine's request errors take anima-research#506's words. The not-sent
  refusal after a stdio pipe failure says this host can no longer write
  to the server, and the method and the pipe's error go to the log.
- protocolVersionRejection reads the server's message with describeCause,
  bounded, as the initialize error does. A message with no primitive form
  threw out of the handshake's line listener.

Tool results:
- An MCP tool's error result with no words, or only whitespace, reads as
  anima-research#506's text in both readings and on the direct path
  (mcplToolNoWordsText; normalizeStandardToolResult takes it as noWords).
- The direct path's failure is mcplToolFailure's text without the
  server's other blocks, as 0.22.3 gives it, with structured kept by
  presence. A script's structured failure keeps its content.
- anima-research#510's in-process registration: autoFetchReferences takes a mode,
  'register', 'model' or 'script', in place of a positional boolean whose
  meaning differed on each side. executeMcplToolCall registers an MCPL
  peer's references only. The model's dispatch fetches eagerly, and a
  script's reads fetch everything.
- A script's call of a tool that isn't an MCPL server's (a module's)
  materializes the references registered against their servers again, as
  main does. An MCPL server's were fetched at dispatch.
- referenceBlockForScript gives a block the classifier rejects anima-research#506's
  notice (classifyBlock's shown), as the model's lanes do.

Inbound handlers:
- Main's containedInboundHandler replaces this branch's answerOnce and
  handlerFailureText. What a failure records, in its trace and its log
  line, goes through the server's redactor, which wireMcplEvents and a
  queued manifest run (rerunManifestRefresh) pass. It defaults to the text
  as it is.

Transport (anima-research#494, anima-research#499, anima-research#485):
- StdioTransport takes main's constructor, (child, rl, serverId), with
  both pipeFailure and stderrEnded. The handshake's wait for stderr's end
  comes through unchanged.
- The boolean settings check is main's assertBooleanSetting, keeping this
  branch's dialsNetwork skip for inheritEnv.

Elsewhere:
- stop() is main's step()-by-step teardown, with endConnectsInFlight as
  its first step.
- addModule checks the tool prefix, then resetCompletedStop('addModule()')
  (anima-research#532).
- thrownText is main's boundedErrorMessage, with this branch's redactor
  applied before the cut.
- Changelog: this branch's fragments say what changes from 0.22.3. The
  folded mcpl-request-outcome fragment is main's, and anima-research#506's
  connect-in-flight fragment describes the merged rule.
- Tests: main's new stub servers answer protocolVersion. Words and log
  lines follow the merged code. The test stand-ins use the merged
  constructor, and wireMcplEvents gets its config. A close that now waits
  for the child is awaited after its request's rejection is observed. Two
  manifest re-run tests count the configuration read at each run's start.
  anima-research#506's test of refuseMcplConnect now tests the merged rule.

Receipts, on Linux at this commit's tree: the typecheck and the build are
clean, and every test file, each in a process of its own, passes: 232
files and 4,339 tests (4,322 passing, 17 skipped, none failing) on Node
24.22.0, and the same on Node 20.20.2, with CI's stop pair passing on
both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nissa-seru pushed a commit to nissa-seru/agent-framework that referenced this pull request Oct 18, 2026
…ze, two stubs drop mcplConnecting, and anima-research#506's fragment names the restart it covers

- mcpl-stderr-tail: the startup test's server ends in EXIT_ON_INITIALIZE,
  as the file's other servers do, so its failure takes the same path on
  every run.
- mcpl-reconnect-reregistration and reaction-baseline-platform no longer
  seed mcplConnecting, which the merge removed and nothing reads.
- mcpl-connect-in-flight.fixed.md: the refusal in its words is for a
  runtime connect, or a restart of another server into the id. A restart
  of the connecting server itself is refused first by the restart's own
  check, which says it is being connected.

The three test files pass at this commit on Node 24.22.0: 21, 4 and 8
tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nissa-seru pushed a commit to nissa-seru/agent-framework that referenced this pull request Oct 18, 2026
…handshake only

The in-flight refusal's doc and its changelog fragment said the words
hold in either engine. They hold where mcpServerIdTaken finds a connect in
flight and no server registered: a legacy connect's handshake, since the
legacy registry takes a server once its handshake is done. A modern
connect registers its connection first, so a second connect of its id
reads as already registered, and a tool call during it reads that
connection's own refusal. Both texts now say so, and
mcplServerAbsentText's doc says when its "connecting" text applies.

Comments and changelog only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tel-0s added a commit that referenced this pull request Oct 18, 2026
…eld writes for modules and the modern MCP client meet the registry

What main brings since 3af13bf (97 merges): the 0.22.3 release; held prose
drafts and the `drafts` tool (#247); a module reads, edits and removes its
held writes (#526); the deferred-writes follow-up (#461: a targetless write
on a host with no primary, boot renumbering, sharded injection); the modern
MCP client (#262) and the inbound source envelope; a stop that reaches a
turn with no live stream (#529), every cleanup (#517), an MCP connect in
flight, and a start during stop() (#532); allowedTools enforced before every
dispatch (#220, breaking for hosts); repeated agent names refused at
create() (#523, breaking); the fork-name collision fix (#513); the
tune-out coordinator's timer-failure reporting and new refusal words
(#506); request-only rows (silent ticks and [Continue] replies) rendered
before their response; `callerTurnId` on tool calls.

Conflicts (36 hunks, 7 files), and what was decided:

- src/framework.ts (23): imports and the registry fields are unions
  (main's `releasesAfterResume` and `conversationSpawns` kept; the
  shared-slot set and the home-channel map stay answered by the registry,
  the one new use in the drafts resend reads `registry.homeChannel`).
  `isAddressedMessage` stays tag-only; main's call sites pass tags alone.
  Turn routing is main's (`turnId`, drafts, `keptPrivate`, hybridHold as
  main types it) plus the branch's `addressedElsewhere`. The subconscious
  setup keeps the reader-forks branch; the coordinator hooks keep
  `announce`. `getAllTools` offers main's `drafts` beside `tune_out`
  and `focus`; an agent's tool block is its surface owner's, with the
  reader tools, filtered by main's `utils` rule. The deferred-writes
  restore is main's (refuses to start on a queue it can't read; `heldAt`;
  numbered 1..N) with the branch's re-wake for a restored tune-out release.
  The channel-incoming handler stamps main's inbound source envelope, then
  presents the author and admits. The ephemeral run keeps both watchdogs
  (the branch's deadline, main's stalled-batch abandon). Dispatch sets
  `callerTurnId` then applies the branch's deny-at-dispatch.
  `endEphemeralRunRefused` is gone: main's #532 refuses the start itself.
  `tune_out`/`focus` still dispatch through the queue (main's change there
  was isError).
- src/tune-out/coordinator.ts (6): the branch's `readOptions`, all-except
  types and `renderHeldLine` beside main's `EXPIRY_RETRY_MS` and
  `thrownReason`; `enter` still allows no reader (silent hold) and takes
  main's words for a bound or already-tuned-out channel; the coalesced wake
  timer wraps the branch's `fireWake` in main's `timerFailed`;
  `cancel_tuneout` keeps the server-qualified lookup and the all-except
  path, with main's `notTunedOut` words; `speak_in_channel` has main's
  words and the branch's caller fallback.
- src/agent.ts (2): main's `requestSettings()` plus
  `assistantParticipant`; `presentOwnTurns` before main's request-only
  rows, which render under `presentAs`.
- src/mcpl/channel-registry.ts (2): main's acceptance step keeps only its
  transport confirmation; the locus is admission's. `onRouteFailure` is
  main's type (with `outcome`).
- src/index.ts, src/types/trace.ts, test/tune-out-timers.test.ts: unions.

Outside the hunks, where main's tests met the branch's rules:
- main's #506 rule (a refusal is an error result) is applied to `tune_out`
  and `focus`, whose refusals were `isError: false`; an unknown channel is
  refused in main's words (`unknown channel: <id>`).
- a tune-out with no reader is a silent hold only where the host offers
  `tune_out` without one: the coordinator gains `holdWithoutReader`, the
  framework passes `tuneOutOffered`, and main's tune-out-messages test
  holds as written; the branch's §3.3 row opts its host in.
- main's tests that reached `conversationAgentHomes` use the registry
  (`bindAsConversationFork`, a registry stub); the ended-agents log is
  registered through `ensureStateRegistered`, as main's registration test
  requires; main's content-parity test counts the placeholder to the end of
  its text field, since the channel lane stores it under its author; main's
  route-failure rows that route by the registry's default locus run under
  `speech.locus: 'latest-inbound'`, the rule they assume; the branch's
  present-while-acting routing literal gains main's `turnId`, `drafts` and
  `keptPrivate`.
- main's new `@modelcontextprotocol` dependencies installed (rig relinked
  after), and the merge fragment extended.

Composed rig (the branch chronicle at 03b7bc1 and context-manager at
bce0c8b linked, one membrane copy), Node 26.5.0: `npm test` 4707 tests,
4679 pass, 0 fail, 28 skipped. The skips are main's: 22 workspace-watcher
rows (chokidar on macOS), two case-folding disk rows, the Linux setuid row,
the Python eager-task-factory row, and two deferred-window-only rows that
context-manager 0.15 makes moot. Typecheck clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
nissa-seru pushed a commit that referenced this pull request Oct 19, 2026
…t, in #506's words

#506 gave the MCPL texts an agent reads the words an agent can act on:
what happened, whether anything took effect, and what works from there,
with the method, the code and the close's details in the host's log.
The modern engine, which #262 added and no release has shipped yet,
still said "MCP server "m" tools/call "x" was not sent: …", "returned
error for tools/call "x": [-32603] …" and "did not answer tools/call "x"
within 60000ms. Cancellation was requested; …". This gives it the same
words where the engines share an outcome, and their shape where they
don't, and the connect-in-flight refusal the same reach.

The modern engine's request failures (requestFailure), each with its
outcome as before:
- No session (closed, or lost and not yet back): legacy's text for a
  request on a closed connection, saying whether this host is
  reconnecting, or, after this host's own close, only that.
- Refused before the wire: after a stdio pipe failure, that this host
  can no longer write to the server, and whether it's reconnecting;
  otherwise that it couldn't send it, and to ask whoever runs this host
  if calling again fails, since the cause can pass (the stdio line
  closing as the connection is lost) or stay (an outputSchema the SDK
  can't compile).
- An error answer: the server's own words, trimmed and bounded, or, with
  none, that the effect is unknown. The code goes to the log.
- A complete answer the SDK rejects: that it answered in a form this host
  can't read, so the effect is unknown. Still a plain error.
- Another round asked for and not sent, or an answer that isn't a complete
  result: the effect is unknown.
- HTTP 401/403: the server refused this host access, so the request didn't
  run; on a later round, the effect is unknown. The status stays in
  data.httpStatus.
- The deadline: this host asked the server to cancel, and the effect is
  unknown.
- Anything else after the request went out: cut off by this host's
  close, or its connection closed under it (request() passes
  session.lost), the legacy engine's words for each, the second saying
  whether this host is reconnecting; otherwise it failed before an answer
  came back, so the effect is unknown, and as the connection may still
  stand, as after an HTTP 500, it says nothing of reconnecting.
A tools/list failure, read only by the refresh's trace, says listing
changes nothing on the server, so trying again is safe, where a tool
call's says to check its effect. Each failure the SDK reports writes one
log line with the method, the tool as a log field, and the code, HTTP
status, deadline or transport's words, redacted; a refusal with no
session writes none, as legacy's on a closed connection. The server's
error words reach the caller unredacted, trimmed and bounded as before,
and the log redacted, whatever JSON value they are (logValueRedacted in
log-value.ts, for both engines).

The legacy engine: #506's log line for a server's error answer now
prints the message redacted, through logValueRedacted. A connection that
doesn't reconnect kept no configuration, so the line had nothing to
redact with; connectChecked now keeps it for every connection, and
reconnecting still turns on reconnect: true alone (reconnectEnabled).

The framework:
- mcpServerIdTaken: while any connect holds the id, in either engine, a
  second connect, or a restart of another server into it, reads #506's
  in-flight words. A modern connect registers its connection before it
  starts, and a legacy one registers its server once its handshake is
  done, so both read "already registered" for part of their connect
  before.
- A tool call, the model's or an in-process one, routed to a modern
  connection whose connect hasn't brought its first session up reads the
  "connecting" text (modernStillConnecting), as one during a legacy
  handshake does, where it read the connection's "not established".
- runConnectMcplServer refuses the id before it reads the server's
  restored trees, as main's order did, so a refused connect reads none.

Changelog: the unreleased modern-mcp-client fragment says its failures
read as an MCPL server's where the two share an outcome, and in their
shape where they don't, and a call during its connect as one during an
MCPL connect; mcpl-connect-in-flight says either engine again;
mcpl-agent-words says the error answer's log line has the message
redacted.

Tests: the modern outcome tests pin each text and its log line, the
no-session texts after a loss with and without reconnect and after this
host's close, a request a host close cuts off, one whose child dies, and
the log redacting an echoed token as a string and inside an object;
mcp-modern-http the HTTP status in the log line; mcpl-agent-words the
legacy line redacting an echoed token likewise; mcp-modern-framework a second connect and a tool call
during a modern connect; mcpl-agent-words the connecting text on both
paths and a call once the session is up, and the in-flight words after a
legacy server registers; mcp-server-ownership that a refused connect
reads no trees, and that a disconnect during a handshake whose child
can't be reaped rejects and keeps the server registered (the second
removal in runDisconnectMcplServer, which no test pinned).

Receipts, on Linux. With this commit's src and tests, on Node 24.22.0:
the twelve changed or most affected test files pass, 271
tests; with src and changelog.d taken from main 5227741 and these tests,
24 tests fail across six files; and each of 35 pieces, removed on its
own, fails at least one test. This commit merged with main ce1a073,
each test file in a process of its own: the typecheck and the build are
clean, and 234 files and 4,421 tests (4,404 passing, 17 skipped, none
failing) pass on Node 24.22.0, and the same on Node 20.20.2, with CI's
stop pair passing on both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nissa-seru added a commit that referenced this pull request Oct 19, 2026
fix(mcp): the modern engine's failed requests, and a connect in flight, in #506's words
nissa-seru pushed a commit to nissa-seru/agent-framework that referenced this pull request Oct 19, 2026
…anima-research#506's words and F240

main moved 331 commits on from 9e3e407, among them anima-research#262 (the modern MCP
client and the legacy engine's protocol boundary), anima-research#506 (the agent's words
for failed requests), anima-research#532, anima-research#537, anima-research#210 and anima-research#542 (F240: a redaction that
can't read its configuration withholds the text). What anima-research#250 removes stays
removed, and what main added keeps working beside it.

Conflicts:
- src/framework.ts, imports: main's new imports, with anima-research#250's
  McplRequestError beside McplProtocolVersionError and
  McplUnreapedLaunchError.
- src/framework.ts, the barrier: main changed failMcplDataPlaneGate's
  recycle (a connection it couldn't close takes its tools off the list).
  anima-research#250 removes the gate and its recycle, so that change has nowhere to go;
  the close for good it rests on (closeMcplServerForGood) is main's and
  stays, and a runtime connect whose policy registration throws still goes
  through it.
- src/framework.ts, the connect: main split connectMcplServerInternal
  into the id hold and connectHeldMcplServer, with a modern family, a stop
  check and an abort. That structure is main's. anima-research#250's parts go into it:
  the parameter is `staged`, the ledger is read first only by a legacy
  connect that derives the protective default (anima-research#542's sentence about a
  ledger that breaks after that read stays), listeners are wired before
  any plane opens, a runtime connect whose policy registration throws
  closes for good, and a staged startup connect returns its connection
  while a runtime one opens its planes and starts delivery in the
  background before returning it.
- src/framework.ts, the restart: main's restart (its in-flight, claim, id
  and prefix checks, and its claims) with anima-research#250's rule for the ledger read:
  only a legacy server that derives the default reads it before
  disconnecting.
- src/mcpl/server-connection.ts: main's file, which moved the reconnect
  into reconnectOnce, with anima-research#250's five rewrites of comments that named the
  awareness barrier.
- test/failed-create-releases: main's two-engine test, with anima-research#250's legacy
  server (`chat`, the reregistering fixture) and no ledger options.
- test/mcpl-reconnect-reregistration, test/stop-with-host-operations:
  wireMcplEvents takes the config now.
- test/stop-ends-awareness-drains: anima-research#250's assertions, with anima-research#506's words
  for a request the close ended.
- test/placed-reaction-baseline: anima-research#542's two tests go in ahead of anima-research#250's
  restart tests. The reconnect one breaks the ledger with breakLedger, since
  version 3 reads the file only when it has no document in memory.
- test/mcpl-awareness-barrier.test.ts and its fixture stay deleted. Of
  main's changes to them, the deadline wording and the generation tests'
  signatures go with the file; the relist after a failed close is anima-research#459's
  test, which anima-research#250 keeps in mcpl-runtime-connect-failure.test.ts, so that
  assertion moves there.

Outside the conflicts:
- test/fixtures/reregistering-mcpl-server.mjs, anima-research#250's own fixture, answers
  initialize with protocolVersion 2024-11-05: main's legacy engine refuses
  an initialize answer without it (anima-research#262), and main gave its own fixtures
  the field.
- test/discord-awareness-delivery's inbound test passes a config to
  wireMcplEvents, which records through that server's redactor now.
- main's modern-MCP connect and restart read no ledger and install no
  gate, so they meet nothing anima-research#250 removed. Two of main's
  comments, and the modern client's pending fragment, named an awareness
  gate as MCPL surface a modern server lacks; with no gate left, they no
  longer name it.
- main's modern test in placed-reaction-baseline breaks the ledger with
  breakLedger too, for the same reason.
- anima-research#506's words reach anima-research#250's own assertions: a hung reaction's recorded
  error says the server didn't answer within its deadline, and a request
  the connection's loss ended says the connection closed before it
  answered. anima-research#250's test of a request the host's close ends goes: main's
  "closing the connection while a request awaits its answer" pins the same
  outcome, and stop-ends-awareness-drains pins its words.
- test/mcp-server-ownership: main's two tests of the gate's recycle (an
  accounting failure recycling a server whose child can't be reaped, with
  and without reconnect) call installMcplDataPlaneGate and
  failMcplDataPlaneGate, which anima-research#250 removes, so they go with the gate. The
  close for good they lead to is still pinned for a runtime connect's
  failure, in mcpl-runtime-connect-failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nissa-seru pushed a commit to nissa-seru/agent-framework that referenced this pull request Oct 19, 2026
…arch#506's words and anima-research#542

main moved 331 commits on from 9e3e407, among them anima-research#262 (the modern MCP
client and the legacy engine's protocol boundary), anima-research#506 (the agent's words
for failed requests), anima-research#532, anima-research#537, anima-research#210 and anima-research#542 (a redaction that can't
read its configuration withholds the text). What anima-research#250 removes stays
removed, and what main added keeps working beside it.

Conflicts:
- src/framework.ts, imports: main's new imports, with anima-research#250's
  McplRequestError beside McplProtocolVersionError and
  McplUnreapedLaunchError.
- src/framework.ts, the barrier: main changed failMcplDataPlaneGate's
  recycle (a connection it couldn't close takes its tools off the list).
  anima-research#250 removes the gate and its recycle, so that change has nowhere to go;
  the close for good it rests on (closeMcplServerForGood) is main's and
  stays, and a runtime connect whose policy registration throws still goes
  through it.
- src/framework.ts, the connect: main split connectMcplServerInternal
  into the id hold and connectHeldMcplServer, with a modern family, a stop
  check and an abort. That structure is main's. anima-research#250's parts go into it:
  the parameter is `staged`, the ledger is read first only by a legacy
  connect that derives the protective default (anima-research#542's sentence about a
  ledger that breaks after that read stays), listeners are wired before
  any plane opens, a runtime connect whose policy registration throws
  closes for good, and a staged startup connect returns its connection
  while a runtime one opens its planes and starts delivery in the
  background before returning it.
- src/framework.ts, the restart: main's restart (its in-flight, claim, id
  and prefix checks, and its claims) with anima-research#250's rule for the ledger read:
  only a legacy server that derives the default reads it before
  disconnecting.
- src/mcpl/server-connection.ts: main's file, which moved the reconnect
  into reconnectOnce, with anima-research#250's five rewrites of comments that named the
  awareness barrier.
- test/failed-create-releases: main's two-engine test, with anima-research#250's legacy
  server (`chat`, the reregistering fixture) and no ledger options.
- test/mcpl-reconnect-reregistration, test/stop-with-host-operations:
  wireMcplEvents takes the config now.
- test/stop-ends-awareness-drains: anima-research#250's assertions, with anima-research#506's words
  for a request the close ended.
- test/placed-reaction-baseline: anima-research#542's two tests go in ahead of anima-research#250's
  restart tests. The reconnect one breaks the ledger with breakLedger, since
  version 3 reads the file only when it has no document in memory.
- test/mcpl-awareness-barrier.test.ts and its fixture stay deleted. Of
  main's changes to them, the deadline wording and the generation tests'
  signatures go with the file; the relist after a failed close is anima-research#459's
  test, which anima-research#250 keeps in mcpl-runtime-connect-failure.test.ts, so that
  assertion moves there.

Outside the conflicts:
- test/fixtures/reregistering-mcpl-server.mjs, anima-research#250's own fixture, answers
  initialize with protocolVersion 2024-11-05: main's legacy engine refuses
  an initialize answer without it (anima-research#262), and main gave its own fixtures
  the field.
- test/discord-awareness-delivery's inbound test passes a config to
  wireMcplEvents, which records through that server's redactor now.
- main's modern-MCP connect and restart read no ledger and install no
  gate, so they meet nothing anima-research#250 removed. Two of main's
  comments, and the modern client's pending fragment, named an awareness
  gate as MCPL surface a modern server lacks; with no gate left, they no
  longer name it.
- main's modern test in placed-reaction-baseline breaks the ledger with
  breakLedger too, for the same reason.
- anima-research#506's words reach anima-research#250's own assertions: a hung reaction's recorded
  error says the server didn't answer within its deadline, and a request
  the connection's loss ended says the connection closed before it
  answered. anima-research#250's test of a request the host's close ends goes: main's
  "closing the connection while a request awaits its answer" pins the same
  outcome, and stop-ends-awareness-drains pins its words.
- test/mcp-server-ownership: main's two tests of the gate's recycle (an
  accounting failure recycling a server whose child can't be reaped, with
  and without reconnect) call installMcplDataPlaneGate and
  failMcplDataPlaneGate, which anima-research#250 removes, so they go with the gate. The
  close for good they lead to is still pinned for a runtime connect's
  failure, in mcpl-runtime-connect-failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nissa-seru pushed a commit to nissa-seru/agent-framework that referenced this pull request Oct 19, 2026
…arch#506's words and anima-research#542

main moved 331 commits on from 9e3e407, among them anima-research#262 (the modern MCP
client and the legacy engine's protocol boundary), anima-research#506 (the agent's words
for failed requests), anima-research#532, anima-research#537, anima-research#210 and anima-research#542 (a redaction that can't
read its configuration withholds the text). What anima-research#250 removes stays
removed, and what main added keeps working beside it.

Conflicts:
- src/framework.ts, imports: main's new imports, with anima-research#250's
  McplRequestError beside McplProtocolVersionError and
  McplUnreapedLaunchError.
- src/framework.ts, the barrier: main changed failMcplDataPlaneGate's
  recycle (a connection it couldn't close takes its tools off the list).
  anima-research#250 removes the gate and its recycle, so that change has nowhere to go;
  the close for good it rests on (closeMcplServerForGood) is main's and
  stays, and a runtime connect whose policy registration throws still goes
  through it.
- src/framework.ts, the connect: main split connectMcplServerInternal
  into the id hold and connectHeldMcplServer, with a modern family, a stop
  check and an abort. That structure is main's. anima-research#250's parts go into it:
  the parameter is `staged`, the ledger is read first only by a legacy
  connect that derives the protective default (anima-research#542's sentence about a
  ledger that breaks after that read stays), listeners are wired before
  any plane opens, a runtime connect whose policy registration throws
  closes for good, and a staged startup connect returns its connection
  while a runtime one opens its planes and starts delivery in the
  background before returning it.
- src/framework.ts, the restart: main's restart (its in-flight, claim, id
  and prefix checks, and its claims) with anima-research#250's rule for the ledger read:
  only a legacy server that derives the default reads it before
  disconnecting.
- src/mcpl/server-connection.ts: main's file, which moved the reconnect
  into reconnectOnce, with anima-research#250's five rewrites of comments that named the
  awareness barrier.
- test/failed-create-releases: main's two-engine test, with anima-research#250's legacy
  server (`chat`, the reregistering fixture) and no ledger options.
- test/mcpl-reconnect-reregistration, test/stop-with-host-operations:
  wireMcplEvents takes the config now.
- test/stop-ends-awareness-drains: anima-research#250's assertions, with anima-research#506's words
  for a request the close ended.
- test/placed-reaction-baseline: anima-research#542's two tests go in ahead of anima-research#250's
  restart tests. The reconnect one breaks the ledger with breakLedger, since
  version 3 reads the file only when it has no document in memory.
- test/mcpl-awareness-barrier.test.ts and its fixture stay deleted. Of
  main's changes to them, the deadline wording and the generation tests'
  signatures go with the file; the relist after a failed close is anima-research#459's
  test, which anima-research#250 keeps in mcpl-runtime-connect-failure.test.ts, so that
  assertion moves there.

Outside the conflicts:
- test/fixtures/reregistering-mcpl-server.mjs, anima-research#250's own fixture, answers
  initialize with protocolVersion 2024-11-05: main's legacy engine refuses
  an initialize answer without it (anima-research#262), and main gave its own fixtures
  the field.
- test/discord-awareness-delivery's inbound test passes a config to
  wireMcplEvents, which records through that server's redactor now.
- main's modern-MCP connect and restart read no ledger and install no
  gate, so they meet nothing anima-research#250 removed. Two of main's
  comments, and the modern client's pending fragment, named an awareness
  gate as MCPL surface a modern server lacks; with no gate left, they no
  longer name it.
- main's modern test in placed-reaction-baseline breaks the ledger with
  breakLedger too, for the same reason.
- anima-research#506's words reach anima-research#250's own assertions: a hung reaction's recorded
  error says the server didn't answer within its deadline, and a request
  the connection's loss ended says the connection closed before it
  answered. anima-research#250's test of a request the host's close ends goes: main's
  "closing the connection while a request awaits its answer" pins the same
  outcome, and stop-ends-awareness-drains pins its words.
- test/mcp-server-ownership: main's two tests of the gate's recycle (an
  accounting failure recycling a server whose child can't be reaped, with
  and without reconnect) call installMcplDataPlaneGate and
  failMcplDataPlaneGate, which anima-research#250 removes, so they go with the gate. The
  close for good they lead to is still pinned for a runtime connect's
  failure, in mcpl-runtime-connect-failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant