From f80fd278a5bcfe090e3d061f7ce94f66088a57dc Mon Sep 17 00:00:00 2001 From: root Date: Mon, 1 Jun 2026 18:08:34 +1000 Subject: [PATCH] feat(db): conversations.findOrCreateForSpace for the ambient companion Co-Authored-By: Claude Opus 4.8 --- lib/db/repos/conversations.js | 17 ++++++++++++++++ tests/db/conversations_companion.test.js | 26 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/db/conversations_companion.test.js diff --git a/lib/db/repos/conversations.js b/lib/db/repos/conversations.js index 2ad1d39..9348b33 100644 --- a/lib/db/repos/conversations.js +++ b/lib/db/repos/conversations.js @@ -42,3 +42,20 @@ export async function setSummary(id, summary) { ); return r; } + +export async function findOrCreateForSpace(space_id, agent_id, actor) { + const { rows: [existing] } = await pool.query( + `SELECT * FROM conversations + WHERE space_id=$1 AND agent_id=$2 AND status='open' + ORDER BY started_at DESC LIMIT 1`, + [space_id, agent_id] + ); + if (existing) return existing; + const { rows: [r] } = await pool.query( + `INSERT INTO conversations(title, space_id, agent_id, metadata) + VALUES($1,$2,$3,$4) RETURNING *`, + ['Companion', space_id, agent_id, {}] + ); + await recordAudit(actor, 'create', 'conversation', r.id, null, r); + return r; +} diff --git a/tests/db/conversations_companion.test.js b/tests/db/conversations_companion.test.js new file mode 100644 index 0000000..a62e025 --- /dev/null +++ b/tests/db/conversations_companion.test.js @@ -0,0 +1,26 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { pool } from '../../lib/db/pool.js'; +import { resetDb } from '../helpers/db.js'; +import { migrateUp } from '../../lib/db/migrate.js'; +import * as conversations from '../../lib/db/repos/conversations.js'; + +const ACTOR = { kind: 'user', id: null }; +let spaceId, agentId; + +beforeAll(async () => { + await resetDb(); await migrateUp(); + ({ rows: [{ id: spaceId }] } = await pool.query( + `INSERT INTO spaces(slug,name) VALUES('s','S') RETURNING id`)); + ({ rows: [{ id: agentId }] } = await pool.query( + `SELECT id FROM agents WHERE slug='companion'`)); +}); + +describe('findOrCreateForSpace', () => { + it('creates once then returns the same row', async () => { + const a = await conversations.findOrCreateForSpace(spaceId, agentId, ACTOR); + const b = await conversations.findOrCreateForSpace(spaceId, agentId, ACTOR); + expect(a.id).toBe(b.id); + expect(a.space_id).toBe(spaceId); + expect(a.agent_id).toBe(agentId); + }); +});