Files
EJClaw/src/db/sessions.ts
Eyejoker 86e3fa7521 [codex] Clear role-scoped sessions (#139)
* add gated codex goals support

* sync README SDK versions

* bump claude agent sdk

* add codex goals settings toggle

* reuse status dashboard message and simplify settings actions

* refine settings page UX

* fix settings nav hash routing

* add dashboard UX verification

* refine dashboard settings and inbox UX

* remove inbox top-level navigation

* remove dashboard health top-level navigation

* fix: allow runtime image attachment paths

* feat: configure attachment allowlist dirs

* fix: clean duplicate dashboard status messages

* fix: poll dashboard duplicate cleanup between status updates

* fix: delete dashboard duplicates on create

* Refine settings IA with tabbed sections

* Add read-only runtime inventory settings

* Fix runtime inventory MCP JSON parsing

* Add room skill settings inventory

* Add room skill setting mutations

* Apply room skill overrides to runner spawn

* Refine scheduled task dashboard UX

* Remove reviewer final prompt reinjection

* Stabilize pnpm verification fixture

* Remove previous owner final prompt carryover

* Clear role-scoped sessions
2026-05-17 01:02:40 +08:00

169 lines
4.0 KiB
TypeScript

import { Database } from 'bun:sqlite';
import { AgentType } from '../types.js';
export type ServiceShadowAgentTypeResolver = (
serviceId: string,
) => AgentType | undefined;
function hasLegacyServiceSessionsTable(database: Database): boolean {
return Boolean(
database
.prepare(
`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'service_sessions'`,
)
.get(),
);
}
export function migrateSessionsTableToCompositePk(
database: Database,
defaultAgentType: AgentType,
): void {
const sessionCols = database
.prepare('PRAGMA table_info(sessions)')
.all() as Array<{ name: string }>;
if (sessionCols.some((col) => col.name === 'agent_type')) {
return;
}
database.exec(`
CREATE TABLE sessions_new (
group_folder TEXT NOT NULL,
agent_type TEXT NOT NULL DEFAULT '${defaultAgentType}',
session_id TEXT NOT NULL,
PRIMARY KEY (group_folder, agent_type)
);
`);
database
.prepare(
`INSERT INTO sessions_new (group_folder, agent_type, session_id)
SELECT group_folder, ?, session_id FROM sessions`,
)
.run(defaultAgentType);
database.exec(`
DROP TABLE sessions;
ALTER TABLE sessions_new RENAME TO sessions;
`);
}
export function backfillLegacyServiceSessions(
database: Database,
resolveAgentTypeFromServiceId: ServiceShadowAgentTypeResolver,
): void {
if (!hasLegacyServiceSessionsTable(database)) {
return;
}
const rows = database
.prepare(
`SELECT group_folder, service_id, session_id
FROM service_sessions`,
)
.all() as Array<{
group_folder: string;
service_id: string;
session_id: string;
}>;
const upsert = database.prepare(
`INSERT OR IGNORE INTO sessions (group_folder, agent_type, session_id)
VALUES (?, ?, ?)`,
);
const tx = database.transaction(
(
sessionRows: Array<{
group_folder: string;
service_id: string;
session_id: string;
}>,
) => {
for (const row of sessionRows) {
const agentType = resolveAgentTypeFromServiceId(row.service_id);
if (!agentType) {
continue;
}
upsert.run(row.group_folder, agentType, row.session_id);
}
},
);
tx(rows);
}
export function dropLegacyServiceSessionsTable(database: Database): void {
if (!hasLegacyServiceSessionsTable(database)) {
return;
}
database.exec(`DROP TABLE service_sessions`);
}
export function getSessionFromDatabase(
database: Database,
groupFolder: string,
agentType: string,
): string | undefined {
const row = database
.prepare(
'SELECT session_id FROM sessions WHERE group_folder = ? AND agent_type = ?',
)
.get(groupFolder, agentType) as { session_id: string } | undefined;
return row?.session_id;
}
export function setSessionInDatabase(
database: Database,
groupFolder: string,
agentType: string,
sessionId: string,
): void {
database
.prepare(
'INSERT OR REPLACE INTO sessions (group_folder, agent_type, session_id) VALUES (?, ?, ?)',
)
.run(groupFolder, agentType, sessionId);
}
export function deleteSessionFromDatabase(
database: Database,
groupFolder: string,
agentType: string,
): void {
database
.prepare('DELETE FROM sessions WHERE group_folder = ? AND agent_type = ?')
.run(groupFolder, agentType);
}
export function deleteAllSessionsForGroupFromDatabase(
database: Database,
groupFolder: string,
): void {
database
.prepare(
`DELETE FROM sessions
WHERE group_folder IN (?, ?, ?)`,
)
.run(groupFolder, `${groupFolder}:reviewer`, `${groupFolder}:arbiter`);
}
export function getAllSessionsForAgentTypeFromDatabase(
database: Database,
agentType: string,
): Record<string, string> {
const rows = database
.prepare(
'SELECT group_folder, session_id FROM sessions WHERE agent_type = ?',
)
.all(agentType) as Array<{
group_folder: string;
session_id: string;
}>;
const result: Record<string, string> = {};
for (const row of rows) {
result[row.group_folder] = row.session_id;
}
return result;
}