Skip to content

Add history.clearContext and Tool.isTerminal across all SDKs - #2129

Open
examon wants to merge 3 commits into
mainfrom
clearcontext-rpc
Open

Add history.clearContext and Tool.isTerminal across all SDKs#2129
examon wants to merge 3 commits into
mainfrom
clearcontext-rpc

Conversation

@examon

@examon examon commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Overview

What

Adds two things to every SDK language surface:

  1. history.clearContext on the generated session RPC client — clears the conversation (keeping system and developer messages) and optionally seeds the fresh context window. Also picks up the new session.context_cleared event.
  2. isTerminal on 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.
const session = await joinSession({
    tools: [{
        name: "clear_context",
        isTerminal: true,
        defer: "never",
        parameters: { type: "object", properties: { prompt: { type: "string" } } },
        handler: async ({ prompt }) => {
            const { messagesCleared } = await session.rpc.history.clearContext({ prompt });
            return { textResultForLlm: `Cleared ${messagesCleared} message(s).`, resultType: "success" };
        },
    }],
});

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 isTerminal such 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. Without clearContext the capability has no API surface at all.

Per-language changes

Generated RPC/event types are regenerated for all six languages. isTerminal is hand-authored per language, matching how overridesBuiltInTool / skipPermission / defer are already carried:

Language Tool flag Serialization
Node.js Tool.isTerminal, defineTool config both createSession / resumeSession sites in client.ts
Go Tool.IsTerminal json:"isTerminal,omitempty"
Python Tool.is_terminal, define_tool overloads both client serialization sites
Rust Tool::is_terminal skipped when false
Java ToolDefinition.isTerminal record component @JsonProperty("isTerminal")
.NET CopilotToolOptions.IsTerminal, is_terminal additional-property key wire ToolDefinition

Coexistence with Tool.metadata. metadata landed on main while 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. ToolDefinition is 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:

  • seven arguments (…, defer) → metadata = null, isTerminal = null
  • eight arguments (…, defer, metadata) → isTerminal = null

so existing call sites — including annotation-processor output — keep compiling unchanged. Tests pin both.

Every fluent copy method (overridesBuiltInTool, skipPermission, defer, metadata) threads isTerminal through, so it is not silently dropped when another option is set afterwards, and a matching isTerminal(boolean) copy method is added for tools built via the from(...) factories.

resumeSession is the path extensions join on via joinSession, so it matters that both serialization sites carry the flag, not just session creation.

Tests

Language Test
Node.js client.test.tsisTerminal forwarded on both session.create and session.resume, and omitted when unset
Go TestIsTerminal — camelCase wire name when set, omitted when false
Rust is_terminal_tests — same two cases via serde_json
Java ToolDefinitionIsTerminalTest — both cases plus the older-arity constructor compatibility guard
.NET CopilotToolTestsis_terminal additional property set when requested, omitted otherwise

Validation

All six SDK test matrices pass in CI across Linux, macOS and Windows, as do every Validate * and CodeQL Analyze * 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, and dotnet build.

