attachRedactionPipeline function

void attachRedactionPipeline(
  1. Agent agent,
  2. RedactionPipeline pipeline, {
  3. RedactionToolPolicy policy = const _ConstPolicy(),
})

Composes the pipeline hooks onto agent, preserving hooks already registered. beforeToolCall: existing hooks run first; if any of them blocks, that verdict stands. afterToolCall/transformContext: existing hooks run first, redaction runs last so content they produce is masked too.

Implementation

// ignore: long-method
void attachRedactionPipeline(
  Agent agent,
  RedactionPipeline pipeline, {
  RedactionToolPolicy policy = const _ConstPolicy(),
}) {
  final hooks = redactionPipelineHooks(pipeline, policy: policy);

  final existingBefore = agent.beforeToolCall;
  agent.beforeToolCall = (context, cancelToken) async {
    if (existingBefore != null) {
      final prior = await existingBefore(context, cancelToken);
      if (prior != null && prior.block) return prior;
    }
    return hooks.beforeToolCall(context, cancelToken);
  };

  final existingAfter = agent.afterToolCall;
  agent.afterToolCall = (context, cancelToken) async {
    final prior = existingAfter == null
        ? null
        : await existingAfter(context, cancelToken);
    final rawResult = context.result;
    final effectiveContent = prior?.content ?? rawResult.content;
    final effectiveContext = AfterToolCallContext(
      assistantMessage: context.assistantMessage,
      toolCall: context.toolCall,
      result: ToolExecutionResult(content: effectiveContent),
      isError: context.isError,
      context: context.context,
    );
    // `await` flattens the hook's FutureOr<AfterToolCallResult?>.
    final ours = await hooks.afterToolCall(effectiveContext, cancelToken);
    if (ours == null && prior == null) return null;
    return AfterToolCallResult(
      content: ours?.content ?? prior?.content ?? context.result.content,
      isError: prior?.isError,
      terminate: prior?.terminate,
    );
  };

  final existingTransform = agent.transformContext;
  agent.transformContext = (messages, cancelToken) async {
    final transformed = existingTransform == null
        ? messages
        : await existingTransform(messages, cancelToken);
    return hooks.transformContext(transformed, cancelToken);
  };
}