diff --git a/packages/client/src/index.test.ts b/packages/client/src/index.test.ts index 0dde83cb4d..be5bd06811 100644 --- a/packages/client/src/index.test.ts +++ b/packages/client/src/index.test.ts @@ -237,6 +237,37 @@ test("createPaseoApi borrows daemon capabilities without exposing connection own expect("skills" in paseo.agents).toBe(false); }); +test("agent handles send permission responses for their agent", async () => { + const { client, ws } = await connectClient(); + + await client.agents.ref("agent_sdk").respondToPermission({ + requestId: "permission-request", + response: { + behavior: "deny", + selectedActionId: "deny-once", + message: "Not approved", + interrupt: true, + }, + }); + + expect(parseSentFrame(ws.sent.at(-1))).toEqual({ + type: "session", + message: { + type: "agent_permission_response", + agentId: "agent_sdk", + requestId: "permission-request", + response: { + behavior: "deny", + selectedActionId: "deny-once", + message: "Not approved", + interrupt: true, + }, + }, + }); + + await client.close(); +}); + test("project actions list registered projects through the existing RPC", async () => { const { client, ws } = await connectClient(); diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 0cd94e5b62..b6b8d8e7b8 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -1,3 +1,4 @@ +import type { AgentPermissionResponse } from "@getpaseo/protocol/agent-types"; import type { AgentSnapshotPayload, CreateAgentRequestMessage, @@ -238,6 +239,12 @@ export interface PaseoAgentRunOptions extends PaseoAgentSendOptions { } export type PaseoAgentRunResult = WaitForFinishResult; +export type PaseoAgentPermissionResponse = AgentPermissionResponse; + +export interface PaseoAgentRespondToPermissionOptions { + requestId: string; + response: PaseoAgentPermissionResponse; +} export interface PaseoAgentCommandsOptions { requestId?: string; @@ -291,6 +298,7 @@ export interface PaseoAgentHandle { current(): PaseoAgent | null; refresh(requestId?: string): Promise; send(text: string, options?: PaseoAgentSendOptions): Promise; + respondToPermission(options: PaseoAgentRespondToPermissionOptions): Promise; /** Sends a prompt and resolves when that turn finishes or needs attention. */ run(text: string, options?: PaseoAgentRunOptions): Promise; /** Waits for the current turn, including one started with `prompt`. */ @@ -668,6 +676,9 @@ function createAgentHandleFactory(daemonClient: DaemonClient): AgentHandleFactor send: async (text, options) => { await daemonClient.sendAgentMessage(id, text, options); }, + respondToPermission: async ({ requestId, response }) => { + await daemonClient.respondToPermission(id, requestId, response); + }, run: async (text, options) => { const { timeoutMs, ...sendOptions } = options ?? {}; await daemonClient.sendAgentMessage(id, text, sendOptions); diff --git a/public-docs/sdk/reference.md b/public-docs/sdk/reference.md index 869fa06ffd..26c657e7f2 100644 --- a/public-docs/sdk/reference.md +++ b/public-docs/sdk/reference.md @@ -72,30 +72,31 @@ Creation options include `config`, `cwd`, `parent`, `title`, `prompt`, `env`, `o ### Agent handle -| Member | Result | Behavior | -| --------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------- | -| `id` | `string` | Stable daemon agent ID. | -| `workspaceId` | `string \| null` | Current workspace placement. | -| `cwd` | `string \| null` | Current working directory. | -| `status` | Agent status or `null` | Current lifecycle status. | -| `capabilities` | Capability flags or `null` | What the provider session supports. | -| `availableModes` | Agent modes or `null` | Modes the session can switch to. | -| `pendingPermissions` | Permission requests or `null` | Requests waiting on an answer. | -| `activeTurn` | Active turn or `null` | The turn in flight, with `turnId` and `startedAt`. | -| `lastUsage` | Usage or `null` | Token counts, cost, and context-window use from the last turn. | -| `lastError` | `string \| null` | Last error the daemon recorded for the agent. | -| `features` | Agent features or `null` | Provider feature toggles and selects with their current values. | -| `runtimeInfo` | Runtime info or `null` | Live provider, session ID, model, thinking option, and mode. | -| `archivedAt` | `string \| null` | Archive timestamp; `null` while the agent is active. | -| `current()` | `PaseoAgent \| null` | Current detailed value observed by this handle; never fetches. | -| `refresh(requestId?)` | `PaseoAgentRefetchResult \| null` | Fetches the current agent and project placement. | -| `send(text, options?)` | `Promise` | Resolves when the daemon accepts the prompt. | -| `run(text, options?)` | `PaseoAgentRunResult` | Sends a prompt and waits for that turn. `timeoutMs` controls the wait; it defaults to 10 minutes. | -| `waitForFinish(timeoutMs?)` | `PaseoAgentRunResult` | Waits for the active turn, including an initial prompt. Default timeout: 10 minutes. | -| `commands(options?)` | `PaseoAgentCommandsResult` | Asks the live session for its slash commands and skills, including built-in ones. Options: `requestId`. | -| `subscribe(handler)` | Unsubscribe function | Filters agent-directory updates to this ID and refreshes the handle properties. | -| `archive()` | `{ archivedAt }` | Soft-deletes the agent and closes its runtime. | -| `detach()` | `Promise` | Removes the parent relationship without stopping the agent. | +| Member | Result | Behavior | +| ------------------------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------- | +| `id` | `string` | Stable daemon agent ID. | +| `workspaceId` | `string \| null` | Current workspace placement. | +| `cwd` | `string \| null` | Current working directory. | +| `status` | Agent status or `null` | Current lifecycle status. | +| `capabilities` | Capability flags or `null` | What the provider session supports. | +| `availableModes` | Agent modes or `null` | Modes the session can switch to. | +| `pendingPermissions` | Permission requests or `null` | Requests waiting on an answer. | +| `activeTurn` | Active turn or `null` | The turn in flight, with `turnId` and `startedAt`. | +| `lastUsage` | Usage or `null` | Token counts, cost, and context-window use from the last turn. | +| `lastError` | `string \| null` | Last error the daemon recorded for the agent. | +| `features` | Agent features or `null` | Provider feature toggles and selects with their current values. | +| `runtimeInfo` | Runtime info or `null` | Live provider, session ID, model, thinking option, and mode. | +| `archivedAt` | `string \| null` | Archive timestamp; `null` while the agent is active. | +| `current()` | `PaseoAgent \| null` | Current detailed value observed by this handle; never fetches. | +| `refresh(requestId?)` | `PaseoAgentRefetchResult \| null` | Fetches the current agent and project placement. | +| `send(text, options?)` | `Promise` | Resolves when the daemon accepts the prompt. | +| `respondToPermission(options)` | `Promise` | Answers a pending permission by `requestId` with an allow or deny `response`. | +| `run(text, options?)` | `PaseoAgentRunResult` | Sends a prompt and waits for that turn. `timeoutMs` controls the wait; it defaults to 10 minutes. | +| `waitForFinish(timeoutMs?)` | `PaseoAgentRunResult` | Waits for the active turn, including an initial prompt. Default timeout: 10 minutes. | +| `commands(options?)` | `PaseoAgentCommandsResult` | Asks the live session for its slash commands and skills, including built-in ones. Options: `requestId`. | +| `subscribe(handler)` | Unsubscribe function | Filters agent-directory updates to this ID and refreshes the handle properties. | +| `archive()` | `{ archivedAt }` | Soft-deletes the agent and closes its runtime. | +| `detach()` | `Promise` | Removes the parent relationship without stopping the agent. | `workspaceId` through `archivedAt` mirror the last snapshot the handle observed. A handle from `ref()` reads `null` for all of them until `refresh()`, `run()`, `waitForFinish()`, a timeline refetch, or `subscribe()` delivers a snapshot. Optional values in an observed snapshot also read as `null`. Call `current()` when you need the whole snapshot or need to distinguish those states.