Host executes tools from read skills after realm verification#5486
Open
lukemelia wants to merge 2 commits into
Open
Conversation
…cation When a tool call's functionName resolves in no enabled skill, message-builder falls back to the skill named by the latest readRealmFile result event's discoveredTools. That annotation is strictly a lookup hint: the codeRef the host executes is re-derived from the skill's realm-indexed frontmatter, loaded through the store with the user's own permissions, and accepted only when the indexed declaration's functionName matches the call. requiresApproval comes from the same verified declaration (absent means approval required), so auto-execute honors it identically to enabled skills. A forged annotation, a skill the user can't read, or a tool the skill no longer declares all leave the request unresolved, surfacing through the existing unrecognized-command invalid-result path. CS-12047 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
c44368e to
c0edde0
Compare
Contributor
Contributor
There was a problem hiding this comment.
Pull request overview
This PR extends the host’s tool-resolution path so that when the model calls a tool it previously discovered via readRealmFile (i.e. from a skill that was read but never enabled in the room), the host can still resolve and execute it only after re-deriving the tool from the declaring skill’s realm-indexed frontmatter (treating the event annotation as a lookup hint, not authority).
Changes:
- Add
findDiscoveredToolSkillUrl(events, functionName)to locate the declaring skill URL from the latestdata.discoveredToolstool-result event. - Update
MessageBuilderto fall back to discovered-skill lookup + verification when no enabled-skill tool matches the request. - Add acceptance tests covering (1) read-skill tool requires approval and executes on Apply, and (2) forged “declaring skill” hints that don’t actually declare the tool are rejected.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/host/tests/acceptance/tools-test.gts | Adds markdown skill fixtures plus acceptance coverage for executing tools discovered via readRealmFile and rejecting forged hints. |
| packages/host/app/lib/skill-tools.ts | Introduces findDiscoveredToolSkillUrl to extract the declaring skill URL hint from prior tool-result events. |
| packages/host/app/lib/matrix-classes/message-builder.ts | Adds a fallback tool-resolution path that loads/verifies the declaring skill’s indexed tool declaration before execution. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The skill-URL hint comes from a bot event, so a load that throws on a malformed or unreadable id degrades to an unresolved tool instead of breaking message building. The hint scan also walks newest-first and returns on the first match rather than scanning the full timeline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
backspace
approved these changes
Jul 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Linear: CS-12047
Stacked on #5485 (event-sourced prompt injection) — the pull model's execution leg. When the model calls a tool it discovered by reading a skill file, the host resolves and executes it with the declaring skill's indexed frontmatter as the authority, never the bot's event alone.
Trust model
The bot's
discoveredToolsannotation is strictly a lookup hint.message-builderre-derives the codeRef from the named skill's realm-indexed frontmatter, loaded vialoadSkillSourcethrough the store with the user's own permissions, and accepts it only when the indexed declaration's computedfunctionNamematches the call. Consequences, each surfacing through the existing unrecognized-command invalid-result path:requiresApprovalcomes from the same verified declaration (absent → approval required), so approval prompting and auto-execute behave identically to enabled skills.Shape
skill-tools.ts:findDiscoveredToolSkillUrl(events, functionName)— the latest readRealmFile result event naming the function wins, matching how prompt assembly picks the freshest read.message-builder.ts: when the enabled-skills scan finds no match, the fallback runs the lookup + verification above; the resolvedMessageToolthen flows through the existing validate/approve/execute machinery unchanged.Testing
Two acceptance tests in
tools-test:appliedresult event;invalidwith the unrecognized-command reason and the tool never executes.The lost-read-access case shares the same code path (
loadSkillSourcereturnsundefinedfor anything the store can't read as a skill), so it isn't separately simulated. Type-check and lint pass; host suite runs in CI.🤖 Generated with Claude Code