Context

System prompt assembly, tool execution gating, and output policies.

Overview

The context layer is an opt-in system that wraps your tools with cross-cutting behavior: blocking tools based on state (execution policy), truncating large outputs with redirection hints (output policy), and assembling a static system prompt from project docs and environment info.

Enable it by passing a context config to createAgentTools:

const { tools, contextLayers } = await createAgentTools(sandbox, {
context: {
executionPolicy: { /* ... */ },
outputPolicy: { maxOutputLength: 50000 },
layers: [myCustomLayer],
extraTools: { MyTool: myTool },
},
});

When context is omitted, tools work exactly as before — no wrapping, no overhead.

Context Layers

A ContextLayer intercepts tool execution with two optional hooks:

  • beforeExecute — return { error: string } to block a tool call, or undefined to allow it
  • afterExecute — transform the tool result (e.g., truncate output)
import { withContext, applyContextLayers } from 'bashkit';
import type { ContextLayer } from 'bashkit';
const loggingLayer: ContextLayer = {
beforeExecute: (toolName, params) => {
console.log(`Calling ${toolName}`);
return undefined; // allow execution
},
afterExecute: (toolName, params, result) => {
console.log(`${toolName} returned`);
return result; // pass through unchanged
},
};
// Wrap a single tool
const wrappedTool = withContext(myTool, 'MyTool', [loggingLayer]);
// Wrap an entire ToolSet
const wrappedTools = applyContextLayers(tools, [loggingLayer]);

Layers compose in order: first beforeExecute rejection wins, afterExecute transforms pipe through sequentially.

Execution Policy

Gates tool execution based on state. The most common use case is plan mode — blocking write tools while allowing read-only tools.

import { createExecutionPolicy } from 'bashkit';
// Plan mode: blocks Bash, Write, Edit by default
const policy = createExecutionPolicy(planModeState);
// Custom blocked tools
const policy = createExecutionPolicy(planModeState, {
planModeBlockedTools: ['Bash', 'Write', 'Edit', 'WebFetch'],
});
// Custom predicate (independent of plan mode)
const policy = createExecutionPolicy(undefined, {
shouldBlock: (toolName, params) => {
if (toolName === 'Bash' && String(params.command).includes('rm')) {
return 'Destructive commands are not allowed';
}
return undefined;
},
});

Tools stay registered in the tool set (prompt cache stable) — only execution is gated.

Output Policy

Handles large tool outputs by truncating and injecting hints that tell the model how to access the full result.

import { createOutputPolicy } from 'bashkit';
// Defaults: maxOutputLength 30000, redirectionThreshold 20000
const policy = createOutputPolicy();
// Custom thresholds
const policy = createOutputPolicy({
maxOutputLength: 50000,
redirectionThreshold: 40000,
excludeTools: ['Read'], // never truncate Read output
});

When output exceeds redirectionThreshold, it gets truncated to maxOutputLength and a _hint field is added with tool-specific guidance (e.g., "use head/tail to see specific parts").

Custom Hints

const policy = createOutputPolicy({
// Simple per-tool hint strings
hints: {
Bash: 'Re-run with | head or | tail to see specific parts.',
Grep: 'Narrow your pattern to reduce results.',
},
// Full control callback
buildHint: (toolName, params, originalLength, result) => {
if (toolName === 'Bash' && params.command === 'git log') {
return 'Use git log with --oneline or -n to limit output.';
}
return undefined; // fall through to hints map / defaults
},
});

Stash to Disk

Optionally save full output to disk before truncating, so the model can Read the file later:

const policy = createOutputPolicy({
stashOutput: {
sandbox,
tools: ['Bash', 'Grep'], // which tools get disk stash
dir: '/tmp/.bashkit/tool-output', // default
},
});

System Prompt Assembly

buildSystemContext assembles a static system prompt from three sources: discovered project instructions (AGENTS.md / CLAUDE.md files), environment info (cwd, platform, git branch), and tool guidance.

import { buildSystemContext } from 'bashkit';
const ctx = await buildSystemContext(sandbox, {
instructions: true, // discover AGENTS.md / CLAUDE.md files
environment: true, // collect cwd, shell, platform, git info
toolGuidance: {
tools: { Bash: 'Run shell commands', Read: 'Read files' },
},
});
// Use in streamText
const result = await streamText({
model,
system: ctx.combined, // all sections joined
tools,
messages,
});
// Or access individual sections
ctx.instructions // project instructions text
ctx.environment // environment XML block
ctx.toolGuidance // tool hint list

Call once at init — the output is deterministic and designed to stay stable across turns for Anthropic prompt caching.

prepareStep

createPrepareStep returns a callback for the AI SDK's prepareStep option. It composes message compaction, context status monitoring, and plan mode hints.

import { createPrepareStep } from 'bashkit';
const prepareStep = createPrepareStep({
compaction: {
model,
maxTokens: 128000,
threshold: 0.7,
},
contextStatus: {
maxTokens: 128000,
},
planModeState,
extend: async (args) => {
// Custom logic runs after built-in steps
return {};
},
});
const result = await streamText({
model,
tools,
messages,
prepareStep,
});

Important: prepareStep never touches the system prompt — all dynamic content is injected as user messages to preserve prompt caching.

Full Example

import {
createLocalSandbox,
createAgentTools,
buildSystemContext,
createPrepareStep,
} from 'bashkit';
import { streamText } from 'ai';
const sandbox = createLocalSandbox({ workingDirectory: '.' });
const { tools, planModeState, contextLayers } = await createAgentTools(
sandbox,
{
context: {
executionPolicy: {}, // plan mode gating with defaults
outputPolicy: {
maxOutputLength: 50000,
stashOutput: { sandbox, tools: ['Bash'] },
},
},
budget: { maxUsd: 5.0 },
},
);
const ctx = await buildSystemContext(sandbox, {
instructions: true,
environment: true,
});
const prepareStep = createPrepareStep({
planModeState,
contextStatus: { maxTokens: 128000 },
});
const result = await streamText({
model,
system: ctx.combined,
tools,
messages,
prepareStep,
});