Add history.clearContext and Tool.isTerminal across all SDKs - #2129
Add history.clearContext and Tool.isTerminal across all SDKs#2129examon wants to merge 3 commits into
history.clearContext and Tool.isTerminal across all SDKs#2129Conversation
There was a problem hiding this comment.
Pull request overview
Adds Node SDK support for clearing session context and terminal tools.
Changes:
- Adds
Tool.isTerminaland forwards it during create/resume. - Adds generated
history.clearContextRPC types and client method.
Show a summary per file
| File | Description |
|---|---|
nodejs/src/types.ts |
Exposes terminal-tool configuration. |
nodejs/src/client.ts |
Serializes isTerminal in session requests. |
nodejs/src/generated/rpc.ts |
Adds context-clearing RPC support. |
Review details
- Files reviewed: 2/3 changed files
- Comments generated: 1
- Review effort level: Medium
history.clearContext and Tool.isTerminalhistory.clearContext and Tool.isTerminal across all SDKs
There was a problem hiding this comment.
Review details
Files not reviewed (4)
- go/rpc/zrpc.go: Generated file
- go/rpc/zsession_encoding.go: Generated file
- go/rpc/zsession_events.go: Generated file
- go/zsession_events.go: Generated file
Comments suppressed due to low confidence (5)
java/src/main/java/com/github/copilot/rpc/ToolDefinition.java:87
- The ergonomic annotation path cannot configure this new flag.
@CopilotToolcurrently exposes override, permission, and defer options, andCopilotToolProcessorforwards each one, but neither has anisTerminalmember. Users defining tools through the documented annotation API therefore cannot declare terminal tools; add the annotation property and processor wiring (with processor coverage).
@JsonProperty("skipPermission") Boolean skipPermission, @JsonProperty("defer") ToolDefer defer,
@JsonProperty("isTerminal") Boolean isTerminal) {
rust/src/types.rs:352
Toolis#[non_exhaustive]and its docs direct consumers to the fluent builder, but this new option has nowith_is_terminalmethod. Every adjacent runtime hint does (with_overrides_built_in_tool,with_skip_permission, andwith_deferattypes.rs:455-477), and the customDebugimplementation attypes.rs:496-511also omits this field. Please integrateis_terminalinto both APIs so consumers do not have to switch to post-construction mutation and diagnostics show the configured value.
#[serde(default, skip_serializing_if = "is_false")]
pub is_terminal: bool,
java/src/main/java/com/github/copilot/rpc/ToolDefinition.java:73
- Java currently receives only the terminal-tool half of this cross-SDK feature. Unlike Node, Python, Go, Rust, and .NET in this diff,
SessionHistoryApistill has noclearContextmethod andSessionEventhas no typedsession.context_clearedevent, so Java consumers cannot implement the context-clearing example. Regenerate the Java RPC/event surface from the updated runtime schemas as well.
This issue also appears on line 86 of the same file.
* @param isTerminal
* when {@code true}, a successful call to this tool ends the agent
* turn: the runtime's tool phase halts instead of feeding the result
* back to the model for another round; {@code null} or {@code false}
* leaves the turn running
python/copilot/tools.py:129
- The public
define_toolsignature now acceptsis_terminal, but itsArgssection documents every option except this one. Add its turn-ending semantics and default to the docstring so decorator and function-style users can discover the option throughhelp()and generated API documentation.
is_terminal: bool = False,
go/types.go:1185
- The PR description says this is “Two small additions to the Node SDK,” lists only three Node files, and reports only npm validation, but the actual change adds public/generated surfaces across Rust, Python, Java, Go, and .NET as well. Please either scope the diff back to Node or update the description and validation evidence for every affected SDK so the review and release impact are accurate.
// IsTerminal reports that a successful call to this tool ends the agent
// turn: the runtime halts instead of feeding the result back to the model
// for another round. A failed call leaves the loop running so the model can
// read the error and retry.
IsTerminal bool `json:"isTerminal,omitempty"`
- Files reviewed: 11/24 changed files
- Comments generated: 0 new
- Review effort level: Medium
There was a problem hiding this comment.
Review details
Files not reviewed (4)
- go/rpc/zrpc.go: Generated file
- go/rpc/zsession_encoding.go: Generated file
- go/rpc/zsession_events.go: Generated file
- go/zsession_events.go: Generated file
Comments suppressed due to low confidence (3)
nodejs/src/client.ts:1394
- The new create/resume forwarding is untested even though
nodejs/test/client.test.tshas paired request-payload tests for the adjacentoverridesBuiltInToolanddeferflags. Add equivalent create and resume assertions forisTerminalso a missing serialization path cannot regress unnoticed.
isTerminal: tool.isTerminal,
python/copilot/client.py:1877
- There is no test covering this new wire serialization, while
python/test_client.pyalready verifies both create and resume forwarding for the neighboring override/defer flags. Add correspondingis_terminal=Truetests for both paths (and omission at the default) to protect the advertised behavior.
if tool.is_terminal:
definition["isTerminal"] = True
java/src/main/java/com/github/copilot/rpc/ToolDefinition.java:87
- The Java portion still does not expose
history.clearContextor the typedsession.context_clearedevent promised for every SDK.SessionHistoryApicurrently ends withsummarizeForHandoff, andSessionEventhas no context-cleared subtype, so Java consumers cannot use either new generated surface. Please regenerate and commit the Java RPC request/result/API and session-event classes as well.
@JsonProperty("skipPermission") Boolean skipPermission, @JsonProperty("defer") ToolDefer defer,
@JsonProperty("isTerminal") Boolean isTerminal) {
- Files reviewed: 12/25 changed files
- Comments generated: 0 new
- Review effort level: Medium
There was a problem hiding this comment.
Review details
Files not reviewed (4)
- go/rpc/zrpc.go: Generated file
- go/rpc/zsession_encoding.go: Generated file
- go/rpc/zsession_events.go: Generated file
- go/zsession_events.go: Generated file
Comments suppressed due to low confidence (2)
java/src/main/java/com/github/copilot/rpc/ToolDefinition.java:87
- Java's annotated-tool surface cannot set this new flag.
@CopilotToolexposes the other tool flags (overridesBuiltInTool,skipPermission, anddefer), but the annotation and processor still emit only the seven-argument constructor, so every annotation-defined tool getsisTerminal = null. Please add anisTerminalannotation member and carry it throughCopilotToolProcessor, with processor coverage, so the feature is available through the SDK's ergonomic tool API rather than only direct record construction.
@JsonProperty("skipPermission") Boolean skipPermission, @JsonProperty("defer") ToolDefer defer,
@JsonProperty("isTerminal") Boolean isTerminal) {
python/copilot/tools.py:129
is_terminalis a new public argument but is missing from the function'sArgsdocumentation, while every other option is documented there. Add its successful-call/failed-call behavior to the docstring so users can discover the flag without reading the dataclass source.
is_terminal: bool = False,
- Files reviewed: 12/30 changed files
- Comments generated: 0 new
- Review effort level: Medium
There was a problem hiding this comment.
Review details
Files not reviewed (4)
- go/rpc/zrpc.go: Generated file
- go/rpc/zsession_encoding.go: Generated file
- go/rpc/zsession_events.go: Generated file
- go/zsession_events.go: Generated file
Comments suppressed due to low confidence (3)
java/src/main/java/com/github/copilot/rpc/ToolDefinition.java:95
- The Java surface still lacks the other half of this PR:
SessionHistoryApihas noclearContextmethod/request/result types, and there is no typedsession.context_clearedevent. As a result, Java consumers cannot use the capability that the PR promises for every SDK. Please regenerate and commit the Java RPC and event sources from the updated schema.
@JsonProperty("metadata") Map<String, Object> metadata, @JsonProperty("isTerminal") Boolean isTerminal) {
python/copilot/client.py:2250
- Please add a unit test that exercises
is_terminalthroughdefine_tooland verifies bothsession.createandsession.resumepayloads, including omission when false. The adjacent metadata test covers this same serialization path, but these new branches and helper propagation currently have no Python coverage.
if tool.is_terminal:
definition["isTerminal"] = True
dotnet/src/Client.cs:2790
- The added tests only verify the intermediate
AIFunction.AdditionalProperties; they do not exercise this conversion or the serialized session request. Please add coverage asserting thatisTerminalreaches bothsession.createandsession.resumepayloads (and is omitted by default), otherwise the actual wire path can regress while the current tests still pass.
var isTerminal = function.AdditionalProperties.TryGetValue(CopilotTool.IsTerminalKey, out var terminalVal) && terminalVal is true;
return new ToolDefinition(function.Name, function.Description, function.JsonSchema,
overrides ? true : null,
skipPerm ? true : null,
defer,
metadata,
isTerminal ? true : null);
- Files reviewed: 13/26 changed files
- Comments generated: 0 new
- Review effort level: Medium
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Regenerates the RPC clients for the new `session.history.clearContext` method and the `session.context_cleared` event, and adds a hand-authored `isTerminal` tool flag to every language surface. `isTerminal` lets a tool declare that a successful call ends the agent turn: the runtime's tool phase halts instead of feeding the result back to the model for another round. A failed call leaves the loop running so the model can read the error and retry. Without it a turn-ending tool can only approximate the behavior by returning a rejected result, which halts the loop but is semantically wrong. Per language: - Node.js: `Tool.isTerminal`, `defineTool` config, both session-config serialization sites. - Go: `Tool.IsTerminal` with `json:"isTerminal,omitempty"`. - Python: `Tool.is_terminal`, `define_tool` overloads, both client serialization sites. - Rust: `Tool::is_terminal`, skipped when false. - Java: `ToolDefinition.isTerminal` as a record component, plus a seven-argument convenience constructor so existing call sites keep compiling. - .NET: `CopilotToolOptions.IsTerminal`, the `is_terminal` additional-property key, and the wire `ToolDefinition`. Adds serialization tests in Go, Rust and Java covering both the camelCase wire name and omission when unset; the Java test also pins the seven-argument constructor so the record change stays source-compatible.
…ests Resolves the rebase onto main where both sides added an eighth tool option: main added metadata and this branch added isTerminal. - Java ToolDefinition now carries metadata and isTerminal as separate record components, keeps the seven- and eight-argument convenience constructors, and threads isTerminal through every fluent copy method so it is no longer dropped by .metadata()/.defer()/etc. - Adds ToolDefinition.isTerminal(boolean) so lambda-defined tools can set it, matching the other flags. - Adds the missing Node regression tests asserting isTerminal is forwarded on both session.create and session.resume, and omitted when unset. - Applies the repo rust formatter to the new is_terminal test.
Auto-committed by java-codegen-check workflow.
There was a problem hiding this comment.
Review details
Files not reviewed (4)
- go/rpc/zrpc.go: Generated file
- go/rpc/zsession_encoding.go: Generated file
- go/rpc/zsession_events.go: Generated file
- go/zsession_events.go: Generated file
Comments suppressed due to low confidence (2)
dotnet/src/Client.cs:2790
- The added tests stop at the
AIFunction.AdditionalPropertiesbag, so they do not exercise this conversion or the serializedsession.create/session.resumepayload. This new bridge is whereis_terminalbecomes wire-levelisTerminal; add a public-API client test using the existing fake server pattern inClientSessionLifetimeTeststhat asserts both requests containtools[0].isTerminal == trueand that the property is omitted when unset.
var isTerminal = function.AdditionalProperties.TryGetValue(CopilotTool.IsTerminalKey, out var terminalVal) && terminalVal is true;
return new ToolDefinition(function.Name, function.Description, function.JsonSchema,
overrides ? true : null,
skipPerm ? true : null,
defer,
metadata,
isTerminal ? true : null);
python/copilot/client.py:2250
- Add Python regression coverage for this new wire mapping.
python/test_client.py:574-609already verifies the analogousmetadataoption on both create and resume, but no test currently exercisesis_terminal; a future edit could silently drop either branch or emit the wrong camel-case key. CoverTrueon both paths and omission for the defaultFalse.
if tool.is_terminal:
definition["isTerminal"] = True
- Files reviewed: 13/26 changed files
- Comments generated: 1
- Review effort level: Medium
| @JsonProperty("overridesBuiltInTool") Boolean overridesBuiltInTool, | ||
| @JsonProperty("skipPermission") Boolean skipPermission, @JsonProperty("defer") ToolDefer defer, | ||
| @JsonProperty("metadata") Map<String, Object> metadata) { | ||
| @JsonProperty("metadata") Map<String, Object> metadata, @JsonProperty("isTerminal") Boolean isTerminal) { |
Cross-SDK Consistency ReviewPR: Add
|
| SDK | Field | Serialization |
|---|---|---|
| Node.js | Tool.isTerminal?: boolean |
isTerminal (omitted when unset) |
| Python | Tool.is_terminal: bool = False |
"isTerminal": True (omitted when false) |
| Go | Tool.IsTerminal bool |
json:"isTerminal,omitempty" |
| .NET | CopilotToolOptions.IsTerminal bool |
"is_terminal": true additional property |
| Java | ToolDefinition.isTerminal record component |
@JsonProperty("isTerminal") |
| Rust | Tool.is_terminal: bool |
#[serde(skip_serializing_if = "is_false")] |
Tests confirm the flag serializes correctly when set and is omitted when unset in all SDKs. ✅
history.clearContext + session.context_cleared — ⚠️ Java generated code missing (acknowledged)
The session.history.clearContext RPC method and session.context_cleared event are present in generated code for 5 of 6 SDKs (Node.js, Python, Go, .NET, Rust), but Java's generated code (java/src/generated/) does not yet include:
SessionHistoryClearContextParams/SessionHistoryClearContextResulttypes- A
clearContext()method onSessionHistoryApi - A
SessionContextClearedEventclass
This matches the known blocker documented in the PR description: the codegen reads schemas from the installed @github/copilot* package, which doesn't yet include history.clearContext or session.context_cleared. The Java generated files will be regenerated once the companion runtime change ships.
No action required — this gap is intentional and tracked. Once the schema is available, running mvn generate-sources -Pcodegen should bring Java into parity automatically.
Summary
isTerminal: Fully consistent across all 6 SDKs ✅history.clearContext/session.context_cleared: 5/6 SDKs (Java pending codegen blocker, acknowledged)⚠️
No unexpected consistency issues found.
Generated by SDK Consistency Review Agent for #2129 · sonnet46 62.9 AIC · ⌖ 5.55 AIC · ⊞ 6.6K · ◷
Overview
What
Adds two things to every SDK language surface:
history.clearContexton the generated session RPC client — clears the conversation (keeping system and developer messages) and optionally seeds the fresh context window. Also picks up the newsession.context_clearedevent.isTerminalon the tool definition — lets a tool declare that a successful call ends the agent turn instead of the result being fed back to the model for another round. A failed call leaves the loop running so the model can read the error and retry.Why
Together these let a context-clearing — or handoff, or any turn-ending — tool be implemented by an SDK consumer or extension, instead of requiring one built into the runtime.
Without
isTerminalsuch a tool can only approximate turn-ending by returning a rejected result, which halts the loop but is semantically wrong and surfaces as a user rejection. WithoutclearContextthe capability has no API surface at all.Per-language changes
Generated RPC/event types are regenerated for all six languages.
isTerminalis hand-authored per language, matching howoverridesBuiltInTool/skipPermission/deferare already carried:Tool.isTerminal,defineToolconfigcreateSession/resumeSessionsites inclient.tsTool.IsTerminaljson:"isTerminal,omitempty"Tool.is_terminal,define_tooloverloadsTool::is_terminalfalseToolDefinition.isTerminalrecord component@JsonProperty("isTerminal")CopilotToolOptions.IsTerminal,is_terminaladditional-property keyToolDefinitionCoexistence with
Tool.metadata.metadatalanded onmainwhile this branch was open, and it touched exactly the same tool-option surfaces. The branch is rebased on top of it and the two are independent, additive options everywhere:Tool.metadata+Tool.isTerminal(Node),metadata+is_terminal(Python),Metadata+IsTerminal(.NET), and so on.Java source compatibility.
ToolDefinitionis a record, so adding a component changes the canonical constructor. The canonical form is now nine components (…, defer, metadata, isTerminal), with two convenience constructors that delegate for the older shapes:…, defer) →metadata = null, isTerminal = null…, defer, metadata) →isTerminal = nullso existing call sites — including annotation-processor output — keep compiling unchanged. Tests pin both.
Every fluent copy method (
overridesBuiltInTool,skipPermission,defer,metadata) threadsisTerminalthrough, so it is not silently dropped when another option is set afterwards, and a matchingisTerminal(boolean)copy method is added for tools built via thefrom(...)factories.resumeSessionis the path extensions join on viajoinSession, so it matters that both serialization sites carry the flag, not just session creation.Tests
client.test.ts—isTerminalforwarded on bothsession.createandsession.resume, and omitted when unsetTestIsTerminal— camelCase wire name when set, omitted when falseis_terminal_tests— same two cases viaserde_jsonToolDefinitionIsTerminalTest— both cases plus the older-arity constructor compatibility guardCopilotToolTests—is_terminaladditional property set when requested, omitted otherwiseValidation
All six SDK test matrices pass in CI across Linux, macOS and Windows, as do every
Validate *and CodeQLAnalyze *job. The .NET tests noted as uncompiled in an earlier revision of this description have since been built and run by CI on all three platforms.Locally:
go build/vet/test,cargo test --lib(193 incl. the 2 new),tsc --noEmit+ the new Vitest cases,mvn test(155 tool tests, JDK 25),ruff check/format, anddotnet build.Node.js
test/e2e/*and Gointernal/e2eneed a Copilot CLI binary that is unavailable locally; they are covered by CI.Known blocker
Codegen Check/ "Verify generated files are up-to-date" fails, and this is expected until the companion runtime change ships.The generator reads its schemas from the installed
@github/copilot*package. No published CLI defineshistory.clearContext,session.context_clearedorTool.isTerminalyet (checked through1.0.76-5), so runningcd scripts/codegen && npm run generateagainst the pinned CLI strips the generated half of this PR. The Java codegen workflow auto-commits the same deletion.Once the runtime change lands and a CLI carrying the new schemas is pinned, the generated files should be regenerated normally and the result is expected to be identical to what is committed here.
Notes
gofmt-ed and Javaspotless:apply-ed, so the generated diffs are additive rather than reformatting noise.Depends on github/copilot-agent-runtime#14002, which adds the runtime surface.