- commit
- 625f808
- parent
- bb4d393
- author
- codex@macbookpro
- date
- 2026-03-30 17:12:03 +0800 CST
fix: ignore inactive remote conversation links
3 files changed,
+138,
-0
+79,
-0
1@@ -41,6 +41,7 @@ import {
2 routeBaaInstruction,
3 writeHttpResponse
4 } from "../dist/index.js";
5+import { observeRenewalConversation } from "../dist/renewal/conversations.js";
6
7 const BROWSER_REQUEST_WAITER_TIMEOUT_MS = 120_000;
8
9@@ -6615,6 +6616,84 @@ test("ConductorRuntime routes browser.final_message into live instruction ingest
10 }
11 });
12
13+test("observeRenewalConversation ignores inactive remote links and creates a new local conversation", async () => {
14+ const rootDir = mkdtempSync(join(tmpdir(), "baa-conductor-renewal-observe-active-link-"));
15+ const stateDir = join(rootDir, "state");
16+ const store = new ArtifactStore({
17+ artifactDir: join(stateDir, ARTIFACTS_DIRNAME),
18+ databasePath: join(stateDir, ARTIFACT_DB_FILENAME)
19+ });
20+
21+ try {
22+ const firstObservation = await observeRenewalConversation({
23+ assistantMessageId: "msg-renewal-observe-1",
24+ clientId: "firefox-chatgpt",
25+ observedAt: Date.UTC(2026, 2, 30, 12, 0, 0),
26+ pageTitle: "Renewal Thread",
27+ pageUrl: "https://chatgpt.com/c/conv-renewal-observe",
28+ platform: "chatgpt",
29+ remoteConversationId: "conv-renewal-observe",
30+ store
31+ });
32+
33+ assert.equal(firstObservation.created, true);
34+ assert.equal(firstObservation.resolvedBy, "new");
35+
36+ await store.upsertConversationLink({
37+ isActive: false,
38+ linkId: firstObservation.link.linkId,
39+ localConversationId: firstObservation.conversation.localConversationId,
40+ observedAt: firstObservation.link.observedAt,
41+ platform: "chatgpt",
42+ updatedAt: Date.UTC(2026, 2, 30, 12, 1, 0)
43+ });
44+
45+ assert.equal(
46+ await store.findConversationLinkByRemoteConversation("chatgpt", "conv-renewal-observe"),
47+ null
48+ );
49+
50+ const secondObservation = await observeRenewalConversation({
51+ assistantMessageId: "msg-renewal-observe-2",
52+ clientId: "firefox-chatgpt",
53+ observedAt: Date.UTC(2026, 2, 30, 12, 2, 0),
54+ pageTitle: "Renewal Thread Reopened",
55+ pageUrl: "https://chatgpt.com/c/conv-renewal-observe",
56+ platform: "chatgpt",
57+ remoteConversationId: "conv-renewal-observe",
58+ store
59+ });
60+
61+ assert.equal(secondObservation.created, true);
62+ assert.equal(secondObservation.resolvedBy, "new");
63+ assert.notEqual(
64+ secondObservation.conversation.localConversationId,
65+ firstObservation.conversation.localConversationId
66+ );
67+ assert.equal(secondObservation.link.linkId, firstObservation.link.linkId);
68+ assert.equal(secondObservation.link.localConversationId, secondObservation.conversation.localConversationId);
69+ assert.equal(secondObservation.link.isActive, true);
70+ assert.deepEqual(
71+ await store.findConversationLinkByRemoteConversation("chatgpt", "conv-renewal-observe"),
72+ secondObservation.link
73+ );
74+
75+ const originalConversation = await store.getLocalConversation(firstObservation.conversation.localConversationId);
76+ assert.ok(originalConversation);
77+ assert.equal(originalConversation.lastMessageId, "msg-renewal-observe-1");
78+
79+ const replacementConversation = await store.getLocalConversation(secondObservation.conversation.localConversationId);
80+ assert.ok(replacementConversation);
81+ assert.equal(replacementConversation.lastMessageId, "msg-renewal-observe-2");
82+ } finally {
83+ store.close();
84+ rmSync(rootDir, {
85+ force: true,
86+ recursive: true
87+ });
88+ }
89+});
90+
91 test("ConductorRuntime persists renewal conversation links from browser.final_message and exposes renewal controls", async () => {
92 const stateDir = mkdtempSync(join(tmpdir(), "baa-conductor-renewal-control-"));
93 const runtime = new ConductorRuntime(
+58,
-0
1@@ -319,3 +319,61 @@ test("ArtifactStore persists renewal storage records and enqueues sync payloads"
2 });
3 }
4 });
5+
6+test("ArtifactStore findConversationLinkByRemoteConversation only returns active links", async () => {
7+ const rootDir = mkdtempSync(join(tmpdir(), "artifact-db-renewal-active-link-test-"));
8+ const stateDir = join(rootDir, "state");
9+ const databasePath = join(stateDir, ARTIFACT_DB_FILENAME);
10+ const artifactDir = join(stateDir, ARTIFACTS_DIRNAME);
11+ const store = new ArtifactStore({
12+ artifactDir,
13+ databasePath
14+ });
15+
16+ try {
17+ await store.upsertLocalConversation({
18+ localConversationId: "lc_active_link_123",
19+ platform: "chatgpt"
20+ });
21+ const activeLink = await store.upsertConversationLink({
22+ linkId: "link_active_link_123",
23+ localConversationId: "lc_active_link_123",
24+ observedAt: Date.UTC(2026, 2, 30, 8, 0, 0),
25+ platform: "chatgpt",
26+ remoteConversationId: "conv_active_link_123"
27+ });
28+
29+ assert.deepEqual(
30+ await store.findConversationLinkByRemoteConversation("chatgpt", "conv_active_link_123"),
31+ activeLink
32+ );
33+
34+ const inactiveLink = await store.upsertConversationLink({
35+ isActive: false,
36+ linkId: activeLink.linkId,
37+ localConversationId: activeLink.localConversationId,
38+ observedAt: activeLink.observedAt,
39+ platform: activeLink.platform,
40+ updatedAt: Date.UTC(2026, 2, 30, 8, 1, 0)
41+ });
42+
43+ assert.deepEqual(
44+ await store.listConversationLinks({
45+ isActive: false,
46+ platform: "chatgpt",
47+ remoteConversationId: "conv_active_link_123"
48+ }),
49+ [inactiveLink]
50+ );
51+ assert.equal(
52+ await store.findConversationLinkByRemoteConversation("chatgpt", "conv_active_link_123"),
53+ null
54+ );
55+ } finally {
56+ store.close();
57+ rmSync(rootDir, {
58+ force: true,
59+ recursive: true
60+ });
61+ }
62+});
+1,
-0
1@@ -478,6 +478,7 @@ export class ArtifactStore {
2 FROM conversation_links
3 WHERE platform = ?
4 AND remote_conversation_id = ?
5+ AND is_active = 1
6 LIMIT 1;
7 `,
8 normalizeRequiredString(platform, "platform"),