refactor: complete room registration ssot cleanup
This commit is contained in:
352
src/db.test.ts
352
src/db.test.ts
@@ -7,6 +7,7 @@ import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import {
|
||||
_initTestDatabase,
|
||||
_initTestDatabaseFromFile,
|
||||
_deleteStoredRoomSettingsForTests,
|
||||
_setMemoryTimestampsForTests,
|
||||
_setRegisteredGroupForTests,
|
||||
assignRoom,
|
||||
@@ -30,6 +31,7 @@ import {
|
||||
getLatestPairedTaskForChat,
|
||||
getLatestTurnNumber,
|
||||
getLastRespondingAgentType,
|
||||
getRegisteredGroup,
|
||||
getMessagesSinceSeq,
|
||||
getNewMessagesBySeq,
|
||||
getOpenWorkItem,
|
||||
@@ -1658,6 +1660,326 @@ describe('room assignment writes', () => {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('recreates inferred room_settings when renaming a legacy projection-only room', () => {
|
||||
_setRegisteredGroupForTests('dc:legacy-rename', {
|
||||
name: 'Legacy Rename',
|
||||
folder: 'legacy-rename',
|
||||
trigger: '@Andy',
|
||||
added_at: '2024-01-01T00:00:00.000Z',
|
||||
agentType: 'claude-code',
|
||||
});
|
||||
_setRegisteredGroupForTests('dc:legacy-rename', {
|
||||
name: 'Legacy Rename',
|
||||
folder: 'legacy-rename',
|
||||
trigger: '@Codex',
|
||||
added_at: '2024-01-01T00:00:00.000Z',
|
||||
agentType: 'codex',
|
||||
});
|
||||
|
||||
_deleteStoredRoomSettingsForTests('dc:legacy-rename');
|
||||
expect(getStoredRoomSettings('dc:legacy-rename')).toBeUndefined();
|
||||
|
||||
updateRegisteredGroupName('dc:legacy-rename', 'Legacy Rename Updated');
|
||||
|
||||
expect(getStoredRoomSettings('dc:legacy-rename')).toMatchObject({
|
||||
chatJid: 'dc:legacy-rename',
|
||||
roomMode: 'tribunal',
|
||||
modeSource: 'inferred',
|
||||
name: 'Legacy Rename Updated',
|
||||
folder: 'legacy-rename',
|
||||
});
|
||||
expect(
|
||||
getAllRegisteredGroups('claude-code')['dc:legacy-rename'],
|
||||
).toMatchObject({
|
||||
name: 'Legacy Rename Updated',
|
||||
agentType: 'claude-code',
|
||||
});
|
||||
expect(getAllRegisteredGroups('codex')['dc:legacy-rename']).toMatchObject({
|
||||
name: 'Legacy Rename Updated',
|
||||
agentType: 'codex',
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores stale registered_groups capability rows once room_settings exists', () => {
|
||||
const tempDir = fs.mkdtempSync('/tmp/ejclaw-room-ssot-');
|
||||
const dbPath = path.join(tempDir, 'messages.db');
|
||||
const legacyDb = new Database(dbPath);
|
||||
|
||||
legacyDb.exec(`
|
||||
CREATE TABLE room_settings (
|
||||
chat_jid TEXT PRIMARY KEY,
|
||||
room_mode TEXT NOT NULL DEFAULT 'single',
|
||||
mode_source TEXT NOT NULL DEFAULT 'explicit',
|
||||
name TEXT,
|
||||
folder TEXT,
|
||||
trigger_pattern TEXT,
|
||||
requires_trigger INTEGER,
|
||||
is_main INTEGER,
|
||||
owner_agent_type TEXT,
|
||||
work_dir TEXT,
|
||||
updated_at TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE registered_groups (
|
||||
jid TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
folder TEXT NOT NULL,
|
||||
trigger_pattern TEXT NOT NULL,
|
||||
added_at TEXT NOT NULL,
|
||||
agent_config TEXT,
|
||||
requires_trigger INTEGER,
|
||||
is_main INTEGER,
|
||||
agent_type TEXT,
|
||||
work_dir TEXT,
|
||||
PRIMARY KEY (jid, agent_type),
|
||||
UNIQUE (folder, agent_type)
|
||||
);
|
||||
`);
|
||||
|
||||
legacyDb
|
||||
.prepare(
|
||||
`INSERT INTO room_settings (
|
||||
chat_jid,
|
||||
room_mode,
|
||||
mode_source,
|
||||
name,
|
||||
folder,
|
||||
trigger_pattern,
|
||||
requires_trigger,
|
||||
is_main,
|
||||
owner_agent_type,
|
||||
work_dir,
|
||||
updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
)
|
||||
.run(
|
||||
'dc:ssot-room',
|
||||
'single',
|
||||
'explicit',
|
||||
'SSOT Room',
|
||||
'ssot-room',
|
||||
'@Andy',
|
||||
1,
|
||||
0,
|
||||
'codex',
|
||||
null,
|
||||
'2026-04-08T00:00:00.000Z',
|
||||
);
|
||||
|
||||
legacyDb
|
||||
.prepare(
|
||||
`INSERT INTO registered_groups (
|
||||
jid,
|
||||
name,
|
||||
folder,
|
||||
trigger_pattern,
|
||||
added_at,
|
||||
agent_config,
|
||||
requires_trigger,
|
||||
is_main,
|
||||
agent_type,
|
||||
work_dir
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
)
|
||||
.run(
|
||||
'dc:ssot-room',
|
||||
'Stale Projection',
|
||||
'ssot-room',
|
||||
'@Codex',
|
||||
'2026-04-08T00:00:00.000Z',
|
||||
null,
|
||||
1,
|
||||
0,
|
||||
'codex',
|
||||
null,
|
||||
);
|
||||
|
||||
legacyDb
|
||||
.prepare(
|
||||
`INSERT OR REPLACE INTO registered_groups (
|
||||
jid,
|
||||
name,
|
||||
folder,
|
||||
trigger_pattern,
|
||||
added_at,
|
||||
agent_config,
|
||||
requires_trigger,
|
||||
is_main,
|
||||
agent_type,
|
||||
work_dir
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
)
|
||||
.run(
|
||||
'dc:ssot-room',
|
||||
'Stale Projection',
|
||||
'ssot-room',
|
||||
'@Claude',
|
||||
'2026-04-08T00:00:00.000Z',
|
||||
null,
|
||||
1,
|
||||
0,
|
||||
'claude-code',
|
||||
null,
|
||||
);
|
||||
legacyDb.close();
|
||||
|
||||
_initTestDatabaseFromFile(dbPath);
|
||||
|
||||
expect(getRegisteredGroup('dc:ssot-room')).toMatchObject({
|
||||
folder: 'ssot-room',
|
||||
agentType: 'codex',
|
||||
});
|
||||
expect(getRegisteredGroup('dc:ssot-room', 'claude-code')).toBeUndefined();
|
||||
expect(getAllRegisteredGroups('claude-code')['dc:ssot-room']).toBeUndefined();
|
||||
expect(getRegisteredAgentTypesForJid('dc:ssot-room')).toEqual(['codex']);
|
||||
});
|
||||
|
||||
it('re-materializes explicit room_settings writes back into the projection rows', () => {
|
||||
const tempDir = fs.mkdtempSync('/tmp/ejclaw-room-writeback-');
|
||||
const dbPath = path.join(tempDir, 'messages.db');
|
||||
const legacyDb = new Database(dbPath);
|
||||
|
||||
legacyDb.exec(`
|
||||
CREATE TABLE room_settings (
|
||||
chat_jid TEXT PRIMARY KEY,
|
||||
room_mode TEXT NOT NULL DEFAULT 'single',
|
||||
mode_source TEXT NOT NULL DEFAULT 'explicit',
|
||||
name TEXT,
|
||||
folder TEXT,
|
||||
trigger_pattern TEXT,
|
||||
requires_trigger INTEGER,
|
||||
is_main INTEGER,
|
||||
owner_agent_type TEXT,
|
||||
work_dir TEXT,
|
||||
updated_at TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE registered_groups (
|
||||
jid TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
folder TEXT NOT NULL,
|
||||
trigger_pattern TEXT NOT NULL,
|
||||
added_at TEXT NOT NULL,
|
||||
agent_config TEXT,
|
||||
requires_trigger INTEGER,
|
||||
is_main INTEGER,
|
||||
agent_type TEXT,
|
||||
work_dir TEXT,
|
||||
PRIMARY KEY (jid, agent_type),
|
||||
UNIQUE (folder, agent_type)
|
||||
);
|
||||
`);
|
||||
|
||||
legacyDb
|
||||
.prepare(
|
||||
`INSERT INTO room_settings (
|
||||
chat_jid,
|
||||
room_mode,
|
||||
mode_source,
|
||||
name,
|
||||
folder,
|
||||
trigger_pattern,
|
||||
requires_trigger,
|
||||
is_main,
|
||||
owner_agent_type,
|
||||
work_dir,
|
||||
updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
)
|
||||
.run(
|
||||
'dc:ssot-writeback',
|
||||
'single',
|
||||
'explicit',
|
||||
'Explicit Writeback',
|
||||
'ssot-writeback',
|
||||
'@Codex',
|
||||
1,
|
||||
0,
|
||||
'codex',
|
||||
null,
|
||||
'2026-04-08T00:00:00.000Z',
|
||||
);
|
||||
|
||||
const insertProjection = legacyDb.prepare(
|
||||
`INSERT INTO registered_groups (
|
||||
jid,
|
||||
name,
|
||||
folder,
|
||||
trigger_pattern,
|
||||
added_at,
|
||||
agent_config,
|
||||
requires_trigger,
|
||||
is_main,
|
||||
agent_type,
|
||||
work_dir
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
);
|
||||
|
||||
insertProjection.run(
|
||||
'dc:ssot-writeback',
|
||||
'Stale Projection',
|
||||
'ssot-writeback',
|
||||
'@Codex',
|
||||
'2026-04-08T00:00:00.000Z',
|
||||
null,
|
||||
1,
|
||||
0,
|
||||
'codex',
|
||||
null,
|
||||
);
|
||||
insertProjection.run(
|
||||
'dc:ssot-writeback',
|
||||
'Stale Projection',
|
||||
'ssot-writeback',
|
||||
'@Claude',
|
||||
'2026-04-08T00:00:00.000Z',
|
||||
null,
|
||||
1,
|
||||
0,
|
||||
'claude-code',
|
||||
null,
|
||||
);
|
||||
legacyDb.close();
|
||||
|
||||
_initTestDatabaseFromFile(dbPath);
|
||||
|
||||
updateRegisteredGroupName('dc:ssot-writeback', 'SSOT Writeback Renamed');
|
||||
|
||||
expect(getStoredRoomSettings('dc:ssot-writeback')).toMatchObject({
|
||||
chatJid: 'dc:ssot-writeback',
|
||||
roomMode: 'single',
|
||||
modeSource: 'explicit',
|
||||
name: 'SSOT Writeback Renamed',
|
||||
ownerAgentType: 'codex',
|
||||
});
|
||||
|
||||
const rawDb = new Database(dbPath);
|
||||
const projectionRows = rawDb
|
||||
.prepare(
|
||||
`SELECT agent_type, name
|
||||
FROM registered_groups
|
||||
WHERE jid = ?
|
||||
ORDER BY agent_type`,
|
||||
)
|
||||
.all('dc:ssot-writeback') as Array<{
|
||||
agent_type: string | null;
|
||||
name: string;
|
||||
}>;
|
||||
rawDb.close();
|
||||
|
||||
expect(projectionRows).toEqual([
|
||||
{
|
||||
agent_type: 'codex',
|
||||
name: 'SSOT Writeback Renamed',
|
||||
},
|
||||
]);
|
||||
|
||||
clearExplicitRoomMode('dc:ssot-writeback');
|
||||
|
||||
expect(getExplicitRoomMode('dc:ssot-writeback')).toBeUndefined();
|
||||
expect(getEffectiveRoomMode('dc:ssot-writeback')).toBe('single');
|
||||
});
|
||||
});
|
||||
|
||||
describe('paired room registration', () => {
|
||||
@@ -1938,6 +2260,36 @@ describe('paired room registration', () => {
|
||||
expect(getEffectiveRuntimeRoomMode('dc:explicit-single')).toBe('single');
|
||||
});
|
||||
|
||||
it('restores inferred paired mode when clearing an explicit single override', () => {
|
||||
_setRegisteredGroupForTests('dc:explicit-single-clear', {
|
||||
name: 'Explicit Single Clear',
|
||||
folder: 'explicit-single-clear',
|
||||
trigger: '@Andy',
|
||||
added_at: '2024-01-01T00:00:00.000Z',
|
||||
agentType: 'claude-code',
|
||||
});
|
||||
_setRegisteredGroupForTests('dc:explicit-single-clear', {
|
||||
name: 'Explicit Single Clear',
|
||||
folder: 'explicit-single-clear',
|
||||
trigger: '@Codex',
|
||||
added_at: '2024-01-01T00:00:00.000Z',
|
||||
agentType: 'codex',
|
||||
});
|
||||
|
||||
setExplicitRoomMode('dc:explicit-single-clear', 'single');
|
||||
|
||||
expect(getExplicitRoomMode('dc:explicit-single-clear')).toBe('single');
|
||||
expect(getEffectiveRoomMode('dc:explicit-single-clear')).toBe('single');
|
||||
|
||||
clearExplicitRoomMode('dc:explicit-single-clear');
|
||||
|
||||
expect(getExplicitRoomMode('dc:explicit-single-clear')).toBeUndefined();
|
||||
expect(getEffectiveRoomMode('dc:explicit-single-clear')).toBe('tribunal');
|
||||
expect(getEffectiveRuntimeRoomMode('dc:explicit-single-clear')).toBe(
|
||||
'tribunal',
|
||||
);
|
||||
});
|
||||
|
||||
it('lets explicit tribunal become runnable when the configured reviewer can run on the solo registration', () => {
|
||||
_setRegisteredGroupForTests('dc:explicit-tribunal', {
|
||||
name: 'Explicit Tribunal Claude',
|
||||
|
||||
170
src/db.ts
170
src/db.ts
@@ -39,6 +39,7 @@ import {
|
||||
normalizeRoomModeSource,
|
||||
normalizeStoredAgentType,
|
||||
parseRegisteredGroupRow,
|
||||
resolveStoredRoomCapabilityTypes,
|
||||
resolveAssignedRoomFolder,
|
||||
updateStoredRoomMetadata,
|
||||
} from './db/room-registration.js';
|
||||
@@ -313,6 +314,14 @@ export function _setStoredRoomOwnerAgentTypeForTests(
|
||||
).run(ownerAgentType, new Date().toISOString(), chatJid);
|
||||
}
|
||||
|
||||
/** @internal - for tests only. */
|
||||
export function _deleteStoredRoomSettingsForTests(chatJid: string): void {
|
||||
if (!db) {
|
||||
throw new Error('Database not initialized');
|
||||
}
|
||||
db.prepare('DELETE FROM room_settings WHERE chat_jid = ?').run(chatJid);
|
||||
}
|
||||
|
||||
/** @internal - for tests only. */
|
||||
export function _setMemoryTimestampsForTests(
|
||||
id: number,
|
||||
@@ -749,12 +758,7 @@ export function getRegisteredGroup(
|
||||
const requestedAgentType = normalizeStoredAgentType(agentType);
|
||||
const stored = getStoredRoomSettingsRowFromDatabase(db, jid);
|
||||
if (stored) {
|
||||
const group = buildRegisteredGroupFromStoredSettings(
|
||||
db,
|
||||
stored,
|
||||
requestedAgentType,
|
||||
);
|
||||
if (group) return group;
|
||||
return buildRegisteredGroupFromStoredSettings(db, stored, requestedAgentType);
|
||||
}
|
||||
return getLegacyRegisteredGroup(db, jid, agentType);
|
||||
}
|
||||
@@ -763,31 +767,58 @@ function writeLegacyRegisteredGroupAndSyncRoomSettings(
|
||||
jid: string,
|
||||
group: RegisteredGroup,
|
||||
): void {
|
||||
const existingStored = getStoredRoomSettingsRowFromDatabase(db, jid);
|
||||
const existingRoomMode = getStoredRoomModeRow(jid);
|
||||
if (!isValidGroupFolder(group.folder)) {
|
||||
throw new Error(`Invalid group folder "${group.folder}" for JID ${jid}`);
|
||||
}
|
||||
const tx = db.transaction(() => {
|
||||
db.prepare(
|
||||
`INSERT OR REPLACE INTO registered_groups (jid, name, folder, trigger_pattern, added_at, agent_config, requires_trigger, is_main, agent_type, work_dir)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
).run(
|
||||
jid,
|
||||
group.name,
|
||||
group.folder,
|
||||
group.trigger,
|
||||
group.added_at,
|
||||
group.agentConfig ? JSON.stringify(group.agentConfig) : null,
|
||||
group.requiresTrigger === undefined ? 1 : group.requiresTrigger ? 1 : 0,
|
||||
group.isMain ? 1 : 0,
|
||||
group.agentType || 'claude-code',
|
||||
group.workDir || null,
|
||||
);
|
||||
const seededAgentType = group.agentType || 'claude-code';
|
||||
const agentTypes = new Set<AgentType>(collectRegisteredAgentTypes(db, jid));
|
||||
agentTypes.add(seededAgentType);
|
||||
const inferredRoomMode = inferRoomModeFromRegisteredAgentTypes([
|
||||
...agentTypes,
|
||||
]);
|
||||
const roomMode =
|
||||
existingRoomMode?.source === 'explicit'
|
||||
? existingRoomMode.roomMode
|
||||
: inferredRoomMode;
|
||||
const ownerAgentType =
|
||||
existingStored?.modeSource === 'explicit' && existingStored.ownerAgentType
|
||||
? existingStored.ownerAgentType
|
||||
: inferOwnerAgentTypeFromRegisteredAgentTypes([...agentTypes]);
|
||||
const snapshot: RoomRegistrationSnapshot = {
|
||||
name: group.name,
|
||||
folder: group.folder,
|
||||
triggerPattern:
|
||||
existingStored?.modeSource === 'explicit' && existingStored.trigger
|
||||
? existingStored.trigger
|
||||
: group.trigger,
|
||||
requiresTrigger:
|
||||
group.requiresTrigger ?? existingStored?.requiresTrigger ?? true,
|
||||
isMain: group.isMain ?? existingStored?.isMain ?? false,
|
||||
ownerAgentType,
|
||||
workDir: group.workDir ?? existingStored?.workDir ?? null,
|
||||
};
|
||||
|
||||
syncStoredRoomRegistrationSnapshotForJid(jid);
|
||||
if (!existingRoomMode || existingRoomMode.source === 'inferred') {
|
||||
syncInferredRoomModeForJid(jid);
|
||||
if (existingStored) {
|
||||
updateStoredRoomMetadata(db, jid, snapshot);
|
||||
if (!existingRoomMode || existingRoomMode.source === 'inferred') {
|
||||
upsertStoredRoomMode(jid, roomMode, 'inferred');
|
||||
}
|
||||
} else {
|
||||
insertStoredRoomSettings(db, jid, roomMode, 'inferred', snapshot);
|
||||
}
|
||||
|
||||
materializeRegisteredGroupsForRoom(
|
||||
db,
|
||||
jid,
|
||||
snapshot,
|
||||
roomMode,
|
||||
ownerAgentType,
|
||||
seededAgentType === ownerAgentType ? group.agentConfig : undefined,
|
||||
group.added_at,
|
||||
);
|
||||
});
|
||||
tx();
|
||||
}
|
||||
@@ -874,18 +905,24 @@ export function assignRoom(
|
||||
}
|
||||
|
||||
export function updateRegisteredGroupName(jid: string, name: string): void {
|
||||
db.prepare(
|
||||
`UPDATE room_settings
|
||||
SET name = ?, updated_at = ?
|
||||
WHERE chat_jid = ?`,
|
||||
).run(name, new Date().toISOString(), jid);
|
||||
db.prepare('UPDATE registered_groups SET name = ? WHERE jid = ?').run(
|
||||
name,
|
||||
jid,
|
||||
);
|
||||
if (!getStoredRoomSettingsRowFromDatabase(db, jid)) {
|
||||
syncStoredRoomRegistrationSnapshotForJid(jid);
|
||||
const plan = buildRoomRegistrationPlanForJid(jid, { name });
|
||||
if (!plan) {
|
||||
return;
|
||||
}
|
||||
db.transaction(() => {
|
||||
if (plan.hasStoredRoom) {
|
||||
updateStoredRoomMetadata(db, jid, plan.snapshot);
|
||||
} else {
|
||||
insertStoredRoomSettings(db, jid, plan.roomMode, 'inferred', plan.snapshot);
|
||||
}
|
||||
materializeRegisteredGroupsForRoom(
|
||||
db,
|
||||
jid,
|
||||
plan.snapshot,
|
||||
plan.roomMode,
|
||||
plan.snapshot.ownerAgentType,
|
||||
);
|
||||
})();
|
||||
}
|
||||
|
||||
export function getAllRegisteredGroups(
|
||||
@@ -921,6 +958,10 @@ export function getAllRegisteredGroups(
|
||||
|
||||
export function getRegisteredAgentTypesForJid(jid: string): AgentType[] {
|
||||
if (!db) return [];
|
||||
const stored = getStoredRoomSettingsRowFromDatabase(db, jid);
|
||||
if (stored) {
|
||||
return resolveStoredRoomCapabilityTypes(db, stored);
|
||||
}
|
||||
return collectRegisteredAgentTypes(db, jid);
|
||||
}
|
||||
|
||||
@@ -977,6 +1018,63 @@ function syncStoredRoomRegistrationSnapshotForJid(chatJid: string): void {
|
||||
updateStoredRoomMetadata(db, chatJid, snapshot);
|
||||
}
|
||||
|
||||
function buildRoomRegistrationSnapshotFromStoredRoom(
|
||||
stored: StoredRoomSettings,
|
||||
overrides?: Partial<Pick<RoomRegistrationSnapshot, 'name'>>,
|
||||
): RoomRegistrationSnapshot {
|
||||
const capabilityTypes = resolveStoredRoomCapabilityTypes(db, stored);
|
||||
const ownerAgentType =
|
||||
stored.ownerAgentType ||
|
||||
(capabilityTypes.length > 0
|
||||
? inferOwnerAgentTypeFromRegisteredAgentTypes(capabilityTypes)
|
||||
: OWNER_AGENT_TYPE);
|
||||
const name = overrides?.name ?? stored.name ?? stored.chatJid;
|
||||
|
||||
return {
|
||||
name,
|
||||
folder: resolveAssignedRoomFolder(db, stored.chatJid, name, stored.folder),
|
||||
triggerPattern: stored.trigger || `@${ASSISTANT_NAME}`,
|
||||
requiresTrigger: stored.requiresTrigger ?? true,
|
||||
isMain: stored.isMain ?? false,
|
||||
ownerAgentType,
|
||||
workDir: stored.workDir ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
function buildRoomRegistrationPlanForJid(
|
||||
chatJid: string,
|
||||
overrides?: Partial<Pick<RoomRegistrationSnapshot, 'name'>>,
|
||||
):
|
||||
| {
|
||||
snapshot: RoomRegistrationSnapshot;
|
||||
roomMode: RoomMode;
|
||||
hasStoredRoom: boolean;
|
||||
}
|
||||
| undefined {
|
||||
const stored = getStoredRoomSettingsRowFromDatabase(db, chatJid);
|
||||
if (stored) {
|
||||
return {
|
||||
snapshot: buildRoomRegistrationSnapshotFromStoredRoom(stored, overrides),
|
||||
roomMode: stored.roomMode,
|
||||
hasStoredRoom: true,
|
||||
};
|
||||
}
|
||||
|
||||
const snapshot = collectRoomRegistrationSnapshot(db, chatJid);
|
||||
if (!snapshot) return undefined;
|
||||
|
||||
return {
|
||||
snapshot: {
|
||||
...snapshot,
|
||||
name: overrides?.name ?? snapshot.name,
|
||||
},
|
||||
roomMode: inferRoomModeFromRegisteredAgentTypes(
|
||||
collectRegisteredAgentTypes(db, chatJid),
|
||||
),
|
||||
hasStoredRoom: false,
|
||||
};
|
||||
}
|
||||
|
||||
function upsertStoredRoomMode(
|
||||
chatJid: string,
|
||||
roomMode: RoomMode,
|
||||
@@ -1010,7 +1108,7 @@ export function setExplicitRoomMode(chatJid: string, roomMode: RoomMode): void {
|
||||
}
|
||||
|
||||
export function clearExplicitRoomMode(chatJid: string): void {
|
||||
const agentTypes = getRegisteredAgentTypesForJid(chatJid);
|
||||
const agentTypes = collectRegisteredAgentTypes(db, chatJid);
|
||||
if (agentTypes.length === 0) {
|
||||
db.prepare('DELETE FROM room_settings WHERE chat_jid = ?').run(chatJid);
|
||||
return;
|
||||
|
||||
@@ -147,9 +147,28 @@ export function getLegacyRegisteredGroupRows(
|
||||
return (
|
||||
agentTypeFilter
|
||||
? database
|
||||
.prepare('SELECT * FROM registered_groups WHERE agent_type = ?')
|
||||
.prepare(
|
||||
`SELECT *
|
||||
FROM registered_groups
|
||||
WHERE agent_type = ?
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM room_settings
|
||||
WHERE chat_jid = registered_groups.jid
|
||||
)`,
|
||||
)
|
||||
.all(agentTypeFilter)
|
||||
: database.prepare('SELECT * FROM registered_groups').all()
|
||||
: database
|
||||
.prepare(
|
||||
`SELECT *
|
||||
FROM registered_groups
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM room_settings
|
||||
WHERE chat_jid = registered_groups.jid
|
||||
)`,
|
||||
)
|
||||
.all()
|
||||
) as RegisteredGroupDatabaseRow[];
|
||||
}
|
||||
|
||||
@@ -158,6 +177,10 @@ export function getLegacyRegisteredGroup(
|
||||
jid: string,
|
||||
agentType?: string,
|
||||
): (RegisteredGroup & { jid: string }) | undefined {
|
||||
if (getStoredRoomSettingsRowFromDatabase(database, jid)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const row = (
|
||||
agentType
|
||||
? database
|
||||
@@ -276,7 +299,7 @@ export function buildRegisteredGroupFromStoredSettings(
|
||||
stored: StoredRoomSettings,
|
||||
requestedAgentType?: AgentType,
|
||||
): (RegisteredGroup & { jid: string }) | undefined {
|
||||
const capabilityTypes = collectRegisteredAgentTypes(database, stored.chatJid);
|
||||
const capabilityTypes = resolveStoredRoomCapabilityTypes(database, stored);
|
||||
const resolvedAgentType = requestedAgentType
|
||||
? capabilityTypes.includes(requestedAgentType)
|
||||
? requestedAgentType
|
||||
@@ -315,6 +338,27 @@ export function buildRegisteredGroupFromStoredSettings(
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveStoredRoomCapabilityTypes(
|
||||
database: Database,
|
||||
stored: StoredRoomSettings,
|
||||
): AgentType[] {
|
||||
const projectedTypes = collectRegisteredAgentTypes(database, stored.chatJid);
|
||||
if (stored.modeSource !== 'explicit') {
|
||||
return projectedTypes;
|
||||
}
|
||||
|
||||
const ownerAgentType =
|
||||
stored.ownerAgentType ||
|
||||
(projectedTypes.length > 0
|
||||
? inferOwnerAgentTypeFromRegisteredAgentTypes(projectedTypes)
|
||||
: OWNER_AGENT_TYPE);
|
||||
const types = new Set<AgentType>([ownerAgentType]);
|
||||
if (stored.roomMode === 'tribunal') {
|
||||
types.add(REVIEWER_AGENT_TYPE);
|
||||
}
|
||||
return [...types];
|
||||
}
|
||||
|
||||
function detectChannelPrefixForFolder(chatJid: string): string {
|
||||
if (chatJid.startsWith('dc:')) return 'discord';
|
||||
if (chatJid.startsWith('tg:')) return 'telegram';
|
||||
|
||||
Reference in New Issue
Block a user