Add room skill settings inventory

This commit is contained in:
ejclaw
2026-05-04 03:03:05 +09:00
parent 940494ab77
commit ce6c863349
15 changed files with 902 additions and 6 deletions

View File

@@ -1,5 +1,25 @@
import { Database } from 'bun:sqlite';
const ROOM_SKILL_OVERRIDES_SCHEMA = `
CREATE TABLE IF NOT EXISTS room_skill_overrides (
chat_jid TEXT NOT NULL,
agent_type TEXT NOT NULL,
skill_scope TEXT NOT NULL,
skill_name TEXT NOT NULL,
enabled INTEGER NOT NULL,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (chat_jid, agent_type, skill_scope, skill_name),
FOREIGN KEY (chat_jid) REFERENCES room_settings(chat_jid) ON DELETE CASCADE,
CHECK (agent_type IN ('claude-code', 'codex')),
CHECK (enabled IN (0, 1)),
CHECK (length(skill_scope) > 0),
CHECK (length(skill_name) > 0)
);
CREATE INDEX IF NOT EXISTS idx_room_skill_overrides_room
ON room_skill_overrides(chat_jid, agent_type);
`;
export function applyBaseSchema(database: Database): void {
database.exec(`
CREATE TABLE IF NOT EXISTS chats (
@@ -410,8 +430,7 @@ export function applyBaseSchema(database: Database): void {
last_used_at TEXT,
archived_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_memories_scope
ON memories(scope_kind, scope_key);
CREATE INDEX IF NOT EXISTS idx_memories_scope ON memories(scope_kind, scope_key);
CREATE INDEX IF NOT EXISTS idx_memories_active
ON memories(scope_kind, scope_key, archived_at, created_at);
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
@@ -435,4 +454,5 @@ export function applyBaseSchema(database: Database): void {
VALUES (new.id, new.content, new.keywords_json);
END;
`);
database.exec(ROOM_SKILL_OVERRIDES_SCHEMA);
}

View File

@@ -40,6 +40,7 @@ function getExpectedSchemaMigrations(): Array<{
{ version: 13, name: 'message_source_kind' },
{ version: 14, name: 'work_item_attachments' },
{ version: 15, name: 'turn_progress_text' },
{ version: 16, name: 'room_skill_overrides' },
];
}

View File

@@ -0,0 +1,29 @@
import type { Database } from 'bun:sqlite';
import type { SchemaMigrationDefinition } from './types.js';
export const ROOM_SKILL_OVERRIDES_MIGRATION: SchemaMigrationDefinition = {
version: 16,
name: 'room_skill_overrides',
apply(database: Database) {
database.exec(`
CREATE TABLE IF NOT EXISTS room_skill_overrides (
chat_jid TEXT NOT NULL,
agent_type TEXT NOT NULL,
skill_scope TEXT NOT NULL,
skill_name TEXT NOT NULL,
enabled INTEGER NOT NULL,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (chat_jid, agent_type, skill_scope, skill_name),
FOREIGN KEY (chat_jid) REFERENCES room_settings(chat_jid) ON DELETE CASCADE,
CHECK (agent_type IN ('claude-code', 'codex')),
CHECK (enabled IN (0, 1)),
CHECK (length(skill_scope) > 0),
CHECK (length(skill_name) > 0)
);
CREATE INDEX IF NOT EXISTS idx_room_skill_overrides_room
ON room_skill_overrides(chat_jid, agent_type);
`);
},
};

View File

