( log: LogOption, sessionId: string, )
| 999 | } |
| 1000 | |
| 1001 | async function extractFacetsFromAPI( |
| 1002 | log: LogOption, |
| 1003 | sessionId: string, |
| 1004 | ): Promise<SessionFacets | null> { |
| 1005 | try { |
| 1006 | // Use summarization for long transcripts |
| 1007 | const transcript = await formatTranscriptWithSummarization(log) |
| 1008 | |
| 1009 | // Build prompt asking for JSON directly (no tool use) |
| 1010 | const jsonPrompt = `${FACET_EXTRACTION_PROMPT}${transcript} |
| 1011 | |
| 1012 | RESPOND WITH ONLY A VALID JSON OBJECT matching this schema: |
| 1013 | { |
| 1014 | "underlying_goal": "What the user fundamentally wanted to achieve", |
| 1015 | "goal_categories": {"category_name": count, ...}, |
| 1016 | "outcome": "fully_achieved|mostly_achieved|partially_achieved|not_achieved|unclear_from_transcript", |
| 1017 | "user_satisfaction_counts": {"level": count, ...}, |
| 1018 | "claude_helpfulness": "unhelpful|slightly_helpful|moderately_helpful|very_helpful|essential", |
| 1019 | "session_type": "single_task|multi_task|iterative_refinement|exploration|quick_question", |
| 1020 | "friction_counts": {"friction_type": count, ...}, |
| 1021 | "friction_detail": "One sentence describing friction or empty", |
| 1022 | "primary_success": "none|fast_accurate_search|correct_code_edits|good_explanations|proactive_help|multi_file_changes|good_debugging", |
| 1023 | "brief_summary": "One sentence: what user wanted and whether they got it" |
| 1024 | }` |
| 1025 | |
| 1026 | const result = await queryWithModel({ |
| 1027 | systemPrompt: asSystemPrompt([]), |
| 1028 | userPrompt: jsonPrompt, |
| 1029 | signal: new AbortController().signal, |
| 1030 | options: { |
| 1031 | model: getAnalysisModel(), |
| 1032 | querySource: 'insights', |
| 1033 | agents: [], |
| 1034 | isNonInteractiveSession: true, |
| 1035 | hasAppendSystemPrompt: false, |
| 1036 | mcpTools: [], |
| 1037 | maxOutputTokensOverride: 4096, |
| 1038 | }, |
| 1039 | }) |
| 1040 | |
| 1041 | const text = extractTextContent(result.message.content) |
| 1042 | |
| 1043 | // Parse JSON from response |
| 1044 | const jsonMatch = text.match(/\{[\s\S]*\}/) |
| 1045 | if (!jsonMatch) return null |
| 1046 | |
| 1047 | const parsed: unknown = jsonParse(jsonMatch[0]) |
| 1048 | if (!isValidSessionFacets(parsed)) return null |
| 1049 | const facets: SessionFacets = { ...parsed, session_id: sessionId } |
| 1050 | return facets |
| 1051 | } catch (err) { |
| 1052 | logError(new Error(`Facet extraction failed: ${toError(err).message}`)) |
| 1053 | return null |
| 1054 | } |
| 1055 | } |
| 1056 | |
| 1057 | /** |
| 1058 | * Detects multi-clauding (using multiple Claude sessions concurrently). |
no test coverage detected