@indexnetwork/protocol 3.7.0-rc.277.1 → 3.7.1-rc.278.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.
Files changed (86) hide show
  1. package/dist/agent/agent.tools.js +1 -1
  2. package/dist/agent/agent.tools.js.map +1 -1
  3. package/dist/chat/chat.agent.d.ts +6 -6
  4. package/dist/chat/chat.agent.d.ts.map +1 -1
  5. package/dist/chat/chat.agent.js +9 -9
  6. package/dist/chat/chat.agent.js.map +1 -1
  7. package/dist/chat/chat.graph.d.ts.map +1 -1
  8. package/dist/chat/chat.graph.js +3 -12
  9. package/dist/chat/chat.graph.js.map +1 -1
  10. package/dist/chat/chat.interrupt.classifier.d.ts.map +1 -1
  11. package/dist/chat/chat.interrupt.classifier.js +1 -3
  12. package/dist/chat/chat.interrupt.classifier.js.map +1 -1
  13. package/dist/chat/chat.suggester.js.map +1 -1
  14. package/dist/context/context.generator.d.ts +2 -0
  15. package/dist/context/context.generator.d.ts.map +1 -1
  16. package/dist/context/context.generator.js +8 -6
  17. package/dist/context/context.generator.js.map +1 -1
  18. package/dist/intent/intent.clarifier.d.ts +2 -0
  19. package/dist/intent/intent.clarifier.d.ts.map +1 -1
  20. package/dist/intent/intent.clarifier.js +9 -23
  21. package/dist/intent/intent.clarifier.js.map +1 -1
  22. package/dist/intent/intent.graph.d.ts.map +1 -1
  23. package/dist/intent/intent.graph.js +29 -26
  24. package/dist/intent/intent.graph.js.map +1 -1
  25. package/dist/intent/intent.tools.d.ts.map +1 -1
  26. package/dist/intent/intent.tools.js +18 -48
  27. package/dist/intent/intent.tools.js.map +1 -1
  28. package/dist/maintenance/maintenance.graph.d.ts.map +1 -1
  29. package/dist/maintenance/maintenance.graph.js +1 -2
  30. package/dist/maintenance/maintenance.graph.js.map +1 -1
  31. package/dist/mcp/mcp.server.d.ts.map +1 -1
  32. package/dist/mcp/mcp.server.js +2 -4
  33. package/dist/mcp/mcp.server.js.map +1 -1
  34. package/dist/negotiation/negotiation.graph.d.ts.map +1 -1
  35. package/dist/negotiation/negotiation.graph.js +13 -20
  36. package/dist/negotiation/negotiation.graph.js.map +1 -1
  37. package/dist/negotiation/negotiation.tools.d.ts.map +1 -1
  38. package/dist/negotiation/negotiation.tools.js +12 -12
  39. package/dist/negotiation/negotiation.tools.js.map +1 -1
  40. package/dist/network/indexer/indexer.graph.d.ts +9 -9
  41. package/dist/network/indexer/indexer.graph.d.ts.map +1 -1
  42. package/dist/network/indexer/indexer.graph.js.map +1 -1
  43. package/dist/network/network.graph.d.ts.map +1 -1
  44. package/dist/network/network.graph.js +19 -25
  45. package/dist/network/network.graph.js.map +1 -1
  46. package/dist/opportunity/feed/feed.categorizer.d.ts.map +1 -1
  47. package/dist/opportunity/feed/feed.categorizer.js +15 -20
  48. package/dist/opportunity/feed/feed.categorizer.js.map +1 -1
  49. package/dist/opportunity/feed/feed.graph.d.ts.map +1 -1
  50. package/dist/opportunity/feed/feed.graph.js +8 -10
  51. package/dist/opportunity/feed/feed.graph.js.map +1 -1
  52. package/dist/opportunity/opportunity.introducer.d.ts.map +1 -1
  53. package/dist/opportunity/opportunity.introducer.js +1 -2
  54. package/dist/opportunity/opportunity.introducer.js.map +1 -1
  55. package/dist/opportunity/opportunity.tools.d.ts.map +1 -1
  56. package/dist/opportunity/opportunity.tools.js +3 -2
  57. package/dist/opportunity/opportunity.tools.js.map +1 -1
  58. package/dist/profile/profile.enricher.d.ts +5 -7
  59. package/dist/profile/profile.enricher.d.ts.map +1 -1
  60. package/dist/profile/profile.enricher.js +8 -10
  61. package/dist/profile/profile.enricher.js.map +1 -1
  62. package/dist/profile/profile.generator.d.ts.map +1 -1
  63. package/dist/profile/profile.generator.js +1 -2
  64. package/dist/profile/profile.generator.js.map +1 -1
  65. package/dist/profile/profile.tools.js +1 -1
  66. package/dist/profile/profile.tools.js.map +1 -1
  67. package/dist/questioner/questioner.presets.d.ts.map +1 -1
  68. package/dist/questioner/questioner.presets.js +24 -38
  69. package/dist/questioner/questioner.presets.js.map +1 -1
  70. package/dist/shared/agent/tool.factory.d.ts.map +1 -1
  71. package/dist/shared/agent/tool.factory.js +1 -2
  72. package/dist/shared/agent/tool.factory.js.map +1 -1
  73. package/dist/shared/agent/tool.runtime.d.ts.map +1 -1
  74. package/dist/shared/agent/tool.runtime.js +20 -13
  75. package/dist/shared/agent/tool.runtime.js.map +1 -1
  76. package/dist/shared/hyde/hyde.graph.d.ts.map +1 -1
  77. package/dist/shared/hyde/hyde.graph.js +3 -2
  78. package/dist/shared/hyde/hyde.graph.js.map +1 -1
  79. package/dist/shared/hyde/hyde.strategies.d.ts +2 -1
  80. package/dist/shared/hyde/hyde.strategies.d.ts.map +1 -1
  81. package/dist/shared/hyde/hyde.strategies.js.map +1 -1
  82. package/dist/shared/observability/trace.d.ts +3 -3
  83. package/dist/shared/observability/trace.d.ts.map +1 -1
  84. package/dist/shared/observability/trace.js +19 -33
  85. package/dist/shared/observability/trace.js.map +1 -1
  86. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"chat.interrupt.classifier.js","sourceRoot":"/","sources":["chat/chat.interrupt.classifier.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEvE,OAAO,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAExE,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;AAEvD,MAAM,aAAa,GAAG;;;;;;oCAMc,CAAC;AAYrC;;;;GAIG;AACH,MAAM,OAAO,uBAAuB;IAGlC;QACE,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,qBAAqB,CAAC,CAAC;IAClD,CAAC;IAED;;;;;OAKG;IAEG,AAAN,KAAK,CAAC,QAAQ,CAAC,KAA6B;QAC1C,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;QAEtC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,KAAK,EAAE;gBACvD,IAAI,aAAa,CAAC,aAAa,CAAC;gBAChC,IAAI,YAAY,CACd,2BAA2B,UAAU,IAAI,MAAM,0BAA0B,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,gBAAgB,CAC/G;aACF,CAAC,CAAC;YAEH,MAAM,IAAI,GACR,OAAO,QAAQ,CAAC,OAAO,KAAK,QAAQ;gBAClC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;gBACvC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAE1D,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;gBAAE,OAAO,OAAO,CAAC;YAC7C,yDAAyD;YACzD,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,+EAA+E,EAAE;gBAC3F,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC9D,CAAC,CAAC;YACH,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;CACF;AA1BO;IADL,KAAK,EAAE;;;;uDA0BP","sourcesContent":["import type { ChatOpenAI } from \"@langchain/openai\";\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\n\nimport { log } from \"../shared/observability/log.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { createModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = log.lib.from(\"ChatInterruptClassifier\");\n\nconst SYSTEM_PROMPT = `You decide whether a new user message sent during an active AI process should STEER or QUEUE.\n\nRules:\n- Reply with ONLY one word: steer or queue (lowercase).\n- STEER: the message redirects, corrects, stops, contradicts, or changes what the AI is doing. Keywords: \"wait\", \"stop\", \"actually\", \"ignore that\", \"instead\", \"no\", \"cancel\".\n- QUEUE: the message adds context, asks a follow-up, or complements what the AI is doing. Keywords: \"also\", \"and\", \"when done\", \"additionally\", \"plus\".\n- When ambiguous, default to steer.`;\n\nexport interface ClassifyInterruptInput {\n /** The new user message sent while the agent is running. */\n message: string;\n /**\n * Current agent activity summary derived from the last few SSE trace event names\n * (e.g. \"tool_start: discover_opportunities, graph_start: opportunity\").\n */\n agentState: string;\n}\n\n/**\n * Binary classifier that decides whether a mid-stream user message should steer\n * (interrupt the current run) or queue (buffer until the run completes).\n * Uses a low-temperature, minimal-token model for sub-1 s latency.\n */\nexport class ChatInterruptClassifier {\n private model: ChatOpenAI;\n\n constructor() {\n this.model = createModel(\"interruptClassifier\");\n }\n\n /**\n * Classify a mid-stream interrupt as steer or queue.\n *\n * @param input - The new message and current agent state context\n * @returns \"steer\" to interrupt the current run; \"queue\" to buffer\n */\n @Timed()\n async classify(input: ClassifyInterruptInput): Promise<\"steer\" | \"queue\"> {\n const { message, agentState } = input;\n\n try {\n const response = await invokeWithAbortSignal(this.model, [\n new SystemMessage(SYSTEM_PROMPT),\n new HumanMessage(\n `Current agent activity: ${agentState || \"idle\"}\\n\\nNew user message: \"${message.slice(0, 500)}\"\\n\\nDecision:`,\n ),\n ]);\n\n const text =\n typeof response.content === \"string\"\n ? response.content.trim().toLowerCase()\n : String(response.content ?? \"\").trim().toLowerCase();\n\n if (text.startsWith(\"queue\")) return \"queue\";\n // Default to steer on any ambiguity or unexpected output\n return \"steer\";\n } catch (error) {\n logger.warn(\"[ChatInterruptClassifier.classify] Classification failed, defaulting to steer\", {\n error: error instanceof Error ? error.message : String(error),\n });\n return \"steer\";\n }\n }\n}\n"]}
