@redaksjon/protokoll-engine 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index26.js","sources":["../src/agentic/executor.ts"],"sourcesContent":["/**\n * Agentic Executor\n * \n * Executes the agentic transcription loop with tool calls.\n * Uses RiotPrompt's ConversationBuilder for conversation management.\n */\n\nimport { ToolContext, TranscriptionState } from './types';\nimport * as Registry from './registry';\nimport * as Reasoning from '../reasoning';\nimport * as Logging from '../logging';\nimport { ConversationBuilder, ToolCall as RiotToolCall } from '@kjerneverk/riotprompt';\nimport { EntityPrepositioner } from '../weighting/prepositioning';\n\nexport interface ContextChangeRecord {\n entityType: 'person' | 'project' | 'company' | 'term' | 'ignored';\n entityId: string;\n entityName: string;\n action: 'created' | 'updated';\n details?: Record<string, unknown>;\n}\n\nexport interface ExecutorInstance {\n process(transcriptText: string): Promise<{\n enhancedText: string;\n state: TranscriptionState;\n toolsUsed: string[];\n iterations: number;\n totalTokens?: number;\n contextChanges?: ContextChangeRecord[];\n }>;\n}\n\n/**\n * Convert internal tool call format to RiotPrompt's ToolCall format\n */\nconst toRiotToolCalls = (toolCalls: Array<{ id: string; name: string; arguments: Record<string, unknown> }>): RiotToolCall[] => {\n return toolCalls.map(tc => ({\n id: tc.id,\n type: 'function' as const,\n function: {\n name: tc.name,\n arguments: JSON.stringify(tc.arguments),\n },\n }));\n};\n\n/**\n * Clean response content by removing any leaked internal processing information\n * that should never appear in the user-facing transcript.\n */\nconst cleanResponseContent = (content: string): string => {\n // Remove common patterns of leaked internal processing\n // Pattern 1: \"Using tools to...\" type commentary\n let cleaned = content.replace(/^(?:Using tools?|Let me|I'll|I will|Now I'll|First,?\\s*I(?:'ll| will)).*?[\\r\\n]+/gim, '');\n \n // Pattern 2: JSON tool call artifacts - match complete JSON objects with \"tool\" key\n // Matches: {\"tool\":\"...\",\"args\":{...}}, {\"tool\":\"...\",\"input\":{...}}, etc.\n // Use a more careful pattern that matches balanced braces\n cleaned = cleaned.replace(/\\{\"tool\":\\s*\"[^\"]+\",\\s*\"(?:args|input)\":\\s*\\{[^}]*\\}\\}/g, '');\n \n // Pattern 3: Tool call references in the format tool_name({...})\n cleaned = cleaned.replace(/\\b\\w+_\\w+\\(\\{[^}]*\\}\\)/g, '');\n \n // Pattern 4: Remove lines with \"to=\" patterns (internal routing artifacts)\n // Matches: \"Այ to=lookup_project.commentary\", \"undefined to=route_note.commentary\"\n // Do this BEFORE Unicode filtering to catch mixed corruption\n cleaned = cleaned.replace(/^.*\\s+to=\\w+\\.\\w+.*$/gm, '');\n \n // Pattern 5: Remove lines that look like spam/SEO (Chinese gambling sites, etc.)\n // Matches lines with Chinese characters followed by \"app\", \"官网\", etc.\n // This is more specific than general Unicode filtering\n const spamPattern = /^.*[\\u4E00-\\u9FFF].*(app|官网|彩票|中彩票).*$/gm;\n cleaned = cleaned.replace(spamPattern, '');\n \n // Pattern 6: Remove lines with suspicious Unicode at the START (corruption indicators)\n // Only remove lines that START with non-Latin scripts (not legitimate content)\n // This catches corruption like \"Այ to=...\" or \"สามสิบเอ็ด\" at line start\n const corruptionStartPattern = /^[\\u0530-\\u058F\\u0E00-\\u0E7F\\u0A80-\\u0AFF\\u0C00-\\u0C7F].*$/gm;\n cleaned = cleaned.replace(corruptionStartPattern, '');\n \n // Pattern 7: Lines that are purely reasoning/commentary before the actual content\n // Look for lines like \"I'll verify...\", \"Checking...\", etc.\n const lines = cleaned.split('\\n');\n let startIndex = 0;\n \n // Skip leading lines that look like internal commentary\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n \n // Skip empty lines\n if (line === '') continue;\n \n // Check if line looks like commentary (starts with action verbs, contains \"tool\", etc.)\n const isCommentary = /^(checking|verifying|looking|searching|analyzing|processing|determining|using|calling|executing|I'm|I am|Let me)/i.test(line)\n || line.includes('tool')\n || line.includes('{\"')\n || line.includes('reasoning')\n || line.includes('undefined');\n \n if (!isCommentary) {\n // This looks like actual content - start from here\n startIndex = i;\n break;\n }\n }\n \n // Rejoin from the first real content line\n if (startIndex > 0) {\n cleaned = lines.slice(startIndex).join('\\n');\n }\n \n // Final cleanup: remove multiple consecutive blank lines\n cleaned = cleaned.replace(/\\n{3,}/g, '\\n\\n');\n \n return cleaned.trim();\n};\n\nexport const create = (\n reasoning: Reasoning.ReasoningInstance,\n ctx: ToolContext\n): ExecutorInstance => {\n const logger = Logging.getLogger();\n const registry = Registry.create(ctx);\n \n const process = async (transcriptText: string) => {\n const state: TranscriptionState = {\n originalText: transcriptText,\n correctedText: transcriptText,\n unknownEntities: [],\n resolvedEntities: new Map(),\n referencedEntities: {\n people: new Set(),\n projects: new Set(),\n terms: new Set(),\n companies: new Set(),\n },\n confidence: 0,\n };\n \n // Make resolvedEntities available to tools so they can avoid re-asking\n ctx.resolvedEntities = state.resolvedEntities;\n \n const toolsUsed: string[] = [];\n const contextChanges: ContextChangeRecord[] = [];\n let iterations = 0;\n let totalTokens = 0;\n const maxIterations = 15;\n \n // Use ConversationBuilder for conversation management with token budget\n const conversation = ConversationBuilder.create({ model: 'gpt-4o' })\n .withTokenBudget({\n max: 100000, // 100k token context window\n reserveForResponse: 4000, // Reserve 4k tokens for response\n strategy: 'summarize', // Summarize old messages if budget exceeded\n onBudgetExceeded: 'compress', // Automatically compress when exceeded\n preserveSystem: true, // Always keep system messages\n preserveRecent: 5 // Keep last 5 messages\n });\n \n // Generate entity prepositioning guidance if weight model is available\n // Note: At this point, routing hasn't happened yet, so we can't use project-specific predictions\n // The weight model will still provide co-occurrence predictions based on any entities found\n const prepositioner = ctx.weightModelProvider\n ? new EntityPrepositioner(ctx.weightModelProvider, ctx.contextInstance)\n : null;\n \n const entityGuidance = prepositioner\n ? prepositioner.generateGuidance(undefined) // No project ID yet\n : { likelyEntities: [], guidance: '' };\n\n // Build the system prompt\n let systemPrompt = `You are an intelligent transcription assistant. Your job is to:\n1. Analyze the transcript for names, projects, and companies\n2. Use the available tools to verify spellings and gather context\n3. Correct any misheard names or terms\n4. Determine the appropriate destination for this note\n5. Produce a clean, accurate Markdown transcript\n\nCRITICAL RULES:\n- This is NOT a summary. Preserve ALL content from the original transcript.\n- Only fix obvious transcription errors like misheard names.\n- When you have finished processing, output the COMPLETE corrected transcript as Markdown.\n- Do NOT say you don't have the transcript - it's in the conversation history.\n\nOUTPUT REQUIREMENTS - EXTREMELY IMPORTANT:\n- Your final response MUST contain ONLY the corrected transcript text.\n- DO NOT include any commentary like \"Using tools to...\" or \"Let me verify...\".\n- DO NOT include any explanations about what you're doing or have done.\n- DO NOT include any tool call information or JSON in your response.\n- DO NOT include any reasoning or processing notes.\n- Your output will be inserted directly into the user-facing document.\n- If you need to use tools, use them silently - don't narrate what you're doing.\n\nAvailable tools:\n- lookup_person: Find information about people (use for any name that might be misspelled)\n- lookup_project: Find project routing information \n- verify_spelling: Ask user about unknown terms (if interactive mode)\n- route_note: Determine where to file this note\n- store_context: Remember new information for future use`;\n\n // Add entity guidance if available\n if (entityGuidance.guidance) {\n systemPrompt += `\\n\\n## Entity Guidance\\n${entityGuidance.guidance}`;\n systemPrompt += '\\n\\nUse this guidance to improve entity recognition, but verify entities exist in the context before referencing them.';\n }\n\n // Add system message using ConversationBuilder\n conversation.addSystemMessage(systemPrompt);\n \n // Add the initial user message with transcript\n const initialPrompt = `Here is the raw transcript to process:\n\n--- BEGIN TRANSCRIPT ---\n${transcriptText}\n--- END TRANSCRIPT ---\n\nPlease:\n1. Identify any names, companies, or technical terms that might be misspelled\n2. Use the lookup_person tool to verify spelling of any names you find\n3. Use route_note to determine the destination\n4. Then output the COMPLETE corrected transcript as clean Markdown\n\nCRITICAL: Your response must contain ONLY the transcript text - no commentary, no explanations, no tool information.\nRemember: preserve ALL content, only fix transcription errors.`;\n\n conversation.addUserMessage(initialPrompt);\n\n try {\n // Initial reasoning call\n logger.debug('Starting agentic transcription - analyzing for names and routing...');\n let response = await reasoning.complete({\n systemPrompt,\n prompt: initialPrompt,\n tools: registry.getToolDefinitions(),\n maxIterations,\n });\n \n // Track token usage\n if (response.usage) {\n totalTokens += response.usage.totalTokens;\n }\n \n // Add assistant response to conversation\n if (response.toolCalls && response.toolCalls.length > 0) {\n conversation.addAssistantWithToolCalls(\n response.content,\n toRiotToolCalls(response.toolCalls)\n );\n } else {\n conversation.addAssistantMessage(response.content);\n }\n \n // Iterative tool use loop\n while (response.toolCalls && response.toolCalls.length > 0 && iterations < maxIterations) {\n iterations++;\n logger.debug('Iteration %d: Processing %d tool calls...', iterations, response.toolCalls.length);\n \n // Collect tool results\n const toolResults: Array<{ id: string; name: string; result: string }> = [];\n \n // Execute each tool call\n for (const toolCall of response.toolCalls) {\n logger.debug('Executing tool: %s', toolCall.name);\n toolsUsed.push(toolCall.name);\n \n try {\n const result = await registry.executeTool(toolCall.name, toolCall.arguments);\n \n // Format result for the model\n const resultStr = JSON.stringify(result.data || { success: result.success, message: result.error || 'OK' });\n toolResults.push({ id: toolCall.id, name: toolCall.name, result: resultStr });\n \n logger.debug('Tool %s result: %s', toolCall.name, result.success ? 'success' : 'failed');\n \n // Handle results that need user input\n // Interactive functionality moved to protokoll-cli\n /* \n // eslint-disable-next-line no-constant-condition\n if (result.needsUserInput && false) {\n logger.info('Interactive: %s requires clarification', toolCall.name);\n \n const termName = String(toolCall.arguments.name || toolCall.arguments.term || '');\n \n const clarification = await ctx.interactiveInstance.handleClarification({\n type: result.data?.clarificationType || 'general',\n term: result.data?.term || termName,\n context: result.userPrompt || '',\n suggestion: result.data?.suggestion,\n options: result.data?.options,\n });\n \n if (clarification.response) {\n state.resolvedEntities.set(termName, clarification.response);\n logger.info('Clarified: %s -> %s', termName, clarification.response);\n \n // Handle new project/term wizard response\n if (result.data?.clarificationType === 'new_project' && clarification.additionalInfo) {\n const wizardResult = clarification.additionalInfo as {\n action: 'create' | 'link' | 'term' | 'skip' | 'ignore';\n projectName?: string;\n destination?: string;\n description?: string;\n linkedProjectIndex?: number;\n linkedTermName?: string;\n aliasName?: string;\n termDescription?: string;\n // For 'term' action\n termName?: string;\n termExpansion?: string;\n termProjects?: number[];\n // For nested project creation from term wizard\n createdProject?: {\n action: 'create' | 'link' | 'skip';\n projectName?: string;\n destination?: string;\n description?: string;\n };\n // For 'ignore' action\n ignoredTerm?: string;\n };\n \n const knownProjects = result.data?.knownProjects as Array<{\n id: string;\n name: string;\n description?: string;\n classification?: { explicit_phrases?: string[]; context_type?: string };\n routing?: { destination: string; structure?: string; filename_options?: string[] };\n }> | undefined;\n \n if (wizardResult.action === 'create') {\n // CREATE NEW PROJECT\n const projectName = wizardResult.projectName || termName;\n const projectId = projectName.toLowerCase().replace(/\\s+/g, '-');\n const projectDestination = wizardResult.destination;\n \n const newProject = {\n id: projectId,\n name: projectName,\n type: 'project' as const,\n description: wizardResult.description || `Project for \"${projectName}\"`,\n classification: {\n context_type: 'work' as const,\n explicit_phrases: [termName.toLowerCase(), projectName.toLowerCase()].filter((v, i, a) => a.indexOf(v) === i),\n },\n routing: {\n // Only include destination if explicitly provided - otherwise uses global default\n ...(projectDestination && { destination: projectDestination }),\n structure: 'month' as const,\n filename_options: ['date', 'time', 'subject'] as Array<'date' | 'time' | 'subject'>,\n },\n active: true,\n };\n \n try {\n await ctx.contextInstance.saveEntity(newProject);\n await ctx.contextInstance.reload(); // Reload so subsequent searches find this entity\n logger.info('Created new project: %s%s', projectName, projectDestination ? ` -> ${projectDestination}` : ' (using default destination)');\n \n contextChanges.push({\n entityType: 'project',\n entityId: projectId,\n entityName: projectName,\n action: 'created',\n details: {\n ...(projectDestination && { destination: projectDestination }),\n description: wizardResult.description,\n triggeredByTerm: termName,\n },\n });\n \n // Update routing if destination was specified\n if (projectDestination) {\n state.routeDecision = {\n projectId,\n destination: { path: projectDestination, structure: 'month' },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: termName, weight: 1.0 }],\n reasoning: `User created new project \"${projectName}\" routing to ${projectDestination}`,\n };\n }\n } catch (error) {\n logger.warn('Failed to save new project: %s', error);\n }\n \n } else if (wizardResult.action === 'link' && wizardResult.linkedTermName) {\n // LINK AS ALIAS TO EXISTING TERM\n const existingTermName = wizardResult.linkedTermName;\n const aliasVariant = wizardResult.aliasName || termName;\n \n // Search for the existing term\n const termSearch = await ctx.contextInstance.search(existingTermName);\n const existingTerm = termSearch.find(e => e.type === 'term' && \n e.name.toLowerCase() === existingTermName.toLowerCase());\n \n if (existingTerm) {\n // Add the new variant to sounds_like\n const existingVariants = (existingTerm as { sounds_like?: string[] }).sounds_like || [];\n const updatedVariants = [...existingVariants, aliasVariant.toLowerCase()]\n .filter((v, i, a) => a.indexOf(v) === i); // dedupe\n \n const updatedTerm = {\n ...existingTerm,\n type: 'term' as const,\n sounds_like: updatedVariants,\n };\n \n try {\n await ctx.contextInstance.saveEntity(updatedTerm);\n await ctx.contextInstance.reload();\n logger.info('Added alias \"%s\" to existing term \"%s\"', aliasVariant, existingTerm.name);\n \n // Mark as resolved\n state.resolvedEntities.set(termName, existingTerm.name);\n state.resolvedEntities.set(aliasVariant, existingTerm.name);\n \n contextChanges.push({\n entityType: 'term',\n entityId: existingTerm.id,\n entityName: existingTerm.name,\n action: 'updated',\n details: {\n addedAlias: aliasVariant,\n sounds_like: updatedVariants,\n },\n });\n \n // If term has associated projects, use for routing\n const termProjects = (existingTerm as { projects?: string[] }).projects || [];\n if (termProjects.length > 0) {\n const allProjects = ctx.contextInstance.getAllProjects();\n const primaryProject = allProjects.find(p => p.id === termProjects[0]);\n if (primaryProject?.routing?.destination) {\n state.routeDecision = {\n projectId: primaryProject.id,\n destination: {\n path: primaryProject.routing.destination,\n structure: 'month'\n },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: existingTerm.name, weight: 1.0 }],\n reasoning: `User linked \"${aliasVariant}\" as alias for term \"${existingTerm.name}\" associated with project \"${primaryProject.name}\"`,\n };\n }\n }\n } catch (error) {\n logger.warn('Failed to add alias to existing term: %s', error);\n }\n } else {\n logger.warn('Could not find existing term \"%s\" to link alias', existingTermName);\n }\n \n } else if (wizardResult.action === 'link' && typeof wizardResult.linkedProjectIndex === 'number') {\n // LINK TO EXISTING PROJECT\n if (knownProjects && wizardResult.linkedProjectIndex < knownProjects.length) {\n const linkedProject = knownProjects[wizardResult.linkedProjectIndex];\n \n // Add the term as an alias\n const existingPhrases = linkedProject.classification?.explicit_phrases || [];\n const updatedPhrases = [...existingPhrases, termName.toLowerCase()]\n .filter((v, i, a) => a.indexOf(v) === i); // dedupe\n \n const updatedProject = {\n ...linkedProject,\n type: 'project' as const,\n // Add term description to project notes if provided\n notes: wizardResult.termDescription \n ? `${linkedProject.description || ''}\\n\\n${termName}: ${wizardResult.termDescription}`.trim()\n : linkedProject.description,\n classification: {\n ...linkedProject.classification,\n context_type: (linkedProject.classification?.context_type || 'work') as 'work' | 'personal' | 'mixed',\n explicit_phrases: updatedPhrases,\n },\n routing: {\n // Preserve existing destination (or omit if not set)\n ...(linkedProject.routing?.destination && { destination: linkedProject.routing.destination }),\n structure: (linkedProject.routing?.structure || 'month') as 'none' | 'year' | 'month' | 'day',\n filename_options: (linkedProject.routing?.filename_options || ['date', 'time']) as Array<'date' | 'time' | 'subject'>,\n },\n };\n \n try {\n await ctx.contextInstance.saveEntity(updatedProject);\n await ctx.contextInstance.reload(); // Reload so subsequent searches find this entity\n logger.info('Linked \"%s\" to project \"%s\"', termName, linkedProject.name);\n \n contextChanges.push({\n entityType: 'project',\n entityId: linkedProject.id,\n entityName: linkedProject.name,\n action: 'updated',\n details: {\n addedAlias: termName,\n termDescription: wizardResult.termDescription,\n explicit_phrases: updatedPhrases,\n },\n });\n \n // Update routing to use the linked project\n if (linkedProject.routing?.destination) {\n state.routeDecision = {\n projectId: linkedProject.id,\n destination: { \n path: linkedProject.routing.destination, \n structure: 'month' \n },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: termName, weight: 1.0 }],\n reasoning: `User linked \"${termName}\" to existing project \"${linkedProject.name}\"`,\n };\n }\n } catch (error) {\n logger.warn('Failed to update project with alias: %s', error);\n }\n }\n } else if (wizardResult.action === 'term') {\n // CREATE NEW TERM ENTITY\n const termNameFinal = wizardResult.termName || termName;\n const termId = termNameFinal.toLowerCase().replace(/\\s+/g, '-');\n \n // Get project IDs from indices\n const projectIds: string[] = [];\n if (wizardResult.termProjects && knownProjects) {\n for (const idx of wizardResult.termProjects) {\n if (idx >= 0 && idx < knownProjects.length) {\n projectIds.push(knownProjects[idx].id);\n }\n }\n }\n \n // Handle nested project creation from term wizard\n if (wizardResult.createdProject?.action === 'create' && wizardResult.createdProject.projectName) {\n const projectName = wizardResult.createdProject.projectName;\n const projectId = projectName.toLowerCase().replace(/\\s+/g, '-');\n const projectDestination = wizardResult.createdProject.destination;\n \n const newProject = {\n id: projectId,\n name: projectName,\n type: 'project' as const,\n description: wizardResult.createdProject.description || `Project for \"${projectName}\"`,\n classification: {\n context_type: 'work' as const,\n explicit_phrases: [projectName.toLowerCase(), termNameFinal.toLowerCase()].filter((v, i, a) => a.indexOf(v) === i),\n },\n routing: {\n // Only include destination if explicitly provided - otherwise uses global default\n ...(projectDestination && { destination: projectDestination }),\n structure: 'month' as const,\n filename_options: ['date', 'time', 'subject'] as Array<'date' | 'time' | 'subject'>,\n },\n active: true,\n };\n \n try {\n await ctx.contextInstance.saveEntity(newProject);\n await ctx.contextInstance.reload(); // Reload so subsequent searches find this entity\n logger.info('Created new project from term wizard: %s%s', projectName, projectDestination ? ` -> ${projectDestination}` : ' (using default destination)');\n \n // Add the new project to the projectIds list for term association\n projectIds.push(projectId);\n \n contextChanges.push({\n entityType: 'project',\n entityId: projectId,\n entityName: projectName,\n action: 'created',\n details: {\n ...(projectDestination && { destination: projectDestination }),\n description: wizardResult.createdProject.description,\n createdForTerm: termNameFinal,\n },\n });\n \n // Update routing to use the new project (if destination was specified)\n if (projectDestination) {\n state.routeDecision = {\n projectId,\n destination: { path: projectDestination, structure: 'month' },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: termNameFinal, weight: 1.0 }],\n reasoning: `User created project \"${projectName}\" for term \"${termNameFinal}\"`,\n };\n }\n } catch (error) {\n logger.warn('Failed to save new project from term wizard: %s', error);\n }\n }\n \n const newTerm = {\n id: termId,\n name: termNameFinal,\n type: 'term' as const,\n expansion: wizardResult.termExpansion,\n notes: wizardResult.termDescription,\n projects: projectIds.length > 0 ? projectIds : undefined,\n sounds_like: [termName.toLowerCase()],\n };\n \n try {\n await ctx.contextInstance.saveEntity(newTerm);\n await ctx.contextInstance.reload(); // Reload so subsequent searches find this entity\n logger.info('Created new term: %s (projects: %s)', \n termNameFinal, \n projectIds.length > 0 ? projectIds.join(', ') : 'none'\n );\n \n contextChanges.push({\n entityType: 'term',\n entityId: termId,\n entityName: termNameFinal,\n action: 'created',\n details: {\n expansion: wizardResult.termExpansion,\n projects: projectIds,\n description: wizardResult.termDescription,\n },\n });\n \n // If term has associated projects and we haven't set routing yet, use the first one\n if (projectIds.length > 0 && !state.routeDecision) {\n // For newly created project, we already set routing above\n // For existing projects, look them up\n if (knownProjects) {\n const primaryProject = knownProjects.find(p => p.id === projectIds[0]);\n if (primaryProject?.routing?.destination) {\n state.routeDecision = {\n projectId: primaryProject.id,\n destination: { \n path: primaryProject.routing.destination, \n structure: 'month' \n },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: termNameFinal, weight: 1.0 }],\n reasoning: `User created term \"${termNameFinal}\" associated with project \"${primaryProject.name}\"`,\n };\n }\n }\n }\n } catch (error) {\n logger.warn('Failed to save new term: %s', error);\n }\n } else if (wizardResult.action === 'ignore' && wizardResult.ignoredTerm) {\n // IGNORE - add term to ignore list so user won't be asked again\n const ignoredTermName = wizardResult.ignoredTerm;\n const ignoredId = ignoredTermName.toLowerCase()\n .replace(/[^a-z0-9]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '');\n \n const newIgnored = {\n id: ignoredId,\n name: ignoredTermName,\n type: 'ignored' as const,\n ignoredAt: new Date().toISOString(),\n };\n \n try {\n await ctx.contextInstance.saveEntity(newIgnored);\n await ctx.contextInstance.reload();\n logger.info('Added to ignore list: %s', ignoredTermName);\n \n contextChanges.push({\n entityType: 'ignored',\n entityId: ignoredId,\n entityName: ignoredTermName,\n action: 'created',\n details: {\n reason: 'User chose to ignore this term',\n },\n });\n } catch (error) {\n logger.warn('Failed to save ignored term: %s', error);\n }\n }\n // 'skip' action - do nothing\n }\n \n // Handle new person wizard response\n if (result.data?.clarificationType === 'new_person' && clarification.additionalInfo) {\n const personWizardResult = clarification.additionalInfo as {\n action: 'create' | 'skip';\n personName?: string;\n organization?: string;\n notes?: string;\n linkedProjectId?: string;\n linkedProjectIndex?: number;\n createdProject?: {\n action: 'create' | 'link' | 'skip';\n projectName?: string;\n destination?: string;\n description?: string;\n };\n };\n \n const knownProjects = result.data?.knownProjects as Array<{\n id: string;\n name: string;\n description?: string;\n classification?: { explicit_phrases?: string[]; context_type?: string };\n routing?: { destination: string; structure?: string; filename_options?: string[] };\n }> | undefined;\n \n if (personWizardResult.action === 'create') {\n let linkedProjectId: string | undefined;\n \n // First, handle any nested project creation\n if (personWizardResult.createdProject?.action === 'create' && personWizardResult.createdProject.projectName) {\n const projectName = personWizardResult.createdProject.projectName;\n const projectId = projectName.toLowerCase().replace(/\\s+/g, '-');\n const projectDestination = personWizardResult.createdProject.destination;\n \n const newProject = {\n id: projectId,\n name: projectName,\n type: 'project' as const,\n description: personWizardResult.createdProject.description || `Project for \"${projectName}\"`,\n classification: {\n context_type: 'work' as const,\n explicit_phrases: [projectName.toLowerCase()],\n },\n routing: {\n // Only include destination if explicitly provided - otherwise uses global default\n ...(projectDestination && { destination: projectDestination }),\n structure: 'month' as const,\n filename_options: ['date', 'time', 'subject'] as Array<'date' | 'time' | 'subject'>,\n },\n active: true,\n };\n \n try {\n await ctx.contextInstance.saveEntity(newProject);\n await ctx.contextInstance.reload(); // Reload so subsequent searches find this entity\n logger.info('Created new project from person wizard: %s%s', projectName, projectDestination ? ` -> ${projectDestination}` : ' (using default destination)');\n linkedProjectId = projectId;\n \n contextChanges.push({\n entityType: 'project',\n entityId: projectId,\n entityName: projectName,\n action: 'created',\n details: {\n ...(projectDestination && { destination: projectDestination }),\n description: personWizardResult.createdProject.description,\n createdForPerson: personWizardResult.personName,\n },\n });\n \n // Update routing to use the new project (if destination was specified)\n if (projectDestination) {\n state.routeDecision = {\n projectId,\n destination: { path: projectDestination, structure: 'month' },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: projectName, weight: 1.0 }],\n reasoning: `User created project \"${projectName}\" for person \"${personWizardResult.personName}\"`,\n };\n }\n } catch (error) {\n logger.warn('Failed to save new project from person wizard: %s', error);\n }\n } else if (typeof personWizardResult.linkedProjectIndex === 'number' && knownProjects) {\n // User linked to existing project\n if (personWizardResult.linkedProjectIndex < knownProjects.length) {\n const linkedProject = knownProjects[personWizardResult.linkedProjectIndex];\n linkedProjectId = linkedProject.id;\n \n // Update routing to use the linked project\n if (linkedProject.routing?.destination) {\n state.routeDecision = {\n projectId: linkedProject.id,\n destination: { \n path: linkedProject.routing.destination, \n structure: 'month' \n },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: personWizardResult.personName || termName, weight: 1.0 }],\n reasoning: `User linked person \"${personWizardResult.personName}\" to project \"${linkedProject.name}\"`,\n };\n }\n }\n }\n \n // Now save the person\n const personName = personWizardResult.personName || termName;\n const personId = personName.toLowerCase().replace(/\\s+/g, '-');\n \n const newPerson = {\n id: personId,\n name: personName,\n type: 'person' as const,\n organization: personWizardResult.organization,\n notes: personWizardResult.notes,\n projects: linkedProjectId ? [linkedProjectId] : [],\n sounds_like: [termName.toLowerCase()],\n };\n \n try {\n await ctx.contextInstance.saveEntity(newPerson);\n await ctx.contextInstance.reload(); // Reload so subsequent searches find this entity\n logger.info('Created new person: %s (org: %s, project: %s)', \n personName, \n personWizardResult.organization || 'none',\n linkedProjectId || 'none'\n );\n \n // Update resolved entities with correct name\n state.resolvedEntities.set(termName, personName);\n \n contextChanges.push({\n entityType: 'person',\n entityId: personId,\n entityName: personName,\n action: 'created',\n details: {\n organization: personWizardResult.organization,\n linkedProject: linkedProjectId,\n notes: personWizardResult.notes,\n heardAs: termName,\n },\n });\n } catch (error) {\n logger.warn('Failed to save new person: %s', error);\n }\n }\n // 'skip' action - do nothing\n }\n }\n }\n */ // End of commented interactive code\n \n // Update state based on tool results\n if (result.data?.person) {\n state.resolvedEntities.set(result.data.person.name, result.data.suggestion);\n // Track person entity reference\n state.referencedEntities.people.add(result.data.person.id);\n }\n \n // Track term entities\n if (result.data?.term) {\n state.referencedEntities.terms.add(result.data.term.id);\n }\n \n // Track company entities\n if (result.data?.company) {\n state.referencedEntities.companies.add(result.data.company.id);\n }\n \n // Capture routing from route_note tool\n if (result.data?.routingDecision?.destination) {\n const routingDecision = result.data.routingDecision;\n state.routeDecision = {\n projectId: routingDecision.projectId,\n destination: routingDecision.destination,\n confidence: routingDecision.confidence || 1.0,\n signals: routingDecision.signals,\n reasoning: routingDecision.reasoning || 'Determined by route_note tool',\n };\n \n // Track project if routing decision includes it\n if (routingDecision.projectId) {\n state.referencedEntities.projects.add(routingDecision.projectId);\n }\n }\n \n // Capture routing from lookup_project when project has routing config\n if (result.data?.found && result.data?.project?.routing?.destination) {\n const project = result.data.project;\n state.routeDecision = {\n projectId: project.id,\n destination: { \n path: project.routing.destination,\n structure: project.routing.structure || 'month',\n },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: project.name, weight: 1.0 }],\n reasoning: `Matched project \"${project.name}\" with routing to ${project.routing.destination}`,\n };\n logger.debug('Captured routing from project lookup: %s -> %s', \n project.name, project.routing.destination);\n \n // Track project entity reference\n state.referencedEntities.projects.add(project.id);\n }\n \n } catch (error) {\n logger.error('Tool execution failed', { tool: toolCall.name, error });\n toolResults.push({ \n id: toolCall.id, \n name: toolCall.name, \n result: JSON.stringify({ error: String(error) }) \n });\n }\n }\n \n // Add tool results to conversation\n for (const tr of toolResults) {\n conversation.addToolResult(tr.id, tr.result, tr.name);\n }\n \n // Build continuation prompt with full context\n const continuationPrompt = `Tool results received. Here's a reminder of your task:\n\nORIGINAL TRANSCRIPT (process this):\n--- BEGIN TRANSCRIPT ---\n${transcriptText}\n--- END TRANSCRIPT ---\n\nCorrections made so far: ${state.resolvedEntities.size > 0 ? Array.from(state.resolvedEntities.entries()).map(([k, v]) => `${k} -> ${v}`).join(', ') : 'none yet'}\n\nContinue analyzing. If you need more information, use the tools. \nWhen you're done with tool calls, output the COMPLETE corrected transcript as Markdown.\nDo NOT summarize - include ALL original content with corrections applied.\n\nCRITICAL REMINDER: Your response must contain ONLY the transcript text. Do NOT include any commentary, explanations, or processing notes - those will leak into the user-facing document.`;\n\n conversation.addUserMessage(continuationPrompt);\n \n // Continue conversation with full context\n response = await reasoning.complete({\n systemPrompt,\n prompt: continuationPrompt,\n tools: registry.getToolDefinitions(),\n });\n \n // Track token usage\n if (response.usage) {\n totalTokens += response.usage.totalTokens;\n }\n \n // Add assistant response to conversation\n if (response.toolCalls && response.toolCalls.length > 0) {\n conversation.addAssistantWithToolCalls(\n response.content,\n toRiotToolCalls(response.toolCalls)\n );\n } else {\n conversation.addAssistantMessage(response.content);\n }\n }\n \n // Extract final corrected text\n if (response.content && response.content.length > 50) {\n // Clean the response to remove any leaked internal processing\n const cleanedContent = cleanResponseContent(response.content);\n \n if (cleanedContent !== response.content) {\n const removedChars = response.content.length - cleanedContent.length;\n logger.warn('Removed leaked internal processing from response (%d -> %d chars, removed %d chars)',\n response.content.length, cleanedContent.length, removedChars);\n \n // Detect severe corruption (>10% of content removed or suspicious patterns)\n const corruptionRatio = removedChars / response.content.length;\n const hasSuspiciousUnicode = /[\\u0530-\\u058F\\u0E00-\\u0E7F\\u4E00-\\u9FFF\\u0A80-\\u0AFF\\u0C00-\\u0C7F]/.test(response.content);\n \n if (corruptionRatio > 0.1 || hasSuspiciousUnicode) {\n logger.error('SEVERE CORRUPTION DETECTED in LLM response (%.1f%% removed, suspicious unicode: %s)',\n corruptionRatio * 100, hasSuspiciousUnicode);\n logger.error('Raw response preview (first 500 chars): %s', \n response.content.substring(0, 500).replace(/\\n/g, '\\\\n'));\n }\n }\n \n state.correctedText = cleanedContent;\n state.confidence = 0.9;\n logger.debug('Final transcript generated: %d characters', cleanedContent.length);\n } else {\n // Model didn't produce content - ask for it explicitly\n logger.debug('Model did not produce transcript, requesting explicitly...');\n \n const finalRequest = `Please output the COMPLETE corrected transcript now.\n\nORIGINAL:\n${transcriptText}\n\nCORRECTIONS TO APPLY:\n${state.resolvedEntities.size > 0 ? Array.from(state.resolvedEntities.entries()).map(([k, v]) => `- \"${k}\" should be \"${v}\"`).join('\\n') : 'None identified'}\n\nOutput the full transcript as clean Markdown. Do NOT summarize.\n\nCRITICAL: Your response must contain ONLY the corrected transcript text - absolutely no commentary, tool information, or explanations.`;\n\n const finalResponse = await reasoning.complete({\n systemPrompt,\n prompt: finalRequest,\n });\n \n // Track token usage\n if (finalResponse.usage) {\n totalTokens += finalResponse.usage.totalTokens;\n }\n \n // Clean the final response as well\n const cleanedFinalContent = cleanResponseContent(finalResponse.content || transcriptText);\n \n if (cleanedFinalContent !== finalResponse.content) {\n const removedChars = (finalResponse.content?.length || 0) - cleanedFinalContent.length;\n logger.warn('Removed leaked internal processing from final response (%d -> %d chars, removed %d chars)',\n finalResponse.content?.length || 0, cleanedFinalContent.length, removedChars);\n \n // Detect severe corruption\n const corruptionRatio = removedChars / (finalResponse.content?.length || 1);\n const hasSuspiciousUnicode = /[\\u0530-\\u058F\\u0E00-\\u0E7F\\u4E00-\\u9FFF\\u0A80-\\u0AFF\\u0C00-\\u0C7F]/.test(finalResponse.content || '');\n \n if (corruptionRatio > 0.1 || hasSuspiciousUnicode) {\n logger.error('SEVERE CORRUPTION DETECTED in final LLM response (%.1f%% removed, suspicious unicode: %s)',\n corruptionRatio * 100, hasSuspiciousUnicode);\n logger.error('Raw response preview (first 500 chars): %s', \n (finalResponse.content || '').substring(0, 500).replace(/\\n/g, '\\\\n'));\n }\n }\n \n state.correctedText = cleanedFinalContent;\n state.confidence = 0.8;\n }\n \n } catch (error) {\n logger.error('Agentic processing failed', { error });\n // Fall back to original text\n state.correctedText = transcriptText;\n state.confidence = 0.5;\n }\n \n return {\n enhancedText: state.correctedText,\n state,\n toolsUsed: [...new Set(toolsUsed)],\n iterations,\n totalTokens: totalTokens > 0 ? totalTokens : undefined,\n contextChanges: contextChanges.length > 0 ? contextChanges : undefined,\n };\n };\n \n return { process };\n};\n\n"],"names":["Logging.getLogger","Registry.create"],"mappings":"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"}
1
+ {"version":3,"file":"index26.js","sources":["../src/agentic/executor.ts"],"sourcesContent":["/**\n * Agentic Executor\n * \n * Executes the agentic transcription loop with tool calls.\n * Uses RiotPrompt's ConversationBuilder for conversation management.\n */\n\nimport { ToolCallLogEntry, ToolContext, TranscriptionState } from './types';\nimport * as Registry from './registry';\nimport * as Reasoning from '../reasoning';\nimport * as Logging from '../logging';\nimport { ConversationBuilder, ToolCall as RiotToolCall } from '@kjerneverk/riotprompt';\nimport { EntityPrepositioner } from '../weighting/prepositioning';\n\nexport interface ContextChangeRecord {\n entityType: 'person' | 'project' | 'company' | 'term' | 'ignored';\n entityId: string;\n entityName: string;\n action: 'created' | 'updated';\n details?: Record<string, unknown>;\n}\n\nexport interface ExecutorInstance {\n process(transcriptText: string): Promise<{\n enhancedText: string;\n state: TranscriptionState;\n toolsUsed: string[];\n iterations: number;\n totalTokens?: number;\n contextChanges?: ContextChangeRecord[];\n }>;\n}\n\n/**\n * Convert internal tool call format to RiotPrompt's ToolCall format\n */\nconst toRiotToolCalls = (toolCalls: Array<{ id: string; name: string; arguments: Record<string, unknown> }>): RiotToolCall[] => {\n return toolCalls.map(tc => ({\n id: tc.id,\n type: 'function' as const,\n function: {\n name: tc.name,\n arguments: JSON.stringify(tc.arguments),\n },\n }));\n};\n\n/**\n * Clean response content by removing any leaked internal processing information\n * that should never appear in the user-facing transcript.\n */\nconst cleanResponseContent = (content: string): string => {\n let cleaned = content;\n\n // Strip markdown code fences - LLMs often wrap output in ```markdown ... ```\n // This avoids \"markdown\" appearing as the first line of the transcript\n cleaned = cleaned.replace(/^```\\s*(?:markdown|md|txt)?\\s*\\r?\\n?/i, '');\n cleaned = cleaned.replace(/\\r?\\n?```\\s*$/m, '');\n // Remove orphaned \"markdown\" line (can occur when AI uses ``` on separate line from language tag)\n cleaned = cleaned.replace(/^\\s*markdown\\s*\\r?\\n/i, '');\n\n // Remove common patterns of leaked internal processing\n // Pattern 1: \"Using tools to...\" type commentary\n cleaned = cleaned.replace(/^(?:Using tools?|Let me|I'll|I will|Now I'll|First,?\\s*I(?:'ll| will)).*?[\\r\\n]+/gim, '');\n \n // Pattern 2: JSON tool call artifacts - match complete JSON objects with \"tool\" key\n // Matches: {\"tool\":\"...\",\"args\":{...}}, {\"tool\":\"...\",\"input\":{...}}, etc.\n // Use a more careful pattern that matches balanced braces\n cleaned = cleaned.replace(/\\{\"tool\":\\s*\"[^\"]+\",\\s*\"(?:args|input)\":\\s*\\{[^}]*\\}\\}/g, '');\n \n // Pattern 3: Tool call references in the format tool_name({...})\n cleaned = cleaned.replace(/\\b\\w+_\\w+\\(\\{[^}]*\\}\\)/g, '');\n \n // Pattern 4: Remove lines with \"to=\" patterns (internal routing artifacts)\n // Matches: \"Այ to=lookup_project.commentary\", \"undefined to=route_note.commentary\"\n // Do this BEFORE Unicode filtering to catch mixed corruption\n cleaned = cleaned.replace(/^.*\\s+to=\\w+\\.\\w+.*$/gm, '');\n \n // Pattern 5: Remove lines that look like spam/SEO (Chinese gambling sites, etc.)\n // Matches lines with Chinese characters followed by \"app\", \"官网\", etc.\n // This is more specific than general Unicode filtering\n const spamPattern = /^.*[\\u4E00-\\u9FFF].*(app|官网|彩票|中彩票).*$/gm;\n cleaned = cleaned.replace(spamPattern, '');\n \n // Pattern 6: Remove lines with suspicious Unicode at the START (corruption indicators)\n // Only remove lines that START with non-Latin scripts (not legitimate content)\n // This catches corruption like \"Այ to=...\" or \"สามสิบเอ็ด\" at line start\n const corruptionStartPattern = /^[\\u0530-\\u058F\\u0E00-\\u0E7F\\u0A80-\\u0AFF\\u0C00-\\u0C7F].*$/gm;\n cleaned = cleaned.replace(corruptionStartPattern, '');\n \n // Pattern 7: Lines that are purely reasoning/commentary before the actual content\n // Look for lines like \"I'll verify...\", \"Checking...\", etc.\n const lines = cleaned.split('\\n');\n let startIndex = 0;\n \n // Skip leading lines that look like internal commentary\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n \n // Skip empty lines\n if (line === '') continue;\n \n // Check if line looks like commentary (starts with action verbs, contains \"tool\", etc.)\n const isCommentary = /^(checking|verifying|looking|searching|analyzing|processing|determining|using|calling|executing|I'm|I am|Let me)/i.test(line)\n || line.includes('tool')\n || line.includes('{\"')\n || line.includes('reasoning')\n || line.includes('undefined');\n \n if (!isCommentary) {\n // This looks like actual content - start from here\n startIndex = i;\n break;\n }\n }\n \n // Rejoin from the first real content line\n if (startIndex > 0) {\n cleaned = lines.slice(startIndex).join('\\n');\n }\n \n // Final cleanup: remove multiple consecutive blank lines\n cleaned = cleaned.replace(/\\n{3,}/g, '\\n\\n');\n \n return cleaned.trim();\n};\n\nexport const create = (\n reasoning: Reasoning.ReasoningInstance,\n ctx: ToolContext\n): ExecutorInstance => {\n const logger = Logging.getLogger();\n const registry = Registry.create(ctx);\n\n const logCleaningWarnings = (original: string, cleaned: string, label: string): void => {\n if (cleaned === original) return;\n const removedChars = original.length - cleaned.length;\n logger.warn('Removed leaked internal processing from %s (%d -> %d chars, removed %d chars)',\n label, original.length, cleaned.length, removedChars);\n const corruptionRatio = removedChars / original.length;\n const hasSuspiciousUnicode = /[\\u0530-\\u058F\\u0E00-\\u0E7F\\u4E00-\\u9FFF\\u0A80-\\u0AFF\\u0C00-\\u0C7F]/.test(original);\n if (corruptionRatio > 0.1 || hasSuspiciousUnicode) {\n logger.error('SEVERE CORRUPTION DETECTED in LLM %s (%.1f%% removed, suspicious unicode: %s)',\n label, corruptionRatio * 100, hasSuspiciousUnicode);\n logger.error('Raw preview (first 500 chars): %s',\n original.substring(0, 500).replace(/\\n/g, '\\\\n'));\n }\n };\n \n const process = async (transcriptText: string) => {\n // Seed referencedEntities from pre-identified entities (from simple-replace phase)\n // so they appear in the transcript metadata even if the LLM doesn't re-look them up.\n const pre = ctx.preIdentifiedEntities;\n const state: TranscriptionState = {\n originalText: transcriptText,\n correctedText: transcriptText,\n unknownEntities: [],\n resolvedEntities: new Map(),\n referencedEntities: {\n people: pre?.people ? new Set(pre.people) : new Set(),\n projects: pre?.projects ? new Set(pre.projects) : new Set(),\n terms: pre?.terms ? new Set(pre.terms) : new Set(),\n companies: pre?.companies ? new Set(pre.companies) : new Set(),\n },\n confidence: 0,\n };\n \n // Make resolvedEntities available to tools so they can avoid re-asking\n ctx.resolvedEntities = state.resolvedEntities;\n \n const toolsUsed: string[] = [];\n const contextChanges: ContextChangeRecord[] = [];\n let iterations = 0;\n let totalTokens = 0;\n const maxIterations = 20;\n \n // Use ConversationBuilder for conversation management with token budget\n const conversation = ConversationBuilder.create({ model: 'gpt-4o' })\n .withTokenBudget({\n max: 100000, // 100k token context window\n reserveForResponse: 4000, // Reserve 4k tokens for response\n strategy: 'summarize', // Summarize old messages if budget exceeded\n onBudgetExceeded: 'compress', // Automatically compress when exceeded\n preserveSystem: true, // Always keep system messages\n preserveRecent: 5 // Keep last 5 messages\n });\n \n // Generate entity prepositioning guidance if weight model is available\n // Note: At this point, routing hasn't happened yet, so we can't use project-specific predictions\n // The weight model will still provide co-occurrence predictions based on any entities found\n const prepositioner = ctx.weightModelProvider\n ? new EntityPrepositioner(ctx.weightModelProvider, ctx.contextInstance)\n : null;\n \n const entityGuidance = prepositioner\n ? prepositioner.generateGuidance(undefined) // No project ID yet\n : { likelyEntities: [], guidance: '' };\n\n // Build the system prompt\n let systemPrompt = `You are a light-touch transcription enhancer. You clean up raw voice transcripts with minimal changes — the output should read very close to the input, just better formatted.\n\n## Your job:\n1. Use the available tools to verify entity names and determine routing\n2. Lightly format the transcript for readability\n3. Preserve the speaker's words as closely as possible\n\n## What to do:\n- **Paragraphs**: Break the wall of text into paragraphs where the speaker shifts to a new idea or topic\n- **Headings**: Add a brief ## heading when there is a clear topic change\n- **Entity corrections**: Fix misspelled names/terms using tool lookups\n- **Light cleanup**: Remove obvious filler (um, uh) and false starts, but keep the speaker's natural phrasing otherwise\n- **Formatting**: Use bullet points only where the speaker is clearly listing items\n\n## What NOT to do:\n- Do NOT rewrite sentences or change the speaker's wording beyond filler removal\n- Do NOT add content, interpretation, or editorial commentary\n- Do NOT summarize or condense — the output should be approximately the same length\n- Do NOT over-structure — only add headings where topics genuinely change\n- Do NOT remove conversational asides or tangents — they are part of the content\n\n## OUTPUT REQUIREMENTS:\n- Your final response MUST contain ONLY the enhanced transcript as Markdown\n- DO NOT wrap in code blocks (no \\`\\`\\`markdown)\n- DO NOT include commentary, explanations, or processing notes\n- DO NOT narrate tool usage — use tools silently\n- Your output goes directly into the user-facing document\n\n## Available tools:\n- lookup_person: Verify spelling of people's names\n- lookup_project: Find project routing information\n- verify_spelling: Ask about unknown terms (if interactive mode)\n- route_note: Determine where to file this note\n- store_context: Remember new information for future use\n\n## Tool call discipline — IMPORTANT:\n- You have a budget of approximately 5–8 tool calls for the ENTIRE session\n- Call lookup_person only for names that are clearly misspelled or genuinely ambiguous\n- Call route_note exactly ONCE to determine filing destination\n- Do NOT call the same tool with the same arguments more than once\n- Once you have the key entity corrections and routing, produce the output immediately — do not keep calling tools`;\n\n // Add entity guidance if available\n if (entityGuidance.guidance) {\n systemPrompt += `\\n\\n## Entity Guidance\\n${entityGuidance.guidance}`;\n systemPrompt += '\\n\\nUse this guidance to improve entity recognition, but verify entities exist in the context before referencing them.';\n }\n\n // Inform the LLM about entities already matched by the simple-replace phase\n if (ctx.preIdentifiedEntities) {\n const lines: string[] = [];\n const { preIdentifiedEntities: pre } = ctx;\n\n for (const termId of pre.terms) {\n const term = ctx.contextInstance.getTerm(termId);\n if (term) lines.push(`- Term: **${term.name}** (id: ${term.id})`);\n }\n for (const projectId of pre.projects) {\n const project = ctx.contextInstance.getProject(projectId);\n if (project) lines.push(`- Project: **${project.name}** (id: ${project.id})`);\n }\n for (const personId of pre.people) {\n const person = ctx.contextInstance.getPerson(personId);\n if (person) lines.push(`- Person: **${person.name}** (id: ${person.id})`);\n }\n for (const companyId of pre.companies) {\n const company = ctx.contextInstance.getCompany(companyId);\n if (company) lines.push(`- Company: **${company.name}** (id: ${company.id})`);\n }\n\n if (lines.length > 0) {\n systemPrompt += `\\n\\n## Pre-matched Entities\\nThe following entities were identified via sounds_like matching before you received the transcript. Their names have already been corrected in the text — do NOT call lookup tools for these:\\n${lines.join('\\n')}`;\n }\n }\n\n // Add system message using ConversationBuilder\n conversation.addSystemMessage(systemPrompt);\n \n // Add the initial user message with transcript\n const initialPrompt = `Here is a raw voice transcript to clean up:\n\n--- BEGIN TRANSCRIPT ---\n${transcriptText}\n--- END TRANSCRIPT ---\n\nSteps:\n1. Use lookup_person for any names that might be misspelled\n2. Use route_note to determine where to file this note\n3. Then output the transcript with light formatting: paragraphs at topic shifts, a heading where topics clearly change, filler words removed. Keep the speaker's own words.`;\n\n conversation.addUserMessage(initialPrompt);\n\n try {\n // Initial reasoning call\n logger.debug('Starting agentic transcription - analyzing for names and routing...');\n let response = await reasoning.complete({\n systemPrompt,\n prompt: initialPrompt,\n tools: registry.getToolDefinitions(),\n maxIterations,\n });\n \n // Track token usage\n if (response.usage) {\n totalTokens += response.usage.totalTokens;\n }\n \n // Add assistant response to conversation\n if (response.toolCalls && response.toolCalls.length > 0) {\n conversation.addAssistantWithToolCalls(\n response.content,\n toRiotToolCalls(response.toolCalls)\n );\n } else {\n conversation.addAssistantMessage(response.content);\n }\n \n // Iterative tool use loop\n while (response.toolCalls && response.toolCalls.length > 0 && iterations < maxIterations) {\n iterations++;\n logger.debug('Iteration %d: Processing %d tool calls...', iterations, response.toolCalls.length);\n \n // Collect tool results\n const toolResults: Array<{ id: string; name: string; result: string }> = [];\n \n // Execute each tool call\n for (const toolCall of response.toolCalls) {\n logger.debug('Executing tool: %s', toolCall.name);\n toolsUsed.push(toolCall.name);\n\n // Notify caller that a tool is starting\n ctx.onToolCallStart?.(toolCall.name, toolCall.arguments);\n \n const callStart = Date.now();\n try {\n const result = await registry.executeTool(toolCall.name, toolCall.arguments);\n \n // Format result for the model\n const resultStr = JSON.stringify(result.data || { success: result.success, message: result.error || 'OK' });\n toolResults.push({ id: toolCall.id, name: toolCall.name, result: resultStr });\n \n logger.debug('Tool %s result: %s', toolCall.name, result.success ? 'success' : 'failed');\n\n // Notify caller that the tool completed\n const callEntry: ToolCallLogEntry = {\n tool: toolCall.name,\n input: toolCall.arguments,\n output: result.data ?? { success: result.success, message: result.error || 'OK' },\n durationMs: Date.now() - callStart,\n success: result.success,\n timestamp: new Date(),\n };\n ctx.onToolCallComplete?.(callEntry);\n \n // Handle results that need user input\n // Interactive functionality moved to protokoll-cli\n /* \n // eslint-disable-next-line no-constant-condition\n if (result.needsUserInput && false) {\n logger.info('Interactive: %s requires clarification', toolCall.name);\n \n const termName = String(toolCall.arguments.name || toolCall.arguments.term || '');\n \n const clarification = await ctx.interactiveInstance.handleClarification({\n type: result.data?.clarificationType || 'general',\n term: result.data?.term || termName,\n context: result.userPrompt || '',\n suggestion: result.data?.suggestion,\n options: result.data?.options,\n });\n \n if (clarification.response) {\n state.resolvedEntities.set(termName, clarification.response);\n logger.info('Clarified: %s -> %s', termName, clarification.response);\n \n // Handle new project/term wizard response\n if (result.data?.clarificationType === 'new_project' && clarification.additionalInfo) {\n const wizardResult = clarification.additionalInfo as {\n action: 'create' | 'link' | 'term' | 'skip' | 'ignore';\n projectName?: string;\n destination?: string;\n description?: string;\n linkedProjectIndex?: number;\n linkedTermName?: string;\n aliasName?: string;\n termDescription?: string;\n // For 'term' action\n termName?: string;\n termExpansion?: string;\n termProjects?: number[];\n // For nested project creation from term wizard\n createdProject?: {\n action: 'create' | 'link' | 'skip';\n projectName?: string;\n destination?: string;\n description?: string;\n };\n // For 'ignore' action\n ignoredTerm?: string;\n };\n \n const knownProjects = result.data?.knownProjects as Array<{\n id: string;\n name: string;\n description?: string;\n classification?: { explicit_phrases?: string[]; context_type?: string };\n routing?: { destination: string; structure?: string; filename_options?: string[] };\n }> | undefined;\n \n if (wizardResult.action === 'create') {\n // CREATE NEW PROJECT\n const projectName = wizardResult.projectName || termName;\n const projectId = projectName.toLowerCase().replace(/\\s+/g, '-');\n const projectDestination = wizardResult.destination;\n \n const newProject = {\n id: projectId,\n name: projectName,\n type: 'project' as const,\n description: wizardResult.description || `Project for \"${projectName}\"`,\n classification: {\n context_type: 'work' as const,\n explicit_phrases: [termName.toLowerCase(), projectName.toLowerCase()].filter((v, i, a) => a.indexOf(v) === i),\n },\n routing: {\n // Only include destination if explicitly provided - otherwise uses global default\n ...(projectDestination && { destination: projectDestination }),\n structure: 'month' as const,\n filename_options: ['date', 'time', 'subject'] as Array<'date' | 'time' | 'subject'>,\n },\n active: true,\n };\n \n try {\n await ctx.contextInstance.saveEntity(newProject);\n await ctx.contextInstance.reload(); // Reload so subsequent searches find this entity\n logger.info('Created new project: %s%s', projectName, projectDestination ? ` -> ${projectDestination}` : ' (using default destination)');\n \n contextChanges.push({\n entityType: 'project',\n entityId: projectId,\n entityName: projectName,\n action: 'created',\n details: {\n ...(projectDestination && { destination: projectDestination }),\n description: wizardResult.description,\n triggeredByTerm: termName,\n },\n });\n \n // Update routing if destination was specified\n if (projectDestination) {\n state.routeDecision = {\n projectId,\n destination: { path: projectDestination, structure: 'month' },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: termName, weight: 1.0 }],\n reasoning: `User created new project \"${projectName}\" routing to ${projectDestination}`,\n };\n }\n } catch (error) {\n logger.warn('Failed to save new project: %s', error);\n }\n \n } else if (wizardResult.action === 'link' && wizardResult.linkedTermName) {\n // LINK AS ALIAS TO EXISTING TERM\n const existingTermName = wizardResult.linkedTermName;\n const aliasVariant = wizardResult.aliasName || termName;\n \n // Search for the existing term\n const termSearch = await ctx.contextInstance.search(existingTermName);\n const existingTerm = termSearch.find(e => e.type === 'term' && \n e.name.toLowerCase() === existingTermName.toLowerCase());\n \n if (existingTerm) {\n // Add the new variant to sounds_like\n const existingVariants = (existingTerm as { sounds_like?: string[] }).sounds_like || [];\n const updatedVariants = [...existingVariants, aliasVariant.toLowerCase()]\n .filter((v, i, a) => a.indexOf(v) === i); // dedupe\n \n const updatedTerm = {\n ...existingTerm,\n type: 'term' as const,\n sounds_like: updatedVariants,\n };\n \n try {\n await ctx.contextInstance.saveEntity(updatedTerm);\n await ctx.contextInstance.reload();\n logger.info('Added alias \"%s\" to existing term \"%s\"', aliasVariant, existingTerm.name);\n \n // Mark as resolved\n state.resolvedEntities.set(termName, existingTerm.name);\n state.resolvedEntities.set(aliasVariant, existingTerm.name);\n \n contextChanges.push({\n entityType: 'term',\n entityId: existingTerm.id,\n entityName: existingTerm.name,\n action: 'updated',\n details: {\n addedAlias: aliasVariant,\n sounds_like: updatedVariants,\n },\n });\n \n // If term has associated projects, use for routing\n const termProjects = (existingTerm as { projects?: string[] }).projects || [];\n if (termProjects.length > 0) {\n const allProjects = ctx.contextInstance.getAllProjects();\n const primaryProject = allProjects.find(p => p.id === termProjects[0]);\n if (primaryProject?.routing?.destination) {\n state.routeDecision = {\n projectId: primaryProject.id,\n destination: {\n path: primaryProject.routing.destination,\n structure: 'month'\n },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: existingTerm.name, weight: 1.0 }],\n reasoning: `User linked \"${aliasVariant}\" as alias for term \"${existingTerm.name}\" associated with project \"${primaryProject.name}\"`,\n };\n }\n }\n } catch (error) {\n logger.warn('Failed to add alias to existing term: %s', error);\n }\n } else {\n logger.warn('Could not find existing term \"%s\" to link alias', existingTermName);\n }\n \n } else if (wizardResult.action === 'link' && typeof wizardResult.linkedProjectIndex === 'number') {\n // LINK TO EXISTING PROJECT\n if (knownProjects && wizardResult.linkedProjectIndex < knownProjects.length) {\n const linkedProject = knownProjects[wizardResult.linkedProjectIndex];\n \n // Add the term as an alias\n const existingPhrases = linkedProject.classification?.explicit_phrases || [];\n const updatedPhrases = [...existingPhrases, termName.toLowerCase()]\n .filter((v, i, a) => a.indexOf(v) === i); // dedupe\n \n const updatedProject = {\n ...linkedProject,\n type: 'project' as const,\n // Add term description to project notes if provided\n notes: wizardResult.termDescription \n ? `${linkedProject.description || ''}\\n\\n${termName}: ${wizardResult.termDescription}`.trim()\n : linkedProject.description,\n classification: {\n ...linkedProject.classification,\n context_type: (linkedProject.classification?.context_type || 'work') as 'work' | 'personal' | 'mixed',\n explicit_phrases: updatedPhrases,\n },\n routing: {\n // Preserve existing destination (or omit if not set)\n ...(linkedProject.routing?.destination && { destination: linkedProject.routing.destination }),\n structure: (linkedProject.routing?.structure || 'month') as 'none' | 'year' | 'month' | 'day',\n filename_options: (linkedProject.routing?.filename_options || ['date', 'time']) as Array<'date' | 'time' | 'subject'>,\n },\n };\n \n try {\n await ctx.contextInstance.saveEntity(updatedProject);\n await ctx.contextInstance.reload(); // Reload so subsequent searches find this entity\n logger.info('Linked \"%s\" to project \"%s\"', termName, linkedProject.name);\n \n contextChanges.push({\n entityType: 'project',\n entityId: linkedProject.id,\n entityName: linkedProject.name,\n action: 'updated',\n details: {\n addedAlias: termName,\n termDescription: wizardResult.termDescription,\n explicit_phrases: updatedPhrases,\n },\n });\n \n // Update routing to use the linked project\n if (linkedProject.routing?.destination) {\n state.routeDecision = {\n projectId: linkedProject.id,\n destination: { \n path: linkedProject.routing.destination, \n structure: 'month' \n },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: termName, weight: 1.0 }],\n reasoning: `User linked \"${termName}\" to existing project \"${linkedProject.name}\"`,\n };\n }\n } catch (error) {\n logger.warn('Failed to update project with alias: %s', error);\n }\n }\n } else if (wizardResult.action === 'term') {\n // CREATE NEW TERM ENTITY\n const termNameFinal = wizardResult.termName || termName;\n const termId = termNameFinal.toLowerCase().replace(/\\s+/g, '-');\n \n // Get project IDs from indices\n const projectIds: string[] = [];\n if (wizardResult.termProjects && knownProjects) {\n for (const idx of wizardResult.termProjects) {\n if (idx >= 0 && idx < knownProjects.length) {\n projectIds.push(knownProjects[idx].id);\n }\n }\n }\n \n // Handle nested project creation from term wizard\n if (wizardResult.createdProject?.action === 'create' && wizardResult.createdProject.projectName) {\n const projectName = wizardResult.createdProject.projectName;\n const projectId = projectName.toLowerCase().replace(/\\s+/g, '-');\n const projectDestination = wizardResult.createdProject.destination;\n \n const newProject = {\n id: projectId,\n name: projectName,\n type: 'project' as const,\n description: wizardResult.createdProject.description || `Project for \"${projectName}\"`,\n classification: {\n context_type: 'work' as const,\n explicit_phrases: [projectName.toLowerCase(), termNameFinal.toLowerCase()].filter((v, i, a) => a.indexOf(v) === i),\n },\n routing: {\n // Only include destination if explicitly provided - otherwise uses global default\n ...(projectDestination && { destination: projectDestination }),\n structure: 'month' as const,\n filename_options: ['date', 'time', 'subject'] as Array<'date' | 'time' | 'subject'>,\n },\n active: true,\n };\n \n try {\n await ctx.contextInstance.saveEntity(newProject);\n await ctx.contextInstance.reload(); // Reload so subsequent searches find this entity\n logger.info('Created new project from term wizard: %s%s', projectName, projectDestination ? ` -> ${projectDestination}` : ' (using default destination)');\n \n // Add the new project to the projectIds list for term association\n projectIds.push(projectId);\n \n contextChanges.push({\n entityType: 'project',\n entityId: projectId,\n entityName: projectName,\n action: 'created',\n details: {\n ...(projectDestination && { destination: projectDestination }),\n description: wizardResult.createdProject.description,\n createdForTerm: termNameFinal,\n },\n });\n \n // Update routing to use the new project (if destination was specified)\n if (projectDestination) {\n state.routeDecision = {\n projectId,\n destination: { path: projectDestination, structure: 'month' },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: termNameFinal, weight: 1.0 }],\n reasoning: `User created project \"${projectName}\" for term \"${termNameFinal}\"`,\n };\n }\n } catch (error) {\n logger.warn('Failed to save new project from term wizard: %s', error);\n }\n }\n \n const newTerm = {\n id: termId,\n name: termNameFinal,\n type: 'term' as const,\n expansion: wizardResult.termExpansion,\n notes: wizardResult.termDescription,\n projects: projectIds.length > 0 ? projectIds : undefined,\n sounds_like: [termName.toLowerCase()],\n };\n \n try {\n await ctx.contextInstance.saveEntity(newTerm);\n await ctx.contextInstance.reload(); // Reload so subsequent searches find this entity\n logger.info('Created new term: %s (projects: %s)', \n termNameFinal, \n projectIds.length > 0 ? projectIds.join(', ') : 'none'\n );\n \n contextChanges.push({\n entityType: 'term',\n entityId: termId,\n entityName: termNameFinal,\n action: 'created',\n details: {\n expansion: wizardResult.termExpansion,\n projects: projectIds,\n description: wizardResult.termDescription,\n },\n });\n \n // If term has associated projects and we haven't set routing yet, use the first one\n if (projectIds.length > 0 && !state.routeDecision) {\n // For newly created project, we already set routing above\n // For existing projects, look them up\n if (knownProjects) {\n const primaryProject = knownProjects.find(p => p.id === projectIds[0]);\n if (primaryProject?.routing?.destination) {\n state.routeDecision = {\n projectId: primaryProject.id,\n destination: { \n path: primaryProject.routing.destination, \n structure: 'month' \n },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: termNameFinal, weight: 1.0 }],\n reasoning: `User created term \"${termNameFinal}\" associated with project \"${primaryProject.name}\"`,\n };\n }\n }\n }\n } catch (error) {\n logger.warn('Failed to save new term: %s', error);\n }\n } else if (wizardResult.action === 'ignore' && wizardResult.ignoredTerm) {\n // IGNORE - add term to ignore list so user won't be asked again\n const ignoredTermName = wizardResult.ignoredTerm;\n const ignoredId = ignoredTermName.toLowerCase()\n .replace(/[^a-z0-9]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '');\n \n const newIgnored = {\n id: ignoredId,\n name: ignoredTermName,\n type: 'ignored' as const,\n ignoredAt: new Date().toISOString(),\n };\n \n try {\n await ctx.contextInstance.saveEntity(newIgnored);\n await ctx.contextInstance.reload();\n logger.info('Added to ignore list: %s', ignoredTermName);\n \n contextChanges.push({\n entityType: 'ignored',\n entityId: ignoredId,\n entityName: ignoredTermName,\n action: 'created',\n details: {\n reason: 'User chose to ignore this term',\n },\n });\n } catch (error) {\n logger.warn('Failed to save ignored term: %s', error);\n }\n }\n // 'skip' action - do nothing\n }\n \n // Handle new person wizard response\n if (result.data?.clarificationType === 'new_person' && clarification.additionalInfo) {\n const personWizardResult = clarification.additionalInfo as {\n action: 'create' | 'skip';\n personName?: string;\n organization?: string;\n notes?: string;\n linkedProjectId?: string;\n linkedProjectIndex?: number;\n createdProject?: {\n action: 'create' | 'link' | 'skip';\n projectName?: string;\n destination?: string;\n description?: string;\n };\n };\n \n const knownProjects = result.data?.knownProjects as Array<{\n id: string;\n name: string;\n description?: string;\n classification?: { explicit_phrases?: string[]; context_type?: string };\n routing?: { destination: string; structure?: string; filename_options?: string[] };\n }> | undefined;\n \n if (personWizardResult.action === 'create') {\n let linkedProjectId: string | undefined;\n \n // First, handle any nested project creation\n if (personWizardResult.createdProject?.action === 'create' && personWizardResult.createdProject.projectName) {\n const projectName = personWizardResult.createdProject.projectName;\n const projectId = projectName.toLowerCase().replace(/\\s+/g, '-');\n const projectDestination = personWizardResult.createdProject.destination;\n \n const newProject = {\n id: projectId,\n name: projectName,\n type: 'project' as const,\n description: personWizardResult.createdProject.description || `Project for \"${projectName}\"`,\n classification: {\n context_type: 'work' as const,\n explicit_phrases: [projectName.toLowerCase()],\n },\n routing: {\n // Only include destination if explicitly provided - otherwise uses global default\n ...(projectDestination && { destination: projectDestination }),\n structure: 'month' as const,\n filename_options: ['date', 'time', 'subject'] as Array<'date' | 'time' | 'subject'>,\n },\n active: true,\n };\n \n try {\n await ctx.contextInstance.saveEntity(newProject);\n await ctx.contextInstance.reload(); // Reload so subsequent searches find this entity\n logger.info('Created new project from person wizard: %s%s', projectName, projectDestination ? ` -> ${projectDestination}` : ' (using default destination)');\n linkedProjectId = projectId;\n \n contextChanges.push({\n entityType: 'project',\n entityId: projectId,\n entityName: projectName,\n action: 'created',\n details: {\n ...(projectDestination && { destination: projectDestination }),\n description: personWizardResult.createdProject.description,\n createdForPerson: personWizardResult.personName,\n },\n });\n \n // Update routing to use the new project (if destination was specified)\n if (projectDestination) {\n state.routeDecision = {\n projectId,\n destination: { path: projectDestination, structure: 'month' },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: projectName, weight: 1.0 }],\n reasoning: `User created project \"${projectName}\" for person \"${personWizardResult.personName}\"`,\n };\n }\n } catch (error) {\n logger.warn('Failed to save new project from person wizard: %s', error);\n }\n } else if (typeof personWizardResult.linkedProjectIndex === 'number' && knownProjects) {\n // User linked to existing project\n if (personWizardResult.linkedProjectIndex < knownProjects.length) {\n const linkedProject = knownProjects[personWizardResult.linkedProjectIndex];\n linkedProjectId = linkedProject.id;\n \n // Update routing to use the linked project\n if (linkedProject.routing?.destination) {\n state.routeDecision = {\n projectId: linkedProject.id,\n destination: { \n path: linkedProject.routing.destination, \n structure: 'month' \n },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: personWizardResult.personName || termName, weight: 1.0 }],\n reasoning: `User linked person \"${personWizardResult.personName}\" to project \"${linkedProject.name}\"`,\n };\n }\n }\n }\n \n // Now save the person\n const personName = personWizardResult.personName || termName;\n const personId = personName.toLowerCase().replace(/\\s+/g, '-');\n \n const newPerson = {\n id: personId,\n name: personName,\n type: 'person' as const,\n organization: personWizardResult.organization,\n notes: personWizardResult.notes,\n projects: linkedProjectId ? [linkedProjectId] : [],\n sounds_like: [termName.toLowerCase()],\n };\n \n try {\n await ctx.contextInstance.saveEntity(newPerson);\n await ctx.contextInstance.reload(); // Reload so subsequent searches find this entity\n logger.info('Created new person: %s (org: %s, project: %s)', \n personName, \n personWizardResult.organization || 'none',\n linkedProjectId || 'none'\n );\n \n // Update resolved entities with correct name\n state.resolvedEntities.set(termName, personName);\n \n contextChanges.push({\n entityType: 'person',\n entityId: personId,\n entityName: personName,\n action: 'created',\n details: {\n organization: personWizardResult.organization,\n linkedProject: linkedProjectId,\n notes: personWizardResult.notes,\n heardAs: termName,\n },\n });\n } catch (error) {\n logger.warn('Failed to save new person: %s', error);\n }\n }\n // 'skip' action - do nothing\n }\n }\n }\n */ // End of commented interactive code\n \n // Update state based on tool results\n if (result.data?.person) {\n state.resolvedEntities.set(result.data.person.name, result.data.suggestion);\n // Track person entity reference\n state.referencedEntities.people.add(result.data.person.id);\n }\n \n // Track term entities\n if (result.data?.term) {\n state.referencedEntities.terms.add(result.data.term.id);\n }\n \n // Track company entities\n if (result.data?.company) {\n state.referencedEntities.companies.add(result.data.company.id);\n }\n \n // Capture routing from route_note tool\n if (result.data?.routingDecision?.destination) {\n const routingDecision = result.data.routingDecision;\n state.routeDecision = {\n projectId: routingDecision.projectId,\n destination: routingDecision.destination,\n confidence: routingDecision.confidence || 1.0,\n signals: routingDecision.signals,\n reasoning: routingDecision.reasoning || 'Determined by route_note tool',\n };\n \n // Track project if routing decision includes it\n if (routingDecision.projectId) {\n state.referencedEntities.projects.add(routingDecision.projectId);\n }\n }\n \n // Capture routing from lookup_project when project has routing config\n if (result.data?.found && result.data?.project?.routing?.destination) {\n const project = result.data.project;\n state.routeDecision = {\n projectId: project.id,\n destination: { \n path: project.routing.destination,\n structure: project.routing.structure || 'month',\n },\n confidence: 1.0,\n signals: [{ type: 'explicit_phrase', value: project.name, weight: 1.0 }],\n reasoning: `Matched project \"${project.name}\" with routing to ${project.routing.destination}`,\n };\n logger.debug('Captured routing from project lookup: %s -> %s', \n project.name, project.routing.destination);\n \n // Track project entity reference\n state.referencedEntities.projects.add(project.id);\n }\n \n } catch (error) {\n logger.error('Tool execution failed', { tool: toolCall.name, error });\n toolResults.push({ \n id: toolCall.id, \n name: toolCall.name, \n result: JSON.stringify({ error: String(error) }) \n });\n ctx.onToolCallComplete?.({\n tool: toolCall.name,\n input: toolCall.arguments,\n output: { error: String(error) },\n durationMs: Date.now() - callStart,\n success: false,\n timestamp: new Date(),\n });\n }\n }\n \n // Add tool results to conversation\n for (const tr of toolResults) {\n conversation.addToolResult(tr.id, tr.result, tr.name);\n }\n \n // Build continuation prompt with full context\n const correctionsNote = state.resolvedEntities.size > 0\n ? `\\nConfirmed corrections: ${Array.from(state.resolvedEntities.entries()).map(([k, v]) => `\"${k}\" → \"${v}\"`).join(', ')}`\n : '';\n\n const toolHistory = toolsUsed.length > 0\n ? `\\nTools called so far (do NOT repeat these): ${toolsUsed.join(', ')}`\n : '';\n\n const urgencyNote = toolsUsed.length >= 8\n ? `\\n\\n🛑 STOP CALLING TOOLS. You have made ${toolsUsed.length} tool calls. Output the formatted transcript NOW — no more tool calls.`\n : toolsUsed.length >= 5\n ? `\\n\\n⚠️ You have made ${toolsUsed.length} tool calls. Make at most 1–2 more critical lookups, then output immediately.`\n : '';\n\n const outputInstruction = toolsUsed.length >= 8\n ? 'Output the formatted transcript immediately. No more tool calls.'\n : 'If you have 1–2 remaining critical lookups, do them now. Then output the lightly formatted transcript: paragraphs at topic shifts, headings where topics clearly change, filler words removed. Keep the speaker\\'s own words — do not rewrite.';\n\n const continuationPrompt = `Tool results processed (iteration ${iterations}, ${toolsUsed.length} tool calls made).${correctionsNote}${toolHistory}${urgencyNote}\n\nORIGINAL TRANSCRIPT (you must use this):\n--- BEGIN TRANSCRIPT ---\n${transcriptText}\n--- END TRANSCRIPT ---\n\n${outputInstruction}`;\n\n conversation.addUserMessage(continuationPrompt);\n \n // Continue conversation with full context\n response = await reasoning.complete({\n systemPrompt,\n prompt: continuationPrompt,\n tools: registry.getToolDefinitions(),\n });\n \n // Track token usage\n if (response.usage) {\n totalTokens += response.usage.totalTokens;\n }\n \n // Add assistant response to conversation\n if (response.toolCalls && response.toolCalls.length > 0) {\n conversation.addAssistantWithToolCalls(\n response.content,\n toRiotToolCalls(response.toolCalls)\n );\n } else {\n conversation.addAssistantMessage(response.content);\n }\n }\n \n // Extract final corrected text\n const needsFinalRequest = !response.content || response.content.length <= 50;\n \n if (needsFinalRequest) {\n if (iterations >= maxIterations) {\n logger.warn('Hit max iterations (%d) without final transcript — requesting explicitly (no tools)', maxIterations);\n } else {\n logger.debug('Model did not produce transcript content, requesting explicitly...');\n }\n \n const correctionsBlock = state.resolvedEntities.size > 0\n ? Array.from(state.resolvedEntities.entries()).map(([k, v]) => `- \"${k}\" should be \"${v}\"`).join('\\n')\n : 'None identified';\n \n const finalRequest = `You have finished analyzing. Now output the lightly formatted transcript.\n\nORIGINAL TRANSCRIPT:\n--- BEGIN ---\n${transcriptText}\n--- END ---\n\nCORRECTIONS TO APPLY:\n${correctionsBlock}\n\nRules:\n- Break into paragraphs where the speaker shifts ideas\n- Add a ## heading only where topics clearly change\n- Remove filler words (um, uh) and false starts\n- Apply entity corrections listed above\n- Keep the speaker's own words — do not rewrite or rephrase\n- Preserve ALL content including asides and tangents\n- Output ONLY the formatted transcript`;\n\n const finalResponse = await reasoning.complete({\n systemPrompt,\n prompt: finalRequest,\n });\n \n if (finalResponse.usage) {\n totalTokens += finalResponse.usage.totalTokens;\n }\n \n if (finalResponse.content && finalResponse.content.length > 50) {\n const cleanedFinalContent = cleanResponseContent(finalResponse.content);\n logCleaningWarnings(finalResponse.content, cleanedFinalContent, 'final');\n state.correctedText = cleanedFinalContent;\n state.confidence = 0.8;\n logger.debug('Final transcript from explicit request: %d characters', cleanedFinalContent.length);\n } else {\n logger.error('Enhancement FAILED: explicit request produced no content (%d chars). Falling back to raw transcript.',\n finalResponse.content?.length || 0);\n state.correctedText = transcriptText;\n state.confidence = 0.5;\n }\n } else {\n const cleanedContent = cleanResponseContent(response.content);\n logCleaningWarnings(response.content, cleanedContent, 'response');\n state.correctedText = cleanedContent;\n state.confidence = 0.9;\n logger.debug('Final transcript generated: %d characters', cleanedContent.length);\n }\n \n } catch (error) {\n logger.error('Agentic processing failed', { error });\n // Fall back to original text\n state.correctedText = transcriptText;\n state.confidence = 0.5;\n }\n \n return {\n enhancedText: state.correctedText,\n state,\n toolsUsed: [...new Set(toolsUsed)],\n iterations,\n totalTokens: totalTokens > 0 ? totalTokens : undefined,\n contextChanges: contextChanges.length > 0 ? contextChanges : undefined,\n };\n };\n \n return { process };\n};\n\n"],"names":["Logging.getLogger","Registry.create","pre"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index28.js","sources":["../src/agentic/types.ts"],"sourcesContent":["/**\n * Agentic Transcription Types\n *\n * Types for tool-based transcription enhancement.\n * Includes Zod schemas for validation and documentation.\n */\n\nimport { z } from 'zod';\nimport * as Context from '@redaksjon/context';\nimport * as Routing from '../routing';\nimport type { WeightModelProvider } from '../weighting/provider';\n\n// ============================================================================\n// Zod Schemas for Structured Outputs\n// ============================================================================\n\n/**\n * Schema for a corrected entity (spelling/name correction)\n */\nexport const CorrectedEntitySchema = z.object({\n original: z.string().describe('Original text from transcript'),\n corrected: z.string().describe('Corrected spelling/name'),\n type: z.enum(['person', 'project', 'term', 'company']).describe('Entity type'),\n confidence: z.number().min(0).max(1).describe('Confidence in correction'),\n});\n\nexport type CorrectedEntity = z.infer<typeof CorrectedEntitySchema>;\n\n/**\n * Schema for routing decision\n * Note: Uses the existing ClassificationSignal types from routing/types.ts\n */\nexport const RoutingDecisionSchema = z.object({\n projectId: z.string().optional().describe('Matched project ID'),\n destination: z.object({\n path: z.string().describe('File destination path'),\n structure: z.enum(['none', 'year', 'month', 'day']).default('month'),\n }),\n confidence: z.number().min(0).max(1).describe('Confidence in routing'),\n signals: z.array(z.object({\n type: z.enum(['explicit_phrase', 'associated_person', 'associated_company', 'topic', 'context_type']),\n value: z.string(),\n weight: z.number(),\n })).optional(),\n reasoning: z.string().optional().describe('Why this destination was chosen'),\n});\n\nexport type RoutingDecision = z.infer<typeof RoutingDecisionSchema>;\n\n/**\n * Schema for referenced entities\n */\nexport const ReferencedEntitiesSchema = z.object({\n people: z.array(z.string()).describe('IDs of people mentioned'),\n projects: z.array(z.string()).describe('IDs of projects mentioned'),\n terms: z.array(z.string()).describe('IDs of terms mentioned'),\n companies: z.array(z.string()).describe('IDs of companies mentioned'),\n});\n\nexport type ReferencedEntitiesOutput = z.infer<typeof ReferencedEntitiesSchema>;\n\n/**\n * Schema for tool result data\n */\nexport const ToolResultSchema = z.object({\n success: z.boolean(),\n data: z.any().optional(),\n error: z.string().optional(),\n needsUserInput: z.boolean().optional(),\n userPrompt: z.string().optional(),\n});\n\n// ============================================================================\n// TypeScript Interfaces (existing, preserved for compatibility)\n// ============================================================================\n\nexport interface TranscriptionTool {\n name: string;\n description: string;\n \n parameters: Record<string, any>; // JSON Schema\n \n execute: (args: any) => Promise<ToolResult>;\n}\n\nexport interface ToolContext {\n transcriptText: string;\n audioDate: Date;\n sourceFile: string;\n contextInstance: Context.ContextInstance;\n routingInstance: Routing.RoutingInstance;\n interactiveMode: boolean;\n // interactiveInstance?: Interactive.InteractiveInstance; // Interactive moved to protokoll-cli\n resolvedEntities?: Map<string, string>; // Entities resolved during this session\n /** Optional: entity affinity graph predictions for LLM prepositioning */\n weightModelProvider?: WeightModelProvider;\n}\n\nexport interface ToolResult {\n success: boolean;\n \n data?: any;\n error?: string;\n needsUserInput?: boolean;\n userPrompt?: string;\n}\n\nexport interface ReferencedEntity {\n id: string;\n name: string;\n type: 'person' | 'project' | 'term' | 'company';\n}\n\nexport interface TranscriptionState {\n originalText: string;\n correctedText: string;\n unknownEntities: string[];\n resolvedEntities: Map<string, string>; // name mapping (old -> new)\n \n routeDecision?: RoutingDecision;\n confidence: number;\n \n // Track all entities referenced during processing\n referencedEntities: {\n people: Set<string>; // IDs of people mentioned\n projects: Set<string>; // IDs of projects mentioned\n terms: Set<string>; // IDs of terms mentioned\n companies: Set<string>; // IDs of companies mentioned\n };\n}\n\n"],"names":[],"mappings":";;AAmBO,MAAM,qBAAA,GAAwB,EAAE,MAAA,CAAO;AAAA,EAC1C,QAAA,EAAU,CAAA,CAAE,MAAA,EAAO,CAAE,SAAS,+BAA+B,CAAA;AAAA,EAC7D,SAAA,EAAW,CAAA,CAAE,MAAA,EAAO,CAAE,SAAS,yBAAyB,CAAA;AAAA,EACxD,IAAA,EAAM,CAAA,CAAE,IAAA,CAAK,CAAC,QAAA,EAAU,SAAA,EAAW,MAAA,EAAQ,SAAS,CAAC,CAAA,CAAE,QAAA,CAAS,aAAa,CAAA;AAAA,EAC7E,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,CAAS,0BAA0B;AAC5E,CAAC;AAQM,MAAM,qBAAA,GAAwB,EAAE,MAAA,CAAO;AAAA,EAC1C,WAAW,CAAA,CAAE,MAAA,GAAS,QAAA,EAAS,CAAE,SAAS,oBAAoB,CAAA;AAAA,EAC9D,WAAA,EAAa,EAAE,MAAA,CAAO;AAAA,IAClB,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO,CAAE,SAAS,uBAAuB,CAAA;AAAA,IACjD,SAAA,EAAW,CAAA,CAAE,IAAA,CAAK,CAAC,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,KAAK,CAAC,CAAA,CAAE,OAAA,CAAQ,OAAO;AAAA,GACtE,CAAA;AAAA,EACD,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,CAAS,uBAAuB,CAAA;AAAA,EACrE,OAAA,EAAS,CAAA,CAAE,KAAA,CAAM,CAAA,CAAE,MAAA,CAAO;AAAA,IACtB,IAAA,EAAM,EAAE,IAAA,CAAK,CAAC,mBAAmB,mBAAA,EAAqB,oBAAA,EAAsB,OAAA,EAAS,cAAc,CAAC,CAAA;AAAA,IACpG,KAAA,EAAO,EAAE,MAAA,EAAO;AAAA,IAChB,MAAA,EAAQ,EAAE,MAAA;AAAO,GACpB,CAAC,CAAA,CAAE,QAAA,EAAS;AAAA,EACb,WAAW,CAAA,CAAE,MAAA,GAAS,QAAA,EAAS,CAAE,SAAS,iCAAiC;AAC/E,CAAC;AAOM,MAAM,wBAAA,GAA2B,EAAE,MAAA,CAAO;AAAA,EAC7C,MAAA,EAAQ,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,SAAS,yBAAyB,CAAA;AAAA,EAC9D,QAAA,EAAU,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,SAAS,2BAA2B,CAAA;AAAA,EAClE,KAAA,EAAO,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,SAAS,wBAAwB,CAAA;AAAA,EAC5D,SAAA,EAAW,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,SAAS,4BAA4B;AACxE,CAAC;AAOM,MAAM,gBAAA,GAAmB,EAAE,MAAA,CAAO;AAAA,EACrC,OAAA,EAAS,EAAE,OAAA,EAAQ;AAAA,EACnB,IAAA,EAAM,CAAA,CAAE,GAAA,EAAI,CAAE,QAAA,EAAS;AAAA,EACvB,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC3B,cAAA,EAAgB,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAA,EAAS;AAAA,EACrC,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AAC3B,CAAC;;;;"}
1
+ {"version":3,"file":"index28.js","sources":["../src/agentic/types.ts"],"sourcesContent":["/**\n * Agentic Transcription Types\n *\n * Types for tool-based transcription enhancement.\n * Includes Zod schemas for validation and documentation.\n */\n\nimport { z } from 'zod';\nimport * as Context from '@redaksjon/context';\nimport * as Routing from '../routing';\nimport type { WeightModelProvider } from '../weighting/provider';\n\n// ============================================================================\n// Zod Schemas for Structured Outputs\n// ============================================================================\n\n/**\n * Schema for a corrected entity (spelling/name correction)\n */\nexport const CorrectedEntitySchema = z.object({\n original: z.string().describe('Original text from transcript'),\n corrected: z.string().describe('Corrected spelling/name'),\n type: z.enum(['person', 'project', 'term', 'company']).describe('Entity type'),\n confidence: z.number().min(0).max(1).describe('Confidence in correction'),\n});\n\nexport type CorrectedEntity = z.infer<typeof CorrectedEntitySchema>;\n\n/**\n * Schema for routing decision\n * Note: Uses the existing ClassificationSignal types from routing/types.ts\n */\nexport const RoutingDecisionSchema = z.object({\n projectId: z.string().optional().describe('Matched project ID'),\n destination: z.object({\n path: z.string().describe('File destination path'),\n structure: z.enum(['none', 'year', 'month', 'day']).default('month'),\n }),\n confidence: z.number().min(0).max(1).describe('Confidence in routing'),\n signals: z.array(z.object({\n type: z.enum(['explicit_phrase', 'associated_person', 'associated_company', 'topic', 'context_type']),\n value: z.string(),\n weight: z.number(),\n })).optional(),\n reasoning: z.string().optional().describe('Why this destination was chosen'),\n});\n\nexport type RoutingDecision = z.infer<typeof RoutingDecisionSchema>;\n\n/**\n * Schema for referenced entities\n */\nexport const ReferencedEntitiesSchema = z.object({\n people: z.array(z.string()).describe('IDs of people mentioned'),\n projects: z.array(z.string()).describe('IDs of projects mentioned'),\n terms: z.array(z.string()).describe('IDs of terms mentioned'),\n companies: z.array(z.string()).describe('IDs of companies mentioned'),\n});\n\nexport type ReferencedEntitiesOutput = z.infer<typeof ReferencedEntitiesSchema>;\n\n/**\n * Schema for tool result data\n */\nexport const ToolResultSchema = z.object({\n success: z.boolean(),\n data: z.any().optional(),\n error: z.string().optional(),\n needsUserInput: z.boolean().optional(),\n userPrompt: z.string().optional(),\n});\n\n// ============================================================================\n// TypeScript Interfaces (existing, preserved for compatibility)\n// ============================================================================\n\nexport interface TranscriptionTool {\n name: string;\n description: string;\n \n parameters: Record<string, any>; // JSON Schema\n \n execute: (args: any) => Promise<ToolResult>;\n}\n\n/**\n * Record of a single tool call made during enhancement\n */\nexport interface ToolCallLogEntry {\n tool: string;\n input: Record<string, unknown>;\n output: unknown;\n durationMs: number;\n success: boolean;\n timestamp: Date;\n}\n\nexport interface ToolContext {\n transcriptText: string;\n audioDate: Date;\n sourceFile: string;\n contextInstance: Context.ContextInstance;\n routingInstance: Routing.RoutingInstance;\n interactiveMode: boolean;\n // interactiveInstance?: Interactive.InteractiveInstance; // Interactive moved to protokoll-cli\n resolvedEntities?: Map<string, string>; // Entities resolved during this session\n /** Optional: entity affinity graph predictions for LLM prepositioning */\n weightModelProvider?: WeightModelProvider;\n /** Optional: called just before a tool executes — enables incremental status writes */\n onToolCallStart?: (tool: string, input: Record<string, unknown>) => void;\n /** Optional: called after a tool completes — enables incremental log writes */\n onToolCallComplete?: (entry: ToolCallLogEntry) => void;\n /**\n * Entities pre-identified by the simple-replace phase before LLM enhancement.\n * These are seeded into referencedEntities so they show up in the transcript\n * metadata even if the LLM doesn't redundantly look them up again.\n */\n preIdentifiedEntities?: {\n people: Set<string>;\n projects: Set<string>;\n terms: Set<string>;\n companies: Set<string>;\n };\n}\n\nexport interface ToolResult {\n success: boolean;\n \n data?: any;\n error?: string;\n needsUserInput?: boolean;\n userPrompt?: string;\n}\n\nexport interface ReferencedEntity {\n id: string;\n name: string;\n type: 'person' | 'project' | 'term' | 'company';\n}\n\nexport interface TranscriptionState {\n originalText: string;\n correctedText: string;\n unknownEntities: string[];\n resolvedEntities: Map<string, string>; // name mapping (old -> new)\n \n routeDecision?: RoutingDecision;\n confidence: number;\n \n // Track all entities referenced during processing\n referencedEntities: {\n people: Set<string>; // IDs of people mentioned\n projects: Set<string>; // IDs of projects mentioned\n terms: Set<string>; // IDs of terms mentioned\n companies: Set<string>; // IDs of companies mentioned\n };\n}\n\n"],"names":[],"mappings":";;AAmBO,MAAM,qBAAA,GAAwB,EAAE,MAAA,CAAO;AAAA,EAC1C,QAAA,EAAU,CAAA,CAAE,MAAA,EAAO,CAAE,SAAS,+BAA+B,CAAA;AAAA,EAC7D,SAAA,EAAW,CAAA,CAAE,MAAA,EAAO,CAAE,SAAS,yBAAyB,CAAA;AAAA,EACxD,IAAA,EAAM,CAAA,CAAE,IAAA,CAAK,CAAC,QAAA,EAAU,SAAA,EAAW,MAAA,EAAQ,SAAS,CAAC,CAAA,CAAE,QAAA,CAAS,aAAa,CAAA;AAAA,EAC7E,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,CAAS,0BAA0B;AAC5E,CAAC;AAQM,MAAM,qBAAA,GAAwB,EAAE,MAAA,CAAO;AAAA,EAC1C,WAAW,CAAA,CAAE,MAAA,GAAS,QAAA,EAAS,CAAE,SAAS,oBAAoB,CAAA;AAAA,EAC9D,WAAA,EAAa,EAAE,MAAA,CAAO;AAAA,IAClB,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO,CAAE,SAAS,uBAAuB,CAAA;AAAA,IACjD,SAAA,EAAW,CAAA,CAAE,IAAA,CAAK,CAAC,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,KAAK,CAAC,CAAA,CAAE,OAAA,CAAQ,OAAO;AAAA,GACtE,CAAA;AAAA,EACD,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,CAAS,uBAAuB,CAAA;AAAA,EACrE,OAAA,EAAS,CAAA,CAAE,KAAA,CAAM,CAAA,CAAE,MAAA,CAAO;AAAA,IACtB,IAAA,EAAM,EAAE,IAAA,CAAK,CAAC,mBAAmB,mBAAA,EAAqB,oBAAA,EAAsB,OAAA,EAAS,cAAc,CAAC,CAAA;AAAA,IACpG,KAAA,EAAO,EAAE,MAAA,EAAO;AAAA,IAChB,MAAA,EAAQ,EAAE,MAAA;AAAO,GACpB,CAAC,CAAA,CAAE,QAAA,EAAS;AAAA,EACb,WAAW,CAAA,CAAE,MAAA,GAAS,QAAA,EAAS,CAAE,SAAS,iCAAiC;AAC/E,CAAC;AAOM,MAAM,wBAAA,GAA2B,EAAE,MAAA,CAAO;AAAA,EAC7C,MAAA,EAAQ,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,SAAS,yBAAyB,CAAA;AAAA,EAC9D,QAAA,EAAU,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,SAAS,2BAA2B,CAAA;AAAA,EAClE,KAAA,EAAO,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,SAAS,wBAAwB,CAAA;AAAA,EAC5D,SAAA,EAAW,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,SAAS,4BAA4B;AACxE,CAAC;AAOM,MAAM,gBAAA,GAAmB,EAAE,MAAA,CAAO;AAAA,EACrC,OAAA,EAAS,EAAE,OAAA,EAAQ;AAAA,EACnB,IAAA,EAAM,CAAA,CAAE,GAAA,EAAI,CAAE,QAAA,EAAS;AAAA,EACvB,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC3B,cAAA,EAAgB,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAA,EAAS;AAAA,EACrC,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AAC3B,CAAC;;;;"}
package/dist/index33.js CHANGED
@@ -4,10 +4,12 @@ import { create as create$2 } from './index53.js';
4
4
  import { create as create$3 } from './index11.js';
5
5
  import { create as create$4 } from './index3.js';
6
6
  import { create as create$5 } from './index2.js';
7
- import { create as create$7 } from './index5.js';
8
- import { create as create$6 } from './index37.js';
7
+ import { create as create$8 } from './index5.js';
8
+ import { create as create$7 } from './index37.js';
9
+ import { create as create$6 } from './index35.js';
9
10
  import { getLogger } from './index47.js';
10
11
  import { randomUUID } from 'node:crypto';
12
+ import * as path from 'node:path';
11
13
  import { extractTagsFromSignals, createRoutingMetadata } from './index15.js';
12
14
 
13
15
  const create = async (config) => {
@@ -69,7 +71,9 @@ const create = async (config) => {
69
71
  reasoningLevel: config.reasoningLevel
70
72
  });
71
73
  logger.debug("Reasoning system initialized with model: %s, reasoning level: %s", config.model, config.reasoningLevel || "medium");
72
- const complete = config.processedDirectory ? create$6({
74
+ const simpleReplace = create$6({ debug: config.debug }, context);
75
+ logger.debug("Simple-replace phase initialized with context instance");
76
+ const complete = config.processedDirectory ? create$7({
73
77
  processedDirectory: config.processedDirectory,
74
78
  outputStructure: config.outputStructure,
75
79
  dryRun: config.dryRun
@@ -78,7 +82,7 @@ const create = async (config) => {
78
82
  logger.debug("Complete phase initialized with processed directory: %s", config.processedDirectory);
79
83
  }
80
84
  const extractTitleFromPath = (outputPath) => {
81
- const filename = outputPath.split("/").pop()?.replace(".md", "");
85
+ const filename = outputPath.split("/").pop()?.replace(/\.(md|pkl)$/, "");
82
86
  if (!filename) return void 0;
83
87
  const withoutDate = filename.replace(/^\d{2}-\d{4}-/, "");
84
88
  if (!withoutDate) return void 0;
@@ -86,6 +90,10 @@ const create = async (config) => {
86
90
  };
87
91
  const isMeaningfulTitle = (title) => {
88
92
  if (!title) return false;
93
+ const uuidPattern = /^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;
94
+ if (uuidPattern.test(title.trim())) return false;
95
+ const uuidSegmentPattern = /^[0-9a-f]{8}\s[0-9a-f]{4}\s/i;
96
+ if (uuidSegmentPattern.test(title.trim())) return false;
89
97
  const stripped = title.replace(/\s+/g, "");
90
98
  const numberRatio = (stripped.match(/\d/g) || []).length / stripped.length;
91
99
  if (numberRatio > 0.5) return false;
@@ -209,21 +217,66 @@ ${textSample}`
209
217
  model: config.transcriptionModel,
210
218
  duration: whisperDuration
211
219
  });
220
+ log("debug", "Running simple-replace (sounds_like entity matching)...");
221
+ const intermediateDir = path.dirname(paths.intermediate.transcript);
222
+ const simpleReplaceResult = await simpleReplace.replace(
223
+ state.rawTranscript || "",
224
+ {
225
+ project: routeResult.projectId ?? void 0,
226
+ confidence: routeResult.confidence
227
+ },
228
+ intermediateDir,
229
+ input.hash
230
+ );
231
+ if (simpleReplaceResult.replacementsMade) {
232
+ log(
233
+ "info",
234
+ "Simple-replace: %d correction(s) applied (%d Tier-1, %d Tier-2)",
235
+ simpleReplaceResult.stats.totalReplacements,
236
+ simpleReplaceResult.stats.tier1Replacements,
237
+ simpleReplaceResult.stats.tier2Replacements
238
+ );
239
+ await output.writeIntermediate(paths, "session", {
240
+ simpleReplace: {
241
+ replacementsMade: true,
242
+ stats: simpleReplaceResult.stats
243
+ }
244
+ });
245
+ } else {
246
+ log("debug", "Simple-replace: no replacements made");
247
+ }
248
+ input.onSimpleReplaceComplete?.(simpleReplaceResult.stats);
249
+ const preIdentifiedEntities = {
250
+ people: /* @__PURE__ */ new Set(),
251
+ projects: /* @__PURE__ */ new Set(),
252
+ terms: /* @__PURE__ */ new Set(),
253
+ companies: /* @__PURE__ */ new Set()
254
+ };
255
+ for (const mapping of simpleReplaceResult.stats.appliedMappings) {
256
+ if (!mapping.entityId || !mapping.entityType) continue;
257
+ if (mapping.entityType === "person") preIdentifiedEntities.people.add(mapping.entityId);
258
+ else if (mapping.entityType === "project") preIdentifiedEntities.projects.add(mapping.entityId);
259
+ else if (mapping.entityType === "term") preIdentifiedEntities.terms.add(mapping.entityId);
260
+ else if (mapping.entityType === "company") preIdentifiedEntities.companies.add(mapping.entityId);
261
+ }
212
262
  log("info", "Enhancing with %s...", config.model);
213
263
  const agenticStart = Date.now();
214
264
  const toolContext = {
215
- transcriptText: state.rawTranscript || "",
265
+ transcriptText: simpleReplaceResult.text,
216
266
  audioDate: input.creation,
217
267
  sourceFile: input.audioFile,
218
268
  contextInstance: context,
219
269
  routingInstance: routing,
270
+ preIdentifiedEntities,
220
271
  interactiveMode: config.interactive,
221
272
  // Interactive moved to protokoll-cli
222
273
  // interactiveInstance: interactive,
223
- weightModelProvider: config.weightModelProvider
274
+ weightModelProvider: config.weightModelProvider,
275
+ onToolCallStart: input.onToolCallStart,
276
+ onToolCallComplete: input.onToolCallComplete
224
277
  };
225
- const executor = create$7(reasoning, toolContext);
226
- const agenticResult = await executor.process(state.rawTranscript || "");
278
+ const executor = create$8(reasoning, toolContext);
279
+ const agenticResult = await executor.process(simpleReplaceResult.text);
227
280
  state.enhancedText = agenticResult.enhancedText;
228
281
  const toolsUsed = agenticResult.toolsUsed;
229
282
  const agenticDuration = Date.now() - agenticStart;
@@ -286,6 +339,8 @@ ${textSample}`
286
339
  transcribedAt: (/* @__PURE__ */ new Date()).toISOString()
287
340
  });
288
341
  log("debug", "Writing final transcript...");
342
+ let finalTitle;
343
+ let finalEntities;
289
344
  if (state.enhancedText) {
290
345
  const buildEntityReferences = () => {
291
346
  const refs = agenticResult.state.referencedEntities;
@@ -353,7 +408,7 @@ ${textSample}`
353
408
  id: transcriptUuid,
354
409
  title,
355
410
  projectId: routeResult.projectId || void 0,
356
- project: routeResult.projectId || void 0,
411
+ project: routeResult.projectId ? context.getProject(routeResult.projectId)?.name || void 0 : void 0,
357
412
  date: input.creation,
358
413
  routing: createRoutingMetadata(routeResult),
359
414
  tags: extractTagsFromSignals(routeResult.signals),
@@ -361,6 +416,8 @@ ${textSample}`
361
416
  entities: buildEntityReferences()
362
417
  };
363
418
  await output.writeTranscript(paths, state.enhancedText, transcriptMetadata);
419
+ finalTitle = title;
420
+ finalEntities = transcriptMetadata.entities;
364
421
  if (config.onTranscriptEntitiesUpdated && transcriptMetadata.entities) {
365
422
  const allEntityIds = [];
366
423
  if (transcriptMetadata.entities.people) {
@@ -425,8 +482,11 @@ ${textSample}`
425
482
  outputPath: paths.final,
426
483
  enhancedText: state.enhancedText || "",
427
484
  rawTranscript: state.rawTranscript || "",
485
+ title: finalTitle || "",
428
486
  routedProject: routeResult.projectId,
487
+ routedProjectName: routeResult.projectId ? context.getProject(routeResult.projectId)?.name || null : null,
429
488
  routingConfidence: routeResult.confidence,
489
+ entities: finalEntities,
430
490
  processingTime,
431
491
  toolsUsed,
432
492
  correctionsApplied: agenticResult.state.resolvedEntities.size,