2b60dd2932
Persist platform_source across session creation, transcript ingestion, API query paths, and viewer state so Claude and Codex data can coexist without bleeding into each other. - add platform-source normalization helpers and persist platform_source in sdk_sessions via migration 24 with backfill and indexing - thread platformSource through CLI hooks, transcript processing, context generation, pagination, search routes, SSE payloads, and session management - expose source-aware project catalogs, viewer tabs, context preview selectors, and source badges for observations, prompts, and summaries - start the transcript watcher from the worker for transcript-based clients and preserve platform source during Codex ingestion - auto-start the worker from the MCP server for MCP-only clients and tighten stdio-driven cleanup during shutdown - keep createSDKSession backward compatible with existing custom-title callers while allowing explicit platform source forwarding
80 lines
3.3 KiB
TypeScript
80 lines
3.3 KiB
TypeScript
/**
|
|
* Observation Handler - PostToolUse
|
|
*
|
|
* Extracted from save-hook.ts - sends tool usage to worker for storage.
|
|
*/
|
|
|
|
import type { EventHandler, NormalizedHookInput, HookResult } from '../types.js';
|
|
import { ensureWorkerRunning, workerHttpRequest } from '../../shared/worker-utils.js';
|
|
import { logger } from '../../utils/logger.js';
|
|
import { HOOK_EXIT_CODES } from '../../shared/hook-constants.js';
|
|
import { isProjectExcluded } from '../../utils/project-filter.js';
|
|
import { SettingsDefaultsManager } from '../../shared/SettingsDefaultsManager.js';
|
|
import { USER_SETTINGS_PATH } from '../../shared/paths.js';
|
|
import { normalizePlatformSource } from '../../shared/platform-source.js';
|
|
|
|
export const observationHandler: EventHandler = {
|
|
async execute(input: NormalizedHookInput): Promise<HookResult> {
|
|
// Ensure worker is running before any other logic
|
|
const workerReady = await ensureWorkerRunning();
|
|
if (!workerReady) {
|
|
// Worker not available - skip observation gracefully
|
|
return { continue: true, suppressOutput: true, exitCode: HOOK_EXIT_CODES.SUCCESS };
|
|
}
|
|
|
|
const { sessionId, cwd, toolName, toolInput, toolResponse } = input;
|
|
const platformSource = normalizePlatformSource(input.platform);
|
|
|
|
if (!toolName) {
|
|
// No tool name provided - skip observation gracefully
|
|
return { continue: true, suppressOutput: true, exitCode: HOOK_EXIT_CODES.SUCCESS };
|
|
}
|
|
|
|
const toolStr = logger.formatTool(toolName, toolInput);
|
|
|
|
logger.dataIn('HOOK', `PostToolUse: ${toolStr}`, {});
|
|
|
|
// Validate required fields before sending to worker
|
|
if (!cwd) {
|
|
throw new Error(`Missing cwd in PostToolUse hook input for session ${sessionId}, tool ${toolName}`);
|
|
}
|
|
|
|
// Check if project is excluded from tracking
|
|
const settings = SettingsDefaultsManager.loadFromFile(USER_SETTINGS_PATH);
|
|
if (isProjectExcluded(cwd, settings.CLAUDE_MEM_EXCLUDED_PROJECTS)) {
|
|
logger.debug('HOOK', 'Project excluded from tracking, skipping observation', { cwd, toolName });
|
|
return { continue: true, suppressOutput: true };
|
|
}
|
|
|
|
// Send to worker - worker handles privacy check and database operations
|
|
try {
|
|
const response = await workerHttpRequest('/api/sessions/observations', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
contentSessionId: sessionId,
|
|
platformSource,
|
|
tool_name: toolName,
|
|
tool_input: toolInput,
|
|
tool_response: toolResponse,
|
|
cwd
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
// Log but don't throw — observation storage failure should not block tool use
|
|
logger.warn('HOOK', 'Observation storage failed, skipping', { status: response.status, toolName });
|
|
return { continue: true, suppressOutput: true, exitCode: HOOK_EXIT_CODES.SUCCESS };
|
|
}
|
|
|
|
logger.debug('HOOK', 'Observation sent successfully', { toolName });
|
|
} catch (error) {
|
|
// Worker unreachable — skip observation gracefully
|
|
logger.warn('HOOK', 'Observation fetch error, skipping', { error: error instanceof Error ? error.message : String(error) });
|
|
return { continue: true, suppressOutput: true, exitCode: HOOK_EXIT_CODES.SUCCESS };
|
|
}
|
|
|
|
return { continue: true, suppressOutput: true };
|
|
}
|
|
};
|