Publish the TypeScript SDK and integration guides (#3141)

* feat(client): publish the TypeScript SDK surface

Make the high-level client API usable for external integrations and document complete agent, workspace, provider, event, and recipe flows.

* fix(client): correlate provider snapshots by daemon cwd

Return the daemon's canonical snapshot identity so waitForReady can match updates for relative, tilde, and otherwise non-canonical paths without duplicating filesystem rules in the SDK.

* test(server): make snapshot cwd assertion portable

* fix(client): gate canonical provider snapshot waits

Older daemons do not expose the canonical snapshot identity needed to correlate workspace updates safely. Require the advertised capability and fail with an update-host message instead of guessing across workspaces.
This commit is contained in:
Mohamed Boudra
2026-08-10 13:22:20 +02:00
committed by GitHub
parent 05a0cabc07
commit 7954babca6
24 changed files with 1956 additions and 197 deletions
@@ -116,6 +116,23 @@ describe("ProviderCatalogSession", () => {
expect(pull?.payload.entries).toEqual(push?.payload.entries);
});
it("returns the canonical cwd used by snapshot updates", async () => {
const getSnapshot = vi.fn((_cwd?: string) => makeEntries());
const { subsystem, emitted } = makeSubsystem({
snapshot: { getSnapshot },
});
await subsystem.handleGetProvidersSnapshotRequest({
type: "get_providers_snapshot_request",
requestId: "canonical-cwd",
cwd: "/repo/./sdk",
});
const canonicalCwd = getSnapshot.mock.calls[0]?.[0];
expect(canonicalCwd).toEqual(expect.any(String));
expect(findByType(emitted, "get_providers_snapshot_response")?.payload.cwd).toBe(canonicalCwd);
});
it("pushes the compact encoding to capable clients", () => {
const { subsystem, emitted, pushSnapshotChange } = makeSubsystem({
supportsCustomModeIcons: true,
@@ -8,6 +8,7 @@ import {
import type { SessionInboundMessage, SessionOutboundMessage } from "../../messages.js";
import {
isGlobalProviderSnapshotKey,
resolveSnapshotCwd,
type ProviderSnapshotManager,
} from "../../agent/provider-snapshot-manager.js";
import {
@@ -391,8 +392,9 @@ export class ProviderCatalogSession {
msg: Extract<SessionInboundMessage, { type: "get_providers_snapshot_request" }>,
): Promise<void> {
// COMPAT(providersSnapshot): keep legacy provider-list RPCs alongside snapshot flow.
const snapshotCwd = msg.cwd?.trim() ? resolveSnapshotCwd(expandTilde(msg.cwd)) : undefined;
const entries = this.providerSnapshotManager
.getSnapshot(msg.cwd ? expandTilde(msg.cwd) : undefined)
.getSnapshot(snapshotCwd)
.filter((entry) => this.host.isProviderVisibleToClient(entry.provider));
const clientEntries = this.downgradeEntryModesForClient(entries);
@@ -402,6 +404,7 @@ export class ProviderCatalogSession {
this.host.emit({
type: "get_providers_snapshot_response",
payload: {
...(snapshotCwd ? { cwd: snapshotCwd } : {}),
entries: [],
...(!notModified ? { compactSnapshot: encoded.compactSnapshot } : {}),
snapshotHash: encoded.snapshotHash,
@@ -416,6 +419,7 @@ export class ProviderCatalogSession {
this.host.emit({
type: "get_providers_snapshot_response",
payload: {
...(snapshotCwd ? { cwd: snapshotCwd } : {}),
entries: clientEntries,
generatedAt: new Date().toISOString(),
requestId: msg.requestId,
@@ -933,6 +933,7 @@ describe("relay external socket reconnect behavior", () => {
expect(serverInfo.features?.stableProjectIdentity).toBe(true);
expect(serverInfo.features?.canonicalSubmittedPrompts).toBe(true);
expect(serverInfo.features?.providersSnapshotCwd).toBe(true);
expect(serverInfo.features?.["terminal-input-mode-replay"]).toBe(true);
expect(serverInfo.features?.["terminal-size-ownership"]).toBe(true);
expect(serverInfo.features?.agentTurnIdentity).toBeUndefined();
@@ -1526,6 +1526,8 @@ export class VoiceAssistantWebSocketServer {
features: {
// COMPAT(providersSnapshot): keep optional until all clients rely on snapshot flow.
providersSnapshot: true,
// COMPAT(providersSnapshotCwd): added in v0.3.2, remove gate after 2027-02-10.
providersSnapshotCwd: true,
// COMPAT(checkoutForgeSetAutoMerge): added in v0.1.106, remove old
// checkoutGithubSetAutoMerge fallback after 2026-12-28.
checkoutForgeSetAutoMerge: true,