From 5828fd5ce48bae02b77dd48d05adc851a08fbfdf Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 5 Nov 2026 19:28:11 +0100 Subject: [PATCH] docs(plugins): an agent.open.before hook starts writes to its own agent without awaiting An agent.open.before hook that awaits setMetadata (or any write) on the agent being opened deadlocks: the write joins the agent's lifecycle lane behind the open, and the open waits on the hook until it times out. The hooks reference now says so and shows the supported shape, a write started without awaiting, which runs right after the open. Deferred, not in this stack: reject such a write immediately instead of letting the hook time out. The host knows which plugin hook it is awaiting for the agent and which channel the write arrives on, so the rejection can be keyed on "this plugin is inside an open hook for this agent" without cross-process tagging, and an unrelated client's write would still wait as it does today. --- docs/plugins.md | 3 +++ public-docs/plugins/hooks.md | 11 +++++++++++ public-docs/plugins/hooks.zh-CN.md | 11 +++++++++++ 3 files changed, 25 insertions(+) diff --git a/docs/plugins.md b/docs/plugins.md index 45caadeae1..81f61c4488 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -474,6 +474,9 @@ path shape: a plugin can place a worktree several directories below the default Emit from the operation owner, not a client subscription. Provider history replay must not trigger live hooks. Notifications must not be awaited inside agent mutations: a handler can send a prompt or answer a permission through its own daemon session. Awaiting it there deadlocks that command. +The same holds for a hook that runs inside an agent's lifecycle lane: an `agent.open.before` hook +that awaits a write to the agent being opened (`setMetadata`, `setLabels`) waits on the open, which +waits on the hook. The hooks reference tells plugin authors to start such a write without awaiting it. ## Contribute a provider diff --git a/public-docs/plugins/hooks.md b/public-docs/plugins/hooks.md index 624c3182dc..ad16eedbcc 100644 --- a/public-docs/plugins/hooks.md +++ b/public-docs/plugins/hooks.md @@ -283,6 +283,17 @@ server.addHook("agent.create.after", async ({ agent }) => { Receives `{ request }`; only `env`, `tools`, and `mcpServers` may change, for this launch. All other fields are read-only. A throw, invalid result, or timeout fails the open. +Do not await a write to the agent being opened, such as `setMetadata`. The write waits for the open to finish, the open waits for your hook, so the hook times out and the open fails. Start the write without awaiting it; it runs right after the open: + +```ts +server.addHook("agent.open.before", async ({ request }, { paseo }) => { + void paseo.agents + .ref(request.agentId) + .setMetadata({ "my.plugin": { openedBy: "hook" } }) + .catch((error) => console.error(error)); +}); +``` + **`agent.open.before` request example:** ```json diff --git a/public-docs/plugins/hooks.zh-CN.md b/public-docs/plugins/hooks.zh-CN.md index 68c903f040..b96202e8ad 100644 --- a/public-docs/plugins/hooks.zh-CN.md +++ b/public-docs/plugins/hooks.zh-CN.md @@ -279,6 +279,17 @@ server.addHook("agent.create.after", async ({ agent }) => { 收到 `{ request }`,只能修改 `env`、`tools` 和 `mcpServers`,修改只用于此次启动。其他字段只读。抛出错误、无效返回或超时会让打开操作失败。 +不要 await 对正在打开的 Agent 的写入,例如 `setMetadata`。该写入要等打开完成,而打开在等你的钩子,结果钩子超时,打开失败。启动写入但不要 await,它会在打开完成后立即执行: + +```ts +server.addHook("agent.open.before", async ({ request }, { paseo }) => { + void paseo.agents + .ref(request.agentId) + .setMetadata({ "my.plugin": { openedBy: "hook" } }) + .catch((error) => console.error(error)); +}); +``` + **`agent.open.before` 请求示例:** ```json