Course Correction - Meta-Agent Monitoring

Level: Enhancement (beyond minimal agent) Prerequisites: Agent loop, tool system, subagents


What This Adds

A meta-agent architecture where a secondary LLM monitors the primary agent and injects corrective guidance when it detects problems.

This is essentially automated code review at inference time.


Key Concept

Question Answer
What monitors the agent? Gemini 3 Pro Preview (external observer)
When does it trigger? Only on end_turn, and only if 5+ tool calls and a file edit occurred (plus whitelist + non-free mode)
How does it intervene? Injects a user message that triggers new inference
Is it visible to users? Yes - displayed as correction

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                    COURSE CORRECTION FLOW                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  User Request                                                    │
│       │                                                          │
│       ▼                                                          │
│  Main Agent (Claude/GPT)                                         │
│       │                                                          │
│       ├── Tools Execute                                           │
│       │   (Read/Edit/Bash/etc.)                                   │
│       │                                                          │
│       ▼                                                          │
│  Agent Claims "Done" (stop_reason: end_turn)                      │
│       │                                                          │
│       ▼                                                          │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  COURSE CORRECTION CHECK (Gemini 3 Pro Preview)          │    │
│  │                                                         │    │
│  │  Gate:                                                  │    │
│  │  - 5+ tools since last user message                     │    │
│  │  - At least one file edit                               │    │
│  │  - Whitelisted + not free mode                          │    │
│  └────────────────────────────┬────────────────────────────┘    │
│                               │                                  │
│                               ▼                                  │
│                    needsCorrection?                              │
│                    ├── true → Inject message, re-run             │
│                    └── false → Continue normally                 │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

When to Intervene

Course correction triggers ONLY for clear problems:

Intervene (needsCorrection: true)

  1. False Completion - Agent claimed "done" but skipped explicit requirements
  2. Wrong Direction - Agent is doing something different from requested
  3. Hallucinated Success - Agent says "fixed" but output shows error
  4. Clear Mistake - Syntax error, missed import, logic bug
  5. Missing Obvious Steps - Didn't update related tests

Stay Silent (needsCorrection: false)

  • Agent is still working (even slowly)
  • Agent is exploring or debugging
  • Minor suboptimal choice
  • Agent completed what was asked
  • Uncertain whether intervention needed

Default to silence. Over-correction degrades performance.


The Course Correction Prompt

# Your Role

You are an EXTERNAL OBSERVER watching a conversation between a human
user and an AI coding agent. You are NOT the agent. You are a separate
system that monitors the agent's work.

Your job: Intervene only when the agent clearly goes off track.
When you intervene, write as if you are the user speaking to the agent.

# Critical Rules

**NEVER intervene to do or suggest anything the user didn't explicitly ask for.**
**NEVER run git operations (commit, push, pull, etc.)**
**NEVER suggest git operations**

# When to Stay Silent

Stay silent in ALL of these cases:
- Agent is still working (even if slowly or taking detours)
- Agent is exploring, debugging, or investigating
- Agent made a minor suboptimal choice but is still heading toward the goal
- You're unsure whether intervention is needed
- Agent completed exactly what was asked

**Default to silence. When in doubt, do not intervene.**

# When to Intervene

Intervene ONLY for clear, obvious problems:
1. Agent claimed "done" but skipped something explicitly requested
2. Agent is doing something completely different from what was requested
3. Agent says it succeeded but the output clearly shows failure
4. Agent made a mistake visible from the code or tool output

# How to Write the Message

Write like the user would - short, casual, direct.

Good examples:
- "I asked you to also run the tests"
- "wait, I wanted TypeScript, not JavaScript"
- "that's not what I meant - I want X, not Y"

Bad examples:
- "Please ensure you complete the testing phase"
- "I notice you have deviated from the specification"

The Tool Schema

Course correction uses a single tool with forced calling:

const courseCorrectionTool = {
  name: "course_correct",
  description: "Report whether the agent needs course correction and provide guidance if so.",
  inputSchema: {
    type: "object",
    properties: {
      needsCorrection: {
        type: "boolean",
        description: "True if intervention is required, false otherwise."
      },
      message: {
        type: "string",
        nullable: true,
        description: "Guidance message when correction is needed, otherwise null."
      }
    },
    required: ["needsCorrection"],
    additionalProperties: false
  }
};

Implementation

Trigger Conditions

const FILE_EDIT_TOOLS = new Set([
  "edit_file",
  "create_file",
  "format_file",
  "delete_file",
  "undo_edit"
]);

