Skip to content

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

Draft
grypez wants to merge 1 commit into
feat/described-exo-combinatorsfrom
feat/capabilities-as-discoverable-exos
Draft

feat(kernel-agents): author built-in capabilities as pattern-guarded exos#959
grypez wants to merge 1 commit into
feat/described-exo-combinatorsfrom
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 the base PR), 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 from the exo via a
new synchronous discoverLocal, so 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.

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.

Note on the local-only agent e2e suite

Because the capability modules now build @endo/patterns guards eagerly at
import — and those builders require frozen patterns — they must be loaded
under a freezing harden. The unit suites get this via their endoify-mock setup
and pass. The local-only @ocap/kernel-test-local sample-agent.e2e suite,
however, imports the agent factories directly into the vitest realm under
mock-endoify (a no-op harden), so it now fails to construct there. That suite
is not end-to-end enough to begin with — it should drive an agent inside a real
hardened kernel and assert via logs (the @ocap/kernel-test pattern) rather than
importing agents into vitest. Tracked in #961. These tests are local-only and
not part of CI (kernel-test-local has no test:e2e:ci), so this does not
affect the CI signal.

Test plan

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 71.32%
⬆️ +0.22%
8837 / 12390
🔵 Statements 71.14%
⬆️ +0.23%
8985 / 12629
🔵 Functions 72.45%
⬆️ +0.26%
2131 / 2941
🔵 Branches 64.79%
⬆️ +0.11%
3558 / 5491
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/kernel-agents/src/capabilities/discover.ts 82.35%
⬆️ +72.35%
100%
🟰 ±0%
75%
⬆️ +75.00%
87.5%
⬆️ +76.39%
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 #4467 for commit 620c437 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
…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.

`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 620c437 to 1812ca4 Compare June 18, 2026 15:56
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