feat(03-01): add searchMessages backend with LIKE query, IPC channel, preload bridge, and tests
- Add searchMessages method to AppDatabase with LIKE query, wildcard escaping, channel scoping, ORDER BY created_at DESC with limit - Add searchMessages IPC channel and searchMessagesSchema to contracts.ts - Register IPC handler in ipc-agent-coordination-handlers.ts with Zod validation - Wire searchMessages through preload bridge - Add 8 unit tests covering body/subject search, wildcard escaping, channel scoping, empty results, limit, and ordering
This commit is contained in:
1 parent
14da6b5ede
commit
ce2c3be065
5 files changed
+251
-1
No files matched your search
@@ -2675,6 +2675,26 @@ export class AppDatabase {
|
||||
return limit > 0 ? messages.slice(-limit) : messages;
|
||||
}
|
||||
|
||||
searchMessages(companyId: string, query: string, opts?: {
|
||||
channel?: string;
|
||||
channelTargetId?: string | null;
|
||||
limit?: number;
|
||||
}): AgentMessageRecord[] {
|
||||
const escaped = query.replace(/[%_]/g, "\\$&");
|
||||
const pattern = `%${escaped}%`;
|
||||
let sql = `SELECT id, company_id AS companyId, from_agent_id AS fromAgentId,
|
||||
to_agent_id AS toAgentId, channel, channel_target_id AS channelTargetId,
|
||||
subject, body, priority, read_at AS readAt, parent_message_id AS parentMessageId,
|
||||
attachments_json AS attachmentsJson, created_at AS createdAt
|
||||
FROM agent_messages WHERE company_id = ? AND (subject LIKE ? ESCAPE '\\' OR body LIKE ? ESCAPE '\\')`;
|
||||
const params: SQLInputValue[] = [companyId, pattern, pattern];
|
||||
if (opts?.channel) { sql += " AND channel = ?"; params.push(opts.channel); }
|
||||
if (opts?.channelTargetId !== undefined && opts?.channelTargetId !== null) { sql += " AND channel_target_id = ?"; params.push(opts.channelTargetId); }
|
||||
sql += " ORDER BY created_at DESC LIMIT ?";
|
||||
params.push(opts?.limit ?? 50);
|
||||
return this.readAll<AgentMessageRecord>(sql, ...params);
|
||||
}
|
||||
|
||||
markMessageRead(messageId: string, companyId: string) {
|
||||
this.markAgentMessageRead(messageId, companyId, BOARD_READER_ID);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
autoAssignSchema,
|
||||
markMessageReadSchema,
|
||||
messageQuerySchema,
|
||||
searchMessagesSchema,
|
||||
} from "@shared/contracts";
|
||||
import { fail, ok, type RegisterDomainHandlersDependencies } from "./ipc-domain-common";
|
||||
import { dispatchAgentMessage } from "./message-dispatch";
|
||||
@@ -94,6 +95,17 @@ export function registerAgentCoordinationHandlers({
|
||||
return ok(true);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.searchMessages, async (_event, payload) => {
|
||||
const parsed = searchMessagesSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
return ok(db.searchMessages(parsed.companyId, parsed.query, {
|
||||
channel: parsed.channel,
|
||||
channelTargetId: parsed.channelTargetId,
|
||||
limit: parsed.limit,
|
||||
}));
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.autoAssignTask, async (_event, payload) => {
|
||||
const parsed = autoAssignSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
|
||||
Reference in new issue
Block a user