fix(server): keep a recipient's pending permission when another agent messages it

A message from another agent (send_agent_message with sourceAgentId) was
enqueued with clearPendingPermissions: true, so it superseded the
recipient's pending permission request and the request was denied before
a human saw it. Agent-sourced messages now leave pending permissions
alone. Typed messages and sourceless `paseo send` still clear them.

Fixes #7602
This commit is contained in:
Dominic Anaeto
2026-10-31 18:42:08 -04:00
parent caf9300358
commit 7069584687
2 changed files with 69 additions and 2 deletions
+68 -1
View File
@@ -67,6 +67,7 @@ import {
createProviderSnapshot,
createProviderSnapshotManagerStub,
} from "./test-utils/session-stubs.js";
import { createTestAgentMessages } from "./test-utils/agent-messages.js";
import { isPlatform } from "../test-utils/platform.js";
import {
GitHubAuthenticationError,
@@ -312,6 +313,7 @@ interface SessionForTestOptions {
permissions?: readonly DaemonPermission[];
agentManager?: { [K in keyof SessionOptions["agentManager"]]?: unknown };
agentStorage?: { [K in keyof SessionOptions["agentStorage"]]?: unknown };
agentMessages?: SessionOptions["agentMessages"];
github?: Partial<ForgeService & GitHubService>;
checkoutDiffManager?: { scheduleRefreshForCwd: ReturnType<typeof vi.fn> };
workspaceGitService?: {
@@ -388,7 +390,7 @@ function createSessionForTest(options: SessionForTestOptions = {}): Session {
const sessionOptions: SessionOptions = {
workspaceRuntimeStore: options.workspaceRuntimeStore ?? createWorkspaceRuntimeStore(),
messageReceipts: createMessageReceiptsStub(),
agentMessages: createAgentMessageDeliveryStub(),
agentMessages: options.agentMessages ?? createAgentMessageDeliveryStub(),
creationService: createTestCreationService(),
clientId: options.clientId ?? "test-client",
onMessage: (message) => messages.push(message),
@@ -5676,6 +5678,71 @@ test("project.list returns every active project descriptor", async () => {
]);
});
describe("send_agent_message_request pending permissions", () => {
const recipientId = "22222222-2222-4222-8222-222222222222";
const senderId = "33333333-3333-4333-8333-333333333333";
function steerableRecipient() {
return {
getAgent: vi.fn((agentId: string) =>
agentId === recipientId
? { id: recipientId, provider: "claude", lifecycle: "running" }
: null,
),
waitForAgentClose: vi.fn().mockResolvedValue(undefined),
tryRunOutOfBand: vi.fn().mockResolvedValue(false),
reloadStalePluginSession: vi.fn().mockResolvedValue(undefined),
steerOrReplaceActiveTurn: vi.fn().mockResolvedValue({ status: "steered" }),
};
}
test("an agent-sourced message steers without clearing the recipient's pending permissions", async () => {
const agentManager = steerableRecipient();
const agentMessages = createTestAgentMessages({
agentManager: asAgentManager(agentManager),
agentStorage: asAgentStorage({ get: vi.fn().mockResolvedValue(undefined) }),
logger: pino({ level: "silent" }),
});
const session = createSessionForTest({ agentManager, agentMessages });
await session.handleMessage({
type: "send_agent_message_request",
requestId: "agent-send",
agentId: recipientId,
text: "Status check: reply when done.",
sourceAgentId: senderId,
messageId: "agent-message",
});
await agentMessages.close();
expect(agentManager.steerOrReplaceActiveTurn).toHaveBeenCalledWith(
recipientId,
expect.anything(),
{ clientMessageId: "agent-message" },
);
});
test("a sourceless steer still clears the recipient's pending permissions", async () => {
const agentManager = steerableRecipient();
const session = createSessionForTest({ agentManager });
await session.handleMessage({
type: "send_agent_message_request",
requestId: "sourceless-send",
agentId: recipientId,
text: "Status check: reply when done.",
messageId: "sourceless-message",
activeTurnBehavior: "steer",
});
expect(agentManager.steerOrReplaceActiveTurn).toHaveBeenCalledWith(
recipientId,
expect.anything(),
{ clientMessageId: "sourceless-message", clearPendingPermissions: true },
);
});
});
describe("agent config setters", () => {
function liveAgentManager(overrides: { [K in keyof SessionOptions["agentManager"]]?: unknown }): {
[K in keyof SessionOptions["agentManager"]]?: unknown;
+1 -1
View File
@@ -8318,7 +8318,7 @@ export class Session {
},
activeTurnBehavior: msg.activeTurnBehavior ?? "steer",
sessionMode: msg.sessionMode,
clearPendingPermissions: true,
clearPendingPermissions: false,
});
deliveryStatus = "queued";
queued();