@indexnetwork/protocol 4.3.3-rc.308.1 → 4.3.4-rc.310.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/IMPLEMENTATION.md +2 -2
- package/dist/chat/chat.agent.d.ts +1 -1
- package/dist/chat/chat.agent.d.ts.map +1 -1
- package/dist/chat/chat.agent.js +12 -2
- package/dist/chat/chat.agent.js.map +1 -1
- package/dist/chat/chat.graph.d.ts +16 -0
- package/dist/chat/chat.graph.d.ts.map +1 -1
- package/dist/chat/chat.graph.js +5 -2
- package/dist/chat/chat.graph.js.map +1 -1
- package/dist/chat/chat.prompt.d.ts.map +1 -1
- package/dist/chat/chat.prompt.js +44 -37
- package/dist/chat/chat.prompt.js.map +1 -1
- package/dist/chat/chat.prompt.modules.js +8 -8
- package/dist/chat/chat.prompt.modules.js.map +1 -1
- package/dist/chat/chat.state.d.ts +7 -4
- package/dist/chat/chat.state.d.ts.map +1 -1
- package/dist/chat/chat.state.js +12 -3
- package/dist/chat/chat.state.js.map +1 -1
- package/dist/chat/chat.streamer.d.ts +5 -0
- package/dist/chat/chat.streamer.d.ts.map +1 -1
- package/dist/chat/chat.streamer.js +13 -4
- package/dist/chat/chat.streamer.js.map +1 -1
- package/dist/chat/tests/chat.graph.mocks.d.ts +1 -1
- package/dist/chat/tests/chat.graph.mocks.js +1 -1
- package/dist/chat/tests/chat.graph.mocks.js.map +1 -1
- package/dist/contact/contact.tools.js +6 -6
- package/dist/contact/contact.tools.js.map +1 -1
- package/dist/enrichment/enrichment.tools.d.ts.map +1 -1
- package/dist/enrichment/enrichment.tools.js +23 -26
- package/dist/enrichment/enrichment.tools.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- 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 +9 -9
- package/dist/intent/intent.graph.js.map +1 -1
- package/dist/intent/intent.indexer.d.ts +1 -1
- package/dist/intent/intent.indexer.js +4 -4
- package/dist/intent/intent.indexer.js.map +1 -1
- package/dist/intent/intent.state.d.ts +8 -8
- package/dist/intent/intent.state.js +8 -8
- package/dist/intent/intent.state.js.map +1 -1
- package/dist/intent/intent.tools.d.ts.map +1 -1
- package/dist/intent/intent.tools.js +87 -70
- package/dist/intent/intent.tools.js.map +1 -1
- package/dist/maintenance/maintenance.graph.d.ts +2 -2
- package/dist/maintenance/maintenance.graph.d.ts.map +1 -1
- package/dist/maintenance/maintenance.graph.js.map +1 -1
- package/dist/mcp/mcp.server.d.ts +5 -7
- package/dist/mcp/mcp.server.d.ts.map +1 -1
- package/dist/mcp/mcp.server.js +11 -20
- package/dist/mcp/mcp.server.js.map +1 -1
- package/dist/negotiation/negotiation.tools.d.ts.map +1 -1
- package/dist/negotiation/negotiation.tools.js +11 -7
- package/dist/negotiation/negotiation.tools.js.map +1 -1
- package/dist/network/indexer/indexer.graph.d.ts +2 -2
- package/dist/network/indexer/indexer.graph.d.ts.map +1 -1
- package/dist/network/indexer/indexer.graph.js +13 -13
- package/dist/network/indexer/indexer.graph.js.map +1 -1
- package/dist/network/indexer/indexer.state.d.ts +3 -3
- package/dist/network/indexer/indexer.state.d.ts.map +1 -1
- package/dist/network/indexer/indexer.state.js +4 -4
- package/dist/network/indexer/indexer.state.js.map +1 -1
- package/dist/network/membership/membership.graph.d.ts +1 -1
- package/dist/network/membership/membership.graph.js +21 -21
- package/dist/network/membership/membership.graph.js.map +1 -1
- package/dist/network/membership/membership.state.d.ts +2 -2
- package/dist/network/membership/membership.state.js +2 -2
- package/dist/network/membership/membership.state.js.map +1 -1
- package/dist/network/network.graph.d.ts +1 -1
- package/dist/network/network.graph.d.ts.map +1 -1
- package/dist/network/network.graph.js +13 -13
- package/dist/network/network.graph.js.map +1 -1
- package/dist/network/network.state.d.ts +3 -3
- package/dist/network/network.state.d.ts.map +1 -1
- package/dist/network/network.state.js +3 -3
- package/dist/network/network.state.js.map +1 -1
- package/dist/network/network.tools.js +68 -68
- package/dist/network/network.tools.js.map +1 -1
- package/dist/opportunity/opportunity.discover.d.ts +1 -1
- package/dist/opportunity/opportunity.discover.d.ts.map +1 -1
- package/dist/opportunity/opportunity.discover.js +1 -1
- package/dist/opportunity/opportunity.discover.js.map +1 -1
- package/dist/opportunity/opportunity.graph.js +11 -11
- package/dist/opportunity/opportunity.graph.js.map +1 -1
- package/dist/opportunity/opportunity.introducer.d.ts +3 -3
- package/dist/opportunity/opportunity.introducer.d.ts.map +1 -1
- package/dist/opportunity/opportunity.introducer.js +2 -2
- package/dist/opportunity/opportunity.introducer.js.map +1 -1
- package/dist/opportunity/opportunity.state.d.ts +1 -1
- package/dist/opportunity/opportunity.state.js +1 -1
- package/dist/opportunity/opportunity.state.js.map +1 -1
- package/dist/opportunity/opportunity.tools.d.ts.map +1 -1
- package/dist/opportunity/opportunity.tools.js +43 -47
- package/dist/opportunity/opportunity.tools.js.map +1 -1
- package/dist/premise/premise.indexer.d.ts +1 -1
- package/dist/premise/premise.indexer.js +5 -5
- package/dist/premise/premise.indexer.js.map +1 -1
- package/dist/questioner/questioner.tools.d.ts +1 -1
- package/dist/questioner/questioner.tools.d.ts.map +1 -1
- package/dist/questioner/questioner.tools.js +5 -6
- package/dist/questioner/questioner.tools.js.map +1 -1
- package/dist/shared/agent/tool.factory.d.ts +1 -1
- package/dist/shared/agent/tool.factory.d.ts.map +1 -1
- package/dist/shared/agent/tool.factory.js +10 -11
- package/dist/shared/agent/tool.factory.js.map +1 -1
- package/dist/shared/agent/tool.helpers.d.ts +6 -6
- package/dist/shared/agent/tool.helpers.d.ts.map +1 -1
- package/dist/shared/agent/tool.helpers.js +2 -2
- package/dist/shared/agent/tool.helpers.js.map +1 -1
- package/dist/shared/agent/tool.registry.js +1 -1
- package/dist/shared/agent/tool.registry.js.map +1 -1
- package/dist/shared/agent/tool.scope.d.ts +12 -0
- package/dist/shared/agent/tool.scope.d.ts.map +1 -1
- package/dist/shared/agent/tool.scope.js +14 -0
- package/dist/shared/agent/tool.scope.js.map +1 -1
- package/dist/shared/agent/utility.tools.js +20 -20
- package/dist/shared/agent/utility.tools.js.map +1 -1
- package/dist/shared/interfaces/auth.interface.d.ts +3 -3
- package/dist/shared/interfaces/auth.interface.js.map +1 -1
- package/dist/shared/interfaces/database.interface.d.ts +59 -59
- package/dist/shared/interfaces/database.interface.d.ts.map +1 -1
- package/dist/shared/interfaces/database.interface.js.map +1 -1
- package/dist/shared/interfaces/discovery-run.interface.d.ts +1 -1
- package/dist/shared/interfaces/discovery-run.interface.d.ts.map +1 -1
- package/dist/shared/interfaces/discovery-run.interface.js.map +1 -1
- package/dist/shared/interfaces/embedder.interface.d.ts +2 -2
- package/dist/shared/interfaces/embedder.interface.d.ts.map +1 -1
- package/dist/shared/interfaces/embedder.interface.js.map +1 -1
- package/dist/shared/interfaces/enrichment-run.interface.d.ts +1 -1
- package/dist/shared/interfaces/enrichment-run.interface.d.ts.map +1 -1
- package/dist/shared/interfaces/enrichment-run.interface.js.map +1 -1
- package/package.json +1 -1
|
@@ -63,7 +63,7 @@ For open-ended connection-seeking ("find me a mentor", "who needs a React dev",
|
|
|
63
63
|
|
|
64
64
|
**CRITICAL: DO NOT create an intent first. Discovery comes FIRST.**
|
|
65
65
|
|
|
66
|
-
**Network scoping**: When the user says "in my network", "from my contacts", "people I know", "among my connections", or similar network-scoping language, pass the user's **personal
|
|
66
|
+
**Network scoping**: When the user says "in my network", "from my contacts", "people I know", "among my connections", or similar network-scoping language, pass the user's **personal network ID** as \`networkId\`. The personal network (\`isPersonal: true\` in preloaded memberships) contains the user's contacts — scoping discovery to it restricts results to people the user already knows. If no network-scoping language is used, do not pass a personal network ID — let discovery run across all networks as usual.
|
|
67
67
|
|
|
68
68
|
- Call \`discover_opportunities(searchQuery=user's request)\` IMMEDIATELY (with networkId when scoped).
|
|
69
69
|
- Do NOT call \`create_intent\` unless the user **explicitly** asks to "create", "save", "add", or "remember" an intent/signal.
|
|
@@ -81,8 +81,8 @@ When the user mentions a specific person via @mention or name AND expresses inte
|
|
|
81
81
|
|
|
82
82
|
\`\`\`
|
|
83
83
|
1. If not already done: read_user_contexts(userId=X) + read_network_memberships(userId=X)
|
|
84
|
-
2. Find shared
|
|
85
|
-
3. If no shared
|
|
84
|
+
2. Find shared networks with the user (intersect with preloaded memberships)
|
|
85
|
+
3. If no shared networks: tell the user you can't find a connection path
|
|
86
86
|
4. discover_opportunities(targetUserId=X, searchQuery="<synthesized reason for connecting based on shared context>")
|
|
87
87
|
5. Present the opportunity card
|
|
88
88
|
\`\`\`
|
|
@@ -129,15 +129,15 @@ const introductionModule = {
|
|
|
129
129
|
|
|
130
130
|
\`\`\`
|
|
131
131
|
1. read_network_memberships(userId=A) + read_network_memberships(userId=B) → find shared networks
|
|
132
|
-
2. If no shared
|
|
132
|
+
2. If no shared networks: tell user they're not in any shared community
|
|
133
133
|
3. read_user_contexts(userId=A) + read_user_contexts(userId=B)
|
|
134
|
-
4. For each shared
|
|
134
|
+
4. For each shared network: read_intents(networkId=X, userId=A) + read_intents(networkId=X, userId=B)
|
|
135
135
|
5. Summarize to user: "Here's what I found about A and B..."
|
|
136
136
|
6. discover_opportunities(partyUserIds=[A,B], entities=[{userId:A, profile:{...}, intents:[...], networkId:shared}, {userId:B, ...}], hint="user's reason")
|
|
137
137
|
7. Present the draft introduction
|
|
138
138
|
\`\`\`
|
|
139
139
|
|
|
140
|
-
The entities array must include each party's userId, profile data, intents from shared
|
|
140
|
+
The entities array must include each party's userId, profile data, intents from shared networks, and the shared networkId. The hint is the user's stated reason (e.g. "both AI devs"). If the user asks to introduce only one person or to "introduce" themselves to someone, explain that introductions connect two other people and suggest they name two people to connect.
|
|
141
141
|
|
|
142
142
|
### 6a. Discover who to introduce to someone
|
|
143
143
|
|
|
@@ -246,7 +246,7 @@ const communityModule = {
|
|
|
246
246
|
\`\`\`
|
|
247
247
|
|
|
248
248
|
### When to mention community/index
|
|
249
|
-
Index and community membership is background: handle it without talking about indexes unless the user asks or it's sign-up, leave, or owner settings. Do not proactively mention "your indexes", "your communities", "which index", "in your current communities", or similar. Only mention indexes (or communities, lists) when: (i) post-onboarding sign-up to a community, (ii) user explicitly asked about their indexes/communities, (iii) user wants to leave one, (iv) owner is changing index/community settings. Otherwise use neutral language ("where you're connected", "people you're connected with") and do not narrate "your indexes", "your current communities", "in this
|
|
249
|
+
Index and community membership is background: handle it without talking about indexes unless the user asks or it's sign-up, leave, or owner settings. Do not proactively mention "your indexes", "your communities", "which index", "in your current communities", or similar. Only mention indexes (or communities, lists) when: (i) post-onboarding sign-up to a community, (ii) user explicitly asked about their indexes/communities, (iii) user wants to leave one, (iv) owner is changing index/community settings. Otherwise use neutral language ("where you're connected", "people you're connected with") and do not narrate "your indexes", "your current communities", "in this network", etc.
|
|
250
250
|
`,
|
|
251
251
|
};
|
|
252
252
|
const contactsModule = {
|
|
@@ -294,7 +294,7 @@ const sharedContextModule = {
|
|
|
294
294
|
1. read_network_memberships(userId=me) → my networks
|
|
295
295
|
2. read_network_memberships(userId=other) → their networks
|
|
296
296
|
3. Intersect networkIds
|
|
297
|
-
4. For each shared
|
|
297
|
+
4. For each shared network: read_intents(networkId=shared)
|
|
298
298
|
5. read_user_contexts(userId=other)
|
|
299
299
|
6. Synthesize: what overlaps, where they could collaborate
|
|
300
300
|
\`\`\`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.prompt.modules.js","sourceRoot":"/","sources":["chat/chat.prompt.modules.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BaseMessage, AIMessage } from \"@langchain/core/messages\";\n\nimport type { ResolvedToolContext } from \"../shared/agent/tool.factory.js\";\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// TYPES\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * A conditional prompt section injected into the system prompt based on triggers.\n */\nexport interface PromptModule {\n /** Unique module identifier. */\n id: string;\n /** Tool names that activate this module. */\n triggers: string[];\n /** Module IDs to suppress when this module activates (unidirectional). */\n excludes?: string[];\n /** Optional filter applied after tool trigger match. Return false to skip despite trigger match. */\n triggerFilter?: (iterCtx: IterationContext) => boolean;\n /** User message pattern that activates this module (secondary trigger). */\n regex?: RegExp;\n /** Returns the prompt text to inject. */\n content: (ctx: ResolvedToolContext) => string;\n}\n\n/**\n * State available to module resolution at each iteration.\n */\nexport interface IterationContext {\n /** Tool calls from all iterations since the last user message. */\n recentTools: Array<{ name: string; args: Record<string, unknown> }>;\n /** Text of the latest user message (for regex matching). */\n currentMessage?: string;\n /** Resolved tool context (user, profile, indexes, etc.). */\n ctx: ResolvedToolContext;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// EXTRACTION\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Extracts tool calls from all AI messages since the last HumanMessage.\n *\n * Scans backwards to find the last HumanMessage, then collects all tool calls\n * from AIMessages after that point. This ensures multi-iteration tool history\n * is available for module resolution within a single user turn.\n *\n * @param messages - The current conversation message array\n * @returns Flattened array of tool name + args from the current agent turn\n */\nexport function extractRecentToolCalls(\n messages: BaseMessage[],\n): Array<{ name: string; args: Record<string, unknown> }> {\n // Find the index of the last HumanMessage\n let lastHumanIdx = -1;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]._getType() === \"human\") {\n lastHumanIdx = i;\n break;\n }\n }\n\n // Collect tool calls from all AIMessages after the last HumanMessage\n const toolCalls: Array<{ name: string; args: Record<string, unknown> }> = [];\n const startIdx = lastHumanIdx + 1;\n\n for (let i = startIdx; i < messages.length; i++) {\n const msg = messages[i];\n if (msg._getType() === \"ai\") {\n const aiMsg = msg as AIMessage;\n const calls = aiMsg.tool_calls ?? [];\n for (const tc of calls) {\n toolCalls.push({\n name: tc.name,\n args: (tc.args ?? {}) as Record<string, unknown>,\n });\n }\n }\n }\n\n return toolCalls;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// HELPERS\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Checks whether recent tool calls include discover_opportunities with\n * introduction-specific arguments (partyUserIds or introTargetUserId).\n */\nfunction hasIntroductionArgs(recentTools: IterationContext[\"recentTools\"]): boolean {\n return recentTools.some(\n (t) =>\n t.name === \"discover_opportunities\" &&\n (t.args.partyUserIds || t.args.introTargetUserId),\n );\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// MODULE DEFINITIONS\n// ═══════════════════════════════════════════════════════════════════════════════\n\nconst discoveryModule: PromptModule = {\n id: \"discovery\",\n triggers: [\"discover_opportunities\", \"update_opportunity\", \"list_opportunities\"],\n triggerFilter: (iterCtx) => !hasIntroductionArgs(iterCtx.recentTools),\n content: () => `\n### 1. User wants to find connections or discover (default for connection-seeking)\n\nFor open-ended connection-seeking (\"find me a mentor\", \"who needs a React dev\", \"I want to meet people in AI\", \"looking for investors\", \"find me X\"), run **discovery first**.\n\n**CRITICAL: DO NOT create an intent first. Discovery comes FIRST.**\n\n**Network scoping**: When the user says \"in my network\", \"from my contacts\", \"people I know\", \"among my connections\", or similar network-scoping language, pass the user's **personal index ID** as \\`networkId\\`. The personal index (\\`isPersonal: true\\` in preloaded memberships) contains the user's contacts — scoping discovery to it restricts results to people the user already knows. If no network-scoping language is used, do not pass a personal index ID — let discovery run across all indexes as usual.\n\n- Call \\`discover_opportunities(searchQuery=user's request)\\` IMMEDIATELY (with networkId when scoped).\n- Do NOT call \\`create_intent\\` unless the user **explicitly** asks to \"create\", \"save\", \"add\", or \"remember\" an intent/signal.\n- Phrases like \"looking for X\", \"find me X\", \"I want to meet X\", \"I need X\" are discovery requests — NOT intent creation requests.\n- If the tool returns \\`createIntentSuggested\\` and \\`suggestedIntentDescription\\`, the system will create an intent and retry discovery automatically; use the final result (candidates or \"no matches\") for your reply.\n- If the tool returns \\`suggestIntentCreationForVisibility: true\\` and \\`suggestedIntentDescription\\`, after presenting the opportunity cards ask the user whether they'd also like to create a signal so others can find them (e.g. *\"Would you also like to create a signal for this so others can find you?\"*). If the user agrees, call \\`create_intent(description=suggestedIntentDescription)\\` and include the returned \\`\\`\\`intent_proposal block verbatim — this is the same proposal flow as explicit intent creation; the user approves or skips via the card. Ask only once per conversation; do not repeat the question on follow-up turns.\n- When the tool indicates all results are exhausted (no remaining candidates), do NOT offer to \"show more\". Instead suggest the user create a signal so others can find them. This uses the same \\`create_intent\\` flow as above.\n- If the user **explicitly** says they want to create/save an intent (e.g. \"add a signal\", \"create an intent\", \"save that I'm looking for X\", \"remember this\"), use pattern 2 instead.\n\n### 1a. User wants to connect with a specific mentioned person\n\nWhen the user mentions a specific person via @mention or name AND expresses interest in connecting, collaborating, or exploring overlap (e.g. \"what can I do with @X\", \"connect me with @X\", user says \"yes\" after you present shared context with someone):\n\n**This is a direct connection — NOT an introduction (introductions connect two OTHER people).**\n\n\\`\\`\\`\n1. If not already done: read_user_contexts(userId=X) + read_network_memberships(userId=X)\n2. Find shared indexes with the user (intersect with preloaded memberships)\n3. If no shared indexes: tell the user you can't find a connection path\n4. discover_opportunities(targetUserId=X, searchQuery=\"<synthesized reason for connecting based on shared context>\")\n5. Present the opportunity card\n\\`\\`\\`\n\n**Do NOT call read_intents before discover_opportunities here.** The opportunity tool fetches intents internally for both discovery and direct connection modes. Only introduction mode (partyUserIds + entities) requires pre-gathered intents.\n\nThe searchQuery should be a brief description of why they'd connect (e.g. \"shared interest in design and technology, both in Kernel community\"). This gives the evaluator context for scoring.\n\n### 7. Opportunities in chat\n\n- **discover_opportunities** — discovers new connections (discovery, introduction, or direct connection).\n- **list_opportunities** — lists existing draft and pending opportunities the user can act on.\n\nWhen the user asks to review, revise, check, or see their current opportunities, call \\`list_opportunities\\`. Only use \\`discover_opportunities\\` for discovery (\"find me connections\"), introductions, or direct connections.\n\nWhen either tool returns \\`\\`\\`opportunity code blocks, include them verbatim in your reply so they render as cards.\n\nWhen \\`discover_opportunities\\` returns a \\`questions\\` array, do **not** rephrase or summarize them in your prose. The frontend renders them as an interactive decision card surface. You may write a single short line referencing that there are decision prompts below; otherwise, leave them alone.\n\nDraft or latent opportunities can be sent (update_opportunity with status='pending'). Status translation: draft/latent → \"draft\", pending → \"sent\", accepted → \"connected\"\n\n**CRITICAL: Only describe what the tool response confirms happened.** \"pending\" sends a notification — not a message or invite. \"accepted\" adds a contact — for ghost users, the invite email is sent only when the user opens a chat and messages them. Never claim you sent invites, connection requests, or messages on behalf of the user.\n\n### Discovery-first; intent as follow-up\n- For connection-seeking (find connections, discover, who's looking for X), use \\`discover_opportunities(searchQuery=...)\\` first. Do not lead with \\`create_intent\\` unless the user explicitly asks to create or save an intent.\n- When the tool returns \\`createIntentSuggested\\`, the system may create an intent and retry; respond from the final discovery result.\n- Visibility-signal follow-up: apply the Pattern 1 rule above (\\`suggestIntentCreationForVisibility\\` → ask once; on yes, call \\`create_intent(description=suggestedIntentDescription)\\` and include the returned \\`\\`\\`intent_proposal block).\n- When the tool response says \"These are all the connections I found\", suggest the user create a signal so others can discover them. Use the existing \\`suggestIntentCreationForVisibility\\` flow: call \\`create_intent(description=suggestedIntentDescription)\\` if the user agrees. Do not ask \"Would you like to see more?\" when there are no more candidates.\n- **Introducer exception**: Never suggest signal/intent creation when \\`introTargetUserId\\` was used. The search describes the other person's needs, not the signed-in user's — creating a signal from it would be meaningless.\n- Only call \\`discover_opportunities\\` for: (a) discovery (\"find me connections\"), (b) introductions between two other people, or (c) direct connection with a specific mentioned person (Pattern 1a).\n`,\n};\n\nconst introductionModule: PromptModule = {\n id: \"introduction\",\n triggers: [\"discover_opportunities\"],\n excludes: [\"discovery\"],\n triggerFilter: (iterCtx) => hasIntroductionArgs(iterCtx.recentTools),\n content: () => `\n### 6. Introduce two people\n\n**An introduction is always between exactly two people.** Do not call discover_opportunities for an introduction unless you have exactly two parties (two distinct people to introduce to each other). The entities array must have exactly two entities. The introducer (current user) must not be included in the entities array; entities must refer to two distinct other users.\n\n**You MUST gather all context before calling discover_opportunities. The tool does NOT fetch data internally.**\n\n\\`\\`\\`\n1. read_network_memberships(userId=A) + read_network_memberships(userId=B) → find shared networks\n2. If no shared indexes: tell user they're not in any shared community\n3. read_user_contexts(userId=A) + read_user_contexts(userId=B)\n4. For each shared index: read_intents(networkId=X, userId=A) + read_intents(networkId=X, userId=B)\n5. Summarize to user: \"Here's what I found about A and B...\"\n6. discover_opportunities(partyUserIds=[A,B], entities=[{userId:A, profile:{...}, intents:[...], networkId:shared}, {userId:B, ...}], hint=\"user's reason\")\n7. Present the draft introduction\n\\`\\`\\`\n\nThe entities array must include each party's userId, profile data, intents from shared indexes, and the shared networkId. The hint is the user's stated reason (e.g. \"both AI devs\"). If the user asks to introduce only one person or to \"introduce\" themselves to someone, explain that introductions connect two other people and suggest they name two people to connect.\n\n### 6a. Discover who to introduce to someone\n\n**When the user asks \"who should I introduce to @Person\" or \"find connections for @Person\"** — they want YOU to discover good connections for that person, presented as introduction cards.\n\n\\`\\`\\`\n1. Identify the person's userId from the @mention (call it mentionedUserId)\n2. discover_opportunities(introTargetUserId=mentionedUserId, searchQuery=\"<optional refinement>\")\n3. Present the returned cards (they will be formatted as introduction cards automatically)\n\\`\\`\\`\n\nThis is different from Pattern 6 (where user names BOTH parties). Here the user names ONE person and asks you to find connections for them. Do NOT use Pattern 6 for this — Pattern 6 requires both parties to be known upfront. Do NOT ask the user for a second person. Do NOT use targetUserId or partyUserIds. The system will find connections automatically.\n\n**CRITICAL — no signal creation in introducer flows:** When \\`introTargetUserId\\` is used (Patterns 6 and 6a), the user is searching for connections on behalf of someone else — the search reflects the other person's needs, not the user's own. Do NOT suggest creating a signal or intent in this context. The search query describes what the *other person* needs (e.g. \"biotech investors for Levi\"), so creating a signal from it for the signed-in user would be wrong. Never offer signal/intent creation CTAs after introducer discovery — not for the other person (users can only create signals for themselves) and not for the signed-in user (the query doesn't represent their intent).\n`,\n};\n\nconst intentCreationModule: PromptModule = {\n id: \"intent-creation\",\n triggers: [\"create_intent\"],\n content: () => `\n### 2. User explicitly wants to create or save an intent\n\n**YOU decide if it's specific enough. The tool proposes — the user confirms.**\n\n\\`\\`\\`\nIF description is vague (\"find a job\", \"meet people\", \"learn something\"):\n 1. read_user_contexts() → get their background\n 2. read_intents() → see existing intents for context\n 3. THINK: given their profile and existing intents, suggest a refined version\n 4. Reply: \"Based on your background in X, did you mean something like 'Y'?\"\n 5. Wait for confirmation\n 6. On \"yes\" → create_intent(description=exact_refined_text)\n\nIF description is specific enough (\"contribute to an open-source LLM project\"):\n → create_intent(description=...) directly\n\\`\\`\\`\n\n**CRITICAL: Never write a \\`\\`\\`intent_proposal block yourself.** To propose an intent you MUST call create_intent(description=...). The tool returns a \\`\\`\\`intent_proposal code block (with proposalId and description). You MUST include that exact block verbatim in your response — it renders as an interactive card. Do not summarize or invent the block; only the tool provides a valid one. Add a brief explanation that creating this intent will let the system look for relevant people in the background.\n\nSpecificity test: Does it contain a concrete domain, action, or scope? If just a single generic verb+noun (\"find a job\"), it's vague. If it has qualifying detail (\"senior UX design role at a tech company in Berlin\"), it's specific.\n`,\n};\n\nconst intentManagementModule: PromptModule = {\n id: \"intent-management\",\n triggers: [\"update_intent\", \"delete_intent\"],\n content: () => `\n### 4. Update or delete an intent\n\n**YOU look up the ID first.**\n\n\\`\\`\\`\n1. read_intents() → get current intents with IDs\n2. Match user's request to the right intent\n3. update_intent(intentId=exact_id, description=...) or delete_intent(intentId=exact_id)\n\\`\\`\\`\n`,\n};\n\nconst personLookupModule: PromptModule = {\n id: \"person-lookup\",\n triggers: [\"read_user_contexts\"],\n content: () => `\n### 0. User asks about a specific person by name\n\nWhen the user mentions a specific person by name (\"find [name]\", \"look up [name]\", \"who is [name]?\", \"tell me about [name]\"), look them up by name first — do NOT use discovery.\n\n- Call \\`read_user_contexts(query=\"the name\")\\` — this finds members by name across the user's indexes\n- If one match: the result already includes their full profile; present it naturally\n- If multiple matches: present the list and ask the user to clarify which person\n- If no matches: tell the user you couldn't find anyone by that name in their network\n- If the user then asks for semantic discovery (e.g. \"find people like them\"), use Pattern 1.\n- If the user wants to connect with this specific person (e.g. \"yes, connect us\", \"what can I do with them\", \"I'd like to reach out\"), use Pattern 1a.\n`,\n};\n\nconst urlScrapingModule: PromptModule = {\n id: \"url-scraping\",\n triggers: [\"scrape_url\"],\n regex: /(https?:\\/\\/)/i,\n content: () => `\n### 3. User includes a URL\n\n**YOU handle scraping before intent creation.**\n\n\\`\\`\\`\n1. scrape_url(url, objective=\"Extract key details for an intent\")\n2. Synthesize a conceptual description from scraped content\n3. create_intent(description=synthesized_summary)\n\\`\\`\\`\n\nException: for profile creation, pass URLs directly to create_user_context (it handles scraping internally).\n\nIf the user pastes or types a profile URL (e.g. linkedin.com/..., github.com/...) to create or update their profile, you MUST pass that exact URL in the corresponding parameter (e.g. linkedinUrl, githubUrl, twitterUrl) to create_user_context, or use scrape_url with that URL then update_user_context; do not use the user's stored social links for that request.\n`,\n};\n\nconst communityModule: PromptModule = {\n id: \"community\",\n triggers: [\"read_networks\", \"create_network\", \"create_network_membership\", \"update_network\", \"delete_network\", \"delete_network_membership\"],\n content: () => `\n### 8. Explore what a community is about\n\n\\`\\`\\`\n0. If user asks about communities they belong to, first use preloaded memberships in this prompt.\n1. read_networks() → get network details (title, prompt)\n2. read_intents(networkId=X) → what members are looking for\n3. read_network_memberships(networkId=X) → who's in it\n4. Synthesize: community purpose, active needs, member composition\n\\`\\`\\`\n\n### When to mention community/index\nIndex and community membership is background: handle it without talking about indexes unless the user asks or it's sign-up, leave, or owner settings. Do not proactively mention \"your indexes\", \"your communities\", \"which index\", \"in your current communities\", or similar. Only mention indexes (or communities, lists) when: (i) post-onboarding sign-up to a community, (ii) user explicitly asked about their indexes/communities, (iii) user wants to leave one, (iv) owner is changing index/community settings. Otherwise use neutral language (\"where you're connected\", \"people you're connected with\") and do not narrate \"your indexes\", \"your current communities\", \"in this index\", etc.\n`,\n};\n\nconst contactsModule: PromptModule = {\n id: \"contacts\",\n triggers: [\"import_gmail_contacts\", \"add_contact\", \"list_contacts\", \"remove_contact\"],\n // Gate on the CONTACTS_ENABLED flag (fail-closed: only `true` enables). When\n // contacts are disabled the import/add tools are de-registered, so this module\n // must not be injected — otherwise the orchestrator keeps advertising Gmail\n // import / add_contact and offers an action that then fails as \"Unknown tool\".\n triggerFilter: (iterCtx) => iterCtx.ctx.contactsEnabled === true,\n content: () => `\n### 9. Import contacts from Gmail\n\n**Single-step workflow:**\n\n\\`\\`\\`\nimport_gmail_contacts()\n→ If not connected: returns { requiresAuth: true, authUrl: \"...\" } — share the URL with the user\n→ If connected: imports contacts directly and returns stats { imported, skipped, newContacts, existingContacts }\n\\`\\`\\`\n\nGhost users are contacts without accounts — they're enriched with public data (LinkedIn, GitHub, X) and can appear in opportunity discovery once enriched.\n\n### 10. Add or manage contacts manually\n\n\\`\\`\\`\n# Add a single contact\nadd_contact(email=\"alice@example.com\", name=\"Alice Smith\")\n\n# List user's network\nlist_contacts() → returns contacts with names, emails, and whether they're ghost users\n\n# Remove a contact\nremove_contact(contactId=X)\n\\`\\`\\`\n`,\n};\n\nconst sharedContextModule: PromptModule = {\n id: \"shared-context\",\n triggers: [\"read_network_memberships\"],\n content: () => `\n### 5. Find shared context between two users\n\n\\`\\`\\`\n1. read_network_memberships(userId=me) → my networks\n2. read_network_memberships(userId=other) → their networks\n3. Intersect networkIds\n4. For each shared index: read_intents(networkId=shared)\n5. read_user_contexts(userId=other)\n6. Synthesize: what overlaps, where they could collaborate\n\\`\\`\\`\n`,\n};\n\nconst mentionsModule: PromptModule = {\n id: \"mentions\",\n triggers: [],\n regex: /@\\[.*?\\]\\(.*?\\)/,\n content: () =>\n `- Messages may contain \\`@[Display Name](userId)\\` markup. The value in parentheses is the userId.\n`,\n};\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// MODULE REGISTRY\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/** All registered prompt modules. */\nexport const PROMPT_MODULES: PromptModule[] = [\n discoveryModule,\n introductionModule,\n intentCreationModule,\n intentManagementModule,\n personLookupModule,\n urlScrapingModule,\n communityModule,\n contactsModule,\n sharedContextModule,\n mentionsModule,\n];\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// RESOLUTION\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Resolves which prompt modules should be injected for the current iteration.\n *\n * Phase 1: Skip all modules when onboarding is active (early exit).\n * Phase 2: Collect candidate modules by checking triggers and regex.\n * Phase 3: Apply exclusions (unidirectional — the excluding module stays).\n *\n * @param iterCtx - Current iteration context (tool history, user message, resolved context)\n * @returns Concatenated prompt text from all matched modules\n */\nexport function resolveModules(iterCtx: IterationContext): string {\n // Phase 1 (early exit): Skip all modules during onboarding\n if (iterCtx.ctx.isOnboarding) {\n return \"\";\n }\n\n const toolNames = new Set(iterCtx.recentTools.map((t) => t.name));\n\n // Phase 2: Collect candidates\n const candidates = new Map<string, PromptModule>();\n\n for (const mod of PROMPT_MODULES) {\n let matched = false;\n\n // Check tool triggers (with optional filter for arg-based disambiguation)\n if (mod.triggers.length > 0 && mod.triggers.some((t) => toolNames.has(t))) {\n matched = mod.triggerFilter ? mod.triggerFilter(iterCtx) : true;\n }\n\n // Check regex trigger\n if (!matched && mod.regex && iterCtx.currentMessage && mod.regex.test(iterCtx.currentMessage)) {\n matched = true;\n }\n\n if (matched) {\n candidates.set(mod.id, mod);\n }\n }\n\n // Phase 3: Apply exclusions (skip self-exclusion)\n for (const mod of candidates.values()) {\n if (mod.excludes) {\n for (const excludedId of mod.excludes) {\n if (excludedId !== mod.id) {\n candidates.delete(excludedId);\n }\n }\n }\n }\n\n // Build output\n const sections: string[] = [];\n for (const mod of candidates.values()) {\n sections.push(mod.content(iterCtx.ctx));\n }\n return sections.join(\"\\n\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"chat.prompt.modules.js","sourceRoot":"/","sources":["chat/chat.prompt.modules.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BaseMessage, AIMessage } from \"@langchain/core/messages\";\n\nimport type { ResolvedToolContext } from \"../shared/agent/tool.factory.js\";\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// TYPES\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * A conditional prompt section injected into the system prompt based on triggers.\n */\nexport interface PromptModule {\n /** Unique module identifier. */\n id: string;\n /** Tool names that activate this module. */\n triggers: string[];\n /** Module IDs to suppress when this module activates (unidirectional). */\n excludes?: string[];\n /** Optional filter applied after tool trigger match. Return false to skip despite trigger match. */\n triggerFilter?: (iterCtx: IterationContext) => boolean;\n /** User message pattern that activates this module (secondary trigger). */\n regex?: RegExp;\n /** Returns the prompt text to inject. */\n content: (ctx: ResolvedToolContext) => string;\n}\n\n/**\n * State available to module resolution at each iteration.\n */\nexport interface IterationContext {\n /** Tool calls from all iterations since the last user message. */\n recentTools: Array<{ name: string; args: Record<string, unknown> }>;\n /** Text of the latest user message (for regex matching). */\n currentMessage?: string;\n /** Resolved tool context (user, profile, indexes, etc.). */\n ctx: ResolvedToolContext;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// EXTRACTION\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Extracts tool calls from all AI messages since the last HumanMessage.\n *\n * Scans backwards to find the last HumanMessage, then collects all tool calls\n * from AIMessages after that point. This ensures multi-iteration tool history\n * is available for module resolution within a single user turn.\n *\n * @param messages - The current conversation message array\n * @returns Flattened array of tool name + args from the current agent turn\n */\nexport function extractRecentToolCalls(\n messages: BaseMessage[],\n): Array<{ name: string; args: Record<string, unknown> }> {\n // Find the index of the last HumanMessage\n let lastHumanIdx = -1;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]._getType() === \"human\") {\n lastHumanIdx = i;\n break;\n }\n }\n\n // Collect tool calls from all AIMessages after the last HumanMessage\n const toolCalls: Array<{ name: string; args: Record<string, unknown> }> = [];\n const startIdx = lastHumanIdx + 1;\n\n for (let i = startIdx; i < messages.length; i++) {\n const msg = messages[i];\n if (msg._getType() === \"ai\") {\n const aiMsg = msg as AIMessage;\n const calls = aiMsg.tool_calls ?? [];\n for (const tc of calls) {\n toolCalls.push({\n name: tc.name,\n args: (tc.args ?? {}) as Record<string, unknown>,\n });\n }\n }\n }\n\n return toolCalls;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// HELPERS\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Checks whether recent tool calls include discover_opportunities with\n * introduction-specific arguments (partyUserIds or introTargetUserId).\n */\nfunction hasIntroductionArgs(recentTools: IterationContext[\"recentTools\"]): boolean {\n return recentTools.some(\n (t) =>\n t.name === \"discover_opportunities\" &&\n (t.args.partyUserIds || t.args.introTargetUserId),\n );\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// MODULE DEFINITIONS\n// ═══════════════════════════════════════════════════════════════════════════════\n\nconst discoveryModule: PromptModule = {\n id: \"discovery\",\n triggers: [\"discover_opportunities\", \"update_opportunity\", \"list_opportunities\"],\n triggerFilter: (iterCtx) => !hasIntroductionArgs(iterCtx.recentTools),\n content: () => `\n### 1. User wants to find connections or discover (default for connection-seeking)\n\nFor open-ended connection-seeking (\"find me a mentor\", \"who needs a React dev\", \"I want to meet people in AI\", \"looking for investors\", \"find me X\"), run **discovery first**.\n\n**CRITICAL: DO NOT create an intent first. Discovery comes FIRST.**\n\n**Network scoping**: When the user says \"in my network\", \"from my contacts\", \"people I know\", \"among my connections\", or similar network-scoping language, pass the user's **personal network ID** as \\`networkId\\`. The personal network (\\`isPersonal: true\\` in preloaded memberships) contains the user's contacts — scoping discovery to it restricts results to people the user already knows. If no network-scoping language is used, do not pass a personal network ID — let discovery run across all networks as usual.\n\n- Call \\`discover_opportunities(searchQuery=user's request)\\` IMMEDIATELY (with networkId when scoped).\n- Do NOT call \\`create_intent\\` unless the user **explicitly** asks to \"create\", \"save\", \"add\", or \"remember\" an intent/signal.\n- Phrases like \"looking for X\", \"find me X\", \"I want to meet X\", \"I need X\" are discovery requests — NOT intent creation requests.\n- If the tool returns \\`createIntentSuggested\\` and \\`suggestedIntentDescription\\`, the system will create an intent and retry discovery automatically; use the final result (candidates or \"no matches\") for your reply.\n- If the tool returns \\`suggestIntentCreationForVisibility: true\\` and \\`suggestedIntentDescription\\`, after presenting the opportunity cards ask the user whether they'd also like to create a signal so others can find them (e.g. *\"Would you also like to create a signal for this so others can find you?\"*). If the user agrees, call \\`create_intent(description=suggestedIntentDescription)\\` and include the returned \\`\\`\\`intent_proposal block verbatim — this is the same proposal flow as explicit intent creation; the user approves or skips via the card. Ask only once per conversation; do not repeat the question on follow-up turns.\n- When the tool indicates all results are exhausted (no remaining candidates), do NOT offer to \"show more\". Instead suggest the user create a signal so others can find them. This uses the same \\`create_intent\\` flow as above.\n- If the user **explicitly** says they want to create/save an intent (e.g. \"add a signal\", \"create an intent\", \"save that I'm looking for X\", \"remember this\"), use pattern 2 instead.\n\n### 1a. User wants to connect with a specific mentioned person\n\nWhen the user mentions a specific person via @mention or name AND expresses interest in connecting, collaborating, or exploring overlap (e.g. \"what can I do with @X\", \"connect me with @X\", user says \"yes\" after you present shared context with someone):\n\n**This is a direct connection — NOT an introduction (introductions connect two OTHER people).**\n\n\\`\\`\\`\n1. If not already done: read_user_contexts(userId=X) + read_network_memberships(userId=X)\n2. Find shared networks with the user (intersect with preloaded memberships)\n3. If no shared networks: tell the user you can't find a connection path\n4. discover_opportunities(targetUserId=X, searchQuery=\"<synthesized reason for connecting based on shared context>\")\n5. Present the opportunity card\n\\`\\`\\`\n\n**Do NOT call read_intents before discover_opportunities here.** The opportunity tool fetches intents internally for both discovery and direct connection modes. Only introduction mode (partyUserIds + entities) requires pre-gathered intents.\n\nThe searchQuery should be a brief description of why they'd connect (e.g. \"shared interest in design and technology, both in Kernel community\"). This gives the evaluator context for scoring.\n\n### 7. Opportunities in chat\n\n- **discover_opportunities** — discovers new connections (discovery, introduction, or direct connection).\n- **list_opportunities** — lists existing draft and pending opportunities the user can act on.\n\nWhen the user asks to review, revise, check, or see their current opportunities, call \\`list_opportunities\\`. Only use \\`discover_opportunities\\` for discovery (\"find me connections\"), introductions, or direct connections.\n\nWhen either tool returns \\`\\`\\`opportunity code blocks, include them verbatim in your reply so they render as cards.\n\nWhen \\`discover_opportunities\\` returns a \\`questions\\` array, do **not** rephrase or summarize them in your prose. The frontend renders them as an interactive decision card surface. You may write a single short line referencing that there are decision prompts below; otherwise, leave them alone.\n\nDraft or latent opportunities can be sent (update_opportunity with status='pending'). Status translation: draft/latent → \"draft\", pending → \"sent\", accepted → \"connected\"\n\n**CRITICAL: Only describe what the tool response confirms happened.** \"pending\" sends a notification — not a message or invite. \"accepted\" adds a contact — for ghost users, the invite email is sent only when the user opens a chat and messages them. Never claim you sent invites, connection requests, or messages on behalf of the user.\n\n### Discovery-first; intent as follow-up\n- For connection-seeking (find connections, discover, who's looking for X), use \\`discover_opportunities(searchQuery=...)\\` first. Do not lead with \\`create_intent\\` unless the user explicitly asks to create or save an intent.\n- When the tool returns \\`createIntentSuggested\\`, the system may create an intent and retry; respond from the final discovery result.\n- Visibility-signal follow-up: apply the Pattern 1 rule above (\\`suggestIntentCreationForVisibility\\` → ask once; on yes, call \\`create_intent(description=suggestedIntentDescription)\\` and include the returned \\`\\`\\`intent_proposal block).\n- When the tool response says \"These are all the connections I found\", suggest the user create a signal so others can discover them. Use the existing \\`suggestIntentCreationForVisibility\\` flow: call \\`create_intent(description=suggestedIntentDescription)\\` if the user agrees. Do not ask \"Would you like to see more?\" when there are no more candidates.\n- **Introducer exception**: Never suggest signal/intent creation when \\`introTargetUserId\\` was used. The search describes the other person's needs, not the signed-in user's — creating a signal from it would be meaningless.\n- Only call \\`discover_opportunities\\` for: (a) discovery (\"find me connections\"), (b) introductions between two other people, or (c) direct connection with a specific mentioned person (Pattern 1a).\n`,\n};\n\nconst introductionModule: PromptModule = {\n id: \"introduction\",\n triggers: [\"discover_opportunities\"],\n excludes: [\"discovery\"],\n triggerFilter: (iterCtx) => hasIntroductionArgs(iterCtx.recentTools),\n content: () => `\n### 6. Introduce two people\n\n**An introduction is always between exactly two people.** Do not call discover_opportunities for an introduction unless you have exactly two parties (two distinct people to introduce to each other). The entities array must have exactly two entities. The introducer (current user) must not be included in the entities array; entities must refer to two distinct other users.\n\n**You MUST gather all context before calling discover_opportunities. The tool does NOT fetch data internally.**\n\n\\`\\`\\`\n1. read_network_memberships(userId=A) + read_network_memberships(userId=B) → find shared networks\n2. If no shared networks: tell user they're not in any shared community\n3. read_user_contexts(userId=A) + read_user_contexts(userId=B)\n4. For each shared network: read_intents(networkId=X, userId=A) + read_intents(networkId=X, userId=B)\n5. Summarize to user: \"Here's what I found about A and B...\"\n6. discover_opportunities(partyUserIds=[A,B], entities=[{userId:A, profile:{...}, intents:[...], networkId:shared}, {userId:B, ...}], hint=\"user's reason\")\n7. Present the draft introduction\n\\`\\`\\`\n\nThe entities array must include each party's userId, profile data, intents from shared networks, and the shared networkId. The hint is the user's stated reason (e.g. \"both AI devs\"). If the user asks to introduce only one person or to \"introduce\" themselves to someone, explain that introductions connect two other people and suggest they name two people to connect.\n\n### 6a. Discover who to introduce to someone\n\n**When the user asks \"who should I introduce to @Person\" or \"find connections for @Person\"** — they want YOU to discover good connections for that person, presented as introduction cards.\n\n\\`\\`\\`\n1. Identify the person's userId from the @mention (call it mentionedUserId)\n2. discover_opportunities(introTargetUserId=mentionedUserId, searchQuery=\"<optional refinement>\")\n3. Present the returned cards (they will be formatted as introduction cards automatically)\n\\`\\`\\`\n\nThis is different from Pattern 6 (where user names BOTH parties). Here the user names ONE person and asks you to find connections for them. Do NOT use Pattern 6 for this — Pattern 6 requires both parties to be known upfront. Do NOT ask the user for a second person. Do NOT use targetUserId or partyUserIds. The system will find connections automatically.\n\n**CRITICAL — no signal creation in introducer flows:** When \\`introTargetUserId\\` is used (Patterns 6 and 6a), the user is searching for connections on behalf of someone else — the search reflects the other person's needs, not the user's own. Do NOT suggest creating a signal or intent in this context. The search query describes what the *other person* needs (e.g. \"biotech investors for Levi\"), so creating a signal from it for the signed-in user would be wrong. Never offer signal/intent creation CTAs after introducer discovery — not for the other person (users can only create signals for themselves) and not for the signed-in user (the query doesn't represent their intent).\n`,\n};\n\nconst intentCreationModule: PromptModule = {\n id: \"intent-creation\",\n triggers: [\"create_intent\"],\n content: () => `\n### 2. User explicitly wants to create or save an intent\n\n**YOU decide if it's specific enough. The tool proposes — the user confirms.**\n\n\\`\\`\\`\nIF description is vague (\"find a job\", \"meet people\", \"learn something\"):\n 1. read_user_contexts() → get their background\n 2. read_intents() → see existing intents for context\n 3. THINK: given their profile and existing intents, suggest a refined version\n 4. Reply: \"Based on your background in X, did you mean something like 'Y'?\"\n 5. Wait for confirmation\n 6. On \"yes\" → create_intent(description=exact_refined_text)\n\nIF description is specific enough (\"contribute to an open-source LLM project\"):\n → create_intent(description=...) directly\n\\`\\`\\`\n\n**CRITICAL: Never write a \\`\\`\\`intent_proposal block yourself.** To propose an intent you MUST call create_intent(description=...). The tool returns a \\`\\`\\`intent_proposal code block (with proposalId and description). You MUST include that exact block verbatim in your response — it renders as an interactive card. Do not summarize or invent the block; only the tool provides a valid one. Add a brief explanation that creating this intent will let the system look for relevant people in the background.\n\nSpecificity test: Does it contain a concrete domain, action, or scope? If just a single generic verb+noun (\"find a job\"), it's vague. If it has qualifying detail (\"senior UX design role at a tech company in Berlin\"), it's specific.\n`,\n};\n\nconst intentManagementModule: PromptModule = {\n id: \"intent-management\",\n triggers: [\"update_intent\", \"delete_intent\"],\n content: () => `\n### 4. Update or delete an intent\n\n**YOU look up the ID first.**\n\n\\`\\`\\`\n1. read_intents() → get current intents with IDs\n2. Match user's request to the right intent\n3. update_intent(intentId=exact_id, description=...) or delete_intent(intentId=exact_id)\n\\`\\`\\`\n`,\n};\n\nconst personLookupModule: PromptModule = {\n id: \"person-lookup\",\n triggers: [\"read_user_contexts\"],\n content: () => `\n### 0. User asks about a specific person by name\n\nWhen the user mentions a specific person by name (\"find [name]\", \"look up [name]\", \"who is [name]?\", \"tell me about [name]\"), look them up by name first — do NOT use discovery.\n\n- Call \\`read_user_contexts(query=\"the name\")\\` — this finds members by name across the user's indexes\n- If one match: the result already includes their full profile; present it naturally\n- If multiple matches: present the list and ask the user to clarify which person\n- If no matches: tell the user you couldn't find anyone by that name in their network\n- If the user then asks for semantic discovery (e.g. \"find people like them\"), use Pattern 1.\n- If the user wants to connect with this specific person (e.g. \"yes, connect us\", \"what can I do with them\", \"I'd like to reach out\"), use Pattern 1a.\n`,\n};\n\nconst urlScrapingModule: PromptModule = {\n id: \"url-scraping\",\n triggers: [\"scrape_url\"],\n regex: /(https?:\\/\\/)/i,\n content: () => `\n### 3. User includes a URL\n\n**YOU handle scraping before intent creation.**\n\n\\`\\`\\`\n1. scrape_url(url, objective=\"Extract key details for an intent\")\n2. Synthesize a conceptual description from scraped content\n3. create_intent(description=synthesized_summary)\n\\`\\`\\`\n\nException: for profile creation, pass URLs directly to create_user_context (it handles scraping internally).\n\nIf the user pastes or types a profile URL (e.g. linkedin.com/..., github.com/...) to create or update their profile, you MUST pass that exact URL in the corresponding parameter (e.g. linkedinUrl, githubUrl, twitterUrl) to create_user_context, or use scrape_url with that URL then update_user_context; do not use the user's stored social links for that request.\n`,\n};\n\nconst communityModule: PromptModule = {\n id: \"community\",\n triggers: [\"read_networks\", \"create_network\", \"create_network_membership\", \"update_network\", \"delete_network\", \"delete_network_membership\"],\n content: () => `\n### 8. Explore what a community is about\n\n\\`\\`\\`\n0. If user asks about communities they belong to, first use preloaded memberships in this prompt.\n1. read_networks() → get network details (title, prompt)\n2. read_intents(networkId=X) → what members are looking for\n3. read_network_memberships(networkId=X) → who's in it\n4. Synthesize: community purpose, active needs, member composition\n\\`\\`\\`\n\n### When to mention community/index\nIndex and community membership is background: handle it without talking about indexes unless the user asks or it's sign-up, leave, or owner settings. Do not proactively mention \"your indexes\", \"your communities\", \"which index\", \"in your current communities\", or similar. Only mention indexes (or communities, lists) when: (i) post-onboarding sign-up to a community, (ii) user explicitly asked about their indexes/communities, (iii) user wants to leave one, (iv) owner is changing index/community settings. Otherwise use neutral language (\"where you're connected\", \"people you're connected with\") and do not narrate \"your indexes\", \"your current communities\", \"in this network\", etc.\n`,\n};\n\nconst contactsModule: PromptModule = {\n id: \"contacts\",\n triggers: [\"import_gmail_contacts\", \"add_contact\", \"list_contacts\", \"remove_contact\"],\n // Gate on the CONTACTS_ENABLED flag (fail-closed: only `true` enables). When\n // contacts are disabled the import/add tools are de-registered, so this module\n // must not be injected — otherwise the orchestrator keeps advertising Gmail\n // import / add_contact and offers an action that then fails as \"Unknown tool\".\n triggerFilter: (iterCtx) => iterCtx.ctx.contactsEnabled === true,\n content: () => `\n### 9. Import contacts from Gmail\n\n**Single-step workflow:**\n\n\\`\\`\\`\nimport_gmail_contacts()\n→ If not connected: returns { requiresAuth: true, authUrl: \"...\" } — share the URL with the user\n→ If connected: imports contacts directly and returns stats { imported, skipped, newContacts, existingContacts }\n\\`\\`\\`\n\nGhost users are contacts without accounts — they're enriched with public data (LinkedIn, GitHub, X) and can appear in opportunity discovery once enriched.\n\n### 10. Add or manage contacts manually\n\n\\`\\`\\`\n# Add a single contact\nadd_contact(email=\"alice@example.com\", name=\"Alice Smith\")\n\n# List user's network\nlist_contacts() → returns contacts with names, emails, and whether they're ghost users\n\n# Remove a contact\nremove_contact(contactId=X)\n\\`\\`\\`\n`,\n};\n\nconst sharedContextModule: PromptModule = {\n id: \"shared-context\",\n triggers: [\"read_network_memberships\"],\n content: () => `\n### 5. Find shared context between two users\n\n\\`\\`\\`\n1. read_network_memberships(userId=me) → my networks\n2. read_network_memberships(userId=other) → their networks\n3. Intersect networkIds\n4. For each shared network: read_intents(networkId=shared)\n5. read_user_contexts(userId=other)\n6. Synthesize: what overlaps, where they could collaborate\n\\`\\`\\`\n`,\n};\n\nconst mentionsModule: PromptModule = {\n id: \"mentions\",\n triggers: [],\n regex: /@\\[.*?\\]\\(.*?\\)/,\n content: () =>\n `- Messages may contain \\`@[Display Name](userId)\\` markup. The value in parentheses is the userId.\n`,\n};\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// MODULE REGISTRY\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/** All registered prompt modules. */\nexport const PROMPT_MODULES: PromptModule[] = [\n discoveryModule,\n introductionModule,\n intentCreationModule,\n intentManagementModule,\n personLookupModule,\n urlScrapingModule,\n communityModule,\n contactsModule,\n sharedContextModule,\n mentionsModule,\n];\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// RESOLUTION\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Resolves which prompt modules should be injected for the current iteration.\n *\n * Phase 1: Skip all modules when onboarding is active (early exit).\n * Phase 2: Collect candidate modules by checking triggers and regex.\n * Phase 3: Apply exclusions (unidirectional — the excluding module stays).\n *\n * @param iterCtx - Current iteration context (tool history, user message, resolved context)\n * @returns Concatenated prompt text from all matched modules\n */\nexport function resolveModules(iterCtx: IterationContext): string {\n // Phase 1 (early exit): Skip all modules during onboarding\n if (iterCtx.ctx.isOnboarding) {\n return \"\";\n }\n\n const toolNames = new Set(iterCtx.recentTools.map((t) => t.name));\n\n // Phase 2: Collect candidates\n const candidates = new Map<string, PromptModule>();\n\n for (const mod of PROMPT_MODULES) {\n let matched = false;\n\n // Check tool triggers (with optional filter for arg-based disambiguation)\n if (mod.triggers.length > 0 && mod.triggers.some((t) => toolNames.has(t))) {\n matched = mod.triggerFilter ? mod.triggerFilter(iterCtx) : true;\n }\n\n // Check regex trigger\n if (!matched && mod.regex && iterCtx.currentMessage && mod.regex.test(iterCtx.currentMessage)) {\n matched = true;\n }\n\n if (matched) {\n candidates.set(mod.id, mod);\n }\n }\n\n // Phase 3: Apply exclusions (skip self-exclusion)\n for (const mod of candidates.values()) {\n if (mod.excludes) {\n for (const excludedId of mod.excludes) {\n if (excludedId !== mod.id) {\n candidates.delete(excludedId);\n }\n }\n }\n }\n\n // Build output\n const sections: string[] = [];\n for (const mod of candidates.values()) {\n sections.push(mod.content(iterCtx.ctx));\n }\n return sections.join(\"\\n\");\n}\n"]}
|
|
@@ -18,7 +18,7 @@ export interface IntentSubgraphResult {
|
|
|
18
18
|
intents?: unknown[];
|
|
19
19
|
count?: number;
|
|
20
20
|
error?: string;
|
|
21
|
-
/** When the intent graph exits early (e.g.
|
|
21
|
+
/** When the intent graph exits early (e.g. network-scoped without intents); surface to user. */
|
|
22
22
|
requiredMessage?: string;
|
|
23
23
|
}
|
|
24
24
|
/** Index subgraph result (memberships, ownedIndexes, specificIndexData). */
|
|
@@ -59,11 +59,14 @@ export declare const ChatGraphState: import("@langchain/langgraph").AnnotationRo
|
|
|
59
59
|
Root: <S extends import("@langchain/langgraph").StateDefinition>(sd: S) => import("@langchain/langgraph").AnnotationRoot<S>;
|
|
60
60
|
};
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* create_intent with networkId, scope index assignment to this index only).
|
|
62
|
+
* Legacy optional index (community) ID when chat is scoped to a specific index.
|
|
63
|
+
* New callers should set scopeType/scopeId; networkId is retained as an edge alias.
|
|
65
64
|
*/
|
|
66
65
|
networkId: import("@langchain/langgraph").BaseChannel<string | undefined, string | import("@langchain/langgraph").OverwriteValue<string | undefined> | undefined, unknown>;
|
|
66
|
+
/** Focused request scope type. Currently only `network` exists. */
|
|
67
|
+
scopeType: import("@langchain/langgraph").BaseChannel<"network" | undefined, "network" | import("@langchain/langgraph").OverwriteValue<"network" | undefined> | undefined, unknown>;
|
|
68
|
+
/** Focused request scope id. When scopeType is `network`, this is the network id. */
|
|
69
|
+
scopeId: import("@langchain/langgraph").BaseChannel<string | undefined, string | import("@langchain/langgraph").OverwriteValue<string | undefined> | undefined, unknown>;
|
|
67
70
|
/**
|
|
68
71
|
* Chat session ID when streaming with context. Used by tools to create draft
|
|
69
72
|
* opportunities tied to this conversation (context.conversationId).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.state.d.ts","sourceRoot":"/","sources":["chat/chat.state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,iCAAiC,EAAE,MAAM,2BAA2B,CAAC;AAMpH,mFAAmF;AACnF,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,+DAA+D;AAC/D,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,
|
|
1
|
+
{"version":3,"file":"chat.state.d.ts","sourceRoot":"/","sources":["chat/chat.state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,iCAAiC,EAAE,MAAM,2BAA2B,CAAC;AAMpH,mFAAmF;AACnF,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,+DAA+D;AAC/D,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gGAAgG;IAChG,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,4EAA4E;AAC5E,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC;IACzB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,uFAAuF;AACvF,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAMD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc;IAKzB;;OAEG;;;;;;IAGH;;;OAGG;;IAMH,mEAAmE;;IAMnE,qFAAqF;;IAMrF;;;OAGG;;IAMH;;;;OAIG;;IAUH;;;OAGG;;IAMH;;;OAGG;;IAUH;;;OAGG;;IAMH;;OAEG;;IAMH;;;OAGG;;eAC4B,MAAM;oBAAc,MAAM;eAAS,iBAAiB,EAAE;aAAO,YAAY;mCAA6B,iCAAiC;;eAAvI,MAAM;oBAAc,MAAM;eAAS,iBAAiB,EAAE;aAAO,YAAY;mCAA6B,iCAAiC;;eAAvI,MAAM;oBAAc,MAAM;eAAS,iBAAiB,EAAE;aAAO,YAAY;mCAA6B,iCAAiC;;IAMtK,8DAA8D;;IAK9D,gEAAgE;;IAKhE,oDAAoD;;EAMpD,CAAC;AAMH;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,cAAc,CAAC,KAAK,CAAC"}
|
package/dist/chat/chat.state.js
CHANGED
|
@@ -22,14 +22,23 @@ export const ChatGraphState = Annotation.Root({
|
|
|
22
22
|
*/
|
|
23
23
|
userId: (Annotation),
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* create_intent with networkId, scope index assignment to this index only).
|
|
25
|
+
* Legacy optional index (community) ID when chat is scoped to a specific index.
|
|
26
|
+
* New callers should set scopeType/scopeId; networkId is retained as an edge alias.
|
|
28
27
|
*/
|
|
29
28
|
networkId: Annotation({
|
|
30
29
|
reducer: (curr, next) => next ?? curr,
|
|
31
30
|
default: () => undefined,
|
|
32
31
|
}),
|
|
32
|
+
/** Focused request scope type. Currently only `network` exists. */
|
|
33
|
+
scopeType: Annotation({
|
|
34
|
+
reducer: (curr, next) => next ?? curr,
|
|
35
|
+
default: () => undefined,
|
|
36
|
+
}),
|
|
37
|
+
/** Focused request scope id. When scopeType is `network`, this is the network id. */
|
|
38
|
+
scopeId: Annotation({
|
|
39
|
+
reducer: (curr, next) => next ?? curr,
|
|
40
|
+
default: () => undefined,
|
|
41
|
+
}),
|
|
33
42
|
/**
|
|
34
43
|
* Chat session ID when streaming with context. Used by tools to create draft
|
|
35
44
|
* opportunities tied to this conversation (context.conversationId).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.state.js","sourceRoot":"/","sources":["chat/chat.state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAgDxE,iFAAiF;AACjF,6CAA6C;AAC7C,iFAAiF;AAEjF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,CAAC;IAC5C,8EAA8E;IAC9E,cAAc;IACd,8EAA8E;IAE9E;;OAEG;IACH,MAAM,EAAE,CAAA,UAAkB,CAAA;IAE1B
|
|
1
|
+
{"version":3,"file":"chat.state.js","sourceRoot":"/","sources":["chat/chat.state.ts"],"names":[],"mappings":"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","sourcesContent":["import { Annotation, messagesStateReducer } from \"@langchain/langgraph\";\nimport type { BaseMessage } from \"@langchain/core/messages\";\n\nimport type { DebugMetaToolCall, DebugMetaLlm, DebugMetaOrchestratorNegotiations } from \"./chat-streaming.types.js\";\n\n// ══════════════════════════════════════════════════════════════════════════════\n// TYPES (used by legacy subgraph nodes; agent-loop graph does not set these)\n// ══════════════════════════════════════════════════════════════════════════════\n\n/** Routing decision from router node (target, operationType, extractedContext). */\nexport interface RoutingDecision {\n target: string;\n operationType: string | null;\n extractedContext?: string | null;\n}\n\n/** Intent subgraph result (actions, inferredIntents, etc.). */\nexport interface IntentSubgraphResult {\n actions?: Array<{ type: string; payload?: string }>;\n inferredIntents?: unknown[];\n indexingResults?: unknown[];\n mode?: string;\n intents?: unknown[];\n count?: number;\n error?: string;\n /** When the intent graph exits early (e.g. network-scoped without intents); surface to user. */\n requiredMessage?: string;\n}\n\n/** Index subgraph result (memberships, ownedIndexes, specificIndexData). */\nexport interface IndexSubgraphResult {\n mode?: string;\n memberships?: unknown[];\n ownedIndexes?: unknown[];\n specificIndexData?: unknown;\n count?: number;\n error?: string;\n}\n\n/** Aggregated results from subgraphs (intent, index, profile, opportunity, scrape). */\nexport interface SubgraphResults {\n intent?: IntentSubgraphResult;\n index?: IndexSubgraphResult;\n profile?: unknown;\n opportunity?: unknown;\n scrape?: unknown;\n}\n\n// ══════════════════════════════════════════════════════════════════════════════\n// CHAT GRAPH STATE (Agent Loop Architecture)\n// ══════════════════════════════════════════════════════════════════════════════\n\n/**\n * The Chat Graph State using LangGraph Annotations.\n * \n * This is a simplified state for the agent loop architecture.\n * The agent handles all routing decisions internally via tool calling.\n * \n * Design Principles:\n * - Messages accumulate through the conversation (includes tool calls/results)\n * - Iteration count tracks loop progress for soft/hard limits\n * - Final response is extracted at the end\n */\nexport const ChatGraphState = Annotation.Root({\n // ═══════════════════════════════════════════════════════════════════════════\n // CORE INPUTS\n // ═══════════════════════════════════════════════════════════════════════════\n\n /**\n * The User ID - required for all operations.\n */\n userId: Annotation<string>,\n\n /**\n * Legacy optional index (community) ID when chat is scoped to a specific index.\n * New callers should set scopeType/scopeId; networkId is retained as an edge alias.\n */\n networkId: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Focused request scope type. Currently only `network` exists. */\n scopeType: Annotation<'network' | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** Focused request scope id. When scopeType is `network`, this is the network id. */\n scopeId: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /**\n * Chat session ID when streaming with context. Used by tools to create draft\n * opportunities tied to this conversation (context.conversationId).\n */\n sessionId: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /**\n * Conversation history using LangGraph's built-in message reducer.\n * Includes: HumanMessage, AIMessage, ToolMessage, SystemMessage\n * Automatically handles message appending, ID management, and ordering.\n */\n messages: Annotation<BaseMessage[]>({\n reducer: messagesStateReducer,\n default: () => [],\n }),\n\n // ═══════════════════════════════════════════════════════════════════════════\n // LOOP CONTROL\n // ═══════════════════════════════════════════════════════════════════════════\n\n /**\n * Current iteration count in the agent loop.\n * Used for soft limit (nudge) and hard limit (force exit).\n */\n iterationCount: Annotation<number>({\n reducer: (curr, next) => next,\n default: () => 0,\n }),\n\n /**\n * Flag indicating whether the agent loop should continue.\n * Set to false when agent produces final response or hits hard limit.\n */\n shouldContinue: Annotation<boolean>({\n reducer: (curr, next) => next,\n default: () => true,\n }),\n\n // ═══════════════════════════════════════════════════════════════════════════\n // OUTPUTS\n // ═══════════════════════════════════════════════════════════════════════════\n\n /**\n * The final generated response text.\n * Set when the agent decides to stop and respond.\n */\n responseText: Annotation<string | undefined>({\n reducer: (curr, next) => next,\n default: () => undefined,\n }),\n\n /**\n * Error message if the agent loop fails.\n */\n error: Annotation<string | undefined>({\n reducer: (curr, next) => next,\n default: () => undefined,\n }),\n\n /**\n * Per-turn debug meta (graph, iterations, tool calls) for copy-debug.\n * Not persisted; only used so the streamer receives it in the updates chunk.\n */\n debugMeta: Annotation<{ graph: string; iterations: number; tools: DebugMetaToolCall[]; llm: DebugMetaLlm; orchestratorNegotiations?: DebugMetaOrchestratorNegotiations } | undefined>({\n reducer: (curr, next) => next,\n default: () => undefined,\n }),\n\n // Legacy subgraph state (used by index/intent/response nodes when present)\n /** Router output: target, operationType, extractedContext. */\n routingDecision: Annotation<RoutingDecision | undefined>({\n reducer: (curr, next) => next,\n default: () => undefined,\n }),\n /** Results from intent/profile/opportunity/scrape subgraphs. */\n subgraphResults: Annotation<SubgraphResults | undefined>({\n reducer: (curr, next) => next,\n default: () => undefined,\n }),\n /** User profile context (e.g. for intent nodes). */\n userProfile: Annotation<unknown>({\n reducer: (curr, next) => next,\n default: () => undefined,\n }),\n\n});\n\n// ══════════════════════════════════════════════════════════════════════════════\n// TYPE EXPORTS\n// ══════════════════════════════════════════════════════════════════════════════\n\n/**\n * The full state type for the Chat Graph.\n * Use this for typing node functions and graph invocations.\n */\nexport type ChatGraphStateType = typeof ChatGraphState.State;\n"]}
|
|
@@ -27,7 +27,10 @@ export declare class ChatStreamer {
|
|
|
27
27
|
message: string;
|
|
28
28
|
sessionId: string;
|
|
29
29
|
maxContextMessages?: number;
|
|
30
|
+
/** @deprecated Use scopeType/scopeId. Kept for REST/session edge compatibility. */
|
|
30
31
|
networkId?: string;
|
|
32
|
+
scopeType?: 'network';
|
|
33
|
+
scopeId?: string;
|
|
31
34
|
prefillMessages?: Array<{
|
|
32
35
|
role: "assistant" | "user";
|
|
33
36
|
content: string;
|
|
@@ -54,6 +57,8 @@ export declare class ChatStreamer {
|
|
|
54
57
|
userId: string;
|
|
55
58
|
messages: BaseMessage[];
|
|
56
59
|
networkId?: string;
|
|
60
|
+
scopeType?: 'network';
|
|
61
|
+
scopeId?: string;
|
|
57
62
|
}, sessionId: string, checkpointer?: BaseCheckpointSaver, signal?: AbortSignal, threadId?: string): AsyncGenerator<ChatStreamEvent>;
|
|
58
63
|
}
|
|
59
64
|
//# sourceMappingURL=chat.streamer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.streamer.d.ts","sourceRoot":"/","sources":["chat/chat.streamer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,KAAK,EAAE,eAAe,EAAmG,MAAM,2BAA2B,CAAC;AAUlK;;;;;;;GAOG;AACH,qBAAa,YAAY;IAErB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,oBAAoB;gBAJpB,kBAAkB,EAAE,CAC1B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,KAChB,OAAO,CAAC,WAAW,EAAE,CAAC,EACnB,oBAAoB,EAAE,CAC5B,YAAY,CAAC,EAAE,mBAAmB,KAC/B,GAAG;IAGV;;;;;;;;OAQG;IACW,2BAA2B,CACvC,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,eAAe,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACzE;;4EAEoE;QACpE,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,EACD,YAAY,CAAC,EAAE,mBAAmB,EAClC,MAAM,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"chat.streamer.d.ts","sourceRoot":"/","sources":["chat/chat.streamer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,KAAK,EAAE,eAAe,EAAmG,MAAM,2BAA2B,CAAC;AAUlK;;;;;;;GAOG;AACH,qBAAa,YAAY;IAErB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,oBAAoB;gBAJpB,kBAAkB,EAAE,CAC1B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,KAChB,OAAO,CAAC,WAAW,EAAE,CAAC,EACnB,oBAAoB,EAAE,CAC5B,YAAY,CAAC,EAAE,mBAAmB,KAC/B,GAAG;IAGV;;;;;;;;OAQG;IACW,2BAA2B,CACvC,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,mFAAmF;QACnF,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,SAAS,CAAC;QACtB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,eAAe,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACzE;;4EAEoE;QACpE,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,EACD,YAAY,CAAC,EAAE,mBAAmB,EAClC,MAAM,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,eAAe,CAAC;IAsElC;;;;;;;;;;;;OAYG;IACW,gBAAgB,CAC5B,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,WAAW,EAAE,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,SAAS,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,EAC/G,SAAS,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,mBAAmB,EAClC,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,MAAM,GAChB,cAAc,CAAC,eAAe,CAAC;CA6MnC"}
|
|
@@ -28,14 +28,17 @@ export class ChatStreamer {
|
|
|
28
28
|
* @yields ChatStreamEvent objects
|
|
29
29
|
*/
|
|
30
30
|
async *streamChatEventsWithContext(input, checkpointer, signal) {
|
|
31
|
-
const { userId, message, sessionId, maxContextMessages = 20, networkId, prefillMessages, runId, } = input;
|
|
31
|
+
const { userId, message, sessionId, maxContextMessages = 20, networkId, scopeType, scopeId, prefillMessages, runId, } = input;
|
|
32
|
+
const effectiveScopeType = scopeType ?? (networkId ? 'network' : undefined);
|
|
33
|
+
const effectiveScopeId = scopeId ?? networkId;
|
|
32
34
|
logger.verbose("Starting context-aware streaming", {
|
|
33
35
|
userId,
|
|
34
36
|
sessionId,
|
|
35
37
|
maxContextMessages,
|
|
36
38
|
hasCheckpointer: !!checkpointer,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
hasIndexScope: !!effectiveScopeId,
|
|
40
|
+
scopeType: effectiveScopeType,
|
|
41
|
+
scopeId: effectiveScopeId,
|
|
39
42
|
});
|
|
40
43
|
try {
|
|
41
44
|
// Load previous conversation context
|
|
@@ -52,7 +55,7 @@ export class ChatStreamer {
|
|
|
52
55
|
// Form composite thread_id when a runId is provided
|
|
53
56
|
const threadId = runId ? `${sessionId}:${runId}` : sessionId;
|
|
54
57
|
// Stream with context using the optional checkpointer
|
|
55
|
-
yield* this.streamChatEvents({ userId, messages: allMessages,
|
|
58
|
+
yield* this.streamChatEvents({ userId, messages: allMessages, scopeType: effectiveScopeType, scopeId: effectiveScopeId }, sessionId, checkpointer, signal, threadId);
|
|
56
59
|
}
|
|
57
60
|
catch (error) {
|
|
58
61
|
logger.error("Stream error", {
|
|
@@ -83,8 +86,14 @@ export class ChatStreamer {
|
|
|
83
86
|
userId: input.userId,
|
|
84
87
|
messages: input.messages,
|
|
85
88
|
};
|
|
89
|
+
const effectiveScopeType = input.scopeType ?? (input.networkId ? 'network' : undefined);
|
|
90
|
+
const effectiveScopeId = input.scopeId ?? input.networkId;
|
|
86
91
|
if (input.networkId)
|
|
87
92
|
initialState.networkId = input.networkId;
|
|
93
|
+
if (effectiveScopeType && effectiveScopeId) {
|
|
94
|
+
initialState.scopeType = effectiveScopeType;
|
|
95
|
+
initialState.scopeId = effectiveScopeId;
|
|
96
|
+
}
|
|
88
97
|
initialState.sessionId = sessionId;
|
|
89
98
|
// Use graph.stream() with custom + updates modes.
|
|
90
99
|
// Custom events come from config.writer() inside agentLoopNode.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.streamer.js","sourceRoot":"/","sources":["chat/chat.streamer.ts"],"names":[],"mappings":"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","sourcesContent":["import { AIMessage, BaseMessage, HumanMessage } from \"@langchain/core/messages\";\nimport { BaseCheckpointSaver } from \"@langchain/langgraph\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport type { ChatStreamEvent, DebugMetaDiscoveryQuestions, DebugMetaToolCall, DebugMetaLlm, DebugMetaOrchestratorNegotiations } from \"./chat-streaming.types.js\";\nimport { createAgentEndEvent, createAgentStartEvent, createDebugMetaEvent, createDecisionQuestionsEvent, createErrorEvent, createGraphEndEvent, createPhaseStartEvent, createPhaseEndEvent, createGraphStartEvent, createIterationStartEvent, createLlmStartEvent, createLlmEndEvent, createResponseCompleteEvent, createResponseResetEvent, createHallucinationDetectedEvent, createStatusEvent, createTokenEvent, createToolActivityEvent, createChatSummarizerStartEvent, createChatSummarizerEndEvent, createQuestionGeneratorStartEvent, createQuestionGeneratorEndEvent } from \"./chat-streaming.types.js\";\nimport type { AgentStreamEvent } from \"./chat.agent.js\";\n\nconst logger = protocolLogger(\"ChatStreamer\");\n\n// ══════════════════════════════════════════════════════════════════════════════\n// CHAT STREAMER (Streaming Narration Architecture)\n// ══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Top-level streaming service for Chat Graph events.\n *\n * Uses `graph.stream()` with `streamMode: [\"custom\", \"updates\"]` so that\n * the agent's `config.writer()` calls arrive as `\"custom\"` chunks (text\n * tokens and tool-activity events) and the final state update arrives as\n * an `\"updates\"` chunk.\n */\nexport class ChatStreamer {\n constructor(\n private loadSessionContext: (\n sessionId: string,\n maxMessages: number,\n ) => Promise<BaseMessage[]>,\n private createStreamingGraph: (\n checkpointer?: BaseCheckpointSaver,\n ) => any,\n ) {}\n\n /**\n * Streams chat events with full session context.\n * Loads previous conversation history and optionally uses a checkpointer\n * for state persistence.\n *\n * @param input - Configuration for context-aware streaming\n * @param checkpointer - Optional checkpointer for state persistence\n * @yields ChatStreamEvent objects\n */\n public async *streamChatEventsWithContext(\n input: {\n userId: string;\n message: string;\n sessionId: string;\n maxContextMessages?: number;\n networkId?: string;\n prefillMessages?: Array<{ role: \"assistant\" | \"user\"; content: string }>;\n /** Per-run identifier used to form a composite LangGraph thread_id (sessionId:runId).\n * When provided, prevents stale checkpoint state from a prior run being resumed.\n * Defaults to sessionId alone when absent (backward compatible). */\n runId?: string;\n },\n checkpointer?: BaseCheckpointSaver,\n signal?: AbortSignal,\n ): AsyncGenerator<ChatStreamEvent> {\n const {\n userId,\n message,\n sessionId,\n maxContextMessages = 20,\n networkId,\n prefillMessages,\n runId,\n } = input;\n logger.verbose(\"Starting context-aware streaming\", {\n userId,\n sessionId,\n maxContextMessages,\n hasCheckpointer: !!checkpointer,\n hasIndexId: !!networkId,\n networkId: networkId ?? undefined,\n });\n\n try {\n // Load previous conversation context\n const previousMessages = await this.loadSessionContext(\n sessionId,\n maxContextMessages,\n );\n\n // Inject prefill messages (e.g. hardcoded onboarding greeting) only for fresh sessions\n const prefill: BaseMessage[] = previousMessages.length === 0\n ? (prefillMessages ?? []).map((pm) =>\n pm.role === \"assistant\" ? new AIMessage(pm.content) : new HumanMessage(pm.content),\n )\n : [];\n\n const allMessages = [...previousMessages, ...prefill, new HumanMessage(message)];\n\n logger.verbose(\"Context prepared\", {\n previousCount: previousMessages.length,\n totalCount: allMessages.length,\n });\n\n // Form composite thread_id when a runId is provided\n const threadId = runId ? `${sessionId}:${runId}` : sessionId;\n\n // Stream with context using the optional checkpointer\n yield* this.streamChatEvents(\n { userId, messages: allMessages, networkId },\n sessionId,\n checkpointer,\n signal,\n threadId,\n );\n } catch (error) {\n logger.error(\"Stream error\", {\n error: error instanceof Error ? error.message : String(error),\n });\n yield createErrorEvent(\n sessionId,\n error instanceof Error\n ? error.message\n : \"Unknown error during context-aware streaming\",\n \"CONTEXT_STREAM_ERROR\",\n );\n }\n }\n\n /**\n * Streams chat events from the graph execution.\n *\n * Uses `graph.stream()` with `streamMode: [\"custom\", \"updates\"]`:\n * - `\"custom\"` chunks carry {@link AgentStreamEvent} objects emitted by\n * `ChatAgent.streamRun()` via `config.writer()`\n * - `\"updates\"` chunks carry the final state update from `agentLoopNode`\n *\n * @param input - The input state for the graph (userId and messages)\n * @param sessionId - The session ID for event attribution\n * @param checkpointer - Optional checkpointer for persistence\n * @yields ChatStreamEvent objects\n */\n public async *streamChatEvents(\n input: { userId: string; messages: BaseMessage[]; networkId?: string },\n sessionId: string,\n checkpointer?: BaseCheckpointSaver,\n signal?: AbortSignal,\n threadId?: string,\n ): AsyncGenerator<ChatStreamEvent> {\n const graph = this.createStreamingGraph(checkpointer);\n\n try {\n const initialState: {\n userId: string;\n messages: BaseMessage[];\n networkId?: string;\n sessionId?: string;\n } = {\n userId: input.userId,\n messages: input.messages,\n };\n if (input.networkId) initialState.networkId = input.networkId;\n initialState.sessionId = sessionId;\n\n // Use graph.stream() with custom + updates modes.\n // Custom events come from config.writer() inside agentLoopNode.\n const eventStream = await graph.stream(initialState, {\n streamMode: [\"custom\", \"updates\"] as const,\n configurable: { thread_id: threadId ?? sessionId, signal },\n signal,\n });\n\n // Emit initial status\n yield createStatusEvent(sessionId, \"Processing your message...\");\n\n for await (const tuple of eventStream) {\n if (signal?.aborted) break;\n // graph.stream with multiple modes yields [mode, chunk] tuples\n const [mode, chunk] = tuple as [string, unknown];\n\n // ─────────────────────────────────────────────────────────────────\n // CUSTOM: writer events from ChatAgent.streamRun()\n // ─────────────────────────────────────────────────────────────────\n if (mode === \"custom\") {\n const event = chunk as AgentStreamEvent;\n\n if (event.type === \"iteration_start\") {\n yield createIterationStartEvent(sessionId, event.iteration);\n }\n\n if (event.type === \"llm_start\") {\n yield createLlmStartEvent(sessionId, event.iteration);\n }\n\n if (event.type === \"text_chunk\" && event.content) {\n yield createTokenEvent(sessionId, event.content);\n }\n\n if (event.type === \"response_reset\") {\n yield createResponseResetEvent(sessionId, event.reason);\n }\n\n if (event.type === \"hallucination_detected\") {\n yield createHallucinationDetectedEvent(sessionId, event.blockType, event.tool);\n }\n\n if (event.type === \"llm_end\") {\n yield createLlmEndEvent(\n sessionId,\n event.iteration,\n event.hasToolCalls,\n event.toolNames,\n );\n }\n\n if (event.type === \"tool_activity\") {\n logger.debug(\"Tool activity\", { name: event.name, phase: event.phase });\n if (event.phase === \"start\") {\n yield createToolActivityEvent(\n sessionId,\n event.name,\n event.name,\n \"start\",\n );\n } else {\n yield createToolActivityEvent(\n sessionId,\n event.name,\n event.name,\n \"end\",\n event.success,\n event.summary,\n event.steps,\n );\n }\n }\n\n if (event.type === \"graph_start\") {\n yield createGraphStartEvent(sessionId, event.name);\n }\n\n if (event.type === \"graph_end\") {\n yield createGraphEndEvent(sessionId, event.name, event.durationMs);\n }\n\n if (event.type === \"phase_start\") {\n yield createPhaseStartEvent(sessionId, event.name);\n }\n\n if (event.type === \"phase_end\") {\n yield createPhaseEndEvent(sessionId, event.name, event.durationMs);\n }\n\n if (event.type === \"agent_start\") {\n yield createAgentStartEvent(sessionId, event.name);\n }\n\n if (event.type === \"agent_end\") {\n yield createAgentEndEvent(sessionId, event.name, event.durationMs, event.summary);\n }\n\n if (event.type === \"decision_questions\") {\n yield createDecisionQuestionsEvent(sessionId, { questions: event.questions });\n }\n\n if (event.type === \"chat_summarizer_start\") {\n yield createChatSummarizerStartEvent(sessionId, event.payload);\n }\n\n if (event.type === \"chat_summarizer_end\") {\n yield createChatSummarizerEndEvent(sessionId, event.payload);\n }\n\n if (event.type === \"question_generator_start\") {\n yield createQuestionGeneratorStartEvent(sessionId, event.payload);\n }\n\n if (event.type === \"question_generator_end\") {\n yield createQuestionGeneratorEndEvent(sessionId, event.payload);\n }\n }\n\n // ─────────────────────────────────────────────────────────────────\n // UPDATES: final state from agentLoopNode\n // ─────────────────────────────────────────────────────────────────\n if (mode === \"updates\") {\n // The updates chunk is { agent_loop: { responseText, error, ... } }\n const updates = chunk as Record<string, Record<string, unknown>>;\n const agentOutput = updates?.agent_loop;\n\n if (agentOutput?.error) {\n logger.warn(\"Agent loop returned error via updates\", {\n error: agentOutput.error,\n });\n yield createErrorEvent(\n sessionId,\n String(agentOutput.error),\n \"AGENT_ERROR\",\n );\n }\n\n // Yield the agent's authoritative response text so the\n // controller can persist it without relying on token accumulation.\n const responseText = typeof agentOutput?.responseText === \"string\"\n ? (agentOutput.responseText as string)\n : \"\";\n yield createResponseCompleteEvent(sessionId, responseText);\n\n const debugMeta = agentOutput?.debugMeta as\n | { graph: string; iterations: number; tools?: DebugMetaToolCall[]; llm?: DebugMetaLlm; orchestratorNegotiations?: DebugMetaOrchestratorNegotiations; discoveryQuestions?: DebugMetaDiscoveryQuestions }\n | undefined;\n if (\n debugMeta?.graph != null &&\n typeof debugMeta.iterations === \"number\"\n ) {\n const llmFallback: DebugMetaLlm = { calls: 0, totalDurationMs: 0, resets: [], hallucinations: [] };\n yield createDebugMetaEvent(\n sessionId,\n debugMeta.graph,\n debugMeta.iterations,\n Array.isArray(debugMeta.tools) ? debugMeta.tools : [],\n debugMeta.llm ?? llmFallback,\n debugMeta.orchestratorNegotiations,\n debugMeta.discoveryQuestions,\n );\n }\n\n logger.verbose(\"Agent loop complete (updates)\", {\n responseLength: responseText.length,\n });\n }\n }\n } catch (error) {\n logger.error(\"Stream error\", {\n error: error instanceof Error ? error.message : String(error),\n });\n yield createErrorEvent(\n sessionId,\n error instanceof Error\n ? error.message\n : \"Unknown error during streaming\",\n \"STREAM_ERROR\",\n );\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"chat.streamer.js","sourceRoot":"/","sources":["chat/chat.streamer.ts"],"names":[],"mappings":"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","sourcesContent":["import { AIMessage, BaseMessage, HumanMessage } from \"@langchain/core/messages\";\nimport { BaseCheckpointSaver } from \"@langchain/langgraph\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport type { ChatStreamEvent, DebugMetaDiscoveryQuestions, DebugMetaToolCall, DebugMetaLlm, DebugMetaOrchestratorNegotiations } from \"./chat-streaming.types.js\";\nimport { createAgentEndEvent, createAgentStartEvent, createDebugMetaEvent, createDecisionQuestionsEvent, createErrorEvent, createGraphEndEvent, createPhaseStartEvent, createPhaseEndEvent, createGraphStartEvent, createIterationStartEvent, createLlmStartEvent, createLlmEndEvent, createResponseCompleteEvent, createResponseResetEvent, createHallucinationDetectedEvent, createStatusEvent, createTokenEvent, createToolActivityEvent, createChatSummarizerStartEvent, createChatSummarizerEndEvent, createQuestionGeneratorStartEvent, createQuestionGeneratorEndEvent } from \"./chat-streaming.types.js\";\nimport type { AgentStreamEvent } from \"./chat.agent.js\";\n\nconst logger = protocolLogger(\"ChatStreamer\");\n\n// ══════════════════════════════════════════════════════════════════════════════\n// CHAT STREAMER (Streaming Narration Architecture)\n// ══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Top-level streaming service for Chat Graph events.\n *\n * Uses `graph.stream()` with `streamMode: [\"custom\", \"updates\"]` so that\n * the agent's `config.writer()` calls arrive as `\"custom\"` chunks (text\n * tokens and tool-activity events) and the final state update arrives as\n * an `\"updates\"` chunk.\n */\nexport class ChatStreamer {\n constructor(\n private loadSessionContext: (\n sessionId: string,\n maxMessages: number,\n ) => Promise<BaseMessage[]>,\n private createStreamingGraph: (\n checkpointer?: BaseCheckpointSaver,\n ) => any,\n ) {}\n\n /**\n * Streams chat events with full session context.\n * Loads previous conversation history and optionally uses a checkpointer\n * for state persistence.\n *\n * @param input - Configuration for context-aware streaming\n * @param checkpointer - Optional checkpointer for state persistence\n * @yields ChatStreamEvent objects\n */\n public async *streamChatEventsWithContext(\n input: {\n userId: string;\n message: string;\n sessionId: string;\n maxContextMessages?: number;\n /** @deprecated Use scopeType/scopeId. Kept for REST/session edge compatibility. */\n networkId?: string;\n scopeType?: 'network';\n scopeId?: string;\n prefillMessages?: Array<{ role: \"assistant\" | \"user\"; content: string }>;\n /** Per-run identifier used to form a composite LangGraph thread_id (sessionId:runId).\n * When provided, prevents stale checkpoint state from a prior run being resumed.\n * Defaults to sessionId alone when absent (backward compatible). */\n runId?: string;\n },\n checkpointer?: BaseCheckpointSaver,\n signal?: AbortSignal,\n ): AsyncGenerator<ChatStreamEvent> {\n const {\n userId,\n message,\n sessionId,\n maxContextMessages = 20,\n networkId,\n scopeType,\n scopeId,\n prefillMessages,\n runId,\n } = input;\n const effectiveScopeType = scopeType ?? (networkId ? 'network' as const : undefined);\n const effectiveScopeId = scopeId ?? networkId;\n logger.verbose(\"Starting context-aware streaming\", {\n userId,\n sessionId,\n maxContextMessages,\n hasCheckpointer: !!checkpointer,\n hasIndexScope: !!effectiveScopeId,\n scopeType: effectiveScopeType,\n scopeId: effectiveScopeId,\n });\n\n try {\n // Load previous conversation context\n const previousMessages = await this.loadSessionContext(\n sessionId,\n maxContextMessages,\n );\n\n // Inject prefill messages (e.g. hardcoded onboarding greeting) only for fresh sessions\n const prefill: BaseMessage[] = previousMessages.length === 0\n ? (prefillMessages ?? []).map((pm) =>\n pm.role === \"assistant\" ? new AIMessage(pm.content) : new HumanMessage(pm.content),\n )\n : [];\n\n const allMessages = [...previousMessages, ...prefill, new HumanMessage(message)];\n\n logger.verbose(\"Context prepared\", {\n previousCount: previousMessages.length,\n totalCount: allMessages.length,\n });\n\n // Form composite thread_id when a runId is provided\n const threadId = runId ? `${sessionId}:${runId}` : sessionId;\n\n // Stream with context using the optional checkpointer\n yield* this.streamChatEvents(\n { userId, messages: allMessages, scopeType: effectiveScopeType, scopeId: effectiveScopeId },\n sessionId,\n checkpointer,\n signal,\n threadId,\n );\n } catch (error) {\n logger.error(\"Stream error\", {\n error: error instanceof Error ? error.message : String(error),\n });\n yield createErrorEvent(\n sessionId,\n error instanceof Error\n ? error.message\n : \"Unknown error during context-aware streaming\",\n \"CONTEXT_STREAM_ERROR\",\n );\n }\n }\n\n /**\n * Streams chat events from the graph execution.\n *\n * Uses `graph.stream()` with `streamMode: [\"custom\", \"updates\"]`:\n * - `\"custom\"` chunks carry {@link AgentStreamEvent} objects emitted by\n * `ChatAgent.streamRun()` via `config.writer()`\n * - `\"updates\"` chunks carry the final state update from `agentLoopNode`\n *\n * @param input - The input state for the graph (userId and messages)\n * @param sessionId - The session ID for event attribution\n * @param checkpointer - Optional checkpointer for persistence\n * @yields ChatStreamEvent objects\n */\n public async *streamChatEvents(\n input: { userId: string; messages: BaseMessage[]; networkId?: string; scopeType?: 'network'; scopeId?: string },\n sessionId: string,\n checkpointer?: BaseCheckpointSaver,\n signal?: AbortSignal,\n threadId?: string,\n ): AsyncGenerator<ChatStreamEvent> {\n const graph = this.createStreamingGraph(checkpointer);\n\n try {\n const initialState: {\n userId: string;\n messages: BaseMessage[];\n networkId?: string;\n scopeType?: 'network';\n scopeId?: string;\n sessionId?: string;\n } = {\n userId: input.userId,\n messages: input.messages,\n };\n const effectiveScopeType = input.scopeType ?? (input.networkId ? 'network' as const : undefined);\n const effectiveScopeId = input.scopeId ?? input.networkId;\n if (input.networkId) initialState.networkId = input.networkId;\n if (effectiveScopeType && effectiveScopeId) {\n initialState.scopeType = effectiveScopeType;\n initialState.scopeId = effectiveScopeId;\n }\n initialState.sessionId = sessionId;\n\n // Use graph.stream() with custom + updates modes.\n // Custom events come from config.writer() inside agentLoopNode.\n const eventStream = await graph.stream(initialState, {\n streamMode: [\"custom\", \"updates\"] as const,\n configurable: { thread_id: threadId ?? sessionId, signal },\n signal,\n });\n\n // Emit initial status\n yield createStatusEvent(sessionId, \"Processing your message...\");\n\n for await (const tuple of eventStream) {\n if (signal?.aborted) break;\n // graph.stream with multiple modes yields [mode, chunk] tuples\n const [mode, chunk] = tuple as [string, unknown];\n\n // ─────────────────────────────────────────────────────────────────\n // CUSTOM: writer events from ChatAgent.streamRun()\n // ─────────────────────────────────────────────────────────────────\n if (mode === \"custom\") {\n const event = chunk as AgentStreamEvent;\n\n if (event.type === \"iteration_start\") {\n yield createIterationStartEvent(sessionId, event.iteration);\n }\n\n if (event.type === \"llm_start\") {\n yield createLlmStartEvent(sessionId, event.iteration);\n }\n\n if (event.type === \"text_chunk\" && event.content) {\n yield createTokenEvent(sessionId, event.content);\n }\n\n if (event.type === \"response_reset\") {\n yield createResponseResetEvent(sessionId, event.reason);\n }\n\n if (event.type === \"hallucination_detected\") {\n yield createHallucinationDetectedEvent(sessionId, event.blockType, event.tool);\n }\n\n if (event.type === \"llm_end\") {\n yield createLlmEndEvent(\n sessionId,\n event.iteration,\n event.hasToolCalls,\n event.toolNames,\n );\n }\n\n if (event.type === \"tool_activity\") {\n logger.debug(\"Tool activity\", { name: event.name, phase: event.phase });\n if (event.phase === \"start\") {\n yield createToolActivityEvent(\n sessionId,\n event.name,\n event.name,\n \"start\",\n );\n } else {\n yield createToolActivityEvent(\n sessionId,\n event.name,\n event.name,\n \"end\",\n event.success,\n event.summary,\n event.steps,\n );\n }\n }\n\n if (event.type === \"graph_start\") {\n yield createGraphStartEvent(sessionId, event.name);\n }\n\n if (event.type === \"graph_end\") {\n yield createGraphEndEvent(sessionId, event.name, event.durationMs);\n }\n\n if (event.type === \"phase_start\") {\n yield createPhaseStartEvent(sessionId, event.name);\n }\n\n if (event.type === \"phase_end\") {\n yield createPhaseEndEvent(sessionId, event.name, event.durationMs);\n }\n\n if (event.type === \"agent_start\") {\n yield createAgentStartEvent(sessionId, event.name);\n }\n\n if (event.type === \"agent_end\") {\n yield createAgentEndEvent(sessionId, event.name, event.durationMs, event.summary);\n }\n\n if (event.type === \"decision_questions\") {\n yield createDecisionQuestionsEvent(sessionId, { questions: event.questions });\n }\n\n if (event.type === \"chat_summarizer_start\") {\n yield createChatSummarizerStartEvent(sessionId, event.payload);\n }\n\n if (event.type === \"chat_summarizer_end\") {\n yield createChatSummarizerEndEvent(sessionId, event.payload);\n }\n\n if (event.type === \"question_generator_start\") {\n yield createQuestionGeneratorStartEvent(sessionId, event.payload);\n }\n\n if (event.type === \"question_generator_end\") {\n yield createQuestionGeneratorEndEvent(sessionId, event.payload);\n }\n }\n\n // ─────────────────────────────────────────────────────────────────\n // UPDATES: final state from agentLoopNode\n // ─────────────────────────────────────────────────────────────────\n if (mode === \"updates\") {\n // The updates chunk is { agent_loop: { responseText, error, ... } }\n const updates = chunk as Record<string, Record<string, unknown>>;\n const agentOutput = updates?.agent_loop;\n\n if (agentOutput?.error) {\n logger.warn(\"Agent loop returned error via updates\", {\n error: agentOutput.error,\n });\n yield createErrorEvent(\n sessionId,\n String(agentOutput.error),\n \"AGENT_ERROR\",\n );\n }\n\n // Yield the agent's authoritative response text so the\n // controller can persist it without relying on token accumulation.\n const responseText = typeof agentOutput?.responseText === \"string\"\n ? (agentOutput.responseText as string)\n : \"\";\n yield createResponseCompleteEvent(sessionId, responseText);\n\n const debugMeta = agentOutput?.debugMeta as\n | { graph: string; iterations: number; tools?: DebugMetaToolCall[]; llm?: DebugMetaLlm; orchestratorNegotiations?: DebugMetaOrchestratorNegotiations; discoveryQuestions?: DebugMetaDiscoveryQuestions }\n | undefined;\n if (\n debugMeta?.graph != null &&\n typeof debugMeta.iterations === \"number\"\n ) {\n const llmFallback: DebugMetaLlm = { calls: 0, totalDurationMs: 0, resets: [], hallucinations: [] };\n yield createDebugMetaEvent(\n sessionId,\n debugMeta.graph,\n debugMeta.iterations,\n Array.isArray(debugMeta.tools) ? debugMeta.tools : [],\n debugMeta.llm ?? llmFallback,\n debugMeta.orchestratorNegotiations,\n debugMeta.discoveryQuestions,\n );\n }\n\n logger.verbose(\"Agent loop complete (updates)\", {\n responseLength: responseText.length,\n });\n }\n }\n } catch (error) {\n logger.error(\"Stream error\", {\n error: error instanceof Error ? error.message : String(error),\n });\n yield createErrorEvent(\n sessionId,\n error instanceof Error\n ? error.message\n : \"Unknown error during streaming\",\n \"STREAM_ERROR\",\n );\n }\n }\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Configurable mock database and fixture data for chat graph workflow tests.
|
|
3
3
|
* Use createChatGraphMockDb(config) to get a full ChatGraphCompositeDatabase
|
|
4
|
-
* with controllable profile, intents,
|
|
4
|
+
* with controllable profile, intents, network membership, and opportunities.
|
|
5
5
|
*/
|
|
6
6
|
import type { ChatGraphCompositeDatabase, ActiveIntent, IndexedIntentDetails, NetworkMembership, OwnedIndex, UserRecord, Opportunity, OpportunityStatus } from "../../shared/interfaces/database.interface.js";
|
|
7
7
|
import type { ChatSessionReader } from "../../shared/interfaces/chat-session.interface.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Configurable mock database and fixture data for chat graph workflow tests.
|
|
3
3
|
* Use createChatGraphMockDb(config) to get a full ChatGraphCompositeDatabase
|
|
4
|
-
* with controllable profile, intents,
|
|
4
|
+
* with controllable profile, intents, network membership, and opportunities.
|
|
5
5
|
*/
|
|
6
6
|
const noop = async () => undefined;
|
|
7
7
|
const noopArray = async () => [];
|