@indexnetwork/protocol 4.5.0-rc.322.1 → 4.5.0-rc.324.1
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.
- package/dist/agent/agent.tools.js +1 -1
- package/dist/agent/agent.tools.js.map +1 -1
- package/dist/chat/chat.interrupt.classifier.js +1 -1
- package/dist/chat/chat.interrupt.classifier.js.map +1 -1
- package/dist/chat/chat.suggester.js +3 -3
- package/dist/chat/chat.suggester.js.map +1 -1
- package/dist/chat/chat.title.generator.js +2 -2
- package/dist/chat/chat.title.generator.js.map +1 -1
- package/dist/contact/contact.tools.js +1 -1
- package/dist/contact/contact.tools.js.map +1 -1
- package/dist/enrichment/enrichment.graph.d.ts.map +1 -1
- package/dist/enrichment/enrichment.graph.js +9 -3
- package/dist/enrichment/enrichment.graph.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/integration/integration.tools.js +1 -1
- package/dist/integration/integration.tools.js.map +1 -1
- package/dist/intent/intent.graph.d.ts.map +1 -1
- package/dist/intent/intent.graph.js +12 -10
- package/dist/intent/intent.graph.js.map +1 -1
- package/dist/intent/intent.indexer.d.ts.map +1 -1
- package/dist/intent/intent.indexer.js +4 -3
- package/dist/intent/intent.indexer.js.map +1 -1
- package/dist/intent/intent.inferrer.d.ts.map +1 -1
- package/dist/intent/intent.inferrer.js +2 -1
- package/dist/intent/intent.inferrer.js.map +1 -1
- package/dist/intent/intent.reconciler.d.ts.map +1 -1
- package/dist/intent/intent.reconciler.js +5 -4
- package/dist/intent/intent.reconciler.js.map +1 -1
- package/dist/intent/intent.verifier.d.ts.map +1 -1
- package/dist/intent/intent.verifier.js +4 -3
- package/dist/intent/intent.verifier.js.map +1 -1
- package/dist/maintenance/maintenance.graph.d.ts.map +1 -1
- package/dist/maintenance/maintenance.graph.js +27 -5
- package/dist/maintenance/maintenance.graph.js.map +1 -1
- package/dist/mcp/mcp.server.d.ts.map +1 -1
- package/dist/mcp/mcp.server.js +5 -4
- package/dist/mcp/mcp.server.js.map +1 -1
- package/dist/negotiation/insight.generator.js +2 -2
- package/dist/negotiation/insight.generator.js.map +1 -1
- package/dist/negotiation/negotiation.graph.d.ts.map +1 -1
- package/dist/negotiation/negotiation.graph.js +15 -11
- package/dist/negotiation/negotiation.graph.js.map +1 -1
- package/dist/negotiation/negotiation.tools.js +1 -1
- package/dist/negotiation/negotiation.tools.js.map +1 -1
- package/dist/network/network.recommender.d.ts.map +1 -1
- package/dist/network/network.recommender.js +5 -4
- package/dist/network/network.recommender.js.map +1 -1
- package/dist/network/network.tools.d.ts.map +1 -1
- package/dist/network/network.tools.js +4 -2
- package/dist/network/network.tools.js.map +1 -1
- package/dist/opportunity/feed/feed.graph.d.ts.map +1 -1
- package/dist/opportunity/feed/feed.graph.js +30 -23
- package/dist/opportunity/feed/feed.graph.js.map +1 -1
- package/dist/opportunity/opportunity.discover.d.ts.map +1 -1
- package/dist/opportunity/opportunity.discover.js +6 -4
- package/dist/opportunity/opportunity.discover.js.map +1 -1
- package/dist/opportunity/opportunity.enricher.js +2 -2
- package/dist/opportunity/opportunity.enricher.js.map +1 -1
- package/dist/opportunity/opportunity.evaluator.d.ts.map +1 -1
- package/dist/opportunity/opportunity.evaluator.js +9 -7
- package/dist/opportunity/opportunity.evaluator.js.map +1 -1
- package/dist/opportunity/opportunity.graph.d.ts.map +1 -1
- package/dist/opportunity/opportunity.graph.js +123 -105
- package/dist/opportunity/opportunity.graph.js.map +1 -1
- package/dist/opportunity/opportunity.introducer.js +5 -5
- package/dist/opportunity/opportunity.introducer.js.map +1 -1
- package/dist/opportunity/opportunity.persist.js +2 -2
- package/dist/opportunity/opportunity.persist.js.map +1 -1
- package/dist/opportunity/opportunity.presenter.d.ts.map +1 -1
- package/dist/opportunity/opportunity.presenter.js +4 -2
- package/dist/opportunity/opportunity.presenter.js.map +1 -1
- package/dist/opportunity/opportunity.tools.d.ts.map +1 -1
- package/dist/opportunity/opportunity.tools.js +2 -1
- package/dist/opportunity/opportunity.tools.js.map +1 -1
- package/dist/opportunity/opportunity.utils.d.ts.map +1 -1
- package/dist/opportunity/opportunity.utils.js +29 -5
- package/dist/opportunity/opportunity.utils.js.map +1 -1
- package/dist/premise/premise.analyzer.d.ts.map +1 -1
- package/dist/premise/premise.analyzer.js +3 -2
- package/dist/premise/premise.analyzer.js.map +1 -1
- package/dist/premise/premise.decomposer.d.ts.map +1 -1
- package/dist/premise/premise.decomposer.js +12 -3
- package/dist/premise/premise.decomposer.js.map +1 -1
- package/dist/premise/premise.graph.d.ts.map +1 -1
- package/dist/premise/premise.graph.js +19 -9
- package/dist/premise/premise.graph.js.map +1 -1
- package/dist/premise/premise.indexer.d.ts.map +1 -1
- package/dist/premise/premise.indexer.js +2 -1
- package/dist/premise/premise.indexer.js.map +1 -1
- package/dist/premise/premise.tools.d.ts.map +1 -1
- package/dist/premise/premise.tools.js +8 -4
- package/dist/premise/premise.tools.js.map +1 -1
- package/dist/shared/agent/tool.factory.d.ts.map +1 -1
- package/dist/shared/agent/tool.factory.js +4 -2
- package/dist/shared/agent/tool.factory.js.map +1 -1
- package/dist/shared/agent/tool.registry.d.ts.map +1 -1
- package/dist/shared/agent/tool.registry.js +6 -4
- package/dist/shared/agent/tool.registry.js.map +1 -1
- package/dist/shared/interfaces/database.interface.d.ts +1 -1
- package/dist/shared/interfaces/database.interface.js.map +1 -1
- package/dist/shared/observability/log.d.ts +3 -2
- package/dist/shared/observability/log.d.ts.map +1 -1
- package/dist/shared/observability/log.js +72 -9
- package/dist/shared/observability/log.js.map +1 -1
- package/dist/shared/observability/protocol.logger.d.ts.map +1 -1
- package/dist/shared/observability/protocol.logger.js +5 -3
- package/dist/shared/observability/protocol.logger.js.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent.inferrer.js","sourceRoot":"/","sources":["intent/intent.inferrer.ts"],"names":[],"mappings":"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","sourcesContent":["import { HumanMessage, SystemMessage, BaseMessage } from \"@langchain/core/messages\";\nimport { tool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { createStructuredModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger(\"ExplicitIntentInferrer\");\n\n// ──────────────────────────────────────────────────────────────\n// 0. INFERRER OPTIONS\n// ──────────────────────────────────────────────────────────────\n\n/**\n * Options to control inferrer behavior.\n * Used to implement safety controls for read/write separation.\n */\nexport interface InferrerOptions {\n /**\n * Whether to fallback to profile inference when content is empty.\n * Should be TRUE for create operations without explicit content.\n * Should be FALSE for query operations.\n * Default: true (for backward compatibility).\n */\n allowProfileFallback?: boolean;\n \n /**\n * The operation mode for context.\n * Helps inferrer understand the user's intent.\n */\n operationMode?: 'create' | 'update' | 'delete';\n \n /**\n * Conversation history for anaphoric resolution.\n * Used to resolve references like \"that intent\", \"this goal\", etc.\n * Optional - if not provided, inference uses only current content.\n */\n conversationContext?: BaseMessage[];\n}\n\n// ──────────────────────────────────────────────────────────────\n// 1. SYSTEM PROMPT\n// ──────────────────────────────────────────────────────────────\n\nconst systemPrompt = `\n You are an expert Intent Analyst. Your goal is to infer the user's current intentions based on their profile and new content.\n\n You have access to:\n 1. User Memory Profile (Identity, Narrative, Attributes) - The long-term context.\n 2. New Content - What they just said/did.\n 3. Conversation History (when available) - Recent messages for resolving references.\n 4. Operation Context - What type of operation is being performed.\n\n YOUR TASK:\n Analyze the \"New Content\" in the context of the \"Profile\", \"Conversation History\", and \"Operation Context\".\n Extract a list of **Inferred Intents**.\n\n INTENT TYPES:\n - 'goal': The user wants to start, continue, or achieve something. (e.g., \"I want to learn Rust\", \"Looking for a co-founder\")\n - 'tombstone': The user explicitly states they have COMPLETED, stopped, or abandoned a goal. (e.g., \"I finished the course\", \"I'm done with crypto\", \"Delete my running goal\")\n\n CRITICAL RULES:\n - Only analyze the \"New Content\" section if it exists.\n - If New Content says \"Return empty intents list\", you MUST return an empty intents array.\n - If New Content says \"No content to analyze\", return an empty intents array.\n - Be precise and self-contained in descriptions (e.g., \"Learn Rust programming\" instead of \"Learn it\").\n - Do NOT try to manage existing IDs or check for duplicates.\n - IGNORE purely phatic communication (e.g., \"Hello\", \"Hi\", \"Good morning\") - return empty intents.\n - For CREATE operations: Extract what the user wants to ADD.\n - For UPDATE operations: Extract what the user wants to CHANGE.\n - For queries/questions: You should not see these - return empty intents.\n\n CONTENT GROUNDING (CRITICAL):\n - When New Content is present, EVERY inferred intent MUST be directly related to the New Content.\n - The User Profile is ENRICHMENT CONTEXT ONLY — use it to add specificity or domain detail to content-derived intents.\n - Do NOT generate intents from the profile that are unrelated to the New Content.\n - If the New Content is a short phrase (e.g., \"artist\", \"photographer\"), treat it as the user's stated goal — infer what they want regarding that topic.\n - Example: New Content = \"artist\", Profile = \"Building a decentralized protocol\" → Intent: \"Find or connect with artists\" (NOT \"Secure partnerships for decentralized protocol\")\n - Example: New Content = \"looking for a photographer\", Profile = \"AI startup founder\" → Intent: \"Find a photographer\" (NOT \"Recruit AI engineers\")\n\n CONCEPT EXTRACTION (CRITICAL FOR MATCHING):\n - Intents must be SELF-CONTAINED and understandable to strangers with no prior context.\n - When a document describes a project, the project's NAME is irrelevant - only WHAT IT DOES and WHAT TECH IT USES matters.\n - STRIP OUT completely (do not include in any form):\n * ANY project/company/product names from the source document - these mean nothing to outsiders\n * URLs and links (https://..., http://...) - NEVER include URLs in intent descriptions\n * Phrases that reference URLs: \"More details at\", \"See ... for more\", \"Project details:\", \"mentioned in\", \"from the document\", \"as discussed\"\n * File names (\"Claude.md\", \"README\", \"the PDF\")\n - Describe the WORK and TECHNOLOGIES, never the project name.\n - Examples:\n * Source mentions \"FooBar Project\" using React/Node → Intent: \"Seeking React/Node.js developers for real-time web apps\" (NO \"FooBar\")\n * Source mentions \"Index Network\" with LangGraph → Intent: \"Seeking LangGraph/PostgreSQL developers for AI agent systems\" (NO \"Index Network\")\n * Source mentions \"Acme Corp\" doing ML → Intent: \"Seeking ML engineers for computer vision pipelines\" (NO \"Acme\")\n \n ANAPHORIC RESOLUTION (UPDATE operations):\n - When conversation history is provided, use it to resolve references like \"that intent\", \"this goal\", \"the project\", etc.\n - Look for previously mentioned intents in the conversation history.\n - If the user says \"make that intent X\", find what \"that intent\" refers to in the history and include ALL its details.\n - PRESERVE all existing details from the referenced intent and only MODIFY the specified parts.\n - Example: If history mentions \"text-based RPG game\" and user says \"make that intent have LLM narration\",\n the output should be \"Create a text-based RPG game with LLM-enhanced narration\" (preserving \"text-based\").\n \n WHEN TO FALLBACK TO PROFILE:\n - Only when explicitly instructed: \"(No content provided. Please infer intents from Profile Narrative and Aspirations)\"\n - This should ONLY happen for CREATE operations with no explicit user input\n - Never infer from profile for query operations\n - When content IS present: profile may inform HOW to describe the intent (e.g., adding domain context), but must NOT change WHAT the intent is about\n`;\n\n// ──────────────────────────────────────────────────────────────\n// 2. RESPONSE SCHEMA (Zod)\n// ──────────────────────────────────────────────────────────────\n\nconst InferredIntentSchema = z.object({\n type: z.enum(['goal', 'tombstone']).describe(\"The type of intent inferred\"),\n description: z.string().describe(\"Concise description of the intent\"),\n reasoning: z.string().describe(\"Why this intent was inferred\"),\n confidence: z.enum(['high', 'medium', 'low']).describe(\"Confidence level of the inference\")\n});\n\nconst responseFormat = z.object({\n intents: z.array(InferredIntentSchema).describe(\"List of inferred intents\")\n});\n\n// ──────────────────────────────────────────────────────────────\n// 3. TYPE DEFINITIONS\n// ──────────────────────────────────────────────────────────────\n\ntype ResponseType = z.infer<typeof responseFormat>;\nexport type InferredIntent = z.infer<typeof InferredIntentSchema>;\n\n// ──────────────────────────────────────────────────────────────\n// 4. CLASS DEFINITION\n// ──────────────────────────────────────────────────────────────\n\nexport class ExplicitIntentInferrer {\n private model: ReturnType<typeof createStructuredModel>;\n\n constructor() {\n this.model = createStructuredModel(\"intentInferrer\", responseFormat, {\n name: \"intent_inferrer\"\n });\n }\n\n /**\n * Main entry point. Invokes the agent with input and returns structured output.\n * @param content - The raw string content to analyze.\n * @param profileContext - The formatted profile context string.\n * @param options - Options controlling inference behavior (fallback, operation mode, conversation context).\n */\n @Timed()\n public async invoke(\n content: string | null,\n profileContext: string,\n options: InferrerOptions = {}\n ) {\n const {\n allowProfileFallback = true, // Default TRUE for backward compatibility\n operationMode = 'create',\n conversationContext = undefined\n } = options;\n \n logger.verbose(\"invoke: received input\", {\n contentPreview: content?.substring(0, 50),\n allowProfileFallback,\n operationMode,\n hasConversationContext: !!conversationContext,\n conversationMessageCount: conversationContext?.length || 0,\n });\n\n // CRITICAL: Don't fallback to profile when explicitly disabled\n // This prevents auto-generation of intents from profile during query operations\n if (!content && !allowProfileFallback) {\n logger.verbose(\"invoke: no content and fallback disabled, returning empty\");\n return { intents: [] };\n }\n\n // Build conversation history section for anaphoric resolution\n const formattedHistory = conversationContext && conversationContext.length > 0\n ? this.formatConversationHistory(conversationContext)\n : '';\n \n const conversationSection = formattedHistory\n ? `# Conversation History (for reference resolution)\\n${formattedHistory}\\n`\n : '';\n\n // Build content section based on fallback setting\n const contentSection = content\n ? `## New Content\\n\\n${content}`\n : allowProfileFallback\n ? '(No content provided. Please infer intents from Profile Narrative and Aspirations)'\n : '(No content to analyze. Return empty intents list.)';\n\n const prompt = `\n Context:\n # User Memory Profile\n ${profileContext}\n\n ${conversationSection}${contentSection}\n \n # Operation Context\n This analysis is for a ${operationMode} operation.\n ${operationMode === 'create' ? 'Extract NEW intents the user wants to add.' : ''}\n ${operationMode === 'update' ? 'Extract MODIFICATIONS to existing intents. Use conversation history to resolve references like \"that intent\".' : ''}\n ${operationMode === 'delete' ? 'This should not execute - delete operations skip inference.' : ''}\n `;\n \n logger.debug(\"invoke: prompt details\", {\n hasConversationHistory: !!conversationSection,\n conversationHistoryLength: formattedHistory.length,\n conversationHistoryPreview: formattedHistory.substring(0, 300),\n contentLength: content?.length ?? 0,\n promptLength: prompt.length,\n promptPreview: prompt.substring(0, 500),\n });\n\n const messages = [\n new SystemMessage(systemPrompt),\n new HumanMessage(prompt)\n ];\n\n try {\n const result = await invokeWithAbortSignal(this.model, messages);\n const output = responseFormat.parse(result);\n\n logger.verbose(`invoke: found ${output.intents.length} intents`, {\n operationMode,\n allowedFallback: allowProfileFallback,\n usedFallback: !content && allowProfileFallback,\n });\n return output;\n } catch (error: unknown) {\n const err = error instanceof Error ? error : new Error(String(error));\n logger.error(\"invoke: error during invocation\", {\n message: err.message,\n stack: err.stack,\n });\n return { intents: [] };\n }\n }\n\n /**\n * Formats conversation history for inclusion in the prompt.\n * Converts BaseMessage[] to readable string format.\n */\n private formatConversationHistory(messages: BaseMessage[]): string {\n const formatted = messages.map((msg, index) => {\n const role = msg._getType() === 'human' ? 'User' : 'Assistant';\n const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);\n // Truncate long messages for token efficiency\n const truncated = content.length > 200 ? content.substring(0, 200) + '...' : content;\n return `[${index + 1}] ${role}: ${truncated}`;\n }).join('\\n');\n \n logger.debug(\"formatConversationHistory: full conversation history\", {\n messageCount: messages.length,\n fullHistory: messages\n .map((msg, index) => {\n const role = msg._getType() === \"human\" ? \"User\" : \"Assistant\";\n const content = typeof msg.content === \"string\" ? msg.content : JSON.stringify(msg.content);\n return `[${index + 1}] ${role}: ${content}`;\n })\n .join(\"\\n\"),\n });\n\n return formatted;\n }\n\n /**\n * Factory method to expose the agent as a LangChain tool.\n * Useful for composing agents into larger graphs.\n */\n public static asTool() {\n return tool(\n async (args: { content: string | null; profileContext: string }) => {\n const agent = new ExplicitIntentInferrer();\n return await agent.invoke(args.content, args.profileContext);\n },\n {\n name: 'explicit_intent_inferrer',\n description: 'Extracts explicit intents from user content and profile context.',\n schema: z.object({\n content: z.string().nullable().describe('The new content to analyze'),\n profileContext: z.string().describe('The user profile context')\n })\n }\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"intent.inferrer.js","sourceRoot":"/","sources":["intent/intent.inferrer.ts"],"names":[],"mappings":"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","sourcesContent":["import { HumanMessage, SystemMessage, BaseMessage } from \"@langchain/core/messages\";\nimport { tool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { createStructuredModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger(\"ExplicitIntentInferrer\");\n\n// ──────────────────────────────────────────────────────────────\n// 0. INFERRER OPTIONS\n// ──────────────────────────────────────────────────────────────\n\n/**\n * Options to control inferrer behavior.\n * Used to implement safety controls for read/write separation.\n */\nexport interface InferrerOptions {\n /**\n * Whether to fallback to profile inference when content is empty.\n * Should be TRUE for create operations without explicit content.\n * Should be FALSE for query operations.\n * Default: true (for backward compatibility).\n */\n allowProfileFallback?: boolean;\n \n /**\n * The operation mode for context.\n * Helps inferrer understand the user's intent.\n */\n operationMode?: 'create' | 'update' | 'delete';\n \n /**\n * Conversation history for anaphoric resolution.\n * Used to resolve references like \"that intent\", \"this goal\", etc.\n * Optional - if not provided, inference uses only current content.\n */\n conversationContext?: BaseMessage[];\n}\n\n// ──────────────────────────────────────────────────────────────\n// 1. SYSTEM PROMPT\n// ──────────────────────────────────────────────────────────────\n\nconst systemPrompt = `\n You are an expert Intent Analyst. Your goal is to infer the user's current intentions based on their profile and new content.\n\n You have access to:\n 1. User Memory Profile (Identity, Narrative, Attributes) - The long-term context.\n 2. New Content - What they just said/did.\n 3. Conversation History (when available) - Recent messages for resolving references.\n 4. Operation Context - What type of operation is being performed.\n\n YOUR TASK:\n Analyze the \"New Content\" in the context of the \"Profile\", \"Conversation History\", and \"Operation Context\".\n Extract a list of **Inferred Intents**.\n\n INTENT TYPES:\n - 'goal': The user wants to start, continue, or achieve something. (e.g., \"I want to learn Rust\", \"Looking for a co-founder\")\n - 'tombstone': The user explicitly states they have COMPLETED, stopped, or abandoned a goal. (e.g., \"I finished the course\", \"I'm done with crypto\", \"Delete my running goal\")\n\n CRITICAL RULES:\n - Only analyze the \"New Content\" section if it exists.\n - If New Content says \"Return empty intents list\", you MUST return an empty intents array.\n - If New Content says \"No content to analyze\", return an empty intents array.\n - Be precise and self-contained in descriptions (e.g., \"Learn Rust programming\" instead of \"Learn it\").\n - Do NOT try to manage existing IDs or check for duplicates.\n - IGNORE purely phatic communication (e.g., \"Hello\", \"Hi\", \"Good morning\") - return empty intents.\n - For CREATE operations: Extract what the user wants to ADD.\n - For UPDATE operations: Extract what the user wants to CHANGE.\n - For queries/questions: You should not see these - return empty intents.\n\n CONTENT GROUNDING (CRITICAL):\n - When New Content is present, EVERY inferred intent MUST be directly related to the New Content.\n - The User Profile is ENRICHMENT CONTEXT ONLY — use it to add specificity or domain detail to content-derived intents.\n - Do NOT generate intents from the profile that are unrelated to the New Content.\n - If the New Content is a short phrase (e.g., \"artist\", \"photographer\"), treat it as the user's stated goal — infer what they want regarding that topic.\n - Example: New Content = \"artist\", Profile = \"Building a decentralized protocol\" → Intent: \"Find or connect with artists\" (NOT \"Secure partnerships for decentralized protocol\")\n - Example: New Content = \"looking for a photographer\", Profile = \"AI startup founder\" → Intent: \"Find a photographer\" (NOT \"Recruit AI engineers\")\n\n CONCEPT EXTRACTION (CRITICAL FOR MATCHING):\n - Intents must be SELF-CONTAINED and understandable to strangers with no prior context.\n - When a document describes a project, the project's NAME is irrelevant - only WHAT IT DOES and WHAT TECH IT USES matters.\n - STRIP OUT completely (do not include in any form):\n * ANY project/company/product names from the source document - these mean nothing to outsiders\n * URLs and links (https://..., http://...) - NEVER include URLs in intent descriptions\n * Phrases that reference URLs: \"More details at\", \"See ... for more\", \"Project details:\", \"mentioned in\", \"from the document\", \"as discussed\"\n * File names (\"Claude.md\", \"README\", \"the PDF\")\n - Describe the WORK and TECHNOLOGIES, never the project name.\n - Examples:\n * Source mentions \"FooBar Project\" using React/Node → Intent: \"Seeking React/Node.js developers for real-time web apps\" (NO \"FooBar\")\n * Source mentions \"Index Network\" with LangGraph → Intent: \"Seeking LangGraph/PostgreSQL developers for AI agent systems\" (NO \"Index Network\")\n * Source mentions \"Acme Corp\" doing ML → Intent: \"Seeking ML engineers for computer vision pipelines\" (NO \"Acme\")\n \n ANAPHORIC RESOLUTION (UPDATE operations):\n - When conversation history is provided, use it to resolve references like \"that intent\", \"this goal\", \"the project\", etc.\n - Look for previously mentioned intents in the conversation history.\n - If the user says \"make that intent X\", find what \"that intent\" refers to in the history and include ALL its details.\n - PRESERVE all existing details from the referenced intent and only MODIFY the specified parts.\n - Example: If history mentions \"text-based RPG game\" and user says \"make that intent have LLM narration\",\n the output should be \"Create a text-based RPG game with LLM-enhanced narration\" (preserving \"text-based\").\n \n WHEN TO FALLBACK TO PROFILE:\n - Only when explicitly instructed: \"(No content provided. Please infer intents from Profile Narrative and Aspirations)\"\n - This should ONLY happen for CREATE operations with no explicit user input\n - Never infer from profile for query operations\n - When content IS present: profile may inform HOW to describe the intent (e.g., adding domain context), but must NOT change WHAT the intent is about\n`;\n\n// ──────────────────────────────────────────────────────────────\n// 2. RESPONSE SCHEMA (Zod)\n// ──────────────────────────────────────────────────────────────\n\nconst InferredIntentSchema = z.object({\n type: z.enum(['goal', 'tombstone']).describe(\"The type of intent inferred\"),\n description: z.string().describe(\"Concise description of the intent\"),\n reasoning: z.string().describe(\"Why this intent was inferred\"),\n confidence: z.enum(['high', 'medium', 'low']).describe(\"Confidence level of the inference\")\n});\n\nconst responseFormat = z.object({\n intents: z.array(InferredIntentSchema).describe(\"List of inferred intents\")\n});\n\n// ──────────────────────────────────────────────────────────────\n// 3. TYPE DEFINITIONS\n// ──────────────────────────────────────────────────────────────\n\ntype ResponseType = z.infer<typeof responseFormat>;\nexport type InferredIntent = z.infer<typeof InferredIntentSchema>;\n\n// ──────────────────────────────────────────────────────────────\n// 4. CLASS DEFINITION\n// ──────────────────────────────────────────────────────────────\n\nexport class ExplicitIntentInferrer {\n private model: ReturnType<typeof createStructuredModel>;\n\n constructor() {\n this.model = createStructuredModel(\"intentInferrer\", responseFormat, {\n name: \"intent_inferrer\"\n });\n }\n\n /**\n * Main entry point. Invokes the agent with input and returns structured output.\n * @param content - The raw string content to analyze.\n * @param profileContext - The formatted profile context string.\n * @param options - Options controlling inference behavior (fallback, operation mode, conversation context).\n */\n @Timed()\n public async invoke(\n content: string | null,\n profileContext: string,\n options: InferrerOptions = {}\n ) {\n const {\n allowProfileFallback = true, // Default TRUE for backward compatibility\n operationMode = 'create',\n conversationContext = undefined\n } = options;\n \n logger.verbose(\"invoke: received input\", {\n contentPreview: content?.substring(0, 50),\n allowProfileFallback,\n operationMode,\n hasConversationContext: !!conversationContext,\n conversationMessageCount: conversationContext?.length || 0,\n });\n\n // CRITICAL: Don't fallback to profile when explicitly disabled\n // This prevents auto-generation of intents from profile during query operations\n if (!content && !allowProfileFallback) {\n logger.verbose(\"invoke: no content and fallback disabled, returning empty\");\n return { intents: [] };\n }\n\n // Build conversation history section for anaphoric resolution\n const formattedHistory = conversationContext && conversationContext.length > 0\n ? this.formatConversationHistory(conversationContext)\n : '';\n \n const conversationSection = formattedHistory\n ? `# Conversation History (for reference resolution)\\n${formattedHistory}\\n`\n : '';\n\n // Build content section based on fallback setting\n const contentSection = content\n ? `## New Content\\n\\n${content}`\n : allowProfileFallback\n ? '(No content provided. Please infer intents from Profile Narrative and Aspirations)'\n : '(No content to analyze. Return empty intents list.)';\n\n const prompt = `\n Context:\n # User Memory Profile\n ${profileContext}\n\n ${conversationSection}${contentSection}\n \n # Operation Context\n This analysis is for a ${operationMode} operation.\n ${operationMode === 'create' ? 'Extract NEW intents the user wants to add.' : ''}\n ${operationMode === 'update' ? 'Extract MODIFICATIONS to existing intents. Use conversation history to resolve references like \"that intent\".' : ''}\n ${operationMode === 'delete' ? 'This should not execute - delete operations skip inference.' : ''}\n `;\n \n logger.debug(\"invoke: prompt details\", {\n hasConversationHistory: !!conversationSection,\n conversationHistoryLength: formattedHistory.length,\n conversationHistoryPreview: formattedHistory.substring(0, 300),\n contentLength: content?.length ?? 0,\n promptLength: prompt.length,\n promptPreview: prompt.substring(0, 500),\n });\n\n const messages = [\n new SystemMessage(systemPrompt),\n new HumanMessage(prompt)\n ];\n\n try {\n const result = await invokeWithAbortSignal(this.model, messages);\n const output = responseFormat.parse(result);\n\n logger.verbose('invoke: found intents', {\n count: output.intents.length,\n operationMode,\n allowedFallback: allowProfileFallback,\n usedFallback: !content && allowProfileFallback,\n });\n return output;\n } catch (error: unknown) {\n const err = error instanceof Error ? error : new Error(String(error));\n logger.error(\"invoke: error during invocation\", {\n message: err.message,\n stack: err.stack,\n });\n return { intents: [] };\n }\n }\n\n /**\n * Formats conversation history for inclusion in the prompt.\n * Converts BaseMessage[] to readable string format.\n */\n private formatConversationHistory(messages: BaseMessage[]): string {\n const formatted = messages.map((msg, index) => {\n const role = msg._getType() === 'human' ? 'User' : 'Assistant';\n const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);\n // Truncate long messages for token efficiency\n const truncated = content.length > 200 ? content.substring(0, 200) + '...' : content;\n return `[${index + 1}] ${role}: ${truncated}`;\n }).join('\\n');\n \n logger.debug(\"formatConversationHistory: full conversation history\", {\n messageCount: messages.length,\n fullHistory: messages\n .map((msg, index) => {\n const role = msg._getType() === \"human\" ? \"User\" : \"Assistant\";\n const content = typeof msg.content === \"string\" ? msg.content : JSON.stringify(msg.content);\n return `[${index + 1}] ${role}: ${content}`;\n })\n .join(\"\\n\"),\n });\n\n return formatted;\n }\n\n /**\n * Factory method to expose the agent as a LangChain tool.\n * Useful for composing agents into larger graphs.\n */\n public static asTool() {\n return tool(\n async (args: { content: string | null; profileContext: string }) => {\n const agent = new ExplicitIntentInferrer();\n return await agent.invoke(args.content, args.profileContext);\n },\n {\n name: 'explicit_intent_inferrer',\n description: 'Extracts explicit intents from user content and profile context.',\n schema: z.object({\n content: z.string().nullable().describe('The new content to analyze'),\n profileContext: z.string().describe('The user profile context')\n })\n }\n );\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent.reconciler.d.ts","sourceRoot":"/","sources":["intent/intent.reconciler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"intent.reconciler.d.ts","sourceRoot":"/","sources":["intent/intent.reconciler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA2ExB,QAAA,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;EAS5B,CAAC;AAEH,QAAA,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;EAO5B,CAAC;AAEH,QAAA,MAAM,wBAAwB;;;;;;;;;;;;EAI5B,CAAC;AAcH,MAAM,MAAM,sBAAsB,GAC9B,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,EAAE,MAAM,CAAC,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC3E,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,EAAE,MAAM,CAAC,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC3E,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,EAAE,MAAM,CAAC,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEhF,MAAM,MAAM,sBAAsB,GAAG;IAAE,OAAO,EAAE,sBAAsB,EAAE,CAAA;CAAE,CAAC;AAe3E,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAA2C;;IAQxD;;;;OAIG;IAEU,MAAM,CAAC,wBAAwB,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM;;;IAqClF;;OAEG;WACW,MAAM;;;;;;;;;;;;;;;;;;CAgBrB"}
|
|
@@ -15,6 +15,7 @@ import { Timed } from "../shared/observability/performance.js";
|
|
|
15
15
|
import { createStructuredModel } from "../shared/agent/model.config.js";
|
|
16
16
|
import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
|
|
17
17
|
const logger = protocolLogger("IntentReconciler");
|
|
18
|
+
const invokeLog = protocolLogger("IntentReconciler:invoke");
|
|
18
19
|
const CreateActionTypeSchema = z.union([z.literal("create"), z.literal("CREATE")]);
|
|
19
20
|
const UpdateActionTypeSchema = z.union([z.literal("update"), z.literal("UPDATE")]);
|
|
20
21
|
const ExpireActionTypeSchema = z.union([z.literal("expire"), z.literal("EXPIRE")]);
|
|
@@ -110,7 +111,7 @@ const normalizeActionType = (type) => {
|
|
|
110
111
|
if (normalized === "create" || normalized === "update" || normalized === "expire") {
|
|
111
112
|
return normalized;
|
|
112
113
|
}
|
|
113
|
-
logger.warn(
|
|
114
|
+
logger.warn('normalizeActionType: unexpected action type, defaulting to "create"', { type });
|
|
114
115
|
return "create";
|
|
115
116
|
};
|
|
116
117
|
// ──────────────────────────────────────────────────────────────
|
|
@@ -128,7 +129,7 @@ export class IntentReconciler {
|
|
|
128
129
|
* @param activeIntentsContext - Formatted string of active intents.
|
|
129
130
|
*/
|
|
130
131
|
async invoke(inferredIntentsFormatted, activeIntentsContext) {
|
|
131
|
-
|
|
132
|
+
invokeLog.verbose(`Reconciling intents...`);
|
|
132
133
|
const prompt = `
|
|
133
134
|
# Active Intents
|
|
134
135
|
${activeIntentsContext}
|
|
@@ -152,11 +153,11 @@ export class IntentReconciler {
|
|
|
152
153
|
...action,
|
|
153
154
|
type: normalizeActionType(action.type),
|
|
154
155
|
}));
|
|
155
|
-
|
|
156
|
+
invokeLog.verbose('Decision computed', { actionCount: normalizedActions.length });
|
|
156
157
|
return { actions: normalizedActions };
|
|
157
158
|
}
|
|
158
159
|
catch (error) {
|
|
159
|
-
logger.error("
|
|
160
|
+
logger.error("Error during invocation", { error });
|
|
160
161
|
return { actions: [] };
|
|
161
162
|
}
|
|
162
163
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent.reconciler.js","sourceRoot":"/","sources":["intent/intent.reconciler.ts"],"names":[],"mappings":"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","sourcesContent":["import { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { tool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { createStructuredModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger(\"IntentReconciler\");\n\n\nconst CreateActionTypeSchema = z.union([z.literal(\"create\"), z.literal(\"CREATE\")]);\nconst UpdateActionTypeSchema = z.union([z.literal(\"update\"), z.literal(\"UPDATE\")]);\nconst ExpireActionTypeSchema = z.union([z.literal(\"expire\"), z.literal(\"EXPIRE\")]);\n\n// ──────────────────────────────────────────────────────────────\n// 1. SYSTEM PROMPT\n// ──────────────────────────────────────────────────────────────\n\n\nconst systemPrompt = `\nYou are an expert Intent Manager. Your goal is to reconcile NEWLY INFERRED intents with the user's ACTIVE intents.\n\nYou have access to:\n1. Inferred Intents: Goals or Tombstones extracted from recent user activity.\n2. Active Intents: What the user is currently working on.\n\nYOUR TASK:\nCompare the Inferred Intents against the Active Intents and decide on the necessary ACTIONS (Create, Update, Expire).\n\nMATCHING LOGIC:\n- You must determine if an Inferred Intent refers to the same underlying goal as an Active Intent.\n- You must detect if an Inferred Intent CONTRADICTS an Active Intent (Change of Mind).\n\nSEMANTIC GOVERNANCE RULES (Donnellan's Distinction):\n- **REFERENTIAL Intents** (Anchor != NULL): These point to specific entities (e.g., \"Google\").\n - Matching logic: Match if the Anchor is the SAME.\n - If Anchor is different (e.g. \"Join Google\" vs \"Join Meta\"), they are DIFFERENT intents.\n- **ATTRIBUTIVE Intents** (Anchor == NULL): These describe a class of things.\n - Matching logic: Match if the description is semantically similar content.\n - E.g. \"Join a startup\" and \"Work for a small tech company\" are the SAME.\n\nACTIONS:\n- CREATE: If an Inferred Goal does NOT match any Active Intent, CREATE it.\n- UPDATE: If an Inferred Goal matches an Active Intent but offers a better/different description, UPDATE it. When the match is an exact duplicate (same goal, no change needed), still output an UPDATE action with that Active Intent's id and the same payload—this allows the caller to link the intent to an index (e.g. add it to a community).\n CRITICAL UPDATE MERGE RULES:\n * When UPDATING an intent, you MUST PRESERVE all existing details from the Active Intent.\n * Only MODIFY or ADD the specific aspects mentioned in the Inferred Intent.\n * NEVER remove existing details unless explicitly contradicted.\n * Examples:\n - Active: \"Create a text-based RPG game\"\n - Inferred: \"Create an RPG game with LLM-enhanced narration\"\n - CORRECT UPDATE: \"Create a text-based RPG game with LLM-enhanced narration\" (preserved \"text-based\")\n - WRONG UPDATE: \"Create an RPG game with LLM-enhanced narration\" (lost \"text-based\")\n * Think of updates as REFINEMENTS or ADDITIONS, not REPLACEMENTS.\n * If the Inferred Intent is a complete restatement, it's fine to use it directly.\n * If the Inferred Intent adds/modifies specific aspects, merge it with existing details.\n\n COMPOUND INTENT SANITY CHECK (CRITICAL):\n * NEVER produce a compound intent — an intent with multiple distinct goals joined by \"and\", \"as well as\", commas, or similar.\n * If merging would create a compound (e.g., \"Find an artist for X, and collaborate on Y\"), the intents are NOT the same — CREATE the new one instead.\n * Valid UPDATE: adding detail to ONE goal (e.g., \"Find a developer\" → \"Find a senior React developer in Berlin\")\n * Invalid UPDATE (compound): joining TWO goals (e.g., \"Find an artist\" + \"Collaborate on art direction\" → \"Find an artist and collaborate on art direction\")\n * When in doubt, CREATE a new intent rather than risk a compound.\n- EXPIRE: If an Inferred Tombstone matches an Active Intent (semantically), EXPIRE it.\n- CONFLICT RESOLUTION: If a NEW Goal contradicts an Active Intent, EXPIRE the old and CREATE the new.\n- DEDUPLICATION: Use Donnellan's Distinction above to merge duplicates. For duplicates, output UPDATE (not an empty list) so the intent can be linked to an index.\n\nOutput a list of specific actions to apply.\nIMPORTANT: The type field MUST be exactly one of: \"create\", \"update\", \"expire\" (lowercase).\n`;\n\n// ──────────────────────────────────────────────────────────────\n// 2. RESPONSE SCHEMA (Zod)\n// ──────────────────────────────────────────────────────────────\n\nconst CreateIntentActionSchema = z.object({\n type: CreateActionTypeSchema,\n payload: z.string().describe(\"The new intent description\"),\n score: z.number().nullable().describe(\"The felicity score (0-100)\"),\n reasoning: z.string().nullable().describe(\"Reasoning for the creation (including felicity)\"),\n // Semantic Governance Fields\n intentMode: z.enum(['REFERENTIAL', 'ATTRIBUTIVE']).nullable().describe(\"Donnellan's Distinction\"),\n referentialAnchor: z.string().nullable().describe(\"Entity anchored to\"),\n semanticEntropy: z.number().nullable().describe(\"Constraint Density Score (0-1)\"),\n});\n\nconst UpdateIntentActionSchema = z.object({\n type: UpdateActionTypeSchema,\n id: z.string().describe(\"The ID of the intent to update\"),\n payload: z.string().describe(\"The updated intent description\"),\n score: z.number().nullable().describe(\"The felicity score (0-100)\"),\n reasoning: z.string().nullable().describe(\"Reasoning for the update\"),\n intentMode: z.enum(['REFERENTIAL', 'ATTRIBUTIVE']).nullable(),\n});\n\nconst ExpireIntentActionSchema = z.object({\n type: ExpireActionTypeSchema,\n id: z.string().describe(\"The ID of the intent to expire\"),\n reason: z.string().describe(\"Why it is expired\")\n});\n\nconst responseFormat = z.object({\n actions: z.array(z.union([\n CreateIntentActionSchema,\n UpdateIntentActionSchema,\n ExpireIntentActionSchema\n ])).describe(\"List of actions to apply\")\n});\n\n// ──────────────────────────────────────────────────────────────\n// 3. TYPE DEFINITIONS (match invoke() return shape: normalized lowercase action types)\n// ──────────────────────────────────────────────────────────────\n\nexport type NormalizedIntentAction =\n | Omit<z.infer<typeof CreateIntentActionSchema>, \"type\"> & { type: \"create\" }\n | Omit<z.infer<typeof UpdateIntentActionSchema>, \"type\"> & { type: \"update\" }\n | Omit<z.infer<typeof ExpireIntentActionSchema>, \"type\"> & { type: \"expire\" };\n\nexport type IntentReconcilerOutput = { actions: NormalizedIntentAction[] };\n\nconst normalizeActionType = (type: string): \"create\" | \"update\" | \"expire\" => {\n const normalized = type.toLowerCase();\n if (normalized === \"create\" || normalized === \"update\" || normalized === \"expire\") {\n return normalized;\n }\n logger.warn(`normalizeActionType: unexpected action type \"${type}\", defaulting to \"create\"`);\n return \"create\";\n};\n\n// ──────────────────────────────────────────────────────────────\n// 4. CLASS DEFINITION\n// ──────────────────────────────────────────────────────────────\n\nexport class IntentReconciler {\n private model: ReturnType<typeof createStructuredModel>;\n\n constructor() {\n this.model = createStructuredModel(\"intentReconciler\", responseFormat, {\n name: \"intent_reconciler\"\n });\n }\n\n /**\n * Reconciles inferred intents with active intents.\n * @param inferredIntentsFormatted - Formatted string of inferred intents.\n * @param activeIntentsContext - Formatted string of active intents.\n */\n @Timed()\n public async invoke(inferredIntentsFormatted: string, activeIntentsContext: string) {\n logger.verbose(`[IntentReconciler.invoke] Reconciling intents...`);\n\n const prompt = `\n # Active Intents\n ${activeIntentsContext}\n\n # Inferred Intents (Candidates)\n ${inferredIntentsFormatted}\n\n Based on the Inferred Intents, determine the actions to modify the Active Intents state.\n IMPORTANT:\n - If you CREATE or UPDATE an intent, you MUST popuate the 'score' and 'reasoning' fields.\n - Extract the 'score', 'semanticEntropy', 'referentialAnchor' from the Inferred Intent's data.\n - Include the verification details in the 'reasoning'.\n `;\n\n const messages = [\n new SystemMessage(systemPrompt),\n new HumanMessage(prompt)\n ];\n\n try {\n const output = responseFormat.parse(await invokeWithAbortSignal(this.model, messages));\n const normalizedActions = output.actions.map((action: z.infer<typeof responseFormat>[\"actions\"][number]) => ({ \n ...action,\n type: normalizeActionType(action.type),\n })) as NormalizedIntentAction[];\n\n logger.verbose(`[IntentReconciler.invoke] Decision: ${normalizedActions.length} actions.`);\n return { actions: normalizedActions };\n } catch (error) {\n logger.error(\"[IntentReconciler] Error during invocation\", { error });\n return { actions: [] };\n }\n }\n\n /**\n * Factory method to expose the agent as a LangChain tool.\n */\n public static asTool() {\n return tool(\n async (args: { inferredIntents: string; activeIntents: string }) => {\n const agent = new IntentReconciler();\n return await agent.invoke(args.inferredIntents, args.activeIntents);\n },\n {\n name: 'intent_reconciler',\n description: 'Reconciles inferred intents with active intents to determine state changes.',\n schema: z.object({\n inferredIntents: z.string().describe('Formatted string of inferred intents'),\n activeIntents: z.string().describe('Formatted string of active intents')\n })\n }\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"intent.reconciler.js","sourceRoot":"/","sources":["intent/intent.reconciler.ts"],"names":[],"mappings":"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","sourcesContent":["import { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { tool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { createStructuredModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger(\"IntentReconciler\");\nconst invokeLog = protocolLogger(\"IntentReconciler:invoke\");\n\n\nconst CreateActionTypeSchema = z.union([z.literal(\"create\"), z.literal(\"CREATE\")]);\nconst UpdateActionTypeSchema = z.union([z.literal(\"update\"), z.literal(\"UPDATE\")]);\nconst ExpireActionTypeSchema = z.union([z.literal(\"expire\"), z.literal(\"EXPIRE\")]);\n\n// ──────────────────────────────────────────────────────────────\n// 1. SYSTEM PROMPT\n// ──────────────────────────────────────────────────────────────\n\n\nconst systemPrompt = `\nYou are an expert Intent Manager. Your goal is to reconcile NEWLY INFERRED intents with the user's ACTIVE intents.\n\nYou have access to:\n1. Inferred Intents: Goals or Tombstones extracted from recent user activity.\n2. Active Intents: What the user is currently working on.\n\nYOUR TASK:\nCompare the Inferred Intents against the Active Intents and decide on the necessary ACTIONS (Create, Update, Expire).\n\nMATCHING LOGIC:\n- You must determine if an Inferred Intent refers to the same underlying goal as an Active Intent.\n- You must detect if an Inferred Intent CONTRADICTS an Active Intent (Change of Mind).\n\nSEMANTIC GOVERNANCE RULES (Donnellan's Distinction):\n- **REFERENTIAL Intents** (Anchor != NULL): These point to specific entities (e.g., \"Google\").\n - Matching logic: Match if the Anchor is the SAME.\n - If Anchor is different (e.g. \"Join Google\" vs \"Join Meta\"), they are DIFFERENT intents.\n- **ATTRIBUTIVE Intents** (Anchor == NULL): These describe a class of things.\n - Matching logic: Match if the description is semantically similar content.\n - E.g. \"Join a startup\" and \"Work for a small tech company\" are the SAME.\n\nACTIONS:\n- CREATE: If an Inferred Goal does NOT match any Active Intent, CREATE it.\n- UPDATE: If an Inferred Goal matches an Active Intent but offers a better/different description, UPDATE it. When the match is an exact duplicate (same goal, no change needed), still output an UPDATE action with that Active Intent's id and the same payload—this allows the caller to link the intent to an index (e.g. add it to a community).\n CRITICAL UPDATE MERGE RULES:\n * When UPDATING an intent, you MUST PRESERVE all existing details from the Active Intent.\n * Only MODIFY or ADD the specific aspects mentioned in the Inferred Intent.\n * NEVER remove existing details unless explicitly contradicted.\n * Examples:\n - Active: \"Create a text-based RPG game\"\n - Inferred: \"Create an RPG game with LLM-enhanced narration\"\n - CORRECT UPDATE: \"Create a text-based RPG game with LLM-enhanced narration\" (preserved \"text-based\")\n - WRONG UPDATE: \"Create an RPG game with LLM-enhanced narration\" (lost \"text-based\")\n * Think of updates as REFINEMENTS or ADDITIONS, not REPLACEMENTS.\n * If the Inferred Intent is a complete restatement, it's fine to use it directly.\n * If the Inferred Intent adds/modifies specific aspects, merge it with existing details.\n\n COMPOUND INTENT SANITY CHECK (CRITICAL):\n * NEVER produce a compound intent — an intent with multiple distinct goals joined by \"and\", \"as well as\", commas, or similar.\n * If merging would create a compound (e.g., \"Find an artist for X, and collaborate on Y\"), the intents are NOT the same — CREATE the new one instead.\n * Valid UPDATE: adding detail to ONE goal (e.g., \"Find a developer\" → \"Find a senior React developer in Berlin\")\n * Invalid UPDATE (compound): joining TWO goals (e.g., \"Find an artist\" + \"Collaborate on art direction\" → \"Find an artist and collaborate on art direction\")\n * When in doubt, CREATE a new intent rather than risk a compound.\n- EXPIRE: If an Inferred Tombstone matches an Active Intent (semantically), EXPIRE it.\n- CONFLICT RESOLUTION: If a NEW Goal contradicts an Active Intent, EXPIRE the old and CREATE the new.\n- DEDUPLICATION: Use Donnellan's Distinction above to merge duplicates. For duplicates, output UPDATE (not an empty list) so the intent can be linked to an index.\n\nOutput a list of specific actions to apply.\nIMPORTANT: The type field MUST be exactly one of: \"create\", \"update\", \"expire\" (lowercase).\n`;\n\n// ──────────────────────────────────────────────────────────────\n// 2. RESPONSE SCHEMA (Zod)\n// ──────────────────────────────────────────────────────────────\n\nconst CreateIntentActionSchema = z.object({\n type: CreateActionTypeSchema,\n payload: z.string().describe(\"The new intent description\"),\n score: z.number().nullable().describe(\"The felicity score (0-100)\"),\n reasoning: z.string().nullable().describe(\"Reasoning for the creation (including felicity)\"),\n // Semantic Governance Fields\n intentMode: z.enum(['REFERENTIAL', 'ATTRIBUTIVE']).nullable().describe(\"Donnellan's Distinction\"),\n referentialAnchor: z.string().nullable().describe(\"Entity anchored to\"),\n semanticEntropy: z.number().nullable().describe(\"Constraint Density Score (0-1)\"),\n});\n\nconst UpdateIntentActionSchema = z.object({\n type: UpdateActionTypeSchema,\n id: z.string().describe(\"The ID of the intent to update\"),\n payload: z.string().describe(\"The updated intent description\"),\n score: z.number().nullable().describe(\"The felicity score (0-100)\"),\n reasoning: z.string().nullable().describe(\"Reasoning for the update\"),\n intentMode: z.enum(['REFERENTIAL', 'ATTRIBUTIVE']).nullable(),\n});\n\nconst ExpireIntentActionSchema = z.object({\n type: ExpireActionTypeSchema,\n id: z.string().describe(\"The ID of the intent to expire\"),\n reason: z.string().describe(\"Why it is expired\")\n});\n\nconst responseFormat = z.object({\n actions: z.array(z.union([\n CreateIntentActionSchema,\n UpdateIntentActionSchema,\n ExpireIntentActionSchema\n ])).describe(\"List of actions to apply\")\n});\n\n// ──────────────────────────────────────────────────────────────\n// 3. TYPE DEFINITIONS (match invoke() return shape: normalized lowercase action types)\n// ──────────────────────────────────────────────────────────────\n\nexport type NormalizedIntentAction =\n | Omit<z.infer<typeof CreateIntentActionSchema>, \"type\"> & { type: \"create\" }\n | Omit<z.infer<typeof UpdateIntentActionSchema>, \"type\"> & { type: \"update\" }\n | Omit<z.infer<typeof ExpireIntentActionSchema>, \"type\"> & { type: \"expire\" };\n\nexport type IntentReconcilerOutput = { actions: NormalizedIntentAction[] };\n\nconst normalizeActionType = (type: string): \"create\" | \"update\" | \"expire\" => {\n const normalized = type.toLowerCase();\n if (normalized === \"create\" || normalized === \"update\" || normalized === \"expire\") {\n return normalized;\n }\n logger.warn('normalizeActionType: unexpected action type, defaulting to \"create\"', { type });\n return \"create\";\n};\n\n// ──────────────────────────────────────────────────────────────\n// 4. CLASS DEFINITION\n// ──────────────────────────────────────────────────────────────\n\nexport class IntentReconciler {\n private model: ReturnType<typeof createStructuredModel>;\n\n constructor() {\n this.model = createStructuredModel(\"intentReconciler\", responseFormat, {\n name: \"intent_reconciler\"\n });\n }\n\n /**\n * Reconciles inferred intents with active intents.\n * @param inferredIntentsFormatted - Formatted string of inferred intents.\n * @param activeIntentsContext - Formatted string of active intents.\n */\n @Timed()\n public async invoke(inferredIntentsFormatted: string, activeIntentsContext: string) {\n invokeLog.verbose(`Reconciling intents...`);\n\n const prompt = `\n # Active Intents\n ${activeIntentsContext}\n\n # Inferred Intents (Candidates)\n ${inferredIntentsFormatted}\n\n Based on the Inferred Intents, determine the actions to modify the Active Intents state.\n IMPORTANT:\n - If you CREATE or UPDATE an intent, you MUST popuate the 'score' and 'reasoning' fields.\n - Extract the 'score', 'semanticEntropy', 'referentialAnchor' from the Inferred Intent's data.\n - Include the verification details in the 'reasoning'.\n `;\n\n const messages = [\n new SystemMessage(systemPrompt),\n new HumanMessage(prompt)\n ];\n\n try {\n const output = responseFormat.parse(await invokeWithAbortSignal(this.model, messages));\n const normalizedActions = output.actions.map((action: z.infer<typeof responseFormat>[\"actions\"][number]) => ({ \n ...action,\n type: normalizeActionType(action.type),\n })) as NormalizedIntentAction[];\n\n invokeLog.verbose('Decision computed', { actionCount: normalizedActions.length });\n return { actions: normalizedActions };\n } catch (error) {\n logger.error(\"Error during invocation\", { error });\n return { actions: [] };\n }\n }\n\n /**\n * Factory method to expose the agent as a LangChain tool.\n */\n public static asTool() {\n return tool(\n async (args: { inferredIntents: string; activeIntents: string }) => {\n const agent = new IntentReconciler();\n return await agent.invoke(args.inferredIntents, args.activeIntents);\n },\n {\n name: 'intent_reconciler',\n description: 'Reconciles inferred intents with active intents to determine state changes.',\n schema: z.object({\n inferredIntents: z.string().describe('Formatted string of inferred intents'),\n activeIntents: z.string().describe('Formatted string of active intents')\n })\n }\n );\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent.verifier.d.ts","sourceRoot":"/","sources":["intent/intent.verifier.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"intent.verifier.d.ts","sourceRoot":"/","sources":["intent/intent.verifier.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB,QAAA,MAAM,wBAAwB,4CAA0C,CAAC;AACzE,QAAA,MAAM,kCAAkC,oFAOtC,CAAC;AAuJH,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+ClB,CAAC;AAMH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC1E,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAC9F,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAMpE,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAA2C;;IAQxD;;;;OAIG;IAEU,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;IA8BpD;;OAEG;WACW,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgBrB"}
|
|
@@ -15,6 +15,7 @@ import { Timed } from "../shared/observability/performance.js";
|
|
|
15
15
|
import { createStructuredModel } from "../shared/agent/model.config.js";
|
|
16
16
|
import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
|
|
17
17
|
const logger = protocolLogger("SemanticVerifier");
|
|
18
|
+
const invokeLog = protocolLogger("SemanticVerifier:invoke");
|
|
18
19
|
const referentialBreadthSchema = z.enum(["narrow", "moderate", "broad"]);
|
|
19
20
|
const missingSelectionalConstraintSchema = z.enum([
|
|
20
21
|
"role",
|
|
@@ -211,7 +212,7 @@ export class SemanticVerifier {
|
|
|
211
212
|
* @param context - The User Profile as a JSON string.
|
|
212
213
|
*/
|
|
213
214
|
async invoke(content, context) {
|
|
214
|
-
|
|
215
|
+
invokeLog.verbose('Verifying content', { preview: content.substring(0, 30) });
|
|
215
216
|
const prompt = `
|
|
216
217
|
# User Profile (Context)
|
|
217
218
|
${context}
|
|
@@ -228,11 +229,11 @@ export class SemanticVerifier {
|
|
|
228
229
|
try {
|
|
229
230
|
const result = await invokeWithAbortSignal(this.model, messages);
|
|
230
231
|
const output = responseFormat.parse(result);
|
|
231
|
-
|
|
232
|
+
invokeLog.verbose('Verdict computed', { classification: output.classification, semanticEntropy: output.semantic_entropy });
|
|
232
233
|
return output;
|
|
233
234
|
}
|
|
234
235
|
catch (error) {
|
|
235
|
-
logger.error("
|
|
236
|
+
logger.error("Error during invocation", { error });
|
|
236
237
|
throw error;
|
|
237
238
|
}
|
|
238
239
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent.verifier.js","sourceRoot":"/","sources":["intent/intent.verifier.ts"],"names":[],"mappings":"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","sourcesContent":["import { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { tool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { createStructuredModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger(\"SemanticVerifier\");\n\nconst referentialBreadthSchema = z.enum([\"narrow\", \"moderate\", \"broad\"]);\nconst missingSelectionalConstraintSchema = z.enum([\n \"role\",\n \"outcome\",\n \"location\",\n \"timeframe\",\n \"domain\",\n \"concrete_need\",\n]);\n\n// ──────────────────────────────────────────────────────────────\n// 1. SYSTEM PROMPT\n// ──────────────────────────────────────────────────────────────\n\nconst systemPrompt = `\nYou are the Semantic Verification Engine for the Index Network — an intent-driven discovery protocol.\n\nYour job: classify a user utterance using Searle's Speech Act Theory, then score its felicity conditions.\n\nAlways reason before classifying. Output reasoning first.\n\n═══════════════════════════════════════════════════\nSTEP 1 — CLASSIFY THE ILLOCUTIONARY ACT\n═══════════════════════════════════════════════════\n\nWork through this decision tree in order. Stop at the first matching branch.\n\nIF the utterance cancels, terminates, or declares a state change (e.g., \"I quit\", \"Project cancelled\", \"This position is closed\"):\n → DECLARATION\n\nELSE IF the utterance expresses a search, need, or request for another party — even without an explicit verb or first-person subject:\n → DIRECTIVE\n\n DIRECTIVE trigger patterns (any of these → DIRECTIVE):\n · \"Looking for [X]\" · \"Seeking [X]\"\n · \"In search of [X]\" · \"Need a [X]\"\n · \"Want to find [X]\" · \"Interested in connecting with [X]\"\n · \"Open to [X]\" · \"Hiring [X]\"\n · \"Would love to meet [X]\" · \"Anyone know [X]\"\n\n KEY RULE: A verbless gerundive like \"Looking for artists for collaboration\" IS a DIRECTIVE.\n The missing first-person subject (\"I am\") is routinely elided in natural intent language.\n The illocutionary force is a search directive aimed at the system, not an assertion about reality.\n\n DIRECTIVE positive examples:\n · \"Looking for artists for collaboration\" → DIRECTIVE (elided subject, search intent)\n · \"Seeking a technical co-founder in NYC\" → DIRECTIVE\n · \"Need a PyTorch expert for a 3-month contract\" → DIRECTIVE\n · \"Open to angel investment opportunities\" → DIRECTIVE\n · \"Anyone building in the DeSci space?\" → DIRECTIVE\n\n DIRECTIVE negative examples (do NOT classify these as DIRECTIVE):\n · \"AI is changing the creative industry\" → ASSERTIVE (states a belief, no search)\n · \"I built a collaboration platform\" → ASSERTIVE/COMMISSIVE (past action, no request)\n · \"Collaboration is important\" → ASSERTIVE (general belief)\n\nELSE IF the utterance commits the speaker to a future action:\n → COMMISSIVE\n\n COMMISSIVE positive examples:\n · \"I will deploy the contract by Friday\" → COMMISSIVE\n · \"I'm going to learn Rust this quarter\" → COMMISSIVE\n · \"I commit to mentoring two junior devs\" → COMMISSIVE\n\n COMMISSIVE negative examples (do NOT classify these as COMMISSIVE):\n · \"I could probably look into it\" → too hedged; score sincerity low instead\n · \"We should build something cool\" → vague, no personal commitment\n\nELSE IF the utterance states a fact, belief, or opinion with no implied request or commitment:\n → ASSERTIVE\n\n ASSERTIVE positive examples:\n · \"Rust is faster than C++\" → ASSERTIVE\n · \"I have 10 years of experience in ML\" → ASSERTIVE (profile statement, not a request)\n · \"The crypto market is volatile\" → ASSERTIVE\n\nELSE IF the utterance expresses a psychological state or social ritual:\n → EXPRESSIVE\n\n EXPRESSIVE examples: \"I'm so excited!\", \"Hello everyone\", \"Congrats to the team\"\n\nIf none of the above apply cleanly:\n → UNKNOWN\n\n═══════════════════════════════════════════════════\nSTEP 2 — SCORE THE FELICITY CONDITIONS (0–100)\n═══════════════════════════════════════════════════\n\nScore AFTER classification. Do not let scores influence the category decision.\n\nCLARITY (Essential Condition)\n How unambiguous and actionable is the utterance?\n 100 → \"Deploy the Solidity contract to Mainnet by March 15\"\n 60 → \"Looking for a developer\" (clear direction, vague spec)\n 20 → \"We should do something cool\"\n\nAUTHORITY (Preparatory Condition)\n Does the speaker's profile support this act?\n Compare stated skills/role against the action or search domain.\n 100 → Profile: Senior ML Engineer | Utterance: \"Seeking a research collaborator on transformers\"\n 20 → Profile: Junior Marketer | Utterance: \"I will rewrite the Rust compiler\"\n For DIRECTIVEs: authority = plausibility that this person would make this search.\n\nSINCERITY (Sincerity Condition)\n Does the linguistic form imply genuine commitment or genuine need?\n For COMMISSIVEs: check modality (will > going to > might > could).\n For DIRECTIVEs: check specificity of the search (specific need > vague wish).\n 100 → \"I need a Rails contractor starting next week, $150/hr, remote\"\n 40 → \"I could maybe try to find someone\"\n\nSEMANTIC ENTROPY (Constraint Density) → semantic_entropy field, range 0.0–1.0\n 0.0 = maximally constrained (time, location, tech stack, quantifiers all present)\n 1.0 = no constraints at all\n 0.0 example: \"Meet 50 senior React devs in SF by Friday\"\n 1.0 example: \"Network\"\n\nREFERENTIAL ANCHOR → referential_anchor field\n Does the utterance name a specific unique entity (Donnellan referential use)?\n If YES → output the entity name string.\n If NO (attributive reference to any member of a class) → output null.\n \"I want to join Google\" → \"Google\"\n \"I want to join a startup\" → null\n\nREFERENTIAL BREADTH → referential_breadth field\n Estimate the extension size of the open candidate class: how many people could plausibly satisfy the description.\n This is NOT the same as semantic_entropy. Semantic entropy measures underspecification/constraint density;\n referential breadth measures whether the attributive description narrows the satisfier class enough for discovery.\n\n narrow → a small satisfier class due to concrete role/outcome/location/timeframe/specific need.\n Example: \"Find a robotics founder in Healdsburg this week to test my meditation hardware prototype\"\n moderate → a bounded professional/community class, but some constraints are still missing.\n Example: \"Meet AI safety researchers working on evals in London\"\n broad → a large satisfier class; many people could plausibly match even if topical constraints are present.\n Example: \"Meet creative people, builders, and makers interested in AI, meditation, and tech exploration\"\n\nMISSING SELECTIONAL CONSTRAINTS → missing_selectional_constraints field\n Report the constraints whose absence keeps the candidate class too broad:\n role, outcome, location, timeframe, domain, concrete_need.\n\nSPECIFICITY WARNING → specificity_warning field\n If referential_breadth is broad, provide a concise user-facing warning asking for a more concrete role,\n outcome, location, timeframe, domain, or need. Otherwise output null.\n\n═══════════════════════════════════════════════════\nSTEP 3 — FLAGS\n═══════════════════════════════════════════════════\n\nAdd flags when scores fall below threshold:\n authority < 70 → \"SKILL_MISMATCH\"\n sincerity < 70 → \"WEAK_COMMITMENT\"\n clarity < 50 → \"VAGUE_INTENT\"\n classification is ASSERTIVE or EXPRESSIVE → \"NOISE\"\n referential_breadth is broad → \"BROAD_ATTRIBUTIVE_REFERENCE\"\n`;\n\n// ──────────────────────────────────────────────────────────────\n// 2. RESPONSE SCHEMA (Zod)\n// ──────────────────────────────────────────────────────────────\n\nconst responseFormat = z.object({\n // reasoning comes first so the model commits to its analysis\n // before the classification token is generated (chain-of-thought anchor)\n reasoning: z.string().describe(\n \"Step-by-step analysis: (1) which decision-tree branch fired and why, \" +\n \"(2) key surface features of the utterance (trigger keywords, elided subject, modality), \" +\n \"(3) felicity condition assessment.\"\n ),\n\n classification: z.enum([\n \"COMMISSIVE\",\n \"DIRECTIVE\",\n \"ASSERTIVE\",\n \"EXPRESSIVE\",\n \"DECLARATION\",\n \"UNKNOWN\"\n ]).describe(\"Searle's Speech Act category — determined by the decision tree in STEP 1\"),\n\n felicity_scores: z.object({\n clarity: z.number().min(0).max(100).describe(\"Essential Condition: how unambiguous and actionable is the utterance (0–100)\"),\n authority: z.number().min(0).max(100).describe(\"Preparatory Condition: does the speaker's profile support this act (0–100)\"),\n sincerity: z.number().min(0).max(100).describe(\"Sincerity Condition: does the linguistic form imply genuine commitment or need (0–100)\"),\n }),\n\n semantic_entropy: z.number().min(0).max(1).describe(\n \"Constraint density: 0.0 = maximally specific (time + location + tech + quantifiers), 1.0 = completely unconstrained\"\n ),\n\n referential_anchor: z.string().nullable().describe(\n \"Named specific entity the utterance refers to (Donnellan referential), or null for attributive reference\"\n ),\n\n referential_breadth: referentialBreadthSchema.describe(\n \"Extension size of the open candidate class: narrow, moderate, or broad. Distinct from semantic entropy.\"\n ),\n\n missing_selectional_constraints: z.array(missingSelectionalConstraintSchema).describe(\n \"Selectional constraints whose absence leaves the satisfier class too broad.\"\n ),\n\n specificity_warning: z.string().nullable().describe(\n \"Concise user-facing warning when referential_breadth is broad; otherwise null.\"\n ),\n\n flags: z.array(z.string()).describe(\n \"Semantic violation tags: SKILL_MISMATCH (authority<70), WEAK_COMMITMENT (sincerity<70), VAGUE_INTENT (clarity<50), NOISE (ASSERTIVE or EXPRESSIVE), BROAD_ATTRIBUTIVE_REFERENCE (referential_breadth=broad)\"\n ),\n});\n\n// ──────────────────────────────────────────────────────────────\n// 3. TYPE DEFINITIONS\n// ──────────────────────────────────────────────────────────────\n\nexport type ReferentialBreadth = z.infer<typeof referentialBreadthSchema>;\nexport type MissingSelectionalConstraint = z.infer<typeof missingSelectionalConstraintSchema>;\nexport type SemanticVerifierOutput = z.infer<typeof responseFormat>;\n\n// ──────────────────────────────────────────────────────────────\n// 4. CLASS DEFINITION\n// ──────────────────────────────────────────────────────────────\n\nexport class SemanticVerifier {\n private model: ReturnType<typeof createStructuredModel>;\n\n constructor() {\n this.model = createStructuredModel(\"intentVerifier\", responseFormat, {\n name: \"semantic_verifier\"\n });\n }\n\n /**\n * Verifies the semantic validity of an intent.\n * @param content - The user's raw utterance.\n * @param context - The User Profile as a JSON string.\n */\n @Timed()\n public async invoke(content: string, context: string) {\n logger.verbose(`[SemanticVerifier.invoke] Verifying: \"${content.substring(0, 30)}...\"`);\n\n const prompt = `\n # User Profile (Context)\n ${context}\n\n # User Utterance (Content)\n \"${content}\"\n \n Verify the Felicity Conditions and Semantic Metrics for this utterance.\n `;\n\n const messages = [\n new SystemMessage(systemPrompt),\n new HumanMessage(prompt)\n ];\n\n try {\n const result = await invokeWithAbortSignal(this.model, messages);\n const output = responseFormat.parse(result);\n\n logger.verbose(`[SemanticVerifier.invoke] Verdict: ${output.classification} Entropy: ${output.semantic_entropy}`);\n return output;\n } catch (error) {\n logger.error(\"[SemanticVerifier] Error during invocation\", { error });\n throw error;\n }\n }\n\n /**\n * Factory method to expose the agent as a LangChain tool.\n */\n public static asTool() {\n return tool(\n async (args: { content: string; context: string }) => {\n const agent = new SemanticVerifier();\n return await agent.invoke(args.content, args.context);\n },\n {\n name: 'semantic_verifier',\n description: 'Verifies the semantic validity and felicity conditions of an intent.',\n schema: z.object({\n content: z.string().describe('The intent content to verify'),\n context: z.string().describe('The user profile context')\n })\n }\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"intent.verifier.js","sourceRoot":"/","sources":["intent/intent.verifier.ts"],"names":[],"mappings":"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","sourcesContent":["import { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { tool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { createStructuredModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger(\"SemanticVerifier\");\nconst invokeLog = protocolLogger(\"SemanticVerifier:invoke\");\n\nconst referentialBreadthSchema = z.enum([\"narrow\", \"moderate\", \"broad\"]);\nconst missingSelectionalConstraintSchema = z.enum([\n \"role\",\n \"outcome\",\n \"location\",\n \"timeframe\",\n \"domain\",\n \"concrete_need\",\n]);\n\n// ──────────────────────────────────────────────────────────────\n// 1. SYSTEM PROMPT\n// ──────────────────────────────────────────────────────────────\n\nconst systemPrompt = `\nYou are the Semantic Verification Engine for the Index Network — an intent-driven discovery protocol.\n\nYour job: classify a user utterance using Searle's Speech Act Theory, then score its felicity conditions.\n\nAlways reason before classifying. Output reasoning first.\n\n═══════════════════════════════════════════════════\nSTEP 1 — CLASSIFY THE ILLOCUTIONARY ACT\n═══════════════════════════════════════════════════\n\nWork through this decision tree in order. Stop at the first matching branch.\n\nIF the utterance cancels, terminates, or declares a state change (e.g., \"I quit\", \"Project cancelled\", \"This position is closed\"):\n → DECLARATION\n\nELSE IF the utterance expresses a search, need, or request for another party — even without an explicit verb or first-person subject:\n → DIRECTIVE\n\n DIRECTIVE trigger patterns (any of these → DIRECTIVE):\n · \"Looking for [X]\" · \"Seeking [X]\"\n · \"In search of [X]\" · \"Need a [X]\"\n · \"Want to find [X]\" · \"Interested in connecting with [X]\"\n · \"Open to [X]\" · \"Hiring [X]\"\n · \"Would love to meet [X]\" · \"Anyone know [X]\"\n\n KEY RULE: A verbless gerundive like \"Looking for artists for collaboration\" IS a DIRECTIVE.\n The missing first-person subject (\"I am\") is routinely elided in natural intent language.\n The illocutionary force is a search directive aimed at the system, not an assertion about reality.\n\n DIRECTIVE positive examples:\n · \"Looking for artists for collaboration\" → DIRECTIVE (elided subject, search intent)\n · \"Seeking a technical co-founder in NYC\" → DIRECTIVE\n · \"Need a PyTorch expert for a 3-month contract\" → DIRECTIVE\n · \"Open to angel investment opportunities\" → DIRECTIVE\n · \"Anyone building in the DeSci space?\" → DIRECTIVE\n\n DIRECTIVE negative examples (do NOT classify these as DIRECTIVE):\n · \"AI is changing the creative industry\" → ASSERTIVE (states a belief, no search)\n · \"I built a collaboration platform\" → ASSERTIVE/COMMISSIVE (past action, no request)\n · \"Collaboration is important\" → ASSERTIVE (general belief)\n\nELSE IF the utterance commits the speaker to a future action:\n → COMMISSIVE\n\n COMMISSIVE positive examples:\n · \"I will deploy the contract by Friday\" → COMMISSIVE\n · \"I'm going to learn Rust this quarter\" → COMMISSIVE\n · \"I commit to mentoring two junior devs\" → COMMISSIVE\n\n COMMISSIVE negative examples (do NOT classify these as COMMISSIVE):\n · \"I could probably look into it\" → too hedged; score sincerity low instead\n · \"We should build something cool\" → vague, no personal commitment\n\nELSE IF the utterance states a fact, belief, or opinion with no implied request or commitment:\n → ASSERTIVE\n\n ASSERTIVE positive examples:\n · \"Rust is faster than C++\" → ASSERTIVE\n · \"I have 10 years of experience in ML\" → ASSERTIVE (profile statement, not a request)\n · \"The crypto market is volatile\" → ASSERTIVE\n\nELSE IF the utterance expresses a psychological state or social ritual:\n → EXPRESSIVE\n\n EXPRESSIVE examples: \"I'm so excited!\", \"Hello everyone\", \"Congrats to the team\"\n\nIf none of the above apply cleanly:\n → UNKNOWN\n\n═══════════════════════════════════════════════════\nSTEP 2 — SCORE THE FELICITY CONDITIONS (0–100)\n═══════════════════════════════════════════════════\n\nScore AFTER classification. Do not let scores influence the category decision.\n\nCLARITY (Essential Condition)\n How unambiguous and actionable is the utterance?\n 100 → \"Deploy the Solidity contract to Mainnet by March 15\"\n 60 → \"Looking for a developer\" (clear direction, vague spec)\n 20 → \"We should do something cool\"\n\nAUTHORITY (Preparatory Condition)\n Does the speaker's profile support this act?\n Compare stated skills/role against the action or search domain.\n 100 → Profile: Senior ML Engineer | Utterance: \"Seeking a research collaborator on transformers\"\n 20 → Profile: Junior Marketer | Utterance: \"I will rewrite the Rust compiler\"\n For DIRECTIVEs: authority = plausibility that this person would make this search.\n\nSINCERITY (Sincerity Condition)\n Does the linguistic form imply genuine commitment or genuine need?\n For COMMISSIVEs: check modality (will > going to > might > could).\n For DIRECTIVEs: check specificity of the search (specific need > vague wish).\n 100 → \"I need a Rails contractor starting next week, $150/hr, remote\"\n 40 → \"I could maybe try to find someone\"\n\nSEMANTIC ENTROPY (Constraint Density) → semantic_entropy field, range 0.0–1.0\n 0.0 = maximally constrained (time, location, tech stack, quantifiers all present)\n 1.0 = no constraints at all\n 0.0 example: \"Meet 50 senior React devs in SF by Friday\"\n 1.0 example: \"Network\"\n\nREFERENTIAL ANCHOR → referential_anchor field\n Does the utterance name a specific unique entity (Donnellan referential use)?\n If YES → output the entity name string.\n If NO (attributive reference to any member of a class) → output null.\n \"I want to join Google\" → \"Google\"\n \"I want to join a startup\" → null\n\nREFERENTIAL BREADTH → referential_breadth field\n Estimate the extension size of the open candidate class: how many people could plausibly satisfy the description.\n This is NOT the same as semantic_entropy. Semantic entropy measures underspecification/constraint density;\n referential breadth measures whether the attributive description narrows the satisfier class enough for discovery.\n\n narrow → a small satisfier class due to concrete role/outcome/location/timeframe/specific need.\n Example: \"Find a robotics founder in Healdsburg this week to test my meditation hardware prototype\"\n moderate → a bounded professional/community class, but some constraints are still missing.\n Example: \"Meet AI safety researchers working on evals in London\"\n broad → a large satisfier class; many people could plausibly match even if topical constraints are present.\n Example: \"Meet creative people, builders, and makers interested in AI, meditation, and tech exploration\"\n\nMISSING SELECTIONAL CONSTRAINTS → missing_selectional_constraints field\n Report the constraints whose absence keeps the candidate class too broad:\n role, outcome, location, timeframe, domain, concrete_need.\n\nSPECIFICITY WARNING → specificity_warning field\n If referential_breadth is broad, provide a concise user-facing warning asking for a more concrete role,\n outcome, location, timeframe, domain, or need. Otherwise output null.\n\n═══════════════════════════════════════════════════\nSTEP 3 — FLAGS\n═══════════════════════════════════════════════════\n\nAdd flags when scores fall below threshold:\n authority < 70 → \"SKILL_MISMATCH\"\n sincerity < 70 → \"WEAK_COMMITMENT\"\n clarity < 50 → \"VAGUE_INTENT\"\n classification is ASSERTIVE or EXPRESSIVE → \"NOISE\"\n referential_breadth is broad → \"BROAD_ATTRIBUTIVE_REFERENCE\"\n`;\n\n// ──────────────────────────────────────────────────────────────\n// 2. RESPONSE SCHEMA (Zod)\n// ──────────────────────────────────────────────────────────────\n\nconst responseFormat = z.object({\n // reasoning comes first so the model commits to its analysis\n // before the classification token is generated (chain-of-thought anchor)\n reasoning: z.string().describe(\n \"Step-by-step analysis: (1) which decision-tree branch fired and why, \" +\n \"(2) key surface features of the utterance (trigger keywords, elided subject, modality), \" +\n \"(3) felicity condition assessment.\"\n ),\n\n classification: z.enum([\n \"COMMISSIVE\",\n \"DIRECTIVE\",\n \"ASSERTIVE\",\n \"EXPRESSIVE\",\n \"DECLARATION\",\n \"UNKNOWN\"\n ]).describe(\"Searle's Speech Act category — determined by the decision tree in STEP 1\"),\n\n felicity_scores: z.object({\n clarity: z.number().min(0).max(100).describe(\"Essential Condition: how unambiguous and actionable is the utterance (0–100)\"),\n authority: z.number().min(0).max(100).describe(\"Preparatory Condition: does the speaker's profile support this act (0–100)\"),\n sincerity: z.number().min(0).max(100).describe(\"Sincerity Condition: does the linguistic form imply genuine commitment or need (0–100)\"),\n }),\n\n semantic_entropy: z.number().min(0).max(1).describe(\n \"Constraint density: 0.0 = maximally specific (time + location + tech + quantifiers), 1.0 = completely unconstrained\"\n ),\n\n referential_anchor: z.string().nullable().describe(\n \"Named specific entity the utterance refers to (Donnellan referential), or null for attributive reference\"\n ),\n\n referential_breadth: referentialBreadthSchema.describe(\n \"Extension size of the open candidate class: narrow, moderate, or broad. Distinct from semantic entropy.\"\n ),\n\n missing_selectional_constraints: z.array(missingSelectionalConstraintSchema).describe(\n \"Selectional constraints whose absence leaves the satisfier class too broad.\"\n ),\n\n specificity_warning: z.string().nullable().describe(\n \"Concise user-facing warning when referential_breadth is broad; otherwise null.\"\n ),\n\n flags: z.array(z.string()).describe(\n \"Semantic violation tags: SKILL_MISMATCH (authority<70), WEAK_COMMITMENT (sincerity<70), VAGUE_INTENT (clarity<50), NOISE (ASSERTIVE or EXPRESSIVE), BROAD_ATTRIBUTIVE_REFERENCE (referential_breadth=broad)\"\n ),\n});\n\n// ──────────────────────────────────────────────────────────────\n// 3. TYPE DEFINITIONS\n// ──────────────────────────────────────────────────────────────\n\nexport type ReferentialBreadth = z.infer<typeof referentialBreadthSchema>;\nexport type MissingSelectionalConstraint = z.infer<typeof missingSelectionalConstraintSchema>;\nexport type SemanticVerifierOutput = z.infer<typeof responseFormat>;\n\n// ──────────────────────────────────────────────────────────────\n// 4. CLASS DEFINITION\n// ──────────────────────────────────────────────────────────────\n\nexport class SemanticVerifier {\n private model: ReturnType<typeof createStructuredModel>;\n\n constructor() {\n this.model = createStructuredModel(\"intentVerifier\", responseFormat, {\n name: \"semantic_verifier\"\n });\n }\n\n /**\n * Verifies the semantic validity of an intent.\n * @param content - The user's raw utterance.\n * @param context - The User Profile as a JSON string.\n */\n @Timed()\n public async invoke(content: string, context: string) {\n invokeLog.verbose('Verifying content', { preview: content.substring(0, 30) });\n\n const prompt = `\n # User Profile (Context)\n ${context}\n\n # User Utterance (Content)\n \"${content}\"\n \n Verify the Felicity Conditions and Semantic Metrics for this utterance.\n `;\n\n const messages = [\n new SystemMessage(systemPrompt),\n new HumanMessage(prompt)\n ];\n\n try {\n const result = await invokeWithAbortSignal(this.model, messages);\n const output = responseFormat.parse(result);\n\n invokeLog.verbose('Verdict computed', { classification: output.classification, semanticEntropy: output.semantic_entropy });\n return output;\n } catch (error) {\n logger.error(\"Error during invocation\", { error });\n throw error;\n }\n }\n\n /**\n * Factory method to expose the agent as a LangChain tool.\n */\n public static asTool() {\n return tool(\n async (args: { content: string; context: string }) => {\n const agent = new SemanticVerifier();\n return await agent.invoke(args.content, args.context);\n },\n {\n name: 'semantic_verifier',\n description: 'Verifies the semantic validity and felicity conditions of an intent.',\n schema: z.object({\n content: z.string().describe('The intent content to verify'),\n context: z.string().describe('The user profile context')\n })\n }\n );\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maintenance.graph.d.ts","sourceRoot":"/","sources":["maintenance/maintenance.graph.ts"],"names":[],"mappings":"AAoBA,wFAAwF;AACxF,MAAM,WAAW,wBAAwB;IACvC,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,KAAK,CAAC;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC,CAAC;IAC/L,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAClF,qEAAqE;IACrE,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3D,kGAAkG;IAClG,8BAA8B,CAC5B,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;CAC3F;AAED,qDAAqD;AACrD,MAAM,WAAW,qBAAqB;IACpC,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7E;AAED,qDAAqD;AACrD,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpK;AAED;;;GAGG;AACH,qBAAa,uBAAuB;IAEhC,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,KAAK;gBAFL,QAAQ,EAAE,wBAAwB,EAClC,KAAK,EAAE,qBAAqB,EAC5B,KAAK,EAAE,qBAAqB;IAGtC,gDAAgD;IAChD,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"maintenance.graph.d.ts","sourceRoot":"/","sources":["maintenance/maintenance.graph.ts"],"names":[],"mappings":"AAoBA,wFAAwF;AACxF,MAAM,WAAW,wBAAwB;IACvC,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,KAAK,CAAC;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC,CAAC;IAC/L,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAClF,qEAAqE;IACrE,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3D,kGAAkG;IAClG,8BAA8B,CAC5B,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;CAC3F;AAED,qDAAqD;AACrD,MAAM,WAAW,qBAAqB;IACpC,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7E;AAED,qDAAqD;AACrD,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpK;AAED;;;GAGG;AACH,qBAAa,uBAAuB;IAEhC,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,KAAK;gBAFL,QAAQ,EAAE,wBAAwB,EAClC,KAAK,EAAE,qBAAqB,EAC5B,KAAK,EAAE,qBAAqB;IAGtC,gDAAgD;IAChD,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkMZ"}
|
|
@@ -83,7 +83,12 @@ export class MaintenanceGraphFactory {
|
|
|
83
83
|
lastRediscoveryAt: state.lastRediscoveryAt,
|
|
84
84
|
freshnessWindowMs: FRESHNESS_WINDOW_MS,
|
|
85
85
|
});
|
|
86
|
-
logger.verbose(
|
|
86
|
+
logger.verbose('Feed health scored', {
|
|
87
|
+
userId: state.userId,
|
|
88
|
+
score: healthResult.score,
|
|
89
|
+
shouldMaintain: healthResult.shouldMaintain,
|
|
90
|
+
connectorFlowCount,
|
|
91
|
+
});
|
|
87
92
|
return { healthResult, connectorFlowCount };
|
|
88
93
|
}
|
|
89
94
|
catch (e) {
|
|
@@ -106,7 +111,7 @@ export class MaintenanceGraphFactory {
|
|
|
106
111
|
for (const r of results) {
|
|
107
112
|
if (r.status === 'rejected') {
|
|
108
113
|
const errMsg = r.reason instanceof Error ? r.reason.message : String(r.reason);
|
|
109
|
-
logger.error(
|
|
114
|
+
logger.error('Rediscovery job enqueue failed', { error: errMsg });
|
|
110
115
|
}
|
|
111
116
|
}
|
|
112
117
|
const enqueued = results.filter((r) => r.status === 'fulfilled').length;
|
|
@@ -130,12 +135,21 @@ export class MaintenanceGraphFactory {
|
|
|
130
135
|
try {
|
|
131
136
|
const connectorFlowTarget = FEED_SOFT_TARGETS.connectorFlow;
|
|
132
137
|
if (!shouldRunIntroducerDiscovery(state.connectorFlowCount, connectorFlowTarget)) {
|
|
133
|
-
logger.verbose(
|
|
138
|
+
logger.verbose('Introducer discovery skipped — connector-flow target met', {
|
|
139
|
+
userId: state.userId,
|
|
140
|
+
connectorFlowCount: state.connectorFlowCount,
|
|
141
|
+
connectorFlowTarget,
|
|
142
|
+
});
|
|
134
143
|
return {};
|
|
135
144
|
}
|
|
136
145
|
// Cast database/queue to introducer discovery interfaces (they are compatible)
|
|
137
146
|
const result = await runIntroducerDiscovery(this.database, this.queue, state.userId);
|
|
138
|
-
logger.info(
|
|
147
|
+
logger.info('Introducer discovery complete', {
|
|
148
|
+
userId: state.userId,
|
|
149
|
+
contactsEvaluated: result.contactsEvaluated,
|
|
150
|
+
jobsEnqueued: result.jobsEnqueued,
|
|
151
|
+
skippedReason: result.skippedReason,
|
|
152
|
+
});
|
|
139
153
|
return { introducerDiscoveryJobsEnqueued: result.jobsEnqueued };
|
|
140
154
|
}
|
|
141
155
|
catch (e) {
|
|
@@ -145,7 +159,15 @@ export class MaintenanceGraphFactory {
|
|
|
145
159
|
}
|
|
146
160
|
};
|
|
147
161
|
const logMaintenanceNode = async (state) => {
|
|
148
|
-
logger.info(
|
|
162
|
+
logger.info('Maintenance complete', {
|
|
163
|
+
userId: state.userId,
|
|
164
|
+
score: state.healthResult?.score,
|
|
165
|
+
shouldMaintain: state.healthResult?.shouldMaintain,
|
|
166
|
+
rediscoveryJobs: state.rediscoveryJobsEnqueued,
|
|
167
|
+
introducerDiscoveryJobs: state.introducerDiscoveryJobsEnqueued,
|
|
168
|
+
activeIntents: state.activeIntents.length,
|
|
169
|
+
connectorFlowCount: state.connectorFlowCount,
|
|
170
|
+
});
|
|
149
171
|
return {};
|
|
150
172
|
};
|
|
151
173
|
const graph = new StateGraph(MaintenanceGraphState)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maintenance.graph.js","sourceRoot":"/","sources":["maintenance/maintenance.graph.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Maintenance Graph: evaluate feed health and trigger rediscovery when unhealthy.\n * Also runs introducer discovery when connector-flow slots are underfilled.\n *\n * Write path — separate from the read-only HomeGraph.\n * Flow: loadCurrentFeed → scoreFeedHealth → [shouldRediscover] → rediscover → introducerDiscovery → logMaintenance → END\n * └─ [skip rediscovery] ─────────────→ introducerDiscovery → logMaintenance → END\n */\nimport { StateGraph, START, END } from '@langchain/langgraph';\n\nimport { MaintenanceGraphState } from './maintenance.state.js';\nimport { computeFeedHealth } from '../opportunity/feed/feed.health.js';\nimport { canUserSeeOpportunity, classifyOpportunity, isActionableForViewer, FEED_SOFT_TARGETS } from '../opportunity/opportunity.utils.js';\nimport { shouldRunIntroducerDiscovery, runIntroducerDiscovery, type IntroducerDiscoveryDatabase, type IntroducerDiscoveryQueue } from '../opportunity/opportunity.introducer.js';\nimport { protocolLogger } from '../shared/observability/protocol.logger.js';\n\nconst logger = protocolLogger('MaintenanceGraph');\n\nconst FRESHNESS_WINDOW_MS = 12 * 60 * 60 * 1000; // 12 hours\n\n/** Database methods needed by the maintenance graph (includes introducer discovery). */\nexport interface MaintenanceGraphDatabase {\n getOpportunitiesForUser(userId: string, options?: { limit?: number }): Promise<Array<{ id: string; actors: Array<{ userId: string; role: string }>; status: string; [key: string]: unknown }>>;\n getActiveIntents(userId: string): Promise<Array<{ id: string; payload: string }>>;\n /** Get the user's personal network ID (for introducer discovery). */\n getPersonalIndexId(userId: string): Promise<string | null>;\n /** Get contacts with intent freshness data from a personal network (for introducer discovery). */\n getContactsWithIntentFreshness(\n personalIndexId: string,\n ownerId: string,\n limit: number,\n ): Promise<Array<{ userId: string; latestIntentAt: string | null; intentCount: number }>>;\n}\n\n/** Cache methods needed by the maintenance graph. */\nexport interface MaintenanceGraphCache {\n get<T = unknown>(key: string): Promise<T | null>;\n set(key: string, value: unknown, options?: { ttl?: number }): Promise<void>;\n}\n\n/** Queue methods needed by the maintenance graph. */\nexport interface MaintenanceGraphQueue {\n addJob(data: { intentId: string; userId: string; indexIds?: string[]; contactUserId?: string }, options?: { priority?: number; jobId?: string }): Promise<unknown>;\n}\n\n/**\n * Factory for the Maintenance Graph.\n * Accepts database, cache, and queue dependencies via constructor injection.\n */\nexport class MaintenanceGraphFactory {\n constructor(\n private database: MaintenanceGraphDatabase,\n private cache: MaintenanceGraphCache,\n private queue: MaintenanceGraphQueue,\n ) {}\n\n /** Compile and return the maintenance graph. */\n createGraph() {\n const loadCurrentFeedNode = async (state: typeof MaintenanceGraphState.State) => {\n if (!state.userId) {\n return { error: 'userId is required' };\n }\n try {\n const raw = await this.database.getOpportunitiesForUser(state.userId, { limit: 150 });\n const actionable = raw.filter((opp) =>\n isActionableForViewer(opp.actors, opp.status, state.userId)\n );\n const expired = raw.filter((opp) =>\n opp.status === 'expired' && canUserSeeOpportunity(opp.actors, opp.status, state.userId)\n );\n const activeIntents = await this.database.getActiveIntents(state.userId);\n\n // Read last rediscovery timestamp from cache\n let lastRediscoveryAt: number | null = null;\n try {\n const cached = await this.cache.get<{ triggeredAt: string }>(`rediscovery:lastRun:${state.userId}`);\n if (typeof cached?.triggeredAt === 'string') {\n const parsed = Date.parse(cached.triggeredAt);\n if (Number.isFinite(parsed)) {\n lastRediscoveryAt = parsed;\n }\n }\n } catch {\n // Cache unavailable — treat as no data\n }\n\n return {\n currentOpportunities: actionable,\n expiredCount: expired.length,\n activeIntents: activeIntents ?? [],\n lastRediscoveryAt,\n };\n } catch (e) {\n logger.error('MaintenanceGraph loadCurrentFeed failed', { error: e });\n return { error: 'Failed to load current feed' };\n }\n };\n\n const scoreFeedHealthNode = async (state: typeof MaintenanceGraphState.State) => {\n if (state.error) return {};\n try {\n const opps = state.currentOpportunities ?? [];\n let connectionCount = 0;\n let connectorFlowCount = 0;\n\n for (const opp of opps) {\n const category = classifyOpportunity(opp, state.userId);\n if (category === 'connection') connectionCount++;\n else if (category === 'connector-flow') connectorFlowCount++;\n }\n\n const healthResult = computeFeedHealth({\n connectionCount,\n connectorFlowCount,\n expiredCount: state.expiredCount,\n totalActionable: opps.length,\n lastRediscoveryAt: state.lastRediscoveryAt,\n freshnessWindowMs: FRESHNESS_WINDOW_MS,\n });\n\n logger.verbose(`[MaintenanceGraph] Feed health scored — userId=${state.userId} score=${healthResult.score} shouldMaintain=${healthResult.shouldMaintain} connectorFlowCount=${connectorFlowCount}`);\n\n return { healthResult, connectorFlowCount };\n } catch (e) {\n logger.error('MaintenanceGraph scoreFeedHealth failed', { error: e });\n return { error: 'Failed to score feed health' };\n }\n };\n\n const shouldRediscover = (state: typeof MaintenanceGraphState.State): string => {\n if (state.error) return 'introducerDiscovery';\n if (state.healthResult?.shouldMaintain && state.activeIntents.length > 0) {\n return 'rediscover';\n }\n return 'introducerDiscovery';\n };\n\n const rediscoverNode = async (state: typeof MaintenanceGraphState.State) => {\n try {\n const bucket = Math.floor(Date.now() / (6 * 60 * 60 * 1000));\n\n const results = await Promise.allSettled(\n state.activeIntents.map((intent) =>\n this.queue.addJob(\n { intentId: intent.id, userId: state.userId },\n { priority: 10, jobId: `rediscovery-${state.userId}-${intent.id}-${bucket}` },\n )\n )\n );\n\n for (const r of results) {\n if (r.status === 'rejected') {\n const errMsg = r.reason instanceof Error ? r.reason.message : String(r.reason);\n logger.error(`[MaintenanceGraph] Rediscovery job enqueue failed: ${errMsg}`);\n }\n }\n const enqueued = results.filter((r) => r.status === 'fulfilled').length;\n\n // Record last run timestamp\n if (enqueued > 0) {\n try {\n await this.cache.set(\n `rediscovery:lastRun:${state.userId}`,\n { triggeredAt: new Date().toISOString() },\n { ttl: 24 * 60 * 60 },\n );\n } catch {\n // Cache write failure is non-fatal\n }\n }\n\n return { rediscoveryJobsEnqueued: enqueued };\n } catch (e) {\n logger.error('MaintenanceGraph rediscover failed', { error: e });\n return { error: 'Failed to enqueue rediscovery jobs' };\n }\n };\n\n const introducerDiscoveryNode = async (state: typeof MaintenanceGraphState.State) => {\n try {\n const connectorFlowTarget = FEED_SOFT_TARGETS.connectorFlow;\n if (!shouldRunIntroducerDiscovery(state.connectorFlowCount, connectorFlowTarget)) {\n logger.verbose(`[MaintenanceGraph] Introducer discovery skipped — connector-flow target met — userId=${state.userId} connectorFlowCount=${state.connectorFlowCount} connectorFlowTarget=${connectorFlowTarget}`);\n return {};\n }\n\n // Cast database/queue to introducer discovery interfaces (they are compatible)\n const result = await runIntroducerDiscovery(\n this.database as IntroducerDiscoveryDatabase,\n this.queue as IntroducerDiscoveryQueue,\n state.userId,\n );\n\n logger.info(`[MaintenanceGraph] Introducer discovery complete — userId=${state.userId} contactsEvaluated=${result.contactsEvaluated} jobsEnqueued=${result.jobsEnqueued}${result.skippedReason ? ` skippedReason=${result.skippedReason}` : ''}`);\n\n return { introducerDiscoveryJobsEnqueued: result.jobsEnqueued };\n } catch (e) {\n logger.error('MaintenanceGraph introducerDiscovery failed', { error: e });\n // Non-fatal: do not set error, just log and continue\n return {};\n }\n };\n\n const logMaintenanceNode = async (state: typeof MaintenanceGraphState.State) => {\n logger.info(`[MaintenanceGraph] Maintenance complete — userId=${state.userId} score=${state.healthResult?.score} shouldMaintain=${state.healthResult?.shouldMaintain} rediscoveryJobs=${state.rediscoveryJobsEnqueued} introducerDiscoveryJobs=${state.introducerDiscoveryJobsEnqueued} activeIntents=${state.activeIntents.length} connectorFlowCount=${state.connectorFlowCount}`);\n return {};\n };\n\n const graph = new StateGraph(MaintenanceGraphState)\n .addNode('loadCurrentFeed', loadCurrentFeedNode)\n .addNode('scoreFeedHealth', scoreFeedHealthNode)\n .addNode('rediscover', rediscoverNode)\n .addNode('introducerDiscovery', introducerDiscoveryNode)\n .addNode('logMaintenance', logMaintenanceNode)\n .addEdge(START, 'loadCurrentFeed')\n .addConditionalEdges('loadCurrentFeed', (state) => (state.error ? 'end' : 'scoreFeedHealth'), {\n scoreFeedHealth: 'scoreFeedHealth',\n end: END,\n })\n .addConditionalEdges('scoreFeedHealth', shouldRediscover, {\n rediscover: 'rediscover',\n introducerDiscovery: 'introducerDiscovery',\n })\n .addEdge('rediscover', 'introducerDiscovery')\n .addEdge('introducerDiscovery', 'logMaintenance')\n .addEdge('logMaintenance', END);\n\n return graph.compile();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"maintenance.graph.js","sourceRoot":"/","sources":["maintenance/maintenance.graph.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Maintenance Graph: evaluate feed health and trigger rediscovery when unhealthy.\n * Also runs introducer discovery when connector-flow slots are underfilled.\n *\n * Write path — separate from the read-only HomeGraph.\n * Flow: loadCurrentFeed → scoreFeedHealth → [shouldRediscover] → rediscover → introducerDiscovery → logMaintenance → END\n * └─ [skip rediscovery] ─────────────→ introducerDiscovery → logMaintenance → END\n */\nimport { StateGraph, START, END } from '@langchain/langgraph';\n\nimport { MaintenanceGraphState } from './maintenance.state.js';\nimport { computeFeedHealth } from '../opportunity/feed/feed.health.js';\nimport { canUserSeeOpportunity, classifyOpportunity, isActionableForViewer, FEED_SOFT_TARGETS } from '../opportunity/opportunity.utils.js';\nimport { shouldRunIntroducerDiscovery, runIntroducerDiscovery, type IntroducerDiscoveryDatabase, type IntroducerDiscoveryQueue } from '../opportunity/opportunity.introducer.js';\nimport { protocolLogger } from '../shared/observability/protocol.logger.js';\n\nconst logger = protocolLogger('MaintenanceGraph');\n\nconst FRESHNESS_WINDOW_MS = 12 * 60 * 60 * 1000; // 12 hours\n\n/** Database methods needed by the maintenance graph (includes introducer discovery). */\nexport interface MaintenanceGraphDatabase {\n getOpportunitiesForUser(userId: string, options?: { limit?: number }): Promise<Array<{ id: string; actors: Array<{ userId: string; role: string }>; status: string; [key: string]: unknown }>>;\n getActiveIntents(userId: string): Promise<Array<{ id: string; payload: string }>>;\n /** Get the user's personal network ID (for introducer discovery). */\n getPersonalIndexId(userId: string): Promise<string | null>;\n /** Get contacts with intent freshness data from a personal network (for introducer discovery). */\n getContactsWithIntentFreshness(\n personalIndexId: string,\n ownerId: string,\n limit: number,\n ): Promise<Array<{ userId: string; latestIntentAt: string | null; intentCount: number }>>;\n}\n\n/** Cache methods needed by the maintenance graph. */\nexport interface MaintenanceGraphCache {\n get<T = unknown>(key: string): Promise<T | null>;\n set(key: string, value: unknown, options?: { ttl?: number }): Promise<void>;\n}\n\n/** Queue methods needed by the maintenance graph. */\nexport interface MaintenanceGraphQueue {\n addJob(data: { intentId: string; userId: string; indexIds?: string[]; contactUserId?: string }, options?: { priority?: number; jobId?: string }): Promise<unknown>;\n}\n\n/**\n * Factory for the Maintenance Graph.\n * Accepts database, cache, and queue dependencies via constructor injection.\n */\nexport class MaintenanceGraphFactory {\n constructor(\n private database: MaintenanceGraphDatabase,\n private cache: MaintenanceGraphCache,\n private queue: MaintenanceGraphQueue,\n ) {}\n\n /** Compile and return the maintenance graph. */\n createGraph() {\n const loadCurrentFeedNode = async (state: typeof MaintenanceGraphState.State) => {\n if (!state.userId) {\n return { error: 'userId is required' };\n }\n try {\n const raw = await this.database.getOpportunitiesForUser(state.userId, { limit: 150 });\n const actionable = raw.filter((opp) =>\n isActionableForViewer(opp.actors, opp.status, state.userId)\n );\n const expired = raw.filter((opp) =>\n opp.status === 'expired' && canUserSeeOpportunity(opp.actors, opp.status, state.userId)\n );\n const activeIntents = await this.database.getActiveIntents(state.userId);\n\n // Read last rediscovery timestamp from cache\n let lastRediscoveryAt: number | null = null;\n try {\n const cached = await this.cache.get<{ triggeredAt: string }>(`rediscovery:lastRun:${state.userId}`);\n if (typeof cached?.triggeredAt === 'string') {\n const parsed = Date.parse(cached.triggeredAt);\n if (Number.isFinite(parsed)) {\n lastRediscoveryAt = parsed;\n }\n }\n } catch {\n // Cache unavailable — treat as no data\n }\n\n return {\n currentOpportunities: actionable,\n expiredCount: expired.length,\n activeIntents: activeIntents ?? [],\n lastRediscoveryAt,\n };\n } catch (e) {\n logger.error('MaintenanceGraph loadCurrentFeed failed', { error: e });\n return { error: 'Failed to load current feed' };\n }\n };\n\n const scoreFeedHealthNode = async (state: typeof MaintenanceGraphState.State) => {\n if (state.error) return {};\n try {\n const opps = state.currentOpportunities ?? [];\n let connectionCount = 0;\n let connectorFlowCount = 0;\n\n for (const opp of opps) {\n const category = classifyOpportunity(opp, state.userId);\n if (category === 'connection') connectionCount++;\n else if (category === 'connector-flow') connectorFlowCount++;\n }\n\n const healthResult = computeFeedHealth({\n connectionCount,\n connectorFlowCount,\n expiredCount: state.expiredCount,\n totalActionable: opps.length,\n lastRediscoveryAt: state.lastRediscoveryAt,\n freshnessWindowMs: FRESHNESS_WINDOW_MS,\n });\n\n logger.verbose('Feed health scored', {\n userId: state.userId,\n score: healthResult.score,\n shouldMaintain: healthResult.shouldMaintain,\n connectorFlowCount,\n });\n\n return { healthResult, connectorFlowCount };\n } catch (e) {\n logger.error('MaintenanceGraph scoreFeedHealth failed', { error: e });\n return { error: 'Failed to score feed health' };\n }\n };\n\n const shouldRediscover = (state: typeof MaintenanceGraphState.State): string => {\n if (state.error) return 'introducerDiscovery';\n if (state.healthResult?.shouldMaintain && state.activeIntents.length > 0) {\n return 'rediscover';\n }\n return 'introducerDiscovery';\n };\n\n const rediscoverNode = async (state: typeof MaintenanceGraphState.State) => {\n try {\n const bucket = Math.floor(Date.now() / (6 * 60 * 60 * 1000));\n\n const results = await Promise.allSettled(\n state.activeIntents.map((intent) =>\n this.queue.addJob(\n { intentId: intent.id, userId: state.userId },\n { priority: 10, jobId: `rediscovery-${state.userId}-${intent.id}-${bucket}` },\n )\n )\n );\n\n for (const r of results) {\n if (r.status === 'rejected') {\n const errMsg = r.reason instanceof Error ? r.reason.message : String(r.reason);\n logger.error('Rediscovery job enqueue failed', { error: errMsg });\n }\n }\n const enqueued = results.filter((r) => r.status === 'fulfilled').length;\n\n // Record last run timestamp\n if (enqueued > 0) {\n try {\n await this.cache.set(\n `rediscovery:lastRun:${state.userId}`,\n { triggeredAt: new Date().toISOString() },\n { ttl: 24 * 60 * 60 },\n );\n } catch {\n // Cache write failure is non-fatal\n }\n }\n\n return { rediscoveryJobsEnqueued: enqueued };\n } catch (e) {\n logger.error('MaintenanceGraph rediscover failed', { error: e });\n return { error: 'Failed to enqueue rediscovery jobs' };\n }\n };\n\n const introducerDiscoveryNode = async (state: typeof MaintenanceGraphState.State) => {\n try {\n const connectorFlowTarget = FEED_SOFT_TARGETS.connectorFlow;\n if (!shouldRunIntroducerDiscovery(state.connectorFlowCount, connectorFlowTarget)) {\n logger.verbose('Introducer discovery skipped — connector-flow target met', {\n userId: state.userId,\n connectorFlowCount: state.connectorFlowCount,\n connectorFlowTarget,\n });\n return {};\n }\n\n // Cast database/queue to introducer discovery interfaces (they are compatible)\n const result = await runIntroducerDiscovery(\n this.database as IntroducerDiscoveryDatabase,\n this.queue as IntroducerDiscoveryQueue,\n state.userId,\n );\n\n logger.info('Introducer discovery complete', {\n userId: state.userId,\n contactsEvaluated: result.contactsEvaluated,\n jobsEnqueued: result.jobsEnqueued,\n skippedReason: result.skippedReason,\n });\n\n return { introducerDiscoveryJobsEnqueued: result.jobsEnqueued };\n } catch (e) {\n logger.error('MaintenanceGraph introducerDiscovery failed', { error: e });\n // Non-fatal: do not set error, just log and continue\n return {};\n }\n };\n\n const logMaintenanceNode = async (state: typeof MaintenanceGraphState.State) => {\n logger.info('Maintenance complete', {\n userId: state.userId,\n score: state.healthResult?.score,\n shouldMaintain: state.healthResult?.shouldMaintain,\n rediscoveryJobs: state.rediscoveryJobsEnqueued,\n introducerDiscoveryJobs: state.introducerDiscoveryJobsEnqueued,\n activeIntents: state.activeIntents.length,\n connectorFlowCount: state.connectorFlowCount,\n });\n return {};\n };\n\n const graph = new StateGraph(MaintenanceGraphState)\n .addNode('loadCurrentFeed', loadCurrentFeedNode)\n .addNode('scoreFeedHealth', scoreFeedHealthNode)\n .addNode('rediscover', rediscoverNode)\n .addNode('introducerDiscovery', introducerDiscoveryNode)\n .addNode('logMaintenance', logMaintenanceNode)\n .addEdge(START, 'loadCurrentFeed')\n .addConditionalEdges('loadCurrentFeed', (state) => (state.error ? 'end' : 'scoreFeedHealth'), {\n scoreFeedHealth: 'scoreFeedHealth',\n end: END,\n })\n .addConditionalEdges('scoreFeedHealth', shouldRediscover, {\n rediscover: 'rediscover',\n introducerDiscovery: 'introducerDiscovery',\n })\n .addEdge('rediscover', 'introducerDiscovery')\n .addEdge('introducerDiscovery', 'logMaintenance')\n .addEdge('logMaintenance', END);\n\n return graph.compile();\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.server.d.ts","sourceRoot":"/","sources":["mcp/mcp.server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,KAAK,EAAiB,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAElF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAE9E,OAAO,KAAK,EAAE,QAAQ,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAGxG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAcrE;;;;GAIG;AACH,KAAK,2BAA2B,GAAG,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,aAAa,GAAG,QAAQ,CAAC,GAAG;IAC9F,UAAU,EAAE,cAAc,CAAC;IAC3B,WAAW,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;CAChD,CAAC;AAIF;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAC5D,iBAAiB,GAAG,aAAa,GAAG,eAAe,GAAG,iBAAiB,GAAG,mBAAmB,CAC9F,GAAG,MAAM,CAQT;AAED;;GAEG;AACH,wBAAgB,iCAAiC,IAAI,IAAI,CAExD;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,2BAA2B,EAAE,CAoB/F;AASD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAI9D;AA8ED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAqBlF;AAKD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI,CAkBxE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,CAErE;AAMD;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,qFAAqF;IACrF,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC;CAC5F;AAED;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,GACxC,cAAc;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,EAAE,EAClE,WAAW,SAAS,GAAG,SAAS,EAChC,SAAS,MAAM,GAAG,IAAI,GAAG,SAAS,KACjC,MAAM,EAGP,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,0BAA0B,GACrC,SAAS,mBAAmB,EAC5B,gBAAgB,MAAM,GAAG,IAAI,GAAG,SAAS,KACxC,IAoBF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAwBjD,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CAyB1E;AAwCD,eAAO,MAAM,gBAAgB,QAsDrB,CAAC;AAET;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAMnE;AAOD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,UAAU,GAAG,KAAK,CAWzE;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,QAAQ,EACd,YAAY,EAAE,eAAe,EAC7B,iBAAiB,EAAE,iBAAiB,GACnC,SAAS,
|
|
1
|
+
{"version":3,"file":"mcp.server.d.ts","sourceRoot":"/","sources":["mcp/mcp.server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,KAAK,EAAiB,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAElF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAE9E,OAAO,KAAK,EAAE,QAAQ,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAGxG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAcrE;;;;GAIG;AACH,KAAK,2BAA2B,GAAG,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,aAAa,GAAG,QAAQ,CAAC,GAAG;IAC9F,UAAU,EAAE,cAAc,CAAC;IAC3B,WAAW,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;CAChD,CAAC;AAIF;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAC5D,iBAAiB,GAAG,aAAa,GAAG,eAAe,GAAG,iBAAiB,GAAG,mBAAmB,CAC9F,GAAG,MAAM,CAQT;AAED;;GAEG;AACH,wBAAgB,iCAAiC,IAAI,IAAI,CAExD;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,2BAA2B,EAAE,CAoB/F;AASD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAI9D;AA8ED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAqBlF;AAKD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI,CAkBxE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,CAErE;AAMD;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,qFAAqF;IACrF,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC;CAC5F;AAED;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,GACxC,cAAc;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,EAAE,EAClE,WAAW,SAAS,GAAG,SAAS,EAChC,SAAS,MAAM,GAAG,IAAI,GAAG,SAAS,KACjC,MAAM,EAGP,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,0BAA0B,GACrC,SAAS,mBAAmB,EAC5B,gBAAgB,MAAM,GAAG,IAAI,GAAG,SAAS,KACxC,IAoBF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAwBjD,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CAyB1E;AAwCD,eAAO,MAAM,gBAAgB,QAsDrB,CAAC;AAET;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAMnE;AAOD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,UAAU,GAAG,KAAK,CAWzE;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,QAAQ,EACd,YAAY,EAAE,eAAe,EAC7B,iBAAiB,EAAE,iBAAiB,GACnC,SAAS,CAgRX"}
|
package/dist/mcp/mcp.server.js
CHANGED
|
@@ -61,7 +61,7 @@ export function getCachedMcpToolMetadata(deps) {
|
|
|
61
61
|
};
|
|
62
62
|
});
|
|
63
63
|
mcpToolMetadataCache.set(cacheKey, metadata);
|
|
64
|
-
logger.verbose(
|
|
64
|
+
logger.verbose('MCP tool metadata cached', { toolCount: metadata.length, cacheKey });
|
|
65
65
|
return metadata;
|
|
66
66
|
}
|
|
67
67
|
function isExpectedMcpAuthError(message) {
|
|
@@ -508,7 +508,8 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory) {
|
|
|
508
508
|
});
|
|
509
509
|
}
|
|
510
510
|
catch (rlErr) {
|
|
511
|
-
logger.warn(
|
|
511
|
+
logger.warn('MCP rate limiter threw — failing open', {
|
|
512
|
+
toolName,
|
|
512
513
|
error: rlErr instanceof Error ? rlErr.message : String(rlErr),
|
|
513
514
|
});
|
|
514
515
|
}
|
|
@@ -665,7 +666,7 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory) {
|
|
|
665
666
|
}
|
|
666
667
|
catch (err) {
|
|
667
668
|
const message = err instanceof Error ? err.message : String(err);
|
|
668
|
-
logger.error(
|
|
669
|
+
logger.error('MCP tool failed', { toolName, error: message });
|
|
669
670
|
if (shouldReportMcpToolError(err)) {
|
|
670
671
|
reportDeps.reportToolError?.(err, {
|
|
671
672
|
subsystem: 'mcp',
|
|
@@ -691,7 +692,7 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory) {
|
|
|
691
692
|
}
|
|
692
693
|
});
|
|
693
694
|
}
|
|
694
|
-
logger.verbose(
|
|
695
|
+
logger.verbose('MCP server created', { toolCount: toolMetadata.length });
|
|
695
696
|
return server;
|
|
696
697
|
}
|
|
697
698
|
//# sourceMappingURL=mcp.server.js.map
|