mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-03 07:03:18 +00:00
fix(codex): include patch details in file change approvals
This commit is contained in:
committed by
paseo-bot[bot]
parent
7495c20f78
commit
930ded5e1a
@@ -227,6 +227,67 @@ function deferred<T>() {
|
||||
return { promise, resolve };
|
||||
}
|
||||
|
||||
describe("Codex file-change approvals", () => {
|
||||
test("file-change approval includes the matching started patch", async () => {
|
||||
const appServer = createFakeCodexAppServer();
|
||||
const { session } = await startPublicSteeringSession(appServer);
|
||||
try {
|
||||
for (const [id, filePath, diff] of [
|
||||
["file-alpha", "/workspace/project/alpha.txt", "@@ -1 +1 @@\n-blue\n+green\n"],
|
||||
["file-beta", "/workspace/project/beta.txt", "@@ -1 +1 @@\n-small\n+large\n"],
|
||||
]) {
|
||||
const started = waitForTimelineToolCall(session, id);
|
||||
appServer.child.stdout.write(
|
||||
`${JSON.stringify({
|
||||
method: "item/started",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "native-A",
|
||||
item: {
|
||||
type: "fileChange",
|
||||
id,
|
||||
status: "inProgress",
|
||||
changes: [{ path: filePath, kind: { type: "update", move_path: null }, diff }],
|
||||
},
|
||||
},
|
||||
})}\n`,
|
||||
);
|
||||
await started;
|
||||
}
|
||||
const permission = waitForNextPermission(session);
|
||||
appServer.requestFileChangeApproval({
|
||||
itemId: "file-alpha",
|
||||
threadId: "thread-1",
|
||||
turnId: "native-A",
|
||||
reason: "Apply the patch",
|
||||
});
|
||||
|
||||
expect((await permission).request).toEqual({
|
||||
id: "permission-file-alpha",
|
||||
provider: "codex",
|
||||
name: "CodexFileChange",
|
||||
kind: "tool",
|
||||
title: "Apply file changes",
|
||||
description: "Apply the patch",
|
||||
detail: {
|
||||
type: "edit",
|
||||
filePath: "alpha.txt",
|
||||
unifiedDiff: "@@ -1 +1 @@\n-blue\n+green\n",
|
||||
},
|
||||
metadata: { itemId: "file-alpha", threadId: "thread-1", turnId: "native-A" },
|
||||
});
|
||||
|
||||
await session.respondToPermission("permission-file-alpha", { behavior: "allow" });
|
||||
await expect(appServer.waitForApprovalDecision("file-alpha")).resolves.toEqual({
|
||||
decision: "accept",
|
||||
});
|
||||
appServer.assertNoErrors();
|
||||
} finally {
|
||||
await session.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Codex active-turn steering admission", () => {
|
||||
test("steers an accepted turn whose native id arrives after startTurn returns", async () => {
|
||||
const appServer = createFakeCodexAppServer({
|
||||
|
||||
@@ -3483,6 +3483,7 @@ export class CodexAppServerAgentSession implements AgentSession {
|
||||
private pendingReasoning = new Map<string, string[]>();
|
||||
private pendingCommandOutputDeltas = new Map<string, string[]>();
|
||||
private pendingFileChangeOutputDeltas = new Map<string, string[]>();
|
||||
private pendingToolCallDetails = new Map<string, ToolCallTimelineItem["detail"]>();
|
||||
private pendingAssistantMessageBoundary = false;
|
||||
private terminalCommandByProcessId = new Map<string, string>();
|
||||
// The root thread's still-running command process ids, keyed by the native turn that
|
||||
@@ -5251,6 +5252,7 @@ export class CodexAppServerAgentSession implements AgentSession {
|
||||
async close(): Promise<void> {
|
||||
this.closed = true;
|
||||
this.clearPendingPermissions();
|
||||
this.pendingToolCallDetails.clear();
|
||||
this.pendingSubAgentNotificationsByThreadId.clear();
|
||||
this.subscribers.clear();
|
||||
this.startupEvents = null;
|
||||
@@ -6416,6 +6418,7 @@ export class CodexAppServerAgentSession implements AgentSession {
|
||||
this.pendingReasoning.clear();
|
||||
this.pendingCommandOutputDeltas.clear();
|
||||
this.pendingFileChangeOutputDeltas.clear();
|
||||
this.pendingToolCallDetails.clear();
|
||||
this.pendingAssistantMessageBoundary = false;
|
||||
this.warnedIncompleteEditToolCallIds.clear();
|
||||
this.pendingRootCompactionItemIds.clear();
|
||||
@@ -6789,6 +6792,9 @@ export class CodexAppServerAgentSession implements AgentSession {
|
||||
if (shouldIgnoreMirroredLifecycleItem(parsed.source, parsed.item)) {
|
||||
return;
|
||||
}
|
||||
if (parsed.item.id) {
|
||||
this.pendingToolCallDetails.delete(parsed.item.id);
|
||||
}
|
||||
this.receiveAsyncQuestion(parsed.threadId, parsed.item);
|
||||
if (this.isUserMessageItem(parsed.item)) {
|
||||
await this.handleUserMessageItem(parsed);
|
||||
@@ -6984,6 +6990,7 @@ export class CodexAppServerAgentSession implements AgentSession {
|
||||
if (!timelineItem || timelineItem.type !== "tool_call") {
|
||||
return;
|
||||
}
|
||||
this.pendingToolCallDetails.set(timelineItem.callId, timelineItem.detail);
|
||||
const registeredChildThreadIds = this.registerSubAgentToolCall({
|
||||
timelineItem,
|
||||
rawItem: parsed.item,
|
||||
@@ -7270,7 +7277,7 @@ export class CodexAppServerAgentSession implements AgentSession {
|
||||
kind: "tool",
|
||||
title: "Apply file changes",
|
||||
description: parsed.reason ?? undefined,
|
||||
detail: {
|
||||
detail: this.pendingToolCallDetails.get(parsed.itemId) ?? {
|
||||
type: "unknown",
|
||||
input: {
|
||||
reason: parsed.reason ?? null,
|
||||
|
||||
Reference in New Issue
Block a user