function shouldTriggerCourseCorrection(params: {
  toolCallsSinceLastUser: ToolCall[];
  lastUserWasCourseCorrection: boolean;
  agentMode: AgentMode;
  whitelisted: boolean;
  hasFileEdits: boolean;
}): boolean {
  if (!params.whitelisted) return false;
  if (isFreeMode(params.agentMode)) return false;
  if (params.lastUserWasCourseCorrection) return false;

  const toolCount = params.toolCallsSinceLastUser.length;
  const hasFileEdit = params.hasFileEdits ||
    params.toolCallsSinceLastUser.some(call => FILE_EDIT_TOOLS.has(call.name));

  return toolCount >= 5 && hasFileEdit;
}

Running the Assessment

async function runCourseCorrection(
  thread: Thread,
  signal: AbortSignal
): Promise<{ needsCorrection: boolean; message?: string }> {
  // 1. Extract AGENTS.md content from thread
  const guidanceContent = extractGuidanceFiles(thread);

  // 2. Build system prompt
  const systemPrompt = buildCourseCorrectPrompt(guidanceContent);

  // 3. Get recent conversation (since last user message)
  const recentMessages = getMessagesSinceLastUser(thread);

  // 4. Convert to markdown for the monitor
  const conversationMarkdown = threadToMarkdown(recentMessages);

  // 5. Call the monitoring model (end_turn only)
  const response = await inference({
    model: "gemini-3-pro-preview",
    system: systemPrompt,
    messages: [{
      role: "user",
      content: [{ type: "text", text: conversationMarkdown }]
    }],
    tools: [courseCorrectionTool],
    toolConfig: { mode: "ANY" },  // FORCE tool call
    temperature: 0.1,  // Very low for consistency
    maxTokens: 1024
  });

  // 6. Extract the tool call result
  const toolCall = response.content.find(c => c.type === "tool_use");
  return toolCall?.args;
}

Integration with Agent Loop

class AgentLoop {
  async onEndTurn(toolCallsSinceLastUser: ToolCall[]) {
    const shouldRun = shouldTriggerCourseCorrection({
      toolCallsSinceLastUser,
      lastUserWasCourseCorrection: this.lastUserSource === "course-correction",
      agentMode: this.mode,
      whitelisted: this.isWhitelisted,
      hasFileEdits: this.editedFiles.size > 0
    });

    if (!shouldRun) return;

    const result = await this.runCourseCorrection();
    if (result?.needsCorrection && result.message) {
      this.injectCorrectionMessage(result.message);
    }
  }

  injectCorrectionMessage(message: string) {
    // Add as user message with special source
    this.thread.messages.push({
      role: "user",
      content: [{ type: "text", text: message }],
      source: { type: "course-correction" }
    });

    // Trigger new inference (with skipCourseCorrection to prevent recursion)
    this.runInference({ skipCourseCorrection: true });
  }
}

End-Turn Assessment

async function onInferenceComplete(response: Response) {
  if (response.stopReason !== "end_turn") return;
  await this.onEndTurn(this.toolCallsSinceLastUser);
}

Model Choice

Using a different model for monitoring is deliberate:

  1. Prevents self-bias - Different model catches different errors
  2. Cost effective - Gemini 3 Pro Preview for monitoring, Claude for work
  3. Large context - 1M token context handles long conversations
  4. Reasoning - Built-in reasoning for analysis

Configuration

const COURSE_CORRECTION_CONFIG = {
  enabled: false,  // Experimental feature
  model: "gemini-3-pro-preview",
  temperature: 0.1,
  maxTokens: 1024,
  timeoutMs: 1000,
  requireWhitelist: true,
  skipFreeModes: true
};

Cost Tracking

Course correction costs are tracked separately:

interface UsageEvent {
  operationType: "inference" | "course-correction";
  model: string;
  tokens: { input: number; output: number };
  credits: number;
}

Even when no correction is applied, usage is tracked for billing.


When to Add Course Correction

Add this enhancement when:

  1. Agent reliability matters - Production use cases
  2. AGENTS.md compliance - Project rules must be enforced
  3. False completion is costly - Users shouldn't verify everything
  4. Budget allows - Additional model calls per interaction

Skip if:

  • Development/testing only
  • Cost is a primary concern
  • Agent tasks are simple/short

Limitations

  1. Latency - Adds ~1 second to critical path
  2. Cost - Additional model API calls
  3. False Positives - Could interrupt valid exploratory work
  4. Conservative - May miss subtle issues

Enhancement based on Amp Code v0.0.1769212917 patterns