1
+ {"version":3,"file":"chat.interrupt.classifier.js","sourceRoot":"/","sources":["chat/chat.interrupt.classifier.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEvE,OAAO,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAExE,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;AAEvD,MAAM,aAAa,GAAG;;;;;;oCAMc,CAAC;AAYrC;;;;GAIG;AACH,MAAM,OAAO,uBAAuB;IAGlC;QACE,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,qBAAqB,CAAC,CAAC;IAClD,CAAC;IAED;;;;;OAKG;IAEG,AAAN,KAAK,CAAC,QAAQ,CAAC,KAA6B;QAC1C,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;QAEtC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,KAAK,EAAE;gBACvD,IAAI,aAAa,CAAC,aAAa,CAAC;gBAChC,IAAI,YAAY,CACd,2BAA2B,UAAU,IAAI,MAAM,0BAA0B,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,gBAAgB,CAC/G;aACF,CAAC,CAAC;YAEH,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAEjE,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;gBAAE,OAAO,OAAO,CAAC;YAC7C,yDAAyD;YACzD,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,+EAA+E,EAAE;gBAC3F,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC9D,CAAC,CAAC;YACH,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;CACF;AAvBO;IADL,KAAK,EAAE;;;;uDAuBP","sourcesContent":["import type { ChatOpenAI } from \"@langchain/openai\";\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\n\nimport { log } from \"../shared/observability/log.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { createModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = log.lib.from(\"ChatInterruptClassifier\");\n\nconst SYSTEM_PROMPT = `You decide whether a new user message sent during an active AI process should STEER or QUEUE.\n\nRules:\n- Reply with ONLY one word: steer or queue (lowercase).\n- STEER: the message redirects, corrects, stops, contradicts, or changes what the AI is doing. Keywords: \"wait\", \"stop\", \"actually\", \"ignore that\", \"instead\", \"no\", \"cancel\".\n- QUEUE: the message adds context, asks a follow-up, or complements what the AI is doing. Keywords: \"also\", \"and\", \"when done\", \"additionally\", \"plus\".\n- When ambiguous, default to steer.`;\n\nexport interface ClassifyInterruptInput {\n /** The new user message sent while the agent is running. */\n message: string;\n /**\n * Current agent activity summary derived from the last few SSE trace event names\n * (e.g. \"tool_start: discover_opportunities, graph_start: opportunity\").\n */\n agentState: string;\n}\n\n/**\n * Binary classifier that decides whether a mid-stream user message should steer\n * (interrupt the current run) or queue (buffer until the run completes).\n * Uses a low-temperature, minimal-token model for sub-1 s latency.\n */\nexport class ChatInterruptClassifier {\n private model: ChatOpenAI;\n\n constructor() {\n this.model = createModel(\"interruptClassifier\");\n }\n\n /**\n * Classify a mid-stream interrupt as steer or queue.\n *\n * @param input - The new message and current agent state context\n * @returns \"steer\" to interrupt the current run; \"queue\" to buffer\n */\n @Timed()\n async classify(input: ClassifyInterruptInput): Promise<\"steer\" | \"queue\"> {\n const { message, agentState } = input;\n\n try {\n const response = await invokeWithAbortSignal(this.model, [\n new SystemMessage(SYSTEM_PROMPT),\n new HumanMessage(\n `Current agent activity: ${agentState || \"idle\"}\\n\\nNew user message: \"${message.slice(0, 500)}\"\\n\\nDecision:`,\n ),\n ]);\n\n const text = String(response.content ?? \"\").trim().toLowerCase();\n\n if (text.startsWith(\"queue\")) return \"queue\";\n // Default to steer on any ambiguity or unexpected output\n return \"steer\";\n } catch (error) {\n logger.warn(\"[ChatInterruptClassifier.classify] Classification failed, defaulting to steer\", {\n error: error instanceof Error ? error.message : String(error),\n });\n return \"steer\";\n }\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"chat.suggester.js","sourceRoot":"/","sources":["chat/chat.suggester.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChatOpenAI } from \"@langchain/openai\";\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { z } from \"zod\";\nimport type { ChatSuggestion } from \"./chat-streaming.types.js\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { createModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger(\"SuggestionGenerator\");\n\nconst suggestionItemSchema = z.object({\n label: z.string().describe(\"Short label for the chip (2-5 words)\"),\n type: z.enum([\"direct\", \"prompt\"]).describe(\"direct = auto-submit message; prompt = prefill input\"),\n followupText: z.string().nullable().describe(\"For type=direct: full message to send when clicked; null for prompt type\"),\n prefill: z.string().nullable().describe(\"For type=prompt: text to prefill the input; null for direct type\"),\n});\n\nconst suggestionsSchema = z.object({\n suggestions: z\n .array(suggestionItemSchema)\n .min(1)\n .max(6)\n .describe(\"3-5 follow-up suggestions based on the conversation\"),\n});\n\nconst SYSTEM_PROMPT = `You generate follow-up suggestions for a chat user based on their conversation.\n\nRules:\n- Return 3-5 suggestions. Mix of \"direct\" (one-click send) and \"prompt\" (prefill for user to edit).\n- Labels must be short: 2-5 words (e.g. \"Find collaborators\", \"Add more details\").\n- For type=direct: provide followupText as the exact message to send (complete sentence).\n- For type=prompt: provide prefill as the start of a sentence the user can complete (e.g. \"I need help with \").\n- Suggestions must be relevant to the last exchange and natural next steps.\n- Do not repeat what the user or assistant just said; suggest logical follow-ups.\n- Voice: Calm, direct; no hype or networking clichés. Prefer words like opportunity, overlap, signal, pattern, relevant. Avoid: search, leverage, networking, match, optimize, scale.`;\n\nexport interface SuggestionGeneratorInput {\n /** Last few messages (user and assistant) to derive context */\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n /** Optional index/community context to tailor suggestions */\n indexContext?: string;\n}\n\n/**\n * Lightweight generator for context-aware chat follow-up suggestions.\n * Uses a fast model and structured output to return 3-5 suggestions per call.\n */\nexport class SuggestionGenerator {\n private model: ReturnType<ChatOpenAI[\"withStructuredOutput\"]>;\n\n constructor() {\n const llm = createModel(\"suggestionGenerator\");\n this.model = llm.withStructuredOutput(suggestionsSchema, { name: \"chat_suggestions\" });\n }\n\n /**\n * Generate follow-up suggestions from the last exchange.\n * Returns empty array on failure (graceful degradation).\n */\n @Timed()\n async generate(input: SuggestionGeneratorInput): Promise<ChatSuggestion[]> {\n const { messages, indexContext } = input;\n if (messages.length === 0) return [];\n\n const excerpt = messages\n .slice(-6)\n .map((m) => `${m.role === \"user\" ? \"User\" : \"Assistant\"}: ${m.content.slice(0, 300)}`)\n .join(\"\\n\\n\");\n\n const userContent = indexContext\n ? `Conversation (community context: ${indexContext}):\\n\\n${excerpt}\\n\\nGenerate 3-5 follow-up suggestions.`\n : `Conversation:\\n\\n${excerpt}\\n\\nGenerate 3-5 follow-up suggestions.`;\n\n try {\n const result = await invokeWithAbortSignal(this.model, [\n new SystemMessage(SYSTEM_PROMPT),\n new HumanMessage(userContent),\n ]);\n const parsed = suggestionsSchema.safeParse(result);\n if (!parsed.success) {\n logger.warn(\"[SuggestionGenerator] Parse failed\", { error: parsed.error.message });\n return [];\n }\n const out: ChatSuggestion[] = parsed.data.suggestions.map((s) => ({\n label: s.label,\n type: s.type as \"direct\" | \"prompt\",\n ...(s.type === \"direct\" && s.followupText != null && { followupText: s.followupText }),\n ...(s.type === \"prompt\" && s.prefill != null && { prefill: s.prefill }),\n }));\n logger.verbose(\"[SuggestionGenerator] Generated\", { count: out.length });\n return out;\n } catch (error) {\n logger.warn(\"[SuggestionGenerator] Failed\", {\n error: error instanceof Error ? error.message : String(error),\n });\n return [];\n }\n }\n}\n"]}
1
+ {"version":3,"file":"chat.suggester.js","sourceRoot":"/","sources":["chat/chat.suggester.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChatOpenAI } from \"@langchain/openai\";\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { z } from \"zod\";\nimport type { ChatSuggestion } from \"./chat-streaming.types.js\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { createModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger(\"SuggestionGenerator\");\n\nconst suggestionItemSchema = z.object({\n label: z.string().describe(\"Short label for the chip (2-5 words)\"),\n type: z.enum([\"direct\", \"prompt\"]).describe(\"direct = auto-submit message; prompt = prefill input\"),\n followupText: z.string().nullable().describe(\"For type=direct: full message to send when clicked; null for prompt type\"),\n prefill: z.string().nullable().describe(\"For type=prompt: text to prefill the input; null for direct type\"),\n});\n\nconst suggestionsSchema = z.object({\n suggestions: z\n .array(suggestionItemSchema)\n .min(1)\n .max(6)\n .describe(\"3-5 follow-up suggestions based on the conversation\"),\n});\n\nconst SYSTEM_PROMPT = `You generate follow-up suggestions for a chat user based on their conversation.\n\nRules:\n- Return 3-5 suggestions. Mix of \"direct\" (one-click send) and \"prompt\" (prefill for user to edit).\n- Labels must be short: 2-5 words (e.g. \"Find collaborators\", \"Add more details\").\n- For type=direct: provide followupText as the exact message to send (complete sentence).\n- For type=prompt: provide prefill as the start of a sentence the user can complete (e.g. \"I need help with \").\n- Suggestions must be relevant to the last exchange and natural next steps.\n- Do not repeat what the user or assistant just said; suggest logical follow-ups.\n- Voice: Calm, direct; no hype or networking clichés. Prefer words like opportunity, overlap, signal, pattern, relevant. Avoid: search, leverage, networking, match, optimize, scale.`;\n\nexport interface SuggestionGeneratorInput {\n /** Last few messages (user and assistant) to derive context */\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n /** Optional index/community context to tailor suggestions */\n indexContext?: string;\n}\n\n/**\n * Lightweight generator for context-aware chat follow-up suggestions.\n * Uses a fast model and structured output to return 3-5 suggestions per call.\n */\nexport class SuggestionGenerator {\n private model: ReturnType<ChatOpenAI[\"withStructuredOutput\"]>;\n\n constructor() {\n const llm = createModel(\"suggestionGenerator\");\n this.model = llm.withStructuredOutput(suggestionsSchema, { name: \"chat_suggestions\" });\n }\n\n /**\n * Generate follow-up suggestions from the last exchange.\n * Returns empty array on failure (graceful degradation).\n */\n @Timed()\n async generate(input: SuggestionGeneratorInput): Promise<ChatSuggestion[]> {\n const { messages, indexContext } = input;\n if (messages.length === 0) return [];\n\n const excerpt = messages\n .slice(-6)\n .map((m) => `${m.role === \"user\" ? \"User\" : \"Assistant\"}: ${m.content.slice(0, 300)}`)\n .join(\"\\n\\n\");\n\n const userContent = indexContext\n ? `Conversation (community context: ${indexContext}):\\n\\n${excerpt}\\n\\nGenerate 3-5 follow-up suggestions.`\n : `Conversation:\\n\\n${excerpt}\\n\\nGenerate 3-5 follow-up suggestions.`;\n\n try {\n const result = await invokeWithAbortSignal(this.model, [\n new SystemMessage(SYSTEM_PROMPT),\n new HumanMessage(userContent),\n ]);\n const parsed = suggestionsSchema.safeParse(result);\n if (!parsed.success) {\n logger.warn(\"[SuggestionGenerator] Parse failed\", { error: parsed.error.message });\n return [];\n }\n const out: ChatSuggestion[] = parsed.data.suggestions.map((s) => ({\n label: s.label,\n type: s.type,\n ...(s.type === \"direct\" && s.followupText != null && { followupText: s.followupText }),\n ...(s.type === \"prompt\" && s.prefill != null && { prefill: s.prefill }),\n }));\n logger.verbose(\"[SuggestionGenerator] Generated\", { count: out.length });\n return out;\n } catch (error) {\n logger.warn(\"[SuggestionGenerator] Failed\", {\n error: error instanceof Error ? error.message : String(error),\n });\n return [];\n }\n }\n}\n"]}
@@ -51,6 +51,8 @@ export declare class UserContextGenerator {
51
51
  * @returns Updated context text with embedding vector
52
52
  */
53
53
  generateIncremental(input: IncrementalContextInput): Promise<UserContextResult>;
54
+ /** Normalize an LLM message content into a plain string. */
55
+ private extractText;
54
56
  /** Format the network context header for LLM prompts. */
55
57
  private formatNetworkContext;
56
58
  /** Describe the premise change for the incremental prompt. */
@@ -1 +1 @@
1
- {"version":3,"file":"context.generator.d.ts","sourceRoot":"/","sources":["context/context.generator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAC;AAErF,2EAA2E;AAC3E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,yEAAyE;AACzE,MAAM,WAAW,uBAAuB;IACtC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAMD;;;GAGG;AACH,qBAAa,oBAAoB;IAGnB,OAAO,CAAC,kBAAkB;IAFtC,OAAO,CAAC,KAAK,CAAuC;gBAEhC,kBAAkB,EAAE,kBAAkB;IAE1D;;;;;OAKG;IACG,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAmB5E;;;;;OAKG;IACG,mBAAmB,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAmBrF,yDAAyD;IACzD,OAAO,CAAC,oBAAoB;IAM5B,8DAA8D;IAC9D,OAAO,CAAC,cAAc;IAatB,iEAAiE;YACnD,KAAK;CAIpB"}
1
+ {"version":3,"file":"context.generator.d.ts","sourceRoot":"/","sources":["context/context.generator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAC;AAErF,2EAA2E;AAC3E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,yEAAyE;AACzE,MAAM,WAAW,uBAAuB;IACtC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAMD;;;GAGG;AACH,qBAAa,oBAAoB;IAGnB,OAAO,CAAC,kBAAkB;IAFtC,OAAO,CAAC,KAAK,CAAuC;gBAEhC,kBAAkB,EAAE,kBAAkB;IAE1D;;;;;OAKG;IACG,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAiB5E;;;;;OAKG;IACG,mBAAmB,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAiBrF,4DAA4D;IAC5D,OAAO,CAAC,WAAW;IAMnB,yDAAyD;IACzD,OAAO,CAAC,oBAAoB;IAM5B,8DAA8D;IAC9D,OAAO,CAAC,cAAc;IAatB,iEAAiE;YACnD,KAAK;CAIpB"}
@@ -33,9 +33,7 @@ export class UserContextGenerator {
33
33
  new SystemMessage(COLD_START_SYSTEM_PROMPT),
34
34
  new HumanMessage(`${networkContext}\n\nPremises:\n${premiseBlock}\n\nWrite a focused context paragraph for this person in this network.`),
35
35
  ]);
36
- const text = typeof response.content === 'string'
37
- ? response.content
38
- : String(response.content ?? '').trim();
36
+ const text = this.extractText(response.content);
39
37
  const embedding = await this.embed(text);
40
38
  return { text, embedding };
41
39
  }
@@ -52,12 +50,16 @@ export class UserContextGenerator {
52
50
  new SystemMessage(INCREMENTAL_SYSTEM_PROMPT),
53
51
  new HumanMessage(`${networkContext}\n\nCurrent context:\n${input.currentContext}\n\nChange:\n${changeDescription}\n\nWrite the updated context paragraph.`),
54
52
  ]);
