Use this file to discover all available pages before exploring further.
Squad’s hooks system provides programmatic enforcement of governance policies. Hooks intercept agent tool calls before and after execution — letting you block dangerous operations, scrub PII from outputs, inject context at session start, and handle errors. Unlike prompt-level rules, hooks run as ordinary TypeScript functions: deterministic, unit-testable, and free from model interpretation.
Pre-tool-use hooks run synchronously (or asynchronously) before a tool call reaches the agent. Every hook returns a PreToolUseResult that tells the pipeline whether to allow, block, or modify the call.
import type { PreToolUseContext, PreToolUseResult } from '@bradygaster/squad-sdk/hooks';export interface PreToolUseContext { /** Name of the tool being called (e.g. 'edit', 'bash', 'ask_user') */ toolName: string; /** Arguments passed to the tool */ arguments: Record<string, unknown>; /** Agent name that invoked the tool */ agentName: string; /** Session ID */ sessionId: string;}export interface PreToolUseResult { action: HookAction; /** Modified arguments — only used when action === 'modify' */ modifiedArguments?: Record<string, unknown>; /** Human-readable reason — only used when action === 'block' */ reason?: string;}
Every PreToolUseResult must carry one of three action values:
Action
Meaning
'allow'
Proceed normally — the tool call continues unchanged.
'block'
Cancel the tool call — the agent receives the reason string as an error.
'modify'
Replace the tool arguments with modifiedArguments before the call proceeds.
The pipeline runs hooks in registration order. As soon as one hook returns 'block', the pipeline short-circuits and no further hooks run. If a hook returns 'modify', the updated arguments are passed to subsequent hooks as the new context.
The built-in file-write guard blocks any edit, create, write_file, or create_file tool call where the target path does not match one of the allowed glob patterns. Glob patterns support * (within a path segment) and ** (across segments).
import { HookPipeline } from '@bradygaster/squad-sdk/hooks';import type { PreToolUseContext } from '@bradygaster/squad-sdk/hooks';const pipeline = new HookPipeline({ allowedWritePaths: ['src/**/*.ts', '.squad/**'],});// Allowed — matches src/**/*.tsconst safeCtx: PreToolUseContext = { toolName: 'create', arguments: { path: 'src/utils/helper.ts' }, agentName: 'McManus', sessionId: 'session-001',};const safeResult = await pipeline.runPreToolHooks(safeCtx);// → { action: 'allow' }// Blocked — /etc/passwd does not match any allowed patternconst dangerCtx: PreToolUseContext = { toolName: 'edit', arguments: { path: '/etc/passwd' }, agentName: 'McManus', sessionId: 'session-001',};const dangerResult = await pipeline.runPreToolHooks(dangerCtx);// → { action: 'block', reason: 'File write blocked: "/etc/passwd" does not match ...' }
The built-in PII scrubber is a post-tool-use hook that replaces email addresses with [EMAIL_REDACTED]. It handles plain string results and recursively scrubs nested objects:
import { HookPipeline } from '@bradygaster/squad-sdk/hooks';import type { PostToolUseContext } from '@bradygaster/squad-sdk/hooks';const piiPipeline = new HookPipeline({ scrubPii: true });const ctx: PostToolUseContext = { toolName: 'bash', arguments: { command: 'git log --oneline' }, result: 'Deploy fix by brady@example.com — cc: alice@company.io', agentName: 'Verbal', sessionId: 'session-002',};const { result } = await piiPipeline.runPostToolHooks(ctx);// → 'Deploy fix by [EMAIL_REDACTED] — cc: [EMAIL_REDACTED]'
The reviewer lockout hook prevents an agent from editing a file after it has been flagged by a reviewer. This enforces the principle that the original author cannot override a review decision:
import { HookPipeline } from '@bradygaster/squad-sdk/hooks';const lockoutPipeline = new HookPipeline({ reviewerLockout: true });const lockout = lockoutPipeline.getReviewerLockout();// Reviewer rejects the auth module — lock out the original authorlockout.lockout('src/auth.ts', 'Backend');// Backend attempts to edit — blockedconst result = await lockoutPipeline.runPreToolHooks({ toolName: 'edit', arguments: { path: 'src/auth.ts' }, agentName: 'Backend', sessionId: 'session-003',});// → { action: 'block', reason: 'Reviewer lockout: Agent "Backend" is locked out ...' }// Frontend is not locked out — allowedconst frontendResult = await lockoutPipeline.runPreToolHooks({ toolName: 'edit', arguments: { path: 'src/auth.ts' }, agentName: 'Frontend', sessionId: 'session-003',});// → { action: 'allow' }// Inspect the registrylockout.getLockedAgents('src/auth.ts'); // → ['Backend']// Clear when the review cycle endslockout.clearLockout('src/auth.ts');
The ReviewerLockoutHook class is also exported directly if you need to manage lockout state independently of a HookPipeline.
Squad’s custom tools are the typed orchestration primitives agents use to coordinate work. They are available on the server-side tool registry and exposed to the Copilot SDK session. Their full type signatures live in @bradygaster/squad-sdk/tools.
The base types for all Squad custom tools, imported from @bradygaster/squad-sdk/adapter:
import type { SquadTool, SquadToolResult } from '@bradygaster/squad-sdk/adapter';
These types align with the @github/copilot-sdk tool registration API and are used internally when registering squad_route, squad_decide, squad_memory, squad_status, and squad_skill with the session.
All tool arguments are sanitized before being recorded as OpenTelemetry span
attributes. Fields matching content, query, token, secret,
password, key, or auth patterns are replaced with [REDACTED].