Skip to content

feat(kernel-agents): author built-in capabilities as pattern-guarded exos - #959

Merged
grypez merged 1 commit into
mainfrom
feat/capabilities-as-discoverable-exos
Jun 25, 2026
Merged

feat(kernel-agents): author built-in capabilities as pattern-guarded exos#959
grypez merged 1 commit into
mainfrom
feat/capabilities-as-discoverable-exos

Conversation

@grypez

@grypezgrypez commented Jun 17, 2026

Copy link
Copy Markdown
Member

Explanation

Rewrites the math, end, and examples capabilities as discoverable exos
built with the described*() combinators (added in #958, now on main), so
each capability's argument shape is enforced by the exo's interface guard at
invocation rather than only advertised in the prompt. A mistyped argument now
fails with a guard rejection at the membrane instead of surfacing deep inside
the capability.

Each module derives its { func, schema } capability specs via a new
synchronous makeInternalCapabilities constructor, which builds the
pattern-guarded exo (kept private as the in-realm enforcement membrane) and
projects a capability record from the just-authored schemas — without
round-tripping through GET_DESCRIPTION. All existing consumers (example
transcripts, the REPL evaluator, prepare-attempt) keep the same spec shape and
makeEnd stays synchronous. end's closed-over result object is intentionally
left un-hardened so the exo method can mutate it.

makeInternalCapabilities asserts at construction that the implementation and
schema method sets match exactly. A missing implementation already throws inside
makeDiscoverableExo, but an extra implementation absent from the schema would
otherwise be silently accepted by the guard's defaultGuards: 'passable' and
never be reachable as a capability — so an authoring typo (e.g. serch vs
search) now fails loudly at construction instead of surfacing as a capability
that resolves to undefined. A colocated discover.test.ts covers the
positional-arg mapping, guard rejection at the membrane, and this construction
check.

Installs the endoify mock as a package-wide vitest setup, since capability
modules now build exos at import and need a harden global before they load.

Notable behavior changes

  • getMoonPhase loses its (already unsupported, @ts-expect-error'd) enum return hint.
  • end's off-spec per-argument required flags are gone; final is required and attachments optional, expressed by the guard.

Test plan


Note

Medium Risk
Touches core agent capability invocation and changes runtime validation behavior (guard rejections vs deep errors), though external capability shapes and makeEnd sync API stay the same.

Overview
Built-in math, end, and examples capabilities are no longer hand-authored with capability(); they are built via new makeInternalCapabilities, which wraps implementations in a private pattern-guarded discoverable exo and projects the same { func, schema } shape agents already use. Invalid or missing arguments are rejected at the exo interface guard before implementation code runs.

discover is refactored to share capabilitiesFrom with the local path so remote and in-realm invocation both map named-arg objects to positional exo calls the same way. makeInternalCapabilities also fails at construction if schema and implementation method names do not match exactly.

Tests cover mapping, membrane rejection, and the construction check; package vitest loads the endoify mock globally because capability modules build exos at import. getMoonPhase no longer advertises an unsupported enum return hint; end optional/required args are expressed only via the guard schema.

Reviewed by Cursor Bugbot for commit 06d6853. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actionsBot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

StatusCategoryPercentageCovered / Total
🔵Lines71.31%
⬆️ +0.08%
8845 / 12403
🔵Statements71.14%
⬆️ +0.09%
8995 / 12644
🔵Functions72.48%
⬆️ +0.12%
2134 / 2944
🔵Branches64.89%
⬆️ +0.05%
3573 / 5506
File Coverage
FileStmtsBranchesFunctionsLinesUncovered Lines
Changed Files
packages/kernel-agents/src/capabilities/discover.ts86.95%
⬆️ +76.95%
100%
🟰 ±0%
80%
⬆️ +80.00%
90%
⬆️ +78.89%
64-70
packages/kernel-agents/src/capabilities/end.ts100%
🟰 ±0%
50%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/kernel-agents/src/capabilities/examples.ts66.66%
🟰 ±0%
100%
🟰 ±0%
0%
🟰 ±0%
66.66%
🟰 ±0%
27-37
packages/kernel-agents/src/capabilities/math.ts100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
Generated in workflow #4476 for commit 06d6853 by the Vitest Coverage Report Action

@grypez
grypezforce-pushed the feat/described-exo-combinators branch from 707aa13 to d7220ebCompareJune 17, 2026 16:04
@grypez
grypezforce-pushed the feat/capabilities-as-discoverable-exos branch from fc7d58e to 9e110e2CompareJune 17, 2026 16:04
@grypez
grypezforce-pushed the feat/described-exo-combinators branch from d7220eb to b8ac175CompareJune 17, 2026 16:41
@grypez
grypezforce-pushed the feat/capabilities-as-discoverable-exos branch 2 times, most recently from 6c6abee to 620c437CompareJune 18, 2026 15:37
@grypez
grypezforce-pushed the feat/described-exo-combinators branch from b8ac175 to 6bc1feeCompareJune 18, 2026 15:50
@grypez
grypezforce-pushed the feat/capabilities-as-discoverable-exos branch from 620c437 to 1812ca4CompareJune 18, 2026 15:56
Base automatically changed from feat/described-exo-combinators to mainJune 23, 2026 12:11
…exos
Rewrite the `math`, `end`, and `examples` capabilities as discoverable exos
built with the `described*()` combinators, so each capability's argument shape
is enforced by the exo's interface guard at invocation rather than only
advertised in the prompt. A mistyped argument now fails with a guard rejection
at the membrane instead of surfacing deep inside the capability.
Each module derives its `{ func, schema }` capability specs via a new
synchronous `makeInternalCapabilities` constructor, which builds the
pattern-guarded exo and projects a capability record from the just-authored
schemas — without round-tripping through `GET_DESCRIPTION`. The exo is kept
private as the in-realm enforcement membrane; internal capabilities are guarded
closures, not passable exos (to cross a boundary, publish an exo and `discover`
it). All existing consumers (example transcripts, e2e tests, the REPL
evaluator, prepare-attempt) keep the same spec shape and `makeEnd` stays
synchronous. `end`'s closed-over result object is intentionally left un-hardened
so the exo method can mutate it.
`makeInternalCapabilities` asserts at construction that the implementation and
schema method sets match exactly, so an authoring typo (an implementation
without a matching schema, which the guard's `defaultGuards: 'passable'` would
otherwise accept as an unreachable passable method) fails loudly instead of
surfacing as a capability that silently resolves to `undefined`. A colocated
`discover.test.ts` covers the positional-arg mapping, guard rejection at the
membrane, and this construction-time check.
`getMoonPhase` loses its (already unsupported, `@ts-expect-error`'d) `enum`
return hint; `end`'s off-spec per-argument `required` flags are gone, with
`final` required and `attachments` optional expressed by the guard.
Install the endoify mock as a package-wide vitest setup so capability modules,
which now build exos at import, have a `harden` global before they load.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@grypez
grypezforce-pushed the feat/capabilities-as-discoverable-exos branch from 1812ca4 to 06d6853CompareJune 23, 2026 18:01
@grypez
grypez marked this pull request as ready for review June 23, 2026 18:45
@grypez
grypez requested a review from a team as a code ownerJune 23, 2026 18:45

@sirtimidsirtimid left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@grypez
grypez added this pull request to the merge queueJun 25, 2026
Merged via the queue into main with commit c631ac4Jun 25, 2026
33 checks passed
@grypez
grypez deleted the feat/capabilities-as-discoverable-exos branch June 25, 2026 11:37
SherfeyInv pushed a commit to SherfeyInv/ocap-kernel that referenced this pull request Jul 29, 2026
…g enforcer (MetaMask#960)
## Explanation
The base PRs ([MetaMask#958](MetaMask#958),
[MetaMask#959](MetaMask#959)) made every
built-in capability a pattern-guarded discoverable exo. Now that the
exo's interface guard already enforces each capability's argument shape,
this PR retires the parallel membraneless authoring and validation paths
so the guard is the single argument enforcer:
- Removes the `capability()` authoring helper and the internal
`validateCapabilityArgs` validator (and its now-dead module). The chat
strategy no longer re-validates arguments before invoking — it relies on
the guard rejection it catches and reports as an `Error calling …` tool
message. That catch is hardened to handle a non-`Error` rejection, so an
invalid-argument tool call surfaces as a tool error instead of crashing
the task (covered by a new regression test).
- Collapses the redundant `CapabilitySchema` type into kernel-utils'
`MethodSchema` (a capability's `schema` is exactly the `MethodSchema`
its exo describes), removing the parallel type and its
`ExtractRecordKeys` helper.
- Adds a `test/make-method-capability.ts` helper that builds a guarded,
discovered single-method capability from an `S.method`, and migrates the
chat and JSON evaluator tests (and the capability test, repurposed to
cover the surviving `extract*` helpers) onto it.
- Drops the now-unused `@metamask/superstruct` dependency.
### Breaking changes
- The `capability()` authoring helper is no longer exported from
`@ocap/kernel-agents/capabilities/capability`. Author capabilities as
pattern-guarded discoverable exos (via the `described*()` combinators in
`@metamask/kernel-utils`) and convert them with `discover`.
(`validateCapabilityArgs` was internal and never exported.)
## Test plan
- [x] \`yarn workspace @ocap/kernel-agents test:dev:quiet\` (56 pass),
incl. a chat-strategy regression test that an invalid-argument tool call
comes back as an \`Error calling …\` tool message instead of crashing
the task
- [x] \`yarn workspace @ocap/kernel-agents-repl test:dev:quiet\` (178
pass)
- [x] \`build\` + \`lint\` for both packages; changelog validates
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Breaking public API (`capability()` removal) affects downstream
authors, but runtime behavior stays aligned with prior exo-backed
builtins; main risk is consumers still using the old helper or assuming
pre-invoke Superstruct errors.
> > **Overview**
> **Breaking:** Removes the exported `capability()` helper and the
internal Superstruct-based `validateCapabilityArgs` path. Capabilities
are expected to be authored as pattern-guarded discoverable exos
(`described*()` + `discover` / `makeInternalCapabilities`);
`CapabilitySpec.schema` is now kernel-utils `MethodSchema` instead of a
parallel `CapabilitySchema` type.
> > Invocation errors from the exo interface guard are normalized in
`capabilitiesFrom` to `Error calling <name>(<params>): …` so chat and
other callers can surface actionable tool messages without a second
validation layer. The chat agent parses tool JSON locally when needed,
invokes capabilities directly, and pushes guard/implementation failures
as tool errors (including a regression test for bad args) instead of
crashing the loop.
> > Tests migrate to `test/make-method-capability.ts`;
`@metamask/superstruct` is dropped from dependencies.
> > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
6dc44a8. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@grypez@sirtimid