55
- const text = typeof response.content === 'string'
56
- ? response.content
57
- : String(response.content ?? '').trim();
53
+ const text = this.extractText(response.content);
58
54
  const embedding = await this.embed(text);
59
55
  return { text, embedding };
60
56
  }
57
+ /** Normalize an LLM message content into a plain string. */
58
+ extractText(content) {
59
+ return typeof content === 'string'
60
+ ? content
61
+ : String(content ?? '').trim();
62
+ }
61
63
  /** Format the network context header for LLM prompts. */
62
64
  formatNetworkContext(prompt, title) {
63
65
  return prompt
@@ -1 +1 @@
1
- {"version":3,"file":"context.generator.js","sourceRoot":"/","sources":["context/context.generator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * User Context Generator\n *\n * Synthesizes network-scoped context paragraphs from user premises.\n * Two modes: cold-start (all premises at once) and incremental\n * (single premise change applied to an existing context).\n * Returns { text, embedding } for storage in the user_contexts table.\n */\n\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\n\nimport { createModel } from \"../shared/agent/model.config.js\";\nimport { getAbortSignalConfig, invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\nimport type { EmbeddingGenerator } from \"../shared/interfaces/embedder.interface.js\";\n\n/** Input for cold-start context generation from a full set of premises. */\nexport interface UserContextInput {\n premises: Array<{ text: string }>;\n networkPrompt: string | null;\n networkTitle: string;\n}\n\n/** Input for incremental context update from a single premise change. */\nexport interface IncrementalContextInput {\n currentContext: string;\n changeType: 'added' | 'updated' | 'retracted' | 'expired';\n premiseText: string;\n previousPremiseText?: string;\n networkPrompt: string | null;\n networkTitle: string;\n}\n\n/** Generated context paragraph with its embedding vector. */\nexport interface UserContextResult {\n text: string;\n embedding: number[];\n}\n\nconst COLD_START_SYSTEM_PROMPT = `You synthesize user context paragraphs for community matching. Given a list of premises (atomic facts about a person) and a network description, write a focused paragraph (3-6 sentences) that captures who this person is through the lens of that network's purpose. Highlight what is most relevant to the network. Write in third person. Be specific and concrete, not generic.`;\n\nconst INCREMENTAL_SYSTEM_PROMPT = `You maintain user context paragraphs for community matching. You will receive the current context paragraph, a change that occurred, and the network description. Update the context paragraph to reflect the change while preserving all other information. Keep the same style: 3-6 sentences, third person, specific and concrete. For retractions/expirations, remove the relevant information. For additions/updates, integrate the new information naturally.`;\n\n/**\n * Generates network-scoped context paragraphs from user premises.\n * Uses LLM synthesis to produce focused context and an embedding vector.\n */\nexport class UserContextGenerator {\n private model = createModel('userContextGenerator');\n\n constructor(private embeddingGenerator: EmbeddingGenerator) {}\n\n /**\n * Generate a context paragraph from the full set of premises (cold-start).\n *\n * @param input - Premises, network prompt, and network title\n * @returns Synthesized context text with embedding vector\n */\n async generateColdStart(input: UserContextInput): Promise<UserContextResult> {\n const premiseBlock = input.premises.map(p => `- ${p.text}`).join('\\n');\n const networkContext = this.formatNetworkContext(input.networkPrompt, input.networkTitle);\n\n const response = await invokeWithAbortSignal(this.model, [\n new SystemMessage(COLD_START_SYSTEM_PROMPT),\n new HumanMessage(\n `${networkContext}\\n\\nPremises:\\n${premiseBlock}\\n\\nWrite a focused context paragraph for this person in this network.`,\n ),\n ]);\n\n const text = typeof response.content === 'string'\n ? response.content\n : String(response.content ?? '').trim();\n\n const embedding = await this.embed(text);\n return { text, embedding };\n }\n\n /**\n * Update an existing context paragraph after a single premise change.\n *\n * @param input - Current context, change details, network metadata\n * @returns Updated context text with embedding vector\n */\n async generateIncremental(input: IncrementalContextInput): Promise<UserContextResult> {\n const networkContext = this.formatNetworkContext(input.networkPrompt, input.networkTitle);\n const changeDescription = this.describeChange(input);\n\n const response = await invokeWithAbortSignal(this.model, [\n new SystemMessage(INCREMENTAL_SYSTEM_PROMPT),\n new HumanMessage(\n `${networkContext}\\n\\nCurrent context:\\n${input.currentContext}\\n\\nChange:\\n${changeDescription}\\n\\nWrite the updated context paragraph.`,\n ),\n ]);\n\n const text = typeof response.content === 'string'\n ? response.content\n : String(response.content ?? '').trim();\n\n const embedding = await this.embed(text);\n return { text, embedding };\n }\n\n /** Format the network context header for LLM prompts. */\n private formatNetworkContext(prompt: string | null, title: string): string {\n return prompt\n ? `Network \"${title}\": ${prompt}`\n : `Network: ${title}`;\n }\n\n /** Describe the premise change for the incremental prompt. */\n private describeChange(input: IncrementalContextInput): string {\n switch (input.changeType) {\n case 'added':\n return `A new fact was learned about this person:\\n\"${input.premiseText}\"`;\n case 'updated':\n return `A fact was updated.\\nOld: \"${input.previousPremiseText}\"\\nNew: \"${input.premiseText}\"`;\n case 'retracted':\n return `A fact was retracted (no longer true):\\n\"${input.premiseText}\"`;\n case 'expired':\n return `A fact has expired (time-bound, no longer current):\\n\"${input.premiseText}\"`;\n }\n }\n\n /** Generate embedding from text, normalizing the return type. */\n private async embed(text: string): Promise<number[]> {\n const result = await this.embeddingGenerator.generate(text, undefined, getAbortSignalConfig());\n return Array.isArray(result[0]) ? result[0] as number[] : result as number[];\n }\n}\n"]}
1
+ {"version":3,"file":"context.generator.js","sourceRoot":"/","sources":["context/context.generator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * User Context Generator\n *\n * Synthesizes network-scoped context paragraphs from user premises.\n * Two modes: cold-start (all premises at once) and incremental\n * (single premise change applied to an existing context).\n * Returns { text, embedding } for storage in the user_contexts table.\n */\n\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\n\nimport { createModel } from \"../shared/agent/model.config.js\";\nimport { getAbortSignalConfig, invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\nimport type { EmbeddingGenerator } from \"../shared/interfaces/embedder.interface.js\";\n\n/** Input for cold-start context generation from a full set of premises. */\nexport interface UserContextInput {\n premises: Array<{ text: string }>;\n networkPrompt: string | null;\n networkTitle: string;\n}\n\n/** Input for incremental context update from a single premise change. */\nexport interface IncrementalContextInput {\n currentContext: string;\n changeType: 'added' | 'updated' | 'retracted' | 'expired';\n premiseText: string;\n previousPremiseText?: string;\n networkPrompt: string | null;\n networkTitle: string;\n}\n\n/** Generated context paragraph with its embedding vector. */\nexport interface UserContextResult {\n text: string;\n embedding: number[];\n}\n\nconst COLD_START_SYSTEM_PROMPT = `You synthesize user context paragraphs for community matching. Given a list of premises (atomic facts about a person) and a network description, write a focused paragraph (3-6 sentences) that captures who this person is through the lens of that network's purpose. Highlight what is most relevant to the network. Write in third person. Be specific and concrete, not generic.`;\n\nconst INCREMENTAL_SYSTEM_PROMPT = `You maintain user context paragraphs for community matching. You will receive the current context paragraph, a change that occurred, and the network description. Update the context paragraph to reflect the change while preserving all other information. Keep the same style: 3-6 sentences, third person, specific and concrete. For retractions/expirations, remove the relevant information. For additions/updates, integrate the new information naturally.`;\n\n/**\n * Generates network-scoped context paragraphs from user premises.\n * Uses LLM synthesis to produce focused context and an embedding vector.\n */\nexport class UserContextGenerator {\n private model = createModel('userContextGenerator');\n\n constructor(private embeddingGenerator: EmbeddingGenerator) {}\n\n /**\n * Generate a context paragraph from the full set of premises (cold-start).\n *\n * @param input - Premises, network prompt, and network title\n * @returns Synthesized context text with embedding vector\n */\n async generateColdStart(input: UserContextInput): Promise<UserContextResult> {\n const premiseBlock = input.premises.map(p => `- ${p.text}`).join('\\n');\n const networkContext = this.formatNetworkContext(input.networkPrompt, input.networkTitle);\n\n const response = await invokeWithAbortSignal(this.model, [\n new SystemMessage(COLD_START_SYSTEM_PROMPT),\n new HumanMessage(\n `${networkContext}\\n\\nPremises:\\n${premiseBlock}\\n\\nWrite a focused context paragraph for this person in this network.`,\n ),\n ]);\n\n const text = this.extractText(response.content);\n\n const embedding = await this.embed(text);\n return { text, embedding };\n }\n\n /**\n * Update an existing context paragraph after a single premise change.\n *\n * @param input - Current context, change details, network metadata\n * @returns Updated context text with embedding vector\n */\n async generateIncremental(input: IncrementalContextInput): Promise<UserContextResult> {\n const networkContext = this.formatNetworkContext(input.networkPrompt, input.networkTitle);\n const changeDescription = this.describeChange(input);\n\n const response = await invokeWithAbortSignal(this.model, [\n new SystemMessage(INCREMENTAL_SYSTEM_PROMPT),\n new HumanMessage(\n `${networkContext}\\n\\nCurrent context:\\n${input.currentContext}\\n\\nChange:\\n${changeDescription}\\n\\nWrite the updated context paragraph.`,\n ),\n ]);\n\n const text = this.extractText(response.content);\n\n const embedding = await this.embed(text);\n return { text, embedding };\n }\n\n /** Normalize an LLM message content into a plain string. */\n private extractText(content: unknown): string {\n return typeof content === 'string'\n ? content\n : String(content ?? '').trim();\n }\n\n /** Format the network context header for LLM prompts. */\n private formatNetworkContext(prompt: string | null, title: string): string {\n return prompt\n ? `Network \"${title}\": ${prompt}`\n : `Network: ${title}`;\n }\n\n /** Describe the premise change for the incremental prompt. */\n private describeChange(input: IncrementalContextInput): string {\n switch (input.changeType) {\n case 'added':\n return `A new fact was learned about this person:\\n\"${input.premiseText}\"`;\n case 'updated':\n return `A fact was updated.\\nOld: \"${input.previousPremiseText}\"\\nNew: \"${input.premiseText}\"`;\n case 'retracted':\n return `A fact was retracted (no longer true):\\n\"${input.premiseText}\"`;\n case 'expired':\n return `A fact has expired (time-bound, no longer current):\\n\"${input.premiseText}\"`;\n }\n }\n\n /** Generate embedding from text, normalizing the return type. */\n private async embed(text: string): Promise<number[]> {\n const result = await this.embeddingGenerator.generate(text, undefined, getAbortSignalConfig());\n return Array.isArray(result[0]) ? result[0] as number[] : result as number[];\n }\n}\n"]}
@@ -21,6 +21,8 @@ export declare class IntentClarifier {
21
21
  private readonly suggestionModel;
22
22
  private readonly clarificationDraftModel;
23
23
  constructor();
24
+ /** Build the shared user prompt with intent, profile, and active-intent context. */
25
+ private buildPrompt;
24
26
  invoke(description: string, profileContext: string, activeIntentsContext: string): Promise<IntentClarifierOutput>;
25
27
  private generateSuggestion;
26
28
  private generateClarificationDraft;
@@ -1 +1 @@
1
- {"version":3,"file":"intent.clarifier.d.ts","sourceRoot":"/","sources":["intent/intent.clarifier.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,QAAA,MAAM,mBAAmB;;;;;;;;;;;;;;;EAKvB,CAAC;AASH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AA4CxE,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2B;IACjD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA2B;IAC3D,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAA2B;;IAiBtD,MAAM,CACjB,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,oBAAoB,EAAE,MAAM,GAC3B,OAAO,CAAC,qBAAqB,CAAC;YA2CnB,kBAAkB;YA6BlB,0BAA0B;CAoCzC"}
1
+ {"version":3,"file":"intent.clarifier.d.ts","sourceRoot":"/","sources":["intent/intent.clarifier.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,QAAA,MAAM,mBAAmB;;;;;;;;;;;;;;;EAKvB,CAAC;AASH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AA4CxE,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2B;IACjD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA2B;IAC3D,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAA2B;;IAgBnE,oFAAoF;IACpF,OAAO,CAAC,WAAW;IAkBN,MAAM,CACjB,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,oBAAoB,EAAE,MAAM,GAC3B,OAAO,CAAC,qBAAqB,CAAC;YAkCnB,kBAAkB;YAoBlB,0BAA0B;CA2BzC"}
@@ -77,9 +77,9 @@ export class IntentClarifier {
77
77
  });
78
78
  this.clarificationDraftModel = baseModel.withStructuredOutput(clarificationDraftSchema, { name: "intent_clarifier_message" });
79
79
  }
80
- async invoke(description, profileContext, activeIntentsContext) {
81
- try {
82
- const prompt = `
80
+ /** Build the shared user prompt with intent, profile, and active-intent context. */
81
+ buildPrompt(description, profileContext, activeIntentsContext) {
82
+ return `
83
83
  # User Input Intent
84
84
  ${description}
85
85
 
@@ -89,6 +89,10 @@ ${profileContext || "none"}
89
89
  # Active Intents
90
90
  ${activeIntentsContext || "none"}
91
91
  `;
92
+ }
93
+ async invoke(description, profileContext, activeIntentsContext) {
94
+ try {
95
+ const prompt = this.buildPrompt(description, profileContext, activeIntentsContext);
92
96
  const result = await invokeWithAbortSignal(this.model, [
93
97
  new SystemMessage(systemPrompt),
94
98
  new HumanMessage(prompt),
@@ -119,16 +123,7 @@ ${activeIntentsContext || "none"}
119
123
  }
120
124
  async generateSuggestion(description, profileContext, activeIntentsContext) {
121
125
  try {
122
- const prompt = `
123
- # User Input Intent
124
- ${description}
125
-
126
- # User Profile
127
- ${profileContext || "none"}
128
-
129
- # Active Intents
130
- ${activeIntentsContext || "none"}
131
- `;
126
+ const prompt = this.buildPrompt(description, profileContext, activeIntentsContext);
132
127
  const output = await invokeWithAbortSignal(this.suggestionModel, [
133
128
  new SystemMessage(suggestionPrompt),
134
129
  new HumanMessage(prompt),
@@ -144,16 +139,7 @@ ${activeIntentsContext || "none"}
144
139
  }
145
140
  async generateClarificationDraft(description, profileContext, activeIntentsContext) {
146
141
  try {
147
- const prompt = `
148
- # User Input Intent
149
- ${description}
150
-
151
- # User Profile
152
- ${profileContext || "none"}
153
-
154
- # Active Intents
155
- ${activeIntentsContext || "none"}
156
- `;
142
+ const prompt = this.buildPrompt(description, profileContext, activeIntentsContext);
157
143
  const output = await invokeWithAbortSignal(this.clarificationDraftModel, [
158
144
  new SystemMessage(clarificationDraftPrompt),
159
145
  new HumanMessage(prompt),
@@ -1 +1 @@
1
- {"version":3,"file":"intent.clarifier.js","sourceRoot":"/","sources":["intent/intent.clarifier.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAC5E,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAE/D,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAExE,MAAM,MAAM,GAAG,cAAc,CAAC,iBAAiB,CAAC,CAAC;AAIjD,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,kBAAkB,EAAE,CAAC,CAAC,OAAO,EAAE;IAC/B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,oBAAoB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3C,oBAAoB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC5C,CAAC,CAAC;AACH,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,oBAAoB,EAAE,CAAC,CAAC,MAAM,EAAE;CACjC,CAAC,CAAC;AACH,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,oBAAoB,EAAE,CAAC,CAAC,MAAM,EAAE;IAChC,oBAAoB,EAAE,CAAC,CAAC,MAAM,EAAE;CACjC,CAAC,CAAC;AAIH,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;CAiBpB,CAAC;AAEF,MAAM,gBAAgB,GAAG;;;;;;;;;CASxB,CAAC;AAEF,MAAM,wBAAwB,GAAG;;;;;;;;mBAQd,GAAG,6CAA6C,GAAG;;CAErE,CAAC;AAEF,MAAM,OAAO,eAAe;IAK1B;QACE,MAAM,SAAS,GAAG,WAAW,CAAC,iBAAiB,CAAC,CAAC;QACjD,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,oBAAoB,CAAC,mBAAmB,EAAE;YAC/D,IAAI,EAAE,kBAAkB;SACzB,CAAC,CAAC;QACH,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC,oBAAoB,CAAC,gBAAgB,EAAE;YACtE,IAAI,EAAE,6BAA6B;SACpC,CAAC,CAAC;QACH,IAAI,CAAC,uBAAuB,GAAG,SAAS,CAAC,oBAAoB,CAC3D,wBAAwB,EACxB,EAAE,IAAI,EAAE,0BAA0B,EAAE,CACrC,CAAC;IACJ,CAAC;IAGY,AAAN,KAAK,CAAC,MAAM,CACjB,WAAmB,EACnB,cAAsB,EACtB,oBAA4B;QAE5B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG;;EAEnB,WAAW;;;EAGX,cAAc,IAAI,MAAM;;;EAGxB,oBAAoB,IAAI,MAAM;CAC/B,CAAC;YAEI,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,KAAK,EAAE;gBACrD,IAAI,aAAa,CAAC,YAAY,CAAC;gBAC/B,IAAI,YAAY,CAAC,MAAM,CAAC;aACzB,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAEjD,IAAI,MAAM,CAAC,kBAAkB,EAAE,CAAC;gBAC9B,8FAA8F;gBAC9F,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,0BAA0B,CAAC,WAAW,EAAE,cAAc,EAAE,oBAAoB,CAAC,CAAC;gBACvG,IAAI,KAAK,EAAE,CAAC;oBACV,OAAO;wBACL,GAAG,MAAM;wBACT,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;wBAChD,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;qBACjD,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,8BAA8B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YACvD,OAAO;gBACL,kBAAkB,EAAE,KAAK;gBACzB,MAAM,EAAE,yBAAyB;gBACjC,oBAAoB,EAAE,IAAI;gBAC1B,oBAAoB,EAAE,IAAI;aAC3B,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAC9B,WAAmB,EACnB,cAAsB,EACtB,oBAA4B;QAE5B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG;;EAEnB,WAAW;;;EAGX,cAAc,IAAI,MAAM;;;EAGxB,oBAAoB,IAAI,MAAM;CAC/B,CAAC;YACI,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,eAAe,EAAE;gBAC/D,IAAI,aAAa,CAAC,gBAAgB,CAAC;gBACnC,IAAI,YAAY,CAAC,MAAM,CAAC;aACzB,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC9C,MAAM,UAAU,GAAG,MAAM,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;YACtD,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;QACnD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YACrD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,0BAA0B,CACtC,WAAmB,EACnB,cAAsB,EACtB,oBAA4B;QAE5B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG;;EAEnB,WAAW;;;EAGX,cAAc,IAAI,MAAM;;;EAGxB,oBAAoB,IAAI,MAAM;CAC/B,CAAC;YACI,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,uBAAuB,EAAE;gBACvE,IAAI,aAAa,CAAC,wBAAwB,CAAC;gBAC3C,IAAI,YAAY,CAAC,MAAM,CAAC;aACzB,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,wBAAwB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACtD,MAAM,oBAAoB,GAAG,MAAM,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;YAChE,MAAM,oBAAoB,GAAG,MAAM,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;YAChE,IAAI,CAAC,oBAAoB,IAAI,CAAC,oBAAoB;gBAAE,OAAO,IAAI,CAAC;YAChE,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,CAAC;QACxD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,oCAAoC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAC7D,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,WAAW,EAAE,cAAc,EAAE,oBAAoB,CAAC,CAAC;YACpG,IAAI,CAAC,UAAU;gBAAE,OAAO,IAAI,CAAC;YAC7B,MAAM,oBAAoB,GAAG,gBAAgB,UAAU,GAAG,CAAC;YAC3D,OAAO;gBACL,oBAAoB,EAAE,UAAU;gBAChC,oBAAoB;aACrB,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AAhHc;IADZ,KAAK,EAAE;;;;6CA8CP","sourcesContent":["import type { ChatOpenAI } from \"@langchain/openai\";\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { z } from \"zod\";\n\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\n\nimport { createModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger(\"IntentClarifier\");\n\ntype ClarifierStructuredModel = ReturnType<ChatOpenAI[\"withStructuredOutput\"]>;\n\nconst clarificationSchema = z.object({\n needsClarification: z.boolean(),\n reason: z.string(),\n suggestedDescription: z.string().nullable(),\n clarificationMessage: z.string().nullable(),\n});\nconst suggestionSchema = z.object({\n suggestedDescription: z.string(),\n});\nconst clarificationDraftSchema = z.object({\n suggestedDescription: z.string(),\n clarificationMessage: z.string(),\n});\n\nexport type IntentClarifierOutput = z.infer<typeof clarificationSchema>;\n\nconst systemPrompt = `\nYou evaluate whether an intent is specific enough to persist without asking the user to confirm a refinement.\n\nOnly set needsClarification=true when the intent is truly vague — e.g. a single generic phrase with no role, domain, location, or other concrete criteria (like \"find a job\", \"I need help\", \"looking for something\").\n\nDo NOT ask for clarification when the user has already given:\n- A role or type (e.g. \"UX designer\", \"technical co-founder\", \"engineer\")\n- A domain or industry (e.g. \"in AI\", \"climate tech\", \"fintech\")\n- A location or format (e.g. \"remote\", \"Berlin\", \"full-time\")\n- Any other concrete detail that makes the intent actionable\n\nDefault to needsClarification=false when in doubt. Only clarify when the intent is so broad that persisting it as-is would be unhelpful (e.g. literally \"a job\" or \"something\" with no other signal).\n\nRules when needsClarification=true:\n- User Profile is the primary source for suggestedDescription; Active Intents are secondary.\n- You MUST provide a concrete suggestedDescription and short clarificationMessage.\n- Do not include JSON in clarificationMessage.\n`;\n\nconst suggestionPrompt = `\nYou generate one concrete, specific intent rewrite.\n\nRules:\n- Output only a concise intent sentence in suggestedDescription.\n- Use profile as primary source of personalization.\n- Use active intents as secondary context for consistency.\n- Keep user intent meaning, but make it actionable and specific.\n- Never return an empty suggestion.\n`;\n\nconst clarificationDraftPrompt = `\nYou draft a concise clarification response for a vague intent.\n\nRules:\n- Return both:\n 1) suggestedDescription (specific rewritten intent)\n 2) clarificationMessage (single short message to the user)\n- clarificationMessage must include the suggestion naturally and ask for confirmation.\n- Use this shape: ` + \"`Did you mean: \\\"<suggestedDescription>\\\"?`\" + ` followed by a brief confirmation instruction.\n- Keep it short. No bullet lists. No JSON.\n`;\n\nexport class IntentClarifier {\n private readonly model: ClarifierStructuredModel;\n private readonly suggestionModel: ClarifierStructuredModel;\n private readonly clarificationDraftModel: ClarifierStructuredModel;\n\n constructor() {\n const baseModel = createModel(\"intentClarifier\");\n this.model = baseModel.withStructuredOutput(clarificationSchema, {\n name: \"intent_clarifier\",\n });\n this.suggestionModel = baseModel.withStructuredOutput(suggestionSchema, {\n name: \"intent_clarifier_suggestion\",\n });\n this.clarificationDraftModel = baseModel.withStructuredOutput(\n clarificationDraftSchema,\n { name: \"intent_clarifier_message\" }\n );\n }\n\n @Timed()\n public async invoke(\n description: string,\n profileContext: string,\n activeIntentsContext: string\n ): Promise<IntentClarifierOutput> {\n try {\n const prompt = `\n# User Input Intent\n${description}\n\n# User Profile\n${profileContext || \"none\"}\n\n# Active Intents\n${activeIntentsContext || \"none\"}\n`;\n\n const result = await invokeWithAbortSignal(this.model, [\n new SystemMessage(systemPrompt),\n new HumanMessage(prompt),\n ]);\n const parsed = clarificationSchema.parse(result);\n\n if (parsed.needsClarification) {\n // Always prefer a dedicated rewrite pass for vague inputs so we avoid generic follow-up text.\n const draft = await this.generateClarificationDraft(description, profileContext, activeIntentsContext);\n if (draft) {\n return {\n ...parsed,\n suggestedDescription: draft.suggestedDescription,\n clarificationMessage: draft.clarificationMessage,\n };\n }\n }\n\n return parsed;\n } catch (error) {\n logger.warn(\"invoke: clarification failed\", { error });\n return {\n needsClarification: false,\n reason: \"fallback_on_model_error\",\n suggestedDescription: null,\n clarificationMessage: null,\n };\n }\n }\n\n private async generateSuggestion(\n description: string,\n profileContext: string,\n activeIntentsContext: string\n ): Promise<string | null> {\n try {\n const prompt = `\n# User Input Intent\n${description}\n\n# User Profile\n${profileContext || \"none\"}\n\n# Active Intents\n${activeIntentsContext || \"none\"}\n`;\n const output = await invokeWithAbortSignal(this.suggestionModel, [\n new SystemMessage(suggestionPrompt),\n new HumanMessage(prompt),\n ]);\n const parsed = suggestionSchema.parse(output);\n const suggestion = parsed.suggestedDescription.trim();\n return suggestion.length > 0 ? suggestion : null;\n } catch (error) {\n logger.warn(\"generateSuggestion: failed\", { error });\n return null;\n }\n }\n\n private async generateClarificationDraft(\n description: string,\n profileContext: string,\n activeIntentsContext: string\n ): Promise<{ suggestedDescription: string; clarificationMessage: string } | null> {\n try {\n const prompt = `\n# User Input Intent\n${description}\n\n# User Profile\n${profileContext || \"none\"}\n\n# Active Intents\n${activeIntentsContext || \"none\"}\n`;\n const output = await invokeWithAbortSignal(this.clarificationDraftModel, [\n new SystemMessage(clarificationDraftPrompt),\n new HumanMessage(prompt),\n ]);\n const parsed = clarificationDraftSchema.parse(output);\n const suggestedDescription = parsed.suggestedDescription.trim();\n const clarificationMessage = parsed.clarificationMessage.trim();\n if (!suggestedDescription || !clarificationMessage) return null;\n return { suggestedDescription, clarificationMessage };\n } catch (error) {\n logger.warn(\"generateClarificationDraft: failed\", { error });\n const suggestion = await this.generateSuggestion(description, profileContext, activeIntentsContext);\n if (!suggestion) return null;\n const clarificationMessage = `Do you mean: ${suggestion}?`;\n return {\n suggestedDescription: suggestion,\n clarificationMessage,\n };\n }\n }\n}\n"]}
1
+ {"version":3,"file":"intent.clarifier.js","sourceRoot":"/","sources":["intent/intent.clarifier.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChatOpenAI } from \"@langchain/openai\";\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { z } from \"zod\";\n\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\n\nimport { createModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger(\"IntentClarifier\");\n\ntype ClarifierStructuredModel = ReturnType<ChatOpenAI[\"withStructuredOutput\"]>;\n\nconst clarificationSchema = z.object({\n needsClarification: z.boolean(),\n reason: z.string(),\n suggestedDescription: z.string().nullable(),\n clarificationMessage: z.string().nullable(),\n});\nconst suggestionSchema = z.object({\n suggestedDescription: z.string(),\n});\nconst clarificationDraftSchema = z.object({\n suggestedDescription: z.string(),\n clarificationMessage: z.string(),\n});\n\nexport type IntentClarifierOutput = z.infer<typeof clarificationSchema>;\n\nconst systemPrompt = `\nYou evaluate whether an intent is specific enough to persist without asking the user to confirm a refinement.\n\nOnly set needsClarification=true when the intent is truly vague — e.g. a single generic phrase with no role, domain, location, or other concrete criteria (like \"find a job\", \"I need help\", \"looking for something\").\n\nDo NOT ask for clarification when the user has already given:\n- A role or type (e.g. \"UX designer\", \"technical co-founder\", \"engineer\")\n- A domain or industry (e.g. \"in AI\", \"climate tech\", \"fintech\")\n- A location or format (e.g. \"remote\", \"Berlin\", \"full-time\")\n- Any other concrete detail that makes the intent actionable\n\nDefault to needsClarification=false when in doubt. Only clarify when the intent is so broad that persisting it as-is would be unhelpful (e.g. literally \"a job\" or \"something\" with no other signal).\n\nRules when needsClarification=true:\n- User Profile is the primary source for suggestedDescription; Active Intents are secondary.\n- You MUST provide a concrete suggestedDescription and short clarificationMessage.\n- Do not include JSON in clarificationMessage.\n`;\n\nconst suggestionPrompt = `\nYou generate one concrete, specific intent rewrite.\n\nRules:\n- Output only a concise intent sentence in suggestedDescription.\n- Use profile as primary source of personalization.\n- Use active intents as secondary context for consistency.\n- Keep user intent meaning, but make it actionable and specific.\n- Never return an empty suggestion.\n`;\n\nconst clarificationDraftPrompt = `\nYou draft a concise clarification response for a vague intent.\n\nRules:\n- Return both:\n 1) suggestedDescription (specific rewritten intent)\n 2) clarificationMessage (single short message to the user)\n- clarificationMessage must include the suggestion naturally and ask for confirmation.\n- Use this shape: ` + \"`Did you mean: \\\"<suggestedDescription>\\\"?`\" + ` followed by a brief confirmation instruction.\n- Keep it short. No bullet lists. No JSON.\n`;\n\nexport class IntentClarifier {\n private readonly model: ClarifierStructuredModel;\n private readonly suggestionModel: ClarifierStructuredModel;\n private readonly clarificationDraftModel: ClarifierStructuredModel;\n\n constructor() {\n const baseModel = createModel(\"intentClarifier\");\n this.model = baseModel.withStructuredOutput(clarificationSchema, {\n name: \"intent_clarifier\",\n });\n this.suggestionModel = baseModel.withStructuredOutput(suggestionSchema, {\n name: \"intent_clarifier_suggestion\",\n });\n this.clarificationDraftModel = baseModel.withStructuredOutput(\n clarificationDraftSchema,\n { name: \"intent_clarifier_message\" }\n );\n }\n\n /** Build the shared user prompt with intent, profile, and active-intent context. */\n private buildPrompt(\n description: string,\n profileContext: string,\n activeIntentsContext: string\n ): string {\n return `\n# User Input Intent\n${description}\n\n# User Profile\n${profileContext || \"none\"}\n\n# Active Intents\n${activeIntentsContext || \"none\"}\n`;\n }\n\n @Timed()\n public async invoke(\n description: string,\n profileContext: string,\n activeIntentsContext: string\n ): Promise<IntentClarifierOutput> {\n try {\n const prompt = this.buildPrompt(description, profileContext, activeIntentsContext);\n\n const result = await invokeWithAbortSignal(this.model, [\n new SystemMessage(systemPrompt),\n new HumanMessage(prompt),\n ]);\n const parsed = clarificationSchema.parse(result);\n\n if (parsed.needsClarification) {\n // Always prefer a dedicated rewrite pass for vague inputs so we avoid generic follow-up text.\n const draft = await this.generateClarificationDraft(description, profileContext, activeIntentsContext);\n if (draft) {\n return {\n ...parsed,\n suggestedDescription: draft.suggestedDescription,\n clarificationMessage: draft.clarificationMessage,\n };\n }\n }\n\n return parsed;\n } catch (error) {\n logger.warn(\"invoke: clarification failed\", { error });\n return {\n needsClarification: false,\n reason: \"fallback_on_model_error\",\n suggestedDescription: null,\n clarificationMessage: null,\n };\n }\n }\n\n private async generateSuggestion(\n description: string,\n profileContext: string,\n activeIntentsContext: string\n ): Promise<string | null> {\n try {\n const prompt = this.buildPrompt(description, profileContext, activeIntentsContext);\n const output = await invokeWithAbortSignal(this.suggestionModel, [\n new SystemMessage(suggestionPrompt),\n new HumanMessage(prompt),\n ]);\n const parsed = suggestionSchema.parse(output);\n const suggestion = parsed.suggestedDescription.trim();\n return suggestion.length > 0 ? suggestion : null;\n } catch (error) {\n logger.warn(\"generateSuggestion: failed\", { error });\n return null;\n }\n }\n\n private async generateClarificationDraft(\n description: string,\n profileContext: string,\n activeIntentsContext: string\n ): Promise<{ suggestedDescription: string; clarificationMessage: string } | null> {\n try {\n const prompt = this.buildPrompt(description, profileContext, activeIntentsContext);\n const output = await invokeWithAbortSignal(this.clarificationDraftModel, [\n new SystemMessage(clarificationDraftPrompt),\n new HumanMessage(prompt),\n ]);\n const parsed = clarificationDraftSchema.parse(output);\n const suggestedDescription = parsed.suggestedDescription.trim();\n const clarificationMessage = parsed.clarificationMessage.trim();\n if (!suggestedDescription || !clarificationMessage) return null;\n return { suggestedDescription, clarificationMessage };\n } catch (error) {\n logger.warn(\"generateClarificationDraft: failed\", { error });\n const suggestion = await this.generateSuggestion(description, profileContext, activeIntentsContext);\n if (!suggestion) return null;\n const clarificationMessage = `Do you mean: ${suggestion}?`;\n return {\n suggestedDescription: suggestion,\n clarificationMessage,\n };\n }\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"intent.graph.d.ts","sourceRoot":"/","sources":["intent/intent.graph.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAKtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AAEjF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAC;AACrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAIhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AA0G7E;;GAEG;AACH,qBAAa,kBAAkB;IAE3B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,QAAQ,CAAC;IACjB,OAAO,CAAC,WAAW,CAAC;IACpB,OAAO,CAAC,iBAAiB,CAAC;gBAHlB,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,CAAC,EAAE,kBAAkB,YAAA,EAC7B,WAAW,CAAC,EAAE,gBAAgB,YAAA,EAC9B,iBAAiB,CAAC,EAAE,mBAAmB,YAAA;IAG1C,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAiDZ,CAAR;wBAEI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAFI,CAAR;4BAEI,CAAA;;;;;;;;;;;0BAFI,CAAR;4BAEI,CAAA;;;;;;;;;;;0BAFI,CAAR;4BAEI,CAAA;;;;;;;;;;CAizBH"}
1
+ {"version":3,"file":"intent.graph.d.ts","sourceRoot":"/","sources":["intent/intent.graph.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAKtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AAEjF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAC;AACrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAIhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AA0G7E;;GAEG;AACH,qBAAa,kBAAkB;IAE3B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,QAAQ,CAAC;IACjB,OAAO,CAAC,WAAW,CAAC;IACpB,OAAO,CAAC,iBAAiB,CAAC;gBAHlB,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,CAAC,EAAE,kBAAkB,YAAA,EAC7B,WAAW,CAAC,EAAE,gBAAgB,YAAA,EAC9B,iBAAiB,CAAC,EAAE,mBAAmB,YAAA;IAG1C,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAiDZ,CAAR;wBAEI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;sBAFI,CAAR;wBAEI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAFI,CAAR;4BAEI,CAAA;;;;;;;;;;;0BAFI,CAAR;4BAEI,CAAA;;;;;;;;;;;0BAFI,CAAR;4BAEI,CAAA;;;;;;;;;;CAwzBH"}
@@ -441,6 +441,33 @@ export class IntentGraphFactory {
441
441
  .trim();
442
442
  return out.replace(/[.,;]\s*$/, "").trim() || payload;
443
443
  };
444
+ /**
445
+ * Generate a flat embedding for an intent payload, swallowing failures so
446
+ * persistence can continue without an embedding. `intentId` is logging-only
447
+ * (present for updates, absent for creates).
448
+ */
449
+ const generateIntentEmbedding = async (sanitizedPayload, intentId) => {
450
+ if (!this.embedder)
451
+ return undefined;
452
+ try {
453
+ const embedding = await this.embedder.generate(sanitizedPayload, undefined, getAbortSignalConfig());
454
+ const flatEmbedding = Array.isArray(embedding?.[0])
455
+ ? embedding[0]
456
+ : embedding;
457
+ logger.verbose("Generated embedding for intent", {
458
+ ...(intentId ? { intentId } : {}),
459
+ dimensions: flatEmbedding?.length,
460
+ });
461
+ return flatEmbedding;
462
+ }
463
+ catch (embErr) {
464
+ logger.error("Failed to generate embedding for intent (continuing without)", {
465
+ ...(intentId ? { intentId } : {}),
466
+ error: embErr,
467
+ });
468
+ return undefined;
469
+ }
470
+ };
444
471
  /**
445
472
  * Node 4: Executor
446
473
  * Executes reconciler actions against the database.
@@ -467,19 +494,7 @@ export class IntentGraphFactory {
467
494
  const matchedVerifiedIntent = verifiedIntentByPayload.get(createAction.payload) ||
468
495
  verifiedIntentByPayload.get(sanitizedPayload);
469
496
  // Generate embedding for the intent payload
470
- let flatEmbedding;
471
- if (this.embedder) {
472
- try {
473
- const embedding = await this.embedder.generate(sanitizedPayload, undefined, getAbortSignalConfig());
474
- flatEmbedding = Array.isArray(embedding?.[0])
475
- ? embedding[0]
476
- : embedding;
477
- logger.verbose("Generated embedding for new intent", { dimensions: flatEmbedding?.length });
478
- }
479
- catch (embErr) {
480
- logger.error("Failed to generate embedding for intent (continuing without)", { error: embErr });
481
- }
482
- }
497
+ const flatEmbedding = await generateIntentEmbedding(sanitizedPayload);
483
498
  const created = await this.database.createIntent({
484
499
  userId: state.userId,
485
500
  payload: sanitizedPayload,
@@ -532,19 +547,7 @@ export class IntentGraphFactory {
532
547
  const matchedVerifiedIntent = verifiedIntentByPayload.get(updateAction.payload) ||
533
548
  verifiedIntentByPayload.get(sanitizedPayload);
534
549
  // Regenerate embedding for the updated payload
535
- let flatEmbedding;
536
- if (this.embedder) {
537
- try {
538
- const embedding = await this.embedder.generate(sanitizedPayload, undefined, getAbortSignalConfig());
539
- flatEmbedding = Array.isArray(embedding?.[0])
540
- ? embedding[0]
541
- : embedding;
542
- logger.verbose("Generated embedding for updated intent", { intentId: updateAction.id, dimensions: flatEmbedding?.length });
543
- }
544
- catch (embErr) {
545
- logger.error("Failed to generate embedding for intent update (continuing without)", { error: embErr });
546
- }
547
- }
550
+ const flatEmbedding = await generateIntentEmbedding(sanitizedPayload, updateAction.id);
548
551
  const updated = await this.database.updateIntent(updateAction.id, {
549
552
  payload: sanitizedPayload,
550
553
  embedding: flatEmbedding,