diff --git a/src/plugins/conversation-binding.test.ts b/src/plugins/conversation-binding.test.ts index 5b966739cb8..ba2f4510c90 100644 --- a/src/plugins/conversation-binding.test.ts +++ b/src/plugins/conversation-binding.test.ts @@ -443,4 +443,61 @@ describe("plugin conversation binding approvals", () => { }), ); }); + + it("migrates a legacy codex thread binding session key through the new approval flow", async () => { + sessionBindingState.setRecord({ + bindingId: "binding-legacy-codex-thread", + targetSessionKey: "openclaw-app-server:thread:019ce411-6322-7db2-a821-1a61c530e7d9", + targetKind: "session", + conversation: { + channel: "telegram", + accountId: "default", + conversationId: "8460800771", + }, + status: "active", + boundAt: Date.now(), + metadata: { + label: "legacy codex thread bind", + }, + }); + + const request = await requestPluginConversationBinding({ + pluginId: "openclaw-codex-app-server", + pluginName: "Codex App Server", + pluginRoot: "/plugins/codex-a", + requestedBySenderId: "user-1", + conversation: { + channel: "telegram", + accountId: "default", + conversationId: "8460800771", + }, + binding: { + summary: "Bind this conversation to Codex thread 019ce411-6322-7db2-a821-1a61c530e7d9.", + }, + }); + + expect(["pending", "bound"]).toContain(request.status); + const binding = + request.status === "pending" + ? await resolvePluginConversationBindingApproval({ + approvalId: request.approvalId, + decision: "allow-once", + senderId: "user-1", + }).then((approved) => { + expect(approved.status).toBe("approved"); + if (approved.status !== "approved") { + throw new Error("expected approved bind result"); + } + return approved.binding; + }) + : request.binding; + + expect(binding).toEqual( + expect.objectContaining({ + pluginId: "openclaw-codex-app-server", + pluginRoot: "/plugins/codex-a", + conversationId: "8460800771", + }), + ); + }); }); diff --git a/src/plugins/conversation-binding.ts b/src/plugins/conversation-binding.ts index 5861de814f8..b454d58864c 100644 --- a/src/plugins/conversation-binding.ts +++ b/src/plugins/conversation-binding.ts @@ -23,6 +23,10 @@ const APPROVALS_PATH = "~/.openclaw/plugin-binding-approvals.json"; const PLUGIN_BINDING_CUSTOM_ID_PREFIX = "pluginbind"; const PLUGIN_BINDING_OWNER = "plugin"; const PLUGIN_BINDING_SESSION_PREFIX = "plugin-binding"; +const LEGACY_CODEX_PLUGIN_SESSION_PREFIXES = [ + "openclaw-app-server:thread:", + "openclaw-codex-app-server:thread:", +] as const; type PluginBindingApprovalDecision = "allow-once" | "allow-always" | "deny"; @@ -209,7 +213,10 @@ function isLegacyPluginBindingRecord(params: { return false; } const targetSessionKey = params.record.targetSessionKey.trim(); - return targetSessionKey.startsWith(`${PLUGIN_BINDING_SESSION_PREFIX}:`); + return ( + targetSessionKey.startsWith(`${PLUGIN_BINDING_SESSION_PREFIX}:`) || + LEGACY_CODEX_PLUGIN_SESSION_PREFIXES.some((prefix) => targetSessionKey.startsWith(prefix)) + ); } function buildDiscordButtonRow(