@@ -15,6 +15,7 @@ import { PAIRED_VERDICT_AND_STEP_TELEMETRY_MIGRATION } from './012_paired-verdic
import { MESSAGE_SOURCE_KIND_MIGRATION } from './013_message-source-kind.js';
import { WORK_ITEM_ATTACHMENTS_MIGRATION } from './014_work-item-attachments.js';
import { TURN_PROGRESS_TEXT_MIGRATION } from './015_turn-progress-text.js';
import { ROOM_SKILL_OVERRIDES_MIGRATION } from './016_room-skill-overrides.js';
import type {
SchemaMigrationArgs,
SchemaMigrationDefinition,
@@ -38,6 +39,7 @@ const ORDERED_SCHEMA_MIGRATIONS: readonly SchemaMigrationDefinition[] = [
MESSAGE_SOURCE_KIND_MIGRATION,
WORK_ITEM_ATTACHMENTS_MIGRATION,
TURN_PROGRESS_TEXT_MIGRATION,
ROOM_SKILL_OVERRIDES_MIGRATION,
];
function ensureSchemaMigrationsTable(database: Database): void {

View File

@@ -28,6 +28,16 @@ interface StoredRoomModeRow {
source: RoomModeSource;
}
export interface StoredRoomSkillOverride {
chatJid: string;
agentType: AgentType;
skillScope: string;
skillName: string;
enabled: boolean;
createdAt: string;
updatedAt: string;
}
export interface AssignRoomInput {
name: string;
roomMode?: RoomMode;
@@ -307,6 +317,62 @@ export function getStoredRoomRoleAgentPlanFromDatabase(
return stored ? resolveStoredRoomRoleAgentPlan(database, stored) : undefined;
}
export function getStoredRoomSkillOverridesFromDatabase(
database: Database,
chatJid?: string,
): StoredRoomSkillOverride[] {
const params: string[] = [];
const where = chatJid ? 'WHERE chat_jid = ?' : '';
if (chatJid) params.push(chatJid);
let rows: Array<{
chat_jid: string;
agent_type: string;
skill_scope: string;
skill_name: string;
enabled: number;
created_at: string;
updated_at: string;
}>;
try {
rows = database
.prepare(
`SELECT chat_jid, agent_type, skill_scope, skill_name, enabled,
created_at, updated_at
FROM room_skill_overrides
${where}
ORDER BY chat_jid, agent_type, skill_scope, skill_name`,
)
.all(...params) as Array<{
chat_jid: string;
agent_type: string;
skill_scope: string;
skill_name: string;
enabled: number;
created_at: string;
updated_at: string;
}>;
} catch {
return [];
}
return rows
.map((row) => {
const agentType = normalizeStoredAgentType(row.agent_type);
if (!agentType) return null;
return {
chatJid: row.chat_jid,
agentType,
skillScope: row.skill_scope,
skillName: row.skill_name,
enabled: row.enabled === 1,
createdAt: row.created_at,
updatedAt: row.updated_at,
};
})
.filter((row): row is StoredRoomSkillOverride => Boolean(row));
}
function getStoredRoomModeRowFromDatabase(
database: Database,
chatJid: string,

View File

@@ -36,9 +36,11 @@ import {
getRegisteredGroupFromDatabase,
getStoredRoomRoleAgentPlanFromDatabase,
getStoredRoomSettingsFromDatabase,
getStoredRoomSkillOverridesFromDatabase,
setExplicitRoomModeInDatabase,
setRegisteredGroupForTestsInDatabase,
setStoredRoomOwnerAgentTypeForTestsInDatabase,
type StoredRoomSkillOverride,
updateRegisteredGroupNameInDatabase,
} from './rooms.js';
import { type StoredRoomSettings } from './room-registration.js';
@@ -363,6 +365,14 @@ export function getStoredRoomRoleAgentPlan(
return getStoredRoomRoleAgentPlanFromDatabase(db, chatJid);
}
export function getStoredRoomSkillOverrides(
chatJid?: string,
): StoredRoomSkillOverride[] {
const db = getDatabaseIfInitialized();
if (!db) return [];
return getStoredRoomSkillOverridesFromDatabase(db, chatJid);
}
export function getExplicitRoomMode(chatJid: string): RoomMode | undefined {
return getExplicitRoomModeFromDatabase(requireDatabase(), chatJid);
}