Overview
I'm interested in running dynamic agents that can evolve over time, where the base system prompt can change. Currently the SDK only supports defining the system prompt as a static agent (see workaround below).
Add the ability for users to update a session's base system message (the default top-level agent's system prompt) while a session is active, without stopping and resuming. The update would apply on the next user turn, queue if a turn is in flight, and preserve the full conversation history.
Problem / Motivation
Agent behavior evolves within a session, but today the system prompt is fixed once the session starts. The only way to change the agent's core instructions is to stop and resume — a heavyweight operation that disrupts a healthy, in-progress session. This forces developers to either cram every phase into one monolithic prompt or tear down sessions just to adjust behavior.
Live system prompt updates would make agents genuinely steerable: applications can adapt an agent's persona, constraints, and priorities on the fly while preserving the full conversation history, unlocking phase-aware workflows, dynamic policy enforcement, and adaptive behavior. By reusing the existing SystemMessageConfig and applying cleanly on the next turn, it delivers this with minimal new surface area and a strong correctness guarantee — a small, well-bounded change with broad impact.
Today the base system message is configurable only at session creation or resume via SystemMessageConfig (createSession / resumeSession). There is no way to update it on a running session:
- The live mutable-options RPC
session.options.update (SessionUpdateOptionsParams) exposes many configurable options — model, tools, working directory, skills, instruction sources, etc. — but has no systemMessage field.
- The
systemMessage.transform callback the runtime uses to materialize customized sections fires only during session creation/resume.
Temporary workaround:
Changing the agent's actual system prompt currently requires a full stop + resume. This is heavyweight (re-initialization, lifecycle event replay) for use cases that need to steer the agent's behavior mid-session — e.g. switching persona/policy after a phase transition, injecting updated org/policy instructions, or adapting tone/guidelines in response to runtime conditions — while keeping the existing conversation intact.
Proposal
Add a dedicated session-scoped JSON-RPC method, modeled after the existing session.agent.setPrompt:
- Method:
session.systemMessage.set
- Params (
SessionSystemMessageSetParams): { sessionId: string; systemMessage: SystemMessageConfig } — reuse the same config shape already accepted by create/resume (plain content, or mode: "customize" with per-section replace / append / remove / prepend / preserve / transform actions).
- Result (
SessionSystemMessageSetResult): { success: boolean; status?: "applied" | "queued" }.
Runtime semantics:
- Store the new config on the session.
- If a turn is active, queue and apply after it completes (
status: "queued"); otherwise apply immediately (status: "applied").
- Rebuild the system prompt from the stored config on the next turn.
- For
transform sections, re-invoke the existing systemMessage.transform callback into the SDK during that rebuild.
- Never mutate the stored conversation event log.
SDK surface (all six languages), mirroring the setAutoTier wrapper pattern:
| SDK |
Method |
| Node.js |
session.updateSystemMessage(config) |
| Python |
session.update_system_message(config) |
| Go |
session.UpdateSystemMessage(ctx, config) |
| .NET |
session.UpdateSystemMessageAsync(config) |
| Rust |
session.update_system_message(config) |
| Java |
session.updateSystemMessage(config) |
Each wrapper would reuse the existing extractTransformCallbacks logic to split dynamic transform sections from the static wire payload, send the payload via the generated binding, and — only on success — swaps the session's live transform-callback map so the runtime's next systemMessage.transform invocation dispatches to new callbacks.
Why This Design
Prompt assembly is owned by the runtime, which rebuilds the system prompt fresh after each turn from two independent components: the system prompt and the separately-stored conversation history. A dedicated RPC that mutates only the stored SystemMessageConfig gives us a clean guarantee: the next turn will see the new system prompt plus the existing history, unchanged.
Context Preservation Guarantee
session.systemMessage.set would mutate only the system-prompt component; it never touches stored conversation events. Nuances:
- Queued timing: an in-flight turn finishes under the old prompt; the next turn uses the new prompt with all history, including that just-completed turn.
- History is not rewritten: prior turns remain in history verbatim, generated under the old prompt. The new prompt governs generation from the next turn forward.
Implementation Notes
A full solution requires a change in two places:
- Runtime (github/copilot-cli): add the
session.systemMessage.set method + schema, implement the store/queue/rebuild/re-transform behavior, and ship it in the next CLI release.
- SDK (this repo): regenerate the RPC bindings, and add the thin handwritten per-language wrappers plus the transform-callback refresh.
This feature in the SDK is blocked until a solution in the runtime method ships, because the SDK wrappers call the generated typed facade (rpc.systemMessage.set), which only exists once the schema includes the method.
Acceptance Criteria
I can be contacted at joshbradley@microsoft.com
Overview
I'm interested in running dynamic agents that can evolve over time, where the base system prompt can change. Currently the SDK only supports defining the system prompt as a static agent (see workaround below).
Add the ability for users to update a session's base system message (the default top-level agent's system prompt) while a session is active, without stopping and resuming. The update would apply on the next user turn, queue if a turn is in flight, and preserve the full conversation history.
Problem / Motivation
Agent behavior evolves within a session, but today the system prompt is fixed once the session starts. The only way to change the agent's core instructions is to stop and resume — a heavyweight operation that disrupts a healthy, in-progress session. This forces developers to either cram every phase into one monolithic prompt or tear down sessions just to adjust behavior.
Live system prompt updates would make agents genuinely steerable: applications can adapt an agent's persona, constraints, and priorities on the fly while preserving the full conversation history, unlocking phase-aware workflows, dynamic policy enforcement, and adaptive behavior. By reusing the existing
SystemMessageConfigand applying cleanly on the next turn, it delivers this with minimal new surface area and a strong correctness guarantee — a small, well-bounded change with broad impact.Today the base system message is configurable only at session creation or resume via
SystemMessageConfig(createSession/resumeSession). There is no way to update it on a running session:session.options.update(SessionUpdateOptionsParams) exposes many configurable options — model, tools, working directory, skills, instruction sources, etc. — but has nosystemMessagefield.systemMessage.transformcallback the runtime uses to materialize customized sections fires only during session creation/resume.Temporary workaround:
Changing the agent's actual system prompt currently requires a full stop + resume. This is heavyweight (re-initialization, lifecycle event replay) for use cases that need to steer the agent's behavior mid-session — e.g. switching persona/policy after a phase transition, injecting updated org/policy instructions, or adapting tone/guidelines in response to runtime conditions — while keeping the existing conversation intact.
Proposal
Add a dedicated session-scoped JSON-RPC method, modeled after the existing
session.agent.setPrompt:session.systemMessage.setSessionSystemMessageSetParams):{ sessionId: string; systemMessage: SystemMessageConfig }— reuse the same config shape already accepted by create/resume (plaincontent, ormode: "customize"with per-sectionreplace/append/remove/prepend/preserve/transformactions).SessionSystemMessageSetResult):{ success: boolean; status?: "applied" | "queued" }.Runtime semantics:
status: "queued"); otherwise apply immediately (status: "applied").transformsections, re-invoke the existingsystemMessage.transformcallback into the SDK during that rebuild.SDK surface (all six languages), mirroring the
setAutoTierwrapper pattern:session.updateSystemMessage(config)session.update_system_message(config)session.UpdateSystemMessage(ctx, config)session.UpdateSystemMessageAsync(config)session.update_system_message(config)session.updateSystemMessage(config)Each wrapper would reuse the existing
extractTransformCallbackslogic to split dynamictransformsections from the static wire payload, send the payload via the generated binding, and — only on success — swaps the session's live transform-callback map so the runtime's nextsystemMessage.transforminvocation dispatches to new callbacks.Why This Design
Prompt assembly is owned by the runtime, which rebuilds the system prompt fresh after each turn from two independent components: the system prompt and the separately-stored conversation history. A dedicated RPC that mutates only the stored
SystemMessageConfiggives us a clean guarantee: the next turn will see the new system prompt plus the existing history, unchanged.Context Preservation Guarantee
session.systemMessage.setwould mutate only the system-prompt component; it never touches stored conversation events. Nuances:Implementation Notes
A full solution requires a change in two places:
session.systemMessage.setmethod + schema, implement the store/queue/rebuild/re-transform behavior, and ship it in the next CLI release.Acceptance Criteria
session.systemMessage.setexists in the runtime schema and is generated into all six SDKs.mode: "customize"(includingtransformsections) updates the prompt on the next turn and re-invokessystemMessage.transformwith the new callbacks.queuedwhen a turn is active,appliedotherwise.I can be contacted at joshbradley@microsoft.com