Skip to content

feat(unstable): add client system prompt schema - #2361

Merged
benbrandt merged 8 commits into
mainfrom
feat/client-system-prompt-schema
Oct 12, 2026
Merged

benbrandt merged 8 commits into
mainfrom
feat/client-system-prompt-schema

Conversation

@benbrandt

@benbrandt benbrandt commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Implements the schema for the Client-Provided System Prompt RFD (#1237) in ACP v1 and v2, behind a new unstable_session_system_prompt feature that is part of the unstable umbrella. The stable schemas are unchanged.

Schema

  • Capability: sessionCapabilities.systemPrompt in v1, session.systemPrompt in v2. {} advertises append mode; { "mode": { "override": {} } } also advertises override mode.
    • Types: SessionSystemPromptCapabilities, SessionSystemPromptModeCapabilities, SessionSystemPromptOverrideCapabilities.
  • Request field: optional systemPrompt: { mode, content, _meta? } (SystemPrompt, SystemPromptMode) on session/new, session/resume, and session/fork, plus session/load in v1. Each request gets a system_prompt(...) builder.
    • mode is required on the wire, so every request states its mode.
    • In Rust, SystemPromptMode defaults to Append, SystemPrompt::new takes only the content, and .mode(...) selects override. Serialization always writes mode.

Design notes

  • Strict request field, lenient capabilities. Capabilities use DefaultOnError, so a malformed capability means "not advertised". systemPrompt has no fallback: the agent acts on it, so a malformed value fails the request with -32602 instead of creating a session without the prompt. This covers a missing or null mode, a missing content, a malformed content block. null is equivalent to omitting the field.
  • mode is extensible in both versions. SystemPromptMode has the standard Other(String) fallback under the enum variant extension rule, as for v2 MCP server transports: unknown modes parse so proxies can forward them, and agents MUST return an error for modes they don't support rather than treating them as append or override.
  • v2 unknown content blocks. v2's ContentBlock keeps unknown block types as ContentBlock::Other. The field docs tell agents they MUST reject block types they don't support.
  • override is a Rust keyword, so the capability field and builder are override_ with #[serde(rename = "override")], following the existing type_ fields.
  • SystemPrompt derives Eq, so the lifecycle requests keep their stable Eq derive. This relies on content being Eq since feat(rust): implement Eq for content blocks #2352.

Docs

  • Draft v1 and v2 initialization guides: the new capability.
  • Draft v1 and v2 session-setup guides: a new "System Prompt (unstable)" section covering modes, lifecycle resend semantics, errors, and capability gating.
  • Regenerated schema.unstable.json and draft schema.mdx for both versions.
  • RFD: implementation note and revision history, plus one spec change: mode is now extensible rather than closed.

Testing

  • npm run check (clippy in three configurations, format, spellcheck, all-features tests)
  • cargo test -p agent-client-protocol-schema with no features, with unstable_session_system_prompt, and with it plus unstable_protocol_v2
  • New tests in v1 and v2:
    • capability round-trips, including the nested override, and fallback on malformed capabilities (a malformed mode capability means append only)
    • request round-trips on every lifecycle request, and override round-trips
    • null equivalent to omitted for systemPrompt, checked on every lifecycle request through a shared helper
    • rejection of malformed values on every lifecycle request: string prompt, missing or null mode, missing content, a non-string mode, non-array content, and a malformed text block
    • both versions preserve unknown modes as SystemPromptMode::Other; v1 rejects unknown content block types, while v2 preserves them as ContentBlock::Other
  • npm run generate is idempotent; mint broken-links passes
  • A scratch merge with current main regenerates identically and passes the system prompt tests

Not included

  • SDK example agents and clients live in the SDK repositories and aren't updated here.
  • The feature stays unstable until the RFD moves to Preview.

Add the unstable_client_system_prompt feature implementing the
Client-Provided System Prompt RFD in ACP v1 and v2: the systemPrompt
session capability with a nested mode.override sub-capability, and a
strict systemPrompt { mode, content, _meta } field on session/new,
session/load (v1), session/resume, and session/fork. Document the
capability and field in the draft initialization and session setup
guides, and regenerate the unstable schemas.
@benbrandt
benbrandt requested a review from a team as a code owner October 12, 2026 11:24
Restore RFD requirements the draft session setup guides and type docs
left out: clients must only send text content, append should get
system-level precedence, override may keep workspace context and moves
responsibility for tool and safety guidance to the client, deferring a
changed prompt is limited to cache or reasoning loss, invalid_params is
the recommended error, and clients should only send trusted content.
Add tests for override round-trips, malformed mode capabilities, and
rejection on load and fork.
Derive Default on SystemPromptMode with Append as the default, make
SystemPrompt::new take only the content, and add a mode builder for
override. The wire format is unchanged: mode is always serialized and
still required when deserializing.
Make SystemPrompt.mode optional on the wire: omitted and null both mean
append, so a request only replaces the agent's default instructions when
it asks for override explicitly. Unknown modes are still rejected.
Update the RFD and draft guides to match.

Rename the feature to unstable_session_system_prompt to match the
session capability and the SessionSystemPrompt* types.
Add an Other(String) fallback to the v2 SystemPromptMode, following the
v2 enum variant extension rule used for other values receivers act on,
such as MCP server transports. Unknown modes now parse so proxies and
older SDKs can store and forward them; agents must still return an error
for a mode they do not support rather than treating it as append or
override. v1 stays closed, like other v1 enums. Update the RFD and the
v2 draft guide to match.
…ntions

Order the system_prompt builders after the existing fields, move the
system prompt types next to the other shared session-setup types, use
the standard new() and Optional doc wording, trim the v2 Other doc to
the standard text, place the feature next to unstable_session_fork,
write the recommended error as JSON-RPC -32602 in the guides, add an
Implemented subsection to the RFD, and test strictness on every
lifecycle request through a shared helper.
Make SystemPrompt.mode required again, as the RFD originally specified,
so SDKs generated from the JSON schema never need to apply a default.
Rust keeps the ergonomics: SystemPromptMode still defaults to Append,
SystemPrompt::new starts in append mode, and serialization always
writes mode. Missing and null modes are now rejected.
Add the same Other(String) fallback to the v1 SystemPromptMode so v1 and
v2 handle unknown modes identically: they parse so proxies can forward
them, and agents return an error for modes they do not support. The
RFD's v1/v2 table now lists the remaining difference, unknown content
block types.
@benbrandt
benbrandt merged commit 2007bb8 into main Oct 12, 2026
14 checks passed
@benbrandt
benbrandt deleted the feat/client-system-prompt-schema branch October 12, 2026 19:16
@acp-release-bot acp-release-bot Bot mentioned this pull request Oct 12, 2026
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