Skip to content

Bug: INTERNAL_AGENT_SIGNATURES false positive blocks nudge injection on main sessions #581

Description

@xuxiong

Bug: INTERNAL_AGENT_SIGNATURES false positive blocks nudge injection on main sessions

Summary

DCP's internal agent detection logic incorrectly identifies main sessions as internal agents when OpenCode passes multiple system prompts in a single API call. This causes DCP to skip nudge injection entirely, making the proactive compress feature non-functional.

Environment

  • OpenCode: v1.17.11
  • DCP: @tarquinen/opencode-dcp@latest (v3.1.13)
  • Model: GLM-5.1-Canary (local-server)
  • Config:
{
  "enabled": true,
  "debug": true,
  "pruneNotification": "detailed",
  "compress": {
    "nudgeForce": "strong",
    "minContextLimit": 40000,
    "maxContextLimit": 80000,
    "nudgeFrequency": 3
  }
}

Bug Details

In lib/hooks.ts, the detection logic joins all system prompts and checks for internal agent signatures:

var INTERNAL_AGENT_SIGNATURES = [
  "You are a title generator",
  "You are a helpful AI assistant tasked with summarizing conversations",
  "You are an anchored context summarization assistant for coding sessions",
  "Summarize what was done in this conversation"
];

function createSystemPromptHandler(state, logger, config, prompts) {
  return async (input, output) => {
    // ...
    const systemText = output.system.join("\n");
    if (INTERNAL_AGENT_SIGNATURES.some((sig) => systemText.includes(sig))) {
      logger.info("Skipping DCP system prompt injection for internal agent");
      return;  // <-- Skips nudge injection entirely
    }
    // ...
  };
}

Root Cause

OpenCode passes multiple system prompts in a single API call. When the main session runs alongside internal agents (title generator, compaction summarizer), output.system contains ALL system prompts concatenated. The joined text includes internal agent signatures, causing the check to match even for the main session.

Observed Behavior

DCP debug log shows:

Skipping DCP system prompt injection for internal agent

...on the main session, not an internal agent. As a result:

  • No TURN_NUDGE is injected at minContextLimit
  • No CONTEXT_LIMIT_NUDGE is injected at maxContextLimit
  • No ITERATION_NUDGE is injected during long iterations
  • The model never calls the compress tool proactively
  • Only native OpenCode compaction fires (at overflow), which is less granular

Expected Behavior

DCP should only skip nudge injection for actual internal agent calls, not for main sessions that happen to share an API call with internal agents.

Reproduction Steps

  1. Install DCP: opencode plugin @tarquinen/opencode-dcp@latest --global
  2. Configure with debug: true and compress.nudgeForce: "strong"
  3. Start an OpenCode session and send several messages to accumulate context past minContextLimit
  4. Check DCP debug log at ~/.config/opencode/logs/dcp/daily/<date>.log
  5. Observe "Skipping DCP system prompt injection for internal agent" on the main session
  6. Observe that no nudge is ever injected and the model never proactively compresses

Workaround

Disable the internal agent check by modifying the condition in the dist file:

// Change:
if (INTERNAL_AGENT_SIGNATURES.some((sig) => systemText.includes(sig))) {
// To:
if (false && INTERNAL_AGENT_SIGNATURES.some((sig) => systemText.includes(sig))) {

After this change, DCP's proactive compress works correctly. In testing, the model called the compress tool when nudged, saving 55,443 tokens in a single session.

Note: This workaround is lost on @latest reinstall.

Suggested Fix

Instead of joining all system prompts and checking the combined text, check each system prompt individually and only skip if the majority or primary system prompt matches an internal agent signature. For example:

// Option A: Check each prompt individually, skip only if ALL match
const isInternalAgent = output.system.every((prompt) =>
  INTERNAL_AGENT_SIGNATURES.some((sig) => prompt.includes(sig))
);

// Option B: Check only the first/primary system prompt
const isInternalAgent = INTERNAL_AGENT_SIGNATURES.some((sig) =>
  output.system[0]?.includes(sig)
);

// Option C: Use a more specific detection method (e.g., check for a dedicated flag in input)

This would correctly distinguish between a true internal agent call and a main session that shares an API call with internal agents.

Impact

Without this fix, DCP's core value proposition — proactive, model-driven context compression — is completely non-functional for users whose OpenCode setup passes multiple system prompts in a single API call. The plugin degrades to only providing passive features (tool result caching, deduplication, TUI panel) while the main compress feature silently fails.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions