mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-01 14:12:51 +00:00
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:
@@ -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;
|
||||
|
||||
@@ -8318,7 +8318,7 @@ export class Session {
|
||||
},
|
||||
activeTurnBehavior: msg.activeTurnBehavior ?? "steer",
|
||||
sessionMode: msg.sessionMode,
|
||||
clearPendingPermissions: true,
|
||||
clearPendingPermissions: false,
|
||||
});
|
||||
deliveryStatus = "queued";
|
||||
queued();
|
||||
|
||||
Reference in New Issue
Block a user