Node.js test/e2e/* and Go internal/e2e need 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 defines history.clearContext, session.context_cleared or Tool.isTerminal yet (checked through 1.0.76-5), so running cd scripts/codegen && npm run generate against 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

  • Purely additive; every new field is optional and absent means today's behavior.
  • Generated files were produced by the repo's own codegen against the runtime schema carrying the companion change, spliced into the currently pinned schema so the diff stays scoped to this change.
  • Go output was gofmt-ed and Java spotless:apply-ed, so the generated diffs are additive rather than reformatting noise.

Depends on github/copilot-agent-runtime#14002, which adds the runtime surface.

Copilot AI review requested due to automatic review settings July 29, 2026 17:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Node SDK support for clearing session context and terminal tools.

Changes:

  • Adds Tool.isTerminal and forwards it during create/resume.
  • Adds generated history.clearContext RPC 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

Comment thread nodejs/src/types.ts
Copilot AI review requested due to automatic review settings July 29, 2026 19:47
@examon
examon force-pushed the clearcontext-rpc branch from 86d300b to 06dc43a Compare July 29, 2026 19:47
@examon examon changed the title Add history.clearContext and Tool.isTerminal Add history.clearContext and Tool.isTerminal across all SDKs Jul 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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. @CopilotTool currently exposes override, permission, and defer options, and CopilotToolProcessor forwards each one, but neither has an isTerminal member. 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

  • Tool is #[non_exhaustive] and its docs direct consumers to the fluent builder, but this new option has no with_is_terminal method. Every adjacent runtime hint does (with_overrides_built_in_tool, with_skip_permission, and with_defer at types.rs:455-477), and the custom Debug implementation at types.rs:496-511 also omits this field. Please integrate is_terminal into 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, SessionHistoryApi still has no clearContext method and SessionEvent has no typed session.context_cleared event, 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_tool signature now accepts is_terminal, but its Args section 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 through help() 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

Copilot AI review requested due to automatic review settings July 29, 2026 20:09
@examon
examon force-pushed the clearcontext-rpc branch from 06dc43a to 11426a7 Compare July 29, 2026 20:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.ts has paired request-payload tests for the adjacent overridesBuiltInTool and defer flags. Add equivalent create and resume assertions for isTerminal so 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.py already verifies both create and resume forwarding for the neighboring override/defer flags. Add corresponding is_terminal=True tests 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.clearContext or the typed session.context_cleared event promised for every SDK. SessionHistoryApi currently ends with summarizeForHandoff, and SessionEvent has 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

@examon
examon force-pushed the clearcontext-rpc branch from 11426a7 to 0b328ff Compare July 29, 2026 20:36
Copilot AI review requested due to automatic review settings July 29, 2026 20:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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. @CopilotTool exposes the other tool flags (overridesBuiltInTool, skipPermission, and defer), but the annotation and processor still emit only the seven-argument constructor, so every annotation-defined tool gets isTerminal = null. Please add an isTerminal annotation member and carry it through CopilotToolProcessor, 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_terminal is a new public argument but is missing from the function's Args documentation, 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

@examon
examon marked this pull request as ready for review July 29, 2026 20:46
@examon
examon requested a review from a team as a code owner July 29, 2026 20:46
@examon
examon force-pushed the clearcontext-rpc branch from 0b328ff to 55cebf9 Compare July 29, 2026 21:21
Copilot AI review requested due to automatic review settings July 29, 2026 21:21
@github-actions github-actions Bot added the dependencies Pull requests that update a dependency file label Jul 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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: SessionHistoryApi has no clearContext method/request/result types, and there is no typed session.context_cleared event. 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_terminal through define_tool and verifies both session.create and session.resume payloads, 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 that isTerminal reaches both session.create and session.resume payloads (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

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

examon and others added 3 commits July 30, 2026 04:46
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.
@examon
examon force-pushed the clearcontext-rpc branch from fccbbc5 to e25257e Compare July 30, 2026 04:51
Copilot AI review requested due to automatic review settings July 30, 2026 04:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.AdditionalProperties bag, so they do not exercise this conversion or the serialized session.create/session.resume payload. This new bridge is where is_terminal becomes wire-level isTerminal; add a public-API client test using the existing fake server pattern in ClientSessionLifetimeTests that asserts both requests contain tools[0].isTerminal == true and 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-609 already verifies the analogous metadata option on both create and resume, but no test currently exercises is_terminal; a future edit could silently drop either branch or emit the wrong camel-case key. Cover True on both paths and omission for the default False.
                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) {
@github-actions

Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review

PR: Add history.clearContext and Tool.isTerminal across all SDKs

Tool.isTerminal — ✅ Consistent across all 6 SDKs

All six SDK implementations correctly add the isTerminal flag to the tool definition with consistent semantics and wire name ("isTerminal"):

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 / SessionHistoryClearContextResult types
  • A clearContext() method on SessionHistoryApi
  • A SessionContextClearedEvent class

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 ·

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants