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
+6 -3
View File
@@ -6,13 +6,16 @@ These examples use only the public SDK root:
import { createPaseoClient, type PaseoClient } from "@getpaseo/client";
```
Pass the daemon WebSocket URL into the exported functions. In worktree dev, read it
Each example takes the daemon WebSocket URL as an argument. In worktree dev, read it
from the portless banner or `portless get daemon`; for the desktop-managed daemon,
use the URL for that daemon.
- `workspaces.ts` covers creating, opening, refetching, and archiving a workspace.
- `agents-and-providers.ts` covers creating agents and choosing providers with `client.providers.*`.
- `quickstart.ts` runs one agent and prints its reply. It is a standalone script with the URL at the top; the rest export functions.
- `workspaces.ts` covers creating a fresh workspace, opening by directory, refreshing, and archiving.
- `agents-and-providers.ts` covers provider discovery, creating agents, and waiting for turns.
- `events-and-timeline.ts` covers subscribing to workspace, agent, and timeline events, plus refetching a timeline page.
- `issue-to-agent.ts` turns an issue record into a visible Paseo workspace and agent.
- `parallel-review.ts` launches several reviewers concurrently and cleans them up.
- `provider-settings.ts` covers provider settings that are currently daemon config-backed.
Provider profiles, provider env vars, custom binaries, and additional models are still raw daemon config behavior. The SDK exposes them through `client.config.get()` and `client.config.patch()` until first-class provider settings RPCs exist.
@@ -14,13 +14,11 @@ export async function createCodexAgent(url: string, cwd: string): Promise<string
const agent = await client.agents.create({
config: {
...client.providers.codex({
model: "gpt-5.2",
modeId: "full-auto",
}),
cwd,
provider: "codex/gpt-5.5",
modeId: "full-access",
},
initialPrompt: "Inspect this repository and summarize the next useful task.",
cwd,
prompt: "Inspect this repository and summarize the next useful task.",
});
return agent.id;
@@ -35,18 +33,18 @@ export async function chooseProviderFromSnapshot(url: string, cwd: string): Prom
try {
await client.connect();
const snapshot = await client.providers.snapshot({ cwd });
const snapshot = await client.providers.waitForReady({ cwd });
const readyProvider = snapshot.entries.find((provider) => provider.status === "ready");
const providerConfig = readyProvider
? client.providers.config(readyProvider.provider)
: client.providers.claude();
const model =
readyProvider?.models?.find((candidate) => candidate.isDefault) ?? readyProvider?.models?.[0];
if (!readyProvider || !model) throw new Error("No provider model is ready");
const agent = await client.agents.create({
config: {
...providerConfig,
cwd,
provider: `${readyProvider.provider}/${model.id}`,
},
initialPrompt: "Start with a quick repository map.",
cwd,
prompt: "Start with a quick repository map.",
});
return agent.id;
@@ -54,3 +52,16 @@ export async function chooseProviderFromSnapshot(url: string, cwd: string): Prom
await client.close();
}
}
export async function runFollowUp(url: string, agentId: string): Promise<string | null> {
const client = createClient(url);
try {
await client.connect();
const result = await client.agents.ref(agentId).run("Summarize your progress and next step.");
if (result.status !== "idle") throw new Error(result.error ?? result.status);
return result.lastMessage;
} finally {
await client.close();
}
}
@@ -0,0 +1,35 @@
import { createPaseoClient, type PaseoClient } from "@getpaseo/client";
interface Issue {
id: string;
title: string;
description: string;
repositoryPath: string;
}
export async function startIssue(client: PaseoClient, issue: Issue) {
const workspace = await client.workspaces.open(issue.repositoryPath);
const agent = await workspace.agents.create({
config: {
provider: "codex/gpt-5.5",
},
title: issue.title,
labels: {
"issue-provider": "my-tracker",
"issue-id": issue.id,
},
prompt: [
`Implement issue ${issue.id}: ${issue.title}`,
"",
issue.description,
"",
"Run focused tests and summarize the result.",
].join("\n"),
});
return { workspaceId: workspace.id, agentId: agent.id };
}
export function createClient(url: string): PaseoClient {
return createPaseoClient({ url });
}
@@ -0,0 +1,37 @@
import type { PaseoAgentHandle, PaseoClient } from "@getpaseo/client";
export async function reviewInParallel(client: PaseoClient, cwd: string): Promise<string[]> {
const agents: PaseoAgentHandle[] = [];
try {
agents.push(
...(await Promise.all([
client.agents.create({
config: {
provider: "codex/gpt-5.5",
},
cwd,
title: "Correctness review",
prompt: "Review the current diff for correctness and missed edge cases.",
}),
client.agents.create({
config: {
provider: "claude/claude-sonnet-5",
},
cwd,
title: "Security review",
prompt: "Review the current diff for security and unsafe input handling.",
}),
])),
);
const results = await Promise.all(agents.map((agent) => agent.waitForFinish()));
return results.map((result) => {
if (result.status !== "idle") throw new Error(result.error ?? result.status);
return result.lastMessage ?? "";
});
} finally {
await Promise.allSettled(agents.map((agent) => agent.archive()));
}
}
+24
View File
@@ -0,0 +1,24 @@
import { createPaseoClient } from "@getpaseo/client";
const client = createPaseoClient({ url: "ws://127.0.0.1:6767/ws" });
await client.connect();
try {
const agent = await client.agents.create({
config: {
provider: "codex/gpt-5.5",
},
cwd: process.cwd(),
prompt: "Inspect this repository and name the next useful task.",
});
const result = await agent.waitForFinish();
if (result.status !== "idle") {
throw new Error(result.error ?? `Agent stopped with status: ${result.status}`);
}
console.log(result.lastMessage);
} finally {
await client.close();
}
+6 -10
View File
@@ -12,18 +12,14 @@ export async function createOpenAndArchiveWorkspace(url: string, cwd: string): P
try {
await client.connect();
const created = await client.workspaces.create(cwd);
if (!created.workspace) {
throw new Error(created.error ?? "Workspace creation failed");
}
const created = await client.workspaces.create({
source: { kind: "directory", path: cwd },
title: "Fresh SDK workspace",
});
const opened = await client.workspaces.open(cwd);
if (!opened.workspace) {
throw new Error(opened.error ?? "Workspace open failed");
}
await opened.workspace.refetch();
await opened.workspace.archive();
await opened.refresh();
await created.archive();
} finally {
await client.close();
}