Files
paseo/public-docs/plugins/hooks.zh-CN.md
T
Mohamed BoudraandMatt Cowger 9fb7808191 feat(worktrees): let plugins choose creation options and placement (#8117)
* feat(worktrees): let plugins choose creation options and placement

Await the worktree creation request hook before Git source planning so plugins can change placement, branch and base without undoing a fetch. Manage arbitrary destinations through marker and registry ownership, and restore them at their recorded paths.

Co-authored-by: Matt Cowger <matt@cowger.us>

* fix(worktrees): compare repository paths across Windows aliases

Deletion must recognize the same repository through Git native and long path spellings. Reuse ownership path equivalence instead of comparing canonical strings, and let the recovery fixture accept Git platform line endings.

Co-authored-by: Matt Cowger <matt@cowger.us>

* fix(worktrees): respect recorded ownership and creation intent

Keep branch checkout distinct from branch-off, validate plugin placement once at the daemon boundary, and share default path collision resolution. A restored external worktree's marker must never override its recorded non-ownership.

Co-authored-by: Matt Cowger <matt@cowger.us>

* test(worktrees): record ownership in session archive fixture

The fixture creates a Paseo-owned worktree, so its durable record must say so. Archive now treats a recorded false value as authoritative instead of replacing it with filesystem discovery.

Co-authored-by: Matt Cowger <matt@cowger.us>

* test(plugins): retry temp cleanup after worktree archive on Windows

Windows releases directory handles after the daemon closes, so rmdir on the
source repository can hit EBUSY. Retry like the other git temp-dir tests.

* test(plugins): compare plugin-chosen worktree paths in canonical spelling

Windows temp directories come back as 8.3 aliases while the daemon records
the resolved spelling, so normalize the expected root the same way.

---------

Co-authored-by: Matt Cowger <matt@cowger.us>
2026-10-28 18:55:50 +07:00

35 KiB
Raw Blame History

title, description, nav
title description nav
插件钩子 为 Agent 和工作区操作注册钩子,修改请求或接收通知。 钩子

插件钩子

在 index.server.ts 中使用 server.addHook(name, handler)。钩子在插件启用时运行,即使没有连接的应用。addHook 从 Paseo 0.14 起可用;将 requirements.paseo 设置为你开发插件时使用的版本。

import type { PluginServerContext } from "@getpaseo/plugin/server";

export default function contribute(server: PluginServerContext) {
  const remove = server.addHook("agent.open.before", async ({ request }) => ({
    ...request,
    env: { ...request.env, REGION: "eu" },
  }));
  return remove;
}

命名与等待

名称遵循 subject.operation.position:主体、操作、before 或 after。名称标识生命周期中的位置;Paseo 是否等待由每个钩子决定,见下表。workspace 包括所有工作区,worktree 仅包括 git worktree。

  • 等待型钩子结束后 Paseo 才继续。抛出错误、返回无效数据或超过 30 秒会让操作失败,错误包含 Plugin <id> <hook> failed: …。修改请求的钩子收到 { request },可以返回完整的替换请求或 undefined。
  • 通知收到事件对象,返回 Promise<void>。Paseo 不等待;错误会记录在插件名下,原操作继续。agent.turn.before 和 agent.permission.before 分别在轮次开始或权限进入待处理状态时发出通知。

所有处理函数都必须为异步函数。轮次完成包括成功、失败和取消,请查看 outcome。权限完成包括所有处理结果,请查看 resolution。

创建 Agent 会发生一次,agent.create.before 修改已保存的配置。打开 Agent 指启动 Provider 进程,包括首次运行、daemon 重启后恢复、读取历史、导入外部会话和重新加载。agent.open.before 为每次启动提供临时的环境变量、工具和 MCP 服务器。创建会打开 Agent;打开已有 Agent 不会创建它;关闭会结束运行中的实例。在应用中打开标签页不属于此操作。

worktree.create.after 在工作区注册前运行,随后是 workspace.create.after,两者都早于第一个 Agent 的 agent.create.before / agent.open.before。

从等待型钩子提前返回

如果操作应立即继续,可以启动独立任务并返回:

server.addHook("worktree.create.after", async ({ worktree }) => {
  void warmCaches(worktree.path);
});

Paseo 只观察返回的 Promise,因此独立任务没有错误报告,也没有取消 signal。

钩子一览

钩子 等待 载荷 可修改
workspace.create.before 是 { request } 整个创建请求
workspace.create.after 否 { workspace }
workspace.archive.after 否 { workspace }
worktree.create.before 是 { request } 创建选项和目标路径
worktree.create.after 是 { worktree } worktree 中的文件;抛出错误中止创建
agent.create.before 是 { request: { config, env? } } 除 cwd 外的已保存配置;env
agent.create.after 否 { agent }
agent.open.before 是 { request } 仅用于启动的 env、tools、mcpServers
agent.close.after 否 { agent }
agent.archive.after 否 { agent, archivedAt }
agent.turn.before 否 { agent, turnId }
agent.turn.after 否 { agent, turnId, outcome, timeline }
agent.permission.before 否 { agent, request }
agent.permission.after 否 { agent, requestId, resolution }

共享载荷

interface PluginHookAgent {
  id: string;
  workspaceId: string | null;
  parentAgentId: string | null;
  provider: string;
  cwd: string;
  title: string | null;
}

interface PluginHookWorkspace {
  id: string;
  projectId: string;
  cwd: string;
  name: string | null;
  archivedAt: string | null;
}

type PluginTurnOutcome =
  | { kind: "completed" }
  | { kind: "failed"; error: { message: string; code?: string } }
  | { kind: "canceled"; reason: string };
字段 结构 / 含义
turnId Provider 报告的 string 或 null;会话重新打开后可能重复
timeline 已弃用,始终为 [];通过 SDK 获取历史。
request SDK 的 AgentPermissionRequest;kind 为 tool、plan、question、mode 或 other
resolution SDK 的 AgentPermissionResponse
archivedAt 时间戳字符串

workspace.create.before

收到 { request },其中包含 source、可选的 title 和 firstAgentContext。可替换整个显式创建请求;抛出错误、无效返回或超时会让创建失败。已有工作区和目录查找、导入不受影响。

选择工作区隔离方式

server.addHook("workspace.create.before", async ({ request }) => {
  if (request.source.kind !== "directory") {
    return request;
  }

  return {
    ...request,
    source: {
      kind: "worktree",
      cwd: request.source.path,
      action: "branch-off",
    },
  };
});

结果:显式的目录创建请求会变成 worktree 请求。已有的工作区以及目录查找/导入操作不受影响。

workspace.create.after

工作区记录创建且目录可用后触发,收到 { workspace }。

server.addHook("workspace.create.after", async ({ workspace }) => {
  console.log(workspace);
});

workspace.archive.after

归档状态保存后触发,收到 { workspace };worktree 清理可能尚未完成。

server.addHook("workspace.archive.after", async ({ workspace }) => {
  console.log(workspace);
});

worktree.create.before

为新 worktree 选择路径和创建选项。通过应用、CLI、MCP 或创建 Agent 新建 worktree 时,Paseo 在 git worktree add 之前等待此钩子。钩子收到 { request },返回完整的替换请求,或返回 undefined 保持原值。抛出错误、返回无效数据或超过 30 秒会终止创建,错误包含插件和钩子名称。

import { basename, dirname, join } from "node:path";

server.addHook("worktree.create.before", async ({ request }) => ({
  ...request,
  path: join(
    dirname(request.sourceCheckout),
    `${basename(request.sourceCheckout)}.${request.branch.replaceAll("/", "-")}`,
  ),
}));

源检出目录为 /home/me/src/paseo、分支为 feature/x 时,目标为 /home/me/src/paseo.feature-x。完整插件见 worktree-placement 示例。

请求字段 含义
kind branch-off、checkout-branch、checkout-change-request 或 checkout-github-pr
branch 请求的本地分支名称
baseRef branch-off 的起始引用;变更请求的比较基准;checkout-branch 不含此字段。要从引用创建分支,返回 kind: "branch-off"。
slug 规范化的 worktree 名称
path 绝对目标路径,初始值为已处理现有路径后缀的默认位置
repoRoot 只读,主仓库根目录
sourceCheckout 只读,源检出根目录,也支持从另一个 worktree 发起创建
forge、changeRequestNumber、headRef 变更请求检出标识;旧版 GitHub 检出用 githubPrNumber 代替 forge 和 changeRequestNumber
headRepositoryOwner、headRepository 可选的源仓库信息;headRepository 仅用于变更请求检出
checkoutRefs 可选的获取源:{ remoteName?: string, remoteRef: string }[]
pushRemoteUrl、trackOriginHead 可选的推送目标和跟踪策略

返回 path 后,slug 仅用于 branch-off 分支命名;CLI 使用路径的最后一级目录名查找 worktree。

除 repoRoot 和 sourceCheckout 外,所有字段都可以修改。返回所选 kind 的全部必填字段。Paseo 在钩子之后解析 Git 源计划,因此钩子可以在获取引用之前修改分支和基准,而 Git 之后仍可能选择唯一的最终分支名称。

Paseo 解析父目录中的符号链接并验证返回的路径。路径必须为绝对路径且不存在,不能是文件系统根目录、用户主目录、仓库根目录、.git 目录内的路径或仓库的祖先目录。目标已存在时创建失败,Paseo 不会为插件选择的路径添加后缀。插件应让 Paseo 创建目录。

顺序:worktree.create.before → git worktree add → worktree.create.after → 注册工作区。恢复缺失的 worktree 时使用记录的原路径,不运行这两个创建钩子。

worktree.create.after

新的 git worktree 在磁盘上创建后,Paseo 会等待此钩子,然后才注册工作区、运行 setup、打开终端或启动 Agent。收到 { worktree: { path, branch, sourceCheckout } },分别是 worktree 绝对路径、分支名、来源 checkout 目录。此时还没有工作区 id。无需返回数据,直接写入 worktree 即可。抛出错误或超过 30 秒会中止创建并删除新 worktree。

此钩子覆盖新建分支、检出分支和检出 PR。恢复缺失的已归档 worktree 使用独立路径,不触发此钩子。

import { copyFile } from "node:fs/promises";

server.addHook("worktree.create.after", async ({ worktree }) => {
  await copyFile(`${worktree.sourceCheckout}/.env`, `${worktree.path}/.env`);
});

agent.create.before

收到 { request: { config, env? } }。配置修改会保存,即使插件以后被停用或移除;env 只用于首次启动,不会保存。不能修改 config.cwd。抛出错误、无效返回或超时会让创建失败。

agent.create.before.config 使用 AgentSessionConfig:

字段 约束
provider, model 独立的字段;修改 Provider 可能需要同时修改模型/模式/选项
modeId, thinkingOptionId, featureValues 特定于 Provider 的选择
title, systemPrompt Agent 配置
providerOptions 不透明的、特定于 Provider 的选项
mcpServers, toolPolicy MCP 配置和精确的工具预批准
cwd 不能修改

修改已保存的配置

在 contribute(server) 中添加这个回调,为新的 Codex Agent 设置已保存的选项:

server.addHook("agent.create.before", async ({ request }) => {
  if (request.config.provider !== "codex") {
    return request;
  }

  return {
    ...request,
    config: {
      ...request.config,
      providerOptions: {
        ...request.config.providerOptions,
        sandbox_mode: "workspace-write",
        approval_policy: "on-request",
      },
    },
  };
});
输入 结果
providerOptions.sandbox_mode: "read-only" "workspace-write"
providerOptions.web_search: "disabled" 由展开运算保留

所选的 Provider 会校验 providerOptions。显式的 Codex 沙盒和批准选项会覆盖它的模式预设。

agent.create.after

常规创建完成后触发,收到 { agent };导入和恢复不会触发。

server.addHook("agent.create.after", async ({ agent }) => {
  console.log(agent);
});

agent.open.before

收到 { request },只能修改 env、tools 和 mcpServers,修改只用于此次启动。其他字段只读。抛出错误、无效返回或超时会让打开操作失败。

agent.open.before 请求示例:

{
  "agentId": "agent-123",
  "workspaceId": "workspace-456",
  "provider": "codex",
  "cwd": "/projects/shop",
  "title": "Workspace notes",
  "labels": { "notes/enabled": "true" },
  "tools": [],
  "mcpServers": {},
  "reason": "resume",
  "purpose": "interactive",
  "env": { "COMPANY_ENV": "development" }
}
字段 取值
workspaceId 字符串或 null
title 只读字符串或 null;创建时使用创建请求的标题,恢复时使用已保存的标题。
labels 只读的字符串映射;创建时使用创建请求的标签,恢复时使用已保存的标签。
tools 可编辑的 AgentToolDefinition[],仅包含此插件的工具。参阅为 Agent 附加工具。
mcpServers 可编辑的服务器映射,仅用于启动且仅包含本插件的条目。参阅注入 MCP 服务器。
reason create、resume、refresh、import
purpose interactive、history
env 启动覆盖映射;不包括 daemon 继承的环境变量。替换整个映射即可添加、替换或移除覆盖项。

在每次打开会话时注入环境变量

server.addHook("agent.open.before", async ({ request }) => {
  return {
    ...request,
    env: {
      ...request.env,
      COMPANY_ENV: "development",
    },
  };
});

会话打开时的环境变量覆盖项会在每次打开时重新计算。agent.create.before.env 只作用于首次启动,不会写入 Agent 的已保存配置。

注入 MCP 服务器

需要将服务器保存为 Agent 配置的一部分时,使用 agent.create.before.config.mcpServers。 插件拥有的服务器使用下面的回调,在 contribute(server) 中添加,并把占位 URL 替换为你的 MCP 端点:

server.addHook("agent.open.before", async ({ request }) => {
  if (request.provider !== "codex") return;

  return {
    ...request,
    mcpServers: {
      ...request.mcpServers,
      company: { type: "http", url: "https://tools.example.com/mcp" },
    },
  };
});

request.mcpServers 使用与 AgentSessionConfig.mcpServers 相同的服务器配置。 每个插件的映射从空开始,只在该插件自己的回调之间累积,不包含 Agent 已保存的服务器或其他插件的服务器。 替换映射即可添加、替换或移除本插件的条目;返回 undefined 会保持不变。 Paseo 按插件 ID 顺序合并各插件的最终映射。

名称与已保存的服务器、Paseo 内部的 paseo 服务器或其他插件的服务器冲突时,会话打开失败,错误会指出服务器名称及双方来源。 不支持 MCP 的 Provider 会报 does not support MCP servers 错误,并指出插件名称。 purpose: "history" 的会话打开会忽略这些服务器。安装、停用和重新加载插件后,现有 Agent 通过会话重新打开行为获取变化。

为 Agent 附加工具

在 index.server.ts 中定义工具,并在 server.addHook("agent.open.before", async handler) 中附加它。 此示例只向带有 notes/enabled: "true" 标签的 Agent 提供 append_workspace_note:

import { writeFile } from "node:fs/promises";
import path from "node:path";
import { z } from "zod";
import { defineAgentTool, type PluginServerContext } from "@getpaseo/plugin/server";

const appendWorkspaceNote = defineAgentTool({
  name: "append_workspace_note",
  description: "Append a note to AGENT_NOTES.md in this agent's working directory.",
  input: z.object({ note: z.string().min(1).describe("Note to append") }),
  async execute({ note }, { agent, signal }) {
    const notePath = path.join(agent.cwd, "AGENT_NOTES.md");
    await writeFile(notePath, `${note}\n`, { encoding: "utf8", flag: "a", signal });
    return {
      text: `Saved note to ${notePath}`,
      structuredContent: { path: notePath },
    };
  },
});

export default function contribute(server: PluginServerContext) {
  server.addHook("agent.open.before", async ({ request }) => {
    if (request.labels["notes/enabled"] !== "true") return;
    return { ...request, tools: [...request.tools, appendWorkspaceNote] };
  });
  return () => {};
}

通过 SDK 创建 Agent 时设置标签:

const agent = await paseo.agents.create({
  config: { provider: "codex/gpt-5.4" },
  cwd: "/projects/shop",
  labels: { "notes/enabled": "true" },
});

使用会话打开请求中的字段选择 Agent:

选择依据 回调中的条件
标签 request.labels["notes/enabled"] === "true"
Provider request.provider === "codex"
工作区 request.workspaceId === "workspace-456"
单个 Agent request.agentId === chosenAgentId

每个插件的 request.tools 都从空数组开始。同一插件中的后续回调会看到前面回调返回的工具; 替换数组即可添加或移除工具。返回 undefined 会保留请求。purpose: "history" 时会忽略工具。

从 @getpaseo/plugin/server 导入 defineAgentTool、AgentToolDefinition、AgentToolContext 和 AgentToolResult。辅助函数会根据 Zod schema 推断验证后的输入类型:

declare function defineAgentTool<Input extends z.ZodType>(tool: {
  name: string;
  title?: string;
  description: string;
  input: Input;
  execute(
    input: z.output<Input>,
    context: AgentToolContext,
  ): AgentToolResult | Promise<AgentToolResult>;
}): AgentToolDefinition;

interface AgentToolResult {
  text?: string;
  structuredContent?: unknown;
  isError?: boolean;
}
执行 context 值
agent 发起调用的 Agent,以 PluginHookAgent 表示,包括 id、workspaceId、provider、cwd 和 title
paseo 已连接到此 daemon 的 SDK
signal 此次工具调用的中止信号;请把它传给文件操作或外部请求

execute 在插件进程中运行。闭包和原始 Zod schema 保留在该进程中;只有工具元数据和调用的 输入、结果会跨进程传递。Paseo 在调用 execute 前验证输入。无效输入会将 schema 问题返回给 Agent。抛出的错误会转换为 { isError: true, text: message },插件继续运行。取消调用或关闭 其传输连接会中止 signal 并丢弃结果。工具调用最长运行 10 分钟,与钩子的 30 秒超时分开。 关闭或重新打开 Agent 会话会移除旧定义。

在 text 中返回模型可见的文本。structuredContent 必须可序列化为 JSON;省略 text 时, Paseo 会将该值格式化为供模型读取的文本。设置 isError: true 可报告工具错误。

Paseo 通过 Agent 工具传输提供附加的工具,按 Provider 的支持情况使用 MCP 或原生注册。 同一份定义和回调适用于这两种方式。附加的工具遵循与 Paseo 工具相同的限制。 只有该 Agent 能看到这些工具。

名称必须匹配 /^[a-z][a-z0-9_]*$/,且不超过 64 个字符。如果名称与同一 Agent 的 Paseo 工具或另一插件的工具冲突,会话打开会失败,并指出双方。一个插件内的重复名称也会导致 会话打开失败。回调未选中的 Agent 不受影响。

变更 Agent 何时收到变更
启动、停止或重新加载声明了 agent.open.before 的插件 已打开的会话会标记为过期。每个会话在下一次空闲提示词前重新打开;正在运行的轮次会继续,不会被中断。重新打开后的下一条提示词复用该会话。
修改回调选择的 Agent 编辑此示例后运行 paseo plugin reload agent-tools,然后在 Agent 空闲时发送提示词。
立即应用 调用 await paseo.agents.ref(id).reload({ requestId: "reload-notes" })。requestId 可选。这会重新打开 Provider 会话,以 reason: "refresh" 再次运行钩子,并中断正在运行的轮次。
读取当前快照 调用 await paseo.agents.ref(id).refresh()。这只读取状态,不会重新打开会话。

如果自动重新打开失败,触发它的提示词会返回该错误。参阅 完整 agent-tools 示例。

agent.close.after

运行中的 Agent 实例关闭后触发,收到 { agent }。

server.addHook("agent.close.after", async ({ agent }) => {
  console.log(agent);
});

agent.archive.after

归档状态保存后触发,收到 { agent, archivedAt };运行时清理可能尚未完成。

server.addHook("agent.archive.after", async ({ agent, archivedAt }) => {
  console.log(agent, archivedAt);
});

agent.turn.before

实时轮次开始后触发,收到 { agent, turnId }。历史重放不会触发。

server.addHook("agent.turn.before", async ({ agent, turnId }) => {
  console.log(agent, turnId);
});

agent.turn.after

实时轮次完成、失败或取消后触发,收到 { agent, turnId, outcome, timeline }。outcome.kind 为 completed、failed 或 canceled。timeline 已弃用且始终为空;请按下面的示例通过 SDK 获取历史。

在轮次结束时发送后续消息

把 server/inspect.ts 复制到你的插件中。该辅助文件从 @getpaseo/protocol/agent-types 导入类型;请把与 daemon 版本相同的 @getpaseo/protocol 加入开发依赖,并在加载插件之前安装它们。latestOutputText 会拼接最近一条用户消息之后的文本片段。

import type { PluginServerContext } from "@getpaseo/plugin/server";
import { latestOutputText } from "./server/inspect";

export default function contribute(server: PluginServerContext) {
  server.addHook("agent.turn.after", async (event, context) => {
    if (event.outcome.kind === "canceled") {
      return;
    }

    const agent = context.paseo.agents.ref(event.agent.id);
    const page = await agent.timeline.refetch({ direction: "tail", limit: 20 });
    const text = latestOutputText(page.entries.map(({ item }) => item));
    if (/out of credits/i.test(text)) {
      await agent.send("Try again.");
    }
  });

  return () => {};
}
Turn ends: "out of credits"
  → plugin sends "Try again."
  → a new turn starts

该处理函数只获取最近 20 条时间线条目,不检查这一页之前的输出。插件需要更多历史时,请用 cursor 和 direction 继续翻页。

这会用现有的 SDK 发送一条新消息。持续匹配会不断发送后续消息;需要时请在插件中添加次数限制或延迟。附件和工具的效果不会被重放。

agent.permission.before

权限或问题进入待处理状态后触发,收到 { agent, request };request 为 AgentPermissionRequest。通过 SDK 回应。

回应权限请求

使用同一个辅助文件中的 shellCommand:

import type { PluginServerContext } from "@getpaseo/plugin/server";
import { shellCommand } from "./server/inspect";

export default function contribute(server: PluginServerContext) {
  server.addHook("agent.permission.before", async (event, context) => {
    const command = shellCommand(event.request);
    if (command === null) {
      return;
    }

    const agent = context.paseo.agents.ref(event.agent.id);
    if (/\brm\s+-rf\b/.test(command)) {
      await agent.respondToPermission({
        requestId: event.request.id,
        response: { behavior: "deny", message: "Recursive deletion is blocked." },
      });
      return;
    }

    if (command.trim() === "git status") {
      await agent.respondToPermission({
        requestId: event.request.id,
        response: { behavior: "allow" },
      });
    }
  });

  return () => {};
}
请求 结果
rm -rf build 拒绝
git status 批准
其他命令或请求 保持待处理,留给用户
已经处理过的请求 SDK 响应失败

这个正则表达式只是一个示例策略,而不是 shell 解析器。权限请求也可能是问题、计划和模式变更;请求权限不会结束当前轮次。

agent.permission.after

待处理请求得到回应或清除后触发,收到 { agent, requestId, resolution };resolution 为 AgentPermissionResponse。

server.addHook("agent.permission.after", async ({ agent, requestId, resolution }) => {
  console.log(agent, requestId, resolution);
});

Agent 钩子不包括 daemon 的私有临时辅助 Agent。公开的后台工作区及其普通 Agent 使用相同钩子。

顺序与返回值

Creation request
  → agent.create.before hooks (plugin-ID order; registration order within each plugin)
  → resolve defaults and validate provider configuration
  → derive launch configuration with Paseo runtime tools and daemon prompt
  → agent.open.before hooks (same ordering; env and plugin-local tools and MCP servers)
  → set PASEO_AGENT_ID and PASEO_AGENT_CWD
  → open provider session and save agent configuration
回调返回 下一个回调收到
{ ...request, env: { ...request.env, REGION: "eu" } } 之前的请求,添加或替换了 REGION
{ ...request, env: { REGION: "eu" } } 之前的请求,整个覆盖映射被替换
undefined 未改变的请求
抛出错误或返回无效数据 操作失败;后面的回调不会运行

没有自动的深度合并。env 在插件之间累积;tools 和 mcpServers 只在同一插件的回调之间累积。 返回替换的映射或数组会替换该范围内之前的值。

context 与清理

契约 行为
context.paseo 已连接到此 daemon 的现有 SDK
context.signal 在调用超时或插件停止时中止;请把它传给外部请求
输入数据 分离的快照;通过返回的请求或 SDK 命令改变状态
注册结果 幂等的移除函数,例如 const remove = server.addHook(...); remove();
重新加载、停用、移除、关闭 移除剩余的注册
未知的钩子名称 注册失败
钩子超时 30 秒;会中止 signal。等待型钩子会让待处理的操作失败;事件处理函数会记录一条错误。
事件处理函数出错 记录在插件名下;原操作继续进行
事件投递 实时、尽力而为;没有重放、持久化或自动重试
事件并发 不同的事件可能重叠;不保证回调完成的顺序

完整示例

插件 包含
lifecycle-logger Agent、工作区和 worktree 钩子;隐去环境变量值的 JSON 日志
lifecycle-actions 后续消息、权限、环境变量、切换 Provider、选择 worktree
agent-configuration MCP 注入以及 Codex 沙盒/批准选项
agent-tools 按标签选择 Agent,并在插件进程中执行文件操作的工具

用 paseo plugin logs lifecycle-logger 或主机的 daemon.log 读取 logger 的输出。

已重命名的钩子

旧注册方法已弃用,会在 2027-04-30 之后移除。现有插件继续按原来的方式运行。同一插件中混合使用新旧注册方法时,处理函数按注册顺序运行。addHook 只接受新名称。

旧注册方式 新注册方式
server.before("workspace.create", handler) server.addHook("workspace.create.before", asyncHandler)
server.on("workspace.created", handler) server.addHook("workspace.create.after", asyncHandler)
server.on("workspace.archived", handler) server.addHook("workspace.archive.after", asyncHandler)
server.before("agent.create", handler) server.addHook("agent.create.before", asyncHandler)
server.on("agent.created", handler) server.addHook("agent.create.after", asyncHandler)
server.before("agent.session_open", handler) server.addHook("agent.open.before", asyncHandler)
server.on("agent.closed", handler) server.addHook("agent.close.after", asyncHandler)
server.on("agent.archived", handler) server.addHook("agent.archive.after", asyncHandler)
server.on("agent.turn_started", handler) server.addHook("agent.turn.before", asyncHandler)
server.on("agent.turn_ended", handler) server.addHook("agent.turn.after", asyncHandler)
server.on("agent.permission_requested", handler) server.addHook("agent.permission.before", asyncHandler)
server.on("agent.permission_resolved", handler) server.addHook("agent.permission.after", asyncHandler)