@indexnetwork/protocol 3.0.0-rc.245.1 → 3.1.0-rc.247.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/index.d.ts +7 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +3 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/intent/intent.indexer.d.ts +3 -3
  6. package/dist/intent/intent.reconciler.d.ts +6 -6
  7. package/dist/network/indexer/indexer.graph.d.ts +22 -11
  8. package/dist/network/indexer/indexer.graph.d.ts.map +1 -1
  9. package/dist/network/indexer/indexer.graph.js +47 -44
  10. package/dist/network/indexer/indexer.graph.js.map +1 -1
  11. package/dist/network/indexer/indexer.state.d.ts +3 -3
  12. package/dist/opportunity/opportunity.evaluator.d.ts +27 -9
  13. package/dist/opportunity/opportunity.evaluator.d.ts.map +1 -1
  14. package/dist/opportunity/opportunity.evaluator.js +9 -1
  15. package/dist/opportunity/opportunity.evaluator.js.map +1 -1
  16. package/dist/opportunity/opportunity.evidence.d.ts +22 -0
  17. package/dist/opportunity/opportunity.evidence.d.ts.map +1 -0
  18. package/dist/opportunity/opportunity.evidence.js +72 -0
  19. package/dist/opportunity/opportunity.evidence.js.map +1 -0
  20. package/dist/opportunity/opportunity.graph.d.ts +8 -7
  21. package/dist/opportunity/opportunity.graph.d.ts.map +1 -1
  22. package/dist/opportunity/opportunity.graph.js +74 -28
  23. package/dist/opportunity/opportunity.graph.js.map +1 -1
  24. package/dist/opportunity/opportunity.state.d.ts +10 -2
  25. package/dist/opportunity/opportunity.state.d.ts.map +1 -1
  26. package/dist/opportunity/opportunity.state.js.map +1 -1
  27. package/dist/premise/premise.graph.d.ts +14 -2
  28. package/dist/premise/premise.graph.d.ts.map +1 -1
  29. package/dist/premise/premise.graph.js +49 -20
  30. package/dist/premise/premise.graph.js.map +1 -1
  31. package/dist/premise/premise.indexer.d.ts +2 -2
  32. package/dist/premise/premise.state.d.ts +2 -0
  33. package/dist/premise/premise.state.d.ts.map +1 -1
  34. package/dist/premise/premise.state.js +8 -0
  35. package/dist/premise/premise.state.js.map +1 -1
  36. package/dist/shared/assignment/network-assignment.policy.d.ts +59 -0
  37. package/dist/shared/assignment/network-assignment.policy.d.ts.map +1 -0
  38. package/dist/shared/assignment/network-assignment.policy.js +101 -0
  39. package/dist/shared/assignment/network-assignment.policy.js.map +1 -0
  40. package/dist/shared/hyde/hyde.graph.d.ts +6 -6
  41. package/dist/shared/hyde/hyde.state.d.ts +2 -2
  42. package/dist/shared/interfaces/database.interface.d.ts +31 -17
  43. package/dist/shared/interfaces/database.interface.d.ts.map +1 -1
  44. package/dist/shared/interfaces/database.interface.js.map +1 -1
  45. package/dist/shared/schemas/network-assignment.schema.d.ts +135 -0
  46. package/dist/shared/schemas/network-assignment.schema.d.ts.map +1 -0
  47. package/dist/shared/schemas/network-assignment.schema.js +55 -0
  48. package/dist/shared/schemas/network-assignment.schema.js.map +1 -0
  49. package/dist/shared/schemas/question.schema.d.ts +2 -2
  50. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"opportunity.evaluator.js","sourceRoot":"/","sources":["opportunity/opportunity.evaluator.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Runnable } from \"@langchain/core/runnables\";\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { tool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport type { Lens } from \"../shared/hyde/lens.inferrer.js\";\nimport type { OpportunityStatus } from \"../shared/interfaces/database.interface.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { stripUuids } from \"./opportunity.presentation.js\";\nimport { createModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger(\"OpportunityEvaluator\");\n\nconst model = createModel(\"opportunityEvaluator\");\n\n// ──────────────────────────────────────────────────────────────\n// 1. SYSTEM PROMPT\n// ──────────────────────────────────────────────────────────────\n\n\nconst systemPrompt = `\n You are an expert \"Opportunity Matcher\" and super-connector.\n Your Goal: Analyze a Source User's profile against a Candidate User's profile to identify A SINGLE HIGH-VALUE opportunity.\n\n Input:\n - Source Context: The Source User's own Profile.\n - Candidate Profile (JSON)\n - Existing Opportunities (Context of matches already made)\n\n Output:\n - A list containing EXACTLY ONE \"Opportunity\" if a match exists.\n - If NO match exists, return an empty list.\n - Score (0-100): How strong is this match?\n - 90-100: \"Must Meet\" (Perfect alignment).\n - 70-89: \"Should Meet\" (Strong overlaps, clear potential).\n - <70: No opportunity (Return empty list).\n\n **CRITICAL: VALENCY & REASONING**\n \n 1. **Valency Analysis**:\n - Determine the semantic role of the Candidate relative to the Source's goal.\n - \"Agent\": The Candidate CAN DO something for the Source (e.g., Source needs a dev, Candidate IS a dev).\n - \"Patient\": The Candidate NEEDS something from the Source (e.g., Source is a mentor, Candidate needs mentoring).\n - \"Peer\": Symmetric collaboration.\n\n 2. **Reasoning (Third-Party Analytical Perspective)**:\n - **reasoning**: A neutral, third-party explanation of why this opportunity exists. Written for other LLM agents to read and understand.\n - Mention BOTH users by their roles (e.g. \"The source user\" and \"The candidate\") and explain why they are a match.\n - Do NOT address either user as \"you\". Write from an objective observer's perspective.\n - Include what each side brings to the connection and why it is mutually valuable.\n - NEVER leak private intents. If someone's intent is confidential, describe their relevant attributes instead.\n\n **VISIBILITY IMPLICATIONS OF ROLE ASSIGNMENT**\n\n The valency role you assign directly controls who sees the opportunity and when:\n - \"Agent\" (helper/provider): LAST to see the opportunity — only after the Patient has committed to reaching out. Agents are protected from noise; they only see high-intent connections.\n - \"Patient\" (seeker/requester): Sees the opportunity early and decides whether to reach out.\n - \"Peer\" (symmetric): Both parties see the opportunity immediately and either can initiate.\n\n Choose the role carefully — it determines the entire flow of how the connection unfolds.\n\n Rules:\n 1. SYNTHESIS (CRITICAL): If multiple distinct match angles exist, SYNTHESIZE them into a SINGLE, robust opportunity.\n 2. NEVER address either user directly — always use third-party references (\"the source\", \"the candidate\").\n 3. COMPREHENSIVE: The single opportunity must capture ALL the value of the connection.\n 4. Be specific about the \"Why\" for BOTH sides in the reasoning.\n 5. DEDUPLICATION: Do NOT suggest opportunities that duplicate \"Existing Opportunities\".\n 6. Do not suggest an opportunity if the source and candidate clearly already know each other (e.g. same company, co-founders, same team).\n 7. SAME-SIDE MATCHING: If both the source and candidate are SEEKING the same resource (e.g., both looking for investors, both seeking a co-founder), this is not an opportunity. Return an empty list unless one side clearly OFFERS what the other SEEKS.\n`;\n\n// Entity-bundle system prompt (C2): entities + four match patterns + actors output\n// NOTE: entityBundleSystemPrompt uses a >= 30 threshold (permissive) while\n// systemPrompt uses >= 70 (strict). This is intentional: batch mode casts a wide\n// net so the calling pipeline can apply its own filters; pairwise mode is strict\n// because it returns a single yes/no decision per candidate pair.\nconst entityBundleSystemPrompt = `\nYou are an expert \"Opportunity Matcher\" and super-connector.\nYour Goal: Analyze a set of entities (people), each with a profile and optional intents, and identify HIGH-VALUE opportunities among them.\n\nInput:\n- DISCOVERER: The user ID who triggered discovery (for context; they may or may not be in the entity list).\n- ENTITIES: A set of entities. Each entity has:\n - userId, networkId (the index through which they were found)\n - profile: name, bio, location, interests, skills, context\n - intents (optional): list of { intentId, payload, summary } — some entities are profile-only, some have intents\n - ragScore, matchedVia (how they were found)\n- EXISTING OPPORTUNITIES: Context of matches already made (for deduplication).\n\nBEFORE SCORING — determine role satisfiability:\n\nDefinitions:\n SUBSTITUTIVE ROLE: The candidate can directly fill the open position in the discoverer's intent. The candidate IS the person/entity the discoverer is seeking. Example: discoverer seeks \"co-founder\" → candidate is an engineer willing to co-found.\n COMPLEMENTARY ROLE: The candidate's contribution is defined relative to the seeker-sought relation from the outside — they fund, advise, recruit for, or enable the sought relationship rather than participating in it as the target. Example: discoverer seeks \"co-founder\" → candidate is a VC (funds the company, does not co-found it).\n\nStep 1 — Identify the open argument in each discoverer intent: what type of person or entity would satisfy the intent if found?\nStep 2 — For each candidate, ask: can this candidate directly fill that open argument position?\n YES → substitutive role. Proceed to scoring.\n NO → complementary role. Apply Rule 7 (score ≤ 30, return no opportunity).\nStep 3 — Contextual override: if the candidate's profile contains explicit evidence that they currently function in the substitutive role (e.g., a former investor who is now building full-time as a technical co-founder), re-evaluate Step 2 against their current role, not their categorical label.\n\nMatch patterns to consider:\n1. Profile-to-profile: Complementary backgrounds (skills, interests, location).\n2. Profile-to-intents+profile: Someone's skills/background match another's stated goals (intents).\n3. Intents+profile-to-profile: Someone's stated goals match another's skills/background.\n4. Intents+profile-to-intents+profile: Complementary or reciprocal goals between two or more people.\n\nOutput:\n- A list of 0..N opportunities. Each opportunity has:\n - reasoning: Third-party analytical explanation (for other LLM agents). Mention entities by role. Do NOT use \"you\". Never leak private intents.\n - score: 0-100.\n - 90-100: Must Meet — candidate's PRIMARY role directly matches what the discoverer seeks.\n Example: discoverer seeks \"AI/ML co-founder\" → candidate IS an AI/ML engineer who wants to co-found.\n - 70-89: Should Meet — meaningful overlap on role type AND complementary intent.\n - 50-69: Worth Considering — tangential overlap only.\n - <30 (return empty): Complementary-role mismatch (candidate cannot fill the discoverer's open argument position), same-side match, or already acquainted.\n Example: discoverer seeks \"co-founder\" → candidate is a VC investor. The investor's contribution is external to the co-founding relation; they cannot substitute into it. Score 0.\n - IMPORTANT: Include ALL reasonable matches with scores >= 30, and ONLY those. Any candidate you would reject (complementary-role mismatch, same-side, already-acquainted, or a hard location mismatch) must score strictly below 30 and be omitted entirely — the surfacing threshold is 30, so a rejected candidate parked at exactly 30 would be wrongly surfaced.\n - actors: At least 2 actors per opportunity. Each actor has:\n - userId\n - role: \"agent\" (can do something for others), \"patient\" (needs something from others), \"peer\" (symmetric collaboration)\n - intentId (optional): if the match is intent-driven, the specific intent ID for that user\n\nVISIBILITY (role controls who sees the opportunity when):\n- agent: Last to see — after the patient has committed to reaching out.\n- patient: Sees early and decides whether to reach out.\n- peer: Both see immediately; either can initiate.\n\nRules:\n1. ONE OPPORTUNITY PER CANDIDATE: Create a SEPARATE opportunity for EACH candidate who matches. Do NOT combine multiple candidates into one opportunity. Each opportunity should have exactly 2 actors: the DISCOVERER and ONE candidate.\n2. INDIVIDUAL REASONING: Write specific reasoning for EACH candidate individually. Do NOT mention other candidates in the reasoning. Focus on why THIS specific candidate matches THIS specific discoverer.\n3. DEDUPLICATION: Do not suggest opportunities that duplicate Existing Opportunities.\n4. Write reasoning from an objective observer's perspective; be specific about the \"Why\" for each side.\n5. When in introduction mode, each opportunity must have exactly two actors — the two people being introduced. The discoverer (introducer) is added by the system and must not be included in your actors list.\n6. ALREADY KNOW EACH OTHER: Do NOT suggest an opportunity if the entities clearly already know each other. Examples: co-founders of the same company, same team at the same organization, same employer, or any relationship that is obviously existing from their profiles (bio, context). When in doubt, if both profiles mention the same company/org/team in a way that implies they work together, return an empty list for that pair.\n7. ROLE-SATISFIABILITY (evaluate before scoring): A candidate satisfies a discoverer's intent only if they can fill the SUBSTITUTIVE ROLE — the open argument position in the intent (the type of person the discoverer is seeking). A candidate in a COMPLEMENTARY ROLE (one that funds, advises, recruits for, or otherwise enables the sought relation from outside it) does not satisfy the intent, regardless of how closely associated their domain is.\n COMPLEMENTARY-ROLE CAP: If the candidate occupies a complementary rather than substitutive role relative to the discoverer's intent, score below 30 (e.g. 0–20). Return no opportunity. A \"reject\" score must be strictly below 30 so the candidate is not surfaced — never park a rejected candidate at exactly 30.\n CONTEXTUAL OVERRIDE: If the candidate's profile contains explicit evidence that they currently function in the substitutive role (not merely historically or tangentially), treat them as substitutive and score normally.\n8. SAME-SIDE MATCHING: Before scoring, check whether the DISCOVERER and CANDIDATE are both SEEKING the same thing. Look at both parties' intents for directionality:\n - SEEKING signals: \"looking for\", \"seeking\", \"want to find\", \"need\", \"raising\", \"hiring\"\n - OFFERING signals: \"can offer\", \"expert in\", \"investing in\", \"mentoring\", \"available for\"\n If both parties have SEEKING intents targeting the same resource (e.g., both seeking investors, both seeking co-founders, both seeking mentorship), this is NOT an opportunity — score <30. An opportunity requires one side to OFFER what the other SEEKS.\n9. LOCATION MATCHING: When the DISCOVERY REQUEST mentions a specific location (city, region, or country):\n a. If a candidate's profile.location is KNOWN and clearly does NOT match the requested location (different city/region), score below 30 (e.g. 0–25) and return no opportunity. An explicitly requested location is a hard constraint: a known geographic mismatch excludes the candidate.\n b. If a candidate's profile.location is UNKNOWN, EMPTY, or AMBIGUOUS, do NOT penalize — allow them through and score based on other factors. Note in reasoning that their location is unverified.\n c. If a candidate's profile.location matches or is reasonably close (e.g., \"Bay Area\" matches \"San Francisco\", \"Remote\" matches any location), score normally.\n d. \"Remote\" or \"Global\" locations are compatible with any requested location.\n10. EVENT NETWORK AWARENESS: When NETWORK CONTEXTS includes an event-type network (identified by dates, location, schedule, or themes):\n a. TEMPORAL RELEVANCE: If the event has start/end dates, factor time into scoring. Intents about meeting at the event, sharing logistics, or collaborating during the event are highly relevant when the event is upcoming or in progress. After the event ends, such intents lose relevance — score lower unless the connection has lasting value beyond the event.\n b. THEME ALIGNMENT: Event networks may list themes or tracks. Candidates whose expertise or intents align with event themes should score higher for that network's entities.\n c. CO-ATTENDANCE SIGNAL: Two entities found through the same event network share a co-attendance signal — they will likely be in the same place at the same time. This is a positive signal for in-person collaboration opportunities.\n d. SCHEDULE CONTEXT: If the event network includes upcoming events or sessions, use them as additional matching context. Two people attending the same session or interested in the same topic track are stronger matches.\n e. CO-ATTENDANCE ROLE: When two co-attendees express mutual or reciprocal collaboration intent — each seeking to work with the other's type, with no clear one-directional provider/seeker split — assign the \"peer\" role to both. Symmetric, two-sided collaboration is a peer relationship, not an agent/patient one; default to \"peer\" rather than \"agent\" for co-attendance matches.\n`;\n\n// ──────────────────────────────────────────────────────────────\n// 2. RESPONSE SCHEMA (Zod)\n// ──────────────────────────────────────────────────────────────\n\nconst OpportunitySchema = z.object({\n reasoning: z.string().describe('Third-party analytical explanation of why this opportunity exists. Mentions both users by role. Written for other LLM agents to understand the match.'),\n score: z.number().min(0).max(100).describe('Relevance score 0-100'),\n valencyRole: z.enum(['Agent', 'Patient', 'Peer']).describe(\"The semantic role of the Candidate relative to the Source\"),\n sourceId: z.string().describe('The user ID of the source'),\n candidateId: z.string().describe('The user ID of the candidate'),\n});\n\nconst responseFormat = z.object({\n opportunities: z.array(OpportunitySchema).describe(\"List of opportunities identified\"),\n});\n\n// ─── Entity-bundle evaluator (C1): types and output schema with actors ───\n\nexport interface EvaluatorEntity {\n userId: string;\n profile: {\n name?: string;\n bio?: string;\n location?: string;\n interests?: string[];\n skills?: string[];\n context?: string;\n };\n intents?: Array<{\n intentId: string;\n payload: string;\n summary?: string;\n }>;\n networkId: string;\n ragScore?: number;\n matchedVia?: string;\n}\n\nexport interface EvaluatorInput {\n /** The user who triggered discovery (for context, not special treatment). */\n discovererId: string;\n /** All relevant entities. In introduction mode, only the people being introduced (no introducer). */\n entities: EvaluatorEntity[];\n /** Existing opportunities for deduplication. */\n existingOpportunities?: string;\n /** When true, DISCOVERER is the introducer; reasoning and actors must be only among ENTITIES. */\n introductionMode?: boolean;\n /** Name of the introducer (for attribution in reasoning when introductionMode is true). */\n introducerName?: string;\n /** Optional hint/context from the introducer about why these people should meet. */\n introductionHint?: string;\n /** Optional discovery query (e.g. from chat). When set, only suggest opportunities where candidates clearly match this request. */\n discoveryQuery?: string;\n /** Pre-rendered network context markdown, keyed by networkId. */\n networkContexts?: Record<string, string>;\n}\n\nconst ActorSchema = z.object({\n userId: z.string(),\n role: z.enum(['agent', 'patient', 'peer']),\n intentId: z.string().nullable().describe('If the match is intent-driven, the specific intent ID; null otherwise'),\n});\n\nconst OpportunityWithActorsSchema = z.object({\n reasoning: z.string(),\n score: z.number().min(0).max(100),\n actors: z.array(ActorSchema).min(2).describe('All actors in this opportunity with their roles'),\n});\n\nconst entityBundleResponseFormat = z.object({\n opportunities: z.array(OpportunityWithActorsSchema).describe('List of opportunities (0..N)'),\n});\n\nexport type EvaluatorActor = z.infer<typeof ActorSchema>;\nexport type EvaluatedOpportunityWithActors = z.infer<typeof OpportunityWithActorsSchema>;\nexport type EvaluatorOutputBundle = z.infer<typeof entityBundleResponseFormat>;\n\n// ──────────────────────────────────────────────────────────────\n// 3. TYPE DEFINITIONS\n// ──────────────────────────────────────────────────────────────\n\ntype Opportunity = z.infer<typeof OpportunitySchema>;\ntype EvaluatorOutput = z.infer<typeof responseFormat>;\n\n// Define CandidateProfile type (simplified for now, ideally imported from shared types)\nexport interface CandidateProfile {\n userId: string;\n identity?: { name?: string; bio?: string; location?: string };\n attributes?: { interests?: string[]; skills?: string[] };\n narrative?: { context?: string };\n score?: number; // Search score\n}\n\ninterface OpportunityEvaluatorOptions {\n minScore?: number;\n limit?: number;\n hydeDescription?: string;\n /** Pre-inferred lenses (if not provided, lens inference runs automatically in HyDE graph). */\n lenses?: Lens[];\n existingOpportunities?: string;\n candidates?: CandidateProfile[]; // For direct evaluation\n filter?: Record<string, unknown>;\n initialStatus?: OpportunityStatus;\n}\n\n// ──────────────────────────────────────────────────────────────\n// 4. CLASS DEFINITION\n// ──────────────────────────────────────────────────────────────\n\n/** Optional test double for entity-bundle model (avoids live LLM in unit tests). */\nexport type OpportunityEvaluatorOptionsConstructor = {\n entityBundleModel?: Runnable;\n};\n\nexport class OpportunityEvaluator {\n private model: Runnable;\n private entityBundleModel: Runnable;\n\n constructor(options?: OpportunityEvaluatorOptionsConstructor) {\n this.model = model.withStructuredOutput(responseFormat, {\n name: \"opportunity_evaluator\"\n });\n this.entityBundleModel = options?.entityBundleModel ?? model.withStructuredOutput(entityBundleResponseFormat, {\n name: \"opportunity_evaluator_entity_bundle\"\n });\n }\n\n /**\n * Main Entry Point: Batch analysis of candidates.\n * \n * @param sourceProfileContext - The profile context string of the user we are finding opportunities FOR.\n * @param candidates - List of potential matches to evaluate.\n * @param options - Config (minScore, valid types, etc).\n * @returns A sorted list of high-value `Opportunity` objects.\n */\n @Timed()\n public async invoke(\n sourceProfileContext: string,\n candidates: CandidateProfile[],\n options: OpportunityEvaluatorOptions\n ): Promise<Opportunity[]> {\n const minScore = options.minScore || 70;\n\n logger.verbose(`[OpportunityEvaluator.invoke] Analyzing ${candidates.length} candidates...`);\n\n if (candidates.length === 0) {\n logger.verbose('[OpportunityEvaluator] No candidates provided.');\n return [];\n }\n\n const opportunities: Opportunity[] = [];\n\n // Analyze each candidate in parallel (bounded)\n const promises = candidates.map(async (candidate) => {\n // Pass existing opportunities context if provided\n const existingContext = options.existingOpportunities || '';\n return this.analyzeMatch(sourceProfileContext, candidate, candidate.userId, existingContext);\n });\n\n const results = await Promise.all(promises);\n results.flat().forEach(op => {\n if (op.score >= minScore) {\n opportunities.push(op as Opportunity);\n }\n });\n\n // Sort by score and take top 1\n const out = opportunities.sort((a, b) => b.score - a.score).slice(0, 1);\n logger.verbose('[OpportunityEvaluator.invoke] Done', { accepted: out.length });\n return out;\n }\n\n /**\n * Analyze a single match pair using the primary Agent model.\n */\n private async analyzeMatch(\n sourceProfileContext: string,\n candidateProfile: CandidateProfile,\n candidateUserId: string,\n existingOpportunities: string\n ): Promise<Opportunity[]> {\n try {\n // Construct the source context part of the prompt\n const sourceContext = `SOURCE PROFILE:\\n${sourceProfileContext}`;\n\n const existingContextPart = existingOpportunities\n ? `\\nEXISTING OPPORTUNITIES (Deduplication Context):\\n${existingOpportunities}\\n`\n : '';\n\n // Create candidate context using template string\n const candidateContext = `\n ID: ${candidateUserId}\n Name: ${candidateProfile.identity?.name || 'Unknown'}\n Bio: ${candidateProfile.identity?.bio || ''}\n Location: ${candidateProfile.identity?.location || ''}\n Interests: ${candidateProfile.attributes?.interests?.join(', ') || ''}\n Skills: ${candidateProfile.attributes?.skills?.join(', ') || ''}\n\n Context: ${candidateProfile.narrative?.context || ''}\n `;\n\n const messages = [\n new SystemMessage(systemPrompt),\n new HumanMessage(`${sourceContext}\\n${existingContextPart}\\nCANDIDATE PROFILE:\\n${candidateContext}`)\n ];\n\n const result = await invokeWithAbortSignal(this.model, messages);\n const output = responseFormat.parse(result);\n\n const mappedOpportunities = output.opportunities.map((op: Opportunity) => ({\n ...op,\n reasoning: stripUuids(op.reasoning),\n candidateId: candidateUserId,\n }));\n\n return mappedOpportunities;\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n logger.warn(`[OpportunityEvaluator] Analysis failed for candidate ${candidateUserId}`, { message });\n throw e;\n }\n }\n\n /**\n * Entity-bundle entry point (C3): single LLM call with all entities, returns 0..N opportunities with actors.\n */\n @Timed()\n public async invokeEntityBundle(\n input: EvaluatorInput,\n options: { minScore?: number; returnAll?: boolean } = {}\n ): Promise<EvaluatedOpportunityWithActors[]> {\n const minScore = options.minScore ?? 70;\n const returnAll = options.returnAll ?? false;\n const totalEntities = input.entities?.length ?? 0;\n if (!input.entities?.length) {\n logger.verbose('[OpportunityEvaluator.invokeEntityBundle] No entities.');\n return [];\n }\n const existingPart = input.existingOpportunities\n ? `\\nEXISTING OPPORTUNITIES:\\n${input.existingOpportunities}\\n`\n : '';\n const introModePart = input.introductionMode\n ? `\\nINTRODUCTION MODE: This is a human-curated introduction. ${input.introducerName ?? 'The introducer'} (DISCOVERER: ${input.discovererId}) explicitly wants these people to connect. This is NOT an automatic discovery — a real person saw value in this connection.\n\nCRITICAL REASONING INSTRUCTIONS FOR INTRODUCTIONS:\n- Your reasoning MUST acknowledge that this is an introduction initiated by ${input.introducerName ?? 'the introducer'}, not a system-discovered match.\n- Start reasoning with something like \"${input.introducerName ?? 'The introducer'} is connecting [Name A] and [Name B] because...\" or \"This introduction by ${input.introducerName ?? 'the introducer'} brings together...\"\n- Even if the parties' intents or profiles don't obviously overlap, the introduction is still valid because the introducer saw the connection. Explain what the introducer likely sees in this match.\n- If explicit intents align, mention them — but frame it as supporting the introducer's judgment, not as the primary reason.${input.introductionHint ? `\\nINTRODUCER'S CONTEXT: \"${input.introductionHint}\" — use this to inform your reasoning about why the introducer made this connection.` : ''}\n- Actors must refer ONLY to the ENTITIES below (the people being introduced). Do not include the DISCOVERER as an actor.\n- You must output exactly two actors per opportunity (the two people being introduced). The introducer is added separately; do not include them in actors.\n- Be generous with scoring (70+ for any introduction with a plausible basis, since a human made the judgment).\n`\n : '';\n const discoveryQueryPart = input.discoveryQuery?.trim()\n ? `\\nDISCOVERY REQUEST: The user asked: \"${input.discoveryQuery.trim()}\"\n\nCRITICAL SCORING RULES FOR DISCOVERY REQUESTS:\n\n0. QUERY IS PRIMARY: The DISCOVERY REQUEST above is the primary evaluation criterion. The source user's stored INTENTS (if listed below) are background context — use them ONLY to fill in blanks when the query is too broad or vague to evaluate on its own. If the query is specific enough to score candidates, score strictly against the query and IGNORE stored intents. Never let a stored intent override or replace the query as the basis for scoring. When a specific query imposes a hard identity, role, capability, or location constraint, candidates that only match background context or thematic adjacency must score strictly below 30 and be omitted.\n\n1. CLASSIFY THE QUERY TYPE — determine the predication type before scoring:\n\n IDENTITY/ROLE QUERY: The query term is a count noun or sortal that can serve as a predicate nominal — \"X IS A [query]\" is grammatical and meaningful. Examples: \"samurai\", \"investors\", \"designers\", \"nurses\", \"founders\". The user wants someone who IS that thing — not someone who works with, creates content about, or is tangentially associated with it.\n → Test: Can you say \"this person IS a [query term]\"? If yes, this is an identity query.\n → CRITICAL: Subject-matter contact is NOT identity. A character designer who draws samurai IS NOT a samurai. An engineer who raised funding IS NOT an investor. A journalist who covers finance IS NOT an investor. IS-A is not transitive through creative subject matter, professional adjacency, or domain familiarity.\n\n TOPICAL/DOMAIN QUERY: The query is a field, domain, or abstract topic — \"X IS A [query]\" is ungrammatical. Examples: \"machine learning\", \"sustainability\", \"blockchain\". The user wants someone who works in or has expertise in this domain.\n → Test: \"This person IS a machine learning\" makes no sense → topical query.\n\n NEED/CAPABILITY QUERY: The query contains a purposive frame — \"someone to help with X\", \"need a Y\", \"looking for Z\". The user describes a specific need.\n\n2. APPLY TYPE-SPECIFIC SCORING:\n\n For IDENTITY/ROLE queries — apply a categorical gate:\n - First, make a binary IS-A judgment for each candidate: does this person's PRIMARY professional identity or self-description place them within the extension of the query term?\n - IS-A = TRUE → score 75-100 (modulated by intent alignment and profile richness)\n - IS-A = FALSE → score <30. Hard ceiling. Return no opportunity. Do NOT award surfacing credit for subject-matter adjacency, thematic association, creative output involving the query term, having received the thing, or being adjacent to people who are the thing.\n - Do NOT rescue a failed IS-A judgment with background intents. If the user searched \"samurai\" and the candidate is a visual artist (IS-A = false), the candidate's alignment with a background intent like \"connect with visual artists\" does not matter — the explicit query takes priority. If the user searched \"investors\" and the candidate merely raised money, score <30. If the user searched \"scouts\" and the candidate was scouted, score <30. If the user searched \"art director\", a solo illustrator is not enough: require explicit evidence that the candidate owns visual direction, leads/briefs other artists, manages a visual system, or is described as an art/creative director. General visual taste, composition skill, or illustration output alone is a failed IS-A judgment and must score <30.\n\n For TOPICAL/DOMAIN queries — apply gradient scoring:\n - 90-100: Deep expertise, primary focus area\n - 70-89: Substantial engagement, meaningful work in the domain\n - 50-69: Peripheral involvement, tangential connection\n - <50: No meaningful engagement\n\n For NEED/CAPABILITY queries — apply capability + availability scoring:\n - Score based on both capability match AND openness to the described collaboration\n - 90-100: Perfect capability match with explicit availability\n - 70-89: Strong capability, plausible availability\n\n3. SAME-SIDE CHECK: If the candidate's intents show they are ALSO SEEKING what the discoverer is seeking (e.g., both looking for investors, both looking for co-founders), this is a same-side match. Score <30 regardless of keyword overlap in bios. The candidate must BE or OFFER what the discoverer is looking for, not also be looking for it.\n\n4. LOCATION ENFORCEMENT: If the discovery request mentions a specific location (e.g., \"in SF\", \"based in London\", \"Istanbul\"), check each candidate's profile.location:\n - KNOWN MISMATCH (e.g., request says \"SF\" but candidate is \"New York\"): Score <30 and return no opportunity. State the mismatch in reasoning if returning diagnostic output. A known location mismatch is a hard query failure, even when the candidate otherwise matches the source's background interests.\n - UNKNOWN/EMPTY location: Do not penalize, and do NOT reject solely because the query contains a location. Treat location as unverified/missing evidence, then score on the non-location parts of the query. If the candidate strongly satisfies the requested role/domain (e.g., query \"Unreal Engine developers in SF\" and candidate is clearly an Unreal Engine developer with empty location), score normally enough to surface (typically 60-89) and note that location is unverified.\n - MATCH or COMPATIBLE (e.g., \"Bay Area\" ≈ \"SF\", \"Remote\" ≈ any): Score normally.\n`\n : '';\n const hasDiscoveryQuery = !!input.discoveryQuery?.trim();\n const entitiesBlock = input.entities.map((e) => {\n const isSource = e.userId === input.discovererId;\n // When an explicit discovery query is active, label the source user's stored\n // intents as background context so the LLM treats the query as primary.\n const intentsLabel = isSource && hasDiscoveryQuery ? 'BACKGROUND INTENTS (use only if query is too vague)' : 'INTENTS';\n const intentsPart = e.intents?.length\n ? `\\n ${intentsLabel}:\\n${e.intents.map((i) => ` - ${i.intentId}: ${i.payload}`).join('\\n')}`\n : '';\n // Mask the discoverer's name so the LLM cannot leak it into reasoning.\n // The system prompt already says \"use third-party references\", but the LLM\n // ignores this when the actual name is visible. Masking it forces role-based\n // language (\"the source user is looking…\" instead of \"Alice is looking…\").\n const displayName = e.userId === input.discovererId\n ? '(source user)'\n : (e.profile.name ?? '');\n return `\n USER: ${e.userId}\n INDEX: ${e.networkId}\n PROFILE: Name: ${displayName} | Bio: ${e.profile.bio ?? ''} | Location: ${e.profile.location ?? ''} | Interests: ${e.profile.interests?.join(', ') ?? ''} | Skills: ${e.profile.skills?.join(', ') ?? ''} | Context: ${e.profile.context ?? ''}${intentsPart}\n RAG SCORE: ${e.ragScore ?? '—'}\n MATCHED VIA: ${e.matchedVia ?? '—'}`;\n }).join('\\n');\n const networkContextPart = input.networkContexts && Object.keys(input.networkContexts).length > 0\n ? `\\n\\nNETWORK CONTEXTS:\\n${Object.entries(input.networkContexts).map(([nid, ctx]) => `[INDEX: ${nid}]\\n${ctx}`).join('\\n\\n')}`\n : '';\n const humanContent = `DISCOVERER: ${input.discovererId}${introModePart}${discoveryQueryPart}${networkContextPart}\\n\\nENTITIES:\\n${entitiesBlock}${existingPart}`;\n const messages = [\n new SystemMessage(entityBundleSystemPrompt),\n new HumanMessage(humanContent),\n ];\n let parsedTotal = 0;\n try {\n const result = await invokeWithAbortSignal(this.entityBundleModel, messages);\n const parsed = entityBundleResponseFormat.parse(result);\n for (const op of parsed.opportunities) {\n op.reasoning = stripUuids(op.reasoning);\n }\n parsedTotal = parsed.opportunities.length;\n const introGuard =\n input.introductionMode\n ? parsed.opportunities.filter((op) => op.actors.length === 2)\n : parsed.opportunities;\n const filtered = introGuard.filter((op) => op.score >= minScore);\n logger.verbose('[OpportunityEvaluator.invokeEntityBundle] Done', {\n total: parsed.opportunities.length,\n afterIntroGuard: introGuard.length,\n accepted: filtered.length,\n returnAll,\n });\n return returnAll ? introGuard : filtered;\n } catch (llmError) {\n logger.error('[OpportunityEvaluator.invokeEntityBundle] Failed', {\n discovererId: input.discovererId,\n totalEntities,\n parsedTotal,\n minScore,\n llmError,\n });\n throw llmError;\n }\n }\n\n /**\n * Factory method to expose the agent as a LangChain tool.\n * Simplified to only accept direct evaluation arguments.\n * PURE: Does not perform any database lookups.\n */\n public static asTool() {\n return tool(\n async (args: {\n sourceProfileContext: string;\n candidatesJson?: string;\n minScore?: number;\n }) => {\n const agent = new OpportunityEvaluator();\n\n const sourceProfileContext = args.sourceProfileContext;\n\n let candidates: CandidateProfile[] = [];\n if (args.candidatesJson) {\n try {\n candidates = JSON.parse(args.candidatesJson);\n } catch (e) {\n logger.error(\"Failed to parse candidates JSON\");\n }\n }\n\n const options: OpportunityEvaluatorOptions = {\n minScore: args.minScore,\n };\n\n return await agent.invoke(sourceProfileContext, candidates, options);\n },\n {\n name: 'opportunity_evaluator',\n description: 'Evaluates candidates against a source profile. SOURCE PROFILE CONTEXT MUST BE PROVIDED.',\n schema: z.object({\n sourceProfileContext: z.string().describe('The resolved source user profile context'),\n candidatesJson: z.string().optional().describe('JSON string list of Candidates'),\n minScore: z.number().optional().describe('Minimum score to accept a match')\n })\n }\n );\n }\n}\n"]}
