diff options
| author | Adam Malczewski <[email protected]> | 2026-06-04 22:26:46 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-04 22:26:46 +0900 |
| commit | fd855ffb335e72a94b6f992ede5a859237460a8b (patch) | |
| tree | 3833674b0957ddec1ed3b3e6140c89360e675037 /packages/kernel/src/contracts/hooks.ts | |
| parent | a6119e0434597399c773da6f0b31363003f6aa09 (diff) | |
| download | dispatch-fd855ffb335e72a94b6f992ede5a859237460a8b.tar.gz dispatch-fd855ffb335e72a94b6f992ede5a859237460a8b.zip | |
feat(kernel): define the ABI contracts (conversation, tool, provider, auth, dispatch, hooks, extension/HostAPI, runtime, events)
Diffstat (limited to 'packages/kernel/src/contracts/hooks.ts')
| -rw-r--r-- | packages/kernel/src/contracts/hooks.ts | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/packages/kernel/src/contracts/hooks.ts b/packages/kernel/src/contracts/hooks.ts new file mode 100644 index 0000000..eb94465 --- /dev/null +++ b/packages/kernel/src/contracts/hooks.ts @@ -0,0 +1,92 @@ +/** + * Hooks and services — typed cross-extension coupling anchors. + * + * Every cross-extension reference is anchored to an exported typed symbol + * (not a raw string), so `lsp references` can compute the true blast radius + * of a change. The id string lives in exactly one place: the owner's + * `defineHook` / `defineFilter` / `defineService` declaration. + * + * Two hook kinds: + * - **Event**: fire-and-forget, N listeners, error-isolated per handler. + * - **Filter**: ordered value-in→value-out chain, in-band, awaited. + * + * Services are NOT hooks — they are single-responder request/response. + */ + +/** + * A typed descriptor for an event hook. The id string is the single source + * of truth; consumers import this symbol, never the raw string. + * + * Event hooks are fire-and-forget: N listeners, errors isolated per handler + * (a thrown handler is caught and logged — it never breaks the turn). + */ +export interface EventHookDescriptor<TPayload> { + readonly kind: "event"; + readonly id: string; + readonly _payload?: TPayload; +} + +/** + * A typed descriptor for a filter hook. Filters are an ordered chain: + * each receives the current value and returns the (possibly transformed) + * next value. Awaited in-band — a slow filter slows the turn, by design. + * + * Fail-open by default (a thrown filter logs and passes the value through); + * the owner may mark a chain fail-closed. + */ +export interface FilterDescriptor<TValue> { + readonly kind: "filter"; + readonly id: string; + readonly _value?: TValue; +} + +/** Union of hook descriptor kinds the kernel mechanism supports. */ +export type HookDescriptor<TPayload> = EventHookDescriptor<TPayload> | FilterDescriptor<TPayload>; + +/** + * A typed service handle. Services are single-responder request/response — + * NOT hooks. Modeling "ask the human for permission" as a hook invites + * "which of N handlers wins?" ambiguity; a service has exactly one provider. + */ +export interface ServiceHandle<T> { + readonly kind: "service"; + readonly id: string; + readonly _type?: T; +} + +/** + * Create a typed event hook descriptor. Pure function, no side effects. + * The returned object is the symbol consumers import and pass to `host.on`. + * + * @param id - Namespaced hook id in `owner/name` form (e.g. "kernel/turn.sealed"). + */ +export function defineEventHook<TPayload>(id: string): EventHookDescriptor<TPayload> { + return { kind: "event", id }; +} + +/** + * Create a typed filter hook descriptor. Pure function, no side effects. + * The returned object is the symbol consumers import and pass to `host.addFilter`. + * + * @param id - Namespaced filter id in `owner/name` form. + */ +export function defineFilter<TValue>(id: string): FilterDescriptor<TValue> { + return { kind: "filter", id }; +} + +/** + * Create a typed service handle. Pure function, no side effects. + * The returned object is the symbol a provider passes to `host.provideService` + * and consumers pass to `host.getService`. + * + * @param id - Namespaced service id in `owner/name` form. + */ +export function defineService<T>(id: string): ServiceHandle<T> { + return { kind: "service", id }; +} + +/** Handler function for an event hook subscription. */ +export type EventHandler<TPayload> = (payload: TPayload) => void | Promise<void>; + +/** Transform function for a filter hook in the chain. */ +export type FilterHandler<TValue> = (value: TValue) => TValue | Promise<TValue>; |
