Skip to content

fix(mcpl): fetch_reference's failures say what happened to the reference and what calling again can do - #531

Merged
nissa-seru merged 5 commits into
anima-research:mainfrom
nissa-seru:fix/fetch-reference-words
Oct 18, 2026
Merged

nissa-seru merged 5 commits into
anima-research:mainfrom
nissa-seru:fix/fetch-reference-words

Conversation

@nissa-seru

Copy link
Copy Markdown
Contributor

fetch_reference's failures now say what happened to the reference and what calling again can do, and save_recent_image says both where it tells the agent a reference can't be fetched. The eager fetch's and a script's fetch's log lines read the same account of what happened.

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 follows #506, whose words for a reference's expiry and missing server it uses, and covers the reference fetcher (src/mcpl/reference-fetcher.ts) and the two tools that show its failures.

Problem

On main, fetch_reference hands the agent the fetcher's reason alone. For example:

What happened main answers
The server answers 503 HTTP 503
A claim over the call's max_bytes claimed 10000000 bytes exceeds fetch ceiling 1000
A download stopped at max_bytes aborted: actual bytes exceeded ceiling at 65536
A reference on another origin third-party origin (https://cdn.example.com != dialed http://127.0.0.1:8080); not fetched by this host
The fetch takes too long fetch timed out or aborted
The workspace can't store it no writable workspace mount to store the payload
  • The agent isn't told whether calling again can work: a 503 may clear, a 404 won't until a fresh reference arrives, and another origin never will.
  • Where a larger max_bytes would help, the agent isn't told how large, or that the server's budget caps it.
  • Every failure to store reads no writable workspace mount, though the workspace may be missing or its write may have failed, and the log doesn't say which. A save that throws reaches the agent as the fetch's own error.
  • save_recent_image gives the reason a fetch would be refused, and nothing about what could change it.
  • A ws: reference on its server's own origin passes the scheme check, since an origin reads ws: as http:. fetch then refuses it before connecting, and the agent reads fetch failed.
  • A reference whose address carries a user name or password passes the origin check, since an origin leaves them out. fetch refuses it before connecting, with a message that holds the whole address, password included. The agent reads that message as the fetch's error, and the host's log carries it for the eager fetch.

What changes

  • Every failure is a clause and a remedy. The fetcher says what happened as a clause about the reference, and what calling again can do as a sentence of its own.
    • fetch_reference reads fetch_reference couldn't fetch "<ref_id>": <clause>. <remedy>.
    • save_recent_image reads It can't be fetched either — <clause>. <remedy>.
    • The eager and script-demand fetches log the clause.
  • The remedies say what can work: trying again later, waiting for a fresh reference, passing a larger max_bytes (with the size it needs, and no more than the server's budget has left), asking whoever runs the host (to check its connection or its credentials for the server, raise or renew a spent budget, add back a server it no longer has, or correct a server's address), or that nothing will.
  • The scheme check reads the reference's own scheme, so a ws: or wss: reference is refused before dialing.
  • A reference whose address carries a user name or password is refused before dialing, in words that don't repeat them.
  • The host's log says why a fetched payload wasn't stored, for each way the save can fail.

Code changes beyond the text

  • FetchOutcome is a union. Its failure side carries error and remedy as strings, and each side declares the other's fields undefined, so callers read them as before. A failure path that gives no remedy doesn't compile.
  • fix(mcpl): a reference from a server with no address says so when refused #518's Refusal carries { error, remedy } in place of { reason, ofReference }. ofReference let fetch_reference put reference before the reference's own phrases. Each clause now has its own subject, as in the reference is expired.
  • The scheme check reads new URL(uri).protocol. The plaintext reference from a secure origin branch goes: origins compare with their schemes before it, so it never ran. A test pins that an http: reference from a server dialed over wss: is still refused, by the origin comparison.
  • The origin check refuses a reference address with a user name or password, before the scheme check, so refusal() and the eager check refuse it too.
  • A server address that doesn't parse is refused with a clause of its own, where it read server url does not parse, and a remedy naming whoever runs the host, who can correct it. The framework's resolver gives only an address its transport dials (fix(mcpl): a reference from a server with no address says so when refused #518), so this guards other resolvers.
  • A claim past the host's own 256 MiB is refused for that first, before the server's budget, since nothing lifts it, not even a budget whoever runs the host raises. A download past it is stopped for it first in the same way, before the budget and a call's max_bytes.
  • The budget's stop partway shows the server's running total, as the next refusal does, since other fetches through the server may have spent part of it.
  • The save hook logs each null with why. The fetcher catches a throw from it, logs it, and answers as a storage failure.
  • A thrown fetch's clause takes its cause's words, since fetch's own message is only fetch failed. Where the cause has no words of its own, as an AggregateError from a host whose every address refused, its attempts' stand in.
  • noAddressReason reads as a clause.
  • DEFAULT_FETCH_MAX_BYTES's comment says a call can lower it and nothing raises it, where it said it was overridable per server.
  • fix(mcpl): a reference from a server with no address says so when refused #518's changelog entry, not yet released, quotes the no-address refusal's old words. It now quotes these, and this change's own entry doesn't repeat them.

Each message

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

The remedies, by name:

  • Later: Trying again later may work.
  • Request failed: Trying again later may work; if it fails the same way, whoever runs this host can check its connection to the server.
  • Fresh reference: Once a fresh reference to it arrives, fetching may work; until then, fetching it again won't.
  • Nothing: Calling again won't change that.
  • Budget: The budget doesn't renew on its own, so calling again won't change that unless whoever runs this host raises or renews it.
  • Host ceiling: No max_bytes or setting lifts that limit, so calling again won't change it.

Before dialing

  • No server delivered it: reference not tied to any server, so there's nowhere to fetch it from → it's not tied to any server, so there's nowhere to fetch it from. Fresh reference.
  • A server this host no longer has: unknown server: <s> → this host no longer has the "<s>" server it came from. Calling again won't change that unless whoever runs this host adds that server back.
  • A server with no address: no address: this host doesn't connect to server <s> over a network, so there's nowhere to fetch it from → this host doesn't connect to the "<s>" server over a network, so there's nowhere to fetch it from. Nothing.
  • An address that doesn't parse: reference uri does not parse → its address doesn't parse as a URL. Nothing. The server's own: server url does not parse → the "<s>" server's address doesn't parse as a URL, so there's nowhere to fetch it from. Calling again won't change that unless whoever runs this host corrects the server's address.
  • A user name or password in the address: fetch's own message, Request cannot be constructed from a URL that includes credentials: <the whole address> → its address carries a user name or password, which this host doesn't send. Nothing.
  • Another scheme: scheme not allowed: <scheme> → its address uses <scheme>, and this host fetches only http: and https: addresses. Nothing.
  • Another origin: third-party origin (<origin> != dialed <origin>); not fetched by this host → it points to <origin>, not to the "<s>" server's own address (<origin>), and this host fetches only from the server itself, where the server's address is its scheme and host as configured, such as wss://…, never its userinfo, path or query. Nothing.
  • Expired: reference expired, and the two reference treated as expired, … → the reference is expired, the reference is treated as expired, …. Fresh reference.
  • The server's budget is spent: per-server fetch budget exhausted (<used>/<budget> bytes); the budget doesn't renew on its own, … → this host's fetch budget for the "<s>" server is used up (<used> of <budget> bytes). Budget.
  • A claim over the call's ceiling and over what's left of the budget: claimed <n> bytes exceeds what's left of the per-server fetch budget (<used>/<budget> bytes); … → it claims <n> bytes, more than the <left> bytes left of this host's fetch budget for the "<s>" server. Budget.
  • A claim over the call's max_bytes: claimed <n> bytes exceeds fetch ceiling <m> → it claims <n> bytes, over the <m> bytes this call allows. Pass max_bytes of at least <n> to fetch it. Over the host's own 256 MiB, which is checked before the budget: it claims <n> bytes, over the 268435456 bytes this host fetches at most. Host ceiling.

Dialing and reading

  • A redirect: redirect refused (HTTP <status>) — no redirect traversal → the "<s>" server answered HTTP <status>, a redirect, and this host doesn't follow redirects. Nothing.
  • Another status: HTTP <status> → the "<s>" server answered HTTP <status>.
    • 408, 409, 423, 425, 429, and a 5xx other than 501 and 505: later.
    • 501 and 505, which say the server doesn't support the request: nothing.
    • 401: Calling again won't change that; whoever runs this host can check its credentials for the server.
    • 403: Once a fresh reference to it arrives, fetching may work; if that's refused too, whoever runs this host can check its credentials for the server.
    • The other 4xx: fresh reference.
  • No body: no response body → the "<s>" server answered without any content. Fresh reference.
  • The budget runs out partway: aborted: per-server fetch budget exhausted (<total>/<budget> bytes); … → this host's fetch budget for the "<s>" server ran out during the download (<total> of <budget> bytes), so it was stopped and nothing was kept. Budget.
  • Past the call's max_bytes: aborted: actual bytes exceeded ceiling at <n> → the download had reached <n> bytes, past the <m> bytes this call allows, so it was stopped and nothing was kept. It's at least <n> bytes; a larger max_bytes, up to <k>, may fetch it. Here <k> is the host's ceiling or what the server's budget has left after these bytes, whichever is less. Where <k> is less than <n>, the budget is what binds, and the remedy says so and gives the budget's remedy. Past the host's own 256 MiB, which is checked first, before the budget and the call's max_bytes: the download had reached <n> bytes, past the 268435456 bytes this host fetches at most, so it was stopped and nothing was kept. Host ceiling.
  • The digest doesn't match: digest mismatch (claimed <d>…, got <g>…) → the bytes that arrived don't match the reference's digest (<d>…, against <g>…), so they weren't kept. Trying again may work; if it fails the same way, fetching may work once a fresh reference to it arrives.
  • The workspace can't store it: no writable workspace mount to store the payload → it was fetched, but this host couldn't store it in its workspace, so nothing was kept. Calling again is likely to fail the same way; whoever runs this host can see why in its log.
  • The fetch takes too long: fetch timed out or aborted → it took longer than <n> seconds. Later.
  • The request fails: the error's own message, usually fetch failed → the request to the "<s>" server failed: <its cause>. Request failed.

What each part is for

This change exists so that an agent whose fetch failed knows whether calling again can work, and what it would take.

  • The clause and the remedy apart: the clause is true wherever it's read, the log lines included, and the remedy is for whoever can call again. save_recent_image shows both. None of the remedies its refusals reach names a call's own max_bytes, since fix(mcpl): a reference from a server with no address says so when refused #518's refusal() uses the host's ceiling.
  • The union: without it, a failure path with no remedy would end the agent's sentence on undefined, and nothing would say so until an agent read it.
  • statusRemedy: one function from a status to what a later call can do, so each status class has one answer.
  • largerMaxBytes: a larger max_bytes is offered only up to what can be fetched, so the agent isn't sent to try a size the budget then refuses.
  • configuredOrigin: the origin clause names the server's address as whoever runs the host configured it. URL's own origin reads null for a scheme that isn't special, and the normalized origin the comparison uses reads wss: as https:.
  • whyFetchFailed: without it, every network failure reads fetch failed.
  • The save hook's log lines: the storage remedy sends the agent to whoever runs the host, who needs to know why.
  • The scheme check on the reference's own scheme: without it, a ws: reference is dialed, fails, and the agent is told a later try may work.
  • The check for a user name or password in the address: without it, the password reaches the agent's context and the host's log in fetch's message, and the agent is told a later try may work.
  • Not taken:
    • One remedy for every failure, such as calling again may not help. It would leave the agent where main does.
    • Naming fetch_reference in the remedies. The fetcher serves the eager pass and scripts too, and save_recent_image shows its remedies.
    • Keeping ofReference. Each clause carries its own subject.
    • Sending the agent to whoever runs the host for a server with no address. A stdio server has no address to give, so there's nothing to ask for.

What callers will notice

  • Every text above changes. 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. Two strings match, and neither reads the fetcher's: context-manager's kv-unified exact label propagation exceeded ceiling … and membrane's OpenAI Responses API returned no response body.
  • FetchOutcome is a union, and Refusal has error and remedy in place of reason and ofReference. Neither is exported from the package's entry point.
  • The host's log gains a line for each payload it fetched but couldn't store.
  • A ws: or wss: reference on its server's own origin is refused before dialing.
  • A reference whose address carries a user name or password is refused before dialing, and the words don't repeat them.

Left as it is

  • fetch_reference's answer for an id with no record, unknown reference: <id> — it may have been evicted; ids are never reused, is the framework's own, and its part of the series rewords it.

Tests

These ran on Linux x86_64 with Node 24.22.0. CI runs the suite and its Node matrix on this pull request.

  • At this head: typecheck and the build are clean, and these pass: mcpl-reference-fetcher 65, mcpl-reference-binding 8, save-recent-image 46, tool-result-reference-uri 20, mcpl-reference-fields 16, mcpl-agent-words 13, tool-presentation 13 and mcpl-ws-transport 13.
  • Red first: this change's tests against main's source (0a7fd161) fail 38 of 65 in mcpl-reference-fetcher, 1 of 8 in mcpl-reference-binding, 3 of 46 in save-recent-image and 1 of 20 in tool-result-reference-uri. Every test this change adds or changes is among them. The one that pins an http: reference from a server dialed over wss: fails there only for its words: its refusal, by the origin comparison, holds on main too, and it stays to guard that comparison.
  • What the new tests cover, in test/mcpl-reference-fetcher.test.ts:
    • each status class's remedy, 409, 423 and 425 among them, and an answer with no content;
    • a refused connection, whose clause carries the cause's words, and a cause with no words of its own, through a stand-in fetch whose rejection has an AggregateError cause, each with the failed request's remedy;
    • a save hook that throws, which is a storage failure with one log line, its cause bounded to one line;
    • an address that doesn't parse, the reference's or its server's, each with its own clause and remedy;
    • a download stopped at max_bytes past what the budget has left, which is told the budget is the limit, with the bytes it reached pinned between max_bytes and what the server sent;
    • the order of the refusals before dialing: a server fault ahead of a claim past the host's own ceiling, and that claim ahead of a spent budget;
    • a server configured on a scheme that isn't special, with userinfo, named in the origin clause by its scheme and host;
    • ws: and wss: references on their server's own origin, refused before dialing, and an http: reference from a server dialed over wss:, refused as another origin;
    • a reference whose address carries a user name and password, a user name alone or a password alone, refused before dialing by refusal(), the eager check and the fetch, with nothing dialed;
    • a payload fetched but not stored, through the framework's own fetcher and fetch_reference, with the host's log line for each way the save hook returns nothing: no workspace, no writable mount, a failed write.
  • test/mcpl-reference-binding.test.ts checks fetch_reference's whole sentence through the framework for the refusals it reaches there.

Not verified

  • The stop for a download past the host's own 256 MiB, its clause and its place before the budget in the read loop. No test reaches it, since a download would have to pass 256 MiB. It's read from the code; a claim past it is tested, in the same order.
  • The AggregateError cause is a stand-in's. That Node's fetch rejects with such a cause when every address of a host refuses is from reading Node's net, not from a run.
  • How each text reads to a model in a live turn. The tests check the text and the case it's reached in.

Merge order

A scan of the open pull requests at 17:02 UTC on October 18, at this head on main e5f50bf3, found 53 others.

Who did what

The texts follow Phoebe-2419's sweep of agent-framework's agent-facing errors. Pippa-2571 and I agreed the clause-and-remedy shape across this change and #518, and Ada-1017 made the design calls. Our internal review read the change at de7741c5, and b9e51557 answers it. A second read it through fb480d97, and ab59c2d7 answers it. Pippa-2571 read it whole and each round after, and her probe found that a password in a reference's address reached the agent in the fetch's error; fb480d97 refuses such an address. I built and tested it.


Written by Cordelia-2434 (Claude Opus 5.5).

Description checked and posted by Daniel-2612 (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 and others added 5 commits October 18, 2026 11:45
…nce and what calling again can do

Every failure of the reference fetcher is now two texts: a clause about
the reference ("the "srv" server answered HTTP 503", "it claims 10000
bytes, over the 1000 bytes this call allows", ...) and a sentence saying
what calling again can do: try later, wait for a fresh reference, pass a
larger max_bytes (no more than the server's budget has left), ask whoever
runs the host, or nothing. fetch_reference reads
`fetch_reference couldn't fetch "<id>": <clause>. <remedy>`.
save_recent_image gives both where a reference can't be fetched, through
anima-research#518's refusal(). The eager and script-demand log lines read the clause.

- FetchOutcome becomes a union whose failure side carries both texts, so
  a failure without a remedy doesn't compile. anima-research#518's Refusal
  {reason, ofReference} becomes {error, remedy}: each clause has its own
  subject.
- Each refusal before dialing says what could change it: a server this
  host no longer has (whoever runs it can add it back), a server with no
  address (nothing), a claim past the host's own ceiling (no max_bytes or
  setting lifts it).
- The scheme check reads the uri's own protocol, so a ws: or wss:
  reference on its server's origin is refused before dialing. The
  unreachable plaintext-from-a-secure-origin branch goes; an http:
  reference from a server dialed over wss: is refused by the origin
  comparison, and a test pins that.
- A server address that doesn't parse is refused as having no address.
- The budget's stop partway shows the server's running total, as the
  next refusal does, since other fetches may have spent part of it.
- The save hook logs why it returns null; a save that throws is a
  storage failure, logged, not the fetch's own error.
- A thrown fetch's clause takes its cause's words, and an
  AggregateError's attempts' when it has none of its own.
- anima-research#518's changelog entry, not yet released, quotes the no-address
  refusal's old words, so it now quotes these, and this change's own
  entry doesn't repeat them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…red, and whyFetchFailed drops a branch nothing reaches

The storage failure's remedy tells the agent whoever runs the host can see
why in its log, so a test now pins each null the framework's save hook
returns to its log line: no workspace, no writable mount, the write's
error. It runs through the framework's own fetcher and fetch_reference.

whyFetchFailed's fallback to an AggregateError's code had no case that
reaches it (an AggregateError from net carries its attempts), so it goes:
with no words and no attempts, the outer error's words stand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ailed request's remedy holds for every cause

What an internal review of this change found:

- A thrown fetch's remedy now holds whatever the cause: "Trying again later
  may work; if it fails the same way, whoever runs this host can check its
  connection to the server." A cause that repeats on every try (a port fetch
  refuses, a name that doesn't resolve, a certificate this host doesn't
  trust) was told only that a later try may work.
- A claim past the host's own 256 MiB is refused for that first, before the
  server's budget, since nothing lifts it, not even a budget whoever runs the
  host raises. A download past it is told the same.
- A server address that doesn't parse gets a clause of its own, where it
  read as a server this host doesn't connect to over a network.
- The origin clause names the server's address as it's configured
  (wss://...), not its normalized origin (https://...); the comparison
  still reads ws as http and wss as https.
- A digest that doesn't match tells the agent that fetching may work once a
  fresh reference arrives, which refreshes this very record, where it said
  the reference can't be fetched here.
- The save-throw log line bounds the cause, as the agent's text does.
- Comments: noAddressReason's and beforeDial's say what each covers, and
  the ws: scheme check says fetch refuses such a uri before connecting.

Tests: the refusal-order test puts a claim past the host's ceiling before a
spent budget; the request-failure tests, the origin clauses and the digest
remedy take the new words; the save-throw test's cause carries a newline;
the two ceiling-remedy tests pin the reached count between max_bytes and
the bytes sent before the pause.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d before dialing

A reference uri such as http://user:secret@host/x passed the origin check,
since an origin drops the userinfo, and reached fetch, which refuses such a
url before connecting with a message that holds the whole uri. That message
became the clause fetch_reference gave the agent, and the eager pass's log
line, so the password reached the agent's context and the host's log, with a
remedy inviting a retry that can't work. Such a uri is now refused in the
origin check, before the scheme check, with a clause that doesn't repeat it
and a remedy saying calling again won't change that; refusal() and the
eager check refuse it too. A user name or password in a uri is a credential
embedded in it, which RFC-005 section 6.1 forbids a server to mint and
lets a host refuse (vector 6); this host neither sends nor repeats it.

statusRemedy: 409 and 423, a conflict or lock the server's state can clear,
and 425, a request the server calls too early, now say a later try may
work, where they said only a fresh reference could change the answer.

Tests: a reference with a user name and password, and one with a user name
alone, each claiming a size within the eager limit, are refused by
refusal(), the eager check and fetch, with nothing dialed; the status table
gains 409, 423 and 425.

Changelog: a fixed entry for the refusal, and the changed entry lists 409,
423 and 425 among the statuses where a later try may work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… name a server's address as configured, and tell an unparseable server address apart

What a second internal review found, with two smaller points from a reader:

- The read loop checks the host's own 256 MiB first, then the server's
  budget, then a call's max_bytes, the order the checks before dialing
  use. A chunk that carried a download past both the host's ceiling and
  the budget was told about the budget, though no budget lifts the
  ceiling. largerMaxBytes now only ever sees a download within the host's
  ceiling, so its branch for one past it goes.
- The origin clause names the server's address by its scheme and host as
  configured. URL's own origin is "null" for a scheme that isn't special,
  which the origin comparison accepts, so the clause could read
  "(null)". Scheme and host also leave out any userinfo, path or query.
- A server address that doesn't parse tells the agent that whoever runs
  the host can correct it, where it said calling again won't change that.
- Comments: Refusal.error's doc names the log lines that read the clause;
  beforeDial's and statusRemedy's are rewrapped and reworded.
- Tests: a password with no user name is refused before dialing like the
  other two shapes; a server configured on a scheme that isn't special,
  with userinfo, shows as its scheme and host; a no-address server with a
  claim past the host's ceiling is refused for the server; the save-throw
  test's cause runs past 200 code points, pinning the log line's bound;
  an unparseable address's test title says what each case now reads.
- Changelog: the changed entry says a download past the host's ceiling is
  stopped for it first, and what an unparseable server address now says.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5 Tier: apex

[Medium impact] The PR appears safe to merge; no actionable issues were found.

Summary

Adds a reason and a next step to fetch_reference and save_recent_image failures.

  • Fetch failures explain what happened and what can help next.
  • Reference addresses with credentials or unsupported schemes are refused before dialing.
  • Failed reference saves tell the host why storage did not work.

Reviews (1) · Last reviewed commit: "fix(mcpl): stop a download past the host..." · Reviewed by Greptile

@nissa-seru
nissa-seru merged commit 8c30a4a into anima-research:main Oct 18, 2026
7 checks passed
nissa-seru pushed a commit to nissa-seru/agent-framework that referenced this pull request Oct 18, 2026
main gained anima-research#526 (modules read, edit and remove the writes the framework
holds for them), anima-research#531 (fetch_reference's failures say what happened and
what calling again can do, and the refs save hook logs why a payload
wasn't stored) and anima-research#532 (start() and addModule() are refused while a
stop() is still running). Where they meet the include:

- reference-fetcher.ts conflicted where both sides changed what the fetch
  answers when the save hook returns null. It takes main's failure and its
  log, and its comment says the null covers no workspace, no writable mount
  without an include, and a failed write.
- The refs save hook's log for a missing mount said "no workspace mount is
  writable". firstWritableMountName also answers null where the only
  writable mount has an include, so the log says "no writable workspace
  mount without an include", and main's test of the hook's log lines says
  so too.
- The rest merged on its own: docs/tool-result-guard.md's two sides change
  different paragraphs, and anima-research#526's and anima-research#532's framework.ts changes touch
  no mount or workspace path.

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
…e dialing that references.ts now holds

main brought anima-research#531, which words each of the
fetcher's refusals as a clause and a remedy, { error, remedy }, while this
branch moved the checks before dialing into references.ts (dialServer,
dialCheck), with anima-research#518's { reason, ofReference }, so that a stub can ask
them. The two conflicted in reference-fetcher.ts and references.ts.

In references.ts:
- Refusal is { error, remedy, inStub? }: anima-research#531's clause and remedy, and this
  branch's stub wording where the clause names parts of the uri.
- The remedies the checks give (NOTHING_TO_DO, FRESH_REFERENCE,
  SERVER_BACK) move here from the fetcher, which imports them.
- originRefusal takes anima-research#531's checks: a reference address and a server
  address that don't parse, a user name or password in the address, the
  uri's own scheme, and the origin, naming the server's address as
  configured. The plaintext-from-secure check and samePlace go: the origin
  clause already shows http://h beside wss://h.
- dialServer and dialCheck give anima-research#531's clauses for an unknown server, one
  with no address, an unbound reference and an expired one, each server id
  through shownServerId.
- A stub shows inStub or the clause, never a remedy.

In reference-fetcher.ts, beforeDial takes dialCheck, then anima-research#531's limits in
anima-research#531's order: the host's ceiling, the budget, then a call's ceiling. Its
other clauses name the server through shownServerId too.

Tests: this branch's tests take anima-research#531's clauses where they pinned the old
reasons, and a stub's ending for an unbound or expired reference reads
"it's not tied to any server, ..." and "the reference is expired"; the stub
bound test adds a server address that doesn't parse and an address with a
user name or password; anima-research#531's refusal table compares error and remedy,
since a refusal can carry inStub; the plaintext half of the plaintext test
goes.

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
…plaintext refusal

The merge of main took anima-research#531's refusals into dialServer and dialCheck, and
dropped the plaintext-from-secure refusal, as anima-research#531 did: an http reference
from a server dialed over TLS is refused as off its server's origin, and
that clause names both addresses. tool-result-blocks.fixed.md no longer
describes the plaintext refusal. It says a stub ends with what
fetch_reference would say happened, without what calling again can do,
and it adds the refusals anima-research#531 brought to what a stub covers: a server
whose address doesn't parse, and a uri with a user name or password.

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 at 5227741 brings anima-research#526, anima-research#531, anima-research#532, anima-research#535, anima-research#533 and anima-research#262. anima-research#262
landed before this branch, so this branch carries how the two meet.

The one conflicting file is src/framework.ts, in two blocks of a script's
tool-result conversion:
- anima-research#262 gives a script an MCP structured result whole
  (scriptStructuredResult), and names the 5,000,000-character cap on what
  a script receives AgentFramework.SCRIPT_RESULT_MAX_CHARS. Both blocks
  take anima-research#262's side.
- This branch named the same cap SCRIPT_TOOL_RESULT_MAX_CHARS and set it
  as a script's call's resultMaxChars. That constant is gone, and
  resultMaxCharsFor gives a script's call anima-research#262's
  AgentFramework.SCRIPT_RESULT_MAX_CHARS, so one name carries the cap a
  script receives and the field that tells a module about it.

anima-research#535 and anima-research#533 change the workspace module without a conflict. Since
anima-research#535, parseFilePath also refuses a path that leads into a nested mount
through a link, and edit and edit_many get that refusal through
editablePath, as every file tool does. anima-research#535's test of a directory replaced
by a link into the nested mount covers edit, write and delete, and now
covers edit_many too, with the refusal's exact text.

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