Extensions
Add typed Tools, lifecycle Hooks, scoped Resources, and Provided Services.
An Extension is a named, reusable unit that can contribute Instructions and Tools or participate in the Agent lifecycle. Use defineExtension and tool from the Promise-first SDK.
A Tool with a scoped Resource
import { Schema } from "effect";
import { defineExtension, tool } from "@mitome/sdk";
interface Notes {
readonly values: Array<string>;
}
export const notes = defineExtension({
name: "notes",
instructions: "Save a note when the user asks you to remember something.",
tools: [
tool<string, string, Notes>({
name: "save_note",
description: "Save one note for this Session",
inputSchema: Schema.String,
outputSchema: Schema.String,
needsApproval: true,
handler: async (text, { resource, signal }) => {
signal.throwIfAborted();
resource.values.push(text);
return `Saved ${resource.values.length} note(s)`;
},
}),
],
setup: async (): Promise<Notes> => ({ values: [] }),
dispose: async (resource) => void resource.values.splice(0),
});Tool input and output are validated. inputSchema may be an Effect Schema or a Standard Schema that also supplies JSON Schema; outputSchema may be either validation form. needsApproval accepts a boolean or an input-aware predicate. The handler receives the Extension's private Resource, an AbortSignal, and access to declared Provided Services.
setup runs once when a Session starts. dispose runs when its Session scope closes, including failure and interruption paths. A resumed Transcript seeds a new Session and therefore acquires fresh Resources.
Hook contracts
Every Promise Hook receives a context containing resource, signal, and getService. Hooks run in deterministic dependency-first, declaration-stable order; teardown is reversed.
| Hook | Contract |
|---|---|
sessionStart(context) | Observe Session startup after Resources are acquired. |
sessionEnd(context) | Observe Session shutdown before Resources are disposed. |
turnStart(text, context) | Observe the user text at the start of a Turn. |
turnEnd(text, context) | Observe successful Turn completion. |
stepStart(prompt, context) | Observe the model prompt before one Step. |
stepEnd(prompt, context) | Observe emitted responseParts; failed Steps include partial parts. |
preStep(prompt, context) | Return the prompt to send, transformed or unchanged. |
preTool(context) | Observe a decoded Tool call; return { reason } to veto it or nothing to continue. |
postTool(context) | Observe and return the Tool result, transformed or unchanged; isFailure identifies failed handlers. |
A Hook rejection fails the Turn with TurnError. preTool is policy before execution, while Approval is a separate Host decision after policy. Do not use one as a substitute for the other.
Extension Dependencies and Provided Services
An Extension may depend on another Extension. Dependencies are auto-included, deduplicated by identity, and loaded dependency-first. Same-name conflicts and cycles fail Agent Definition compilation.
A dependency exposes only the services listed in provides; its other Resources remain private. One Provided Service instance is shared by all declared dependents for a Session. Promise Extensions use Effect service tags as typed contracts:
import { Context } from "effect";
import { defineExtension, tool } from "@mitome/sdk";
import { Schema } from "effect";
class Counter extends Context.Service<Counter, { count: number }>()("example/Counter") {}
const counter = defineExtension({
name: "counter",
provides: [Counter],
tools: [],
setup: async () => ({ count: 0 }),
});
export const increment = defineExtension({
name: "increment",
dependencies: [counter],
tools: [
tool({
name: "increment",
dependencies: [Counter],
inputSchema: Schema.Void,
outputSchema: Schema.Number,
handler: async (_input, { getService }) => ++getService(Counter).count,
}),
],
});The compile-time coverage rule rejects Tool handlers or Hooks that ask for a service not supplied by their own Resource or a declared dependency's Provided Services.
Approvals and cancellation
A Tool marked needsApproval pauses the Turn and emits approval-required. The Host must call approve() or deny(reason?). Interrupting event iteration cancels the Turn and triggers the context AbortSignal, allowing Tools and Hooks to stop external work.
See the SDK event loop for Host-side handling, Transcript persistence for resume and commit semantics, and ADR-0035 for dependency rationale.