Skip to content

Commit 9f888d9

Browse files
committed
fix(deadcode): move current conversation bindings to sqlite
1 parent 11a2e03 commit 9f888d9

6 files changed

Lines changed: 711 additions & 133 deletions

src/commands/doctor.e2e-harness.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,10 @@ function createLegacyStateMigrationDetectionResult(params?: {
251251
sourcePath: "/tmp/state/update-check.json",
252252
hasLegacy: false,
253253
},
254+
currentConversationBindings: {
255+
sourcePath: "/tmp/state/bindings/current-conversations.json",
256+
hasLegacy: false,
257+
},
254258
execApprovals: {
255259
sourcePath: "/tmp/state/exec-approvals.legacy.json",
256260
targetPath: "/tmp/state/exec-approvals.json",

src/infra/outbound/current-conversation-bindings.test.ts

Lines changed: 90 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import os from "node:os";
55
import path from "node:path";
66
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
77
import { setActivePluginRegistry } from "../../plugins/runtime.js";
8+
import type { DB as OpenClawStateKyselyDatabase } from "../../state/openclaw-state-db.generated.js";
9+
import {
10+
closeOpenClawStateDatabaseForTest,
11+
runOpenClawStateWriteTransaction,
12+
} from "../../state/openclaw-state-db.js";
813
import { createTestRegistry } from "../../test-utils/channel-plugins.js";
14+
import { executeSqliteQuerySync, getNodeSqliteKysely } from "../kysely-sync.js";
915
import {
1016
testing,
1117
bindGenericCurrentConversation,
@@ -17,6 +23,11 @@ import {
1723
} from "./current-conversation-bindings.js";
1824
import type { SessionBindingRecord } from "./session-binding.types.js";
1925

26+
type CurrentConversationBindingDatabase = Pick<
27+
OpenClawStateKyselyDatabase,
28+
"current_conversation_bindings"
29+
>;
30+
2031
function expectSessionBinding(bound: SessionBindingRecord | null): SessionBindingRecord {
2132
if (bound === null) {
2233
throw new Error("Expected current-conversation binding");
@@ -45,6 +56,40 @@ function expectBindingMetadata(
4556
}
4657
}
4758

59+
function buildConversationKey(ref: SessionBindingRecord["conversation"]): string {
60+
return [ref.channel, ref.accountId, ref.parentConversationId ?? "", ref.conversationId].join(
61+
"\u241f",
62+
);
63+
}
64+
65+
function seedPersistedBinding(record: SessionBindingRecord): void {
66+
runOpenClawStateWriteTransaction(({ db }) => {
67+
const bindingDb = getNodeSqliteKysely<CurrentConversationBindingDatabase>(db);
68+
executeSqliteQuerySync(
69+
db,
70+
bindingDb.insertInto("current_conversation_bindings").values({
71+
binding_key: buildConversationKey(record.conversation),
72+
binding_id: record.bindingId,
73+
target_agent_id: "codex",
74+
target_session_id: null,
75+
target_session_key: record.targetSessionKey,
76+
channel: record.conversation.channel,
77+
account_id: record.conversation.accountId,
78+
conversation_kind: "current",
79+
parent_conversation_id: record.conversation.parentConversationId ?? null,
80+
conversation_id: record.conversation.conversationId,
81+
target_kind: record.targetKind,
82+
status: record.status,
83+
bound_at: record.boundAt,
84+
expires_at: record.expiresAt ?? null,
85+
metadata_json: record.metadata ? JSON.stringify(record.metadata) : null,
86+
record_json: JSON.stringify(record),
87+
updated_at: record.boundAt,
88+
}),
89+
);
90+
});
91+
}
92+
4893
function setMinimalCurrentConversationRegistry(): void {
4994
setActivePluginRegistry(
5095
createTestRegistry([
@@ -82,6 +127,7 @@ describe("generic current-conversation bindings", () => {
82127
testing.resetCurrentConversationBindingsForTests({
83128
deletePersistedFile: true,
84129
});
130+
closeOpenClawStateDatabaseForTest();
85131
if (previousStateDir == null) {
86132
delete process.env.OPENCLAW_STATE_DIR;
87133
} else {
@@ -155,31 +201,21 @@ describe("generic current-conversation bindings", () => {
155201
});
156202

157203
it("normalizes persisted target session keys on reload", async () => {
158-
const filePath = testing.resolveBindingsFilePath();
159-
await fs.mkdir(path.dirname(filePath), { recursive: true });
160-
await fs.writeFile(
161-
filePath,
162-
JSON.stringify({
163-
version: 1,
164-
bindings: [
165-
{
166-
bindingId: "generic:workspace\u241fdefault\u241f\u241fuser:U123",
167-
targetSessionKey: " agent:codex:acp:workspace-dm ",
168-
targetKind: "session",
169-
conversation: {
170-
channel: "workspace",
171-
accountId: "default",
172-
conversationId: "user:U123",
173-
},
174-
status: "active",
175-
boundAt: 1234,
176-
metadata: {
177-
label: "workspace-dm",
178-
},
179-
},
180-
],
181-
}),
182-
);
204+
seedPersistedBinding({
205+
bindingId: "generic:workspace\u241fdefault\u241f\u241fuser:U123",
206+
targetSessionKey: " agent:codex:acp:workspace-dm ",
207+
targetKind: "session",
208+
conversation: {
209+
channel: "workspace",
210+
accountId: "default",
211+
conversationId: "user:U123",
212+
},
213+
status: "active",
214+
boundAt: 1234,
215+
metadata: {
216+
label: "workspace-dm",
217+
},
218+
});
183219

184220
const resolved = resolveGenericCurrentConversationBinding({
185221
channel: "workspace",
@@ -237,32 +273,22 @@ describe("generic current-conversation bindings", () => {
237273
});
238274

239275
it("migrates persisted legacy self-parent binding ids on load", async () => {
240-
const filePath = testing.resolveBindingsFilePath();
241-
await fs.mkdir(path.dirname(filePath), { recursive: true });
242-
await fs.writeFile(
243-
filePath,
244-
JSON.stringify({
245-
version: 1,
246-
bindings: [
247-
{
248-
bindingId: "generic:forum\u241fdefault\u241f6098642967\u241f6098642967",
249-
targetSessionKey: "agent:codex:acp:forum-dm",
250-
targetKind: "session",
251-
conversation: {
252-
channel: "forum",
253-
accountId: "default",
254-
conversationId: "6098642967",
255-
parentConversationId: "6098642967",
256-
},
257-
status: "active",
258-
boundAt: 1234,
259-
metadata: {
260-
label: "forum-dm",
261-
},
262-
},
263-
],
264-
}),
265-
);
276+
seedPersistedBinding({
277+
bindingId: "generic:forum\u241fdefault\u241f6098642967\u241f6098642967",
278+
targetSessionKey: "agent:codex:acp:forum-dm",
279+
targetKind: "session",
280+
conversation: {
281+
channel: "forum",
282+
accountId: "default",
283+
conversationId: "6098642967",
284+
parentConversationId: "6098642967",
285+
},
286+
status: "active",
287+
boundAt: 1234,
288+
metadata: {
289+
label: "forum-dm",
290+
},
291+
});
266292

267293
const resolved = resolveGenericCurrentConversationBinding({
268294
channel: "forum",
@@ -328,29 +354,19 @@ describe("generic current-conversation bindings", () => {
328354
});
329355

330356
it("drops persisted bindings with invalid expiration timestamps", async () => {
331-
const filePath = testing.resolveBindingsFilePath();
332-
await fs.mkdir(path.dirname(filePath), { recursive: true });
333-
await fs.writeFile(
334-
filePath,
335-
JSON.stringify({
336-
version: 1,
337-
bindings: [
338-
{
339-
bindingId: "generic:workspace\u241fdefault\u241f\u241fuser:U123",
340-
targetSessionKey: "agent:codex:acp:workspace-dm",
341-
targetKind: "session",
342-
conversation: {
343-
channel: "workspace",
344-
accountId: "default",
345-
conversationId: "user:U123",
346-
},
347-
status: "active",
348-
boundAt: 1234,
349-
expiresAt: 8_640_000_000_000_001,
350-
},
351-
],
352-
}),
353-
);
357+
seedPersistedBinding({
358+
bindingId: "generic:workspace\u241fdefault\u241f\u241fuser:U123",
359+
targetSessionKey: "agent:codex:acp:workspace-dm",
360+
targetKind: "session",
361+
conversation: {
362+
channel: "workspace",
363+
accountId: "default",
364+
conversationId: "user:U123",
365+
},
366+
status: "active",
367+
boundAt: 1234,
368+
expiresAt: 8_640_000_000_000_001,
369+
});
354370

355371
expect(
356372
resolveGenericCurrentConversationBinding({

0 commit comments

Comments
 (0)