Skip to content

Hooks

Hooks: typed questions the built-in tasks ask the extensions their conversation selects, in extension order, before they commit a decision. A hook may return its answer, nothing, or an effect that reads committed state (Session) or uses the asking task’s memos (TaskRuntime).

import { Extension, Hooks } from "@effective-harness/core"
const Permissions = Extension.make("permissions", {
hooks: [Hooks.tool({ beforeTool: (call) => (call.name === "bash" ? { block: "Needs approval" } : undefined) })]
})
hook composition on failure
beforeRequest replacement chain reported, skipped
afterResponse all observers reported
onYield first continuation wins reported, skipped
afterTools all observers reported
beforeTool argument replacement chain; first block wins blocks the call with the error text
afterTool result replacement chain reported, skipped
beforeCompact first decision wins reported, skipped

Since v0.0.0


Hooks of the pi.compaction task.

Signature

export interface CompactionHooks {
/** After range selection, before summarizing: decline the compaction, or supply the summary instead of the model. */
readonly beforeCompact?: (
compaction: CompactionRange
) => HookResult<{ readonly decline: true } | { readonly summary: string }>
}

Source

Since v0.0.0

What beforeCompact sees: the range a summary would replace.

Signature

export interface CompactionRange {
readonly reason: CompactionReason
/** The active entries the summary replaces, the head marker first. */
readonly entries: ReadonlyArray<EntryRecord>
/** Their model context: the summarizer's source. */
readonly messages: ReadonlyArray<Message>
/** The first entry kept verbatim; the summary's `head`. */
readonly firstKept: EntryId
readonly instructions?: string
}

Source

Since v0.0.0

Hooks of the pi.generation task.

Signature

export interface GenerationHooks {
/** Before every request attempt, including recovery; the result is used for that request only. */
readonly beforeRequest?: (request: {
readonly messages: ReadonlyArray<Message>
}) => HookResult<{ readonly messages: ReadonlyArray<Message> }>
/** Every terminal provider message, before classification. */
readonly afterResponse?: (message: AssistantMessage) => HookResult<void>
/** A final answer; `continue` appends a user message and continues the run. */
readonly onYield?: (answer: AssistantMessage) => HookResult<{ readonly continue: UserContent }>
/** After every tool of the round is terminal; `results` are the round's result entries in call order. */
readonly afterTools?: (assistant: EntryId, results: ReadonlyArray<EntryId>) => HookResult<void>
}

Source

Since v0.0.0

An answer, nothing, or an effect producing either.

Signature

type HookResult<A> = A | undefined | void | Effect.Effect<A | undefined | void, unknown, Session | TaskRuntime>

Source

Since v0.0.0

Hooks of the pi.tool task.

Signature

export interface ToolHooks {
/** Before intent; replaces the arguments or blocks the call with error text. */
readonly beforeTool?: (call: ToolCall) => HookResult<{ readonly arguments?: JsonObject; readonly block?: string }>
/** After execution, before the result entry; replaces the result. */
readonly afterTool?: (call: ToolCall, result: ToolResult) => HookResult<ToolResult>
}

Source

Since v0.0.0

A tool’s settled result, as afterTool sees and may replace it.

Signature

export interface ToolResult extends Tool.Result {
readonly diagnostics?: ReadonlyArray<ToolDiagnostic>
}

Source

Since v0.0.0

Register compaction hooks.

Signature

declare const compaction: (handlers: CompactionHooks) => HookRegistration

Source

Since v0.0.0

Register generation hooks.

Signature

declare const generation: (handlers: GenerationHooks) => HookRegistration

Source

Since v0.0.0

Register tool hooks.

Signature

declare const tool: (handlers: ToolHooks) => HookRegistration

Source

Since v0.0.0