Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,34 @@ See [GitHub Releases](https://github.com/github/copilot-sdk/releases) for the fu

## [Unreleased]

### Feature: structured Policy Helper failures and scoped recheck

Node and Rust managed-settings snapshots now expose bounded structured Policy Helper failures, including actionable messages, remediation, details, explicit help links, and secondary diagnostics. The same DTO is preserved in applicable `session.create`, `session.resume`, and policy-dependent activation RPC error data without replacing the operation's primary code or message.

Use `policyHelperCapabilities` to detect support. Older runtimes omit the capability object; the absence of `policyHelperFailure` alone does not prove recovery or support. When `recheck` is true, the new session-scoped RPC performs a fresh helper evaluation for the current policy context.

```ts
const snapshot = await session.rpc.managedSettings.get();
if (snapshot.policyHelperCapabilities?.recheck) {
await session.rpc.managedSettings.recheckPolicyHelper();
}
```

```rust
let snapshot = session.rpc().managed_settings().get().await?;
if snapshot
.policy_helper_capabilities
.as_ref()
.is_some_and(|capabilities| capabilities.recheck)
{
session
.rpc()
.managed_settings()
.recheck_policy_helper()
.await?;
}
```

## [v1.0.14](https://github.com/github/copilot-sdk/releases/tag/v1.0.14) (2026-09-16)

### Feature: typed message provenance for user, system, and agent sources
Expand Down
27 changes: 27 additions & 0 deletions nodejs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,33 @@ security checks, user permissions, or sandbox policy. `permissions.limitTo` neit
enables the sandbox nor changes `sandbox.userPolicy.network`; configure sandbox
network restrictions explicitly.

### Policy Helper failures and scoped recheck

Managed-settings snapshots may include a structured `policyHelperFailure` and
the explicit `policyHelperCapabilities` supported by the runtime. Treat helper
text as untrusted plain text, keep `diagnostic` secondary, and sanitize an
explicit `helpLink` again before navigation.

```typescript
const snapshot = await session.rpc.managedSettings.get();

if (snapshot.policyHelperFailure) {
console.warn(snapshot.policyHelperFailure.message);
}

if (snapshot.policyHelperCapabilities?.recheck === true) {
const refreshed = await session.rpc.managedSettings.recheckPolicyHelper();
// Only a current capable snapshot without policyHelperFailure establishes recovery.
console.log(refreshed.policyHelperFailure ?? "Policy Helper recovered");
}
```

Older runtimes omit `policyHelperCapabilities`. Do not infer support or recovery
only because `policyHelperFailure` is absent. Applicable `session.create`,
`session.resume`, and policy-dependent activation failures preserve the same DTO
under JSON-RPC `error.data.policyHelperFailure` while retaining the primary
operation error code and message.

### Permission Result Kinds

The handler must return one of the `PermissionDecision` shapes (or `{ kind: "no-result" }`). Approval scopes are present-tense — they describe the decision to apply, not the outcome reported back on session events:
Expand Down
18 changes: 17 additions & 1 deletion nodejs/src/generated/rpc.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

91 changes: 91 additions & 0 deletions nodejs/src/generated/session-events.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions nodejs/test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { join, resolve } from "node:path";
import { describe, expect, it, onTestFinished, vi } from "vitest";
import {
createMessageConnection,
ResponseError,
StreamMessageReader,
StreamMessageWriter,
type MessageConnection,
Expand All @@ -25,6 +26,7 @@ import {
type GitHubTelemetryNotification,
type ManagedSettings,
type ModelInfo,
type PolicyHelperFailure,
type SubagentStartHookOutput,
type SubagentStopHookOutput,
type SessionFsProvider,
Expand Down Expand Up @@ -5478,6 +5480,52 @@ connection.listen();
});
});

describe("RPC error data preservation", () => {
it.each(["session.create", "session.resume"] as const)(
"preserves structured data from %s",
async (method) => {
const policyHelperFailure = {
authority: "future-authority",
category: "future-category",
code: "helper.future-code",
failureMode: "future-mode",
message: "The Policy Helper rejected the current configuration.",
previousPolicyRetained: true,
reason: "future-reason",
severity: "future-severity",
} satisfies PolicyHelperFailure;
const data = { policyHelperFailure };
const responseError = new ResponseError(-32001, "session operation failed", data);
const client = new CopilotClient({ autoStart: false });
(client as any).connection = {
sendRequest: vi.fn(async (requestedMethod: string) => {
if (requestedMethod !== method) {
throw new Error(`Unexpected method: ${requestedMethod}`);
}
throw responseError;
}),
};

const operation =
method === "session.create"
? client.createSession({ onPermissionRequest: approveAll })
: client.resumeSession("structured-error-session", {
onPermissionRequest: approveAll,
});
const error = await operation.then(
() => undefined,
(reason: unknown) => reason
);

expect(error).toBe(responseError);
expect(responseError.code).toBe(-32001);
expect(responseError.message).toBe("session operation failed");
expect(responseError.data).toBe(data);
expect(responseError.data.policyHelperFailure).toBe(policyHelperFailure);
}
);
});

describe("managedSettings serialization", () => {
async function captureCreateParams(config: Record<string, unknown>): Promise<any> {
const client = new CopilotClient();
Expand Down
Loading
Loading