Paseo SDK 实战食谱:用 TypeScript 编排 Issue 流转、并行审查与常驻 Agent
2026/9/21 23:16:17 网站建设 项目流程

Paseo SDK 实战食谱:用 TypeScript 编排 Issue 流转、并行审查与常驻 Agent

【免费下载链接】paseoOrchestrate multiple coding agents from desktop and mobile项目地址: https://gitcode.com/gh_mirrors/pa/paseo

@getpaseo/client是 Paseo 的 TypeScript SDK,它通过 WebSocket 驱动本地 Paseo 守护进程(daemon)来创建、运行和回收编码 Agent。本篇文章以官方 SDK Recipes 文档为主线,完整呈现四个可直接落地的高频模式——把 Issue 变成可见工作、并行运行多个审查者、跨进程重启保持常驻角色、以及安全清理临时 Agent——并结合仓库源码与官方示例深入讲解其底层机制。读完你将掌握如何在自己的集成程序里创建、复用、追踪和归档 Agent,并把它们与 Paseo App 中的手动任务无缝并排。

前置准备:连接守护进程

SDK Recipes 中的所有示例都假设你已经有一个“已连接的client”,它从公共包根导入:

import { createPaseoClient } from "@getpaseo/client"; const client = createPaseoClient({ url: "ws://127.0.0.1:6767/ws" }); await client.connect();

守护进程默认监听ws://127.0.0.1:6767/ws,可通过npx @getpaseo/cli启动(详见 public-docs/sdk/index.md)。若守护进程设置了密码,或需要连接远程 daemon,则在配置中传入password字段:

const client = createPaseoClient({ url: "wss://devbox.example.com/ws", password: "my-secret", });

从源码看,createPaseoClient在 packages/client/src/index.ts#L506-L526 中构造DaemonClient,并返回带有connect()close()agentsworkspacesprovidersconfig等命名空间的对象。connect()会一直等到守护进程完成自我标识才 resolve;而close()只关闭 SDK 连接,不会归档任何 Agent——Agent 会继续在 daemon 上运行并保持可见(详见 public-docs/sdk/quickstart.md)。

食谱一:把一个 Issue 变成可见的工作

这是最经典的集成场景:把外部系统(Issue 跟踪器、告警、Webhook)中的一条任务,转化为一个由编码 Agent 执行的可见工作项。

type Issue = { id: string; title: string; description: string; repositoryPath: string; }; async function startIssue(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 }; }

