godcrm/backend/routes/v3/ai-agents/sharedClaudeCodeEvents.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

102 lines
4.5 KiB
JavaScript

/**
* Claude Code event handler builder for agent execution.
* Handles terminal command tracking and step message saving.
*/
import { dbGet, dbRun } from '../../../database/connection.js';
import { apiLogger } from '../../../utils/logger.js';
import { saveStepMessage } from './shared.js';
/**
* Build a Claude Code onEvent handler that tracks terminal commands
* and saves step messages to the conversation.
*
* @param {Object} opts
* @param {number} opts.conversationId
* @param {number} opts.agentId
* @param {string} opts.model
* @param {number} opts.userId
* @returns {{ onEvent: Function|undefined, toolUseMap: Map, getTerminalSessionId: Function }}
*/
export async function buildClaudeCodeEventHandler({ conversationId, agentId, model, userId }) {
const toolUseMap = new Map();
let agentTerminalSessionId = null;
if (conversationId) {
try {
const agentSessionTitle = `Agent #${agentId}`;
let agentSession = await dbGet(
`SELECT * FROM terminal_sessions WHERE user_id = $1 AND title = $2 AND status = $3`,
[userId, agentSessionTitle, 'active']
);
if (!agentSession) {
const { createSession } = await import('../../../services/TerminalService.js');
agentSession = await createSession(userId, agentSessionTitle);
}
agentTerminalSessionId = agentSession.id;
} catch { /* terminal not critical */ }
}
const onEvent = conversationId ? (event) => {
if (event.type === 'assistant' && event.message?.content) {
for (const block of event.message.content) {
if (block.type === 'tool_use') {
saveStepMessage(conversationId, {
content: block.name, contentType: 'tool_call', role: 'assistant',
senderType: 'agent', agentId, modelUsed: model,
toolResults: { tool: block.name, args: block.input, terminal_session_id: agentTerminalSessionId }
}).catch(() => {});
let insertPromise = null;
if (block.name === 'Bash' && block.input?.command && agentTerminalSessionId) {
insertPromise = dbRun(
`INSERT INTO terminal_commands (session_id, command, risk_level, approval_status, source, agent_name)
VALUES ($1, $2, $3, $4, $5, $6)`,
[agentTerminalSessionId, block.input.command, 'safe', 'auto', 'agent', `Agent #${agentId}`]
).then(result => {
apiLogger.debug({ cmdId: result?.lastInsertRowid, command: block.input?.command?.substring(0, 50) }, 'Terminal command inserted');
return result;
}).catch(err => {
apiLogger.warn({ err }, 'Failed to insert terminal command');
return null;
});
}
toolUseMap.set(block.id, { name: block.name, command: block.input?.command, insertPromise });
} else if (block.type === 'text' && block.text) {
saveStepMessage(conversationId, {
content: block.text, contentType: 'thinking', role: 'assistant',
senderType: 'agent', agentId, modelUsed: model
}).catch(() => {});
}
}
} else if (event.type === 'user' && event.message?.content) {
for (const block of event.message.content) {
if (block.type === 'tool_result') {
const resultContent = typeof block.content === 'string'
? block.content : JSON.stringify(block.content);
const truncated = resultContent.length > 2000 ? resultContent.substring(0, 2000) + '...' : resultContent;
saveStepMessage(conversationId, {
content: truncated, contentType: 'tool_result', role: 'tool',
senderType: 'agent', agentId,
toolResults: { tool_use_id: block.tool_use_id, content: truncated }
}).catch(() => {});
const toolInfo = toolUseMap.get(block.tool_use_id);
if (toolInfo?.insertPromise) {
const output = resultContent;
const exitCode = block.is_error ? 1 : 0;
toolInfo.insertPromise.then(result => {
const cmdId = result?.lastInsertRowid;
if (cmdId) {
dbRun(
`UPDATE terminal_commands SET output = $1, exit_code = $2, completed_at = NOW() WHERE id = $3`,
[output.substring(0, 100 * 1024), exitCode, cmdId]
).catch(err => apiLogger.warn({ err, cmdId }, 'Failed to update terminal command'));
}
}).catch(() => {});
}
}
}
}
} : undefined;
return { onEvent, toolUseMap };
}