Skip to content

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

Open
grypez wants to merge 1 commit into
mainfrom
feat/capabilities-as-discoverable-exos
Open

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

Conversation

@grypez

@grypez grypez commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

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.

Follow-ups (out of scope here)

  • Tie S.interface's return type to its method-name keys (DescribedInterface<Method>) so an implementation/schema name mismatch is caught at compile time, not just by the runtime construction check above. Purely a type change; touches kernel-utils/described.ts.
  • Add enum support to the capability schema so getMoonPhase can advertise its allowed return values.

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-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 71.31%
⬆️ +0.08%
8845 / 12403
🔵 Statements 71.14%
⬆️ +0.09%
8995 / 12644
🔵 Functions 72.48%
⬆️ +0.12%
2134 / 2944
🔵 Branches 64.89%
⬆️ +0.05%
3573 / 5506
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/kernel-agents/src/capabilities/discover.ts 86.95%
⬆️ +76.95%
100%
🟰 ±0%
80%
⬆️ +80.00%
90%
⬆️ +78.89%
64-70
packages/kernel-agents/src/capabilities/end.ts 100%
🟰 ±0%
50%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/kernel-agents/src/capabilities/examples.ts 66.66%
🟰 ±0%
100%
🟰 ±0%
0%
🟰 ±0%
66.66%
🟰 ±0%
27-37
packages/kernel-agents/src/capabilities/math.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
Generated in workflow #4476 for commit 06d6853 by the Vitest Coverage Report Action

@grypez grypez force-pushed the feat/described-exo-combinators branch from 707aa13 to d7220eb Compare June 17, 2026 16:04
@grypez grypez force-pushed the feat/capabilities-as-discoverable-exos branch from fc7d58e to 9e110e2 Compare June 17, 2026 16:04
@grypez grypez force-pushed the feat/described-exo-combinators branch from d7220eb to b8ac175 Compare June 17, 2026 16:41
@grypez grypez force-pushed the feat/capabilities-as-discoverable-exos branch 2 times, most recently from 6c6abee to 620c437 Compare June 18, 2026 15:37
@grypez grypez force-pushed the feat/described-exo-combinators branch from b8ac175 to 6bc1fee Compare June 18, 2026 15:50
@grypez grypez force-pushed the feat/capabilities-as-discoverable-exos branch from 620c437 to 1812ca4 Compare June 18, 2026 15:56
Base automatically changed from feat/described-exo-combinators to main June 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 grypez force-pushed the feat/capabilities-as-discoverable-exos branch from 1812ca4 to 06d6853 Compare June 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 owner June 23, 2026 18:45
Sign up for free to 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.

1 participant