Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/fetch-reference-credentials.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- A reference whose address carries a user name or password is refused before anything is dialed, in words that don't repeat them. `fetch` refused such an address before connecting, with a message holding the whole address, password included, which `fetch_reference` gave the agent as its error, and the host's log carried it when the eager fetch tried it.
6 changes: 6 additions & 0 deletions changelog.d/fetch-reference-words.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- `fetch_reference`'s failures now say what happened to the reference and what calling again can do: `fetch_reference couldn't fetch "<ref_id>": <what happened>. <what calling again can do>`, where they gave the fetcher's reason alone, such as `HTTP 404`, `fetch timed out or aborted`, `claimed <n> bytes exceeds fetch ceiling <m>` or `third-party origin (<origin> != dialed <origin>); not fetched by this host`. Where `save_recent_image` says a reference can't be fetched, it gives the same two.
- **What happened** names the server and what it answered (`the "<server>" server answered HTTP 503`), the limit a fetch met (`it claims <n> bytes, over the <m> bytes this call allows`, `the download had reached <n> bytes, past …`), what kept a fetch from dialing (`it points to <origin>, not to the "<server>" server's own address (<origin>), and this host fetches only from the server itself`), or that what arrived didn't match the reference's digest or couldn't be stored. The eager fetch's and a script's fetch's log lines read the same words.
- **What calling again can do**, such as trying again later (after a timeout, 408, 409, 423, 425, 429, or a 5xx other than 501 and 505, and after a failed request, with whoever runs the host to check its connection to the server if it fails the same way), waiting for a fresh reference to it (an expired or unbound reference, and the other 4xx), passing a larger `max_bytes` (naming the size it needs, and no more than the server's fetch budget has left), asking whoever runs the host (who can check its credentials for the server after a 401 or 403, raise or renew a spent budget, or add back a server it no longer has), or nothing, as for an address on another origin, a server this host doesn't connect to over a network, or a claim past the 256 MiB this host fetches at most, which no `max_bytes` or setting lifts. A claim past those 256 MiB is refused for them first, and a download past them is stopped for them first, whatever the server's budget has left.
- **The budget's texts** read `this host's fetch budget for the "<server>" server is used up (<used> of <budget> bytes)` and, partway, `this host's fetch budget for the "<server>" server ran out during the download (<total> of <budget> bytes), so it was stopped and nothing was kept`, where they read `per-server fetch budget exhausted (<total>/<budget> bytes)`, the one partway after `aborted: `.
- **A `ws:` or `wss:` reference** on its server's own origin is refused for its scheme before anything is dialed, where `fetch` refused it before connecting and the agent read `fetch failed`. A server whose address doesn't parse is refused, saying so, and that whoever runs the host can correct it.
- **The host's log** says why a fetched payload couldn't be stored: no workspace, no writable mount, or the write's error. A save that throws is that failure too, logged, where its message reached the agent as the fetch's own error.
6 changes: 3 additions & 3 deletions changelog.d/no-address-server-refusal.fixed.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
- Where `fetch_reference` refused a reference from an MCPL server the host
doesn't connect to over a network, such as a stdio server, as
`unknown server: <id>`, it now says `no address: this host doesn't
connect to server <id> over a network, so there's nowhere to fetch it
from`. A server the host spawns from a command has no address either,
`unknown server: <id>`, it now says `this host doesn't connect to the
"<id>" server over a network, so there's nowhere to fetch it from`. A
server the host spawns from a command has no address either,
even when its config also holds a `url`, on whose origin its references
used to be fetched; nor has a WebSocket server whose `url` the transport
won't dial: one that isn't a `ws://` or `wss://` url, or that has a
Expand Down
20 changes: 16 additions & 4 deletions src/framework.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4920,11 +4920,19 @@ export class AgentFramework {
}
return { url: cfg.url, token: cfg.token, autofetch: cfg.autofetch };
},
// A null tells the agent only that the payload couldn't be stored;
// why, which can name the host's paths, goes to the log.
async (fileName, data, mimeType) => {
const workspace = this.getWorkspaceModule();
if (!workspace) return null;
if (!workspace) {
console.error(`[refs] couldn't store ${fileName}: this host has no workspace`);
return null;
}
const mount = this.firstWritableMountName(workspace);
if (!mount) return null;
if (!mount) {
console.error(`[refs] couldn't store ${fileName}: no workspace mount is writable`);
return null;
}
const path = `${mount}/refs/${fileName}`;
const res = await workspace.writeBinary(path, data, mimeType);
if (res.success) return path;
Expand All @@ -4934,6 +4942,7 @@ export class AgentFramework {
console.error(`[refs] ${path} is in the workspace's Chronicle tree, but the rest of its write failed: ${res.error ?? 'unknown error'}`);
return path;
}
console.error(`[refs] couldn't store ${path}: ${res.error ?? 'unknown error'}`);
return null;
},
);
Expand Down Expand Up @@ -23684,7 +23693,10 @@ export class AgentFramework {
result = outcome.ok
? { success: true, data: JSON.stringify({ path: outcome.path, bytes: outcome.bytes,
mime_type: outcome.mimeType, digest_verified: outcome.digestVerified ?? 'no digest claimed' }) }
: { success: false, isError: true, error: outcome.error };
// The fetcher says what happened as a clause about the reference,
// and what calling again can do as a sentence of its own.
: { success: false, isError: true,
error: `fetch_reference couldn't fetch "${refId}": ${outcome.error}. ${outcome.remedy}` };
}
this.emitTrace({
type: result.isError ? 'tool:failed' : 'tool:completed',
Expand Down Expand Up @@ -23937,7 +23949,7 @@ export class AgentFramework {
"from elsewhere never had one), so fetch_reference can't fetch it either. If its tool can produce " +
'it again, re-run that tool for a fresh reference.'
: refusal
? `It can't be fetched either — ${refusal.reason}.`
? `It can't be fetched either — ${refusal.error}. ${refusal.remedy}`
: 'Fetch it with fetch_reference and save the materialized file instead.'),
);
}
Expand Down
Loading
Loading