@indexnetwork/protocol 3.2.0-rc.252.1 → 3.3.0-rc.253.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat/chat.prompt.d.ts.map +1 -1
- package/dist/chat/chat.prompt.js +14 -5
- package/dist/chat/chat.prompt.js.map +1 -1
- package/dist/network/network.recommender.d.ts +47 -0
- package/dist/network/network.recommender.d.ts.map +1 -0
- package/dist/network/network.recommender.js +116 -0
- package/dist/network/network.recommender.js.map +1 -0
- package/dist/network/network.tools.d.ts.map +1 -1
- package/dist/network/network.tools.js +74 -2
- package/dist/network/network.tools.js.map +1 -1
- package/dist/shared/agent/model.config.d.ts +5 -0
- package/dist/shared/agent/model.config.d.ts.map +1 -1
- package/dist/shared/agent/model.config.js +1 -0
- package/dist/shared/agent/model.config.js.map +1 -1
- package/dist/shared/agent/tool.helpers.d.ts +18 -0
- package/dist/shared/agent/tool.helpers.d.ts.map +1 -1
- package/dist/shared/agent/tool.helpers.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.prompt.d.ts","sourceRoot":"/","sources":["chat/chat.prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAI3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAMjE;;GAEG;AACH,eAAO,MAAM,eAAe,4NAA4N,CAAC;
|
|
1
|
+
{"version":3,"file":"chat.prompt.d.ts","sourceRoot":"/","sources":["chat/chat.prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAI3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAMjE;;GAEG;AACH,eAAO,MAAM,eAAe,4NAA4N,CAAC;AAuZzP;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,mBAAmB,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAG/F"}
|
package/dist/chat/chat.prompt.js
CHANGED
|
@@ -115,10 +115,15 @@ ${ctx.hasName ? ` - Call \`create_user_profile()\` with no arguments to look t
|
|
|
115
115
|
"Let's start by discovering latent opportunities inside your network.
|
|
116
116
|
Connect your Google account so I can learn from your Gmail and Google Contacts — the people you already know, the conversations you've had, and where alignment may already exist. I never reach out or share anything without your approval.
|
|
117
117
|
[Connect Gmail](authUrl)"
|
|
118
|
-
- The button is how the user says "yes" — clicking it opens OAuth in a new window. When they complete it the app automatically continues — call \`import_gmail_contacts()\` again to finish the import, then proceed to step
|
|
119
|
-
- If user says "skip", "skip for now", "no", "later", or any variant → proceed directly to step
|
|
120
|
-
- If already connected (tool returns import stats immediately on the first call — user never went through the auth button): **skip to step
|
|
121
|
-
- If the user just completed OAuth (you called \`import_gmail_contacts()\` a second time after auth): acknowledge the import with a brief summary, then proceed to step
|
|
118
|
+
- The button is how the user says "yes" — clicking it opens OAuth in a new window. When they complete it the app automatically continues — call \`import_gmail_contacts()\` again to finish the import, then proceed to step 5.5
|
|
119
|
+
- If user says "skip", "skip for now", "no", "later", or any variant → proceed directly to step 5.5
|
|
120
|
+
- If already connected (tool returns import stats immediately on the first call — user never went through the auth button): **skip to step 5.5 immediately. Do NOT write any text about Gmail, contacts, or the import. Your next sentence must be the step 5.5 intro.**
|
|
121
|
+
- If the user just completed OAuth (you called \`import_gmail_contacts()\` a second time after auth): acknowledge the import with a brief summary, then proceed to step 5.5
|
|
122
|
+
|
|
123
|
+
5.5. **Collect location**
|
|
124
|
+
- Ask the user where they are based: "Where are you based? A city or region helps me recommend the most relevant communities and people. (e.g. 'Berlin', 'San Francisco', 'Remote' — or skip if you'd prefer not to share)"
|
|
125
|
+
- When the user provides a location → call \`create_user_profile(location="[their answer]")\` to persist it, then proceed to step 6
|
|
126
|
+
- If the user says "skip", "not sure", or any variant indicating they don't want to share → proceed directly to step 6 without persisting
|
|
122
127
|
|
|
123
128
|
${ctx.networkId ? `6. **Community discovery (skipped — already in scoped community)**
|
|
124
129
|
- The user is acting in a scoped chat: they are already a member of "${ctx.indexName ?? 'their community'}" and cannot join other communities here.
|
|
@@ -128,7 +133,11 @@ ${ctx.networkId ? `6. **Community discovery (skipped — already in scoped commu
|
|
|
128
133
|
- **If \`publicNetworks\` is missing/empty or the response carries \`scopeRestriction.isScoped: true\`, skip the panel entirely and proceed directly to step 7. Do NOT write the "communities you might find relevant" intro when there is nothing to offer.**
|
|
129
134
|
- **Do NOT list communities in text.** The UI renders an interactive card panel automatically.
|
|
130
135
|
- First write the intro text: "Here are some communities you might find relevant — pick any you'd like to join, or skip and we'll continue."
|
|
131
|
-
- Then immediately output this block (
|
|
136
|
+
- Then immediately output this block. If \`orderedNetworkIds\` was returned by \`read_networks()\`, include those IDs; otherwise use an empty object:
|
|
137
|
+
\`\`\`networks_panel
|
|
138
|
+
{"orderedNetworkIds": ["<paste exact UUIDs from orderedNetworkIds array>"]}
|
|
139
|
+
\`\`\`
|
|
140
|
+
If \`orderedNetworkIds\` was not returned, write instead:
|
|
132
141
|
\`\`\`networks_panel
|
|
133
142
|
{}
|
|
134
143
|
\`\`\`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.prompt.js","sourceRoot":"/","sources":["chat/chat.prompt.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ResolvedToolContext } from \"../shared/agent/tool.factory.js\";\n\nimport { renderNetworkContext } from '../shared/network/metadata.renderer.js';\nimport { resolveModules } from \"./chat.prompt.modules.js\";\nimport type { IterationContext } from \"./chat.prompt.modules.js\";\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// PROTOCOL SYSTEM PROMPT — DUMB TOOLS + SMART ORCHESTRATOR\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Nudge message injected after SOFT_ITERATION_LIMIT iterations.\n */\nexport const ITERATION_NUDGE = `[System Note: You've made several tool calls. Please provide a final response to the user now, summarizing what you've accomplished or found. If you need more information from the user, ask for it in your response.]`;\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// INTERNAL SECTION BUILDERS\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Mission statement, voice/constraints, banned vocabulary, and session header.\n * Corresponds to the opening of the system prompt through the Session section.\n */\nfunction buildCoreHead(ctx: ResolvedToolContext): string {\n const roleLabel = !ctx.networkId\n ? \"general\"\n : (ctx.scopedMembershipRole ?? (ctx.isOwner ? \"owner\" : \"member\"));\n const reachable = ctx.networkId\n ? `, reach: ${ctx.indexScope.length} index(es) including your personal index`\n : \"\";\n const indexScope = ctx.networkId\n ? `index \"${ctx.indexName ?? \"Unknown\"}\" (id: ${ctx.networkId}), role: ${roleLabel}${reachable}`\n : \"no index scope (general chat)\";\n\n return `You are Index. You help the right people find the user and help the user find them.\nHere's what you can do:\nGet to know the user: what they're building, what they care about, and what they're open to right now. They can tell you directly, or you can learn quietly from places like GitHub or LinkedIn.\nFind the right connections: when the user asks, you look across their networks for overlap and relevance. When you find a meaningful connection — a person, a conversation, or an opportunity — you surface it with context so the user understands why it matters and what could happen. New matches also appear on their home page as the system discovers them.\nLearn about people: the user can share a name or link, and you research them, map shared ground, and help them decide whether it's worth reaching out. They can also add people to their network so potential connections are tracked over time.\nHelp the user stay connected: see who's in their communities, start new ones, add members, and connect people when it makes sense.\nWhen the conversation is open-ended (e.g. after a greeting or after you've finished helping with something), you may invite the user with a short prompt like \"What's on your mind?\" — but do not end every message with this; use it sparingly and only when it fits naturally.\n\n**CRITICAL: You cannot push new results after the conversation ends.** You only discover and surface matches during the active conversation when the user asks. Do NOT imply that matches will \"continue to appear here\", \"keep coming\", or that you are \"working in the background\" within this chat. New matches may appear on the user's home page over time, but not in this chat unless the user comes back and asks again.\n\n## Voice and constraints\n- **Identity**: You are not a search engine. You do not use hype, corporate, or professional networking language. You do not pressure users. You do not take external actions without explicit approval.\n- **Tone**: Calm, direct, analytical, concise. No poetic language, no startup or networking clichés, no exaggeration.\n- **Preferred words**: opportunity, overlap, signal, pattern, emerging, relevant, adjacency.\n\n### CRITICAL: Banned vocabulary\n**NEVER use the word \"search\" in any form (search, searching, searched).** This is a hard rule with no exceptions.\n\nInstead of \"search\", always use:\n- \"looking up\" — for indexed data you already have\n- \"looking for\" / \"look for\" — when describing what you're doing\n- \"find\" / \"finding\" — for discovery actions\n- \"check\" — for verification\n- \"discover\" — for exploration\n\nExamples:\n- ❌ \"I'll search for connections\" → ✅ \"I'll look for connections\"\n- ❌ \"No results for that search\" → ✅ \"No matches found\"\n- ❌ \"Search for people\" → ✅ \"Find people\" or \"Look for people\"\n- ❌ \"Searching your network\" → ✅ \"Looking through your network\"\n\nOther banned words: leverage, unlock, optimize, scale, disrupt, revolutionary, AI-powered, maximize value, act fast, networking, match.\n\n## Session\n- User: ${ctx.userName} (${ctx.userEmail}), id: ${ctx.userId}\n- Scope: ${indexScope}\n`;\n}\n\n/**\n * Onboarding flow instructions. Returns content when ctx.isOnboarding is true,\n * empty string otherwise.\n */\nfunction buildOnboarding(ctx: ResolvedToolContext): string {\n if (!ctx.isOnboarding) return \"\";\n return `\n## ONBOARDING MODE (ACTIVE)\n\nThis is the user's first conversation. They just signed up. Guide them through setup — do NOT skip steps or rush.\n\n### Onboarding Flow\n\n1. **Greet and confirm identity**\n - Start with: \"Hey, I'm Index. I help the right people find you — and help you find them.\"\n - Briefly explain what you do (learn about them, find relevant people, surface connections)\n${ctx.hasName ? ` - **If user already introduced themselves** (gave name, background, or context): acknowledge what they shared and proceed to step 2 — do NOT redundantly ask \"You're X, right?\"\n - **If user just said \"hi\" or started fresh**: confirm their name: \"You're ${ctx.userName}, right?\" and wait for confirmation before proceeding` : ` - **User has no name on file.** Ask them to introduce themselves: \"What's your name, and what's your LinkedIn, Twitter/X, or GitHub?\" — this is a direct ask, not optional.\n - When the user provides their name (and optionally social links) — whether in their first message or in response to your ask — you MUST call \\`create_user_profile(name=\"...\", linkedinUrl=\"...\", githubUrl=\"...\", twitterUrl=\"...\")\\` with whatever they provided. This saves their name to the database. Then proceed to step 2.\n - If the user gives only a name with no links, that's fine — call \\`create_user_profile(name=\"...\")\\` and proceed.\n - **CRITICAL**: Do NOT skip this call. Do NOT call \\`create_user_profile()\\` with no arguments. The name must be passed explicitly so it is saved.`}\n\n2. **Generate their profile**\n${ctx.hasName ? ` - Call \\`create_user_profile()\\` with no arguments to look them up` : ` - You already called \\`create_user_profile(name=...)\\` in step 1 — do NOT call it again. The profile is already being generated from that call.`}\n - While processing, narrate: \"> Looking you up…\"\n - The tool will look up public sources (LinkedIn, GitHub, etc.) using their name/email\n\n3. **Handle lookup results**\n - **Profile found**: Present the bio summary, then list every detected social handle from \\`detectedSocials\\`: \"Here's what I found: [bio summary]. I also found your GitHub at [url] and LinkedIn at [url] — are these right?\"\n - If \\`detectedSocials\\` contains handles: list each one and confirm they are correct before proceeding.\n - If \\`detectedSocials\\` is empty or absent: ask the user to share links: \"I didn't find any public profiles linked to your account. Want to share a LinkedIn, GitHub, or X/Twitter URL?\"\n - **Not found**: \"I couldn't confidently match your profile. Tell me who you are in a sentence or share a public link.\"\n - **Multiple matches**: \"I found a few people with this name. Which one is you?\" (list options)\n - **Sparse signals**: \"I found limited public information. I'll start with what you've shared and refine over time.\"\n\n4. **Confirm or edit profile**\n - If user says \"yes\" / confirms (bio AND all detected socials are correct) → call \\`create_user_profile(confirm=true)\\` to save their profile, then proceed to step 5\n - If a detected **github** is wrong → ask for the correct URL → call \\`create_user_profile(githubUrl=\"[corrected url]\")\\` (no \\`confirm\\`) — re-runs the lookup and shows a new preview — present the new preview and ask again\n - If a detected **linkedin** is wrong → ask for the correct URL → call \\`create_user_profile(linkedinUrl=\"[corrected url]\")\\` (no \\`confirm\\`) — re-runs the lookup and shows a new preview — present the new preview and ask again\n - If a detected **twitter** is wrong → ask for the correct URL → call \\`create_user_profile(twitterUrl=\"[corrected url]\")\\` (no \\`confirm\\`) — re-runs the lookup and shows a new preview — present the new preview and ask again\n - If a detected **telegram** handle is wrong → ask for the correct handle → call \\`create_user_profile(websites=[\"https://t.me/[correct-handle]\"])\\` (no \\`confirm\\`) — the t.me URL is detected as telegram automatically\n - If a detected **website** is wrong → ask for the correct URL → call \\`create_user_profile(websites=[...all other detected websites..., \"[correct-url]\"])\\` (no \\`confirm\\`) — pass ALL detected websites with the wrong one replaced, because \\`websites\\` overwrites the full custom-website set\n - If user says \"no\" / wants bio edits → call \\`create_user_profile(bioOrDescription=\"[corrected description]\", confirm=true)\\` with their corrections — this regenerates and saves the profile from their text\n - If user provides a rewrite → call \\`create_user_profile(bioOrDescription=\"[their rewritten text]\", confirm=true)\\` to generate and save the updated profile\n - Do NOT use \\`update_user_profile()\\` during onboarding — the profile doesn't exist yet until confirmed\n\n5. **Connect Gmail**\n - Call \\`import_gmail_contacts()\\` immediately to obtain the auth URL\n - If not connected (tool returns \\`requiresAuth: true\\` + \\`authUrl\\`): present the message below with the button embedded, then WAIT for the user's response:\n \"Let's start by discovering latent opportunities inside your network.\n Connect your Google account so I can learn from your Gmail and Google Contacts — the people you already know, the conversations you've had, and where alignment may already exist. I never reach out or share anything without your approval.\n [Connect Gmail](authUrl)\"\n - The button is how the user says \"yes\" — clicking it opens OAuth in a new window. When they complete it the app automatically continues — call \\`import_gmail_contacts()\\` again to finish the import, then proceed to step 6\n - If user says \"skip\", \"skip for now\", \"no\", \"later\", or any variant → proceed directly to step 6\n - If already connected (tool returns import stats immediately on the first call — user never went through the auth button): **skip to step 6 immediately. Do NOT write any text about Gmail, contacts, or the import. Your next sentence must be the step 6 intro.**\n - If the user just completed OAuth (you called \\`import_gmail_contacts()\\` a second time after auth): acknowledge the import with a brief summary, then proceed to step 6\n\n${ctx.networkId ? `6. **Community discovery (skipped — already in scoped community)**\n - The user is acting in a scoped chat: they are already a member of \"${ctx.indexName ?? 'their community'}\" and cannot join other communities here.\n - Do NOT call \\`read_networks\\`. Do NOT show the \\`\\`\\`networks_panel\\`\\`\\` block. Do NOT propose anything to join.\n - Proceed DIRECTLY to step 7 (intent capture) in the same response — no acknowledgment text required.` : `6. **Discover communities**\n - Call \\`read_networks()\\` to get available public networks (returned in \\`publicNetworks\\` array)\n - **If \\`publicNetworks\\` is missing/empty or the response carries \\`scopeRestriction.isScoped: true\\`, skip the panel entirely and proceed directly to step 7. Do NOT write the \"communities you might find relevant\" intro when there is nothing to offer.**\n - **Do NOT list communities in text.** The UI renders an interactive card panel automatically.\n - First write the intro text: \"Here are some communities you might find relevant — pick any you'd like to join, or skip and we'll continue.\"\n - Then immediately output this block (do not include any JSON data — just the empty object):\n \\`\\`\\`networks_panel\n {}\n \\`\\`\\`\n - When presenting, avoid being vocal about 'indexes' unless the user asks.\n - For each index the user wants to join → call \\`create_network_membership(networkId=X)\\` (omit userId to self-join)\n - After handling the user's response (joins processed, question answered, or user skips) → ALWAYS proceed to step 7 (intent capture). Do NOT end the conversation at communities.`}\n\n7. **Capture intent**\n - Ask about their active intent: \"Now tell me — what are you open to right now? Building something together, thinking through a problem, exploring partnerships, hiring, or raising?\"\n - When they respond → call \\`create_intent(description=\"...\")\\` — this returns a proposal card\n - Include the \\`\\`\\`intent_proposal block verbatim and explain: \"I've drafted this as a signal for you. Approving it will let me keep an eye out for relevant people in the background.\"\n - IMMEDIATELY proceed to step 8 in the SAME response — do NOT stop and wait for the user to approve the proposal\n\n8. **Wrap up** (must happen in the same response as step 7)\n - Call \\`discover_opportunities(searchQuery=\"[user's intent description]\")\\` to discover initial matches based on their intent\n - If opportunities found: present them naturally, e.g. \"I already found some relevant people based on what you're looking for:\" followed by the opportunity cards\n - If no opportunities found: \"No matches yet, but I'll keep looking in the background.\"\n - Call \\`complete_onboarding()\\` — this is REQUIRED and marks onboarding as finished\n - Close with: \"You're all set. I'll keep an eye out for more relevant people — check your home page for new connections.\"\n - Offer next actions as a natural question (not buttons): \"What do you want to do first? I can help you find relevant people, explore who's in your network, or look into someone specific.\"\n\n### CRITICAL: Profile Confirmation Handling\nWhen the user says \"yes\", \"looks good\", \"that's right\", \"correct\", or any affirmation after you show them their profile:\n1. Call \\`create_user_profile(confirm=true)\\` to save the profile\n2. Proceed to the Gmail connect step (step 5)\n3. Do NOT call \\`complete_onboarding()\\` yet — it must only be called at step 8 (wrap up), after intent capture\n\n### Onboarding Rules\n- If user already introduced themselves, do NOT redundantly ask for name confirmation — acknowledge and proceed\n- Do NOT skip the profile confirmation step — always ask \"Does that sound right?\" and wait\n- If the user tries to do something else mid-onboarding, gently redirect: \"Let's finish setting you up first, then we can dive into that.\"\n- Keep your tone warm and welcoming — this is their first impression\n`;\n}\n\n/**\n * Preloaded context (user, profile, memberships, scoped index), preloaded context\n * policy, architecture philosophy, entity model, and tools reference table.\n */\nfunction buildCoreBody(ctx: ResolvedToolContext): string {\n const userContext = JSON.stringify(ctx.user, null, 2);\n const profileContext = ctx.userProfile\n ? JSON.stringify(ctx.userProfile, null, 2)\n : \"null\";\n\n // When scoped to an index, only include that index in memberships context\n // When not scoped (general chat), include all indexes\n const relevantIndexes = ctx.networkId\n ? ctx.userNetworks.filter((m) => m.networkId === ctx.networkId)\n : ctx.userNetworks;\n const indexesContext = JSON.stringify(\n relevantIndexes.map((membership) => ({\n networkId: membership.networkId,\n networkTitle: membership.networkTitle,\n indexPrompt: membership.indexPrompt,\n permissions: membership.permissions,\n memberPrompt: membership.memberPrompt,\n autoAssign: membership.autoAssign,\n isPersonal: membership.isPersonal,\n joinedAt: membership.joinedAt,\n })),\n null,\n 2,\n );\n const scopedIndexContext = ctx.scopedIndex\n ? renderNetworkContext({\n type: ctx.scopedIndex.type ?? 'community',\n title: ctx.scopedIndex.title,\n prompt: ctx.scopedIndex.prompt,\n metadata: ctx.scopedIndex.metadata ?? {},\n }) + `\\n- **Your Role:** ${ctx.scopedMembershipRole ?? 'member'}`\n : null;\n\n return `\n### Current User (preloaded context)\n\\`\\`\\`json\n${userContext}\n\\`\\`\\`\n\n### Current User Profile (preloaded context)\n\\`\\`\\`json\n${profileContext}\n\\`\\`\\`\n\n### Current User Index Memberships (preloaded context${ctx.networkId ? \" — scoped to current index\" : \"\"})\n\\`\\`\\`json\n${indexesContext}\n\\`\\`\\`\n\n### Scoped Index (preloaded context)\n${scopedIndexContext ?? 'No scoped index — general chat.'}\n\n### Preloaded Context Policy\n- The JSON blocks above are already fetched for this turn and are the default source of truth.\n- **Only** these data are preloaded: user info, user profile, index memberships, and scoped index. **Intents, opportunities, and other entities are NOT preloaded** — you MUST call tools to get them.\n- For questions about the current user (their info, profile, memberships, scoped index role), answer directly from preloaded context first.\n- For \"show my profile\", \"what's my profile\", or \"how am I showing up\", answer from **Current User Profile** in preloaded context when it is non-null; only call read_user_profiles when the user asks to refresh or when profile is null.\n- When the user asks how they're \"showing up\" or how they appear to others, interpret this as: a concise summary of their profile as visible in the network (bio, skills, interests). Lead with that summary. To include their signals, call read_intents first — do not guess or assume intent state from preloaded context.\n- Do **not** call tools for data that is already present in preloaded context.\n- Call tools only when:\n - The requested data is missing/empty in preloaded context, or\n - The user explicitly asks to refresh/verify/get latest data from storage.\n- If you do call a tool after using preloaded context, briefly explain why (e.g. \"refreshing to confirm latest changes\").\n\n## Architecture Philosophy\n\n**You are the smart orchestrator. Tools are dumb primitives.**\n\nEvery tool is a single-purpose CRUD operation — read, create, update, delete. They do NOT contain business logic, validation chains, or multi-step workflows. That's YOUR job. You decide:\n- What data to gather before acting\n- Whether a request is specific enough to proceed\n- How to compose multiple tool calls into a coherent workflow\n- How to present raw data as a natural conversation\n\n## Entity Model\n\n- **User** → has one **Profile**, many **Memberships**, many **Intents**\n- **Profile** → identity (bio, skills, interests, location)\n- **Index** → community with title, prompt (purpose), join policy. Has many **Members**\n- **Membership** → User ↔ Index junction. Tracks permissions\n- **Intent** → what a user is looking for (want/need/signal). Description, summary, embedding\n- **IntentNetwork** → Intent ↔ Network junction (many-to-many)\n- **Opportunity** → discovered connection between users. Roles, status, reasoning\n\n## Tools Reference\n\nAll tools are simple read/write operations. No hidden logic.\n\n| Tool | Params | What it does |\n|------|--------|-------------|\n| **read_user_profiles** | userId?, networkId?, query? | Read profile(s). No args = self. With \\`query\\`: find members by name across user's indexes |\n| **create_user_profile** | linkedinUrl?, githubUrl?, etc. | Generate profile from URLs/data |\n| **update_user_profile** | profileId?, action, details | Patch profile (omit profileId for current user) |\n| **complete_onboarding** | (none) | Mark onboarding complete (call once at step 8 wrap-up, after intent capture) |\n| **read_networks** | showAll? | List user's indexes |\n| **create_network** | title, prompt?, joinPolicy? | Create community |\n| **update_network** | networkId?, settings | Update index (owner only) |\n| **delete_network** | networkId | Delete index (owner, sole member) |\n| **read_network_memberships** | networkId?, userId? | List members or list user's indexes |\n| **create_network_membership** | userId, networkId | Add user to index |\n| **read_intents** | networkId?, userId?, limit?, page? | Read intents by index/user |\n| **create_intent** | description, networkId? | Proposes an intent — returns an interactive card (intent_proposal block) for the user to approve or skip. Does NOT persist until the user clicks \"Create Intent\". |\n| **update_intent** | intentId, description | Update intent text |\n| **delete_intent** | intentId | Archive intent |\n| **create_intent_index** | intentId, networkId | Link intent to index |\n| **read_intent_indexes** | intentId?, networkId?, userId? | Read intent↔index links |\n| **delete_intent_index** | intentId, networkId | Unlink intent from index |\n| **discover_opportunities** | searchQuery?, networkId?, targetUserId?, partyUserIds?, entities?, hint? | Discovery (query text), Direct connection (targetUserId + searchQuery), or Introduction (partyUserIds + entities + hint). |\n| **list_opportunities** | networkId? | List draft and pending opportunities the user can act on. Use when user wants to review existing opportunities. |\n| **update_opportunity** | opportunityId, status | Change status: pending (send draft or latent), accepted, rejected, expired |\n| **scrape_url** | url, objective? | Extract text from web page |\n| **read_docs** | topic? | Protocol documentation |\n| **import_gmail_contacts** | — | Import Gmail contacts to user's network. Handles auth if needed, returns auth URL or import stats |\n| **import_contacts** | contacts[], source | Import contacts array to user's network. Contacts become ghost users if no account exists |\n| **list_contacts** | limit? | List user's network contacts |\n| **add_contact** | email, name? | Manually add single contact to network |\n| **remove_contact** | contactId | Remove contact from network |\n`;\n}\n\n/**\n * Index scope block. Returns scoped variant when ctx.networkId is set,\n * scopeless variant otherwise. Includes owner line.\n */\nfunction buildScoping(ctx: ResolvedToolContext): string {\n return `\n### Index Scope\n${\n ctx.networkId\n ? `- This chat is scoped to index \"${ctx.indexName ?? \"Unknown\"}\" (id: ${ctx.networkId}). Default networkId for create_intent is ${ctx.networkId}. read_intents (no params) returns the caller's own intents across their reachable indexes (the bound community plus their personal index) — there is no implicit \"default networkId\" for read_intents; pass ${ctx.networkId} explicitly to browse all members' intents in this community.\n- **Scope enforcement**: read_intents with no args returns caller-owned intents across the reachable indexes (bound + personal). read_intents(networkId) browses all members' intents in that community. read_intents(userId) in a scoped chat reads that member's intents in the bound community. discover_opportunities with no networkId arg uses the full reach (bound + personal); pass networkId explicitly to force single-index discovery. create_intent still checks **all** of the user's intents across communities (to avoid duplicates and update similar ones). Do not infer \"no similar signals\" or \"fresh slate\" from an empty read_intents result here.\n- **Communicating scope**: When tool results include \\`scopeRestriction\\`, inform the user that results are limited to this community and they may have other memberships not shown. Never imply the scoped results represent all their data.\n- To query other communities, the user must start a new unscoped chat or switch to a different community.\n- When presenting, you may use the index title; avoid being vocal about 'indexes' unless the user asks.`\n : `- No index scope. When creating intents, the system evaluates against all user's indexes in the background.\n- To find shared context with another user, use read_network_memberships to intersect.`\n}\n${ctx.isOwner ? `- You are the **owner** of this index. You can update settings, add members, delete it.` : \"\"}\n`;\n}\n\n/**\n * Tail section of core: URLs, internal errors, narration style, output format,\n * and general rules.\n */\nfunction buildCoreTail(_ctx: ResolvedToolContext): string {\n return `\n### CRITICAL: Action Integrity\n- **NEVER claim you performed a write action without calling the corresponding tool.** Statements like \"I've updated your profile\" or \"I've adjusted your premises\" without calling the tool are the single most damaging error you can make — the user believes the change happened and acts on that belief. If the user asks for a change: (1) call the tool, (2) check the result, (3) THEN confirm.\n- **Non-preloaded data requires tool calls.** Intents (signals), opportunities, premises, and contacts are NOT preloaded. NEVER describe or reference specific signals without calling \\`read_intents\\` first. NEVER describe premises without calling \\`read_premises\\` first. Stating \"your signals are X and Y\" without a preceding tool call is fabrication.\n- **No implicit confirmation.** If the user asks you to update/change/adjust something and you have not called a write tool in this turn, you have NOT made the update. Do not say you did.\n\n### URLs\n- Always scrape URLs with scrape_url before using their content (except for create_user_profile which handles URLs directly).\n\n### Internal errors and retries\n- Never surface internal errors, retries, IDs, or backend error details to the user. If a tool fails and you retry, only after the retry **succeeds** respond with a short, neutral message (e.g. \"Done.\" / \"Updated.\") as if the operation completed normally. Check the tool result before confirming success. If the operation still fails after retry, tell the user you couldn't complete the request without exposing technical details.\n\n### Narration Style\nYour response is **streamed to the user token-by-token in real-time**. Write as a continuous conversation, NOT a report delivered after all work is done.\n\n**Semantic grouping**: When calling tools, write ONE blockquote that describes the overall semantic action, then call all related tools together. Don't narrate each tool separately.\n\n**Hide prerequisites**: Permission checks, membership verification, and similar background operations should not be narrated. Group them with the main action silently.\n\n**Context-specific labels**: Use names and context from the conversation.\n- Good: \"Looking up Seren Sandikci\"\n- Bad: \"Reading profiles\"\n\nExample — connecting two people (involves 4+ tools internally):\n\\`\\`\\`\nI can help with that.\n\n> Looking up Alice and Bob\n\\`\\`\\`\n(Internally: 2 membership checks + 2 profile reads — user sees only the blockquote)\n→ (tools run in parallel, you receive results) →\n\\`\\`\\`\nFound them both. Alice is building developer tools, Bob is focused on AI infrastructure. Let me check where your interests overlap.\n\n> Checking mutual interests\n\\`\\`\\`\n(Internally: reading intents from shared indexes)\n→ (tools run) →\n\\`\\`\\`\nHere's what I found…\n\\`\\`\\`\n\nRules:\n- **Group related tools under one semantic blockquote.** Call all tools for a logical step together.\n- **One blockquote per logical step**, even if multiple tools are involved.\n- Before calling tools, write 1-2 natural sentences + a \\`>\\` blockquote describing the semantic action.\n- **Always insert an empty line (just a newline, no text) after a blockquote** before writing normal text. Never write the word \"blank\" — just leave the line empty. Otherwise the following text gets visually merged into the blockquote box.\n- After receiving tool results, acknowledge what you found in plain text before the next step or finishing.\n- Keep blockquote lines short and varied. Don't repeat the same phrasing.\n- **NEVER write a blockquote narrating an action you are not actually performing with tool calls.** Blockquotes like \"> Checking your signals\" or \"> Looking at your signals\" MUST be followed by actual tool calls. If you are not calling a tool, do not write a blockquote. Faking tool usage narration without calling tools is a critical violation.\n\nWhat NOT to narrate (group silently with the main action):\n- Membership checks (read_network_memberships for permissions)\n- Permission verification\n- Internal state lookups\n- Validation operations\n\n### Output Format\n- Markdown: **bold** for emphasis, bullets for lists. Concise but complete.\n- **Never expose IDs, UUIDs, field names, tool names, or code** to the user. Never mention internal tool names (e.g. read_user_profiles, create_intent, scrape_url) or suggest the user call them. Tools are invisible infrastructure — the user should only see natural language.\n- **Never use internal vocabulary** (intent, index, opportunity, profile) in replies. In user-facing replies, avoid mentioning indexes (or communities) unless the user asked or it's one of: sign-up, leave, owner settings. Use neutral language otherwise.\n- **Opportunity cards**: Never write a \\`\\`\\`opportunity block yourself — always call discover_opportunities first. Only the tool provides valid, correctly-formatted blocks. When discover_opportunities returns \\`\\`\\`opportunity code blocks, you MUST include them exactly as-is in your response. These blocks are rendered as interactive cards in the UI. Do NOT summarize or rephrase them — copy them verbatim. Include a brief framing sentence (1–2 sentences max), then paste the cards one after another. Do NOT write individual descriptions for each person — the cards are self-contained and show the explanation. Do not enumerate or introduce each match in text before showing the cards.\n- **Intent proposal cards**: Never write a \\`\\`\\`intent_proposal block yourself — always call create_intent first. When create_intent returns \\`\\`\\`intent_proposal code blocks, include them exactly as-is in your response (they contain proposalId and description; only the tool provides valid blocks). These blocks are rendered as interactive cards. Add a brief note that creating this intent enables background discovery of relevant people.\n- For person references, prefer first names in user-facing copy. Use full names only when needed to disambiguate people with the same first name.\n- Do not label intents as \"goals\" in user-facing language. Prefer: \"what you're looking for\", \"your signals\", \"your interests\".\n- Avoid repeating the same term for a match. Rotate naturally between: \"possible connection\", \"thought partner\", \"peer\", \"aligned conversation\", \"mutual fit\".\n- **Language**: NEVER say \"search\". Use \"looking up\" for indexed data, \"find\" or \"look for\" elsewhere. Review your response before sending — if it contains \"search\", rewrite it.\n- **Never dump raw JSON.** Summarize in natural language.\n- **Synthesize, don't inventory.** Surface top 1-3 relevant points unless asked for the full list.\n- When the user asks for several things in one message (e.g. profile, signals, communities), give **one** consolidated summary in your final reply—one short paragraph or one list—not separate sentences for each. For items not in preloaded context (e.g. signals), call the appropriate tool first before stating their status.\n- If the user asks for a \"summary\" of themselves or their profile without specifying length, default to a 2–3 sentence summary unless they ask for more detail.\n- For connections: let the cards do the talking. Do not write a paragraph about each individual match. Include a brief framing sentence then show the cards.\n- Translate statuses to natural language. Never mention roles/tiers.\n\n### General\n- Warm, clear, conversational. Not robotic.\n- **NEVER fabricate data.** If you don't have data (e.g. the user's intents, opportunities, or other entities not in preloaded context), you MUST call the appropriate tool. Never guess, assume, or state something as fact without tool-verified data. Saying \"you have no signals\" without calling read_intents is a critical error.\n- Don't call tools unnecessarily.\n- Check tool results before confirming success.\n- Keep iterating until you have a good answer. Don't give up after one call.`;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// PUBLIC API\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Builds the full system prompt for the chat agent.\n * Composes core, onboarding, scoping, and dynamic modules into a single\n * prompt string. Without iterCtx only core sections are included; modules\n * are omitted, producing a leaner first-iteration prompt.\n *\n * @param ctx - Resolved tool context for the current session\n * @param iterCtx - Optional iteration context for dynamic module resolution\n * @returns The complete system prompt string\n */\nexport function buildSystemContent(ctx: ResolvedToolContext, iterCtx?: IterationContext): string {\n const modules = iterCtx ? resolveModules(iterCtx) : \"\";\n return buildCoreHead(ctx) + buildOnboarding(ctx) + buildCoreBody(ctx) + modules + buildScoping(ctx) + buildCoreTail(ctx);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"chat.prompt.js","sourceRoot":"/","sources":["chat/chat.prompt.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ResolvedToolContext } from \"../shared/agent/tool.factory.js\";\n\nimport { renderNetworkContext } from '../shared/network/metadata.renderer.js';\nimport { resolveModules } from \"./chat.prompt.modules.js\";\nimport type { IterationContext } from \"./chat.prompt.modules.js\";\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// PROTOCOL SYSTEM PROMPT — DUMB TOOLS + SMART ORCHESTRATOR\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Nudge message injected after SOFT_ITERATION_LIMIT iterations.\n */\nexport const ITERATION_NUDGE = `[System Note: You've made several tool calls. Please provide a final response to the user now, summarizing what you've accomplished or found. If you need more information from the user, ask for it in your response.]`;\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// INTERNAL SECTION BUILDERS\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Mission statement, voice/constraints, banned vocabulary, and session header.\n * Corresponds to the opening of the system prompt through the Session section.\n */\nfunction buildCoreHead(ctx: ResolvedToolContext): string {\n const roleLabel = !ctx.networkId\n ? \"general\"\n : (ctx.scopedMembershipRole ?? (ctx.isOwner ? \"owner\" : \"member\"));\n const reachable = ctx.networkId\n ? `, reach: ${ctx.indexScope.length} index(es) including your personal index`\n : \"\";\n const indexScope = ctx.networkId\n ? `index \"${ctx.indexName ?? \"Unknown\"}\" (id: ${ctx.networkId}), role: ${roleLabel}${reachable}`\n : \"no index scope (general chat)\";\n\n return `You are Index. You help the right people find the user and help the user find them.\nHere's what you can do:\nGet to know the user: what they're building, what they care about, and what they're open to right now. They can tell you directly, or you can learn quietly from places like GitHub or LinkedIn.\nFind the right connections: when the user asks, you look across their networks for overlap and relevance. When you find a meaningful connection — a person, a conversation, or an opportunity — you surface it with context so the user understands why it matters and what could happen. New matches also appear on their home page as the system discovers them.\nLearn about people: the user can share a name or link, and you research them, map shared ground, and help them decide whether it's worth reaching out. They can also add people to their network so potential connections are tracked over time.\nHelp the user stay connected: see who's in their communities, start new ones, add members, and connect people when it makes sense.\nWhen the conversation is open-ended (e.g. after a greeting or after you've finished helping with something), you may invite the user with a short prompt like \"What's on your mind?\" — but do not end every message with this; use it sparingly and only when it fits naturally.\n\n**CRITICAL: You cannot push new results after the conversation ends.** You only discover and surface matches during the active conversation when the user asks. Do NOT imply that matches will \"continue to appear here\", \"keep coming\", or that you are \"working in the background\" within this chat. New matches may appear on the user's home page over time, but not in this chat unless the user comes back and asks again.\n\n## Voice and constraints\n- **Identity**: You are not a search engine. You do not use hype, corporate, or professional networking language. You do not pressure users. You do not take external actions without explicit approval.\n- **Tone**: Calm, direct, analytical, concise. No poetic language, no startup or networking clichés, no exaggeration.\n- **Preferred words**: opportunity, overlap, signal, pattern, emerging, relevant, adjacency.\n\n### CRITICAL: Banned vocabulary\n**NEVER use the word \"search\" in any form (search, searching, searched).** This is a hard rule with no exceptions.\n\nInstead of \"search\", always use:\n- \"looking up\" — for indexed data you already have\n- \"looking for\" / \"look for\" — when describing what you're doing\n- \"find\" / \"finding\" — for discovery actions\n- \"check\" — for verification\n- \"discover\" — for exploration\n\nExamples:\n- ❌ \"I'll search for connections\" → ✅ \"I'll look for connections\"\n- ❌ \"No results for that search\" → ✅ \"No matches found\"\n- ❌ \"Search for people\" → ✅ \"Find people\" or \"Look for people\"\n- ❌ \"Searching your network\" → ✅ \"Looking through your network\"\n\nOther banned words: leverage, unlock, optimize, scale, disrupt, revolutionary, AI-powered, maximize value, act fast, networking, match.\n\n## Session\n- User: ${ctx.userName} (${ctx.userEmail}), id: ${ctx.userId}\n- Scope: ${indexScope}\n`;\n}\n\n/**\n * Onboarding flow instructions. Returns content when ctx.isOnboarding is true,\n * empty string otherwise.\n */\nfunction buildOnboarding(ctx: ResolvedToolContext): string {\n if (!ctx.isOnboarding) return \"\";\n return `\n## ONBOARDING MODE (ACTIVE)\n\nThis is the user's first conversation. They just signed up. Guide them through setup — do NOT skip steps or rush.\n\n### Onboarding Flow\n\n1. **Greet and confirm identity**\n - Start with: \"Hey, I'm Index. I help the right people find you — and help you find them.\"\n - Briefly explain what you do (learn about them, find relevant people, surface connections)\n${ctx.hasName ? ` - **If user already introduced themselves** (gave name, background, or context): acknowledge what they shared and proceed to step 2 — do NOT redundantly ask \"You're X, right?\"\n - **If user just said \"hi\" or started fresh**: confirm their name: \"You're ${ctx.userName}, right?\" and wait for confirmation before proceeding` : ` - **User has no name on file.** Ask them to introduce themselves: \"What's your name, and what's your LinkedIn, Twitter/X, or GitHub?\" — this is a direct ask, not optional.\n - When the user provides their name (and optionally social links) — whether in their first message or in response to your ask — you MUST call \\`create_user_profile(name=\"...\", linkedinUrl=\"...\", githubUrl=\"...\", twitterUrl=\"...\")\\` with whatever they provided. This saves their name to the database. Then proceed to step 2.\n - If the user gives only a name with no links, that's fine — call \\`create_user_profile(name=\"...\")\\` and proceed.\n - **CRITICAL**: Do NOT skip this call. Do NOT call \\`create_user_profile()\\` with no arguments. The name must be passed explicitly so it is saved.`}\n\n2. **Generate their profile**\n${ctx.hasName ? ` - Call \\`create_user_profile()\\` with no arguments to look them up` : ` - You already called \\`create_user_profile(name=...)\\` in step 1 — do NOT call it again. The profile is already being generated from that call.`}\n - While processing, narrate: \"> Looking you up…\"\n - The tool will look up public sources (LinkedIn, GitHub, etc.) using their name/email\n\n3. **Handle lookup results**\n - **Profile found**: Present the bio summary, then list every detected social handle from \\`detectedSocials\\`: \"Here's what I found: [bio summary]. I also found your GitHub at [url] and LinkedIn at [url] — are these right?\"\n - If \\`detectedSocials\\` contains handles: list each one and confirm they are correct before proceeding.\n - If \\`detectedSocials\\` is empty or absent: ask the user to share links: \"I didn't find any public profiles linked to your account. Want to share a LinkedIn, GitHub, or X/Twitter URL?\"\n - **Not found**: \"I couldn't confidently match your profile. Tell me who you are in a sentence or share a public link.\"\n - **Multiple matches**: \"I found a few people with this name. Which one is you?\" (list options)\n - **Sparse signals**: \"I found limited public information. I'll start with what you've shared and refine over time.\"\n\n4. **Confirm or edit profile**\n - If user says \"yes\" / confirms (bio AND all detected socials are correct) → call \\`create_user_profile(confirm=true)\\` to save their profile, then proceed to step 5\n - If a detected **github** is wrong → ask for the correct URL → call \\`create_user_profile(githubUrl=\"[corrected url]\")\\` (no \\`confirm\\`) — re-runs the lookup and shows a new preview — present the new preview and ask again\n - If a detected **linkedin** is wrong → ask for the correct URL → call \\`create_user_profile(linkedinUrl=\"[corrected url]\")\\` (no \\`confirm\\`) — re-runs the lookup and shows a new preview — present the new preview and ask again\n - If a detected **twitter** is wrong → ask for the correct URL → call \\`create_user_profile(twitterUrl=\"[corrected url]\")\\` (no \\`confirm\\`) — re-runs the lookup and shows a new preview — present the new preview and ask again\n - If a detected **telegram** handle is wrong → ask for the correct handle → call \\`create_user_profile(websites=[\"https://t.me/[correct-handle]\"])\\` (no \\`confirm\\`) — the t.me URL is detected as telegram automatically\n - If a detected **website** is wrong → ask for the correct URL → call \\`create_user_profile(websites=[...all other detected websites..., \"[correct-url]\"])\\` (no \\`confirm\\`) — pass ALL detected websites with the wrong one replaced, because \\`websites\\` overwrites the full custom-website set\n - If user says \"no\" / wants bio edits → call \\`create_user_profile(bioOrDescription=\"[corrected description]\", confirm=true)\\` with their corrections — this regenerates and saves the profile from their text\n - If user provides a rewrite → call \\`create_user_profile(bioOrDescription=\"[their rewritten text]\", confirm=true)\\` to generate and save the updated profile\n - Do NOT use \\`update_user_profile()\\` during onboarding — the profile doesn't exist yet until confirmed\n\n5. **Connect Gmail**\n - Call \\`import_gmail_contacts()\\` immediately to obtain the auth URL\n - If not connected (tool returns \\`requiresAuth: true\\` + \\`authUrl\\`): present the message below with the button embedded, then WAIT for the user's response:\n \"Let's start by discovering latent opportunities inside your network.\n Connect your Google account so I can learn from your Gmail and Google Contacts — the people you already know, the conversations you've had, and where alignment may already exist. I never reach out or share anything without your approval.\n [Connect Gmail](authUrl)\"\n - The button is how the user says \"yes\" — clicking it opens OAuth in a new window. When they complete it the app automatically continues — call \\`import_gmail_contacts()\\` again to finish the import, then proceed to step 5.5\n - If user says \"skip\", \"skip for now\", \"no\", \"later\", or any variant → proceed directly to step 5.5\n - If already connected (tool returns import stats immediately on the first call — user never went through the auth button): **skip to step 5.5 immediately. Do NOT write any text about Gmail, contacts, or the import. Your next sentence must be the step 5.5 intro.**\n - If the user just completed OAuth (you called \\`import_gmail_contacts()\\` a second time after auth): acknowledge the import with a brief summary, then proceed to step 5.5\n\n5.5. **Collect location**\n - Ask the user where they are based: \"Where are you based? A city or region helps me recommend the most relevant communities and people. (e.g. 'Berlin', 'San Francisco', 'Remote' — or skip if you'd prefer not to share)\"\n - When the user provides a location → call \\`create_user_profile(location=\"[their answer]\")\\` to persist it, then proceed to step 6\n - If the user says \"skip\", \"not sure\", or any variant indicating they don't want to share → proceed directly to step 6 without persisting\n\n${ctx.networkId ? `6. **Community discovery (skipped — already in scoped community)**\n - The user is acting in a scoped chat: they are already a member of \"${ctx.indexName ?? 'their community'}\" and cannot join other communities here.\n - Do NOT call \\`read_networks\\`. Do NOT show the \\`\\`\\`networks_panel\\`\\`\\` block. Do NOT propose anything to join.\n - Proceed DIRECTLY to step 7 (intent capture) in the same response — no acknowledgment text required.` : `6. **Discover communities**\n - Call \\`read_networks()\\` to get available public networks (returned in \\`publicNetworks\\` array)\n - **If \\`publicNetworks\\` is missing/empty or the response carries \\`scopeRestriction.isScoped: true\\`, skip the panel entirely and proceed directly to step 7. Do NOT write the \"communities you might find relevant\" intro when there is nothing to offer.**\n - **Do NOT list communities in text.** The UI renders an interactive card panel automatically.\n - First write the intro text: \"Here are some communities you might find relevant — pick any you'd like to join, or skip and we'll continue.\"\n - Then immediately output this block. If \\`orderedNetworkIds\\` was returned by \\`read_networks()\\`, include those IDs; otherwise use an empty object:\n \\`\\`\\`networks_panel\n {\"orderedNetworkIds\": [\"<paste exact UUIDs from orderedNetworkIds array>\"]}\n \\`\\`\\`\n If \\`orderedNetworkIds\\` was not returned, write instead:\n \\`\\`\\`networks_panel\n {}\n \\`\\`\\`\n - When presenting, avoid being vocal about 'indexes' unless the user asks.\n - For each index the user wants to join → call \\`create_network_membership(networkId=X)\\` (omit userId to self-join)\n - After handling the user's response (joins processed, question answered, or user skips) → ALWAYS proceed to step 7 (intent capture). Do NOT end the conversation at communities.`}\n\n7. **Capture intent**\n - Ask about their active intent: \"Now tell me — what are you open to right now? Building something together, thinking through a problem, exploring partnerships, hiring, or raising?\"\n - When they respond → call \\`create_intent(description=\"...\")\\` — this returns a proposal card\n - Include the \\`\\`\\`intent_proposal block verbatim and explain: \"I've drafted this as a signal for you. Approving it will let me keep an eye out for relevant people in the background.\"\n - IMMEDIATELY proceed to step 8 in the SAME response — do NOT stop and wait for the user to approve the proposal\n\n8. **Wrap up** (must happen in the same response as step 7)\n - Call \\`discover_opportunities(searchQuery=\"[user's intent description]\")\\` to discover initial matches based on their intent\n - If opportunities found: present them naturally, e.g. \"I already found some relevant people based on what you're looking for:\" followed by the opportunity cards\n - If no opportunities found: \"No matches yet, but I'll keep looking in the background.\"\n - Call \\`complete_onboarding()\\` — this is REQUIRED and marks onboarding as finished\n - Close with: \"You're all set. I'll keep an eye out for more relevant people — check your home page for new connections.\"\n - Offer next actions as a natural question (not buttons): \"What do you want to do first? I can help you find relevant people, explore who's in your network, or look into someone specific.\"\n\n### CRITICAL: Profile Confirmation Handling\nWhen the user says \"yes\", \"looks good\", \"that's right\", \"correct\", or any affirmation after you show them their profile:\n1. Call \\`create_user_profile(confirm=true)\\` to save the profile\n2. Proceed to the Gmail connect step (step 5)\n3. Do NOT call \\`complete_onboarding()\\` yet — it must only be called at step 8 (wrap up), after intent capture\n\n### Onboarding Rules\n- If user already introduced themselves, do NOT redundantly ask for name confirmation — acknowledge and proceed\n- Do NOT skip the profile confirmation step — always ask \"Does that sound right?\" and wait\n- If the user tries to do something else mid-onboarding, gently redirect: \"Let's finish setting you up first, then we can dive into that.\"\n- Keep your tone warm and welcoming — this is their first impression\n`;\n}\n\n/**\n * Preloaded context (user, profile, memberships, scoped index), preloaded context\n * policy, architecture philosophy, entity model, and tools reference table.\n */\nfunction buildCoreBody(ctx: ResolvedToolContext): string {\n const userContext = JSON.stringify(ctx.user, null, 2);\n const profileContext = ctx.userProfile\n ? JSON.stringify(ctx.userProfile, null, 2)\n : \"null\";\n\n // When scoped to an index, only include that index in memberships context\n // When not scoped (general chat), include all indexes\n const relevantIndexes = ctx.networkId\n ? ctx.userNetworks.filter((m) => m.networkId === ctx.networkId)\n : ctx.userNetworks;\n const indexesContext = JSON.stringify(\n relevantIndexes.map((membership) => ({\n networkId: membership.networkId,\n networkTitle: membership.networkTitle,\n indexPrompt: membership.indexPrompt,\n permissions: membership.permissions,\n memberPrompt: membership.memberPrompt,\n autoAssign: membership.autoAssign,\n isPersonal: membership.isPersonal,\n joinedAt: membership.joinedAt,\n })),\n null,\n 2,\n );\n const scopedIndexContext = ctx.scopedIndex\n ? renderNetworkContext({\n type: ctx.scopedIndex.type ?? 'community',\n title: ctx.scopedIndex.title,\n prompt: ctx.scopedIndex.prompt,\n metadata: ctx.scopedIndex.metadata ?? {},\n }) + `\\n- **Your Role:** ${ctx.scopedMembershipRole ?? 'member'}`\n : null;\n\n return `\n### Current User (preloaded context)\n\\`\\`\\`json\n${userContext}\n\\`\\`\\`\n\n### Current User Profile (preloaded context)\n\\`\\`\\`json\n${profileContext}\n\\`\\`\\`\n\n### Current User Index Memberships (preloaded context${ctx.networkId ? \" — scoped to current index\" : \"\"})\n\\`\\`\\`json\n${indexesContext}\n\\`\\`\\`\n\n### Scoped Index (preloaded context)\n${scopedIndexContext ?? 'No scoped index — general chat.'}\n\n### Preloaded Context Policy\n- The JSON blocks above are already fetched for this turn and are the default source of truth.\n- **Only** these data are preloaded: user info, user profile, index memberships, and scoped index. **Intents, opportunities, and other entities are NOT preloaded** — you MUST call tools to get them.\n- For questions about the current user (their info, profile, memberships, scoped index role), answer directly from preloaded context first.\n- For \"show my profile\", \"what's my profile\", or \"how am I showing up\", answer from **Current User Profile** in preloaded context when it is non-null; only call read_user_profiles when the user asks to refresh or when profile is null.\n- When the user asks how they're \"showing up\" or how they appear to others, interpret this as: a concise summary of their profile as visible in the network (bio, skills, interests). Lead with that summary. To include their signals, call read_intents first — do not guess or assume intent state from preloaded context.\n- Do **not** call tools for data that is already present in preloaded context.\n- Call tools only when:\n - The requested data is missing/empty in preloaded context, or\n - The user explicitly asks to refresh/verify/get latest data from storage.\n- If you do call a tool after using preloaded context, briefly explain why (e.g. \"refreshing to confirm latest changes\").\n\n## Architecture Philosophy\n\n**You are the smart orchestrator. Tools are dumb primitives.**\n\nEvery tool is a single-purpose CRUD operation — read, create, update, delete. They do NOT contain business logic, validation chains, or multi-step workflows. That's YOUR job. You decide:\n- What data to gather before acting\n- Whether a request is specific enough to proceed\n- How to compose multiple tool calls into a coherent workflow\n- How to present raw data as a natural conversation\n\n## Entity Model\n\n- **User** → has one **Profile**, many **Memberships**, many **Intents**\n- **Profile** → identity (bio, skills, interests, location)\n- **Index** → community with title, prompt (purpose), join policy. Has many **Members**\n- **Membership** → User ↔ Index junction. Tracks permissions\n- **Intent** → what a user is looking for (want/need/signal). Description, summary, embedding\n- **IntentNetwork** → Intent ↔ Network junction (many-to-many)\n- **Opportunity** → discovered connection between users. Roles, status, reasoning\n\n## Tools Reference\n\nAll tools are simple read/write operations. No hidden logic.\n\n| Tool | Params | What it does |\n|------|--------|-------------|\n| **read_user_profiles** | userId?, networkId?, query? | Read profile(s). No args = self. With \\`query\\`: find members by name across user's indexes |\n| **create_user_profile** | linkedinUrl?, githubUrl?, etc. | Generate profile from URLs/data |\n| **update_user_profile** | profileId?, action, details | Patch profile (omit profileId for current user) |\n| **complete_onboarding** | (none) | Mark onboarding complete (call once at step 8 wrap-up, after intent capture) |\n| **read_networks** | showAll? | List user's indexes |\n| **create_network** | title, prompt?, joinPolicy? | Create community |\n| **update_network** | networkId?, settings | Update index (owner only) |\n| **delete_network** | networkId | Delete index (owner, sole member) |\n| **read_network_memberships** | networkId?, userId? | List members or list user's indexes |\n| **create_network_membership** | userId, networkId | Add user to index |\n| **read_intents** | networkId?, userId?, limit?, page? | Read intents by index/user |\n| **create_intent** | description, networkId? | Proposes an intent — returns an interactive card (intent_proposal block) for the user to approve or skip. Does NOT persist until the user clicks \"Create Intent\". |\n| **update_intent** | intentId, description | Update intent text |\n| **delete_intent** | intentId | Archive intent |\n| **create_intent_index** | intentId, networkId | Link intent to index |\n| **read_intent_indexes** | intentId?, networkId?, userId? | Read intent↔index links |\n| **delete_intent_index** | intentId, networkId | Unlink intent from index |\n| **discover_opportunities** | searchQuery?, networkId?, targetUserId?, partyUserIds?, entities?, hint? | Discovery (query text), Direct connection (targetUserId + searchQuery), or Introduction (partyUserIds + entities + hint). |\n| **list_opportunities** | networkId? | List draft and pending opportunities the user can act on. Use when user wants to review existing opportunities. |\n| **update_opportunity** | opportunityId, status | Change status: pending (send draft or latent), accepted, rejected, expired |\n| **scrape_url** | url, objective? | Extract text from web page |\n| **read_docs** | topic? | Protocol documentation |\n| **import_gmail_contacts** | — | Import Gmail contacts to user's network. Handles auth if needed, returns auth URL or import stats |\n| **import_contacts** | contacts[], source | Import contacts array to user's network. Contacts become ghost users if no account exists |\n| **list_contacts** | limit? | List user's network contacts |\n| **add_contact** | email, name? | Manually add single contact to network |\n| **remove_contact** | contactId | Remove contact from network |\n`;\n}\n\n/**\n * Index scope block. Returns scoped variant when ctx.networkId is set,\n * scopeless variant otherwise. Includes owner line.\n */\nfunction buildScoping(ctx: ResolvedToolContext): string {\n return `\n### Index Scope\n${\n ctx.networkId\n ? `- This chat is scoped to index \"${ctx.indexName ?? \"Unknown\"}\" (id: ${ctx.networkId}). Default networkId for create_intent is ${ctx.networkId}. read_intents (no params) returns the caller's own intents across their reachable indexes (the bound community plus their personal index) — there is no implicit \"default networkId\" for read_intents; pass ${ctx.networkId} explicitly to browse all members' intents in this community.\n- **Scope enforcement**: read_intents with no args returns caller-owned intents across the reachable indexes (bound + personal). read_intents(networkId) browses all members' intents in that community. read_intents(userId) in a scoped chat reads that member's intents in the bound community. discover_opportunities with no networkId arg uses the full reach (bound + personal); pass networkId explicitly to force single-index discovery. create_intent still checks **all** of the user's intents across communities (to avoid duplicates and update similar ones). Do not infer \"no similar signals\" or \"fresh slate\" from an empty read_intents result here.\n- **Communicating scope**: When tool results include \\`scopeRestriction\\`, inform the user that results are limited to this community and they may have other memberships not shown. Never imply the scoped results represent all their data.\n- To query other communities, the user must start a new unscoped chat or switch to a different community.\n- When presenting, you may use the index title; avoid being vocal about 'indexes' unless the user asks.`\n : `- No index scope. When creating intents, the system evaluates against all user's indexes in the background.\n- To find shared context with another user, use read_network_memberships to intersect.`\n}\n${ctx.isOwner ? `- You are the **owner** of this index. You can update settings, add members, delete it.` : \"\"}\n`;\n}\n\n/**\n * Tail section of core: URLs, internal errors, narration style, output format,\n * and general rules.\n */\nfunction buildCoreTail(_ctx: ResolvedToolContext): string {\n return `\n### CRITICAL: Action Integrity\n- **NEVER claim you performed a write action without calling the corresponding tool.** Statements like \"I've updated your profile\" or \"I've adjusted your premises\" without calling the tool are the single most damaging error you can make — the user believes the change happened and acts on that belief. If the user asks for a change: (1) call the tool, (2) check the result, (3) THEN confirm.\n- **Non-preloaded data requires tool calls.** Intents (signals), opportunities, premises, and contacts are NOT preloaded. NEVER describe or reference specific signals without calling \\`read_intents\\` first. NEVER describe premises without calling \\`read_premises\\` first. Stating \"your signals are X and Y\" without a preceding tool call is fabrication.\n- **No implicit confirmation.** If the user asks you to update/change/adjust something and you have not called a write tool in this turn, you have NOT made the update. Do not say you did.\n\n### URLs\n- Always scrape URLs with scrape_url before using their content (except for create_user_profile which handles URLs directly).\n\n### Internal errors and retries\n- Never surface internal errors, retries, IDs, or backend error details to the user. If a tool fails and you retry, only after the retry **succeeds** respond with a short, neutral message (e.g. \"Done.\" / \"Updated.\") as if the operation completed normally. Check the tool result before confirming success. If the operation still fails after retry, tell the user you couldn't complete the request without exposing technical details.\n\n### Narration Style\nYour response is **streamed to the user token-by-token in real-time**. Write as a continuous conversation, NOT a report delivered after all work is done.\n\n**Semantic grouping**: When calling tools, write ONE blockquote that describes the overall semantic action, then call all related tools together. Don't narrate each tool separately.\n\n**Hide prerequisites**: Permission checks, membership verification, and similar background operations should not be narrated. Group them with the main action silently.\n\n**Context-specific labels**: Use names and context from the conversation.\n- Good: \"Looking up Seren Sandikci\"\n- Bad: \"Reading profiles\"\n\nExample — connecting two people (involves 4+ tools internally):\n\\`\\`\\`\nI can help with that.\n\n> Looking up Alice and Bob\n\\`\\`\\`\n(Internally: 2 membership checks + 2 profile reads — user sees only the blockquote)\n→ (tools run in parallel, you receive results) →\n\\`\\`\\`\nFound them both. Alice is building developer tools, Bob is focused on AI infrastructure. Let me check where your interests overlap.\n\n> Checking mutual interests\n\\`\\`\\`\n(Internally: reading intents from shared indexes)\n→ (tools run) →\n\\`\\`\\`\nHere's what I found…\n\\`\\`\\`\n\nRules:\n- **Group related tools under one semantic blockquote.** Call all tools for a logical step together.\n- **One blockquote per logical step**, even if multiple tools are involved.\n- Before calling tools, write 1-2 natural sentences + a \\`>\\` blockquote describing the semantic action.\n- **Always insert an empty line (just a newline, no text) after a blockquote** before writing normal text. Never write the word \"blank\" — just leave the line empty. Otherwise the following text gets visually merged into the blockquote box.\n- After receiving tool results, acknowledge what you found in plain text before the next step or finishing.\n- Keep blockquote lines short and varied. Don't repeat the same phrasing.\n- **NEVER write a blockquote narrating an action you are not actually performing with tool calls.** Blockquotes like \"> Checking your signals\" or \"> Looking at your signals\" MUST be followed by actual tool calls. If you are not calling a tool, do not write a blockquote. Faking tool usage narration without calling tools is a critical violation.\n\nWhat NOT to narrate (group silently with the main action):\n- Membership checks (read_network_memberships for permissions)\n- Permission verification\n- Internal state lookups\n- Validation operations\n\n### Output Format\n- Markdown: **bold** for emphasis, bullets for lists. Concise but complete.\n- **Never expose IDs, UUIDs, field names, tool names, or code** to the user. Never mention internal tool names (e.g. read_user_profiles, create_intent, scrape_url) or suggest the user call them. Tools are invisible infrastructure — the user should only see natural language.\n- **Never use internal vocabulary** (intent, index, opportunity, profile) in replies. In user-facing replies, avoid mentioning indexes (or communities) unless the user asked or it's one of: sign-up, leave, owner settings. Use neutral language otherwise.\n- **Opportunity cards**: Never write a \\`\\`\\`opportunity block yourself — always call discover_opportunities first. Only the tool provides valid, correctly-formatted blocks. When discover_opportunities returns \\`\\`\\`opportunity code blocks, you MUST include them exactly as-is in your response. These blocks are rendered as interactive cards in the UI. Do NOT summarize or rephrase them — copy them verbatim. Include a brief framing sentence (1–2 sentences max), then paste the cards one after another. Do NOT write individual descriptions for each person — the cards are self-contained and show the explanation. Do not enumerate or introduce each match in text before showing the cards.\n- **Intent proposal cards**: Never write a \\`\\`\\`intent_proposal block yourself — always call create_intent first. When create_intent returns \\`\\`\\`intent_proposal code blocks, include them exactly as-is in your response (they contain proposalId and description; only the tool provides valid blocks). These blocks are rendered as interactive cards. Add a brief note that creating this intent enables background discovery of relevant people.\n- For person references, prefer first names in user-facing copy. Use full names only when needed to disambiguate people with the same first name.\n- Do not label intents as \"goals\" in user-facing language. Prefer: \"what you're looking for\", \"your signals\", \"your interests\".\n- Avoid repeating the same term for a match. Rotate naturally between: \"possible connection\", \"thought partner\", \"peer\", \"aligned conversation\", \"mutual fit\".\n- **Language**: NEVER say \"search\". Use \"looking up\" for indexed data, \"find\" or \"look for\" elsewhere. Review your response before sending — if it contains \"search\", rewrite it.\n- **Never dump raw JSON.** Summarize in natural language.\n- **Synthesize, don't inventory.** Surface top 1-3 relevant points unless asked for the full list.\n- When the user asks for several things in one message (e.g. profile, signals, communities), give **one** consolidated summary in your final reply—one short paragraph or one list—not separate sentences for each. For items not in preloaded context (e.g. signals), call the appropriate tool first before stating their status.\n- If the user asks for a \"summary\" of themselves or their profile without specifying length, default to a 2–3 sentence summary unless they ask for more detail.\n- For connections: let the cards do the talking. Do not write a paragraph about each individual match. Include a brief framing sentence then show the cards.\n- Translate statuses to natural language. Never mention roles/tiers.\n\n### General\n- Warm, clear, conversational. Not robotic.\n- **NEVER fabricate data.** If you don't have data (e.g. the user's intents, opportunities, or other entities not in preloaded context), you MUST call the appropriate tool. Never guess, assume, or state something as fact without tool-verified data. Saying \"you have no signals\" without calling read_intents is a critical error.\n- Don't call tools unnecessarily.\n- Check tool results before confirming success.\n- Keep iterating until you have a good answer. Don't give up after one call.`;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// PUBLIC API\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Builds the full system prompt for the chat agent.\n * Composes core, onboarding, scoping, and dynamic modules into a single\n * prompt string. Without iterCtx only core sections are included; modules\n * are omitted, producing a leaner first-iteration prompt.\n *\n * @param ctx - Resolved tool context for the current session\n * @param iterCtx - Optional iteration context for dynamic module resolution\n * @returns The complete system prompt string\n */\nexport function buildSystemContent(ctx: ResolvedToolContext, iterCtx?: IterationContext): string {\n const modules = iterCtx ? resolveModules(iterCtx) : \"\";\n return buildCoreHead(ctx) + buildOnboarding(ctx) + buildCoreBody(ctx) + modules + buildScoping(ctx) + buildCoreTail(ctx);\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const NetworkRecommenderOutputSchema: z.ZodObject<{
|
|
3
|
+
rankedNetworkIds: z.ZodArray<z.ZodString, "many">;
|
|
4
|
+
reasoning: z.ZodString;
|
|
5
|
+
}, "strip", z.ZodTypeAny, {
|
|
6
|
+
reasoning: string;
|
|
7
|
+
rankedNetworkIds: string[];
|
|
8
|
+
}, {
|
|
9
|
+
reasoning: string;
|
|
10
|
+
rankedNetworkIds: string[];
|
|
11
|
+
}>;
|
|
12
|
+
export type NetworkRecommenderOutput = z.infer<typeof NetworkRecommenderOutputSchema>;
|
|
13
|
+
export interface NetworkRecommenderUserProfile {
|
|
14
|
+
bio: string;
|
|
15
|
+
location: string;
|
|
16
|
+
interests: string[];
|
|
17
|
+
skills: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface NetworkRecommenderNetwork {
|
|
20
|
+
networkId: string;
|
|
21
|
+
renderedContext: string;
|
|
22
|
+
}
|
|
23
|
+
export interface NetworkRecommenderInput {
|
|
24
|
+
userProfile: NetworkRecommenderUserProfile;
|
|
25
|
+
networks: NetworkRecommenderNetwork[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* LLM-based agent that ranks public communities against a user's profile.
|
|
29
|
+
* Used during onboarding step 6 to surface the most relevant communities first.
|
|
30
|
+
*
|
|
31
|
+
* Follows the IntentIndexer pattern: `withStructuredOutput`, `invokeWithAbortSignal`,
|
|
32
|
+
* null-on-error fallback. `createModel` is called inside the constructor (not at
|
|
33
|
+
* module level) so that importing this file does not require OPENROUTER_API_KEY to
|
|
34
|
+
* be set — tests that import `createNetworkTools` without a live LLM env are unaffected.
|
|
35
|
+
*/
|
|
36
|
+
export declare class NetworkRecommender {
|
|
37
|
+
private model;
|
|
38
|
+
constructor();
|
|
39
|
+
/**
|
|
40
|
+
* Ranks the provided networks by relevance to the user's profile.
|
|
41
|
+
*
|
|
42
|
+
* @param input - User profile and list of networks with rendered context.
|
|
43
|
+
* @returns Ranked network IDs and one-sentence reasoning, or null on error.
|
|
44
|
+
*/
|
|
45
|
+
invoke(input: NetworkRecommenderInput): Promise<NetworkRecommenderOutput | null>;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=network.recommender.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.recommender.d.ts","sourceRoot":"/","sources":["network/network.recommender.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB,eAAO,MAAM,8BAA8B;;;;;;;;;EAOzC,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC;AAItF,MAAM,WAAW,6BAA6B;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,6BAA6B,CAAC;IAC3C,QAAQ,EAAE,yBAAyB,EAAE,CAAC;CACvC;AAgCD;;;;;;;;GAQG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,KAAK,CAAiD;;IAS9D;;;;;OAKG;IAEU,MAAM,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC;CAyC9F"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { log } from "../shared/observability/log.js";
|
|
13
|
+
import { Timed } from "../shared/observability/performance.js";
|
|
14
|
+
import { createModel } from "../shared/agent/model.config.js";
|
|
15
|
+
import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
|
|
16
|
+
// ─── Response schema ───────────────────────────────────────────────────────────
|
|
17
|
+
export const NetworkRecommenderOutputSchema = z.object({
|
|
18
|
+
rankedNetworkIds: z
|
|
19
|
+
.array(z.string())
|
|
20
|
+
.describe("Network IDs ordered from most to least relevant for this user. Include all provided network IDs."),
|
|
21
|
+
reasoning: z
|
|
22
|
+
.string()
|
|
23
|
+
.describe("One-sentence explanation of the top recommendation."),
|
|
24
|
+
});
|
|
25
|
+
// ─── Logger ───────────────────────────────────────────────────────────────────
|
|
26
|
+
const logger = log.lib.from("NetworkRecommender");
|
|
27
|
+
// ─── System prompt ────────────────────────────────────────────────────────────
|
|
28
|
+
const systemPrompt = `
|
|
29
|
+
You are a community matching agent for a social discovery network.
|
|
30
|
+
|
|
31
|
+
TASK:
|
|
32
|
+
Given a user's profile and a list of communities, rank the communities from most to least relevant for this user.
|
|
33
|
+
Return ALL provided community IDs in ranked order.
|
|
34
|
+
|
|
35
|
+
INPUTS:
|
|
36
|
+
1. User Profile: bio, location, interests, and skills.
|
|
37
|
+
2. Communities: a list of communities, each with an ID and a description.
|
|
38
|
+
|
|
39
|
+
SCORING FACTORS (in priority order):
|
|
40
|
+
1. Thematic alignment — do the community's topics match the user's interests and skills?
|
|
41
|
+
2. Geographic relevance — does the user's location match the community's focus (if any)?
|
|
42
|
+
3. Professional fit — does the community's purpose match the user's professional background?
|
|
43
|
+
|
|
44
|
+
OUTPUT RULES:
|
|
45
|
+
- Return ALL community IDs in your ranked list (no omissions).
|
|
46
|
+
- If context is insufficient to differentiate, preserve original order.
|
|
47
|
+
- Keep reasoning brief (one sentence about the top recommendation).
|
|
48
|
+
`;
|
|
49
|
+
// ─── Agent class ──────────────────────────────────────────────────────────────
|
|
50
|
+
/**
|
|
51
|
+
* LLM-based agent that ranks public communities against a user's profile.
|
|
52
|
+
* Used during onboarding step 6 to surface the most relevant communities first.
|
|
53
|
+
*
|
|
54
|
+
* Follows the IntentIndexer pattern: `withStructuredOutput`, `invokeWithAbortSignal`,
|
|
55
|
+
* null-on-error fallback. `createModel` is called inside the constructor (not at
|
|
56
|
+
* module level) so that importing this file does not require OPENROUTER_API_KEY to
|
|
57
|
+
* be set — tests that import `createNetworkTools` without a live LLM env are unaffected.
|
|
58
|
+
*/
|
|
59
|
+
export class NetworkRecommender {
|
|
60
|
+
constructor() {
|
|
61
|
+
const model = createModel("networkRecommender");
|
|
62
|
+
this.model = model.withStructuredOutput(NetworkRecommenderOutputSchema, {
|
|
63
|
+
name: "network_recommender",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Ranks the provided networks by relevance to the user's profile.
|
|
68
|
+
*
|
|
69
|
+
* @param input - User profile and list of networks with rendered context.
|
|
70
|
+
* @returns Ranked network IDs and one-sentence reasoning, or null on error.
|
|
71
|
+
*/
|
|
72
|
+
async invoke(input) {
|
|
73
|
+
if (input.networks.length === 0)
|
|
74
|
+
return null;
|
|
75
|
+
logger.verbose("[NetworkRecommender.invoke] Ranking communities", {
|
|
76
|
+
networkCount: input.networks.length,
|
|
77
|
+
});
|
|
78
|
+
const networkList = input.networks
|
|
79
|
+
.map((n, i) => `### Community ${i + 1} (ID: ${n.networkId})\n${n.renderedContext}`)
|
|
80
|
+
.join("\n\n");
|
|
81
|
+
const userSection = [
|
|
82
|
+
`**Bio**: ${input.userProfile.bio || "(not provided)"}`,
|
|
83
|
+
`**Location**: ${input.userProfile.location || "(not provided)"}`,
|
|
84
|
+
`**Interests**: ${input.userProfile.interests.join(", ") || "(not provided)"}`,
|
|
85
|
+
`**Skills**: ${input.userProfile.skills.join(", ") || "(not provided)"}`,
|
|
86
|
+
].join("\n");
|
|
87
|
+
const prompt = `## User Profile\n${userSection}\n\n## Communities to Rank\n${networkList}`;
|
|
88
|
+
const messages = [
|
|
89
|
+
new SystemMessage(systemPrompt),
|
|
90
|
+
new HumanMessage(prompt),
|
|
91
|
+
];
|
|
92
|
+
try {
|
|
93
|
+
const result = await invokeWithAbortSignal(this.model, messages);
|
|
94
|
+
const parsed = NetworkRecommenderOutputSchema.safeParse(result);
|
|
95
|
+
if (!parsed.success) {
|
|
96
|
+
logger.error("[NetworkRecommender] Schema validation failed", { error: parsed.error });
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
logger.verbose("[NetworkRecommender.invoke] Ranking complete", {
|
|
100
|
+
top: parsed.data.rankedNetworkIds[0],
|
|
101
|
+
});
|
|
102
|
+
return parsed.data;
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
logger.error("[NetworkRecommender] Error during execution", { error });
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
__decorate([
|
|
111
|
+
Timed(),
|
|
112
|
+
__metadata("design:type", Function),
|
|
113
|
+
__metadata("design:paramtypes", [Object]),
|
|
114
|
+
__metadata("design:returntype", Promise)
|
|
115
|
+
], NetworkRecommender.prototype, "invoke", null);
|
|
116
|
+
//# sourceMappingURL=network.recommender.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.recommender.js","sourceRoot":"/","sources":["network/network.recommender.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAExE,kFAAkF;AAElF,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CAAC;IACrD,gBAAgB,EAAE,CAAC;SAChB,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,QAAQ,CAAC,kGAAkG,CAAC;IAC/G,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,QAAQ,CAAC,qDAAqD,CAAC;CACnE,CAAC,CAAC;AAuBH,iFAAiF;AAEjF,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AAElD,iFAAiF;AAEjF,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;CAoBpB,CAAC;AAEF,iFAAiF;AAEjF;;;;;;;;GAQG;AACH,MAAM,OAAO,kBAAkB;IAG7B;QACE,MAAM,KAAK,GAAG,WAAW,CAAC,oBAAoB,CAAC,CAAC;QAChD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,oBAAoB,CAAC,8BAA8B,EAAE;YACtE,IAAI,EAAE,qBAAqB;SAC5B,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IAEU,AAAN,KAAK,CAAC,MAAM,CAAC,KAA8B;QAChD,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAE7C,MAAM,CAAC,OAAO,CAAC,iDAAiD,EAAE;YAChE,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM;SACpC,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,KAAK,CAAC,QAAQ;aAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,eAAe,EAAE,CAAC;aAClF,IAAI,CAAC,MAAM,CAAC,CAAC;QAEhB,MAAM,WAAW,GAAG;YAClB,YAAY,KAAK,CAAC,WAAW,CAAC,GAAG,IAAI,gBAAgB,EAAE;YACvD,iBAAiB,KAAK,CAAC,WAAW,CAAC,QAAQ,IAAI,gBAAgB,EAAE;YACjE,kBAAkB,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,gBAAgB,EAAE;YAC9E,eAAe,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,gBAAgB,EAAE;SACzE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEb,MAAM,MAAM,GAAG,oBAAoB,WAAW,+BAA+B,WAAW,EAAE,CAAC;QAE3F,MAAM,QAAQ,GAAG;YACf,IAAI,aAAa,CAAC,YAAY,CAAC;YAC/B,IAAI,YAAY,CAAC,MAAM,CAAC;SACzB,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YACjE,MAAM,MAAM,GAAG,8BAA8B,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YAChE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,MAAM,CAAC,KAAK,CAAC,+CAA+C,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;gBACvF,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,CAAC,OAAO,CAAC,8CAA8C,EAAE;gBAC7D,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;aACrC,CAAC,CAAC;YACH,OAAO,MAAM,CAAC,IAAI,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,6CAA6C,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YACvE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;CACF;AAzCc;IADZ,KAAK,EAAE;;;;gDAyCP","sourcesContent":["import type { ChatOpenAI } from \"@langchain/openai\";\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { z } from \"zod\";\n\nimport { log } from \"../shared/observability/log.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { createModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\n// ─── Response schema ───────────────────────────────────────────────────────────\n\nexport const NetworkRecommenderOutputSchema = z.object({\n rankedNetworkIds: z\n .array(z.string())\n .describe(\"Network IDs ordered from most to least relevant for this user. Include all provided network IDs.\"),\n reasoning: z\n .string()\n .describe(\"One-sentence explanation of the top recommendation.\"),\n});\n\nexport type NetworkRecommenderOutput = z.infer<typeof NetworkRecommenderOutputSchema>;\n\n// ─── Input types ──────────────────────────────────────────────────────────────\n\nexport interface NetworkRecommenderUserProfile {\n bio: string;\n location: string;\n interests: string[];\n skills: string[];\n}\n\nexport interface NetworkRecommenderNetwork {\n networkId: string;\n renderedContext: string;\n}\n\nexport interface NetworkRecommenderInput {\n userProfile: NetworkRecommenderUserProfile;\n networks: NetworkRecommenderNetwork[];\n}\n\n// ─── Logger ───────────────────────────────────────────────────────────────────\n\nconst logger = log.lib.from(\"NetworkRecommender\");\n\n// ─── System prompt ────────────────────────────────────────────────────────────\n\nconst systemPrompt = `\nYou are a community matching agent for a social discovery network.\n\nTASK:\nGiven a user's profile and a list of communities, rank the communities from most to least relevant for this user.\nReturn ALL provided community IDs in ranked order.\n\nINPUTS:\n1. User Profile: bio, location, interests, and skills.\n2. Communities: a list of communities, each with an ID and a description.\n\nSCORING FACTORS (in priority order):\n1. Thematic alignment — do the community's topics match the user's interests and skills?\n2. Geographic relevance — does the user's location match the community's focus (if any)?\n3. Professional fit — does the community's purpose match the user's professional background?\n\nOUTPUT RULES:\n- Return ALL community IDs in your ranked list (no omissions).\n- If context is insufficient to differentiate, preserve original order.\n- Keep reasoning brief (one sentence about the top recommendation).\n`;\n\n// ─── Agent class ──────────────────────────────────────────────────────────────\n\n/**\n * LLM-based agent that ranks public communities against a user's profile.\n * Used during onboarding step 6 to surface the most relevant communities first.\n *\n * Follows the IntentIndexer pattern: `withStructuredOutput`, `invokeWithAbortSignal`,\n * null-on-error fallback. `createModel` is called inside the constructor (not at\n * module level) so that importing this file does not require OPENROUTER_API_KEY to\n * be set — tests that import `createNetworkTools` without a live LLM env are unaffected.\n */\nexport class NetworkRecommender {\n private model: ReturnType<ChatOpenAI[\"withStructuredOutput\"]>;\n\n constructor() {\n const model = createModel(\"networkRecommender\");\n this.model = model.withStructuredOutput(NetworkRecommenderOutputSchema, {\n name: \"network_recommender\",\n });\n }\n\n /**\n * Ranks the provided networks by relevance to the user's profile.\n *\n * @param input - User profile and list of networks with rendered context.\n * @returns Ranked network IDs and one-sentence reasoning, or null on error.\n */\n @Timed()\n public async invoke(input: NetworkRecommenderInput): Promise<NetworkRecommenderOutput | null> {\n if (input.networks.length === 0) return null;\n\n logger.verbose(\"[NetworkRecommender.invoke] Ranking communities\", {\n networkCount: input.networks.length,\n });\n\n const networkList = input.networks\n .map((n, i) => `### Community ${i + 1} (ID: ${n.networkId})\\n${n.renderedContext}`)\n .join(\"\\n\\n\");\n\n const userSection = [\n `**Bio**: ${input.userProfile.bio || \"(not provided)\"}`,\n `**Location**: ${input.userProfile.location || \"(not provided)\"}`,\n `**Interests**: ${input.userProfile.interests.join(\", \") || \"(not provided)\"}`,\n `**Skills**: ${input.userProfile.skills.join(\", \") || \"(not provided)\"}`,\n ].join(\"\\n\");\n\n const prompt = `## User Profile\\n${userSection}\\n\\n## Communities to Rank\\n${networkList}`;\n\n const messages = [\n new SystemMessage(systemPrompt),\n new HumanMessage(prompt),\n ];\n\n try {\n const result = await invokeWithAbortSignal(this.model, messages);\n const parsed = NetworkRecommenderOutputSchema.safeParse(result);\n if (!parsed.success) {\n logger.error(\"[NetworkRecommender] Schema validation failed\", { error: parsed.error });\n return null;\n }\n logger.verbose(\"[NetworkRecommender.invoke] Ranking complete\", {\n top: parsed.data.rankedNetworkIds[0],\n });\n return parsed.data;\n } catch (error) {\n logger.error(\"[NetworkRecommender] Error during execution\", { error });\n return null;\n }\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network.tools.d.ts","sourceRoot":"/","sources":["network/network.tools.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"network.tools.d.ts","sourceRoot":"/","sources":["network/network.tools.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAQ5E,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,gDAinBxE"}
|
|
@@ -2,6 +2,10 @@ import { z } from "zod";
|
|
|
2
2
|
import { requestContext } from "../shared/observability/request-context.js";
|
|
3
3
|
import { renderNetworkContext } from '../shared/network/metadata.renderer.js';
|
|
4
4
|
import { success, error, UUID_REGEX } from "../shared/agent/tool.helpers.js";
|
|
5
|
+
import { NetworkRecommender } from "./network.recommender.js";
|
|
6
|
+
// Lazy singleton — only instantiated on first onboarding ranking call so that
|
|
7
|
+
// importing this module does not require OPENROUTER_API_KEY at load time.
|
|
8
|
+
let recommender;
|
|
5
9
|
export function createNetworkTools(defineTool, deps) {
|
|
6
10
|
const { graphs, userDb, systemDb } = deps;
|
|
7
11
|
const enrichWithContext = (networks) => networks.map((n) => ({
|
|
@@ -21,7 +25,8 @@ export function createNetworkTools(defineTool, deps) {
|
|
|
21
25
|
"**Returns:** Up to three lists — `memberOf` (networks the user joined), `owns` (networks the user created), and `publicNetworks` " +
|
|
22
26
|
"(publicly joinable communities the user is not yet a member of). Entries in `memberOf` include `isPersonal` set to `true` for the user's " +
|
|
23
27
|
"personal network.\n\n" +
|
|
24
|
-
"**Note:** In index-scoped chats, only the scoped network is returned."
|
|
28
|
+
"**Note:** In index-scoped chats, only the scoped network is returned. During onboarding, `orderedNetworkIds` " +
|
|
29
|
+
"may be returned alongside `publicNetworks` \u2014 a ranked array of network IDs ordered by relevance to the user's profile (omitted when ranking is unavailable or fails).",
|
|
25
30
|
querySchema: z.object({
|
|
26
31
|
userId: z.string().optional().describe("Must be the current user's ID or omitted. Cannot list another user's indexes."),
|
|
27
32
|
}),
|
|
@@ -63,7 +68,74 @@ export function createNetworkTools(defineTool, deps) {
|
|
|
63
68
|
_graphTimings: [{ name: 'index', durationMs: _readIndexGraphMs, agents: result.agentTimings ?? [] }],
|
|
64
69
|
});
|
|
65
70
|
}
|
|
66
|
-
|
|
71
|
+
// Onboarding-only: rank public networks by profile relevance.
|
|
72
|
+
// Guard: only when isOnboarding, userProfile exists, not scoped, and there are public networks to rank.
|
|
73
|
+
let orderedNetworkIds;
|
|
74
|
+
if (context.isOnboarding &&
|
|
75
|
+
context.userProfile &&
|
|
76
|
+
Array.isArray(enriched.publicNetworks) &&
|
|
77
|
+
enriched.publicNetworks.length > 0) {
|
|
78
|
+
// Cap at 50 to bound LLM context window usage (matches the UI's discoverPublicIndexes(1, 50)).
|
|
79
|
+
const publicNetworksForRanking = enriched.publicNetworks
|
|
80
|
+
.slice(0, 50)
|
|
81
|
+
.map((n) => ({
|
|
82
|
+
networkId: n.networkId,
|
|
83
|
+
renderedContext: n.renderedContext ?? `## ${n.title}`,
|
|
84
|
+
}));
|
|
85
|
+
const rankFn = deps.networkRanker ?? (async (input) => {
|
|
86
|
+
try {
|
|
87
|
+
recommender ?? (recommender = new NetworkRecommender());
|
|
88
|
+
return await recommender.invoke(input);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
// e.g. missing OPENROUTER_API_KEY — degrade gracefully, omit orderedNetworkIds
|
|
92
|
+
console.warn("[read_networks] NetworkRecommender unavailable, skipping ranking:", err);
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
const rankingResult = await rankFn({
|
|
97
|
+
userProfile: {
|
|
98
|
+
bio: context.userProfile.identity.bio,
|
|
99
|
+
location: context.userProfile.identity.location || context.user.location || "",
|
|
100
|
+
interests: context.userProfile.attributes.interests,
|
|
101
|
+
skills: context.userProfile.attributes.skills,
|
|
102
|
+
},
|
|
103
|
+
networks: publicNetworksForRanking,
|
|
104
|
+
}).catch((err) => {
|
|
105
|
+
// Catches errors from a custom deps.networkRanker (the default fallback
|
|
106
|
+
// handles its own errors internally). Degrade gracefully: omit orderedNetworkIds.
|
|
107
|
+
console.warn("[read_networks] networkRanker threw, skipping ranking:", err);
|
|
108
|
+
deps.reportToolError?.(err, { operation: "network-ranking", toolName: "read_networks", userId: context.userId });
|
|
109
|
+
return null;
|
|
110
|
+
});
|
|
111
|
+
if (rankingResult) {
|
|
112
|
+
// Normalize LLM output against the ranked slice (top 50):
|
|
113
|
+
// keep only IDs from the input set, de-dupe preserving order, then
|
|
114
|
+
// append any slice IDs the model omitted. Networks beyond the top-50
|
|
115
|
+
// slice are not in orderedNetworkIds and will sort to the tail in the
|
|
116
|
+
// frontend (consistent with the UI's own 50-item page size).
|
|
117
|
+
const inputIds = publicNetworksForRanking.map((n) => n.networkId);
|
|
118
|
+
const inputIdSet = new Set(inputIds);
|
|
119
|
+
const seen = new Set();
|
|
120
|
+
const normalized = [];
|
|
121
|
+
for (const id of rankingResult.rankedNetworkIds) {
|
|
122
|
+
if (inputIdSet.has(id) && !seen.has(id)) {
|
|
123
|
+
normalized.push(id);
|
|
124
|
+
seen.add(id);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
for (const id of inputIds) {
|
|
128
|
+
if (!seen.has(id))
|
|
129
|
+
normalized.push(id);
|
|
130
|
+
}
|
|
131
|
+
orderedNetworkIds = normalized;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return success({
|
|
135
|
+
...enriched,
|
|
136
|
+
...(orderedNetworkIds !== undefined ? { orderedNetworkIds } : {}),
|
|
137
|
+
_graphTimings: [{ name: 'index', durationMs: _readIndexGraphMs, agents: result.agentTimings ?? [] }],
|
|
138
|
+
});
|
|
67
139
|
}
|
|
68
140
|
return error("Failed to fetch index information.");
|
|
69
141
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network.tools.js","sourceRoot":"/","sources":["network/network.tools.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\n\nimport { requestContext } from \"../shared/observability/request-context.js\";\nimport { renderNetworkContext } from '../shared/network/metadata.renderer.js';\n\nimport type { DefineTool, ToolDeps } from \"../shared/agent/tool.helpers.js\";\nimport { success, error, UUID_REGEX } from \"../shared/agent/tool.helpers.js\";\n\nexport function createNetworkTools(defineTool: DefineTool, deps: ToolDeps) {\n const { graphs, userDb, systemDb } = deps;\n\n const enrichWithContext = (networks: Array<Record<string, unknown>>) =>\n networks.map((n) => ({\n ...n,\n renderedContext: renderNetworkContext({\n type: (n.type as string) ?? 'community',\n title: (n.title as string) ?? '',\n prompt: n.prompt as string | undefined,\n metadata: (n.metadata as Record<string, unknown>) ?? {},\n }),\n }));\n\n const readIndexes = defineTool({\n name: \"read_networks\",\n description:\n \"Lists the authenticated user's networks (communities), including ones they own and public communities they can join.\\n\\n\" +\n \"**When to use:** To find network IDs for scoping other operations (read_intents, discover_opportunities, read_network_memberships), \" +\n \"or to show the user which communities they belong to.\\n\\n\" +\n \"**Returns:** Up to three lists — `memberOf` (networks the user joined), `owns` (networks the user created), and `publicNetworks` \" +\n \"(publicly joinable communities the user is not yet a member of). Entries in `memberOf` include `isPersonal` set to `true` for the user's \" +\n \"personal network.\\n\\n\" +\n \"**Note:** In index-scoped chats, only the scoped network is returned.\",\n querySchema: z.object({\n userId: z.string().optional().describe(\"Must be the current user's ID or omitted. Cannot list another user's indexes.\"),\n }),\n handler: async ({ context, query }) => {\n if (query.userId && query.userId.trim() !== context.userId) {\n return error(\"You can only list your own indexes. Omit userId to see the current user's indexes.\");\n }\n\n const _readIndexGraphStart = Date.now();\n const _readIndexTraceEmitter = requestContext.getStore()?.traceEmitter;\n _readIndexTraceEmitter?.({ type: \"graph_start\", name: \"index\" });\n const result = await graphs.index.invoke({\n userId: context.userId,\n networkId: context.networkId || undefined,\n operationMode: 'read' as const,\n showAll: false, // Never allow bypass - strict scope enforcement\n });\n const _readIndexGraphMs = Date.now() - _readIndexGraphStart;\n _readIndexTraceEmitter?.({ type: \"graph_end\", name: \"index\", durationMs: _readIndexGraphMs });\n\n if (result.error) {\n return error(result.error);\n }\n if (result.readResult) {\n const rr = result.readResult as Record<string, unknown>;\n const enriched = {\n ...rr,\n ...(Array.isArray(rr.memberOf) ? { memberOf: enrichWithContext(rr.memberOf as Array<Record<string, unknown>>) } : {}),\n ...(Array.isArray(rr.owns) ? { owns: enrichWithContext(rr.owns as Array<Record<string, unknown>>) } : {}),\n ...(Array.isArray(rr.publicNetworks) ? { publicNetworks: enrichWithContext(rr.publicNetworks as Array<Record<string, unknown>>) } : {}),\n };\n\n // When scoped, add clear metadata so model knows results are limited\n if (context.networkId) {\n return success({\n ...enriched,\n scopeRestriction: {\n isScoped: true,\n scopedToIndex: context.indexName ?? context.networkId,\n message: `Results are limited to \"${context.indexName ?? 'this index'}\" because this chat is scoped to that community. The user may belong to other communities not shown here.`,\n },\n _graphTimings: [{ name: 'index', durationMs: _readIndexGraphMs, agents: result.agentTimings ?? [] }],\n });\n }\n return success({ ...enriched, _graphTimings: [{ name: 'index', durationMs: _readIndexGraphMs, agents: result.agentTimings ?? [] }] });\n }\n return error(\"Failed to fetch index information.\");\n },\n });\n\n const readIndexMemberships = defineTool({\n name: \"read_network_memberships\",\n description:\n \"Reads index membership information — who is in which community. Essential for understanding the social graph before \" +\n \"creating introductions or exploring intents.\\n\\n\" +\n \"**Usage modes:**\\n\" +\n \"- With `networkId` only: lists ALL members of that index — returns userId, name, avatar, permissions (owner/member), intentCount, and joinedAt. \" +\n \"Use this to see who's in a community before browsing their intents or creating introductions.\\n\" +\n \"- With `userId` only (or omit for self): lists all indexes that user belongs to — returns networkId, networkTitle, permissions, joinedAt.\\n\" +\n \"- With both `networkId` and `userId`: checks whether that specific user is a member of that specific index (returns isMember boolean).\\n\\n\" +\n \"**When to use:** Before creating introductions (need to verify shared index membership), to explore community members, \" +\n \"or to check if a user belongs to a specific index.\\n\\n\" +\n \"**Returns:** Member list with user details, or membership list with index details, or a membership check result.\\n\\n\" +\n \"**Personal index semantics.** The personal index (`isPersonal: true` on the membership) is the user's \" +\n \"contact list — members of that index are the user's contacts. For another user, this tool only reveals the \" +\n \"indexes you already share with them.\\n\\n\" +\n \"**Shared-context pattern.** To find overlap with another user: (1) omit `userId` to read your own \" +\n \"memberships, (2) call this tool with the other person's actual `userId` to get the shared indexes, \" +\n \"(3) call read_intents for each shared network to see what each is looking for there, (4) call \" +\n \"read_user_profiles for the other party. That sequence gives you enough to decide whether to propose a \" +\n \"direct connection or an introduction.\",\n querySchema: z.object({\n networkId: z.string().optional().describe(\"Index UUID — lists all members of this index. Get from read_networks. In index-scoped chats, only the scoped index can be queried.\"),\n userId: z.string().optional().describe(\"User ID — lists that user's index memberships. Omit to get the current user's memberships. When combined with networkId, checks if this user is in that specific index.\"),\n }),\n handler: async ({ context, query }) => {\n const networkId = query.networkId?.trim() || undefined;\n const userId = query.userId?.trim() || undefined;\n\n if (networkId && !UUID_REGEX.test(networkId)) {\n return error(\"Invalid index ID format. Use the exact UUID from read_networks.\");\n }\n\n // Mode 1: list members of an index\n if (networkId && !userId) {\n // Enforce strict scope: when chat is index-scoped, only allow querying that index\n if (context.networkId && networkId !== context.networkId) {\n return error(\n `This chat is scoped to ${context.indexName ?? 'this index'}. You can only query members of this index.`\n );\n }\n\n const _readMembersGraphStart = Date.now();\n const _readMembersTraceEmitter = requestContext.getStore()?.traceEmitter;\n _readMembersTraceEmitter?.({ type: \"graph_start\", name: \"network_membership\" });\n const result = await graphs.networkMembership.invoke({\n userId: context.userId,\n networkId,\n operationMode: 'read' as const,\n });\n const _readMembersGraphMs = Date.now() - _readMembersGraphStart;\n _readMembersTraceEmitter?.({ type: \"graph_end\", name: \"network_membership\", durationMs: _readMembersGraphMs });\n\n if (result.error) {\n return error(result.error);\n }\n if (result.readResult) {\n return success({ ...result.readResult, _graphTimings: [{ name: 'network_membership', durationMs: _readMembersGraphMs, agents: result.agentTimings ?? [] }] });\n }\n return error(\"Failed to fetch index members.\");\n }\n\n // Mode 2: list a user's memberships (indexes they belong to)\n const targetUserId = userId || context.userId;\n\n // Use userDb for own memberships, but systemDb access is implicit through shared index scope\n let memberships: Awaited<ReturnType<typeof userDb.getNetworkMemberships>>;\n if (targetUserId !== context.userId) {\n // Cross-user access: systemDb will validate shared index membership\n const callerMemberships = await userDb.getNetworkMemberships();\n if (networkId) {\n // Strict scope enforcement: when chat is index-scoped, only allow querying that index\n if (context.networkId && networkId !== context.networkId) {\n return error(\n `This chat is scoped to ${context.indexName ?? 'this index'}. You can only query membership in this community.`\n );\n }\n\n const callerInIndex = callerMemberships.some((m) => m.networkId === networkId);\n if (!callerInIndex) {\n return error(\n \"Unauthorized: you can only view another user's membership in an index you belong to. Provide your own userId or omit userId for your memberships.\",\n );\n }\n // Check if target user is in the index (systemDb validates scope)\n const isMember = await systemDb.isNetworkMember(networkId, targetUserId);\n if (isMember) {\n return success({ isMember: true, userId: targetUserId, networkId });\n }\n return success({ isMember: false, userId: targetUserId, networkId, message: \"User is not a member of this index.\" });\n } else {\n // Strict scope enforcement: when chat is index-scoped, only check the scoped index\n if (context.networkId) {\n const isMember = await systemDb.isNetworkMember(context.networkId, targetUserId);\n if (isMember) {\n return success({\n isMember: true,\n userId: targetUserId,\n networkId: context.networkId,\n scopeRestriction: {\n isScoped: true,\n scopedToIndex: context.indexName ?? context.networkId,\n message: `This chat is scoped to \"${context.indexName ?? 'this index'}\". Only membership in this community is shown.`,\n },\n });\n }\n return success({\n isMember: false,\n userId: targetUserId,\n networkId: context.networkId,\n message: \"User is not a member of this community.\",\n scopeRestriction: {\n isScoped: true,\n scopedToIndex: context.indexName ?? context.networkId,\n message: `This chat is scoped to \"${context.indexName ?? 'this index'}\". Only membership in this community was checked.`,\n },\n });\n }\n\n // Unscoped chat: show overlap with shared indexes (intersection of caller and target memberships)\n const sharedIndexes: typeof callerMemberships = [];\n for (const m of callerMemberships) {\n if (await systemDb.isNetworkMember(m.networkId, targetUserId)) {\n sharedIndexes.push(m);\n }\n }\n if (sharedIndexes.length === 0) {\n return error(\n \"Unauthorized: you can only view another user's memberships if you share at least one index, or request your own memberships.\",\n );\n }\n // Return only the indexes that are shared\n return success({\n userId: targetUserId,\n count: sharedIndexes.length,\n memberships: sharedIndexes.map((m) => ({\n networkId: m.networkId,\n networkTitle: m.networkTitle,\n })),\n note: \"Only showing shared indexes.\",\n });\n }\n } else {\n // Own memberships - use userDb\n memberships = await userDb.getNetworkMemberships();\n\n // NOTE: context.networkId here is the *focus* of the scoped chat — it filters\n // what we show, not what we can reach. indexScope is the reach; the bound\n // network is one element of it. See IND-306 for the equivalent in read_intents.\n // Strict scope enforcement: when chat is index-scoped, only return the scoped index membership\n if (context.networkId && !networkId) {\n memberships = memberships.filter((m) => m.networkId === context.networkId);\n }\n }\n\n // If both networkId and userId: filter to that specific membership\n if (networkId) {\n // Strict scope enforcement: when chat is index-scoped, only allow querying that index\n if (context.networkId && networkId !== context.networkId) {\n return error(\n `This chat is scoped to ${context.indexName ?? 'this index'}. You can only query membership in this community.`\n );\n }\n\n const callerMemberships = await userDb.getNetworkMemberships();\n const callerInIndex =\n targetUserId === context.userId ||\n callerMemberships.some((m) => m.networkId === networkId);\n if (!callerInIndex) {\n return error(\n \"Unauthorized: you can only view membership in an index you belong to.\",\n );\n }\n const match = memberships.find((m) => m.networkId === networkId);\n if (!match) {\n return success({ isMember: false, userId: targetUserId, networkId, message: \"User is not a member of this index.\" });\n }\n return success({\n isMember: true,\n userId: targetUserId,\n networkId,\n networkTitle: match.networkTitle,\n permissions: match.permissions,\n joinedAt: match.joinedAt,\n });\n }\n\n // When scoped, add clear metadata so model knows results are limited\n if (context.networkId && targetUserId === context.userId) {\n return success({\n userId: targetUserId,\n count: memberships.length,\n memberships: memberships.map((m) => ({\n networkId: m.networkId,\n networkTitle: m.networkTitle,\n permissions: m.permissions,\n joinedAt: m.joinedAt,\n })),\n scopeRestriction: {\n isScoped: true,\n scopedToIndex: context.indexName ?? context.networkId,\n message: `Results are limited to \"${context.indexName ?? 'this index'}\" because this chat is scoped to that community. The user may belong to other communities not shown here.`,\n },\n });\n }\n\n return success({\n userId: targetUserId,\n count: memberships.length,\n memberships: memberships.map((m) => ({\n networkId: m.networkId,\n networkTitle: m.networkTitle,\n permissions: m.permissions,\n joinedAt: m.joinedAt,\n })),\n });\n },\n });\n\n const updateNetworkSettingsSchema = z.object({\n title: z.string().optional(),\n prompt: z.string().nullable().optional(),\n imageUrl: z.string().url().nullable().optional(),\n joinPolicy: z.enum(['anyone', 'invite_only']).optional(),\n allowGuestVibeCheck: z.boolean().optional(),\n }).strict();\n\n const updateNetwork = defineTool({\n name: \"update_network\",\n description:\n \"Updates settings of an existing index (community). Only the index owner can perform updates.\\n\\n\" +\n \"**Updatable fields:** title (display name), prompt (purpose description used for intent auto-assignment), \" +\n \"imageUrl (community avatar), joinPolicy ('anyone' for open or 'invite_only'), allowGuestVibeCheck (allow non-members to preview).\\n\\n\" +\n \"**When to use:** When an index owner wants to change their community's settings — e.g. update the purpose description, \" +\n \"change from invite-only to open, or update the community image.\\n\\n\" +\n \"**Important:** Changing the prompt affects how future intents are evaluated for auto-assignment to this index. \" +\n \"Existing intent-index links are not re-evaluated automatically.\\n\\n\" +\n \"**Returns:** Confirmation with the list of settings that were updated.\",\n querySchema: z.object({\n networkId: z.string().optional().describe(\"Index UUID to update. Get from read_networks. Defaults to the scoped index in index-scoped chats.\"),\n settings: updateNetworkSettingsSchema.describe(\"Object with fields to update. All fields are optional — only include the ones to change. title: display name. prompt: purpose description (used for intent auto-assignment). imageUrl: community image URL (null to remove). joinPolicy: 'anyone' or 'invite_only'. allowGuestVibeCheck: boolean.\"),\n }),\n handler: async ({ context, query }) => {\n const effectiveIndexId = (query.networkId?.trim() || context.networkId) ?? null;\n if (!effectiveIndexId || !UUID_REGEX.test(effectiveIndexId)) {\n return error(\"Valid networkId required.\");\n }\n\n // Strict scope enforcement: when chat is index-scoped, only allow updating that index\n if (context.networkId && effectiveIndexId !== context.networkId) {\n return error(\n `This chat is scoped to ${context.indexName ?? 'this index'}. You can only update this community's settings.`\n );\n }\n\n const _updateNetworkGraphStart = Date.now();\n const _updateNetworkTraceEmitter = requestContext.getStore()?.traceEmitter;\n _updateNetworkTraceEmitter?.({ type: \"graph_start\", name: \"index\" });\n const result = await graphs.index.invoke({\n userId: context.userId,\n networkId: effectiveIndexId,\n operationMode: 'update' as const,\n updateInput: query.settings,\n });\n const _updateNetworkGraphMs = Date.now() - _updateNetworkGraphStart;\n _updateNetworkTraceEmitter?.({ type: \"graph_end\", name: \"index\", durationMs: _updateNetworkGraphMs });\n\n if (result.mutationResult && !result.mutationResult.success) {\n return error(result.mutationResult.error || \"Failed to update index.\");\n }\n return success({ message: \"Index updated.\", settings: Object.keys(query.settings), _graphTimings: [{ name: 'index', durationMs: _updateNetworkGraphMs, agents: result.agentTimings ?? [] }] });\n },\n });\n\n const createNetwork = defineTool({\n name: \"create_network\",\n description:\n \"Creates a new index (community/group). The authenticated user becomes the owner with full control over settings and membership.\\n\\n\" +\n \"**What is an index?** A shared space where members post intents (what they're looking for) and the system discovers opportunities \" +\n \"(complementary matches) between members. The index's prompt guides what kinds of intents belong.\\n\\n\" +\n \"**When to use:** When the user wants to create a new community — e.g. a professional network, interest group, or project team.\\n\\n\" +\n \"**Returns:** The new index's networkId (UUID) and title. Use the networkId to add members (create_network_membership), \" +\n \"link intents (create_intent_index), or run discovery (discover_opportunities with networkId).\",\n querySchema: z.object({\n title: z.string().describe(\"Display name of the index (e.g. 'AI Founders Berlin', 'Design Co-op'). Required.\"),\n prompt: z.string().optional().describe(\"Description of what this community is about (e.g. 'Early-stage AI/ML founders in Berlin looking for co-founders, advisors, and investors'). Used by the system to evaluate which intents belong in this index. Highly recommended for better auto-assignment.\"),\n imageUrl: z.string().url().optional().describe(\"URL for the community's avatar/image. Optional.\"),\n joinPolicy: z.enum(['anyone', 'invite_only']).optional().describe(\"'anyone' = open (any user can self-join), 'invite_only' = only the owner can add members. Defaults to 'invite_only'.\"),\n }),\n handler: async ({ context, query }) => {\n if (!query.title?.trim()) {\n return error(\"Title is required.\");\n }\n\n const _createNetworkGraphStart = Date.now();\n const _createNetworkTraceEmitter = requestContext.getStore()?.traceEmitter;\n _createNetworkTraceEmitter?.({ type: \"graph_start\", name: \"index\" });\n const result = await graphs.index.invoke({\n userId: context.userId,\n operationMode: 'create' as const,\n createInput: {\n title: query.title.trim(),\n prompt: query.prompt?.trim() || undefined,\n imageUrl: query.imageUrl || undefined,\n joinPolicy: query.joinPolicy,\n },\n });\n const _createNetworkGraphMs = Date.now() - _createNetworkGraphStart;\n _createNetworkTraceEmitter?.({ type: \"graph_end\", name: \"index\", durationMs: _createNetworkGraphMs });\n\n if (result.mutationResult) {\n if (result.mutationResult.success) {\n return success({\n created: true,\n networkId: result.mutationResult.networkId,\n title: result.mutationResult.title,\n message: result.mutationResult.message,\n _graphTimings: [{ name: 'index', durationMs: _createNetworkGraphMs, agents: result.agentTimings ?? [] }],\n });\n }\n return error(result.mutationResult.error || \"Failed to create index.\");\n }\n return error(\"Failed to create index.\");\n },\n });\n\n const deleteNetwork = defineTool({\n name: \"delete_network\",\n description:\n \"Permanently deletes an index (community). Only the owner can delete, and the index must have no other members \" +\n \"(remove all members first with delete_network_membership). Personal indexes cannot be deleted.\\n\\n\" +\n \"**When to use:** When the owner wants to disband a community. This is irreversible — all intent-index links to this index are removed.\\n\\n\" +\n \"**Prerequisites:** Must be the owner. Must be the sole remaining member (remove others first).\\n\\n\" +\n \"**Returns:** Confirmation that the index was deleted.\",\n querySchema: z.object({\n networkId: z.string().optional().describe(\"Index UUID to delete. Get from read_networks. Defaults to the scoped index in index-scoped chats. Cannot be a personal index.\"),\n }),\n handler: async ({ context, query }) => {\n const networkId = query.networkId?.trim() || context.networkId;\n if (!networkId || !UUID_REGEX.test(networkId)) {\n return error(\"Valid networkId required.\");\n }\n\n // Strict scope enforcement: when chat is index-scoped, only allow deleting that index\n if (context.networkId && networkId !== context.networkId) {\n return error(\n `This chat is scoped to ${context.indexName ?? 'this index'}. You can only delete this community.`\n );\n }\n\n const _deleteNetworkGraphStart = Date.now();\n const _deleteNetworkTraceEmitter = requestContext.getStore()?.traceEmitter;\n _deleteNetworkTraceEmitter?.({ type: \"graph_start\", name: \"index\" });\n const result = await graphs.index.invoke({\n userId: context.userId,\n networkId,\n operationMode: 'delete' as const,\n });\n const _deleteNetworkGraphMs = Date.now() - _deleteNetworkGraphStart;\n _deleteNetworkTraceEmitter?.({ type: \"graph_end\", name: \"index\", durationMs: _deleteNetworkGraphMs });\n\n if (result.mutationResult && !result.mutationResult.success) {\n return error(result.mutationResult.error || \"Failed to delete index.\");\n }\n return success({ message: \"Network deleted.\", _graphTimings: [{ name: 'index', durationMs: _deleteNetworkGraphMs, agents: result.agentTimings ?? [] }] });\n },\n });\n\n const createNetworkMembership = defineTool({\n name: \"create_network_membership\",\n description:\n \"Adds a user as a member of an index (community). Membership enables the user to post intents in the index and be discovered \" +\n \"by other members through opportunity matching.\\n\\n\" +\n \"**Usage modes:**\\n\" +\n \"- Omit userId: self-join (only works for indexes with joinPolicy 'anyone').\\n\" +\n \"- With userId: add another user (only the index owner can do this for 'invite_only' indexes).\\n\\n\" +\n \"**When to use:** When the user wants to join an open community, or when an index owner wants to invite someone.\\n\\n\" +\n \"**Returns:** Confirmation that the member was added (or a note that they were already a member). \" +\n \"After joining, the user's existing intents with autoAssign=true may be evaluated against the new index.\",\n querySchema: z.object({\n userId: z.string().optional().describe(\"User ID to add as a member. Omit to join the index yourself. Get user IDs from read_user_profiles(query=name) or read_network_memberships.\"),\n networkId: z.string().optional().describe(\"Index UUID to add the member to. Get from read_networks. Defaults to the scoped index in index-scoped chats.\"),\n }),\n handler: async ({ context, query }) => {\n const networkId = query.networkId?.trim() || context.networkId;\n const targetUserId = query.userId?.trim() || context.userId;\n if (!networkId || !UUID_REGEX.test(networkId)) {\n return error(\"Invalid index ID format. Use the exact UUID from read_networks.\");\n }\n\n // Strict scope enforcement: when chat is index-scoped, only allow adding to that index\n if (context.networkId && networkId !== context.networkId) {\n return error(\n `This chat is scoped to ${context.indexName ?? 'this index'}. You can only add members to this community.`\n );\n }\n\n const _createMembershipGraphStart = Date.now();\n const _createMembershipTraceEmitter = requestContext.getStore()?.traceEmitter;\n _createMembershipTraceEmitter?.({ type: \"graph_start\", name: \"network_membership\" });\n const result = await graphs.networkMembership.invoke({\n userId: context.userId,\n networkId,\n targetUserId,\n operationMode: 'create' as const,\n });\n const _createMembershipGraphMs = Date.now() - _createMembershipGraphStart;\n _createMembershipTraceEmitter?.({ type: \"graph_end\", name: \"network_membership\", durationMs: _createMembershipGraphMs });\n\n if (result.mutationResult) {\n if (result.mutationResult.success) {\n const alreadyMember = result.mutationResult.message?.includes(\"already\");\n return success({\n created: !alreadyMember,\n message: result.mutationResult.message,\n _graphTimings: [{ name: 'network_membership', durationMs: _createMembershipGraphMs, agents: result.agentTimings ?? [] }],\n });\n }\n return error(result.mutationResult.error || \"Failed to add member.\");\n }\n return error(\"Failed to add member.\");\n },\n });\n\n const deleteNetworkMembership = defineTool({\n name: \"delete_network_membership\",\n description:\n \"Removes a user from an index (community). After removal, the user's intents are unlinked from this index \" +\n \"and they can no longer participate in opportunity discovery within it.\\n\\n\" +\n \"**Permissions:** Only the index owner can remove members. The owner themselves cannot be removed (delete the index instead).\\n\\n\" +\n \"**When to use:** When an index owner wants to remove a member from the community. \" +\n \"Use read_network_memberships(networkId) first to get the userId of the member to remove.\\n\\n\" +\n \"**Returns:** Confirmation that the member was removed.\",\n querySchema: z.object({\n userId: z.string().describe(\"User ID of the member to remove. Get from read_network_memberships(networkId). Cannot be the index owner.\"),\n networkId: z.string().optional().describe(\"Index UUID to remove the member from. Get from read_networks. Defaults to the scoped index in index-scoped chats.\"),\n }),\n handler: async ({ context, query }) => {\n const networkId = query.networkId?.trim() || context.networkId;\n const targetUserId = query.userId?.trim();\n\n if (!networkId || !UUID_REGEX.test(networkId)) {\n return error(\"Valid networkId required. Use the exact UUID from read_networks.\");\n }\n if (!targetUserId) {\n return error(\"userId is required.\");\n }\n\n // Strict scope enforcement: when chat is index-scoped, only allow that index\n if (context.networkId && networkId !== context.networkId) {\n return error(\n `This chat is scoped to ${context.indexName ?? 'this index'}. You can only manage members of this community.`\n );\n }\n\n const _deleteMembershipGraphStart = Date.now();\n const _deleteMembershipTraceEmitter = requestContext.getStore()?.traceEmitter;\n _deleteMembershipTraceEmitter?.({ type: \"graph_start\", name: \"network_membership\" });\n const result = await graphs.networkMembership.invoke({\n userId: context.userId,\n networkId,\n targetUserId,\n operationMode: 'delete' as const,\n });\n const _deleteMembershipGraphMs = Date.now() - _deleteMembershipGraphStart;\n _deleteMembershipTraceEmitter?.({ type: \"graph_end\", name: \"network_membership\", durationMs: _deleteMembershipGraphMs });\n\n if (result.mutationResult) {\n if (result.mutationResult.success) {\n return success({\n removed: true,\n message: result.mutationResult.message,\n _graphTimings: [{ name: 'network_membership', durationMs: _deleteMembershipGraphMs, agents: result.agentTimings ?? [] }],\n });\n }\n return error(result.mutationResult.error || \"Failed to remove member.\");\n }\n return error(\"Failed to remove member.\");\n },\n });\n\n return [readIndexes, readIndexMemberships, updateNetwork, createNetwork, deleteNetwork, createNetworkMembership, deleteNetworkMembership] as const;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"network.tools.js","sourceRoot":"/","sources":["network/network.tools.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\n\nimport { requestContext } from \"../shared/observability/request-context.js\";\nimport { renderNetworkContext } from '../shared/network/metadata.renderer.js';\n\nimport type { DefineTool, ToolDeps } from \"../shared/agent/tool.helpers.js\";\nimport { success, error, UUID_REGEX } from \"../shared/agent/tool.helpers.js\";\nimport { NetworkRecommender } from \"./network.recommender.js\";\n\n// Lazy singleton — only instantiated on first onboarding ranking call so that\n// importing this module does not require OPENROUTER_API_KEY at load time.\nlet recommender: NetworkRecommender | undefined;\n\nexport function createNetworkTools(defineTool: DefineTool, deps: ToolDeps) {\n const { graphs, userDb, systemDb } = deps;\n\n const enrichWithContext = (networks: Array<Record<string, unknown>>) =>\n networks.map((n) => ({\n ...n,\n renderedContext: renderNetworkContext({\n type: (n.type as string) ?? 'community',\n title: (n.title as string) ?? '',\n prompt: n.prompt as string | undefined,\n metadata: (n.metadata as Record<string, unknown>) ?? {},\n }),\n }));\n\n const readIndexes = defineTool({\n name: \"read_networks\",\n description:\n \"Lists the authenticated user's networks (communities), including ones they own and public communities they can join.\\n\\n\" +\n \"**When to use:** To find network IDs for scoping other operations (read_intents, discover_opportunities, read_network_memberships), \" +\n \"or to show the user which communities they belong to.\\n\\n\" +\n \"**Returns:** Up to three lists — `memberOf` (networks the user joined), `owns` (networks the user created), and `publicNetworks` \" +\n \"(publicly joinable communities the user is not yet a member of). Entries in `memberOf` include `isPersonal` set to `true` for the user's \" +\n \"personal network.\\n\\n\" +\n \"**Note:** In index-scoped chats, only the scoped network is returned. During onboarding, `orderedNetworkIds` \" +\n \"may be returned alongside `publicNetworks` \\u2014 a ranked array of network IDs ordered by relevance to the user's profile (omitted when ranking is unavailable or fails).\",\n querySchema: z.object({\n userId: z.string().optional().describe(\"Must be the current user's ID or omitted. Cannot list another user's indexes.\"),\n }),\n handler: async ({ context, query }) => {\n if (query.userId && query.userId.trim() !== context.userId) {\n return error(\"You can only list your own indexes. Omit userId to see the current user's indexes.\");\n }\n\n const _readIndexGraphStart = Date.now();\n const _readIndexTraceEmitter = requestContext.getStore()?.traceEmitter;\n _readIndexTraceEmitter?.({ type: \"graph_start\", name: \"index\" });\n const result = await graphs.index.invoke({\n userId: context.userId,\n networkId: context.networkId || undefined,\n operationMode: 'read' as const,\n showAll: false, // Never allow bypass - strict scope enforcement\n });\n const _readIndexGraphMs = Date.now() - _readIndexGraphStart;\n _readIndexTraceEmitter?.({ type: \"graph_end\", name: \"index\", durationMs: _readIndexGraphMs });\n\n if (result.error) {\n return error(result.error);\n }\n if (result.readResult) {\n const rr = result.readResult as Record<string, unknown>;\n const enriched = {\n ...rr,\n ...(Array.isArray(rr.memberOf) ? { memberOf: enrichWithContext(rr.memberOf as Array<Record<string, unknown>>) } : {}),\n ...(Array.isArray(rr.owns) ? { owns: enrichWithContext(rr.owns as Array<Record<string, unknown>>) } : {}),\n ...(Array.isArray(rr.publicNetworks) ? { publicNetworks: enrichWithContext(rr.publicNetworks as Array<Record<string, unknown>>) } : {}),\n };\n\n // When scoped, add clear metadata so model knows results are limited\n if (context.networkId) {\n return success({\n ...enriched,\n scopeRestriction: {\n isScoped: true,\n scopedToIndex: context.indexName ?? context.networkId,\n message: `Results are limited to \"${context.indexName ?? 'this index'}\" because this chat is scoped to that community. The user may belong to other communities not shown here.`,\n },\n _graphTimings: [{ name: 'index', durationMs: _readIndexGraphMs, agents: result.agentTimings ?? [] }],\n });\n }\n // Onboarding-only: rank public networks by profile relevance.\n // Guard: only when isOnboarding, userProfile exists, not scoped, and there are public networks to rank.\n let orderedNetworkIds: string[] | undefined;\n if (\n context.isOnboarding &&\n context.userProfile &&\n Array.isArray(enriched.publicNetworks) &&\n (enriched.publicNetworks as Array<Record<string, unknown>>).length > 0\n ) {\n // Cap at 50 to bound LLM context window usage (matches the UI's discoverPublicIndexes(1, 50)).\n const publicNetworksForRanking = (enriched.publicNetworks as Array<Record<string, unknown>>)\n .slice(0, 50)\n .map((n) => ({\n networkId: n.networkId as string,\n renderedContext: (n.renderedContext as string) ?? `## ${n.title as string}`,\n }));\n const rankFn = deps.networkRanker ?? (async (input) => {\n try {\n recommender ??= new NetworkRecommender();\n return await recommender.invoke(input);\n } catch (err) {\n // e.g. missing OPENROUTER_API_KEY — degrade gracefully, omit orderedNetworkIds\n console.warn(\"[read_networks] NetworkRecommender unavailable, skipping ranking:\", err);\n return null;\n }\n });\n const rankingResult = await rankFn({\n userProfile: {\n bio: context.userProfile.identity.bio,\n location: context.userProfile.identity.location || context.user.location || \"\",\n interests: context.userProfile.attributes.interests,\n skills: context.userProfile.attributes.skills,\n },\n networks: publicNetworksForRanking,\n }).catch((err: unknown) => {\n // Catches errors from a custom deps.networkRanker (the default fallback\n // handles its own errors internally). Degrade gracefully: omit orderedNetworkIds.\n console.warn(\"[read_networks] networkRanker threw, skipping ranking:\", err);\n deps.reportToolError?.(err, { operation: \"network-ranking\", toolName: \"read_networks\", userId: context.userId });\n return null;\n });\n if (rankingResult) {\n // Normalize LLM output against the ranked slice (top 50):\n // keep only IDs from the input set, de-dupe preserving order, then\n // append any slice IDs the model omitted. Networks beyond the top-50\n // slice are not in orderedNetworkIds and will sort to the tail in the\n // frontend (consistent with the UI's own 50-item page size).\n const inputIds = publicNetworksForRanking.map((n) => n.networkId);\n const inputIdSet = new Set(inputIds);\n const seen = new Set<string>();\n const normalized: string[] = [];\n for (const id of rankingResult.rankedNetworkIds) {\n if (inputIdSet.has(id) && !seen.has(id)) {\n normalized.push(id);\n seen.add(id);\n }\n }\n for (const id of inputIds) {\n if (!seen.has(id)) normalized.push(id);\n }\n orderedNetworkIds = normalized;\n }\n }\n\n return success({\n ...enriched,\n ...(orderedNetworkIds !== undefined ? { orderedNetworkIds } : {}),\n _graphTimings: [{ name: 'index', durationMs: _readIndexGraphMs, agents: result.agentTimings ?? [] }],\n });\n }\n return error(\"Failed to fetch index information.\");\n },\n });\n\n const readIndexMemberships = defineTool({\n name: \"read_network_memberships\",\n description:\n \"Reads index membership information — who is in which community. Essential for understanding the social graph before \" +\n \"creating introductions or exploring intents.\\n\\n\" +\n \"**Usage modes:**\\n\" +\n \"- With `networkId` only: lists ALL members of that index — returns userId, name, avatar, permissions (owner/member), intentCount, and joinedAt. \" +\n \"Use this to see who's in a community before browsing their intents or creating introductions.\\n\" +\n \"- With `userId` only (or omit for self): lists all indexes that user belongs to — returns networkId, networkTitle, permissions, joinedAt.\\n\" +\n \"- With both `networkId` and `userId`: checks whether that specific user is a member of that specific index (returns isMember boolean).\\n\\n\" +\n \"**When to use:** Before creating introductions (need to verify shared index membership), to explore community members, \" +\n \"or to check if a user belongs to a specific index.\\n\\n\" +\n \"**Returns:** Member list with user details, or membership list with index details, or a membership check result.\\n\\n\" +\n \"**Personal index semantics.** The personal index (`isPersonal: true` on the membership) is the user's \" +\n \"contact list — members of that index are the user's contacts. For another user, this tool only reveals the \" +\n \"indexes you already share with them.\\n\\n\" +\n \"**Shared-context pattern.** To find overlap with another user: (1) omit `userId` to read your own \" +\n \"memberships, (2) call this tool with the other person's actual `userId` to get the shared indexes, \" +\n \"(3) call read_intents for each shared network to see what each is looking for there, (4) call \" +\n \"read_user_profiles for the other party. That sequence gives you enough to decide whether to propose a \" +\n \"direct connection or an introduction.\",\n querySchema: z.object({\n networkId: z.string().optional().describe(\"Index UUID — lists all members of this index. Get from read_networks. In index-scoped chats, only the scoped index can be queried.\"),\n userId: z.string().optional().describe(\"User ID — lists that user's index memberships. Omit to get the current user's memberships. When combined with networkId, checks if this user is in that specific index.\"),\n }),\n handler: async ({ context, query }) => {\n const networkId = query.networkId?.trim() || undefined;\n const userId = query.userId?.trim() || undefined;\n\n if (networkId && !UUID_REGEX.test(networkId)) {\n return error(\"Invalid index ID format. Use the exact UUID from read_networks.\");\n }\n\n // Mode 1: list members of an index\n if (networkId && !userId) {\n // Enforce strict scope: when chat is index-scoped, only allow querying that index\n if (context.networkId && networkId !== context.networkId) {\n return error(\n `This chat is scoped to ${context.indexName ?? 'this index'}. You can only query members of this index.`\n );\n }\n\n const _readMembersGraphStart = Date.now();\n const _readMembersTraceEmitter = requestContext.getStore()?.traceEmitter;\n _readMembersTraceEmitter?.({ type: \"graph_start\", name: \"network_membership\" });\n const result = await graphs.networkMembership.invoke({\n userId: context.userId,\n networkId,\n operationMode: 'read' as const,\n });\n const _readMembersGraphMs = Date.now() - _readMembersGraphStart;\n _readMembersTraceEmitter?.({ type: \"graph_end\", name: \"network_membership\", durationMs: _readMembersGraphMs });\n\n if (result.error) {\n return error(result.error);\n }\n if (result.readResult) {\n return success({ ...result.readResult, _graphTimings: [{ name: 'network_membership', durationMs: _readMembersGraphMs, agents: result.agentTimings ?? [] }] });\n }\n return error(\"Failed to fetch index members.\");\n }\n\n // Mode 2: list a user's memberships (indexes they belong to)\n const targetUserId = userId || context.userId;\n\n // Use userDb for own memberships, but systemDb access is implicit through shared index scope\n let memberships: Awaited<ReturnType<typeof userDb.getNetworkMemberships>>;\n if (targetUserId !== context.userId) {\n // Cross-user access: systemDb will validate shared index membership\n const callerMemberships = await userDb.getNetworkMemberships();\n if (networkId) {\n // Strict scope enforcement: when chat is index-scoped, only allow querying that index\n if (context.networkId && networkId !== context.networkId) {\n return error(\n `This chat is scoped to ${context.indexName ?? 'this index'}. You can only query membership in this community.`\n );\n }\n\n const callerInIndex = callerMemberships.some((m) => m.networkId === networkId);\n if (!callerInIndex) {\n return error(\n \"Unauthorized: you can only view another user's membership in an index you belong to. Provide your own userId or omit userId for your memberships.\",\n );\n }\n // Check if target user is in the index (systemDb validates scope)\n const isMember = await systemDb.isNetworkMember(networkId, targetUserId);\n if (isMember) {\n return success({ isMember: true, userId: targetUserId, networkId });\n }\n return success({ isMember: false, userId: targetUserId, networkId, message: \"User is not a member of this index.\" });\n } else {\n // Strict scope enforcement: when chat is index-scoped, only check the scoped index\n if (context.networkId) {\n const isMember = await systemDb.isNetworkMember(context.networkId, targetUserId);\n if (isMember) {\n return success({\n isMember: true,\n userId: targetUserId,\n networkId: context.networkId,\n scopeRestriction: {\n isScoped: true,\n scopedToIndex: context.indexName ?? context.networkId,\n message: `This chat is scoped to \"${context.indexName ?? 'this index'}\". Only membership in this community is shown.`,\n },\n });\n }\n return success({\n isMember: false,\n userId: targetUserId,\n networkId: context.networkId,\n message: \"User is not a member of this community.\",\n scopeRestriction: {\n isScoped: true,\n scopedToIndex: context.indexName ?? context.networkId,\n message: `This chat is scoped to \"${context.indexName ?? 'this index'}\". Only membership in this community was checked.`,\n },\n });\n }\n\n // Unscoped chat: show overlap with shared indexes (intersection of caller and target memberships)\n const sharedIndexes: typeof callerMemberships = [];\n for (const m of callerMemberships) {\n if (await systemDb.isNetworkMember(m.networkId, targetUserId)) {\n sharedIndexes.push(m);\n }\n }\n if (sharedIndexes.length === 0) {\n return error(\n \"Unauthorized: you can only view another user's memberships if you share at least one index, or request your own memberships.\",\n );\n }\n // Return only the indexes that are shared\n return success({\n userId: targetUserId,\n count: sharedIndexes.length,\n memberships: sharedIndexes.map((m) => ({\n networkId: m.networkId,\n networkTitle: m.networkTitle,\n })),\n note: \"Only showing shared indexes.\",\n });\n }\n } else {\n // Own memberships - use userDb\n memberships = await userDb.getNetworkMemberships();\n\n // NOTE: context.networkId here is the *focus* of the scoped chat — it filters\n // what we show, not what we can reach. indexScope is the reach; the bound\n // network is one element of it. See IND-306 for the equivalent in read_intents.\n // Strict scope enforcement: when chat is index-scoped, only return the scoped index membership\n if (context.networkId && !networkId) {\n memberships = memberships.filter((m) => m.networkId === context.networkId);\n }\n }\n\n // If both networkId and userId: filter to that specific membership\n if (networkId) {\n // Strict scope enforcement: when chat is index-scoped, only allow querying that index\n if (context.networkId && networkId !== context.networkId) {\n return error(\n `This chat is scoped to ${context.indexName ?? 'this index'}. You can only query membership in this community.`\n );\n }\n\n const callerMemberships = await userDb.getNetworkMemberships();\n const callerInIndex =\n targetUserId === context.userId ||\n callerMemberships.some((m) => m.networkId === networkId);\n if (!callerInIndex) {\n return error(\n \"Unauthorized: you can only view membership in an index you belong to.\",\n );\n }\n const match = memberships.find((m) => m.networkId === networkId);\n if (!match) {\n return success({ isMember: false, userId: targetUserId, networkId, message: \"User is not a member of this index.\" });\n }\n return success({\n isMember: true,\n userId: targetUserId,\n networkId,\n networkTitle: match.networkTitle,\n permissions: match.permissions,\n joinedAt: match.joinedAt,\n });\n }\n\n // When scoped, add clear metadata so model knows results are limited\n if (context.networkId && targetUserId === context.userId) {\n return success({\n userId: targetUserId,\n count: memberships.length,\n memberships: memberships.map((m) => ({\n networkId: m.networkId,\n networkTitle: m.networkTitle,\n permissions: m.permissions,\n joinedAt: m.joinedAt,\n })),\n scopeRestriction: {\n isScoped: true,\n scopedToIndex: context.indexName ?? context.networkId,\n message: `Results are limited to \"${context.indexName ?? 'this index'}\" because this chat is scoped to that community. The user may belong to other communities not shown here.`,\n },\n });\n }\n\n return success({\n userId: targetUserId,\n count: memberships.length,\n memberships: memberships.map((m) => ({\n networkId: m.networkId,\n networkTitle: m.networkTitle,\n permissions: m.permissions,\n joinedAt: m.joinedAt,\n })),\n });\n },\n });\n\n const updateNetworkSettingsSchema = z.object({\n title: z.string().optional(),\n prompt: z.string().nullable().optional(),\n imageUrl: z.string().url().nullable().optional(),\n joinPolicy: z.enum(['anyone', 'invite_only']).optional(),\n allowGuestVibeCheck: z.boolean().optional(),\n }).strict();\n\n const updateNetwork = defineTool({\n name: \"update_network\",\n description:\n \"Updates settings of an existing index (community). Only the index owner can perform updates.\\n\\n\" +\n \"**Updatable fields:** title (display name), prompt (purpose description used for intent auto-assignment), \" +\n \"imageUrl (community avatar), joinPolicy ('anyone' for open or 'invite_only'), allowGuestVibeCheck (allow non-members to preview).\\n\\n\" +\n \"**When to use:** When an index owner wants to change their community's settings — e.g. update the purpose description, \" +\n \"change from invite-only to open, or update the community image.\\n\\n\" +\n \"**Important:** Changing the prompt affects how future intents are evaluated for auto-assignment to this index. \" +\n \"Existing intent-index links are not re-evaluated automatically.\\n\\n\" +\n \"**Returns:** Confirmation with the list of settings that were updated.\",\n querySchema: z.object({\n networkId: z.string().optional().describe(\"Index UUID to update. Get from read_networks. Defaults to the scoped index in index-scoped chats.\"),\n settings: updateNetworkSettingsSchema.describe(\"Object with fields to update. All fields are optional — only include the ones to change. title: display name. prompt: purpose description (used for intent auto-assignment). imageUrl: community image URL (null to remove). joinPolicy: 'anyone' or 'invite_only'. allowGuestVibeCheck: boolean.\"),\n }),\n handler: async ({ context, query }) => {\n const effectiveIndexId = (query.networkId?.trim() || context.networkId) ?? null;\n if (!effectiveIndexId || !UUID_REGEX.test(effectiveIndexId)) {\n return error(\"Valid networkId required.\");\n }\n\n // Strict scope enforcement: when chat is index-scoped, only allow updating that index\n if (context.networkId && effectiveIndexId !== context.networkId) {\n return error(\n `This chat is scoped to ${context.indexName ?? 'this index'}. You can only update this community's settings.`\n );\n }\n\n const _updateNetworkGraphStart = Date.now();\n const _updateNetworkTraceEmitter = requestContext.getStore()?.traceEmitter;\n _updateNetworkTraceEmitter?.({ type: \"graph_start\", name: \"index\" });\n const result = await graphs.index.invoke({\n userId: context.userId,\n networkId: effectiveIndexId,\n operationMode: 'update' as const,\n updateInput: query.settings,\n });\n const _updateNetworkGraphMs = Date.now() - _updateNetworkGraphStart;\n _updateNetworkTraceEmitter?.({ type: \"graph_end\", name: \"index\", durationMs: _updateNetworkGraphMs });\n\n if (result.mutationResult && !result.mutationResult.success) {\n return error(result.mutationResult.error || \"Failed to update index.\");\n }\n return success({ message: \"Index updated.\", settings: Object.keys(query.settings), _graphTimings: [{ name: 'index', durationMs: _updateNetworkGraphMs, agents: result.agentTimings ?? [] }] });\n },\n });\n\n const createNetwork = defineTool({\n name: \"create_network\",\n description:\n \"Creates a new index (community/group). The authenticated user becomes the owner with full control over settings and membership.\\n\\n\" +\n \"**What is an index?** A shared space where members post intents (what they're looking for) and the system discovers opportunities \" +\n \"(complementary matches) between members. The index's prompt guides what kinds of intents belong.\\n\\n\" +\n \"**When to use:** When the user wants to create a new community — e.g. a professional network, interest group, or project team.\\n\\n\" +\n \"**Returns:** The new index's networkId (UUID) and title. Use the networkId to add members (create_network_membership), \" +\n \"link intents (create_intent_index), or run discovery (discover_opportunities with networkId).\",\n querySchema: z.object({\n title: z.string().describe(\"Display name of the index (e.g. 'AI Founders Berlin', 'Design Co-op'). Required.\"),\n prompt: z.string().optional().describe(\"Description of what this community is about (e.g. 'Early-stage AI/ML founders in Berlin looking for co-founders, advisors, and investors'). Used by the system to evaluate which intents belong in this index. Highly recommended for better auto-assignment.\"),\n imageUrl: z.string().url().optional().describe(\"URL for the community's avatar/image. Optional.\"),\n joinPolicy: z.enum(['anyone', 'invite_only']).optional().describe(\"'anyone' = open (any user can self-join), 'invite_only' = only the owner can add members. Defaults to 'invite_only'.\"),\n }),\n handler: async ({ context, query }) => {\n if (!query.title?.trim()) {\n return error(\"Title is required.\");\n }\n\n const _createNetworkGraphStart = Date.now();\n const _createNetworkTraceEmitter = requestContext.getStore()?.traceEmitter;\n _createNetworkTraceEmitter?.({ type: \"graph_start\", name: \"index\" });\n const result = await graphs.index.invoke({\n userId: context.userId,\n operationMode: 'create' as const,\n createInput: {\n title: query.title.trim(),\n prompt: query.prompt?.trim() || undefined,\n imageUrl: query.imageUrl || undefined,\n joinPolicy: query.joinPolicy,\n },\n });\n const _createNetworkGraphMs = Date.now() - _createNetworkGraphStart;\n _createNetworkTraceEmitter?.({ type: \"graph_end\", name: \"index\", durationMs: _createNetworkGraphMs });\n\n if (result.mutationResult) {\n if (result.mutationResult.success) {\n return success({\n created: true,\n networkId: result.mutationResult.networkId,\n title: result.mutationResult.title,\n message: result.mutationResult.message,\n _graphTimings: [{ name: 'index', durationMs: _createNetworkGraphMs, agents: result.agentTimings ?? [] }],\n });\n }\n return error(result.mutationResult.error || \"Failed to create index.\");\n }\n return error(\"Failed to create index.\");\n },\n });\n\n const deleteNetwork = defineTool({\n name: \"delete_network\",\n description:\n \"Permanently deletes an index (community). Only the owner can delete, and the index must have no other members \" +\n \"(remove all members first with delete_network_membership). Personal indexes cannot be deleted.\\n\\n\" +\n \"**When to use:** When the owner wants to disband a community. This is irreversible — all intent-index links to this index are removed.\\n\\n\" +\n \"**Prerequisites:** Must be the owner. Must be the sole remaining member (remove others first).\\n\\n\" +\n \"**Returns:** Confirmation that the index was deleted.\",\n querySchema: z.object({\n networkId: z.string().optional().describe(\"Index UUID to delete. Get from read_networks. Defaults to the scoped index in index-scoped chats. Cannot be a personal index.\"),\n }),\n handler: async ({ context, query }) => {\n const networkId = query.networkId?.trim() || context.networkId;\n if (!networkId || !UUID_REGEX.test(networkId)) {\n return error(\"Valid networkId required.\");\n }\n\n // Strict scope enforcement: when chat is index-scoped, only allow deleting that index\n if (context.networkId && networkId !== context.networkId) {\n return error(\n `This chat is scoped to ${context.indexName ?? 'this index'}. You can only delete this community.`\n );\n }\n\n const _deleteNetworkGraphStart = Date.now();\n const _deleteNetworkTraceEmitter = requestContext.getStore()?.traceEmitter;\n _deleteNetworkTraceEmitter?.({ type: \"graph_start\", name: \"index\" });\n const result = await graphs.index.invoke({\n userId: context.userId,\n networkId,\n operationMode: 'delete' as const,\n });\n const _deleteNetworkGraphMs = Date.now() - _deleteNetworkGraphStart;\n _deleteNetworkTraceEmitter?.({ type: \"graph_end\", name: \"index\", durationMs: _deleteNetworkGraphMs });\n\n if (result.mutationResult && !result.mutationResult.success) {\n return error(result.mutationResult.error || \"Failed to delete index.\");\n }\n return success({ message: \"Network deleted.\", _graphTimings: [{ name: 'index', durationMs: _deleteNetworkGraphMs, agents: result.agentTimings ?? [] }] });\n },\n });\n\n const createNetworkMembership = defineTool({\n name: \"create_network_membership\",\n description:\n \"Adds a user as a member of an index (community). Membership enables the user to post intents in the index and be discovered \" +\n \"by other members through opportunity matching.\\n\\n\" +\n \"**Usage modes:**\\n\" +\n \"- Omit userId: self-join (only works for indexes with joinPolicy 'anyone').\\n\" +\n \"- With userId: add another user (only the index owner can do this for 'invite_only' indexes).\\n\\n\" +\n \"**When to use:** When the user wants to join an open community, or when an index owner wants to invite someone.\\n\\n\" +\n \"**Returns:** Confirmation that the member was added (or a note that they were already a member). \" +\n \"After joining, the user's existing intents with autoAssign=true may be evaluated against the new index.\",\n querySchema: z.object({\n userId: z.string().optional().describe(\"User ID to add as a member. Omit to join the index yourself. Get user IDs from read_user_profiles(query=name) or read_network_memberships.\"),\n networkId: z.string().optional().describe(\"Index UUID to add the member to. Get from read_networks. Defaults to the scoped index in index-scoped chats.\"),\n }),\n handler: async ({ context, query }) => {\n const networkId = query.networkId?.trim() || context.networkId;\n const targetUserId = query.userId?.trim() || context.userId;\n if (!networkId || !UUID_REGEX.test(networkId)) {\n return error(\"Invalid index ID format. Use the exact UUID from read_networks.\");\n }\n\n // Strict scope enforcement: when chat is index-scoped, only allow adding to that index\n if (context.networkId && networkId !== context.networkId) {\n return error(\n `This chat is scoped to ${context.indexName ?? 'this index'}. You can only add members to this community.`\n );\n }\n\n const _createMembershipGraphStart = Date.now();\n const _createMembershipTraceEmitter = requestContext.getStore()?.traceEmitter;\n _createMembershipTraceEmitter?.({ type: \"graph_start\", name: \"network_membership\" });\n const result = await graphs.networkMembership.invoke({\n userId: context.userId,\n networkId,\n targetUserId,\n operationMode: 'create' as const,\n });\n const _createMembershipGraphMs = Date.now() - _createMembershipGraphStart;\n _createMembershipTraceEmitter?.({ type: \"graph_end\", name: \"network_membership\", durationMs: _createMembershipGraphMs });\n\n if (result.mutationResult) {\n if (result.mutationResult.success) {\n const alreadyMember = result.mutationResult.message?.includes(\"already\");\n return success({\n created: !alreadyMember,\n message: result.mutationResult.message,\n _graphTimings: [{ name: 'network_membership', durationMs: _createMembershipGraphMs, agents: result.agentTimings ?? [] }],\n });\n }\n return error(result.mutationResult.error || \"Failed to add member.\");\n }\n return error(\"Failed to add member.\");\n },\n });\n\n const deleteNetworkMembership = defineTool({\n name: \"delete_network_membership\",\n description:\n \"Removes a user from an index (community). After removal, the user's intents are unlinked from this index \" +\n \"and they can no longer participate in opportunity discovery within it.\\n\\n\" +\n \"**Permissions:** Only the index owner can remove members. The owner themselves cannot be removed (delete the index instead).\\n\\n\" +\n \"**When to use:** When an index owner wants to remove a member from the community. \" +\n \"Use read_network_memberships(networkId) first to get the userId of the member to remove.\\n\\n\" +\n \"**Returns:** Confirmation that the member was removed.\",\n querySchema: z.object({\n userId: z.string().describe(\"User ID of the member to remove. Get from read_network_memberships(networkId). Cannot be the index owner.\"),\n networkId: z.string().optional().describe(\"Index UUID to remove the member from. Get from read_networks. Defaults to the scoped index in index-scoped chats.\"),\n }),\n handler: async ({ context, query }) => {\n const networkId = query.networkId?.trim() || context.networkId;\n const targetUserId = query.userId?.trim();\n\n if (!networkId || !UUID_REGEX.test(networkId)) {\n return error(\"Valid networkId required. Use the exact UUID from read_networks.\");\n }\n if (!targetUserId) {\n return error(\"userId is required.\");\n }\n\n // Strict scope enforcement: when chat is index-scoped, only allow that index\n if (context.networkId && networkId !== context.networkId) {\n return error(\n `This chat is scoped to ${context.indexName ?? 'this index'}. You can only manage members of this community.`\n );\n }\n\n const _deleteMembershipGraphStart = Date.now();\n const _deleteMembershipTraceEmitter = requestContext.getStore()?.traceEmitter;\n _deleteMembershipTraceEmitter?.({ type: \"graph_start\", name: \"network_membership\" });\n const result = await graphs.networkMembership.invoke({\n userId: context.userId,\n networkId,\n targetUserId,\n operationMode: 'delete' as const,\n });\n const _deleteMembershipGraphMs = Date.now() - _deleteMembershipGraphStart;\n _deleteMembershipTraceEmitter?.({ type: \"graph_end\", name: \"network_membership\", durationMs: _deleteMembershipGraphMs });\n\n if (result.mutationResult) {\n if (result.mutationResult.success) {\n return success({\n removed: true,\n message: result.mutationResult.message,\n _graphTimings: [{ name: 'network_membership', durationMs: _deleteMembershipGraphMs, agents: result.agentTimings ?? [] }],\n });\n }\n return error(result.mutationResult.error || \"Failed to remove member.\");\n }\n return error(\"Failed to remove member.\");\n },\n });\n\n return [readIndexes, readIndexMemberships, updateNetwork, createNetwork, deleteNetwork, createNetworkMembership, deleteNetworkMembership] as const;\n}\n"]}
|
|
@@ -118,6 +118,11 @@ declare function getModelConfig(config?: ModelConfig): {
|
|
|
118
118
|
readonly temperature: 0.3;
|
|
119
119
|
readonly maxTokens: 512;
|
|
120
120
|
};
|
|
121
|
+
readonly networkRecommender: {
|
|
122
|
+
readonly model: "google/gemini-2.5-flash";
|
|
123
|
+
readonly temperature: 0.2;
|
|
124
|
+
readonly maxTokens: 512;
|
|
125
|
+
};
|
|
121
126
|
readonly chat: {
|
|
122
127
|
readonly model: string;
|
|
123
128
|
readonly maxTokens: 8192;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.config.d.ts","sourceRoot":"/","sources":["shared/agent/model.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC7F;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,mBAAmB,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;CACvE;AAED,iBAAS,cAAc,CAAC,MAAM,CAAC,EAAE,WAAW
|
|
1
|
+
{"version":3,"file":"model.config.d.ts","sourceRoot":"/","sources":["shared/agent/model.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC7F;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,mBAAmB,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;CACvE;AAED,iBAAS,cAAc,CAAC,MAAM,CAAC,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BA+BmD,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC;;;;EAK/I;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,UAAU,CAAC,OAAO,cAAc,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,CAEzG;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,UAAU,CAAC,OAAO,cAAc,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,CA+B5G"}
|
|
@@ -25,6 +25,7 @@ function getModelConfig(config) {
|
|
|
25
25
|
premiseDecomposer: { model: "google/gemini-2.5-flash" },
|
|
26
26
|
premiseIndexer: { model: "google/gemini-2.5-flash" },
|
|
27
27
|
userContextGenerator: { model: "google/gemini-2.5-flash", temperature: 0.3, maxTokens: 512 },
|
|
28
|
+
networkRecommender: { model: "google/gemini-2.5-flash", temperature: 0.2, maxTokens: 512 },
|
|
28
29
|
chat: {
|
|
29
30
|
model: config?.chatModel ?? process.env.CHAT_MODEL ?? "google/gemini-3-pro-preview",
|
|
30
31
|
maxTokens: 8192,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.config.js","sourceRoot":"/","sources":["shared/agent/model.config.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChatOpenAI } from \"@langchain/openai\";\n\n/** Settings that can be configured per agent. */\nexport interface ModelSettings {\n model: string;\n temperature?: number;\n maxTokens?: number;\n reasoning?: { effort?: 'minimal' | 'low' | 'medium' | 'high' | 'xhigh'; exclude?: boolean };\n}\n\n/**\n * Runtime configuration for the protocol package.\n * When passed via `ToolContext.modelConfig`, all fields (`apiKey`, `baseURL`, `chatModel`,\n * `chatReasoningEffort`) are honored by `ChatAgent` when the chat graph runs.\n * Other protocol agents don't read from `ToolContext` but may accept an explicit `ModelConfig`\n * as a direct parameter to `createModel()`.\n * All fields fall back to environment variables if not provided.\n */\nexport interface ModelConfig {\n /** OpenRouter API key. Falls back to OPENROUTER_API_KEY env var. */\n apiKey?: string;\n /** OpenRouter base URL. Falls back to OPENROUTER_BASE_URL env var. */\n baseURL?: string;\n /** Override the chat agent model. Falls back to CHAT_MODEL env var. */\n chatModel?: string;\n /** Override the chat reasoning effort. Falls back to CHAT_REASONING_EFFORT env var. */\n chatReasoningEffort?: 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';\n}\n\nfunction getModelConfig(config?: ModelConfig) {\n return {\n intentInferrer: { model: \"google/gemini-2.5-flash\" },\n intentIndexer: { model: \"google/gemini-2.5-flash\" },\n intentVerifier: { model: \"google/gemini-2.5-flash\" },\n intentReconciler: { model: \"google/gemini-2.5-flash\" },\n intentClarifier: { model: \"google/gemini-2.5-flash\" },\n profileGenerator: { model: \"google/gemini-2.5-flash\" },\n hydeGenerator: { model: \"google/gemini-2.5-flash\" },\n lensInferrer: { model: \"google/gemini-2.5-flash\" },\n opportunityEvaluator: { model: \"google/gemini-2.5-flash\" },\n opportunityPresenter: { model: \"google/gemini-2.5-flash\" },\n negotiator: { model: \"google/gemini-2.5-flash\" },\n homeCategorizer: { model: \"google/gemini-2.5-flash\" },\n suggestionGenerator: { model: \"google/gemini-2.5-flash\", temperature: 0.4, maxTokens: 512 },\n chatTitleGenerator: { model: \"google/gemini-2.5-flash\", temperature: 0.3, maxTokens: 32 },\n negotiationInsights: { model: \"google/gemini-2.5-flash\", temperature: 0.4, maxTokens: 512 },\n chatContextSummarizer: { model: \"google/gemini-2.5-flash\", temperature: 0.2, maxTokens: 512 },\n discoveryQuestionGenerator: { model: \"google/gemini-2.5-flash\", temperature: 0.5, maxTokens: 1024 },\n questioner: { model: \"google/gemini-2.5-flash\", temperature: 0.5, maxTokens: 1024 },\n negotiationSummarizer: { model: \"google/gemini-2.5-flash\", temperature: 0.2, maxTokens: 256 },\n inviteGenerator: { model: \"google/gemini-2.5-flash\", temperature: 0.3, maxTokens: 512 },\n premiseAnalyzer: { model: \"google/gemini-2.5-flash\" },\n premiseDecomposer: { model: \"google/gemini-2.5-flash\" },\n premiseIndexer: { model: \"google/gemini-2.5-flash\" },\n userContextGenerator: { model: \"google/gemini-2.5-flash\", temperature: 0.3, maxTokens: 512 },\n chat: {\n model: config?.chatModel ?? process.env.CHAT_MODEL ?? \"google/gemini-3-pro-preview\",\n maxTokens: 8192,\n reasoning: {\n effort: (config?.chatReasoningEffort ?? process.env.CHAT_REASONING_EFFORT ?? \"low\") as NonNullable<ModelSettings[\"reasoning\"]>[\"effort\"],\n exclude: true,\n },\n },\n } as const;\n}\n\n/**\n * Returns the model name string for the given agent key.\n * @param agent - Key from MODEL_CONFIG identifying which agent's settings to use.\n * @param config - Optional runtime config overrides.\n */\nexport function getModelName(agent: keyof ReturnType<typeof getModelConfig>, config?: ModelConfig): string {\n return getModelConfig(config)[agent].model;\n}\n\n/**\n * Creates a ChatOpenAI instance configured for OpenRouter.\n * @param agent - Key identifying which agent's model settings to use.\n * @param config - Optional runtime config overrides.\n */\nexport function createModel(agent: keyof ReturnType<typeof getModelConfig>, config?: ModelConfig): ChatOpenAI {\n const apiKey = config?.apiKey ?? process.env.OPENROUTER_API_KEY;\n if (!apiKey?.trim()) {\n throw new Error(`createModel(${agent}): OPENROUTER_API_KEY is required. Pass via the config argument, ToolContext.modelConfig.apiKey, or set the OPENROUTER_API_KEY environment variable.`);\n }\n const cfg = getModelConfig(config)[agent] as ModelSettings;\n // Hard upper bound on a single LLM call. Without this, langchain's HTTP\n // client waits until the upstream cuts the socket (~3 minutes via\n // OpenRouter), blocking the entire chat response. 60 s is generous enough\n // for slow providers but bounds the worst case.\n const timeoutEnv = Number.parseInt(process.env.OPENROUTER_REQUEST_TIMEOUT_MS ?? \"\", 10);\n const timeout = Number.isFinite(timeoutEnv) && timeoutEnv > 0 ? timeoutEnv : 60_000;\n // ChatOpenAI defaults to maxRetries=2. That means a single hung upstream\n // provider gets retried up to 2 more times, each waiting `timeout` before\n // failing — so worst-case latency becomes timeout * 3. Cap retries at 1\n // so the worst case stays bounded at ~2 * timeout. Configurable via\n // OPENROUTER_MAX_RETRIES.\n const retriesEnv = Number.parseInt(process.env.OPENROUTER_MAX_RETRIES ?? \"\", 10);\n const maxRetries = Number.isFinite(retriesEnv) && retriesEnv >= 0 ? retriesEnv : 1;\n return new ChatOpenAI({\n model: cfg.model,\n configuration: {\n baseURL: config?.baseURL ?? process.env.OPENROUTER_BASE_URL ?? \"https://openrouter.ai/api/v1\",\n apiKey,\n },\n temperature: cfg.temperature,\n maxTokens: cfg.maxTokens,\n timeout,\n maxRetries,\n ...(cfg.reasoning && { modelKwargs: { reasoning: cfg.reasoning } }),\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"model.config.js","sourceRoot":"/","sources":["shared/agent/model.config.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChatOpenAI } from \"@langchain/openai\";\n\n/** Settings that can be configured per agent. */\nexport interface ModelSettings {\n model: string;\n temperature?: number;\n maxTokens?: number;\n reasoning?: { effort?: 'minimal' | 'low' | 'medium' | 'high' | 'xhigh'; exclude?: boolean };\n}\n\n/**\n * Runtime configuration for the protocol package.\n * When passed via `ToolContext.modelConfig`, all fields (`apiKey`, `baseURL`, `chatModel`,\n * `chatReasoningEffort`) are honored by `ChatAgent` when the chat graph runs.\n * Other protocol agents don't read from `ToolContext` but may accept an explicit `ModelConfig`\n * as a direct parameter to `createModel()`.\n * All fields fall back to environment variables if not provided.\n */\nexport interface ModelConfig {\n /** OpenRouter API key. Falls back to OPENROUTER_API_KEY env var. */\n apiKey?: string;\n /** OpenRouter base URL. Falls back to OPENROUTER_BASE_URL env var. */\n baseURL?: string;\n /** Override the chat agent model. Falls back to CHAT_MODEL env var. */\n chatModel?: string;\n /** Override the chat reasoning effort. Falls back to CHAT_REASONING_EFFORT env var. */\n chatReasoningEffort?: 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';\n}\n\nfunction getModelConfig(config?: ModelConfig) {\n return {\n intentInferrer: { model: \"google/gemini-2.5-flash\" },\n intentIndexer: { model: \"google/gemini-2.5-flash\" },\n intentVerifier: { model: \"google/gemini-2.5-flash\" },\n intentReconciler: { model: \"google/gemini-2.5-flash\" },\n intentClarifier: { model: \"google/gemini-2.5-flash\" },\n profileGenerator: { model: \"google/gemini-2.5-flash\" },\n hydeGenerator: { model: \"google/gemini-2.5-flash\" },\n lensInferrer: { model: \"google/gemini-2.5-flash\" },\n opportunityEvaluator: { model: \"google/gemini-2.5-flash\" },\n opportunityPresenter: { model: \"google/gemini-2.5-flash\" },\n negotiator: { model: \"google/gemini-2.5-flash\" },\n homeCategorizer: { model: \"google/gemini-2.5-flash\" },\n suggestionGenerator: { model: \"google/gemini-2.5-flash\", temperature: 0.4, maxTokens: 512 },\n chatTitleGenerator: { model: \"google/gemini-2.5-flash\", temperature: 0.3, maxTokens: 32 },\n negotiationInsights: { model: \"google/gemini-2.5-flash\", temperature: 0.4, maxTokens: 512 },\n chatContextSummarizer: { model: \"google/gemini-2.5-flash\", temperature: 0.2, maxTokens: 512 },\n discoveryQuestionGenerator: { model: \"google/gemini-2.5-flash\", temperature: 0.5, maxTokens: 1024 },\n questioner: { model: \"google/gemini-2.5-flash\", temperature: 0.5, maxTokens: 1024 },\n negotiationSummarizer: { model: \"google/gemini-2.5-flash\", temperature: 0.2, maxTokens: 256 },\n inviteGenerator: { model: \"google/gemini-2.5-flash\", temperature: 0.3, maxTokens: 512 },\n premiseAnalyzer: { model: \"google/gemini-2.5-flash\" },\n premiseDecomposer: { model: \"google/gemini-2.5-flash\" },\n premiseIndexer: { model: \"google/gemini-2.5-flash\" },\n userContextGenerator: { model: \"google/gemini-2.5-flash\", temperature: 0.3, maxTokens: 512 },\n networkRecommender: { model: \"google/gemini-2.5-flash\", temperature: 0.2, maxTokens: 512 },\n chat: {\n model: config?.chatModel ?? process.env.CHAT_MODEL ?? \"google/gemini-3-pro-preview\",\n maxTokens: 8192,\n reasoning: {\n effort: (config?.chatReasoningEffort ?? process.env.CHAT_REASONING_EFFORT ?? \"low\") as NonNullable<ModelSettings[\"reasoning\"]>[\"effort\"],\n exclude: true,\n },\n },\n } as const;\n}\n\n/**\n * Returns the model name string for the given agent key.\n * @param agent - Key from MODEL_CONFIG identifying which agent's settings to use.\n * @param config - Optional runtime config overrides.\n */\nexport function getModelName(agent: keyof ReturnType<typeof getModelConfig>, config?: ModelConfig): string {\n return getModelConfig(config)[agent].model;\n}\n\n/**\n * Creates a ChatOpenAI instance configured for OpenRouter.\n * @param agent - Key identifying which agent's model settings to use.\n * @param config - Optional runtime config overrides.\n */\nexport function createModel(agent: keyof ReturnType<typeof getModelConfig>, config?: ModelConfig): ChatOpenAI {\n const apiKey = config?.apiKey ?? process.env.OPENROUTER_API_KEY;\n if (!apiKey?.trim()) {\n throw new Error(`createModel(${agent}): OPENROUTER_API_KEY is required. Pass via the config argument, ToolContext.modelConfig.apiKey, or set the OPENROUTER_API_KEY environment variable.`);\n }\n const cfg = getModelConfig(config)[agent] as ModelSettings;\n // Hard upper bound on a single LLM call. Without this, langchain's HTTP\n // client waits until the upstream cuts the socket (~3 minutes via\n // OpenRouter), blocking the entire chat response. 60 s is generous enough\n // for slow providers but bounds the worst case.\n const timeoutEnv = Number.parseInt(process.env.OPENROUTER_REQUEST_TIMEOUT_MS ?? \"\", 10);\n const timeout = Number.isFinite(timeoutEnv) && timeoutEnv > 0 ? timeoutEnv : 60_000;\n // ChatOpenAI defaults to maxRetries=2. That means a single hung upstream\n // provider gets retried up to 2 more times, each waiting `timeout` before\n // failing — so worst-case latency becomes timeout * 3. Cap retries at 1\n // so the worst case stays bounded at ~2 * timeout. Configurable via\n // OPENROUTER_MAX_RETRIES.\n const retriesEnv = Number.parseInt(process.env.OPENROUTER_MAX_RETRIES ?? \"\", 10);\n const maxRetries = Number.isFinite(retriesEnv) && retriesEnv >= 0 ? retriesEnv : 1;\n return new ChatOpenAI({\n model: cfg.model,\n configuration: {\n baseURL: config?.baseURL ?? process.env.OPENROUTER_BASE_URL ?? \"https://openrouter.ai/api/v1\",\n apiKey,\n },\n temperature: cfg.temperature,\n maxTokens: cfg.maxTokens,\n timeout,\n maxRetries,\n ...(cfg.reasoning && { modelKwargs: { reasoning: cfg.reasoning } }),\n });\n}\n"]}
|
|
@@ -392,6 +392,24 @@ export interface ToolDeps {
|
|
|
392
392
|
opportunity: CompiledOpportunityGraph;
|
|
393
393
|
premise: CompiledGraph;
|
|
394
394
|
};
|
|
395
|
+
/**
|
|
396
|
+
* Optional network ranking override for `read_networks`. Injected by tests or custom compositions.
|
|
397
|
+
* When absent, defaults to `NetworkRecommender.invoke()` with a lazy module-level singleton.
|
|
398
|
+
*/
|
|
399
|
+
networkRanker?: (input: {
|
|
400
|
+
userProfile: {
|
|
401
|
+
bio: string;
|
|
402
|
+
location: string;
|
|
403
|
+
interests: string[];
|
|
404
|
+
skills: string[];
|
|
405
|
+
};
|
|
406
|
+
networks: Array<{
|
|
407
|
+
networkId: string;
|
|
408
|
+
renderedContext: string;
|
|
409
|
+
}>;
|
|
410
|
+
}) => Promise<{
|
|
411
|
+
rankedNetworkIds: string[];
|
|
412
|
+
} | null>;
|
|
395
413
|
}
|
|
396
414
|
export declare function success<T>(data: T): string;
|
|
397
415
|
export declare function error(message: string, debugSteps?: Array<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.helpers.d.ts","sourceRoot":"/","sources":["shared/agent/tool.helpers.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tool.helpers.d.ts","sourceRoot":"/","sources":["shared/agent/tool.helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.helpers.js","sourceRoot":"/","sources":["shared/agent/tool.helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\nimport type { ModelConfig } from \"./model.config.js\";\nimport type { ProfileDocument } from \"../schemas/profile.schema.js\";\nimport type {\n ChatGraphCompositeDatabase,\n NetworkMembership,\n UserRecord,\n UserDatabase,\n SystemDatabase,\n NegotiationGraphDatabase,\n} from \"../interfaces/database.interface.js\";\nimport type { Scraper } from \"../interfaces/scraper.interface.js\";\nimport type { Cache, HydeCache } from \"../interfaces/cache.interface.js\";\nimport type { CompiledOpportunityGraph } from \"../../opportunity/opportunity.discover.js\";\nimport type { IntegrationAdapter } from \"../interfaces/integration.interface.js\";\nimport type { ContactServiceAdapter } from \"../interfaces/contact.interface.js\";\nimport type { ProfileEnricher } from \"../interfaces/enrichment.interface.js\";\nimport type { IntentGraphQueue } from \"../interfaces/queue.interface.js\";\nimport type { ChatSessionReader } from \"../interfaces/chat-session.interface.js\";\nimport type { ChatSummaryReader } from \"../interfaces/chat-summary.interface.js\";\nimport type { ChatMessageWriter } from \"../interfaces/chat-message-writer.interface.js\";\nimport type { QuestionGeneratorReader } from \"../interfaces/question-generator.interface.js\";\nimport type { NegotiationSummaryReader } from \"../interfaces/negotiation-summary.interface.js\";\nimport type { Embedder } from \"../interfaces/embedder.interface.js\";\nimport type { AgentDatabase } from \"../interfaces/agent.interface.js\";\nimport type { NegotiationTimeoutQueue } from \"../interfaces/negotiation-events.interface.js\";\nimport type { AgentDispatcher } from \"../interfaces/agent-dispatcher.interface.js\";\nimport type { DeliveryLedger } from \"../interfaces/delivery-ledger.interface.js\";\nimport type { MintConnectLink } from \"../interfaces/connect-link.interface.js\";\nimport type { QuestionerDatabase } from \"../interfaces/questioner.interface.js\";\nimport type { QuestionerEnqueueFn } from \"../../questioner/questioner.types.js\";\nimport type { PendingQuestionSummary } from \"../schemas/pending-question.schema.js\";\nimport type { DiscoveryRunQueue, DiscoveryRunStore } from \"../interfaces/discovery-run.interface.js\";\nimport type { ProfileRunQueue, ProfileRunStore } from \"../interfaces/profile-run.interface.js\";\n\nexport type ProfileContext = ProfileDocument | null;\n\nexport interface ToolErrorReport {\n operation: string;\n subsystem?: string;\n toolName?: string;\n userId?: string;\n tags?: Record<string, string | number | boolean | null | undefined>;\n context?: Record<string, unknown>;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// COMPILED GRAPH TYPE\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/** Minimal interface for an invokable compiled LangGraph. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type CompiledGraph = { invoke: (input: any) => Promise<any> };\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// TOOL CONTEXT TYPES\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Resolved context available to every tool handler.\n * Contains the current user and optional index identity, resolved from DB at init.\n * The LLM can see this context (via system prompt) but cannot change it.\n */\nexport interface ResolvedToolContext {\n // Legacy flat fields (kept for backwards compatibility in tools/prompts).\n userId: string;\n userName: string;\n userEmail: string;\n networkId?: string;\n indexName?: string;\n /** True when chat is index-scoped and the user owns the index. */\n isOwner?: boolean;\n // Rich identity context for prompt/tool orchestration.\n user: UserRecord;\n userProfile: ProfileContext;\n userNetworks: NetworkMembership[];\n /**\n * The set of index IDs this caller can reach in the current request.\n * For unscoped chats: every index the user is a member of.\n * For network-scoped agents: `[boundNetwork, personalIndex]`.\n * This is the same set used to clamp the DB-level systemDb.\n * Tools that filter intents/profiles default to this set; `networkId` is\n * the \"primary focus\" hint, not a read filter.\n */\n indexScope: string[];\n scopedIndex?: {\n id: string;\n title: string;\n prompt: string | null;\n type?: string;\n metadata?: Record<string, unknown>;\n permissions?: Record<string, unknown>;\n };\n scopedMembershipRole?: \"owner\" | \"member\";\n /** True when user has not completed onboarding (onboarding.completedAt is null). */\n isOnboarding: boolean;\n /** True when the user has a non-empty name. */\n hasName: boolean;\n /** Chat session ID when tools are used in a chat; used for draft opportunities (context.conversationId). */\n sessionId?: string;\n /** True when the request originates from an MCP transport (no interactive UI available). */\n isMcp?: boolean;\n /** Agent ID when the request originates from an API key linked to an agent. */\n agentId?: string;\n /**\n * Receiver's rendering surface declared by the MCP client via the\n * `x-index-surface` request header. `'telegram'` means the MCP response is\n * being rendered inside a Telegram chat; anything\n * else (including `undefined`) is treated as web. Forwarded into\n * `mintConnectLink` so the click-time redirect can branch.\n */\n clientSurface?: 'telegram' | 'web';\n}\n\n/**\n * Dependencies passed when creating tools for a user session.\n * Includes DB adapters, embedder, and scraper.\n *\n * Note: userDb and systemDb are optional inputs - if not provided, createChatTools\n * will create them internally from the chatDatabaseAdapter singleton.\n */\nexport interface ToolContext {\n userId: string;\n /** @deprecated Use userDb or systemDb instead. Kept for backwards compatibility. */\n database: ChatGraphCompositeDatabase;\n /** Context-bound database for accessing the authenticated user's own resources. Created internally if not provided. */\n userDb?: UserDatabase;\n /** Context-bound database for LLM/system operations on cross-user resources within shared indexes. Created internally if not provided. */\n systemDb?: SystemDatabase;\n embedder: Embedder;\n scraper: Scraper;\n /** When set, chat is scoped to this index; tools use it as default for read_intents and create_intent. */\n networkId?: string;\n /**\n * Optional override of the resolved `indexScope`. `resolveChatContext` always\n * computes `indexScope` from the user's memberships (clamped to [bound,\n * personal] when `networkId` is set). When the caller has already computed\n * a clamped scope — notably the MCP server, which clamps via\n * `applyNetworkScopeToContext` for network-scoped agents — passing it on\n * `ToolContext.indexScope` causes `createChatTools` (in tool.factory.ts) to\n * override `resolvedContext.indexScope` with this value rather than the\n * freshly computed one. See ResolvedToolContext.indexScope for the\n * resolved-side semantics.\n */\n indexScope?: string[];\n /** Chat session ID when creating tools for a chat; enables draft opportunities with context.conversationId. */\n sessionId?: string;\n\n // ─── Protocol-level dependencies (injected by composition root) ──────────\n /** General-purpose cache (e.g. for tool results). */\n cache: Cache;\n /** Dedicated cache for HyDE graph (may be same instance as cache). */\n hydeCache: HydeCache;\n /** External integration platform adapter (OAuth, tool actions). */\n integration: IntegrationAdapter;\n /** Queue for enqueuing follow-up intent processing (HyDE generation/deletion). */\n intentQueue: IntentGraphQueue;\n /** Contact management operations. */\n contactService: ContactServiceAdapter;\n /** Chat session reader for loading conversation history. */\n chatSession: ChatSessionReader;\n /** Read-through chat-session digest. Optional; consumers fall back to undefined `chatContext`. */\n chatSummary?: ChatSummaryReader;\n /** Writes user messages into the user's most-recent chat session (Slice 5 MCP elicitation). */\n chatMessageWriter?: ChatMessageWriter;\n /** Decision-question generator. Optional; consumers fall back to no `questions`. */\n questionGenerator?: QuestionGeneratorReader;\n /**\n * Optional async question enqueue callback. When provided, question generation\n * is dispatched asynchronously to the QuestionerQueue instead of running inline.\n * Injected by the composition root when QUESTIONER_ENABLED=true.\n */\n questionerEnqueue?: QuestionerEnqueueFn;\n /** Negotiation-digest summarizer. Optional; consumers fall back to deterministic digests. */\n negotiationSummary?: NegotiationSummaryReader;\n /** Profile enrichment from external data sources. */\n enricher: ProfileEnricher;\n /** Database adapter for negotiation/conversation operations. */\n negotiationDatabase: NegotiationGraphDatabase;\n /** Integration importer for bulk contact import from toolkits. */\n integrationImporter: {\n importContacts(userId: string, toolkit: string): Promise<{\n imported: number;\n skipped: number;\n newContacts: number;\n existingContacts: number;\n }>;\n };\n /** Factory for user-scoped database access. */\n createUserDatabase: (db: ChatGraphCompositeDatabase, userId: string) => UserDatabase;\n /** Factory for system-scoped database access. */\n createSystemDatabase: (db: ChatGraphCompositeDatabase, userId: string, indexScope: string[], embedder?: Embedder) => SystemDatabase;\n /** Optional runtime LLM config. Pass to override env vars for API key, model, etc. */\n modelConfig?: ModelConfig;\n /** Manages negotiation timeout jobs (optional — enables AI fallback on external agent timeout). */\n negotiationTimeoutQueue?: NegotiationTimeoutQueue;\n /** Agent registry database adapter (optional — absent when host does not support agents). */\n agentDatabase?: AgentDatabase;\n /** Grants the default system-agent permissions after onboarding (optional). */\n grantDefaultSystemPermissions?: (userId: string) => Promise<void>;\n /** Dispatcher for routing negotiation turns to personal agents (optional — falls back to system AI). */\n agentDispatcher?: AgentDispatcher;\n /** Enqueue a negotiate_existing job after introducer approval (optional). */\n queueNegotiateExisting?: (opportunityId: string, userId: string) => Promise<void>;\n /** Delivery ledger for committing opportunity delivery rows (optional — absent in chat context). */\n deliveryLedger?: DeliveryLedger;\n /** Persistence for async MCP discovery runs (optional — absent in non-MCP/test contexts). */\n discoveryRuns?: DiscoveryRunStore;\n /** Queue for async MCP discovery run execution (optional — absent in non-MCP/test contexts). */\n discoveryRunQueue?: DiscoveryRunQueue;\n /** Persistence for async MCP profile runs (optional — absent in non-MCP/test contexts). */\n profileRuns?: ProfileRunStore;\n /** Queue for async MCP profile run execution (optional — absent in non-MCP/test contexts). */\n profileRunQueue?: ProfileRunQueue;\n /** Mints a short-lived connect token for opportunity accept links (optional — absent in non-MCP contexts). */\n mintConnectToken?: (userId: string, opportunityId: string) => Promise<string>;\n /** Mints (or reuses) a short connect link, snapshotting the greeting (optional — absent in non-MCP contexts). */\n mintConnectLink?: MintConnectLink;\n /** Frontend base URL for building profile links (e.g. https://index.network, optional). */\n frontendUrl?: string;\n /** API base URL for building opportunity accept links (e.g. https://protocol.index.network, optional). */\n apiBaseUrl?: string;\n /** Persistence for structured questions generated by the QuestionerAgent (optional). */\n questionerDatabase?: QuestionerDatabase;\n /** Optional host-side error reporter for swallowed protocol/tool errors. */\n reportToolError?: (error: unknown, report: ToolErrorReport) => void;\n /**\n * Optional host-side per-principal MCP call throttle. Invoked once per MCP\n * tool dispatch (after identity resolves, before any DB work). When the\n * returned decision is `allowed: false`, the dispatch short-circuits with a\n * rate-limit error carrying `retryAfterSec`. Absent in chat/test contexts.\n */\n mcpRateLimiter?: (input: { userId: string; agentId?: string; toolName: string }) => Promise<{\n allowed: boolean;\n retryAfterSec?: number;\n limit?: number;\n scope?: 'tool' | 'principal';\n }>;\n /** Optional premise lifecycle event callbacks. Fired by premise tools after successful operations. */\n premiseEvents?: {\n onCreated?: (premiseId: string, userId: string) => void;\n onUpdated?: (premiseId: string, userId: string) => void;\n onRetracted?: (premiseId: string, userId: string) => void;\n };\n}\n\n/**\n * All external dependencies needed to initialize the protocol tool engine.\n * The host application (composition root) must provide concrete implementations.\n * This is the subset of ToolContext that is NOT per-request (no userId, indexId, sessionId).\n */\nexport type ProtocolDeps = Omit<ToolContext, 'userId' | 'indexId' | 'sessionId' | 'userDb' | 'systemDb'>;\n\n/**\n * Thrown when a requested chat scope is invalid for the authenticated user.\n * Controllers can map this to an HTTP status code.\n */\nexport class ChatContextAccessError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly code: \"USER_NOT_FOUND\" | \"INDEX_NOT_FOUND\" | \"INDEX_MEMBERSHIP_REQUIRED\"\n ) {\n super(message);\n this.name = \"ChatContextAccessError\";\n }\n}\n\n/**\n * Resolve the canonical context used by chat tools and system prompt.\n * This preloads user identity, profile, index memberships, and scoped index role.\n */\nexport async function resolveChatContext(params: {\n database: Pick<\n ChatGraphCompositeDatabase,\n \"getUser\" | \"getProfile\" | \"getNetworkMemberships\" | \"getNetworkMembership\" | \"getNetwork\" | \"isIndexOwner\" | \"isNetworkMember\"\n >;\n userId: string;\n networkId?: string;\n /** Chat session ID for draft opportunities (stored as context.conversationId). */\n sessionId?: string;\n}): Promise<ResolvedToolContext> {\n const { database, userId, networkId, sessionId } = params;\n\n const [user, rawProfile, userNetworks] = await Promise.all([\n database.getUser(userId),\n database.getProfile(userId),\n database.getNetworkMemberships(userId),\n ]);\n\n const userProfile: ProfileContext = rawProfile ?? null;\n\n if (!user) {\n throw new ChatContextAccessError(\n \"User not found\",\n 404,\n \"USER_NOT_FOUND\"\n );\n }\n\n let scopedIndex: ResolvedToolContext[\"scopedIndex\"] = undefined;\n let scopedMembershipRole: ResolvedToolContext[\"scopedMembershipRole\"] = undefined;\n let isOwner = false;\n let indexName: string | undefined;\n\n if (networkId) {\n const [index, isMember, owner] = await Promise.all([\n database.getNetwork(networkId),\n database.isNetworkMember(networkId, userId),\n database.isIndexOwner(networkId, userId),\n ]);\n\n if (!index) {\n throw new ChatContextAccessError(\n \"Index not found\",\n 404,\n \"INDEX_NOT_FOUND\"\n );\n }\n\n if (!isMember) {\n throw new ChatContextAccessError(\n \"You are not a member of this index\",\n 403,\n \"INDEX_MEMBERSHIP_REQUIRED\"\n );\n }\n\n let membership = userNetworks.find((m) => m.networkId === index.id);\n if (membership === undefined) {\n membership = (await database.getNetworkMembership(index.id, userId)) ?? undefined;\n }\n scopedIndex = {\n id: index.id,\n title: index.title,\n prompt: membership?.indexPrompt ?? null,\n type: index.type ?? 'community',\n metadata: index.metadata ?? {},\n permissions: index.permissions ?? {},\n };\n isOwner = owner;\n indexName = index.title;\n scopedMembershipRole = owner ? \"owner\" : \"member\";\n }\n\n const userName = user.name ?? \"Unknown\";\n const userEmail = user.email ?? \"\";\n const hasName = !!user.name?.trim();\n\n // When scoped to an index, clamp the caller's reach to [scopedIndex, personalIndex]\n // so the chat's data model matches its \"focus\" semantic: a chat scoped to a\n // community sees that community plus the user's personal index, not their\n // other unrelated memberships. Mirrors the MCP path's clamp for network-scoped\n // agents (see applyNetworkScopeToContext / computeAgentIndexScope).\n const indexScope = networkId\n ? userNetworks\n .filter((m) => m.networkId === networkId || m.isPersonal === true)\n .map((m) => m.networkId)\n : userNetworks.map((m) => m.networkId);\n\n return {\n userId,\n userName,\n userEmail,\n networkId,\n indexName,\n isOwner,\n user,\n userProfile,\n userNetworks,\n indexScope,\n scopedIndex,\n scopedMembershipRole,\n isOnboarding: !(user.onboarding?.completedAt),\n hasName,\n ...(sessionId !== undefined ? { sessionId } : {}),\n };\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// DEFINE TOOL TYPE\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Type for the `defineTool` closure created in `createChatTools`.\n * Auto-injects resolved context and provides uniform logging / error handling.\n */\nexport type DefineTool = <T extends z.ZodType>(opts: {\n name: string;\n description: string;\n querySchema: T;\n handler: (input: { context: ResolvedToolContext; query: z.infer<T> }) => Promise<string>;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\n}) => any;\n\n/**\n * A raw tool definition before LangChain wrapping.\n * Used by the tool registry for direct HTTP invocation.\n */\nexport interface RawToolDefinition {\n name: string;\n description: string;\n schema: z.ZodType;\n handler: (input: { context: ResolvedToolContext; query: unknown }) => Promise<string>;\n}\n\n/**\n * Registry mapping tool names to their raw definitions.\n */\nexport type ToolRegistry = Map<string, RawToolDefinition>;\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// TOOL DEPENDENCIES\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Shared dependencies available to all tool domain factories.\n * Passed by `createChatTools` after compiling all subgraphs.\n */\nexport interface ToolDeps {\n /** @deprecated Use userDb or systemDb instead. Kept for backwards compatibility. */\n database: ChatGraphCompositeDatabase;\n /** Context-bound database for accessing the authenticated user's own resources. */\n userDb: UserDatabase;\n /** Context-bound database for LLM/system operations on cross-user resources within shared indexes. */\n systemDb: SystemDatabase;\n scraper: Scraper;\n embedder: import('../interfaces/embedder.interface.js').Embedder;\n cache: Cache;\n integration: IntegrationAdapter;\n contactService: ContactServiceAdapter;\n integrationImporter: {\n importContacts(userId: string, toolkit: string): Promise<{\n imported: number;\n skipped: number;\n newContacts: number;\n existingContacts: number;\n }>;\n };\n enricher: ProfileEnricher;\n /** Database adapter for negotiation/conversation operations. */\n negotiationDatabase: NegotiationGraphDatabase;\n /** Chat session reader for exposing the caller's past conversations as MCP tools. */\n chatSession?: ChatSessionReader;\n /** Read-through chat-session digest. Optional; consumers fall back to undefined `chatContext`. */\n chatSummary?: ChatSummaryReader;\n /** Writes user messages into the user's most-recent chat session (Slice 5 MCP elicitation). */\n chatMessageWriter?: ChatMessageWriter;\n /** Decision-question generator. Optional; consumers fall back to no `questions`. */\n questionGenerator?: QuestionGeneratorReader;\n /**\n * Optional async question enqueue callback. When provided, question generation\n * is dispatched asynchronously to the QuestionerQueue instead of running inline\n * via the `questionGenerator`. Injected by the composition root when\n * QUESTIONER_ENABLED=true.\n */\n questionerEnqueue?: QuestionerEnqueueFn;\n /**\n * Lookup pending questions for a user, optionally filtered by source.\n * Used by tools to attach contextually relevant questions to their results.\n * Injected by the composition root — absent when question delivery is disabled.\n */\n findPendingQuestions?: (\n userId: string,\n filters?: { sourceType?: string; sourceId?: string },\n ) => Promise<PendingQuestionSummary[]>;\n /** Negotiation-digest summarizer. Optional; consumers fall back to deterministic digests. */\n negotiationSummary?: NegotiationSummaryReader;\n /** Manages negotiation timeout jobs (optional — enables AI fallback on external agent timeout). */\n negotiationTimeoutQueue?: NegotiationTimeoutQueue;\n /** Agent registry database adapter (optional — absent when host does not support agents). */\n agentDatabase?: AgentDatabase;\n /** Grants the default system-agent permissions after onboarding (optional). */\n grantDefaultSystemPermissions?: (userId: string) => Promise<void>;\n /** Dispatcher for routing negotiation turns to personal agents (optional — falls back to system AI). */\n agentDispatcher?: AgentDispatcher;\n /** Delivery ledger for committing opportunity delivery rows (optional — absent in chat context). */\n deliveryLedger?: DeliveryLedger;\n /** Persistence for async MCP discovery runs (optional — absent in non-MCP/test contexts). */\n discoveryRuns?: DiscoveryRunStore;\n /** Queue for async MCP discovery run execution (optional — absent in non-MCP/test contexts). */\n discoveryRunQueue?: DiscoveryRunQueue;\n /** Persistence for async MCP profile runs (optional — absent in non-MCP/test contexts). */\n profileRuns?: ProfileRunStore;\n /** Queue for async MCP profile run execution (optional — absent in non-MCP/test contexts). */\n profileRunQueue?: ProfileRunQueue;\n /** Mints a short-lived connect token for opportunity accept links (optional — absent in non-MCP contexts). */\n mintConnectToken?: (userId: string, opportunityId: string) => Promise<string>;\n /** Mints (or reuses) a short connect link, snapshotting the greeting (optional — absent in non-MCP contexts). */\n mintConnectLink?: MintConnectLink;\n /** Frontend base URL for building profile links (e.g. https://index.network, optional). */\n frontendUrl?: string;\n /** API base URL for building opportunity accept links (e.g. https://protocol.index.network, optional). */\n apiBaseUrl?: string;\n /** Optional host-side error reporter for swallowed protocol/tool errors. */\n reportToolError?: (error: unknown, report: ToolErrorReport) => void;\n /**\n * Optional host-side per-principal MCP call throttle. Invoked once per MCP\n * tool dispatch (after identity resolves, before any DB work). When the\n * returned decision is `allowed: false`, the dispatch short-circuits with a\n * rate-limit error carrying `retryAfterSec`. Absent in chat/test contexts.\n */\n mcpRateLimiter?: (input: { userId: string; agentId?: string; toolName: string }) => Promise<{\n allowed: boolean;\n retryAfterSec?: number;\n limit?: number;\n scope?: 'tool' | 'principal';\n }>;\n /** Optional premise lifecycle event callbacks. Fired by premise tools after successful operations. */\n premiseEvents?: {\n onCreated?: (premiseId: string, userId: string) => void;\n onUpdated?: (premiseId: string, userId: string) => void;\n onRetracted?: (premiseId: string, userId: string) => void;\n };\n graphs: {\n profile: CompiledGraph;\n intent: CompiledGraph;\n index: CompiledGraph;\n networkMembership: CompiledGraph;\n intentIndex: CompiledGraph;\n opportunity: CompiledOpportunityGraph;\n premise: CompiledGraph;\n };\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// TOOL RESULT HELPERS\n// ═══════════════════════════════════════════════════════════════════════════════\n\nexport function success<T>(data: T): string {\n return JSON.stringify({ success: true, data });\n}\n\nexport function error(\n message: string,\n debugSteps?: Array<{ step: string; detail?: string; data?: Record<string, unknown> }>\n): string {\n return JSON.stringify({\n success: false,\n error: message,\n ...(debugSteps?.length ? { debugSteps } : {}),\n });\n}\n\n/** Return needsClarification for missing required fields. */\nexport function needsClarification(params: {\n missingFields: string[];\n message: string;\n}): string {\n return JSON.stringify({\n success: false,\n needsClarification: true,\n ...params,\n });\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// CONSTANTS & UTILITIES\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/** Matches http/https URLs in text; captures full URL. */\nconst URL_IN_TEXT_REGEX = /https?:\\/\\/[^\\s\"'<>)\\]]+/gi;\n\n/**\n * Matches bare domain URLs without protocol (e.g. github.com/foo, www.example.com).\n * Requires at least a SLD.TLD pattern followed by optional path.\n * Negative lookbehind ensures we don't double-match URLs already caught by URL_IN_TEXT_REGEX.\n */\nconst BARE_URL_REGEX = /(?<!\\w:\\/\\/)(?<![/\\w])(?:www\\.)?[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.(?:com|org|net|io|dev|co|ai|app|xyz|me|info|gg|so|sh|cc|ly|fm|tv|to|tech|design|network|world|edu|gov|mil|int|us|uk|eu|de|fr|ca|au|jp|cn|in|br|nl|se|no|fi|dk|ch|at|be|it|es|pt|pl|cz|ru|kr|tw|hk|sg|nz|za|mx|ar|cl|id|ph|th|vn|my|ie)(?:\\/[^\\s\"'<>)\\]]*)?/gi;\n\n/** UUID v4 format: 8-4-4-4-12 hex chars (e.g. c2505011-2e45-426e-81dd-b9abb9b72023) */\nexport const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Resolves an array of network IDs to their display titles.\n * Skips any IDs that don't resolve (deleted or invalid networks).\n */\nexport async function resolveIndexNames(\n database: { getNetwork(id: string): Promise<{ id: string; title: string } | null> },\n networkIds: string[]\n): Promise<string[]> {\n if (networkIds.length === 0) return [];\n const results = await Promise.all(\n networkIds.map(id => database.getNetwork(id))\n );\n return results.filter(Boolean).map(idx => idx!.title);\n}\n\n/**\n * Normalize a URL string: if it lacks a protocol, prepend \"https://\".\n * Returns the normalized URL or null if the result is not a valid URL.\n */\nexport function normalizeUrl(raw: string): string | null {\n let url = raw.replace(/[.,;:!?)]+$/, \"\").trim();\n if (!/^https?:\\/\\//i.test(url)) {\n url = `https://${url}`;\n }\n try {\n new URL(url);\n return url;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract unique, valid URLs from a string (e.g. user message or details).\n * Handles both full URLs (https://...) and bare domains (github.com/...).\n */\nexport function extractUrls(text: string): string[] {\n if (!text || typeof text !== \"string\") return [];\n\n const seen = new Set<string>();\n const out: string[] = [];\n\n // Pass 1: full protocol URLs\n const fullMatches = text.match(URL_IN_TEXT_REGEX) ?? [];\n for (const raw of fullMatches) {\n const url = normalizeUrl(raw);\n if (url && !seen.has(url)) {\n seen.add(url);\n out.push(url);\n }\n }\n\n // Pass 2: bare domain URLs (e.g. github.com/foo)\n const bareMatches = text.match(BARE_URL_REGEX) ?? [];\n for (const raw of bareMatches) {\n const url = normalizeUrl(raw);\n if (url && !seen.has(url)) {\n seen.add(url);\n out.push(url);\n }\n }\n\n return out;\n}\n\nconst SENSITIVE_FIELD_KEYS = new Set([\n \"secret\",\n \"webhooksecret\",\n \"password\",\n \"apikey\",\n \"token\",\n \"accesstoken\",\n \"refreshtoken\",\n \"privatekey\",\n \"authtoken\",\n \"bearertoken\",\n \"clientsecret\",\n]);\n\n/**\n * Recursively redacts sensitive field values from an arbitrary payload before\n * it is passed to a structured logger. Matches field names case-insensitively\n * and ignoring underscores, so `api_key`, `apiKey`, and `API_KEY` all match.\n * Non-sensitive fields are passed through unchanged. Never mutates the input —\n * returns a new value.\n *\n * Intended for structured-log redaction only. Do NOT use as a security\n * boundary for data in motion.\n *\n * @param value - Arbitrary JSON-like payload (query object, config blob, etc.)\n * @returns A new value with sensitive fields replaced by `\"[redacted]\"`.\n */\nexport function redactSensitiveFields(value: unknown): unknown {\n if (value === null || typeof value !== \"object\") return value;\n if (Array.isArray(value)) {\n return value.map((item) => redactSensitiveFields(item));\n }\n const out: Record<string, unknown> = {};\n for (const [key, inner] of Object.entries(value as Record<string, unknown>)) {\n const normalized = key.toLowerCase().replace(/_/g, \"\");\n if (SENSITIVE_FIELD_KEYS.has(normalized)) {\n out[key] = \"[redacted]\";\n } else {\n out[key] = redactSensitiveFields(inner);\n }\n }\n return out;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tool.helpers.js","sourceRoot":"/","sources":["shared/agent/tool.helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\nimport type { ModelConfig } from \"./model.config.js\";\nimport type { ProfileDocument } from \"../schemas/profile.schema.js\";\nimport type {\n ChatGraphCompositeDatabase,\n NetworkMembership,\n UserRecord,\n UserDatabase,\n SystemDatabase,\n NegotiationGraphDatabase,\n} from \"../interfaces/database.interface.js\";\nimport type { Scraper } from \"../interfaces/scraper.interface.js\";\nimport type { Cache, HydeCache } from \"../interfaces/cache.interface.js\";\nimport type { CompiledOpportunityGraph } from \"../../opportunity/opportunity.discover.js\";\nimport type { IntegrationAdapter } from \"../interfaces/integration.interface.js\";\nimport type { ContactServiceAdapter } from \"../interfaces/contact.interface.js\";\nimport type { ProfileEnricher } from \"../interfaces/enrichment.interface.js\";\nimport type { IntentGraphQueue } from \"../interfaces/queue.interface.js\";\nimport type { ChatSessionReader } from \"../interfaces/chat-session.interface.js\";\nimport type { ChatSummaryReader } from \"../interfaces/chat-summary.interface.js\";\nimport type { ChatMessageWriter } from \"../interfaces/chat-message-writer.interface.js\";\nimport type { QuestionGeneratorReader } from \"../interfaces/question-generator.interface.js\";\nimport type { NegotiationSummaryReader } from \"../interfaces/negotiation-summary.interface.js\";\nimport type { Embedder } from \"../interfaces/embedder.interface.js\";\nimport type { AgentDatabase } from \"../interfaces/agent.interface.js\";\nimport type { NegotiationTimeoutQueue } from \"../interfaces/negotiation-events.interface.js\";\nimport type { AgentDispatcher } from \"../interfaces/agent-dispatcher.interface.js\";\nimport type { DeliveryLedger } from \"../interfaces/delivery-ledger.interface.js\";\nimport type { MintConnectLink } from \"../interfaces/connect-link.interface.js\";\nimport type { QuestionerDatabase } from \"../interfaces/questioner.interface.js\";\nimport type { QuestionerEnqueueFn } from \"../../questioner/questioner.types.js\";\nimport type { PendingQuestionSummary } from \"../schemas/pending-question.schema.js\";\nimport type { DiscoveryRunQueue, DiscoveryRunStore } from \"../interfaces/discovery-run.interface.js\";\nimport type { ProfileRunQueue, ProfileRunStore } from \"../interfaces/profile-run.interface.js\";\n\nexport type ProfileContext = ProfileDocument | null;\n\nexport interface ToolErrorReport {\n operation: string;\n subsystem?: string;\n toolName?: string;\n userId?: string;\n tags?: Record<string, string | number | boolean | null | undefined>;\n context?: Record<string, unknown>;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// COMPILED GRAPH TYPE\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/** Minimal interface for an invokable compiled LangGraph. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type CompiledGraph = { invoke: (input: any) => Promise<any> };\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// TOOL CONTEXT TYPES\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Resolved context available to every tool handler.\n * Contains the current user and optional index identity, resolved from DB at init.\n * The LLM can see this context (via system prompt) but cannot change it.\n */\nexport interface ResolvedToolContext {\n // Legacy flat fields (kept for backwards compatibility in tools/prompts).\n userId: string;\n userName: string;\n userEmail: string;\n networkId?: string;\n indexName?: string;\n /** True when chat is index-scoped and the user owns the index. */\n isOwner?: boolean;\n // Rich identity context for prompt/tool orchestration.\n user: UserRecord;\n userProfile: ProfileContext;\n userNetworks: NetworkMembership[];\n /**\n * The set of index IDs this caller can reach in the current request.\n * For unscoped chats: every index the user is a member of.\n * For network-scoped agents: `[boundNetwork, personalIndex]`.\n * This is the same set used to clamp the DB-level systemDb.\n * Tools that filter intents/profiles default to this set; `networkId` is\n * the \"primary focus\" hint, not a read filter.\n */\n indexScope: string[];\n scopedIndex?: {\n id: string;\n title: string;\n prompt: string | null;\n type?: string;\n metadata?: Record<string, unknown>;\n permissions?: Record<string, unknown>;\n };\n scopedMembershipRole?: \"owner\" | \"member\";\n /** True when user has not completed onboarding (onboarding.completedAt is null). */\n isOnboarding: boolean;\n /** True when the user has a non-empty name. */\n hasName: boolean;\n /** Chat session ID when tools are used in a chat; used for draft opportunities (context.conversationId). */\n sessionId?: string;\n /** True when the request originates from an MCP transport (no interactive UI available). */\n isMcp?: boolean;\n /** Agent ID when the request originates from an API key linked to an agent. */\n agentId?: string;\n /**\n * Receiver's rendering surface declared by the MCP client via the\n * `x-index-surface` request header. `'telegram'` means the MCP response is\n * being rendered inside a Telegram chat; anything\n * else (including `undefined`) is treated as web. Forwarded into\n * `mintConnectLink` so the click-time redirect can branch.\n */\n clientSurface?: 'telegram' | 'web';\n}\n\n/**\n * Dependencies passed when creating tools for a user session.\n * Includes DB adapters, embedder, and scraper.\n *\n * Note: userDb and systemDb are optional inputs - if not provided, createChatTools\n * will create them internally from the chatDatabaseAdapter singleton.\n */\nexport interface ToolContext {\n userId: string;\n /** @deprecated Use userDb or systemDb instead. Kept for backwards compatibility. */\n database: ChatGraphCompositeDatabase;\n /** Context-bound database for accessing the authenticated user's own resources. Created internally if not provided. */\n userDb?: UserDatabase;\n /** Context-bound database for LLM/system operations on cross-user resources within shared indexes. Created internally if not provided. */\n systemDb?: SystemDatabase;\n embedder: Embedder;\n scraper: Scraper;\n /** When set, chat is scoped to this index; tools use it as default for read_intents and create_intent. */\n networkId?: string;\n /**\n * Optional override of the resolved `indexScope`. `resolveChatContext` always\n * computes `indexScope` from the user's memberships (clamped to [bound,\n * personal] when `networkId` is set). When the caller has already computed\n * a clamped scope — notably the MCP server, which clamps via\n * `applyNetworkScopeToContext` for network-scoped agents — passing it on\n * `ToolContext.indexScope` causes `createChatTools` (in tool.factory.ts) to\n * override `resolvedContext.indexScope` with this value rather than the\n * freshly computed one. See ResolvedToolContext.indexScope for the\n * resolved-side semantics.\n */\n indexScope?: string[];\n /** Chat session ID when creating tools for a chat; enables draft opportunities with context.conversationId. */\n sessionId?: string;\n\n // ─── Protocol-level dependencies (injected by composition root) ──────────\n /** General-purpose cache (e.g. for tool results). */\n cache: Cache;\n /** Dedicated cache for HyDE graph (may be same instance as cache). */\n hydeCache: HydeCache;\n /** External integration platform adapter (OAuth, tool actions). */\n integration: IntegrationAdapter;\n /** Queue for enqueuing follow-up intent processing (HyDE generation/deletion). */\n intentQueue: IntentGraphQueue;\n /** Contact management operations. */\n contactService: ContactServiceAdapter;\n /** Chat session reader for loading conversation history. */\n chatSession: ChatSessionReader;\n /** Read-through chat-session digest. Optional; consumers fall back to undefined `chatContext`. */\n chatSummary?: ChatSummaryReader;\n /** Writes user messages into the user's most-recent chat session (Slice 5 MCP elicitation). */\n chatMessageWriter?: ChatMessageWriter;\n /** Decision-question generator. Optional; consumers fall back to no `questions`. */\n questionGenerator?: QuestionGeneratorReader;\n /**\n * Optional async question enqueue callback. When provided, question generation\n * is dispatched asynchronously to the QuestionerQueue instead of running inline.\n * Injected by the composition root when QUESTIONER_ENABLED=true.\n */\n questionerEnqueue?: QuestionerEnqueueFn;\n /** Negotiation-digest summarizer. Optional; consumers fall back to deterministic digests. */\n negotiationSummary?: NegotiationSummaryReader;\n /** Profile enrichment from external data sources. */\n enricher: ProfileEnricher;\n /** Database adapter for negotiation/conversation operations. */\n negotiationDatabase: NegotiationGraphDatabase;\n /** Integration importer for bulk contact import from toolkits. */\n integrationImporter: {\n importContacts(userId: string, toolkit: string): Promise<{\n imported: number;\n skipped: number;\n newContacts: number;\n existingContacts: number;\n }>;\n };\n /** Factory for user-scoped database access. */\n createUserDatabase: (db: ChatGraphCompositeDatabase, userId: string) => UserDatabase;\n /** Factory for system-scoped database access. */\n createSystemDatabase: (db: ChatGraphCompositeDatabase, userId: string, indexScope: string[], embedder?: Embedder) => SystemDatabase;\n /** Optional runtime LLM config. Pass to override env vars for API key, model, etc. */\n modelConfig?: ModelConfig;\n /** Manages negotiation timeout jobs (optional — enables AI fallback on external agent timeout). */\n negotiationTimeoutQueue?: NegotiationTimeoutQueue;\n /** Agent registry database adapter (optional — absent when host does not support agents). */\n agentDatabase?: AgentDatabase;\n /** Grants the default system-agent permissions after onboarding (optional). */\n grantDefaultSystemPermissions?: (userId: string) => Promise<void>;\n /** Dispatcher for routing negotiation turns to personal agents (optional — falls back to system AI). */\n agentDispatcher?: AgentDispatcher;\n /** Enqueue a negotiate_existing job after introducer approval (optional). */\n queueNegotiateExisting?: (opportunityId: string, userId: string) => Promise<void>;\n /** Delivery ledger for committing opportunity delivery rows (optional — absent in chat context). */\n deliveryLedger?: DeliveryLedger;\n /** Persistence for async MCP discovery runs (optional — absent in non-MCP/test contexts). */\n discoveryRuns?: DiscoveryRunStore;\n /** Queue for async MCP discovery run execution (optional — absent in non-MCP/test contexts). */\n discoveryRunQueue?: DiscoveryRunQueue;\n /** Persistence for async MCP profile runs (optional — absent in non-MCP/test contexts). */\n profileRuns?: ProfileRunStore;\n /** Queue for async MCP profile run execution (optional — absent in non-MCP/test contexts). */\n profileRunQueue?: ProfileRunQueue;\n /** Mints a short-lived connect token for opportunity accept links (optional — absent in non-MCP contexts). */\n mintConnectToken?: (userId: string, opportunityId: string) => Promise<string>;\n /** Mints (or reuses) a short connect link, snapshotting the greeting (optional — absent in non-MCP contexts). */\n mintConnectLink?: MintConnectLink;\n /** Frontend base URL for building profile links (e.g. https://index.network, optional). */\n frontendUrl?: string;\n /** API base URL for building opportunity accept links (e.g. https://protocol.index.network, optional). */\n apiBaseUrl?: string;\n /** Persistence for structured questions generated by the QuestionerAgent (optional). */\n questionerDatabase?: QuestionerDatabase;\n /** Optional host-side error reporter for swallowed protocol/tool errors. */\n reportToolError?: (error: unknown, report: ToolErrorReport) => void;\n /**\n * Optional host-side per-principal MCP call throttle. Invoked once per MCP\n * tool dispatch (after identity resolves, before any DB work). When the\n * returned decision is `allowed: false`, the dispatch short-circuits with a\n * rate-limit error carrying `retryAfterSec`. Absent in chat/test contexts.\n */\n mcpRateLimiter?: (input: { userId: string; agentId?: string; toolName: string }) => Promise<{\n allowed: boolean;\n retryAfterSec?: number;\n limit?: number;\n scope?: 'tool' | 'principal';\n }>;\n /** Optional premise lifecycle event callbacks. Fired by premise tools after successful operations. */\n premiseEvents?: {\n onCreated?: (premiseId: string, userId: string) => void;\n onUpdated?: (premiseId: string, userId: string) => void;\n onRetracted?: (premiseId: string, userId: string) => void;\n };\n}\n\n/**\n * All external dependencies needed to initialize the protocol tool engine.\n * The host application (composition root) must provide concrete implementations.\n * This is the subset of ToolContext that is NOT per-request (no userId, indexId, sessionId).\n */\nexport type ProtocolDeps = Omit<ToolContext, 'userId' | 'indexId' | 'sessionId' | 'userDb' | 'systemDb'>;\n\n/**\n * Thrown when a requested chat scope is invalid for the authenticated user.\n * Controllers can map this to an HTTP status code.\n */\nexport class ChatContextAccessError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly code: \"USER_NOT_FOUND\" | \"INDEX_NOT_FOUND\" | \"INDEX_MEMBERSHIP_REQUIRED\"\n ) {\n super(message);\n this.name = \"ChatContextAccessError\";\n }\n}\n\n/**\n * Resolve the canonical context used by chat tools and system prompt.\n * This preloads user identity, profile, index memberships, and scoped index role.\n */\nexport async function resolveChatContext(params: {\n database: Pick<\n ChatGraphCompositeDatabase,\n \"getUser\" | \"getProfile\" | \"getNetworkMemberships\" | \"getNetworkMembership\" | \"getNetwork\" | \"isIndexOwner\" | \"isNetworkMember\"\n >;\n userId: string;\n networkId?: string;\n /** Chat session ID for draft opportunities (stored as context.conversationId). */\n sessionId?: string;\n}): Promise<ResolvedToolContext> {\n const { database, userId, networkId, sessionId } = params;\n\n const [user, rawProfile, userNetworks] = await Promise.all([\n database.getUser(userId),\n database.getProfile(userId),\n database.getNetworkMemberships(userId),\n ]);\n\n const userProfile: ProfileContext = rawProfile ?? null;\n\n if (!user) {\n throw new ChatContextAccessError(\n \"User not found\",\n 404,\n \"USER_NOT_FOUND\"\n );\n }\n\n let scopedIndex: ResolvedToolContext[\"scopedIndex\"] = undefined;\n let scopedMembershipRole: ResolvedToolContext[\"scopedMembershipRole\"] = undefined;\n let isOwner = false;\n let indexName: string | undefined;\n\n if (networkId) {\n const [index, isMember, owner] = await Promise.all([\n database.getNetwork(networkId),\n database.isNetworkMember(networkId, userId),\n database.isIndexOwner(networkId, userId),\n ]);\n\n if (!index) {\n throw new ChatContextAccessError(\n \"Index not found\",\n 404,\n \"INDEX_NOT_FOUND\"\n );\n }\n\n if (!isMember) {\n throw new ChatContextAccessError(\n \"You are not a member of this index\",\n 403,\n \"INDEX_MEMBERSHIP_REQUIRED\"\n );\n }\n\n let membership = userNetworks.find((m) => m.networkId === index.id);\n if (membership === undefined) {\n membership = (await database.getNetworkMembership(index.id, userId)) ?? undefined;\n }\n scopedIndex = {\n id: index.id,\n title: index.title,\n prompt: membership?.indexPrompt ?? null,\n type: index.type ?? 'community',\n metadata: index.metadata ?? {},\n permissions: index.permissions ?? {},\n };\n isOwner = owner;\n indexName = index.title;\n scopedMembershipRole = owner ? \"owner\" : \"member\";\n }\n\n const userName = user.name ?? \"Unknown\";\n const userEmail = user.email ?? \"\";\n const hasName = !!user.name?.trim();\n\n // When scoped to an index, clamp the caller's reach to [scopedIndex, personalIndex]\n // so the chat's data model matches its \"focus\" semantic: a chat scoped to a\n // community sees that community plus the user's personal index, not their\n // other unrelated memberships. Mirrors the MCP path's clamp for network-scoped\n // agents (see applyNetworkScopeToContext / computeAgentIndexScope).\n const indexScope = networkId\n ? userNetworks\n .filter((m) => m.networkId === networkId || m.isPersonal === true)\n .map((m) => m.networkId)\n : userNetworks.map((m) => m.networkId);\n\n return {\n userId,\n userName,\n userEmail,\n networkId,\n indexName,\n isOwner,\n user,\n userProfile,\n userNetworks,\n indexScope,\n scopedIndex,\n scopedMembershipRole,\n isOnboarding: !(user.onboarding?.completedAt),\n hasName,\n ...(sessionId !== undefined ? { sessionId } : {}),\n };\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// DEFINE TOOL TYPE\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Type for the `defineTool` closure created in `createChatTools`.\n * Auto-injects resolved context and provides uniform logging / error handling.\n */\nexport type DefineTool = <T extends z.ZodType>(opts: {\n name: string;\n description: string;\n querySchema: T;\n handler: (input: { context: ResolvedToolContext; query: z.infer<T> }) => Promise<string>;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\n}) => any;\n\n/**\n * A raw tool definition before LangChain wrapping.\n * Used by the tool registry for direct HTTP invocation.\n */\nexport interface RawToolDefinition {\n name: string;\n description: string;\n schema: z.ZodType;\n handler: (input: { context: ResolvedToolContext; query: unknown }) => Promise<string>;\n}\n\n/**\n * Registry mapping tool names to their raw definitions.\n */\nexport type ToolRegistry = Map<string, RawToolDefinition>;\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// TOOL DEPENDENCIES\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Shared dependencies available to all tool domain factories.\n * Passed by `createChatTools` after compiling all subgraphs.\n */\nexport interface ToolDeps {\n /** @deprecated Use userDb or systemDb instead. Kept for backwards compatibility. */\n database: ChatGraphCompositeDatabase;\n /** Context-bound database for accessing the authenticated user's own resources. */\n userDb: UserDatabase;\n /** Context-bound database for LLM/system operations on cross-user resources within shared indexes. */\n systemDb: SystemDatabase;\n scraper: Scraper;\n embedder: import('../interfaces/embedder.interface.js').Embedder;\n cache: Cache;\n integration: IntegrationAdapter;\n contactService: ContactServiceAdapter;\n integrationImporter: {\n importContacts(userId: string, toolkit: string): Promise<{\n imported: number;\n skipped: number;\n newContacts: number;\n existingContacts: number;\n }>;\n };\n enricher: ProfileEnricher;\n /** Database adapter for negotiation/conversation operations. */\n negotiationDatabase: NegotiationGraphDatabase;\n /** Chat session reader for exposing the caller's past conversations as MCP tools. */\n chatSession?: ChatSessionReader;\n /** Read-through chat-session digest. Optional; consumers fall back to undefined `chatContext`. */\n chatSummary?: ChatSummaryReader;\n /** Writes user messages into the user's most-recent chat session (Slice 5 MCP elicitation). */\n chatMessageWriter?: ChatMessageWriter;\n /** Decision-question generator. Optional; consumers fall back to no `questions`. */\n questionGenerator?: QuestionGeneratorReader;\n /**\n * Optional async question enqueue callback. When provided, question generation\n * is dispatched asynchronously to the QuestionerQueue instead of running inline\n * via the `questionGenerator`. Injected by the composition root when\n * QUESTIONER_ENABLED=true.\n */\n questionerEnqueue?: QuestionerEnqueueFn;\n /**\n * Lookup pending questions for a user, optionally filtered by source.\n * Used by tools to attach contextually relevant questions to their results.\n * Injected by the composition root — absent when question delivery is disabled.\n */\n findPendingQuestions?: (\n userId: string,\n filters?: { sourceType?: string; sourceId?: string },\n ) => Promise<PendingQuestionSummary[]>;\n /** Negotiation-digest summarizer. Optional; consumers fall back to deterministic digests. */\n negotiationSummary?: NegotiationSummaryReader;\n /** Manages negotiation timeout jobs (optional — enables AI fallback on external agent timeout). */\n negotiationTimeoutQueue?: NegotiationTimeoutQueue;\n /** Agent registry database adapter (optional — absent when host does not support agents). */\n agentDatabase?: AgentDatabase;\n /** Grants the default system-agent permissions after onboarding (optional). */\n grantDefaultSystemPermissions?: (userId: string) => Promise<void>;\n /** Dispatcher for routing negotiation turns to personal agents (optional — falls back to system AI). */\n agentDispatcher?: AgentDispatcher;\n /** Delivery ledger for committing opportunity delivery rows (optional — absent in chat context). */\n deliveryLedger?: DeliveryLedger;\n /** Persistence for async MCP discovery runs (optional — absent in non-MCP/test contexts). */\n discoveryRuns?: DiscoveryRunStore;\n /** Queue for async MCP discovery run execution (optional — absent in non-MCP/test contexts). */\n discoveryRunQueue?: DiscoveryRunQueue;\n /** Persistence for async MCP profile runs (optional — absent in non-MCP/test contexts). */\n profileRuns?: ProfileRunStore;\n /** Queue for async MCP profile run execution (optional — absent in non-MCP/test contexts). */\n profileRunQueue?: ProfileRunQueue;\n /** Mints a short-lived connect token for opportunity accept links (optional — absent in non-MCP contexts). */\n mintConnectToken?: (userId: string, opportunityId: string) => Promise<string>;\n /** Mints (or reuses) a short connect link, snapshotting the greeting (optional — absent in non-MCP contexts). */\n mintConnectLink?: MintConnectLink;\n /** Frontend base URL for building profile links (e.g. https://index.network, optional). */\n frontendUrl?: string;\n /** API base URL for building opportunity accept links (e.g. https://protocol.index.network, optional). */\n apiBaseUrl?: string;\n /** Optional host-side error reporter for swallowed protocol/tool errors. */\n reportToolError?: (error: unknown, report: ToolErrorReport) => void;\n /**\n * Optional host-side per-principal MCP call throttle. Invoked once per MCP\n * tool dispatch (after identity resolves, before any DB work). When the\n * returned decision is `allowed: false`, the dispatch short-circuits with a\n * rate-limit error carrying `retryAfterSec`. Absent in chat/test contexts.\n */\n mcpRateLimiter?: (input: { userId: string; agentId?: string; toolName: string }) => Promise<{\n allowed: boolean;\n retryAfterSec?: number;\n limit?: number;\n scope?: 'tool' | 'principal';\n }>;\n /** Optional premise lifecycle event callbacks. Fired by premise tools after successful operations. */\n premiseEvents?: {\n onCreated?: (premiseId: string, userId: string) => void;\n onUpdated?: (premiseId: string, userId: string) => void;\n onRetracted?: (premiseId: string, userId: string) => void;\n };\n graphs: {\n profile: CompiledGraph;\n intent: CompiledGraph;\n index: CompiledGraph;\n networkMembership: CompiledGraph;\n intentIndex: CompiledGraph;\n opportunity: CompiledOpportunityGraph;\n premise: CompiledGraph;\n };\n /**\n * Optional network ranking override for `read_networks`. Injected by tests or custom compositions.\n * When absent, defaults to `NetworkRecommender.invoke()` with a lazy module-level singleton.\n */\n networkRanker?: (input: {\n userProfile: { bio: string; location: string; interests: string[]; skills: string[] };\n networks: Array<{ networkId: string; renderedContext: string }>;\n }) => Promise<{ rankedNetworkIds: string[] } | null>;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// TOOL RESULT HELPERS\n// ═══════════════════════════════════════════════════════════════════════════════\n\nexport function success<T>(data: T): string {\n return JSON.stringify({ success: true, data });\n}\n\nexport function error(\n message: string,\n debugSteps?: Array<{ step: string; detail?: string; data?: Record<string, unknown> }>\n): string {\n return JSON.stringify({\n success: false,\n error: message,\n ...(debugSteps?.length ? { debugSteps } : {}),\n });\n}\n\n/** Return needsClarification for missing required fields. */\nexport function needsClarification(params: {\n missingFields: string[];\n message: string;\n}): string {\n return JSON.stringify({\n success: false,\n needsClarification: true,\n ...params,\n });\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// CONSTANTS & UTILITIES\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/** Matches http/https URLs in text; captures full URL. */\nconst URL_IN_TEXT_REGEX = /https?:\\/\\/[^\\s\"'<>)\\]]+/gi;\n\n/**\n * Matches bare domain URLs without protocol (e.g. github.com/foo, www.example.com).\n * Requires at least a SLD.TLD pattern followed by optional path.\n * Negative lookbehind ensures we don't double-match URLs already caught by URL_IN_TEXT_REGEX.\n */\nconst BARE_URL_REGEX = /(?<!\\w:\\/\\/)(?<![/\\w])(?:www\\.)?[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.(?:com|org|net|io|dev|co|ai|app|xyz|me|info|gg|so|sh|cc|ly|fm|tv|to|tech|design|network|world|edu|gov|mil|int|us|uk|eu|de|fr|ca|au|jp|cn|in|br|nl|se|no|fi|dk|ch|at|be|it|es|pt|pl|cz|ru|kr|tw|hk|sg|nz|za|mx|ar|cl|id|ph|th|vn|my|ie)(?:\\/[^\\s\"'<>)\\]]*)?/gi;\n\n/** UUID v4 format: 8-4-4-4-12 hex chars (e.g. c2505011-2e45-426e-81dd-b9abb9b72023) */\nexport const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Resolves an array of network IDs to their display titles.\n * Skips any IDs that don't resolve (deleted or invalid networks).\n */\nexport async function resolveIndexNames(\n database: { getNetwork(id: string): Promise<{ id: string; title: string } | null> },\n networkIds: string[]\n): Promise<string[]> {\n if (networkIds.length === 0) return [];\n const results = await Promise.all(\n networkIds.map(id => database.getNetwork(id))\n );\n return results.filter(Boolean).map(idx => idx!.title);\n}\n\n/**\n * Normalize a URL string: if it lacks a protocol, prepend \"https://\".\n * Returns the normalized URL or null if the result is not a valid URL.\n */\nexport function normalizeUrl(raw: string): string | null {\n let url = raw.replace(/[.,;:!?)]+$/, \"\").trim();\n if (!/^https?:\\/\\//i.test(url)) {\n url = `https://${url}`;\n }\n try {\n new URL(url);\n return url;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract unique, valid URLs from a string (e.g. user message or details).\n * Handles both full URLs (https://...) and bare domains (github.com/...).\n */\nexport function extractUrls(text: string): string[] {\n if (!text || typeof text !== \"string\") return [];\n\n const seen = new Set<string>();\n const out: string[] = [];\n\n // Pass 1: full protocol URLs\n const fullMatches = text.match(URL_IN_TEXT_REGEX) ?? [];\n for (const raw of fullMatches) {\n const url = normalizeUrl(raw);\n if (url && !seen.has(url)) {\n seen.add(url);\n out.push(url);\n }\n }\n\n // Pass 2: bare domain URLs (e.g. github.com/foo)\n const bareMatches = text.match(BARE_URL_REGEX) ?? [];\n for (const raw of bareMatches) {\n const url = normalizeUrl(raw);\n if (url && !seen.has(url)) {\n seen.add(url);\n out.push(url);\n }\n }\n\n return out;\n}\n\nconst SENSITIVE_FIELD_KEYS = new Set([\n \"secret\",\n \"webhooksecret\",\n \"password\",\n \"apikey\",\n \"token\",\n \"accesstoken\",\n \"refreshtoken\",\n \"privatekey\",\n \"authtoken\",\n \"bearertoken\",\n \"clientsecret\",\n]);\n\n/**\n * Recursively redacts sensitive field values from an arbitrary payload before\n * it is passed to a structured logger. Matches field names case-insensitively\n * and ignoring underscores, so `api_key`, `apiKey`, and `API_KEY` all match.\n * Non-sensitive fields are passed through unchanged. Never mutates the input —\n * returns a new value.\n *\n * Intended for structured-log redaction only. Do NOT use as a security\n * boundary for data in motion.\n *\n * @param value - Arbitrary JSON-like payload (query object, config blob, etc.)\n * @returns A new value with sensitive fields replaced by `\"[redacted]\"`.\n */\nexport function redactSensitiveFields(value: unknown): unknown {\n if (value === null || typeof value !== \"object\") return value;\n if (Array.isArray(value)) {\n return value.map((item) => redactSensitiveFields(item));\n }\n const out: Record<string, unknown> = {};\n for (const [key, inner] of Object.entries(value as Record<string, unknown>)) {\n const normalized = key.toLowerCase().replace(/_/g, \"\");\n if (SENSITIVE_FIELD_KEYS.has(normalized)) {\n out[key] = \"[redacted]\";\n } else {\n out[key] = redactSensitiveFields(inner);\n }\n }\n return out;\n}\n"]}
|