Skip to content

Emit a structured clear_boundary signal mirroring compact_boundary #656

@Seluj78

Description

@Seluj78

Note: Claude handled the investigation and prose via back-and-forth. Idea, decisions, and everything else are mine. — @Seluj78

Problem

When the user runs /clear, the Claude Code SDK ends its current internal conversation and starts a fresh one. The agent has no memory of prior turns after this point. From the ACP client's perspective, there is no structured signal that this happened: the adapter forwards /clear as a regular user prompt, the SDK handles it internally, and the next assistant turn just looks like a normal response with empty context.

Compare against compact_boundary, which the adapter explicitly handles in acp-agent.js:478-507:

case "compact_boundary": {
    // Send used:0 ... post-compaction context size dropped dramatically
    await this.client.sessionUpdate({
        sessionId: message.session_id,
        update: { sessionUpdate: "usage_update", used: 0, size: session.contextWindowSize },
    });
    await this.client.sessionUpdate({
        sessionId: message.session_id,
        update: {
            sessionUpdate: "agent_message_chunk",
            content: { type: "text", text: "\n\nCompacting completed." },
        },
    });
    break;
}

compact_boundary lets ACP clients render a visible divider, reset transcript expectations, and update the context-window indicator. /clear produces the same kind of state transition (context wiped, transcript misleading from the model's perspective) but the adapter emits nothing comparable.

Why this matters for ACP clients

Without a structured signal, clients have to detect /clear heuristically (substring match on the user prompt text). That fragile pattern:

  • Breaks on locale variants if /clear ever gets localised.
  • Breaks on user-defined custom clear commands.
  • Can't tell "user pasted the word /clear" from "user invoked the /clear command."

Most importantly, the transcript divergence after /clear is severe: the agent has no continuity at all (vs /compact which retains a summary). Without a boundary signal, the client either renders the full pre-clear transcript as if continuity exists (misleading) or has to text-match heuristically. Neither is robust.

What we'd like

Mirror compact_boundary. When /clear completes inside the SDK, emit either:

Option 1: _meta flag on the next agent message chunk

Same convention used for parentToolUseId. On the first chunk emitted after the clear:

update: {
    sessionUpdate: "agent_message_chunk",
    content: { type: "text", text: ... },
    _meta: { claudeCode: { clearBoundary: true } },
}

Minimal protocol surface. ACP clients that understand the flag render a clear boundary; clients that ignore it see a normal chunk. Same shape as compact_boundary's _meta would be if it were ever extended.

Option 2: dedicated sessionUpdate variant

A new top-level sessionUpdate: "session_cleared" variant the adapter emits when the SDK reports the clear boundary. Cleaner long-term protocol but needs ACP spec coordination.

Option 1 is the practical v1; Option 2 the right shape if the spec is being extended.

Concrete behavioral asks

When /clear lands inside the SDK and the adapter sees the corresponding boundary system message:

  1. Reset the cached lastAssistantTotalUsage / lastAssistantUsage to zero (matches compact_boundary behavior).
  2. Emit a usage_update with used: 0, size: session.contextWindowSize. Clients keep their context-window indicator from showing stale pre-clear numbers.
  3. Emit the clear-boundary signal (Option 1 or 2) so clients can render a divider and update transcript-presentation logic.
  4. Trigger sendAvailableCommandsUpdate(sessionId) so the client refreshes its slash palette (separate issue tracking this; mentioned here because the two land naturally together).

Out of scope

  • Surfacing the SDK's internal "what was cleared" payload. Clients only need to know the boundary happened; the prior transcript is on the client side already.
  • Locale handling of /clear itself. Boundary signal is independent of which surface command triggered it.

Context

Filed from the agent-of-empires side as the upstream half of njbrake/agent-of-empires#1101 (cockpit /clear UX gap). The aoe-side fix can ship with text-match detection as v1 (mirroring how /compact was handled in aoe), but a structured signal would eliminate that fragility and let any ACP client implement the same UX without per-client heuristics.

Tested against @agentclientprotocol/claude-agent-acp package at the version currently installed via npx claude-agent-acp. Adapter source inspected at dist/acp-agent.js lines 460-520 (system message switch) and 1796+ (getAvailableSlashCommands).

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