diff --git a/docs/plugins.md b/docs/plugins.md
index 21a924c4e8..9cab47214e 100644
--- a/docs/plugins.md
+++ b/docs/plugins.md
@@ -432,6 +432,9 @@ Server entries register lifecycle observers with `server.on()` and request trans
`server.before()`. The [public reference](../public-docs/plugins/reference.md#lifecycle-hooks)
owns callback shapes, ordering, and failure behavior. `plugin-examples/lifecycle-logger` registers all
eleven hooks; `plugin-examples/lifecycle-actions` demonstrates common automation callbacks.
+Session-open hooks also [attach tools to individual agents](../public-docs/plugins/reference.md#attach-tools-to-agents),
+as shown in `plugin-examples/agent-tools`. The agent manager stamps their metadata per session;
+executable definitions stay in the plugin process.
Emit from the operation owner, not a client subscription. Provider history replay must not trigger
live hooks. Observers must not be awaited inside agent mutations: a callback can send a prompt or
diff --git a/packages/server/src/server/plugins/agent-tools.e2e.test.ts b/packages/server/src/server/plugins/agent-tools.e2e.test.ts
index 48f4ffc490..14629aa15e 100644
--- a/packages/server/src/server/plugins/agent-tools.e2e.test.ts
+++ b/packages/server/src/server/plugins/agent-tools.e2e.test.ts
@@ -1,3 +1,4 @@
+import { fileURLToPath } from "node:url";
import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
@@ -707,3 +708,45 @@ test("explicit SDK reload interrupts a running turn before reopening its session
await h.close();
}
}, 60_000);
+
+test("the agent-tools example appends notes only for labeled agents", async () => {
+ const h = await setup();
+ try {
+ await h.client.installDirectoryPlugin(
+ fileURLToPath(new URL("../../../../../plugin-examples/agent-tools", import.meta.url)),
+ );
+ const a = await h.create({ "notes/enabled": "true" });
+ const mcpA = await h.mcp(a.id);
+ expect((await mcpA.listTools()).tools.map((tool) => tool.name)).toContain(
+ "append_workspace_note",
+ );
+ expect(
+ await mcpA.callTool({
+ name: "append_workspace_note",
+ arguments: { note: "Run the targeted tests before committing." },
+ }),
+ ).toMatchObject({
+ content: [
+ { type: "text", text: `Saved note to ${path.join(h.directory, "AGENT_NOTES.md")}` },
+ ],
+ structuredContent: { path: path.join(h.directory, "AGENT_NOTES.md") },
+ });
+ expect(await readFile(path.join(h.directory, "AGENT_NOTES.md"), "utf8")).toBe(
+ "Run the targeted tests before committing.\n",
+ );
+ await mcpA.callTool({
+ name: "append_workspace_note",
+ arguments: { note: "Keep existing notes." },
+ });
+ expect(await readFile(path.join(h.directory, "AGENT_NOTES.md"), "utf8")).toBe(
+ "Run the targeted tests before committing.\nKeep existing notes.\n",
+ );
+ await h.create();
+ const mcpB = await h.mcp();
+ expect((await mcpB.listTools()).tools.map((tool) => tool.name)).not.toContain(
+ "append_workspace_note",
+ );
+ } finally {
+ await h.close();
+ }
+}, 60_000);
diff --git a/plugin-examples/agent-tools/README.md b/plugin-examples/agent-tools/README.md
new file mode 100644
index 0000000000..e6ece3978e
--- /dev/null
+++ b/plugin-examples/agent-tools/README.md
@@ -0,0 +1,33 @@
+# Agent tools
+
+Attaches `append_workspace_note` to agents whose `notes/enabled` label is `"true"`.
+Calling the tool with `{ "note": "Run the targeted tests before committing." }` appends that
+line to `AGENT_NOTES.md` in the calling agent's working directory and returns the file path.
+It creates the file if it does not exist.
+
+Install on a daemon with plugins enabled:
+
+```bash
+paseo plugin install /absolute/path/to/plugin-examples/agent-tools
+```
+
+Create a labeled agent through the SDK:
+
+```ts
+const agent = await paseo.agents.create({
+ config: { provider: "codex/gpt-5.4" },
+ cwd: "/projects/shop",
+ labels: { "notes/enabled": "true" },
+});
+```
+
+The session-open callback also runs when an agent resumes or reloads. After changing an existing
+agent's labels, call `paseo.agents.ref(id).reload()` to apply the selection immediately. This
+interrupts a running turn. `refresh()` reads the agent snapshot without reopening its session.
+
+From the repository root, `npm run typecheck` includes this example. Outside this repository,
+use `paseo plugin init` to create the package and TypeScript setup, then copy `index.server.ts`
+and the manifest into that project.
+
+See [Attach tools to agents](../../public-docs/plugins/reference.md#attach-tools-to-agents)
+for targeting, execution, provider gates, collisions, and plugin reload behavior.
diff --git a/plugin-examples/agent-tools/index.server.ts b/plugin-examples/agent-tools/index.server.ts
new file mode 100644
index 0000000000..600d743fbd
--- /dev/null
+++ b/plugin-examples/agent-tools/index.server.ts
@@ -0,0 +1,26 @@
+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.before("agent.session_open", ({ request }) => {
+ if (request.labels["notes/enabled"] !== "true") return;
+ return { ...request, tools: [...request.tools, appendWorkspaceNote] };
+ });
+ return () => {};
+}
diff --git a/plugin-examples/agent-tools/paseo-plugin.json b/plugin-examples/agent-tools/paseo-plugin.json
new file mode 100644
index 0000000000..eaec7ffaf6
--- /dev/null
+++ b/plugin-examples/agent-tools/paseo-plugin.json
@@ -0,0 +1,4 @@
+{
+ "id": "agent-tools",
+ "requirements": { "paseo": ">=0.13.2" }
+}
diff --git a/public-docs/plugins/reference.md b/public-docs/plugins/reference.md
index 81b99b0dd0..69608a83a7 100644
--- a/public-docs/plugins/reference.md
+++ b/public-docs/plugins/reference.md
@@ -711,6 +711,120 @@ server.before("agent.session_open", ({ request }) => {
This runs on create, resume, refresh, and import. To inject only on creation, set `env` in an
`agent.create` callback instead.
+### Attach tools to agents
+
+Define a tool in `index.server.ts` and attach it in `before("agent.session_open")`.
+This example gives `append_workspace_note` only to agents with the label `notes/enabled: "true"`:
+
+```ts
+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.before("agent.session_open", ({ request }) => {
+ if (request.labels["notes/enabled"] !== "true") return;
+ return { ...request, tools: [...request.tools, appendWorkspaceNote] };
+ });
+ return () => {};
+}
+```
+
+Set the label when creating an agent with the SDK:
+
+```ts
+const agent = await paseo.agents.create({
+ config: { provider: "codex/gpt-5.4" },
+ cwd: "/projects/shop",
+ labels: { "notes/enabled": "true" },
+});
+```
+
+Select agents using any of the session-open request fields:
+
+| Select by | Condition in the callback |
+| ---------------- | -------------------------------------------- |
+| Label | `request.labels["notes/enabled"] === "true"` |
+| Provider | `request.provider === "codex"` |
+| Workspace | `request.workspaceId === "workspace-456"` |
+| Individual agent | `request.agentId === chosenAgentId` |
+
+`request.tools` starts empty for each plugin. Each callback sees the tools returned by earlier
+callbacks in that plugin; replace the array to add or remove tools. Returning `undefined` preserves
+the request. Tools are ignored for `purpose: "history"`.
+
+Import `defineAgentTool`, `AgentToolDefinition`, `AgentToolContext`, and `AgentToolResult` from
+`@getpaseo/plugin/server`. The helper infers the validated input from your Zod schema:
+
+```ts
+declare function defineAgentTool(tool: {
+ name: string;
+ title?: string;
+ description: string;
+ input: Input;
+ execute(
+ input: z.output,
+ context: AgentToolContext,
+ ): AgentToolResult | Promise;
+}): AgentToolDefinition;
+
+interface AgentToolResult {
+ text?: string;
+ structuredContent?: unknown;
+ isError?: boolean;
+}
+```
+
+| Execution context | Value |
+| ----------------- | --------------------------------------------------------------------------------------------------- |
+| `agent` | Calling agent as a `PluginHookAgent`, including `id`, `workspaceId`, `provider`, `cwd`, and `title` |
+| `paseo` | SDK connected to this daemon |
+| `signal` | Abort signal for this tool call; pass it to file operations or external requests |
+
+`execute` runs in the plugin process. Closures and the original Zod schema stay there; only tool
+metadata and call inputs/results cross the process boundary. Paseo validates input before calling
+`execute`. Invalid input returns schema issues to the agent. A thrown error becomes
+`{ isError: true, text: message }` and the plugin keeps running. Canceling the call or closing its
+transport aborts `signal` and discards the result. Tool calls have a 10 minute ceiling, separate
+from the 30 second hook timeout. Closing or reopening an agent session drops its old definitions.
+
+Return model-visible text in `text`. `structuredContent` must be JSON-serializable; when `text` is
+omitted, Paseo formats that value as text for the model. Set `isError: true` to report a tool error.
+
+Paseo delivers attached tools through its agent tool transport, using MCP or native registration
+as the provider supports. The same definition and callback work with either. Attached tools obey
+the [same gates as Paseo tools](/docs/mcp#limit-paseo-tools-by-provider). Only that agent sees them.
+
+Names must match `/^[a-z][a-z0-9_]*$/` and be at most 64 characters. A name colliding with a Paseo
+tool or another plugin's tool on the same agent fails the session open and names both owners.
+A duplicate name within one plugin also fails the session open. Agents the callback does not
+target are unaffected.
+
+| Change | When the agent receives it |
+| -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Start, stop, or reload a plugin declaring `agent.session_open` | Open sessions become stale. Each reopens before its next idle prompt; a running turn continues without interruption. The next prompt after that reopen reuses the session. |
+| Change which agents the callback targets | Run `paseo plugin reload agent-tools` after editing this example, then send the agent a prompt when it is idle. |
+| Apply immediately | Call `await paseo.agents.ref(id).reload({ requestId: "reload-notes" })`. `requestId` is optional. This reopens the provider session, reruns hooks with `reason: "refresh"`, and interrupts a running turn. |
+| Read the current snapshot | Call `await paseo.agents.ref(id).refresh()`. This reads state without reopening the session. |
+
+If an automatic reopen fails, the triggering prompt fails with that error. See the
+[complete agent-tools example](https://github.com/getpaseo/paseo/tree/main/plugin-examples/agent-tools).
+
### Choose workspace isolation
```ts
@@ -878,11 +992,11 @@ type PluginTurnOutcome =
### Before hooks
-| Name | Request fields | Editable |
-| -------------------- | ----------------------------------------------------------------------- | --------------------------------------- |
-| `agent.create` | `config`, optional `env` | Public agent config except `cwd`; `env` |
-| `agent.session_open` | `agentId`, `workspaceId`, `provider`, `cwd`, `reason`, `purpose`, `env` | Only `env` |
-| `workspace.create` | `source`, optional `title`, `firstAgentContext` | Entire explicit creation request |
+| Name | Request fields | Editable |
+| -------------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
+| `agent.create` | `config`, optional `env` | Public agent config except `cwd`; `env` |
+| `agent.session_open` | `agentId`, `workspaceId`, `provider`, `cwd`, read-only `title` and `labels`, `reason`, `purpose`, `env`, `tools` | `env` and `tools` |
+| `workspace.create` | `source`, optional `title`, `firstAgentContext` | Entire explicit creation request |
**`agent.create.config`** uses `AgentSessionConfig`:
@@ -903,6 +1017,9 @@ type PluginTurnOutcome =
"workspaceId": "workspace-456",
"provider": "codex",
"cwd": "/projects/shop",
+ "title": "Workspace notes",
+ "labels": { "notes/enabled": "true" },
+ "tools": [],
"reason": "resume",
"purpose": "interactive",
"env": { "COMPANY_ENV": "development" }
@@ -912,6 +1029,9 @@ type PluginTurnOutcome =
| Field | Values |
| ------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `workspaceId` | String or `null` |
+| `title` | Read-only string or `null`; creation title on create, stored title on resume. |
+| `labels` | Read-only string map; creation labels on create, stored labels on resume. |
+| `tools` | Editable `AgentToolDefinition[]`, local to this plugin. See [Attach tools to agents](#attach-tools-to-agents). |
| `reason` | `create`, `resume`, `refresh`, `import` |
| `purpose` | `interactive`, `history` |
| `env` | Launch override map; excludes the daemon's inherited environment. Replace the map to add, replace, or remove overrides. |
@@ -923,7 +1043,7 @@ Creation request
→ agent.create 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.session_open hooks (same ordering; env only)
+ → agent.session_open hooks (same ordering; env and plugin-local tools)
→ set PASEO_AGENT_ID and PASEO_AGENT_CWD
→ open provider session and save agent configuration
```
@@ -960,6 +1080,7 @@ saved; environment overrides are not persisted with it.
| [lifecycle-logger](https://github.com/getpaseo/paseo/tree/main/plugin-examples/lifecycle-logger) | All eleven hooks; JSON logs with environment values redacted |
| [lifecycle-actions](https://github.com/getpaseo/paseo/tree/main/plugin-examples/lifecycle-actions) | Follow-ups, permissions, environment, provider switching, worktree selection |
| [agent-configuration](https://github.com/getpaseo/paseo/tree/main/plugin-examples/agent-configuration) | MCP injection and Codex sandbox/approval options |
+| [agent-tools](https://github.com/getpaseo/paseo/tree/main/plugin-examples/agent-tools) | Label-targeted tools and file operations in the plugin process |
Read logger output with `paseo plugin logs lifecycle-logger` or the host's `daemon.log`.
diff --git a/public-docs/plugins/reference.zh-CN.md b/public-docs/plugins/reference.zh-CN.md
index 62caec83e5..3c84f8cc0c 100644
--- a/public-docs/plugins/reference.zh-CN.md
+++ b/public-docs/plugins/reference.zh-CN.md
@@ -585,6 +585,117 @@ server.before("agent.session_open", ({ request }) => {
它会在创建、恢复、刷新和导入时运行。如果只想在创建时注入,请改为在 `agent.create` 回调中设置 `env`。
+### 为 Agent 附加工具 {#attach-tools-to-agents}
+
+在 `index.server.ts` 中定义工具,并在 `before("agent.session_open")` 中附加它。
+此示例只向带有 `notes/enabled: "true"` 标签的 Agent 提供 `append_workspace_note`:
+
+```ts
+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.before("agent.session_open", ({ request }) => {
+ if (request.labels["notes/enabled"] !== "true") return;
+ return { ...request, tools: [...request.tools, appendWorkspaceNote] };
+ });
+ return () => {};
+}
+```
+
+通过 SDK 创建 Agent 时设置标签:
+
+```ts
+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 推断验证后的输入类型:
+
+```ts
+declare function defineAgentTool(tool: {
+ name: string;
+ title?: string;
+ description: string;
+ input: Input;
+ execute(
+ input: z.output,
+ context: AgentToolContext,
+ ): AgentToolResult | Promise;
+}): 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 工具相同的限制](/docs/mcp#limit-paseo-tools-by-provider)。
+只有该 Agent 能看到这些工具。
+
+名称必须匹配 `/^[a-z][a-z0-9_]*$/`,且不超过 64 个字符。如果名称与同一 Agent 的 Paseo
+工具或另一插件的工具冲突,会话打开会失败,并指出双方。一个插件内的重复名称也会导致
+会话打开失败。回调未选中的 Agent 不受影响。
+
+| 变更 | Agent 何时收到变更 |
+| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 启动、停止或重新加载声明了 `agent.session_open` 的插件 | 已打开的会话会标记为过期。每个会话在下一次空闲提示词前重新打开;正在运行的轮次会继续,不会被中断。重新打开后的下一条提示词复用该会话。 |
+| 修改回调选择的 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 示例](https://github.com/getpaseo/paseo/tree/main/plugin-examples/agent-tools)。
+
### 选择工作区隔离方式 {#choose-workspace-isolation}
```ts
@@ -741,11 +852,11 @@ type PluginTurnOutcome =
### before 钩子 {#before-hooks}
-| 名称 | 请求字段 | 可编辑 |
-| -------------------- | ----------------------------------------------------------------------- | ----------------------------------- |
-| `agent.create` | `config`,可选的 `env` | 除 `cwd` 外的公开 Agent 配置;`env` |
-| `agent.session_open` | `agentId`, `workspaceId`, `provider`, `cwd`, `reason`, `purpose`, `env` | 仅 `env` |
-| `workspace.create` | `source`,可选的 `title`,`firstAgentContext` | 整个显式创建请求 |
+| 名称 | 请求字段 | 可编辑 |
+| -------------------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------------- |
+| `agent.create` | `config`,可选的 `env` | 除 `cwd` 外的公开 Agent 配置;`env` |
+| `agent.session_open` | `agentId`, `workspaceId`, `provider`, `cwd`,只读的 `title` 和 `labels`,`reason`, `purpose`, `env`, `tools` | `env` 和 `tools` |
+| `workspace.create` | `source`,可选的 `title`,`firstAgentContext` | 整个显式创建请求 |
**`agent.create.config`** 使用 `AgentSessionConfig`:
@@ -766,18 +877,24 @@ type PluginTurnOutcome =
"workspaceId": "workspace-456",
"provider": "codex",
"cwd": "/projects/shop",
+ "title": "Workspace notes",
+ "labels": { "notes/enabled": "true" },
+ "tools": [],
"reason": "resume",
"purpose": "interactive",
"env": { "COMPANY_ENV": "development" }
}
```
-| 字段 | 取值 |
-| ------------- | ------------------------------------------------------------------------------------ |
-| `workspaceId` | 字符串或 `null` |
-| `reason` | `create`、`resume`、`refresh`、`import` |
-| `purpose` | `interactive`、`history` |
-| `env` | 启动覆盖映射;不包括 daemon 继承的环境变量。替换整个映射即可添加、替换或移除覆盖项。 |
+| 字段 | 取值 |
+| ------------- | -------------------------------------------------------------------------------------------------------- |
+| `workspaceId` | 字符串或 `null` |
+| `title` | 只读字符串或 `null`;创建时使用创建请求的标题,恢复时使用已保存的标题。 |
+| `labels` | 只读的字符串映射;创建时使用创建请求的标签,恢复时使用已保存的标签。 |
+| `tools` | 可编辑的 `AgentToolDefinition[]`,仅包含此插件的工具。参阅[为 Agent 附加工具](#attach-tools-to-agents)。 |
+| `reason` | `create`、`resume`、`refresh`、`import` |
+| `purpose` | `interactive`、`history` |
+| `env` | 启动覆盖映射;不包括 daemon 继承的环境变量。替换整个映射即可添加、替换或移除覆盖项。 |
### 顺序与返回值 {#ordering-and-returned-values}
@@ -786,7 +903,7 @@ Creation request
→ agent.create 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.session_open hooks (same ordering; env only)
+ → agent.session_open hooks (same ordering; env and plugin-local tools)
→ set PASEO_AGENT_ID and PASEO_AGENT_CWD
→ open provider session and save agent configuration
```
@@ -822,6 +939,7 @@ Creation request
| [lifecycle-logger](https://github.com/getpaseo/paseo/tree/main/plugin-examples/lifecycle-logger) | 全部十一个钩子;隐去环境变量值的 JSON 日志 |
| [lifecycle-actions](https://github.com/getpaseo/paseo/tree/main/plugin-examples/lifecycle-actions) | 后续消息、权限、环境变量、切换 Provider、选择 worktree |
| [agent-configuration](https://github.com/getpaseo/paseo/tree/main/plugin-examples/agent-configuration) | MCP 注入以及 Codex 沙盒/批准选项 |
+| [agent-tools](https://github.com/getpaseo/paseo/tree/main/plugin-examples/agent-tools) | 按标签选择 Agent,并在插件进程中执行文件操作的工具 |
用 `paseo plugin logs lifecycle-logger` 或主机的 `daemon.log` 读取 logger 的输出。