1
+ {"version":3,"file":"opportunity.evaluator.js","sourceRoot":"/","sources":["opportunity/opportunity.evaluator.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Runnable } from \"@langchain/core/runnables\";\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { tool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport type { Lens } from \"../shared/hyde/lens.inferrer.js\";\nimport type { OpportunityStatus } from \"../shared/interfaces/database.interface.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { stripUuids } from \"./opportunity.presentation.js\";\nimport { createModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\nimport type { OpportunityEvidence } from '../shared/schemas/network-assignment.schema.js';\nimport { renderOpportunityEvidenceForPrompt } from './opportunity.evidence.js';\n\nconst logger = protocolLogger(\"OpportunityEvaluator\");\n\nconst model = createModel(\"opportunityEvaluator\");\n\n// ──────────────────────────────────────────────────────────────\n// 1. SYSTEM PROMPT\n// ──────────────────────────────────────────────────────────────\n\n\nconst systemPrompt = `\n You are an expert \"Opportunity Matcher\" and super-connector.\n Your Goal: Analyze a Source User's profile against a Candidate User's profile to identify A SINGLE HIGH-VALUE opportunity.\n\n Input:\n - Source Context: The Source User's own Profile.\n - Candidate Profile (JSON)\n - Existing Opportunities (Context of matches already made)\n\n Output:\n - A list containing EXACTLY ONE \"Opportunity\" if a match exists.\n - If NO match exists, return an empty list.\n - Score (0-100): How strong is this match?\n - 90-100: \"Must Meet\" (Perfect alignment).\n - 70-89: \"Should Meet\" (Strong overlaps, clear potential).\n - <70: No opportunity (Return empty list).\n\n **CRITICAL: VALENCY & REASONING**\n \n 1. **Valency Analysis**:\n - Determine the semantic role of the Candidate relative to the Source's goal.\n - \"Agent\": The Candidate CAN DO something for the Source (e.g., Source needs a dev, Candidate IS a dev).\n - \"Patient\": The Candidate NEEDS something from the Source (e.g., Source is a mentor, Candidate needs mentoring).\n - \"Peer\": Symmetric collaboration.\n\n 2. **Reasoning (Third-Party Analytical Perspective)**:\n - **reasoning**: A neutral, third-party explanation of why this opportunity exists. Written for other LLM agents to read and understand.\n - Mention BOTH users by their roles (e.g. \"The source user\" and \"The candidate\") and explain why they are a match.\n - Do NOT address either user as \"you\". Write from an objective observer's perspective.\n - Include what each side brings to the connection and why it is mutually valuable.\n - NEVER leak private intents. If someone's intent is confidential, describe their relevant attributes instead.\n\n **VISIBILITY IMPLICATIONS OF ROLE ASSIGNMENT**\n\n The valency role you assign directly controls who sees the opportunity and when:\n - \"Agent\" (helper/provider): LAST to see the opportunity — only after the Patient has committed to reaching out. Agents are protected from noise; they only see high-intent connections.\n - \"Patient\" (seeker/requester): Sees the opportunity early and decides whether to reach out.\n - \"Peer\" (symmetric): Both parties see the opportunity immediately and either can initiate.\n\n Choose the role carefully — it determines the entire flow of how the connection unfolds.\n\n Rules:\n 1. SYNTHESIS (CRITICAL): If multiple distinct match angles exist, SYNTHESIZE them into a SINGLE, robust opportunity.\n 2. NEVER address either user directly — always use third-party references (\"the source\", \"the candidate\").\n 3. COMPREHENSIVE: The single opportunity must capture ALL the value of the connection.\n 4. Be specific about the \"Why\" for BOTH sides in the reasoning.\n 5. DEDUPLICATION: Do NOT suggest opportunities that duplicate \"Existing Opportunities\".\n 6. Do not suggest an opportunity if the source and candidate clearly already know each other (e.g. same company, co-founders, same team).\n 7. SAME-SIDE MATCHING: If both the source and candidate are SEEKING the same resource (e.g., both looking for investors, both seeking a co-founder), this is not an opportunity. Return an empty list unless one side clearly OFFERS what the other SEEKS.\n`;\n\n// Entity-bundle system prompt (C2): entities + four match patterns + actors output\n// NOTE: entityBundleSystemPrompt uses a >= 30 threshold (permissive) while\n// systemPrompt uses >= 70 (strict). This is intentional: batch mode casts a wide\n// net so the calling pipeline can apply its own filters; pairwise mode is strict\n// because it returns a single yes/no decision per candidate pair.\nconst entityBundleSystemPrompt = `\nYou are an expert \"Opportunity Matcher\" and super-connector.\nYour Goal: Analyze a set of entities (people), each with a profile and optional intents, and identify HIGH-VALUE opportunities among them.\n\nInput:\n- DISCOVERER: The user ID who triggered discovery (for context; they may or may not be in the entity list).\n- ENTITIES: A set of entities. Each entity has:\n - userId, networkId (the index through which they were found)\n - evidenceKey (stable key for the matched resource when available)\n - profile: name, bio, location, interests, skills, context\n - intents (optional): list of { intentId, payload, summary } — some entities are profile-only, some have intents\n - ragScore, matchedVia (how they were found)\n - evidence (typed retrieval evidence: discovery kind, network, score, lens, source/candidate ids)\n- EXISTING OPPORTUNITIES: Context of matches already made (for deduplication).\n\nBEFORE SCORING — determine role satisfiability:\n\nDefinitions:\n SUBSTITUTIVE ROLE: The candidate can directly fill the open position in the discoverer's intent. The candidate IS the person/entity the discoverer is seeking. Example: discoverer seeks \"co-founder\" → candidate is an engineer willing to co-found.\n COMPLEMENTARY ROLE: The candidate's contribution is defined relative to the seeker-sought relation from the outside — they fund, advise, recruit for, or enable the sought relationship rather than participating in it as the target. Example: discoverer seeks \"co-founder\" → candidate is a VC (funds the company, does not co-found it).\n\nStep 1 — Identify the open argument in each discoverer intent: what type of person or entity would satisfy the intent if found?\nStep 2 — For each candidate, ask: can this candidate directly fill that open argument position?\n YES → substitutive role. Proceed to scoring.\n NO → complementary role. Apply Rule 7 (score ≤ 30, return no opportunity).\nStep 3 — Contextual override: if the candidate's profile contains explicit evidence that they currently function in the substitutive role (e.g., a former investor who is now building full-time as a technical co-founder), re-evaluate Step 2 against their current role, not their categorical label.\n\nMatch patterns to consider:\n1. Profile-to-profile: Complementary backgrounds (skills, interests, location).\n2. Profile-to-intents+profile: Someone's skills/background match another's stated goals (intents).\n3. Intents+profile-to-profile: Someone's stated goals match another's skills/background.\n4. Intents+profile-to-intents+profile: Complementary or reciprocal goals between two or more people.\n\nOutput:\n- A list of 0..N opportunities. Each opportunity has:\n - reasoning: Third-party analytical explanation (for other LLM agents). Mention entities by role. Do NOT use \"you\". Never leak private intents.\n - score: 0-100.\n - 90-100: Must Meet — candidate's PRIMARY role directly matches what the discoverer seeks.\n Example: discoverer seeks \"AI/ML co-founder\" → candidate IS an AI/ML engineer who wants to co-found.\n - 70-89: Should Meet — meaningful overlap on role type AND complementary intent.\n - 50-69: Worth Considering — tangential overlap only.\n - <30 (return empty): Complementary-role mismatch (candidate cannot fill the discoverer's open argument position), same-side match, or already acquainted.\n Example: discoverer seeks \"co-founder\" → candidate is a VC investor. The investor's contribution is external to the co-founding relation; they cannot substitute into it. Score 0.\n - IMPORTANT: Include ALL reasonable matches with scores >= 30, and ONLY those. Any candidate you would reject (complementary-role mismatch, same-side, already-acquainted, or a hard location mismatch) must score strictly below 30 and be omitted entirely — the surfacing threshold is 30, so a rejected candidate parked at exactly 30 would be wrongly surfaced.\n - actors: At least 2 actors per opportunity. Each actor has:\n - userId\n - role: \"agent\" (can do something for others), \"patient\" (needs something from others), \"peer\" (symmetric collaboration)\n - intentId (optional): if the match is intent-driven, the specific intent ID for that user\n - evidenceKey (optional): copy the entity evidenceKey when the actor is driven by a specific listed entity\n\nVISIBILITY (role controls who sees the opportunity when):\n- agent: Last to see — after the patient has committed to reaching out.\n- patient: Sees early and decides whether to reach out.\n- peer: Both see immediately; either can initiate.\n\nRules:\n1. ONE OPPORTUNITY PER CANDIDATE: Create a SEPARATE opportunity for EACH candidate who matches. Do NOT combine multiple candidates into one opportunity. Each opportunity should have exactly 2 actors: the DISCOVERER and ONE candidate.\n2. INDIVIDUAL REASONING: Write specific reasoning for EACH candidate individually. Do NOT mention other candidates in the reasoning. Focus on why THIS specific candidate matches THIS specific discoverer.\n3. DEDUPLICATION: Do not suggest opportunities that duplicate Existing Opportunities.\n4. Write reasoning from an objective observer's perspective; be specific about the \"Why\" for each side.\n5. When in introduction mode, each opportunity must have exactly two actors — the two people being introduced. The discoverer (introducer) is added by the system and must not be included in your actors list.\n6. ALREADY KNOW EACH OTHER: Do NOT suggest an opportunity if the entities clearly already know each other. Examples: co-founders of the same company, same team at the same organization, same employer, or any relationship that is obviously existing from their profiles (bio, context). When in doubt, if both profiles mention the same company/org/team in a way that implies they work together, return an empty list for that pair.\n7. ROLE-SATISFIABILITY (evaluate before scoring): A candidate satisfies a discoverer's intent only if they can fill the SUBSTITUTIVE ROLE — the open argument position in the intent (the type of person the discoverer is seeking). A candidate in a COMPLEMENTARY ROLE (one that funds, advises, recruits for, or otherwise enables the sought relation from outside it) does not satisfy the intent, regardless of how closely associated their domain is.\n COMPLEMENTARY-ROLE CAP: If the candidate occupies a complementary rather than substitutive role relative to the discoverer's intent, score below 30 (e.g. 0–20). Return no opportunity. A \"reject\" score must be strictly below 30 so the candidate is not surfaced — never park a rejected candidate at exactly 30.\n CONTEXTUAL OVERRIDE: If the candidate's profile contains explicit evidence that they currently function in the substitutive role (not merely historically or tangentially), treat them as substitutive and score normally.\n8. SAME-SIDE MATCHING: Before scoring, check whether the DISCOVERER and CANDIDATE are both SEEKING the same thing. Look at both parties' intents for directionality:\n - SEEKING signals: \"looking for\", \"seeking\", \"want to find\", \"need\", \"raising\", \"hiring\"\n - OFFERING signals: \"can offer\", \"expert in\", \"investing in\", \"mentoring\", \"available for\"\n If both parties have SEEKING intents targeting the same resource (e.g., both seeking investors, both seeking co-founders, both seeking mentorship), this is NOT an opportunity — score <30. An opportunity requires one side to OFFER what the other SEEKS.\n9. LOCATION MATCHING: When the DISCOVERY REQUEST mentions a specific location (city, region, or country):\n a. If a candidate's profile.location is KNOWN and clearly does NOT match the requested location (different city/region), score below 30 (e.g. 0–25) and return no opportunity. An explicitly requested location is a hard constraint: a known geographic mismatch excludes the candidate.\n b. If a candidate's profile.location is UNKNOWN, EMPTY, or AMBIGUOUS, do NOT penalize — allow them through and score based on other factors. Note in reasoning that their location is unverified.\n c. If a candidate's profile.location matches or is reasonably close (e.g., \"Bay Area\" matches \"San Francisco\", \"Remote\" matches any location), score normally.\n d. \"Remote\" or \"Global\" locations are compatible with any requested location.\n10. EVENT NETWORK AWARENESS: When NETWORK CONTEXTS includes an event-type network (identified by dates, location, schedule, or themes):\n a. TEMPORAL RELEVANCE: If the event has start/end dates, factor time into scoring. Intents about meeting at the event, sharing logistics, or collaborating during the event are highly relevant when the event is upcoming or in progress. After the event ends, such intents lose relevance — score lower unless the connection has lasting value beyond the event.\n b. THEME ALIGNMENT: Event networks may list themes or tracks. Candidates whose expertise or intents align with event themes should score higher for that network's entities.\n c. CO-ATTENDANCE SIGNAL: Two entities found through the same event network share a co-attendance signal — they will likely be in the same place at the same time. This is a positive signal for in-person collaboration opportunities.\n d. SCHEDULE CONTEXT: If the event network includes upcoming events or sessions, use them as additional matching context. Two people attending the same session or interested in the same topic track are stronger matches.\n e. CO-ATTENDANCE ROLE: When two co-attendees express mutual or reciprocal collaboration intent — each seeking to work with the other's type, with no clear one-directional provider/seeker split — assign the \"peer\" role to both. Symmetric, two-sided collaboration is a peer relationship, not an agent/patient one; default to \"peer\" rather than \"agent\" for co-attendance matches.\n`;\n\n// ──────────────────────────────────────────────────────────────\n// 2. RESPONSE SCHEMA (Zod)\n// ──────────────────────────────────────────────────────────────\n\nconst OpportunitySchema = z.object({\n reasoning: z.string().describe('Third-party analytical explanation of why this opportunity exists. Mentions both users by role. Written for other LLM agents to understand the match.'),\n score: z.number().min(0).max(100).describe('Relevance score 0-100'),\n valencyRole: z.enum(['Agent', 'Patient', 'Peer']).describe(\"The semantic role of the Candidate relative to the Source\"),\n sourceId: z.string().describe('The user ID of the source'),\n candidateId: z.string().describe('The user ID of the candidate'),\n});\n\nconst responseFormat = z.object({\n opportunities: z.array(OpportunitySchema).describe(\"List of opportunities identified\"),\n});\n\n// ─── Entity-bundle evaluator (C1): types and output schema with actors ───\n\nexport interface EvaluatorEntity {\n userId: string;\n profile: {\n name?: string;\n bio?: string;\n location?: string;\n interests?: string[];\n skills?: string[];\n context?: string;\n };\n intents?: Array<{\n intentId: string;\n payload: string;\n summary?: string;\n }>;\n networkId: string;\n evidenceKey?: string;\n ragScore?: number;\n matchedVia?: string;\n evidence?: OpportunityEvidence[];\n}\n\nexport interface EvaluatorInput {\n /** The user who triggered discovery (for context, not special treatment). */\n discovererId: string;\n /** All relevant entities. In introduction mode, only the people being introduced (no introducer). */\n entities: EvaluatorEntity[];\n /** Existing opportunities for deduplication. */\n existingOpportunities?: string;\n /** When true, DISCOVERER is the introducer; reasoning and actors must be only among ENTITIES. */\n introductionMode?: boolean;\n /** Name of the introducer (for attribution in reasoning when introductionMode is true). */\n introducerName?: string;\n /** Optional hint/context from the introducer about why these people should meet. */\n introductionHint?: string;\n /** Optional discovery query (e.g. from chat). When set, only suggest opportunities where candidates clearly match this request. */\n discoveryQuery?: string;\n /** Pre-rendered network context markdown, keyed by networkId. */\n networkContexts?: Record<string, string>;\n}\n\nconst ActorSchema = z.object({\n userId: z.string(),\n role: z.enum(['agent', 'patient', 'peer']),\n intentId: z.string().nullable().describe('If the match is intent-driven, the specific intent ID; null otherwise'),\n evidenceKey: z.string().nullable().optional().describe('Stable evidence key for the matched entity; null if unknown'),\n});\n\nconst OpportunityWithActorsSchema = z.object({\n reasoning: z.string(),\n score: z.number().min(0).max(100),\n actors: z.array(ActorSchema).min(2).describe('All actors in this opportunity with their roles'),\n});\n\nconst entityBundleResponseFormat = z.object({\n opportunities: z.array(OpportunityWithActorsSchema).describe('List of opportunities (0..N)'),\n});\n\nexport type EvaluatorActor = z.infer<typeof ActorSchema>;\nexport type EvaluatedOpportunityWithActors = z.infer<typeof OpportunityWithActorsSchema>;\nexport type EvaluatorOutputBundle = z.infer<typeof entityBundleResponseFormat>;\n\n// ──────────────────────────────────────────────────────────────\n// 3. TYPE DEFINITIONS\n// ──────────────────────────────────────────────────────────────\n\ntype Opportunity = z.infer<typeof OpportunitySchema>;\ntype EvaluatorOutput = z.infer<typeof responseFormat>;\n\n// Define CandidateProfile type (simplified for now, ideally imported from shared types)\nexport interface CandidateProfile {\n userId: string;\n identity?: { name?: string; bio?: string; location?: string };\n attributes?: { interests?: string[]; skills?: string[] };\n narrative?: { context?: string };\n score?: number; // Search score\n}\n\ninterface OpportunityEvaluatorOptions {\n minScore?: number;\n limit?: number;\n hydeDescription?: string;\n /** Pre-inferred lenses (if not provided, lens inference runs automatically in HyDE graph). */\n lenses?: Lens[];\n existingOpportunities?: string;\n candidates?: CandidateProfile[]; // For direct evaluation\n filter?: Record<string, unknown>;\n initialStatus?: OpportunityStatus;\n}\n\n// ──────────────────────────────────────────────────────────────\n// 4. CLASS DEFINITION\n// ──────────────────────────────────────────────────────────────\n\n/** Optional test double for entity-bundle model (avoids live LLM in unit tests). */\nexport type OpportunityEvaluatorOptionsConstructor = {\n entityBundleModel?: Runnable;\n};\n\nexport class OpportunityEvaluator {\n private model: Runnable;\n private entityBundleModel: Runnable;\n\n constructor(options?: OpportunityEvaluatorOptionsConstructor) {\n this.model = model.withStructuredOutput(responseFormat, {\n name: \"opportunity_evaluator\"\n });\n this.entityBundleModel = options?.entityBundleModel ?? model.withStructuredOutput(entityBundleResponseFormat, {\n name: \"opportunity_evaluator_entity_bundle\"\n });\n }\n\n /**\n * Main Entry Point: Batch analysis of candidates.\n * \n * @param sourceProfileContext - The profile context string of the user we are finding opportunities FOR.\n * @param candidates - List of potential matches to evaluate.\n * @param options - Config (minScore, valid types, etc).\n * @returns A sorted list of high-value `Opportunity` objects.\n */\n @Timed()\n public async invoke(\n sourceProfileContext: string,\n candidates: CandidateProfile[],\n options: OpportunityEvaluatorOptions\n ): Promise<Opportunity[]> {\n const minScore = options.minScore || 70;\n\n logger.verbose(`[OpportunityEvaluator.invoke] Analyzing ${candidates.length} candidates...`);\n\n if (candidates.length === 0) {\n logger.verbose('[OpportunityEvaluator] No candidates provided.');\n return [];\n }\n\n const opportunities: Opportunity[] = [];\n\n // Analyze each candidate in parallel (bounded)\n const promises = candidates.map(async (candidate) => {\n // Pass existing opportunities context if provided\n const existingContext = options.existingOpportunities || '';\n return this.analyzeMatch(sourceProfileContext, candidate, candidate.userId, existingContext);\n });\n\n const results = await Promise.all(promises);\n results.flat().forEach(op => {\n if (op.score >= minScore) {\n opportunities.push(op as Opportunity);\n }\n });\n\n // Sort by score and take top 1\n const out = opportunities.sort((a, b) => b.score - a.score).slice(0, 1);\n logger.verbose('[OpportunityEvaluator.invoke] Done', { accepted: out.length });\n return out;\n }\n\n /**\n * Analyze a single match pair using the primary Agent model.\n */\n private async analyzeMatch(\n sourceProfileContext: string,\n candidateProfile: CandidateProfile,\n candidateUserId: string,\n existingOpportunities: string\n ): Promise<Opportunity[]> {\n try {\n // Construct the source context part of the prompt\n const sourceContext = `SOURCE PROFILE:\\n${sourceProfileContext}`;\n\n const existingContextPart = existingOpportunities\n ? `\\nEXISTING OPPORTUNITIES (Deduplication Context):\\n${existingOpportunities}\\n`\n : '';\n\n // Create candidate context using template string\n const candidateContext = `\n ID: ${candidateUserId}\n Name: ${candidateProfile.identity?.name || 'Unknown'}\n Bio: ${candidateProfile.identity?.bio || ''}\n Location: ${candidateProfile.identity?.location || ''}\n Interests: ${candidateProfile.attributes?.interests?.join(', ') || ''}\n Skills: ${candidateProfile.attributes?.skills?.join(', ') || ''}\n\n Context: ${candidateProfile.narrative?.context || ''}\n `;\n\n const messages = [\n new SystemMessage(systemPrompt),\n new HumanMessage(`${sourceContext}\\n${existingContextPart}\\nCANDIDATE PROFILE:\\n${candidateContext}`)\n ];\n\n const result = await invokeWithAbortSignal(this.model, messages);\n const output = responseFormat.parse(result);\n\n const mappedOpportunities = output.opportunities.map((op: Opportunity) => ({\n ...op,\n reasoning: stripUuids(op.reasoning),\n candidateId: candidateUserId,\n }));\n\n return mappedOpportunities;\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n logger.warn(`[OpportunityEvaluator] Analysis failed for candidate ${candidateUserId}`, { message });\n throw e;\n }\n }\n\n /**\n * Entity-bundle entry point (C3): single LLM call with all entities, returns 0..N opportunities with actors.\n */\n @Timed()\n public async invokeEntityBundle(\n input: EvaluatorInput,\n options: { minScore?: number; returnAll?: boolean } = {}\n ): Promise<EvaluatedOpportunityWithActors[]> {\n const minScore = options.minScore ?? 70;\n const returnAll = options.returnAll ?? false;\n const totalEntities = input.entities?.length ?? 0;\n if (!input.entities?.length) {\n logger.verbose('[OpportunityEvaluator.invokeEntityBundle] No entities.');\n return [];\n }\n const existingPart = input.existingOpportunities\n ? `\\nEXISTING OPPORTUNITIES:\\n${input.existingOpportunities}\\n`\n : '';\n const introModePart = input.introductionMode\n ? `\\nINTRODUCTION MODE: This is a human-curated introduction. ${input.introducerName ?? 'The introducer'} (DISCOVERER: ${input.discovererId}) explicitly wants these people to connect. This is NOT an automatic discovery — a real person saw value in this connection.\n\nCRITICAL REASONING INSTRUCTIONS FOR INTRODUCTIONS:\n- Your reasoning MUST acknowledge that this is an introduction initiated by ${input.introducerName ?? 'the introducer'}, not a system-discovered match.\n- Start reasoning with something like \"${input.introducerName ?? 'The introducer'} is connecting [Name A] and [Name B] because...\" or \"This introduction by ${input.introducerName ?? 'the introducer'} brings together...\"\n- Even if the parties' intents or profiles don't obviously overlap, the introduction is still valid because the introducer saw the connection. Explain what the introducer likely sees in this match.\n- If explicit intents align, mention them — but frame it as supporting the introducer's judgment, not as the primary reason.${input.introductionHint ? `\\nINTRODUCER'S CONTEXT: \"${input.introductionHint}\" — use this to inform your reasoning about why the introducer made this connection.` : ''}\n- Actors must refer ONLY to the ENTITIES below (the people being introduced). Do not include the DISCOVERER as an actor.\n- You must output exactly two actors per opportunity (the two people being introduced). The introducer is added separately; do not include them in actors.\n- Be generous with scoring (70+ for any introduction with a plausible basis, since a human made the judgment).\n`\n : '';\n const discoveryQueryPart = input.discoveryQuery?.trim()\n ? `\\nDISCOVERY REQUEST: The user asked: \"${input.discoveryQuery.trim()}\"\n\nCRITICAL SCORING RULES FOR DISCOVERY REQUESTS:\n\n0. QUERY IS PRIMARY: The DISCOVERY REQUEST above is the primary evaluation criterion. The source user's stored INTENTS (if listed below) are background context — use them ONLY to fill in blanks when the query is too broad or vague to evaluate on its own. If the query is specific enough to score candidates, score strictly against the query and IGNORE stored intents. Never let a stored intent override or replace the query as the basis for scoring. When a specific query imposes a hard identity, role, capability, or location constraint, candidates that only match background context or thematic adjacency must score strictly below 30 and be omitted.\n\n1. CLASSIFY THE QUERY TYPE — determine the predication type before scoring:\n\n IDENTITY/ROLE QUERY: The query term is a count noun or sortal that can serve as a predicate nominal — \"X IS A [query]\" is grammatical and meaningful. Examples: \"samurai\", \"investors\", \"designers\", \"nurses\", \"founders\". The user wants someone who IS that thing — not someone who works with, creates content about, or is tangentially associated with it.\n → Test: Can you say \"this person IS a [query term]\"? If yes, this is an identity query.\n → CRITICAL: Subject-matter contact is NOT identity. A character designer who draws samurai IS NOT a samurai. An engineer who raised funding IS NOT an investor. A journalist who covers finance IS NOT an investor. IS-A is not transitive through creative subject matter, professional adjacency, or domain familiarity.\n\n TOPICAL/DOMAIN QUERY: The query is a field, domain, or abstract topic — \"X IS A [query]\" is ungrammatical. Examples: \"machine learning\", \"sustainability\", \"blockchain\". The user wants someone who works in or has expertise in this domain.\n → Test: \"This person IS a machine learning\" makes no sense → topical query.\n\n NEED/CAPABILITY QUERY: The query contains a purposive frame — \"someone to help with X\", \"need a Y\", \"looking for Z\". The user describes a specific need.\n\n2. APPLY TYPE-SPECIFIC SCORING:\n\n For IDENTITY/ROLE queries — apply a categorical gate:\n - First, make a binary IS-A judgment for each candidate: does this person's PRIMARY professional identity or self-description place them within the extension of the query term?\n - IS-A = TRUE → score 75-100 (modulated by intent alignment and profile richness)\n - IS-A = FALSE → score <30. Hard ceiling. Return no opportunity. Do NOT award surfacing credit for subject-matter adjacency, thematic association, creative output involving the query term, having received the thing, or being adjacent to people who are the thing.\n - Do NOT rescue a failed IS-A judgment with background intents. If the user searched \"samurai\" and the candidate is a visual artist (IS-A = false), the candidate's alignment with a background intent like \"connect with visual artists\" does not matter — the explicit query takes priority. If the user searched \"investors\" and the candidate merely raised money, score <30. If the user searched \"scouts\" and the candidate was scouted, score <30. If the user searched \"art director\", a solo illustrator is not enough: require explicit evidence that the candidate owns visual direction, leads/briefs other artists, manages a visual system, or is described as an art/creative director. General visual taste, composition skill, or illustration output alone is a failed IS-A judgment and must score <30.\n\n For TOPICAL/DOMAIN queries — apply gradient scoring:\n - 90-100: Deep expertise, primary focus area\n - 70-89: Substantial engagement, meaningful work in the domain\n - 50-69: Peripheral involvement, tangential connection\n - <50: No meaningful engagement\n\n For NEED/CAPABILITY queries — apply capability + availability scoring:\n - Score based on both capability match AND openness to the described collaboration\n - 90-100: Perfect capability match with explicit availability\n - 70-89: Strong capability, plausible availability\n\n3. SAME-SIDE CHECK: If the candidate's intents show they are ALSO SEEKING what the discoverer is seeking (e.g., both looking for investors, both looking for co-founders), this is a same-side match. Score <30 regardless of keyword overlap in bios. The candidate must BE or OFFER what the discoverer is looking for, not also be looking for it.\n\n4. LOCATION ENFORCEMENT: If the discovery request mentions a specific location (e.g., \"in SF\", \"based in London\", \"Istanbul\"), check each candidate's profile.location:\n - KNOWN MISMATCH (e.g., request says \"SF\" but candidate is \"New York\"): Score <30 and return no opportunity. State the mismatch in reasoning if returning diagnostic output. A known location mismatch is a hard query failure, even when the candidate otherwise matches the source's background interests.\n - UNKNOWN/EMPTY location: Do not penalize, and do NOT reject solely because the query contains a location. Treat location as unverified/missing evidence, then score on the non-location parts of the query. If the candidate strongly satisfies the requested role/domain (e.g., query \"Unreal Engine developers in SF\" and candidate is clearly an Unreal Engine developer with empty location), score normally enough to surface (typically 60-89) and note that location is unverified.\n - MATCH or COMPATIBLE (e.g., \"Bay Area\" ≈ \"SF\", \"Remote\" ≈ any): Score normally.\n`\n : '';\n const hasDiscoveryQuery = !!input.discoveryQuery?.trim();\n const entitiesBlock = input.entities.map((e) => {\n const isSource = e.userId === input.discovererId;\n // When an explicit discovery query is active, label the source user's stored\n // intents as background context so the LLM treats the query as primary.\n const intentsLabel = isSource && hasDiscoveryQuery ? 'BACKGROUND INTENTS (use only if query is too vague)' : 'INTENTS';\n const intentsPart = e.intents?.length\n ? `\\n ${intentsLabel}:\\n${e.intents.map((i) => ` - ${i.intentId}: ${i.payload}`).join('\\n')}`\n : '';\n // Mask the discoverer's name so the LLM cannot leak it into reasoning.\n // The system prompt already says \"use third-party references\", but the LLM\n // ignores this when the actual name is visible. Masking it forces role-based\n // language (\"the source user is looking…\" instead of \"Alice is looking…\").\n const displayName = e.userId === input.discovererId\n ? '(source user)'\n : (e.profile.name ?? '');\n return `\n USER: ${e.userId}\n INDEX: ${e.networkId}\n EVIDENCE KEY: ${e.evidenceKey ?? '—'}\n PROFILE: Name: ${displayName} | Bio: ${e.profile.bio ?? ''} | Location: ${e.profile.location ?? ''} | Interests: ${e.profile.interests?.join(', ') ?? ''} | Skills: ${e.profile.skills?.join(', ') ?? ''} | Context: ${e.profile.context ?? ''}${intentsPart}\n RAG SCORE: ${e.ragScore ?? '—'}\n MATCHED VIA: ${e.matchedVia ?? '—'}\n EVIDENCE:\n${renderOpportunityEvidenceForPrompt(e.evidence ?? [])}`;\n }).join('\\n');\n const networkContextPart = input.networkContexts && Object.keys(input.networkContexts).length > 0\n ? `\\n\\nNETWORK CONTEXTS:\\n${Object.entries(input.networkContexts).map(([nid, ctx]) => `[INDEX: ${nid}]\\n${ctx}`).join('\\n\\n')}`\n : '';\n const humanContent = `DISCOVERER: ${input.discovererId}${introModePart}${discoveryQueryPart}${networkContextPart}\\n\\nENTITIES:\\n${entitiesBlock}${existingPart}`;\n const messages = [\n new SystemMessage(entityBundleSystemPrompt),\n new HumanMessage(humanContent),\n ];\n let parsedTotal = 0;\n try {\n const result = await invokeWithAbortSignal(this.entityBundleModel, messages);\n const parsed = entityBundleResponseFormat.parse(result);\n for (const op of parsed.opportunities) {\n op.reasoning = stripUuids(op.reasoning);\n }\n parsedTotal = parsed.opportunities.length;\n const introGuard =\n input.introductionMode\n ? parsed.opportunities.filter((op) => op.actors.length === 2)\n : parsed.opportunities;\n const filtered = introGuard.filter((op) => op.score >= minScore);\n logger.verbose('[OpportunityEvaluator.invokeEntityBundle] Done', {\n total: parsed.opportunities.length,\n afterIntroGuard: introGuard.length,\n accepted: filtered.length,\n returnAll,\n });\n return returnAll ? introGuard : filtered;\n } catch (llmError) {\n logger.error('[OpportunityEvaluator.invokeEntityBundle] Failed', {\n discovererId: input.discovererId,\n totalEntities,\n parsedTotal,\n minScore,\n llmError,\n });\n throw llmError;\n }\n }\n\n /**\n * Factory method to expose the agent as a LangChain tool.\n * Simplified to only accept direct evaluation arguments.\n * PURE: Does not perform any database lookups.\n */\n public static asTool() {\n return tool(\n async (args: {\n sourceProfileContext: string;\n candidatesJson?: string;\n minScore?: number;\n }) => {\n const agent = new OpportunityEvaluator();\n\n const sourceProfileContext = args.sourceProfileContext;\n\n let candidates: CandidateProfile[] = [];\n if (args.candidatesJson) {\n try {\n candidates = JSON.parse(args.candidatesJson);\n } catch (e) {\n logger.error(\"Failed to parse candidates JSON\");\n }\n }\n\n const options: OpportunityEvaluatorOptions = {\n minScore: args.minScore,\n };\n\n return await agent.invoke(sourceProfileContext, candidates, options);\n },\n {\n name: 'opportunity_evaluator',\n description: 'Evaluates candidates against a source profile. SOURCE PROFILE CONTEXT MUST BE PROVIDED.',\n schema: z.object({\n sourceProfileContext: z.string().describe('The resolved source user profile context'),\n candidatesJson: z.string().optional().describe('JSON string list of Candidates'),\n minScore: z.number().optional().describe('Minimum score to accept a match')\n })\n }\n );\n }\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import type { OpportunityEvidence } from '../shared/schemas/network-assignment.schema.js';
2
+ export interface EvidenceCandidateInput {
3
+ networkId: string;
4
+ similarity: number;
5
+ lens: string;
6
+ discoverySource?: 'query' | 'premise-similarity' | 'context-to-intent';
7
+ matchedStrategies?: string[];
8
+ sourcePremiseId?: string;
9
+ candidatePremiseId?: string;
10
+ candidateIntentId?: string;
11
+ sourceContextId?: string;
12
+ candidatePayload?: string;
13
+ candidateSummary?: string;
14
+ }
15
+ export declare function buildCandidateEvidence(candidate: EvidenceCandidateInput): OpportunityEvidence;
16
+ export declare function withCandidateEvidence<T extends EvidenceCandidateInput>(candidate: T): T & {
17
+ evidence: OpportunityEvidence[];
18
+ };
19
+ export declare function mergeOpportunityEvidence(...groups: Array<OpportunityEvidence[] | undefined>): OpportunityEvidence[];
20
+ export declare function withMatchedStrategies(evidence: OpportunityEvidence[], strategies: string[]): OpportunityEvidence[];
21
+ export declare function renderOpportunityEvidenceForPrompt(evidence: OpportunityEvidence[]): string;
22
+ //# sourceMappingURL=opportunity.evidence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opportunity.evidence.d.ts","sourceRoot":"/","sources":["opportunity/opportunity.evidence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gDAAgD,CAAC;AAE1F,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,OAAO,GAAG,oBAAoB,GAAG,mBAAmB,CAAC;IACvE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,sBAAsB,GAAG,mBAAmB,CAiB7F;AAED,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,sBAAsB,EAAE,SAAS,EAAE,CAAC,GAAG,CAAC,GAAG;IAAE,QAAQ,EAAE,mBAAmB,EAAE,CAAA;CAAE,CAE7H;AAED,wBAAgB,wBAAwB,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,mBAAmB,EAAE,GAAG,SAAS,CAAC,GAAG,mBAAmB,EAAE,CAgBnH;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,mBAAmB,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,mBAAmB,EAAE,CAKlH;AAED,wBAAgB,kCAAkC,CAAC,QAAQ,EAAE,mBAAmB,EAAE,GAAG,MAAM,CAa1F"}
@@ -0,0 +1,72 @@
1
+ export function buildCandidateEvidence(candidate) {
2
+ const kind = resolveEvidenceKind(candidate);
3
+ return {
4
+ kind,
5
+ networkId: candidate.networkId,
6
+ score: candidate.similarity,
7
+ lens: candidate.lens,
8
+ discoverySource: candidate.discoverySource,
9
+ matchedStrategies: candidate.matchedStrategies,
10
+ sourcePremiseId: candidate.sourcePremiseId,
11
+ candidatePremiseId: candidate.candidatePremiseId,
12
+ candidateIntentId: candidate.candidateIntentId,
13
+ sourceContextId: candidate.sourceContextId,
14
+ payload: candidate.candidatePayload,
15
+ summary: candidate.candidateSummary,
16
+ assertionText: candidate.candidatePremiseId ? candidate.candidatePayload : undefined,
17
+ };
18
+ }
19
+ export function withCandidateEvidence(candidate) {
20
+ return { ...candidate, evidence: [buildCandidateEvidence(candidate)] };
21
+ }
22
+ export function mergeOpportunityEvidence(...groups) {
23
+ const byKey = new Map();
24
+ for (const evidence of groups.flatMap((group) => group ?? [])) {
25
+ const key = [
26
+ evidence.kind,
27
+ evidence.networkId,
28
+ evidence.sourcePremiseId ?? '',
29
+ evidence.candidatePremiseId ?? '',
30
+ evidence.candidateIntentId ?? '',
31
+ evidence.sourceContextId ?? '',
32
+ evidence.lens ?? '',
33
+ ].join('|');
34
+ const existing = byKey.get(key);
35
+ if (!existing || (evidence.score ?? 0) > (existing.score ?? 0))
36
+ byKey.set(key, evidence);
37
+ }
38
+ return Array.from(byKey.values());
39
+ }
40
+ export function withMatchedStrategies(evidence, strategies) {
41
+ return evidence.map((item) => ({
42
+ ...item,
43
+ matchedStrategies: Array.from(new Set([...(item.matchedStrategies ?? []), ...strategies])),
44
+ }));
45
+ }
46
+ export function renderOpportunityEvidenceForPrompt(evidence) {
47
+ if (evidence.length === 0)
48
+ return ' —';
49
+ return evidence.map((item) => {
50
+ const refs = [
51
+ item.sourcePremiseId ? `sourcePremise=${item.sourcePremiseId}` : undefined,
52
+ item.candidatePremiseId ? `candidatePremise=${item.candidatePremiseId}` : undefined,
53
+ item.candidateIntentId ? `candidateIntent=${item.candidateIntentId}` : undefined,
54
+ item.sourceContextId ? `sourceContext=${item.sourceContextId}` : undefined,
55
+ item.matchedStrategies?.length ? `strategies=${item.matchedStrategies.join(',')}` : undefined,
56
+ ].filter(Boolean).join(', ');
57
+ const text = item.summary ?? item.payload ?? item.assertionText ?? '';
58
+ return ` - ${item.kind} on ${item.networkId} via ${item.lens ?? 'unknown'} score=${item.score?.toFixed(3) ?? '—'}${refs ? ` (${refs})` : ''}${text ? `: ${text}` : ''}`;
59
+ }).join('\n');
60
+ }
61
+ function resolveEvidenceKind(candidate) {
62
+ if (candidate.discoverySource === 'premise-similarity')
63
+ return 'premise_similarity';
64
+ if (candidate.discoverySource === 'context-to-intent')
65
+ return 'context_to_intent';
66
+ if (candidate.candidatePremiseId)
67
+ return 'query_premise';
68
+ if (candidate.candidateIntentId)
69
+ return 'query_intent';
70
+ return 'profile';
71
+ }
72
+ //# sourceMappingURL=opportunity.evidence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opportunity.evidence.js","sourceRoot":"/","sources":["opportunity/opportunity.evidence.ts"],"names":[],"mappings":"AAgBA,MAAM,UAAU,sBAAsB,CAAC,SAAiC;IACtE,MAAM,IAAI,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAC5C,OAAO;QACL,IAAI;QACJ,SAAS,EAAE,SAAS,CAAC,SAAS;QAC9B,KAAK,EAAE,SAAS,CAAC,UAAU;QAC3B,IAAI,EAAE,SAAS,CAAC,IAAI;QACpB,eAAe,EAAE,SAAS,CAAC,eAAe;QAC1C,iBAAiB,EAAE,SAAS,CAAC,iBAAiB;QAC9C,eAAe,EAAE,SAAS,CAAC,eAAe;QAC1C,kBAAkB,EAAE,SAAS,CAAC,kBAAkB;QAChD,iBAAiB,EAAE,SAAS,CAAC,iBAAiB;QAC9C,eAAe,EAAE,SAAS,CAAC,eAAe;QAC1C,OAAO,EAAE,SAAS,CAAC,gBAAgB;QACnC,OAAO,EAAE,SAAS,CAAC,gBAAgB;QACnC,aAAa,EAAE,SAAS,CAAC,kBAAkB,CAAC,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS;KACrF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAmC,SAAY;IAClF,OAAO,EAAE,GAAG,SAAS,EAAE,QAAQ,EAAE,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,GAAG,MAAgD;IAC1F,MAAM,KAAK,GAAG,IAAI,GAAG,EAA+B,CAAC;IACrD,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;QAC9D,MAAM,GAAG,GAAG;YACV,QAAQ,CAAC,IAAI;YACb,QAAQ,CAAC,SAAS;YAClB,QAAQ,CAAC,eAAe,IAAI,EAAE;YAC9B,QAAQ,CAAC,kBAAkB,IAAI,EAAE;YACjC,QAAQ,CAAC,iBAAiB,IAAI,EAAE;YAChC,QAAQ,CAAC,eAAe,IAAI,EAAE;YAC9B,QAAQ,CAAC,IAAI,IAAI,EAAE;SACpB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACZ,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC3F,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,QAA+B,EAAE,UAAoB;IACzF,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC7B,GAAG,IAAI;QACP,iBAAiB,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAC,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC;KAC3F,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,kCAAkC,CAAC,QAA+B;IAChF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IAC1C,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAC3B,MAAM,IAAI,GAAG;YACX,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,iBAAiB,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,SAAS;YAC1E,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,oBAAoB,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,SAAS;YACnF,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,mBAAmB,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,SAAS;YAChF,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,iBAAiB,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,SAAS;YAC1E,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,CAAC,cAAc,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;SAC9F,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC;QACtE,OAAO,SAAS,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,SAAS,QAAQ,IAAI,CAAC,IAAI,IAAI,SAAS,UAAU,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IAC7K,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,SAAS,mBAAmB,CAAC,SAAiC;IAC5D,IAAI,SAAS,CAAC,eAAe,KAAK,oBAAoB;QAAE,OAAO,oBAAoB,CAAC;IACpF,IAAI,SAAS,CAAC,eAAe,KAAK,mBAAmB;QAAE,OAAO,mBAAmB,CAAC;IAClF,IAAI,SAAS,CAAC,kBAAkB;QAAE,OAAO,eAAe,CAAC;IACzD,IAAI,SAAS,CAAC,iBAAiB;QAAE,OAAO,cAAc,CAAC;IACvD,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["import type { OpportunityEvidence } from '../shared/schemas/network-assignment.schema.js';\n\nexport interface EvidenceCandidateInput {\n networkId: string;\n similarity: number;\n lens: string;\n discoverySource?: 'query' | 'premise-similarity' | 'context-to-intent';\n matchedStrategies?: string[];\n sourcePremiseId?: string;\n candidatePremiseId?: string;\n candidateIntentId?: string;\n sourceContextId?: string;\n candidatePayload?: string;\n candidateSummary?: string;\n}\n\nexport function buildCandidateEvidence(candidate: EvidenceCandidateInput): OpportunityEvidence {\n const kind = resolveEvidenceKind(candidate);\n return {\n kind,\n networkId: candidate.networkId,\n score: candidate.similarity,\n lens: candidate.lens,\n discoverySource: candidate.discoverySource,\n matchedStrategies: candidate.matchedStrategies,\n sourcePremiseId: candidate.sourcePremiseId,\n candidatePremiseId: candidate.candidatePremiseId,\n candidateIntentId: candidate.candidateIntentId,\n sourceContextId: candidate.sourceContextId,\n payload: candidate.candidatePayload,\n summary: candidate.candidateSummary,\n assertionText: candidate.candidatePremiseId ? candidate.candidatePayload : undefined,\n };\n}\n\nexport function withCandidateEvidence<T extends EvidenceCandidateInput>(candidate: T): T & { evidence: OpportunityEvidence[] } {\n return { ...candidate, evidence: [buildCandidateEvidence(candidate)] };\n}\n\nexport function mergeOpportunityEvidence(...groups: Array<OpportunityEvidence[] | undefined>): OpportunityEvidence[] {\n const byKey = new Map<string, OpportunityEvidence>();\n for (const evidence of groups.flatMap((group) => group ?? [])) {\n const key = [\n evidence.kind,\n evidence.networkId,\n evidence.sourcePremiseId ?? '',\n evidence.candidatePremiseId ?? '',\n evidence.candidateIntentId ?? '',\n evidence.sourceContextId ?? '',\n evidence.lens ?? '',\n ].join('|');\n const existing = byKey.get(key);\n if (!existing || (evidence.score ?? 0) > (existing.score ?? 0)) byKey.set(key, evidence);\n }\n return Array.from(byKey.values());\n}\n\nexport function withMatchedStrategies(evidence: OpportunityEvidence[], strategies: string[]): OpportunityEvidence[] {\n return evidence.map((item) => ({\n ...item,\n matchedStrategies: Array.from(new Set([...(item.matchedStrategies ?? []), ...strategies])),\n }));\n}\n\nexport function renderOpportunityEvidenceForPrompt(evidence: OpportunityEvidence[]): string {\n if (evidence.length === 0) return ' —';\n return evidence.map((item) => {\n const refs = [\n item.sourcePremiseId ? `sourcePremise=${item.sourcePremiseId}` : undefined,\n item.candidatePremiseId ? `candidatePremise=${item.candidatePremiseId}` : undefined,\n item.candidateIntentId ? `candidateIntent=${item.candidateIntentId}` : undefined,\n item.sourceContextId ? `sourceContext=${item.sourceContextId}` : undefined,\n item.matchedStrategies?.length ? `strategies=${item.matchedStrategies.join(',')}` : undefined,\n ].filter(Boolean).join(', ');\n const text = item.summary ?? item.payload ?? item.assertionText ?? '';\n return ` - ${item.kind} on ${item.networkId} via ${item.lens ?? 'unknown'} score=${item.score?.toFixed(3) ?? '—'}${refs ? ` (${refs})` : ''}${text ? `: ${text}` : ''}`;\n }).join('\\n');\n}\n\nfunction resolveEvidenceKind(candidate: EvidenceCandidateInput): OpportunityEvidence['kind'] {\n if (candidate.discoverySource === 'premise-similarity') return 'premise_similarity';\n if (candidate.discoverySource === 'context-to-intent') return 'context_to_intent';\n if (candidate.candidatePremiseId) return 'query_premise';\n if (candidate.candidateIntentId) return 'query_intent';\n return 'profile';\n}\n"]}
@@ -36,6 +36,7 @@ export type OpportunityEvaluatorLike = {
36
36
  userId: string;
37
37
  role: 'agent' | 'patient' | 'peer';
38
38
  intentId?: string | null;
39
+ evidenceKey?: string | null;
39
40
  }>;
40
41
  }>>;
41
42
  };