这个模式与仓库中的官方示例 packages/client/examples/issue-to-agent.ts 完全一致。其核心设计有四点:

  • workspaces.open(path)以目录为身份:重复打开同一目录会复用已有的活跃 workspace(源码位于 packages/client/src/index.ts#L209-L213),避免重复创建项目。
  • 通过 workspace handle 创建 Agentworkspace.agents.create()由 handle 自动补全cwdworkspaceId的放置参数,避免调用方重复传目录(见 packages/client/src/index.ts#L818-L829)。config.provider始终是provider/model格式,比如codex/gpt-5.5claude/claude-sonnet-5
  • Label 是集成方的元数据"issue-provider""issue-id"这类键是应用自有的,用于后续检索和去重。
  • create()在会话建立后即返回,此时 prompt 仍在运行中,Agent 会立即出现在 Paseo App 里,和手动启动的任务并排显示。

关键要点:持久化返回的workspaceIdagentId。在下一个 Webhook 或页面加载时,用workspaces.ref()agents.ref()恢复句柄,而不是重复创建造成重复 Agent:

const agent = client.agents.ref(savedAgentId); await agent.refresh(); await agent.run("继续完成剩余部分。");

ref()不会立即联系 daemon,它只是构建一个“零观察”的句柄;需要确认 Agent 是否仍存在时,先调用refresh()——若不存在会返回null(详见 public-docs/sdk/agents.md)。

食谱二:并行运行多个审查者

把一个 diff 同时交给不同角度的审查者,收集所有结论。官方食谱用Promise.all并发创建,再用waitForFinish()并发等待:

const prompts = [ "Review the diff for correctness and missed edge cases.", "Review the diff for security and unsafe input handling.", "Review the diff for unnecessary complexity.", ]; const reviewers = await Promise.all( prompts.map((prompt, index) => client.agents.create({ config: { provider: index === 1 ? "claude/claude-sonnet-5" : "codex/gpt-5.5", }, cwd: process.cwd(), title: `Review ${index + 1}`, prompt, }), ), ); const results = await Promise.all(reviewers.map((reviewer) => reviewer.waitForFinish())); for (const result of results) { console.log(result.status, result.lastMessage); }

该模式与仓库示例 packages/client/examples/parallel-review.ts 高度吻合,示例中还演示了最佳实践:把创建出的 Agent 句柄收集进数组,在finally中用Promise.allSettled逐一归档,避免并发审查结束后留下游离 Agent。

关于等待与状态waitForFinish()默认最多等待 10 分钟(源码中DEFAULT_WAIT_FOR_FINISH_MS = 10 * 60_000,见 packages/client/src/index.ts#L61-L65),可以传入毫秒数调整。它返回四种状态之一:

状态含义
idle本轮结束,Agent 可以接收下一条 prompt。
permissionAgent 正在 Paseo 中等待某人响应权限请求。
errorProvider 以错误结束本轮。
timeout等待超时,Agent 可能仍在运行。超时不会取消 Agent。

由于waitForFinish在 packages/client/src/index.ts#L962-L971 的实现里会把最终快照写回句柄,所以等待完成后可以直接读result.lastMessageresult.status

食谱三:跨进程重启保持常驻角色

有些场景需要一个“一直存在”的角色(比如规划者 Planner、长期审查员),程序重启后不重建,而是找到旧实例继续对话。官方食谱给出的getPlanner模式:

async function getPlanner() { const listed = await client.agents.list({ filter: { includeArchived: false }, page: { limit: 100 }, }); const existing = listed.entries.find(({ agent }) => agent.labels["my-app-role"] === "planner"); if (existing) return client.agents.ref(existing.agent); return client.agents.create({ config: { provider: "claude/claude-sonnet-5", }, cwd: process.cwd(), title: "Planner", labels: { "my-app-role": "planner" }, }); } const planner = await getPlanner(); const plan = await planner.run("Plan the next small, shippable improvement.");

Labels 是应用拥有的元数据。当多个工具可能在同一台 daemon 上管理 Agent 时,务必对键做命名空间化(如my-app-role而非role),避免与其他工具冲突。创建时打上 label,列表时由 daemon 侧完成过滤匹配:

const page = await client.agents.list({ filter: { labels: { "issue-provider": "my-tracker" } }, });

这与 public-docs/sdk/agents.md 中“Find agents by label”一节一致:agents.list()filter支持includeArchivedlabels等条件,page.limit控制每页条数。

常驻角色找到后的续话ref()得到句柄后直接run()即可发送新一轮 prompt。run()send()的区别在于:run()会等待本轮结束并返回结果(其底层实现为发送消息后调用waitForFinish,见 packages/client/src/index.ts#L950-L961),而send()是即发即忘;按需选择。

食谱四:安全清理临时 Agent

临时创建的“冒烟测试”Agent 用完必须归档,否则会永久留在 daemon 上占用会话。官方食谱用try/finally保证任何路径下都会清理,且只清理自己创建的 Agent:

const temporaryAgents = []; try { const agent = await client.agents.create({ config: { provider: "codex/gpt-5.5", }, cwd: process.cwd(), title: "Temporary smoke test", }); temporaryAgents.push(agent); const result = await agent.run("Reply with READY and nothing else.", { timeoutMs: 2 * 60_000, }); if (result.status !== "idle") { throw new Error(result.error ?? result.status); } } finally { await Promise.allSettled(temporaryAgents.map((agent) => agent.archive())); }

几个值得强调的细节:

  • 只归档自己创建的 Agent:食谱明确警告“Do not archive agents your integration did not create”。因为 Agent 归档是软删除并关闭其运行时(源码 packages/client/src/index.ts#L973-L979 将archive()实现为 daemon 的archiveAgentRPC 并回写archivedAt),误删他人任务不可逆。
  • allSettled而非all:确保某个归档失败不会阻断其余清理。
  • 超时参数run()的第二个参数可传timeoutMs(此处 2 分钟),结合waitForFinish的四状态结果判断成功与否。

关闭 SDK 连接不会归档 Agent。如果你希望临时 Agent 在程序结束后消失,必须显式归档,正如 public-docs/sdk/agents.md 结尾所强调的:"Archive temporary agents explicitly, preferably infinally"。

从源码看这些食谱背后的句柄机制

理解 Recipes 的关键在于 Paseo SDK 的“句柄(handle)”设计——一个句柄持有稳定的 Agent ID 或 Workspace ID,并暴露回合生命周期,而不直接暴露 daemon RPC。相关实现集中在 packages/client/src/index.ts:

  • createAgentHandleFactory(index.ts#L858-L997):为 Agent 构建句柄。workspaceIdcwdstatuspendingPermissionslastUsageruntimeInfoarchivedAt等属性全部读取“句柄最近观察到的那份快照”,永不主动拉取;来自ref()的句柄从未观察到任何快照,因此这些属性在refresh()run()waitForFinish()subscribe()投递快照之前一律为null
  • createWorkspaceHandleFactory(index.ts#L774-L856):Workspace 句柄同样持有 ID 与快照,agents.create()会先用当前快照(必要时refresh())取得workspaceDirectory,再把它作为放置参数传给底层createAgent——这正是“handle 负责摆放,调用方无需重复目录”的实现依据。
  • toDaemonAgentCreateOptions(index.ts#L528-L549):把provider/model字符串拆分为providermodel,并把prompt转为initialPromptparent转为callerAgentId,是 Recipes 中各参数落地为 RPC 的转换层。
  • createPaseoClient(index.ts#L506-L526):将所有 action 命名空间(agentsworkspacesprovidersconfig等)组装成客户端对象,close()依次释放订阅并关闭传输。

关于父子的补充:通过 workspace 创建子 Agent 时传入parent可建立父子关系,归档父 Agent 会级联归档其子;若子 Agent 需要独立存活,先调用detach()解除关系。这一机制与 public-docs/sdk/agents.md 中“Create a subagent”一节对应。

把这些食谱组合成完整集成

四个食谱可以自然串联成一个完整的 Webhook 集成生命周期:

  1. 收到 Issue→ 食谱一:workspaces.open(repositoryPath)+workspace.agents.create(...),持久化workspaceId/agentId与 label(issue-providerissue-id)。
  2. PR 就绪→ 食谱二:并发创建多个审查者,waitForFinish()汇总结果。
  3. 长期角色→ 食谱三:用agents.list()按 label 查找,命中则ref()复用,否则创建;重启后依然找得到。
  4. 临时任务→ 食谱四:try/finally中显式归档自建 Agent;整个程序的退出只调用client.close()关闭连接,不隐式清理任何东西。

Workspace 归档与 Agent 归档相互独立:workspace.archive()只归档工作区,Agent 需按你的集成所拥有的生命周期分别归档(详见 public-docs/sdk/workspaces.md)。这套“创建 → 标记 → 复用 → 回收”的闭环,就是基于@getpaseo/client构建稳定自动化工作流的核心方法论,更多细节可继续阅读 public-docs/sdk/agents.md、public-docs/sdk/workspaces.md、public-docs/sdk/providers.md 与 public-docs/sdk/events.md。

【免费下载链接】paseoOrchestrate multiple coding agents from desktop and mobile项目地址: https://gitcode.com/gh_mirrors/pa/paseo

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询