mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-02 22:52:58 +00:00
test(codex): express patch approval through provider actions
Keep protocol framing in the fake app-server and precompute the approval title. Streamed completion already requires an item ID; expose that narrowing so completion cleanup stays inline without repeating its guard.
This commit is contained in:
@@ -236,21 +236,13 @@ describe("Codex file-change approvals", () => {
|
||||
["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"],
|
||||
]) {
|
||||
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`,
|
||||
);
|
||||
appServer.startsFileChange({
|
||||
threadId: "thread-1",
|
||||
turnId: "native-A",
|
||||
callId: id,
|
||||
path: filePath,
|
||||
diff,
|
||||
});
|
||||
}
|
||||
const permission = waitForNextPermission(session);
|
||||
appServer.requestFileChangeApproval({
|
||||
|
||||
@@ -6781,10 +6781,6 @@ export class CodexAppServerAgentSession implements AgentSession {
|
||||
return !this.closed && this.activeForegroundTurnId === turnId;
|
||||
}
|
||||
|
||||
private forgetPendingToolCallDetail(itemId: string | null | undefined): void {
|
||||
if (itemId) this.pendingToolCallDetails.delete(itemId);
|
||||
}
|
||||
|
||||
private async handleItemCompletedNotification(
|
||||
parsed: Extract<ParsedCodexNotification, { kind: "item_completed" }>,
|
||||
): Promise<void> {
|
||||
@@ -6796,7 +6792,9 @@ export class CodexAppServerAgentSession implements AgentSession {
|
||||
if (shouldIgnoreMirroredLifecycleItem(parsed.source, parsed.item)) {
|
||||
return;
|
||||
}
|
||||
this.forgetPendingToolCallDetail(parsed.item.id);
|
||||
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);
|
||||
@@ -6844,10 +6842,8 @@ export class CodexAppServerAgentSession implements AgentSession {
|
||||
if (timelineItem.type === "assistant_message") {
|
||||
this.pendingAssistantMessageBoundary = true;
|
||||
}
|
||||
if (itemId) {
|
||||
this.emittedItemCompletedIds.add(itemId);
|
||||
this.emittedItemStartedIds.delete(itemId);
|
||||
}
|
||||
this.emittedItemCompletedIds.add(itemId);
|
||||
this.emittedItemStartedIds.delete(itemId);
|
||||
await this.replayPendingSubAgentNotifications(registeredChildThreadIds);
|
||||
return;
|
||||
}
|
||||
@@ -6883,7 +6879,7 @@ export class CodexAppServerAgentSession implements AgentSession {
|
||||
private consumeStreamedTextCompletion(
|
||||
timelineItem: AgentTimelineItem,
|
||||
itemId: string | null | undefined,
|
||||
): boolean {
|
||||
): itemId is string {
|
||||
if (!itemId) {
|
||||
return false;
|
||||
}
|
||||
@@ -7280,15 +7276,16 @@ export class CodexAppServerAgentSession implements AgentSession {
|
||||
input: { reason: parsed.reason ?? null },
|
||||
output: null,
|
||||
};
|
||||
const title =
|
||||
detail.type === "edit" && detail.filePath
|
||||
? `Apply file changes: ${detail.filePath}`
|
||||
: "Apply file changes";
|
||||
const request: AgentPermissionRequest = {
|
||||
id: requestId,
|
||||
provider: CODEX_PROVIDER,
|
||||
name: "CodexFileChange",
|
||||
kind: "tool",
|
||||
title:
|
||||
detail.type === "edit" && detail.filePath
|
||||
? `Apply file changes: ${detail.filePath}`
|
||||
: "Apply file changes",
|
||||
title,
|
||||
description: parsed.reason ?? undefined,
|
||||
detail,
|
||||
metadata: {
|
||||
|
||||
@@ -43,6 +43,13 @@ interface FakeTerminalInput {
|
||||
processId: string;
|
||||
text: string;
|
||||
}
|
||||
interface FakeFileChange {
|
||||
threadId: string;
|
||||
turnId: string;
|
||||
callId: string;
|
||||
path: string;
|
||||
diff: string;
|
||||
}
|
||||
interface FakeLegacyPatch {
|
||||
threadId: string;
|
||||
callId: string;
|
||||
@@ -88,6 +95,7 @@ export interface FakeCodexAppServer {
|
||||
completesSubAgentActivity(params: FakeSubAgentActivity): void;
|
||||
completesCompaction(params: { threadId: string; itemId: string }): void;
|
||||
runsLegacyCommand(params: FakeLegacyCommand): void;
|
||||
startsFileChange(params: FakeFileChange): void;
|
||||
appliesLegacyPatch(params: FakeLegacyPatch): void;
|
||||
completesCommand(params: FakeLegacyCommand): void;
|
||||
completesSilentCommand(params: FakeSilentCommand): void;
|
||||
@@ -467,6 +475,20 @@ export function createFakeCodexAppServer(
|
||||
success: true,
|
||||
});
|
||||
},
|
||||
startsFileChange(params) {
|
||||
writeNotification("item/started", {
|
||||
threadId: params.threadId,
|
||||
turnId: params.turnId,
|
||||
item: {
|
||||
type: "fileChange",
|
||||
id: params.callId,
|
||||
status: "inProgress",
|
||||
changes: [
|
||||
{ path: params.path, kind: { type: "update", move_path: null }, diff: params.diff },
|
||||
],
|
||||
},
|
||||
});
|
||||
},
|
||||
appliesLegacyPatch(params) {
|
||||
const changes = [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user