June 10, 2026

ToolResult Contract Rollout & Shared Infrastructure

A sweeping contract migration lands across the entire ability surface

A sweeping contract migration lands across the entire ability surface. Every tool now returns a sealed ToolResult envelope — ok() for success, err() for failure — replacing the legacy pattern where errors masqueraded as success prose and code=“error” was a mechanical placeholder. Stable kebab-case error codes, structured JSON bodies, and ACTION_REQUIRED pre-gates are now the norm.

The dispatcher gains a pre-gate that rejects malformed calls (missing params, unknown actions) before the policy gate, preventing bogus permission seeding and deadlocked turns. A new classify_action hook lets abilities like bash derive the permission key from the command itself, closing a prompt-injection vector where a model could declare a safe action for a destructive command.

Shared infrastructure is extracted to eliminate duplication and drift. A single SSRF guard in services/ssrf.py replaces two hand-maintained blocklists that had diverged. Web fetch logic consolidates into services/web_fetch.py with named profiles (BROWSER, API, DOWNLOAD). The memory retrieval engine and document chunking move to services, leaving abilities as thin adapters. A CapabilityAbility base class absorbs the repeated delegation block from five capability tools, with contacts as the exemplar.

BudgetCappedAbility and CompactionConfig base classes are extracted from duplicated scaffolding. The skill_manager module merges into skill_builder, removing a content-corrupting blanket string replace. The programming_docs_search ability collapses 23 copy-paste source subclasses into a single declarative SOURCES table and stops fabricating fallback pages — a truthfulness fix.

Numerous abilities receive targeted hardening. find_skills adds a loud index-probe guardrail so a corrupt database can no longer masquerade as zero hits. web_browse hands screenshot doc_ids to the caller mechanically, preventing them from dying with the delegate session. calendar blocks year-0001 sentinel writes. place returns meaningful results instead of empty strings. memory surfaces backend errors instead of silently reporting zero results. document delete disambiguates fuzzy matches instead of silently deleting the first hit.

The MCP passthrough proxy now maps remote outcomes onto the ToolResult contract, surfacing mcp-unreachable, mcp-unknown-tool, and mcp-tool-error with the server name. The rich media contract is unified: abilities pass a rich payload to ToolResult, and the dispatcher owns ordinal assignment and span instructions, eliminating dead _rich_media_ordinal reader paths across the codebase.

Documentation is updated to reflect the new contracts, architecture shifts, and corrected delegate caps. Policy defaults are pruned of unreachable bash.execute rows, and the seed count drops to 231 rows per channel.

  • All abilities now return ToolResult with stable kebab-case error codes; code=“error” placeholder eliminated.

  • ACTION_REQUIRED pre-gate rejects missing params and unknown actions before the policy gate, preventing deadlocked turns.

  • SSRF guard unified into single services/ssrf.py, closing a drift where the browser interceptor missed blocked ranges.

  • Web fetch stack consolidated into services/web_fetch.py with BROWSER, API, and DOWNLOAD profiles.

  • CapabilityAbility base class absorbs repeated delegation logic; contacts migrated as exemplar.

  • bash.classify_action derives permission key from command heuristics, blocking prompt-injected action forgery.