Skip to content

feat(agent): implement /clear and advertise it as a capability - #76942

Closed
haacked wants to merge 2 commits into
masterfrom
haacked/clear-command-agent
Closed

feat(agent): implement /clear and advertise it as a capability#76942
haacked wants to merge 2 commits into
masterfrom
haacked/clear-command-agent

Conversation

@haacked

@haacked haacked commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Problem

/clear didn't work in cloud, which was blocking the feature. Two independent reasons:

The adapter read the leading slash command off the converted SDK message. In cloud that's almost never the user's text. promptToClaude prepends detected-PR and local-skill context, and the agent-server wraps a pending message in a hidden resume preamble. Since a finished cloud run turns every send into a new run, that resume shape is the common path, so /clear reached the model as literal text instead of clearing anything.

Separately, /clear wasn't advertised at all, so it never appeared in the composer's command menu.

Changes

Command detection now reads the ACP prompt and skips blocks the host injected rather than the user (_meta.ui.hidden, plus the local-skill chunk promptToClaude consumes). That also fixes the desktop case where shell-execute recaps precede the prompt. The hidden-block predicate already existed in the codex adapter, so it moved to a shared adapters/prompt-blocks.ts that both use.

The adapter also advertises a conversationClear capability in its initialize response, and the cloud agent-server relays it on _posthog/run_started.

That capability exists because of how this ships. The agent is published as @posthog/agent and baked into the sandbox image, on its own release, while the backend that records a clear boundary ships with PostHog. An agent predating the marker ignores it on resume and rebuilds the conversation the boundary was meant to retire. Without something to gate on, a user could be shown a cleared conversation that wasn't. The stacked PR consumes this.

This is the bottom of a two-PR stack, and it has to ship first. It is also useful alone: /clear on a live run works end to end once an agent-v* release lands.

How did you test this code?

Automated, all run locally:

  • 989 tests across packages/agent (adapters + sagas), including 16 in claude-agent.clear.test.ts
  • 389 in packages/core sessions
  • typecheck clean on agent, core, ui, shared

New tests, and the regression each catches:

  • claude-agent.clear.test.ts — a cloud-shaped prompt where /clear sits behind a hidden resume preamble still clears. This is the exact bug above; every existing clear test sent a bare /clear block, so none of them caught it.
  • resume-saga.test.ts — a conversation_cleared marker naming no session resumes nothing. findSessionId used to skip such an entry and keep scanning back to an earlier run_started, which on a warm sandbox would natively resume the conversation the marker retired.

I have not exercised /clear against a live cloud run. That needs the agent published and the sandbox image rolled, neither of which can happen before this merges.

Automatic notifications

  • Publish to changelog?

Docs update

UPSTREAM.md notes that PostHog Code now implements /clear itself rather than forwarding the SDK's.

Intercept /clear in the Claude adapter instead of forwarding it to the SDK:
retire the current query and swap in a brand-new SDK session (fresh id, no
resume) under the same ACP session. A _posthog/conversation_cleared marker
records the boundary in the append-only session log, and the rehydration
paths (jsonl hydration, ResumeSaga) treat it as a conversation boundary so
desktop reconnects and cloud resumes rebuild only the post-clear
conversation. The UI renders a "Conversation cleared" divider and resets the
context indicator.

Session.clearing (a promise, claimed synchronously) serializes the swap: a
second /clear is refused, cancel/interrupt is ignored mid-clear,
refreshSession refuses, and a racing prompt waits for the clear to settle
instead of pushing into the retired input stream. The "/clear" prompt is
broadcast only once the new session is confirmed live, so a timeout leaves no
orphaned entry in the log, and any error terminates the unproven replacement
query, closes the session, and resolves the spinner with clearing_failed.

The command is read off the ACP prompt rather than the converted SDK message,
skipping blocks the host injected rather than the user: promptToClaude
prepends detected-PR and local-skill context, and cloud prompts lead with
hidden blocks (a resume preamble; on desktop, shell-execute recaps). Matching
the first text block of either read host context as the user's command and
missed the command entirely, which is why /clear never fired on a resumed
cloud run.

The adapter advertises conversationClear in its initialize capabilities and
the cloud agent-server relays it on _posthog/run_started, so a host can tell
whether the agent it is talking to honours the boundary. Hosts that record
one without an agent gate on it; an agent that predates the marker ignores it
on resume and would rebuild the conversation the boundary was meant to
retire.

Claude-Session: https://claude.ai/code/session_01HJQHhq27qXnKGj98x7UrXZ
@trunk-io

trunk-io Bot commented Aug 3, 2026

Copy link
Copy Markdown

Merging to master in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@haacked haacked changed the title haacked/clear command agent feat(agent): implement /clear and advertise it as a capability Aug 3, 2026
@trunk-io

trunk-io Bot commented Aug 3, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

applySessionMode updated the running query and session.permissionMode but
left session.queryOptions.permissionMode at the value the session was created
with. Both query rebuilds (/clear, refreshSession) seed the replacement from
queryOptions, so a session that started in bypassPermissions and was later
narrowed silently returned to bypassPermissions after a clear, with nothing on
screen to say the mode had moved.

Sync the mode into queryOptions when it changes, matching how effort and the
1M-context beta already do it, so every rebuild inherits the live value rather
than each one re-rooting its own fields.

Reported by veria-ai on #76457.
@haacked

haacked commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #76457, which carries this same agent-side work. I'd opened this before spotting that #76457 was already the PR for it.

@haacked haacked closed this Aug 3, 2026
@haacked
haacked deleted the haacked/clear-command-agent branch August 6, 2026 18:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant