From e8dfc8f392906a27477c36715f804880834eb357 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 4 Jun 2026 21:04:31 +1000 Subject: [PATCH] feat(agents): conversations.findOrCreateGlobal for space-less agents Co-Authored-By: Claude Opus 4.8 --- lib/db/repos/conversations.js | 16 ++++++++++++++++ tests/db/conversations_global.test.js | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/db/conversations_global.test.js diff --git a/lib/db/repos/conversations.js b/lib/db/repos/conversations.js index 9348b33..a42d54c 100644 --- a/lib/db/repos/conversations.js +++ b/lib/db/repos/conversations.js @@ -43,6 +43,22 @@ export async function setSummary(id, summary) { return r; } +export async function findOrCreateGlobal(agent_id, actor) { + const { rows: [existing] } = await pool.query( + `SELECT * FROM conversations + WHERE agent_id=$1 AND space_id IS NULL AND status='open' + ORDER BY started_at DESC LIMIT 1`, + [agent_id] + ); + if (existing) return existing; + const { rows: [r] } = await pool.query( + `INSERT INTO conversations(title, agent_id, metadata) VALUES($1,$2,$3) RETURNING *`, + [null, agent_id, {}] + ); + await recordAudit(actor, 'create', 'conversation', r.id, null, r); + return r; +} + export async function findOrCreateForSpace(space_id, agent_id, actor) { const { rows: [existing] } = await pool.query( `SELECT * FROM conversations diff --git a/tests/db/conversations_global.test.js b/tests/db/conversations_global.test.js new file mode 100644 index 0000000..006b9dc --- /dev/null +++ b/tests/db/conversations_global.test.js @@ -0,0 +1,19 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { resetDb } from '../helpers/db.js'; +import { migrateUp } from '../../lib/db/migrate.js'; +import * as agents from '../../lib/db/repos/agents.js'; +import * as conversations from '../../lib/db/repos/conversations.js'; + +const owner = { kind: 'user', id: null }; +beforeAll(async () => { await resetDb(); await migrateUp(); }); + +describe('findOrCreateGlobal', () => { + it('creates one space-less open conversation and reuses it', async () => { + const y = await agents.getBySlug('yerin'); // seeded by 011_yerin.sql + const c1 = await conversations.findOrCreateGlobal(y.id, owner); + expect(c1.space_id).toBeNull(); + expect(c1.agent_id).toBe(y.id); + const c2 = await conversations.findOrCreateGlobal(y.id, owner); + expect(c2.id).toBe(c1.id); + }); +});