Scope Registry
The agent works with its Scopes via the Scope Registry — each one a named bundle of tools and context that the loop reads on every iteration.
Usage
Registering
register(scope: Scope): () => void
unregister(name: string): voidregister returns an idempotent disposer. Call it to remove the scope, or use the named form.
import { agent } from './agent';
import { pullRequest } from './pull-request';
const dispose = agent.scopes.register(pullRequest);
// later
dispose();
// or
agent.scopes.unregister('pullRequest');You often don't need to unregister
If availability depends on state (role, feature flag, route), prefer the enabled predicate — it gates the scope dynamically without re-registering when conditions flip.
Reach for unregister only when the scope's lifetime genuinely ends.
Reading
getSnapshot(): Scope[]
get(name: string): Scope | undefinedgetSnapshot returns an immutable array — same reference between mutations.
get(name) looks up a single scope by name, or undefined if not registered.
const scopes = agent.scopes.getSnapshot();
console.log(`${scopes.length} scope(s) active`);
const pullRequest = agent.scopes.get('pullRequest');Subscribing
subscribe(listener: () => void): () => voidThe listener fires after every register / unregister. Returns an unsubscribe function.
const unsubscribe = agent.scopes.subscribe(() => {
render(agent.scopes.getSnapshot());
});
// later
unsubscribe();Hooks
The agent fires these around registry mutations. See Hooks for full signatures and examples:
onScopeRegister— fires afterregister(scope)commits a scope.onScopeUnregister— fires afterunregister(name)removes a scope.
Runtime Errors
Ag2bError
Thrown by register on a collision — setup-time only, not recoverable.
- Scope name — another scope with the same
nameis already registered. - Tool name — a tool in the incoming scope shares a
namewith a tool already in another registered scope.