From 4184513ce5bc8af16dbcca274aff0b1aa36b4f32 Mon Sep 17 00:00:00 2001 From: grypez <143971198+grypez@users.noreply.github.com> Date: Wed, 4 Mar 2026 09:04:52 -0500 Subject: [PATCH] refactor(kernel-exo): rename `describe()` to `__getDescription__()` dunder Aligns discoverable exo introspection with the existing dunder convention (`__getInterfaceGuard__`, `__getMethodNames__`) established by `@endo/exo`. Co-Authored-By: Claude Opus 4.6 --- .../src/capabilities/discover.ts | 6 ++++- packages/kernel-exo/src/discoverable.test.ts | 22 ++++++++++--------- packages/kernel-exo/src/discoverable.ts | 15 ++++++++----- packages/kernel-exo/src/index.ts | 2 +- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/packages/kernel-agents/src/capabilities/discover.ts b/packages/kernel-agents/src/capabilities/discover.ts index f3077f81c..25b9f8dfa 100644 --- a/packages/kernel-agents/src/capabilities/discover.ts +++ b/packages/kernel-agents/src/capabilities/discover.ts @@ -1,4 +1,5 @@ import { E } from '@endo/eventual-send'; +import { GET_DESCRIPTION } from '@metamask/kernel-exo'; import type { DiscoverableExo, MethodSchema } from '@metamask/kernel-exo'; import type { CapabilityRecord, CapabilitySpec } from '../types.ts'; @@ -14,7 +15,10 @@ export const discover = async ( exo: DiscoverableExo, ): Promise => { // @ts-expect-error - E type doesn't remember method names - const description = (await E(exo).describe()) as Record; + const description = (await E(exo)[GET_DESCRIPTION]()) as Record< + string, + MethodSchema + >; const capabilities: CapabilityRecord = Object.fromEntries( Object.entries(description).map(([name, schema]) => { diff --git a/packages/kernel-exo/src/discoverable.test.ts b/packages/kernel-exo/src/discoverable.test.ts index 32969b470..3075c09b0 100644 --- a/packages/kernel-exo/src/discoverable.test.ts +++ b/packages/kernel-exo/src/discoverable.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; -import { makeDiscoverableExo } from './discoverable.ts'; +import { GET_DESCRIPTION, makeDiscoverableExo } from './discoverable.ts'; import type { MethodSchema } from './schema.ts'; const makeExoMock = vi.hoisted(() => @@ -56,16 +56,16 @@ describe('makeDiscoverableExo', () => { expect(exo).toBeDefined(); expect(exo.greet).toBeDefined(); expect(exo.add).toBeDefined(); - expect(exo.describe).toBeDefined(); + expect(exo[GET_DESCRIPTION]).toBeDefined(); }); - it('returns full schema when describe is called', () => { + it('returns full schema when __getDescription__ is called', () => { const methods = { greet: (name: string) => `Hello, ${name}!` }; const schema = { greet: greetSchema }; const exo = makeDiscoverableExo('TestExo', methods, schema); - expect(exo.describe()).toStrictEqual(schema); + expect(exo[GET_DESCRIPTION]()).toStrictEqual(schema); }); it('preserves method functionality', () => { @@ -94,7 +94,7 @@ describe('makeDiscoverableExo', () => { const exo = makeDiscoverableExo('TestExo', methods, schema); expect(exo.getValue()).toBe(42); - expect(exo.describe()).toStrictEqual({ + expect(exo[GET_DESCRIPTION]()).toStrictEqual({ getValue: schema.getValue, }); }); @@ -117,18 +117,18 @@ describe('makeDiscoverableExo', () => { exo.doSomething(); expect(called).toBe(true); - expect(exo.describe()).toStrictEqual({ + expect(exo[GET_DESCRIPTION]()).toStrictEqual({ doSomething: schema.doSomething, }); }); - it('throws if describe is already a method', () => { + it('throws if __getDescription__ is already a method', () => { const methods = { - describe: () => 'original describe', + [GET_DESCRIPTION]: () => 'original describe', greet: (name: string) => `Hello, ${name}!`, }; const schema: Record = { - describe: { + [GET_DESCRIPTION]: { description: 'Original describe method', args: {}, returns: { type: 'string', description: 'Original description' }, @@ -138,7 +138,9 @@ describe('makeDiscoverableExo', () => { expect(() => { makeDiscoverableExo('TestExo', methods, schema); - }).toThrow('The `describe` method name is reserved for discoverable exos.'); + }).toThrow( + `The \`${GET_DESCRIPTION}\` method name is reserved for discoverable exos.`, + ); }); it('re-throws errors from makeExo that are not about describe key', () => { diff --git a/packages/kernel-exo/src/discoverable.ts b/packages/kernel-exo/src/discoverable.ts index 72af57b0b..324ab12ed 100644 --- a/packages/kernel-exo/src/discoverable.ts +++ b/packages/kernel-exo/src/discoverable.ts @@ -7,7 +7,12 @@ import { makeDefaultInterface } from './exo.ts'; import type { MethodSchema } from './schema.ts'; /** - * A discoverable exo object that extends a base exo interface with a `describe` method + * The dunder method name used to retrieve a discoverable exo's schema. + */ +export const GET_DESCRIPTION = '__getDescription__'; + +/** + * A discoverable exo object that extends a base exo interface with a `__getDescription__` method * for runtime introspection of method schemas. */ export type DiscoverableExo< @@ -24,7 +29,7 @@ export type DiscoverableExo< * * @returns A schema of the methods. */ - describe: () => Schema; + [GET_DESCRIPTION]: () => Schema; } > >; @@ -64,16 +69,16 @@ export const makeDiscoverableExo = < * * @returns A schema of the methods. */ - describe: () => schema, + [GET_DESCRIPTION]: () => schema, }), ); } catch (error) { if ( error instanceof Error && - error.message.includes('Duplicate keys in records: describe') + error.message.includes(`Duplicate keys in records: ${GET_DESCRIPTION}`) ) { throw new Error( - 'The `describe` method name is reserved for discoverable exos.', + `The \`${GET_DESCRIPTION}\` method name is reserved for discoverable exos.`, ); } throw error; diff --git a/packages/kernel-exo/src/index.ts b/packages/kernel-exo/src/index.ts index fed1cd518..7ccc9c9c9 100644 --- a/packages/kernel-exo/src/index.ts +++ b/packages/kernel-exo/src/index.ts @@ -1,4 +1,4 @@ export { makeDefaultInterface, makeDefaultExo } from './exo.ts'; -export { makeDiscoverableExo } from './discoverable.ts'; +export { GET_DESCRIPTION, makeDiscoverableExo } from './discoverable.ts'; export type { DiscoverableExo } from './discoverable.ts'; export type { JsonSchema, MethodSchema } from './schema.ts';