@@ -135,7 +136,7 @@ export declare class OpportunityGraphFactory {
135
136
  userNetworks: Id<"networks">[];
136
137
  targetNetworks: TargetNetwork[];
137
138
  indexRelevancyScores: Record<string, number>;
138
- discoverySource: "profile" | "intent";
139
+ discoverySource: "intent" | "profile";
139
140
  resolvedTriggerIntentId: Id<"intents"> | undefined;
140
141
  sourceProfile: SourceProfileData | null;
141
142
  sourcePremises: {
@@ -227,7 +228,7 @@ export declare class OpportunityGraphFactory {
227
228
  userNetworks?: Id<"networks">[] | import("@langchain/langgraph").OverwriteValue<Id<"networks">[]> | undefined;
228
229
  targetNetworks?: TargetNetwork[] | import("@langchain/langgraph").OverwriteValue<TargetNetwork[]> | undefined;
229
230
  indexRelevancyScores?: Record<string, number> | import("@langchain/langgraph").OverwriteValue<Record<string, number>> | undefined;
230
- discoverySource?: "profile" | "intent" | import("@langchain/langgraph").OverwriteValue<"profile" | "intent"> | undefined;
231
+ discoverySource?: "intent" | "profile" | import("@langchain/langgraph").OverwriteValue<"intent" | "profile"> | undefined;
231
232
  resolvedTriggerIntentId?: Id<"intents"> | import("@langchain/langgraph").OverwriteValue<Id<"intents"> | undefined> | undefined;
232
233
  sourceProfile?: SourceProfileData | import("@langchain/langgraph").OverwriteValue<SourceProfileData | null> | null | undefined;
233
234
  sourcePremises?: {
@@ -324,7 +325,7 @@ export declare class OpportunityGraphFactory {
324
325
  agentTimings?: DebugMetaAgent[] | import("@langchain/langgraph").OverwriteValue<DebugMetaAgent[]> | undefined;
325
326
  discoveryNegotiations?: import("./question.prompt.js").DiscoveryNegotiation[] | import("@langchain/langgraph").OverwriteValue<import("./question.prompt.js").DiscoveryNegotiation[]> | undefined;
326
327
  discoverySummary?: import("./question.prompt.js").DiscoverySummary | import("@langchain/langgraph").OverwriteValue<import("./question.prompt.js").DiscoverySummary | null> | null | undefined;
327
- }, "send" | "update" | "discovery" | "read" | "__start__" | "prep" | "negotiate_existing" | "approve_introduction" | "scope" | "resolve" | "evaluation" | "ranking" | "intro_validation" | "intro_evaluation" | "persist" | "delete_opp" | "negotiate", {
328
+ }, "scope" | "send" | "update" | "discovery" | "read" | "__start__" | "prep" | "negotiate_existing" | "approve_introduction" | "resolve" | "evaluation" | "ranking" | "intro_validation" | "intro_evaluation" | "persist" | "delete_opp" | "negotiate", {
328
329
  userId: import("@langchain/langgraph").BaseChannel<Id<"users">, Id<"users"> | import("@langchain/langgraph").OverwriteValue<Id<"users">>, unknown>;
329
330
  searchQuery: import("@langchain/langgraph").BaseChannel<string | undefined, string | import("@langchain/langgraph").OverwriteValue<string | undefined> | undefined, unknown>;
330
331
  networkId: import("@langchain/langgraph").BaseChannel<Id<"networks"> | undefined, Id<"networks"> | import("@langchain/langgraph").OverwriteValue<Id<"networks"> | undefined> | undefined, unknown>;
@@ -361,7 +362,7 @@ export declare class OpportunityGraphFactory {
361
362
  userNetworks: import("@langchain/langgraph").BaseChannel<Id<"networks">[], Id<"networks">[] | import("@langchain/langgraph").OverwriteValue<Id<"networks">[]>, unknown>;
362
363
  targetNetworks: import("@langchain/langgraph").BaseChannel<TargetNetwork[], TargetNetwork[] | import("@langchain/langgraph").OverwriteValue<TargetNetwork[]>, unknown>;
363
364
  indexRelevancyScores: import("@langchain/langgraph").BaseChannel<Record<string, number>, Record<string, number> | import("@langchain/langgraph").OverwriteValue<Record<string, number>>, unknown>;
364
- discoverySource: import("@langchain/langgraph").BaseChannel<"profile" | "intent", "profile" | "intent" | import("@langchain/langgraph").OverwriteValue<"profile" | "intent">, unknown>;
365
+ discoverySource: import("@langchain/langgraph").BaseChannel<"intent" | "profile", "intent" | "profile" | import("@langchain/langgraph").OverwriteValue<"intent" | "profile">, unknown>;
365
366
  resolvedTriggerIntentId: import("@langchain/langgraph").BaseChannel<Id<"intents"> | undefined, Id<"intents"> | import("@langchain/langgraph").OverwriteValue<Id<"intents"> | undefined> | undefined, unknown>;
366
367
  sourceProfile: import("@langchain/langgraph").BaseChannel<SourceProfileData | null, SourceProfileData | import("@langchain/langgraph").OverwriteValue<SourceProfileData | null> | null, unknown>;
367
368
  sourcePremises: import("@langchain/langgraph").BaseChannel<{
@@ -532,7 +533,7 @@ export declare class OpportunityGraphFactory {
532
533
  userNetworks: import("@langchain/langgraph").BaseChannel<Id<"networks">[], Id<"networks">[] | import("@langchain/langgraph").OverwriteValue<Id<"networks">[]>, unknown>;
533
534
  targetNetworks: import("@langchain/langgraph").BaseChannel<TargetNetwork[], TargetNetwork[] | import("@langchain/langgraph").OverwriteValue<TargetNetwork[]>, unknown>;
534
535
  indexRelevancyScores: import("@langchain/langgraph").BaseChannel<Record<string, number>, Record<string, number> | import("@langchain/langgraph").OverwriteValue<Record<string, number>>, unknown>;
535
- discoverySource: import("@langchain/langgraph").BaseChannel<"profile" | "intent", "profile" | "intent" | import("@langchain/langgraph").OverwriteValue<"profile" | "intent">, unknown>;
536
+ discoverySource: import("@langchain/langgraph").BaseChannel<"intent" | "profile", "intent" | "profile" | import("@langchain/langgraph").OverwriteValue<"intent" | "profile">, unknown>;
536
537
  resolvedTriggerIntentId: import("@langchain/langgraph").BaseChannel<Id<"intents"> | undefined, Id<"intents"> | import("@langchain/langgraph").OverwriteValue<Id<"intents"> | undefined> | undefined, unknown>;
537
538
  sourceProfile: import("@langchain/langgraph").BaseChannel<SourceProfileData | null, SourceProfileData | import("@langchain/langgraph").OverwriteValue<SourceProfileData | null> | null, unknown>;
538
539
  sourcePremises: import("@langchain/langgraph").BaseChannel<{
@@ -762,7 +763,7 @@ export declare class OpportunityGraphFactory {
762
763
  resolve: {
763
764
  resolvedTriggerIntentId: Id<"intents">;
764
765
  resolvedIntentInIndex: boolean;
765
- discoverySource: "profile" | "intent";
766
+ discoverySource: "intent" | "profile";
766
767
  error?: undefined;
767
768
  trace?: undefined;
768
769
  } | {
@@ -984,7 +985,7 @@ export declare class OpportunityGraphFactory {
984
985
  userNetworks: import("@langchain/langgraph").BaseChannel<Id<"networks">[], Id<"networks">[] | import("@langchain/langgraph").OverwriteValue<Id<"networks">[]>, unknown>;
985
986
  targetNetworks: import("@langchain/langgraph").BaseChannel<TargetNetwork[], TargetNetwork[] | import("@langchain/langgraph").OverwriteValue<TargetNetwork[]>, unknown>;
986
987
  indexRelevancyScores: import("@langchain/langgraph").BaseChannel<Record<string, number>, Record<string, number> | import("@langchain/langgraph").OverwriteValue<Record<string, number>>, unknown>;
987
- discoverySource: import("@langchain/langgraph").BaseChannel<"profile" | "intent", "profile" | "intent" | import("@langchain/langgraph").OverwriteValue<"profile" | "intent">, unknown>;
988
+ discoverySource: import("@langchain/langgraph").BaseChannel<"intent" | "profile", "intent" | "profile" | import("@langchain/langgraph").OverwriteValue<"intent" | "profile">, unknown>;
988
989
  resolvedTriggerIntentId: import("@langchain/langgraph").BaseChannel<Id<"intents"> | undefined, Id<"intents"> | import("@langchain/langgraph").OverwriteValue<Id<"intents"> | undefined> | undefined, unknown>;
989
990
  sourceProfile: import("@langchain/langgraph").BaseChannel<SourceProfileData | null, SourceProfileData | import("@langchain/langgraph").OverwriteValue<SourceProfileData | null> | null, unknown>;
990
991
  sourcePremises: import("@langchain/langgraph").BaseChannel<{
@@ -1 +1 @@
1
- {"version":3,"file":"opportunity.graph.d.ts","sourceRoot":"/","sources":["opportunity/opportunity.graph.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"opportunity.graph.d.ts","sourceRoot":"/","sources":["opportunity/opportunity.graph.ts"],"names":[],"mappings":"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"}