You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The console tells the owner an agent cannot reach outside the platform, while fetch_url is switched on
Measured on the deployed API, 2026-08-15, over all 34 instances on the operator account: 10 of them render
"This agent can change its own data — its memory, tasks and stored documents — but has no tool
that reaches outside the platform."
and fetch_url is allowed: true on every one of the ten (and on all 34 — it is a BASE
tool). fetch_url takes a caller-chosen method and body
(workers/api/src/lib/tools.ts:368-376), so it POSTs arbitrary payloads to arbitrary public HTTPS
hosts. The ten: Repo Chat, AIPA repo chat, Job Application Assistant, Language Buddy, Lead Outreach
Assistant, Creator OS, Facebook Friends, Fridges, Google Drive Doc Chat Test, Codex MCP Browser Test.
The other 24 render the "…each one also needs its connector granted below" sentence, which is
correct. Nothing renders the read-only sentence — that is #577's finding and it is confirmed
separately in a comment there.
This is the sentence an owner reads when deciding what to put in an agent's knowledge base. It is
rendered by store/console/src/components/ToolPermissions.tsx:123, directly above the tool
switches, in the Settings tab of every instance.
Where it is
store/console/src/lib/toolPolicy.ts:122-130:
exportfunctiontoolScopeSummary(policy: readonlyToolPolicyEntry[]): string{if(writeConnectors(policy).length>0){…}if(writesOwnData(policy)){return" This agent can change its own data — its memory, tasks and stored documents — but has no tool that reaches outside the platform.";}return" This agent has no tools that can change anything — it can only read.";}
writeConnectors uses "names a connector" as the proxy for "reaches outside the platform."
That was exactly true while the listing held connector tools and nothing else, and the function's
own docstring still describes its job as "other people's systems".
send_to_cli — {scope:"write", mutates:true, allowed:true, connector:null, tier:"runtime"}
(it is in FULL, so every agent that declares no capabilities.tools receives it)
None of the three is visible to writeConnectors, and all three reach outside the platform:
arbitrary outbound HTTP, typing into a CLI on the owner's machine, and posting a form on a
third-party site.
#525's own regression note anticipated the mirror of this — "a base tool with no connector
resolves to writeConsent:"n/a" … so it must not start rendering as ungated external access".
The listing was made exhaustive without a corresponding change to the predicate that reads it, and
the error shipped in the opposite direction: not "internal shown as external", but external shown
as absent.
What to do — cheapest first
Stop claiming the negative. Delete the "…but has no tool that reaches outside the platform"
clause and state only what the agent has. One line, no new data on the wire, cannot be wrong,
ships today. The claim it removes is the one an auditor should never have taken from a field that
does not answer it.
Derive it in the console from tier. Fails on the most common case: fetch_url is tier:"base", the same tier as write_memory, so tier separates send_to_cli (runtime) from
platform tools but cannot separate "reaches the internet" from "writes my memory". Measured on the
live listing, not assumed.
No instance renders a sentence asserting the agent has no reach outside the platform while it
holds fetch_url, send_to_cli or submit_job_application.
A test builds a policy containing fetch_url (allowed:true, connector:null) and no
connector tool, and asserts the rendered sentence does not claim the negative. It must fail on
today's code.
writeConnectors feeds both the sentence and the write-access checkbox set
(toolPolicy.ts:90-94, consumed by ToolPermissions.tsx). Changing the sentence must not change
which connectors get a checkbox, or #351's fix ("http_request gets a switch for the grant its
mutating calls are refused for") regresses silently. store/console/src/lib/toolPolicy.test.ts is
the guard; it currently has no case with a connector-less outward-reaching tool.
Verified vs inferred
Verified: the 10/34 and 24/34 split, and fetch_url: allowed:true on all ten — measured live
via GET /v1/instances/:id/tools for every instance on the account; the three tool rows quoted
above, from that same response; every file:line; that the sentence renders in the Settings tab.
Inferred: nothing load-bearing. send_to_cli on a runtime:null instance would refuse at
call time for want of a runner — but it is listed allowed:true, and the sentence is a claim
about what the agent holds. fetch_url alone falsifies the clause on all ten regardless.
Related: #577 (the read-only clause of the same function — latent, not observed), #525 (made the
listing exhaustive), #563 (mutates, the precedent for sending the fact instead of inferring it), #351 (the write-consent chip and checkbox set this must not disturb).
The console tells the owner an agent cannot reach outside the platform, while
fetch_urlis switched onMeasured on the deployed API, 2026-08-15, over all 34 instances on the operator account:
10 of them render
and
fetch_urlisallowed: trueon every one of the ten (and on all 34 — it is aBASEtool).
fetch_urltakes a caller-chosenmethodand body(
workers/api/src/lib/tools.ts:368-376), so it POSTs arbitrary payloads to arbitrary public HTTPShosts. The ten: Repo Chat, AIPA repo chat, Job Application Assistant, Language Buddy, Lead Outreach
Assistant, Creator OS, Facebook Friends, Fridges, Google Drive Doc Chat Test, Codex MCP Browser Test.
The other 24 render the "…each one also needs its connector granted below" sentence, which is
correct. Nothing renders the read-only sentence — that is #577's finding and it is confirmed
separately in a comment there.
This is the sentence an owner reads when deciding what to put in an agent's knowledge base. It is
rendered by
store/console/src/components/ToolPermissions.tsx:123, directly above the toolswitches, in the Settings tab of every instance.
Where it is
store/console/src/lib/toolPolicy.ts:122-130:and
:90-94:Mechanism — two correct decisions composing
writeConnectorsuses "names a connector" as the proxy for "reaches outside the platform."That was exactly true while the listing held connector tools and nothing else, and the function's
own docstring still describes its job as "other people's systems".
list_instance_tools says "a tool absent from the allowed set cannot be invoked, by chat or by call_instance_tool" — 11 tools every agent runs in chat are absent, and 2 of them ran on the audited instance #525 made the listing exhaustive. The built-in facilities now appear too, and they carry
connector: null. So connector-less no longer implies platform-internal. Measured on the livelisting for
6d3b28f1-…(Language Buddy):fetch_url—{scope:"read", mutates:true, allowed:true, connector:null, tier:"base"}send_to_cli—{scope:"write", mutates:true, allowed:true, connector:null, tier:"runtime"}(it is in
FULL, so every agent that declares nocapabilities.toolsreceives it)submit_job_application—{scope:"write", mutates:true, allowed:true, connector:null, tier:"standard"}None of the three is visible to
writeConnectors, and all three reach outside the platform:arbitrary outbound HTTP, typing into a CLI on the owner's machine, and posting a form on a
third-party site.
#525's own regression note anticipated the mirror of this — "a base tool with no
connectorresolves to
writeConsent:"n/a"… so it must not start rendering as ungated external access".The listing was made exhaustive without a corresponding change to the predicate that reads it, and
the error shipped in the opposite direction: not "internal shown as external", but external shown
as absent.
What to do — cheapest first
clause and state only what the agent has. One line, no new data on the wire, cannot be wrong,
ships today. The claim it removes is the one an auditor should never have taken from a field that
does not answer it.
reachfact to each listing row(
"platform" | "machine" | "internet") besidemutates, and derive the sentence from it. Thisis list_instance_tools reports nine mutating tools as
scope: "read"— one field carries two incompatible meanings, and its sibling deliberately fails the other way #563's precedent applied again: when the console needs a fact the server owns, the serversends it rather than the console inferring it from a field that means something else.
writeConnectorskeeps its existing job — deciding which connectors get a write-accesscheckbox — untouched.
Alternatives considered and rejected
tier. Fails on the most common case:fetch_urlistier:"base", the same tier aswrite_memory, so tier separatessend_to_cli(runtime) fromplatform tools but cannot separate "reaches the internet" from "writes my memory". Measured on the
live listing, not assumed.
fetch_urlto a hardcoded name list in the console. Puts a fact about the tool catalog inthe UI, where it will drift the first time a base tool is added — the failure mode list_instance_tools reports nine mutating tools as
scope: "read"— one field carries two incompatible meanings, and its sibling deliberately fails the other way #563 and list_instance_tools says "a tool absent from the allowed set cannot be invoked, by chat or by call_instance_tool" — 11 tools every agent runs in chat are absent, and 2 of them ran on the audited instance #525both ended in.
writesOwnDatato derive frommutates, which would movefetch_url(mutates:true) into theown-data list and leave this clause false in exactly the same place. Two issues, one file, stated
order: The console's read-only claim is derived from scope, which #563 established does not mean 'does not mutate' #577's is latent, this one is on screen today.
Acceptance criteria
holds
fetch_url,send_to_cliorsubmit_job_application.fetch_url(allowed:true, connector:null) and noconnector tool, and asserts the rendered sentence does not claim the negative. It must fail on
today's code.
writeConnectors's output is unchanged for every policy — the grant checkboxes it feeds are[bug] The tool listing reports write-gated tools as allowed=true, reason=ok — agents burn a turn discovering the consent gate #351's promise and are not part of this change.
Regression risk
writeConnectorsfeeds both the sentence and the write-access checkbox set(
toolPolicy.ts:90-94, consumed byToolPermissions.tsx). Changing the sentence must not changewhich connectors get a checkbox, or #351's fix ("
http_requestgets a switch for the grant itsmutating calls are refused for") regresses silently.
store/console/src/lib/toolPolicy.test.tsisthe guard; it currently has no case with a connector-less outward-reaching tool.
Verified vs inferred
fetch_url: allowed:trueon all ten — measured livevia
GET /v1/instances/:id/toolsfor every instance on the account; the three tool rows quotedabove, from that same response; every
file:line; that the sentence renders in the Settings tab.send_to_clion aruntime:nullinstance would refuse atcall time for want of a runner — but it is listed
allowed:true, and the sentence is a claimabout what the agent holds.
fetch_urlalone falsifies the clause on all ten regardless.Related: #577 (the read-only clause of the same function — latent, not observed), #525 (made the
listing exhaustive), #563 (
mutates, the precedent for sending the fact instead of inferring it),#351 (the write-consent chip and checkbox set this must not disturb).