@indexnetwork/protocol 4.0.0-rc.289.1 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat/chat.prompt.js +21 -21
- package/dist/chat/chat.prompt.js.map +1 -1
- package/dist/chat/chat.prompt.modules.js +8 -8
- package/dist/chat/chat.prompt.modules.js.map +1 -1
- package/dist/contact/contact.tools.js +3 -3
- package/dist/contact/contact.tools.js.map +1 -1
- package/dist/enrichment/enrichment.graph.js +1 -1
- package/dist/enrichment/enrichment.graph.js.map +1 -1
- package/dist/enrichment/enrichment.tools.js +47 -47
- package/dist/enrichment/enrichment.tools.js.map +1 -1
- package/dist/intent/intent.graph.d.ts.map +1 -1
- package/dist/intent/intent.graph.js +7 -6
- package/dist/intent/intent.graph.js.map +1 -1
- package/dist/intent/intent.tools.js +2 -2
- package/dist/intent/intent.tools.js.map +1 -1
- package/dist/mcp/mcp.server.d.ts +1 -1
- package/dist/mcp/mcp.server.d.ts.map +1 -1
- package/dist/mcp/mcp.server.js +12 -4
- package/dist/mcp/mcp.server.js.map +1 -1
- package/dist/negotiation/negotiation.tools.js +1 -1
- package/dist/negotiation/negotiation.tools.js.map +1 -1
- package/dist/network/network.tools.js +2 -2
- package/dist/network/network.tools.js.map +1 -1
- package/dist/opportunity/opportunity.graph.d.ts +8 -8
- package/dist/opportunity/opportunity.graph.js +7 -7
- package/dist/opportunity/opportunity.graph.js.map +1 -1
- package/dist/opportunity/opportunity.state.d.ts +2 -2
- package/dist/opportunity/opportunity.state.d.ts.map +1 -1
- package/dist/opportunity/opportunity.state.js +1 -1
- package/dist/opportunity/opportunity.state.js.map +1 -1
- package/dist/opportunity/opportunity.tools.js +8 -8
- package/dist/opportunity/opportunity.tools.js.map +1 -1
- package/dist/shared/agent/tool.registry.d.ts.map +1 -1
- package/dist/shared/agent/tool.registry.js +26 -0
- package/dist/shared/agent/tool.registry.js.map +1 -1
- package/dist/shared/agent/tool.runtime.d.ts.map +1 -1
- package/dist/shared/agent/tool.runtime.js +6 -0
- package/dist/shared/agent/tool.runtime.js.map +1 -1
- package/dist/shared/agent/utility.tools.js +5 -5
- package/dist/shared/agent/utility.tools.js.map +1 -1
- package/dist/shared/interfaces/database.interface.d.ts +2 -2
- package/dist/shared/interfaces/database.interface.js.map +1 -1
- package/dist/shared/interfaces/enrichment-run.interface.d.ts +1 -1
- package/dist/shared/interfaces/enrichment-run.interface.d.ts.map +1 -1
- package/dist/shared/interfaces/enrichment-run.interface.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.registry.d.ts","sourceRoot":"/","sources":["shared/agent/tool.registry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAmC,QAAQ,EAAqB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAmBpH;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,GAAG,YAAY,
|
|
1
|
+
{"version":3,"file":"tool.registry.d.ts","sourceRoot":"/","sources":["shared/agent/tool.registry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAmC,QAAQ,EAAqB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAmBpH;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,GAAG,YAAY,CAwF/D"}
|
|
@@ -73,6 +73,32 @@ export function createToolRegistry(deps) {
|
|
|
73
73
|
if (deps.chatSession) {
|
|
74
74
|
createChatTools(dt, deps);
|
|
75
75
|
}
|
|
76
|
+
// Deprecated tool-name aliases (IND-371). The canonical implementations are now
|
|
77
|
+
// registered under their *_user_context / *_enrichment_run names; we expose the
|
|
78
|
+
// legacy *_user_profile / *_profile_run names as thin aliases that delegate to the
|
|
79
|
+
// exact same handler + schema, so existing MCP clients keep working while they
|
|
80
|
+
// migrate. The old names are removed in IND-373.
|
|
81
|
+
const DEPRECATED_TOOL_ALIASES = [
|
|
82
|
+
["read_user_profiles", "read_user_contexts"],
|
|
83
|
+
["create_user_profile", "create_user_context"],
|
|
84
|
+
["update_user_profile", "update_user_context"],
|
|
85
|
+
["confirm_user_profile", "confirm_user_context"],
|
|
86
|
+
["preview_user_profile", "preview_user_context"],
|
|
87
|
+
["get_profile_run", "get_enrichment_run"],
|
|
88
|
+
["cancel_profile_run", "cancel_enrichment_run"],
|
|
89
|
+
];
|
|
90
|
+
for (const [oldName, canonicalName] of DEPRECATED_TOOL_ALIASES) {
|
|
91
|
+
const canonical = registry.get(canonicalName);
|
|
92
|
+
if (!canonical) {
|
|
93
|
+
logger.warn(`Cannot register deprecated alias ${oldName}: canonical ${canonicalName} not found`);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
registry.set(oldName, {
|
|
97
|
+
...canonical,
|
|
98
|
+
name: oldName,
|
|
99
|
+
description: `[DEPRECATED — use \`${canonicalName}\` instead; this alias is retained for backward compatibility and will be removed.] ${canonical.description}`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
76
102
|
logger.verbose(`Tool registry created with ${registry.size} tools`);
|
|
77
103
|
return registry;
|
|
78
104
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.registry.js","sourceRoot":"/","sources":["shared/agent/tool.registry.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAErE,MAAM,MAAM,GAAG,cAAc,CAAC,cAAc,CAAC,CAAC;AAE9C;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAc;IAC/C,MAAM,QAAQ,GAAiB,IAAI,GAAG,EAAE,CAAC;IAEzC,0DAA0D;IAC1D,SAAS,UAAU,CAAsB,IAKxC;QACC,MAAM,KAAK,GAAsB;YAC/B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,MAAM,EAAE,IAAI,CAAC,WAAW;YACxB,OAAO,EAAE,KAAK,EAAE,KAAuD,EAAE,EAAE;gBACzE,MAAM,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,IAAI,EAAE,EAAE;oBACnC,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE;oBAC7E,KAAK,EAAE,qBAAqB,CAAC,KAAK,CAAC,KAAK,CAAC;iBAC1C,CAAC,CAAC;gBACH,IAAI,CAAC;oBACH,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAmB,EAAE,CAAC,CAAC;gBAC1F,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,WAAW,GAAG,cAAc,CAAC,QAAQ,EAAE,EAAE,WAAW,CAAC;oBAC3D,IAAI,WAAW,EAAE,OAAO,EAAE,CAAC;wBACzB,MAAM,GAAG,CAAC;oBACZ,CAAC;oBACD,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,SAAS,EAAE;wBAClC,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;qBACxD,CAAC,CAAC;oBACH,OAAO,KAAK,CAAC,qBAAqB,IAAI,CAAC,IAAI,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACtG,CAAC;YACH,CAAC;SACF,CAAC;QAEF,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAE/B,6EAA6E;QAC7E,qEAAqE;QACrE,OAAO,IAAe,CAAC;IACzB,CAAC;IAED,uFAAuF;IACvF,0EAA0E;IAC1E,MAAM,EAAE,GAAG,UAAwB,CAAC;IACpC,qBAAqB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAChC,iBAAiB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC5B,kBAAkB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC7B,sBAAsB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACjC,kBAAkB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC7B,sBAAsB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACjC,kBAAkB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC7B,gBAAgB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC3B,sBAAsB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACjC,kBAAkB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC7B,qBAAqB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAChC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,eAAe,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,8BAA8B,QAAQ,CAAC,IAAI,QAAQ,CAAC,CAAC;IACpE,OAAO,QAAQ,CAAC;AAClB,CAAC","sourcesContent":["import { z } from 'zod';\n\nimport type { DefineTool, ResolvedToolContext, ToolDeps, RawToolDefinition, ToolRegistry } from './tool.helpers.js';\nimport { error, redactSensitiveFields } from './tool.helpers.js';\nimport { createEnrichmentTools } from '../../enrichment/enrichment.tools.js';\nimport { createIntentTools } from '../../intent/intent.tools.js';\nimport { createNetworkTools } from '../../network/network.tools.js';\nimport { createOpportunityTools } from '../../opportunity/opportunity.tools.js';\nimport { createUtilityTools } from './utility.tools.js';\nimport { createIntegrationTools } from '../../integration/integration.tools.js';\nimport { createContactTools } from '../../contact/contact.tools.js';\nimport { createAgentTools } from '../../agent/agent.tools.js';\nimport { createNegotiationTools } from '../../negotiation/negotiation.tools.js';\nimport { createChatTools } from '../../chat/chat.tools.js';\nimport { createPremiseTools } from '../../premise/premise.tools.js';\nimport { createQuestionerTools } from '../../questioner/questioner.tools.js';\nimport { protocolLogger } from '../observability/protocol.logger.js';\nimport { requestContext } from '../observability/request-context.js';\n\nconst logger = protocolLogger('ToolRegistry');\n\n/**\n * Creates a tool registry containing all tool handlers indexed by name.\n * Handlers are raw async functions (not LangChain tool() wrappers) that\n * accept { context, query } and return a JSON string.\n *\n * @param deps - Shared tool dependencies (graphs, database, embedder, etc.)\n * @param context - Resolved user context for this request.\n * @returns Map of tool name to raw tool definition.\n */\nexport function createToolRegistry(deps: ToolDeps): ToolRegistry {\n const registry: ToolRegistry = new Map();\n\n // defineTool that captures raw handlers into the registry\n function defineTool<T extends z.ZodType>(opts: {\n name: string;\n description: string;\n querySchema: T;\n handler: (input: { context: ResolvedToolContext; query: z.infer<T> }) => Promise<string>;\n }) {\n const entry: RawToolDefinition = {\n name: opts.name,\n description: opts.description,\n schema: opts.querySchema,\n handler: async (input: { context: ResolvedToolContext; query: unknown }) => {\n logger.verbose(`Tool: ${opts.name}`, {\n context: { userId: input.context.userId, networkId: input.context.networkId },\n query: redactSensitiveFields(input.query),\n });\n try {\n return await opts.handler({ context: input.context, query: input.query as z.infer<T> });\n } catch (err) {\n const abortSignal = requestContext.getStore()?.abortSignal;\n if (abortSignal?.aborted) {\n throw err;\n }\n logger.error(`${opts.name} failed`, {\n error: err instanceof Error ? err.message : String(err),\n });\n return error(`Failed to execute ${opts.name}: ${err instanceof Error ? err.message : String(err)}`);\n }\n },\n };\n\n registry.set(opts.name, entry);\n\n // Return a dummy — create*Tools functions collect return values into arrays,\n // but for the registry path we only need the side-effect on the Map.\n return null as unknown;\n }\n\n // Create all tool domains -- each one calls defineTool() which populates the registry.\n // The local defineTool is compatible with DefineTool (which returns any).\n const dt = defineTool as DefineTool;\n createEnrichmentTools(dt, deps);\n createIntentTools(dt, deps);\n createNetworkTools(dt, deps);\n createOpportunityTools(dt, deps);\n createUtilityTools(dt, deps);\n createIntegrationTools(dt, deps);\n createContactTools(dt, deps);\n createAgentTools(dt, deps);\n createNegotiationTools(dt, deps);\n createPremiseTools(dt, deps);\n createQuestionerTools(dt, deps);\n if (deps.chatSession) {\n createChatTools(dt, deps);\n }\n\n logger.verbose(`Tool registry created with ${registry.size} tools`);\n return registry;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tool.registry.js","sourceRoot":"/","sources":["shared/agent/tool.registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\n\nimport type { DefineTool, ResolvedToolContext, ToolDeps, RawToolDefinition, ToolRegistry } from './tool.helpers.js';\nimport { error, redactSensitiveFields } from './tool.helpers.js';\nimport { createEnrichmentTools } from '../../enrichment/enrichment.tools.js';\nimport { createIntentTools } from '../../intent/intent.tools.js';\nimport { createNetworkTools } from '../../network/network.tools.js';\nimport { createOpportunityTools } from '../../opportunity/opportunity.tools.js';\nimport { createUtilityTools } from './utility.tools.js';\nimport { createIntegrationTools } from '../../integration/integration.tools.js';\nimport { createContactTools } from '../../contact/contact.tools.js';\nimport { createAgentTools } from '../../agent/agent.tools.js';\nimport { createNegotiationTools } from '../../negotiation/negotiation.tools.js';\nimport { createChatTools } from '../../chat/chat.tools.js';\nimport { createPremiseTools } from '../../premise/premise.tools.js';\nimport { createQuestionerTools } from '../../questioner/questioner.tools.js';\nimport { protocolLogger } from '../observability/protocol.logger.js';\nimport { requestContext } from '../observability/request-context.js';\n\nconst logger = protocolLogger('ToolRegistry');\n\n/**\n * Creates a tool registry containing all tool handlers indexed by name.\n * Handlers are raw async functions (not LangChain tool() wrappers) that\n * accept { context, query } and return a JSON string.\n *\n * @param deps - Shared tool dependencies (graphs, database, embedder, etc.)\n * @param context - Resolved user context for this request.\n * @returns Map of tool name to raw tool definition.\n */\nexport function createToolRegistry(deps: ToolDeps): ToolRegistry {\n const registry: ToolRegistry = new Map();\n\n // defineTool that captures raw handlers into the registry\n function defineTool<T extends z.ZodType>(opts: {\n name: string;\n description: string;\n querySchema: T;\n handler: (input: { context: ResolvedToolContext; query: z.infer<T> }) => Promise<string>;\n }) {\n const entry: RawToolDefinition = {\n name: opts.name,\n description: opts.description,\n schema: opts.querySchema,\n handler: async (input: { context: ResolvedToolContext; query: unknown }) => {\n logger.verbose(`Tool: ${opts.name}`, {\n context: { userId: input.context.userId, networkId: input.context.networkId },\n query: redactSensitiveFields(input.query),\n });\n try {\n return await opts.handler({ context: input.context, query: input.query as z.infer<T> });\n } catch (err) {\n const abortSignal = requestContext.getStore()?.abortSignal;\n if (abortSignal?.aborted) {\n throw err;\n }\n logger.error(`${opts.name} failed`, {\n error: err instanceof Error ? err.message : String(err),\n });\n return error(`Failed to execute ${opts.name}: ${err instanceof Error ? err.message : String(err)}`);\n }\n },\n };\n\n registry.set(opts.name, entry);\n\n // Return a dummy — create*Tools functions collect return values into arrays,\n // but for the registry path we only need the side-effect on the Map.\n return null as unknown;\n }\n\n // Create all tool domains -- each one calls defineTool() which populates the registry.\n // The local defineTool is compatible with DefineTool (which returns any).\n const dt = defineTool as DefineTool;\n createEnrichmentTools(dt, deps);\n createIntentTools(dt, deps);\n createNetworkTools(dt, deps);\n createOpportunityTools(dt, deps);\n createUtilityTools(dt, deps);\n createIntegrationTools(dt, deps);\n createContactTools(dt, deps);\n createAgentTools(dt, deps);\n createNegotiationTools(dt, deps);\n createPremiseTools(dt, deps);\n createQuestionerTools(dt, deps);\n if (deps.chatSession) {\n createChatTools(dt, deps);\n }\n\n // Deprecated tool-name aliases (IND-371). The canonical implementations are now\n // registered under their *_user_context / *_enrichment_run names; we expose the\n // legacy *_user_profile / *_profile_run names as thin aliases that delegate to the\n // exact same handler + schema, so existing MCP clients keep working while they\n // migrate. The old names are removed in IND-373.\n const DEPRECATED_TOOL_ALIASES: ReadonlyArray<readonly [oldName: string, canonicalName: string]> = [\n [\"read_user_profiles\", \"read_user_contexts\"],\n [\"create_user_profile\", \"create_user_context\"],\n [\"update_user_profile\", \"update_user_context\"],\n [\"confirm_user_profile\", \"confirm_user_context\"],\n [\"preview_user_profile\", \"preview_user_context\"],\n [\"get_profile_run\", \"get_enrichment_run\"],\n [\"cancel_profile_run\", \"cancel_enrichment_run\"],\n ];\n for (const [oldName, canonicalName] of DEPRECATED_TOOL_ALIASES) {\n const canonical = registry.get(canonicalName);\n if (!canonical) {\n logger.warn(`Cannot register deprecated alias ${oldName}: canonical ${canonicalName} not found`);\n continue;\n }\n registry.set(oldName, {\n ...canonical,\n name: oldName,\n description: `[DEPRECATED — use \\`${canonicalName}\\` instead; this alias is retained for backward compatibility and will be removed.] ${canonical.description}`,\n });\n }\n\n logger.verbose(`Tool registry created with ${registry.size} tools`);\n return registry;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.runtime.d.ts","sourceRoot":"/","sources":["shared/agent/tool.runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAGxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAEhF,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,cAAc,GAAG,iBAAiB,CAAC;AAC3E,MAAM,MAAM,oBAAoB,GAAG,cAAc,GAAG,gBAAgB,GAAG,uBAAuB,CAAC;AAE/F,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,gBAAgB,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC;IACzC,OAAO,EAAE,mBAAmB,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;
|
|
1
|
+
{"version":3,"file":"tool.runtime.d.ts","sourceRoot":"/","sources":["shared/agent/tool.runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAGxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAEhF,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,cAAc,GAAG,iBAAiB,CAAC;AAC3E,MAAM,MAAM,oBAAoB,GAAG,cAAc,GAAG,gBAAgB,GAAG,uBAAuB,CAAC;AAE/F,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,gBAAgB,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC;IACzC,OAAO,EAAE,mBAAmB,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAyED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CA0BxE;AAED,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,IAAI,EAAE,oBAAoB;aAE1B,QAAQ,EAAE,MAAM;aAChB,MAAM,EAAE,iBAAiB;gBAHzB,IAAI,EAAE,oBAAoB,EAC1C,OAAO,EAAE,MAAM,EACC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,iBAAiB;CAK5C;AA+CD,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAE1F;AA4DD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAapE"}
|
|
@@ -31,6 +31,12 @@ const FAST_TOOLS = new Set([
|
|
|
31
31
|
"retract_premise",
|
|
32
32
|
]);
|
|
33
33
|
const ASYNC_CANDIDATE_TOOLS = new Set([
|
|
34
|
+
// Canonical *_user_context names (IND-371)
|
|
35
|
+
"read_user_contexts",
|
|
36
|
+
"preview_user_context",
|
|
37
|
+
"create_user_context",
|
|
38
|
+
"update_user_context",
|
|
39
|
+
// Deprecated *_user_profile aliases (kept until IND-373 retires them)
|
|
34
40
|
"read_user_profiles",
|
|
35
41
|
"preview_user_profile",
|
|
36
42
|
"create_user_profile",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.runtime.js","sourceRoot":"/","sources":["shared/agent/tool.runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { timed } from \"../observability/performance.js\";\nimport type { TraceEmitter } from \"../observability/request-context.js\";\nimport { requestContext } from \"../observability/request-context.js\";\n\nimport type { RawToolDefinition, ResolvedToolContext } from \"./tool.helpers.js\";\n\nexport type ToolTimeoutClass = \"fast\" | \"bounded_slow\" | \"async_candidate\";\nexport type ToolRuntimeErrorCode = \"TOOL_TIMEOUT\" | \"TOOL_CANCELLED\" | \"TOOL_OUTPUT_TOO_LARGE\";\n\nexport interface ToolTimeoutPolicy {\n class: ToolTimeoutClass;\n timeoutMs: number;\n maxOutputBytes: number;\n}\n\nexport interface ToolInvocationRuntimeInput {\n toolName: string;\n tool: Pick<RawToolDefinition, \"handler\">;\n context: ResolvedToolContext;\n query: unknown;\n signal?: AbortSignal;\n traceEmitter?: TraceEmitter;\n timeoutMs?: number;\n maxOutputBytes?: number;\n}\n\nconst FAST_TIMEOUT_MS = 10_000;\nconst BOUNDED_SLOW_TIMEOUT_MS = 45_000;\nconst ASYNC_CANDIDATE_TIMEOUT_MS = 50_000;\nconst DEFAULT_MAX_OUTPUT_BYTES = 1_000_000;\n\nconst FAST_TOOLS = new Set([\n \"record_onboarding_privacy_consent\",\n \"create_intent_index\",\n \"delete_intent_index\",\n \"search_intents\",\n \"read_networks\",\n \"update_network\",\n \"create_network\",\n \"delete_network\",\n \"create_network_membership\",\n \"delete_network_membership\",\n \"confirm_opportunity_delivery\",\n \"read_docs\",\n \"get_discovery_run\",\n \"cancel_discovery_run\",\n \"get_profile_run\",\n \"cancel_profile_run\",\n \"remove_contact\",\n \"register_agent\",\n \"list_agents\",\n \"update_agent\",\n \"delete_agent\",\n \"grant_agent_permission\",\n \"revoke_agent_permission\",\n \"retract_premise\",\n]);\n\nconst ASYNC_CANDIDATE_TOOLS = new Set([\n \"read_user_profiles\",\n \"preview_user_profile\",\n \"create_user_profile\",\n \"update_user_profile\",\n \"create_intent\",\n \"update_intent\",\n \"discover_opportunities\",\n \"scrape_url\",\n \"import_gmail_contacts\",\n \"import_contacts\",\n \"respond_to_negotiation\",\n \"create_premise\",\n \"update_premise\",\n]);\n\nfunction parsePositiveIntEnv(name: string, fallback: number): number {\n const raw = process.env[name];\n if (!raw) return fallback;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed > 0 && parsed <= Number.MAX_SAFE_INTEGER\n ? parsed\n : fallback;\n}\n\nfunction toolNameEnv(toolName: string): string {\n return `MCP_TOOL_TIMEOUT_${toolName.toUpperCase().replace(/[^A-Z0-9]+/g, \"_\")}_MS`;\n}\n\nfunction toolNameOutputEnv(toolName: string): string {\n return `MCP_TOOL_MAX_OUTPUT_${toolName.toUpperCase().replace(/[^A-Z0-9]+/g, \"_\")}_BYTES`;\n}\n\nexport function getToolTimeoutPolicy(toolName: string): ToolTimeoutPolicy {\n const classification: ToolTimeoutClass = FAST_TOOLS.has(toolName)\n ? \"fast\"\n : ASYNC_CANDIDATE_TOOLS.has(toolName)\n ? \"async_candidate\"\n : \"bounded_slow\";\n\n let classDefault: number;\n switch (classification) {\n case \"fast\":\n classDefault = parsePositiveIntEnv(\"MCP_TOOL_TIMEOUT_FAST_MS\", FAST_TIMEOUT_MS);\n break;\n case \"async_candidate\":\n classDefault = parsePositiveIntEnv(\"MCP_TOOL_TIMEOUT_ASYNC_CANDIDATE_MS\", ASYNC_CANDIDATE_TIMEOUT_MS);\n break;\n default:\n classDefault = parsePositiveIntEnv(\"MCP_TOOL_TIMEOUT_BOUNDED_SLOW_MS\", BOUNDED_SLOW_TIMEOUT_MS);\n }\n\n const defaultMaxOutputBytes = parsePositiveIntEnv(\"MCP_TOOL_MAX_OUTPUT_BYTES\", DEFAULT_MAX_OUTPUT_BYTES);\n\n return {\n class: classification,\n timeoutMs: parsePositiveIntEnv(toolNameEnv(toolName), classDefault),\n maxOutputBytes: parsePositiveIntEnv(toolNameOutputEnv(toolName), defaultMaxOutputBytes),\n };\n}\n\nexport class ToolRuntimeError extends Error {\n constructor(\n public readonly code: ToolRuntimeErrorCode,\n message: string,\n public readonly toolName: string,\n public readonly policy: ToolTimeoutPolicy,\n ) {\n super(message);\n this.name = \"ToolRuntimeError\";\n }\n}\n\n/** Builds the typed timeout/cancellation error shared by the abort race and catch paths. */\nfunction makeAbortError(timedOut: boolean, toolName: string, policy: ToolTimeoutPolicy): ToolRuntimeError {\n const code: ToolRuntimeErrorCode = timedOut ? \"TOOL_TIMEOUT\" : \"TOOL_CANCELLED\";\n return new ToolRuntimeError(\n code,\n code === \"TOOL_TIMEOUT\"\n ? `Tool ${toolName} timed out after ${policy.timeoutMs}ms.`\n : `Tool ${toolName} was cancelled before it completed.`,\n toolName,\n policy,\n );\n}\n\nfunction combineSignals(signals: Array<AbortSignal | undefined>): {\n signal: AbortSignal;\n abort: (reason?: unknown) => void;\n cleanup: () => void;\n} {\n const controller = new AbortController();\n const listeners: Array<() => void> = [];\n\n for (const source of signals) {\n if (!source) continue;\n if (source.aborted) {\n controller.abort(source.reason);\n break;\n }\n const onAbort = () => {\n if (!controller.signal.aborted) controller.abort(source.reason);\n };\n source.addEventListener(\"abort\", onAbort, { once: true });\n listeners.push(() => source.removeEventListener(\"abort\", onAbort));\n }\n\n return {\n signal: controller.signal,\n abort: (reason?: unknown) => {\n if (!controller.signal.aborted) controller.abort(reason);\n },\n cleanup: () => {\n for (const cleanup of listeners) cleanup();\n },\n };\n}\n\nexport async function invokeToolRuntime(input: ToolInvocationRuntimeInput): Promise<string> {\n return timed(`ToolRuntime.${input.toolName}`, () => invokeToolRuntimeInner(input));\n}\n\nasync function invokeToolRuntimeInner(input: ToolInvocationRuntimeInput): Promise<string> {\n const basePolicy = getToolTimeoutPolicy(input.toolName);\n const policy = {\n ...basePolicy,\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n ...(input.maxOutputBytes !== undefined ? { maxOutputBytes: input.maxOutputBytes } : {}),\n };\n const inherited = requestContext.getStore();\n const combined = combineSignals([input.signal, inherited?.abortSignal]);\n let timedOut = false;\n const timer = setTimeout(() => {\n timedOut = true;\n combined.abort(new Error(`Tool ${input.toolName} timed out after ${policy.timeoutMs}ms`));\n }, policy.timeoutMs);\n\n let removeAbortListener = () => {};\n const abortPromise = new Promise<never>((_, reject) => {\n const onAbort = () => {\n reject(makeAbortError(timedOut, input.toolName, policy));\n };\n combined.signal.addEventListener(\"abort\", onAbort, { once: true });\n removeAbortListener = () => combined.signal.removeEventListener(\"abort\", onAbort);\n });\n\n try {\n const run = () => input.tool.handler({ context: input.context, query: input.query });\n const toolPromise = requestContext.run(\n {\n ...inherited,\n abortSignal: combined.signal,\n traceEmitter: input.traceEmitter ?? inherited?.traceEmitter,\n },\n run,\n );\n const result = await Promise.race([toolPromise, abortPromise]);\n const outputBytes = new TextEncoder().encode(result).byteLength;\n if (outputBytes > policy.maxOutputBytes) {\n throw new ToolRuntimeError(\n \"TOOL_OUTPUT_TOO_LARGE\",\n `Tool ${input.toolName} returned ${outputBytes} bytes, exceeding the ${policy.maxOutputBytes} byte limit.`,\n input.toolName,\n policy,\n );\n }\n return result;\n } catch (err) {\n if (err instanceof ToolRuntimeError) throw err;\n if (combined.signal.aborted) {\n throw makeAbortError(timedOut, input.toolName, policy);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n removeAbortListener();\n combined.cleanup();\n }\n}\n\nexport function toolRuntimeErrorToResult(err: unknown): string | null {\n if (!(err instanceof ToolRuntimeError)) return null;\n return JSON.stringify({\n success: false,\n code: err.code,\n error: err.message,\n data: {\n tool: err.toolName,\n timeoutClass: err.policy.class,\n timeoutMs: err.policy.timeoutMs,\n maxOutputBytes: err.policy.maxOutputBytes,\n },\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tool.runtime.js","sourceRoot":"/","sources":["shared/agent/tool.runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { timed } from \"../observability/performance.js\";\nimport type { TraceEmitter } from \"../observability/request-context.js\";\nimport { requestContext } from \"../observability/request-context.js\";\n\nimport type { RawToolDefinition, ResolvedToolContext } from \"./tool.helpers.js\";\n\nexport type ToolTimeoutClass = \"fast\" | \"bounded_slow\" | \"async_candidate\";\nexport type ToolRuntimeErrorCode = \"TOOL_TIMEOUT\" | \"TOOL_CANCELLED\" | \"TOOL_OUTPUT_TOO_LARGE\";\n\nexport interface ToolTimeoutPolicy {\n class: ToolTimeoutClass;\n timeoutMs: number;\n maxOutputBytes: number;\n}\n\nexport interface ToolInvocationRuntimeInput {\n toolName: string;\n tool: Pick<RawToolDefinition, \"handler\">;\n context: ResolvedToolContext;\n query: unknown;\n signal?: AbortSignal;\n traceEmitter?: TraceEmitter;\n timeoutMs?: number;\n maxOutputBytes?: number;\n}\n\nconst FAST_TIMEOUT_MS = 10_000;\nconst BOUNDED_SLOW_TIMEOUT_MS = 45_000;\nconst ASYNC_CANDIDATE_TIMEOUT_MS = 50_000;\nconst DEFAULT_MAX_OUTPUT_BYTES = 1_000_000;\n\nconst FAST_TOOLS = new Set([\n \"record_onboarding_privacy_consent\",\n \"create_intent_index\",\n \"delete_intent_index\",\n \"search_intents\",\n \"read_networks\",\n \"update_network\",\n \"create_network\",\n \"delete_network\",\n \"create_network_membership\",\n \"delete_network_membership\",\n \"confirm_opportunity_delivery\",\n \"read_docs\",\n \"get_discovery_run\",\n \"cancel_discovery_run\",\n \"get_profile_run\",\n \"cancel_profile_run\",\n \"remove_contact\",\n \"register_agent\",\n \"list_agents\",\n \"update_agent\",\n \"delete_agent\",\n \"grant_agent_permission\",\n \"revoke_agent_permission\",\n \"retract_premise\",\n]);\n\nconst ASYNC_CANDIDATE_TOOLS = new Set([\n // Canonical *_user_context names (IND-371)\n \"read_user_contexts\",\n \"preview_user_context\",\n \"create_user_context\",\n \"update_user_context\",\n // Deprecated *_user_profile aliases (kept until IND-373 retires them)\n \"read_user_profiles\",\n \"preview_user_profile\",\n \"create_user_profile\",\n \"update_user_profile\",\n \"create_intent\",\n \"update_intent\",\n \"discover_opportunities\",\n \"scrape_url\",\n \"import_gmail_contacts\",\n \"import_contacts\",\n \"respond_to_negotiation\",\n \"create_premise\",\n \"update_premise\",\n]);\n\nfunction parsePositiveIntEnv(name: string, fallback: number): number {\n const raw = process.env[name];\n if (!raw) return fallback;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed > 0 && parsed <= Number.MAX_SAFE_INTEGER\n ? parsed\n : fallback;\n}\n\nfunction toolNameEnv(toolName: string): string {\n return `MCP_TOOL_TIMEOUT_${toolName.toUpperCase().replace(/[^A-Z0-9]+/g, \"_\")}_MS`;\n}\n\nfunction toolNameOutputEnv(toolName: string): string {\n return `MCP_TOOL_MAX_OUTPUT_${toolName.toUpperCase().replace(/[^A-Z0-9]+/g, \"_\")}_BYTES`;\n}\n\nexport function getToolTimeoutPolicy(toolName: string): ToolTimeoutPolicy {\n const classification: ToolTimeoutClass = FAST_TOOLS.has(toolName)\n ? \"fast\"\n : ASYNC_CANDIDATE_TOOLS.has(toolName)\n ? \"async_candidate\"\n : \"bounded_slow\";\n\n let classDefault: number;\n switch (classification) {\n case \"fast\":\n classDefault = parsePositiveIntEnv(\"MCP_TOOL_TIMEOUT_FAST_MS\", FAST_TIMEOUT_MS);\n break;\n case \"async_candidate\":\n classDefault = parsePositiveIntEnv(\"MCP_TOOL_TIMEOUT_ASYNC_CANDIDATE_MS\", ASYNC_CANDIDATE_TIMEOUT_MS);\n break;\n default:\n classDefault = parsePositiveIntEnv(\"MCP_TOOL_TIMEOUT_BOUNDED_SLOW_MS\", BOUNDED_SLOW_TIMEOUT_MS);\n }\n\n const defaultMaxOutputBytes = parsePositiveIntEnv(\"MCP_TOOL_MAX_OUTPUT_BYTES\", DEFAULT_MAX_OUTPUT_BYTES);\n\n return {\n class: classification,\n timeoutMs: parsePositiveIntEnv(toolNameEnv(toolName), classDefault),\n maxOutputBytes: parsePositiveIntEnv(toolNameOutputEnv(toolName), defaultMaxOutputBytes),\n };\n}\n\nexport class ToolRuntimeError extends Error {\n constructor(\n public readonly code: ToolRuntimeErrorCode,\n message: string,\n public readonly toolName: string,\n public readonly policy: ToolTimeoutPolicy,\n ) {\n super(message);\n this.name = \"ToolRuntimeError\";\n }\n}\n\n/** Builds the typed timeout/cancellation error shared by the abort race and catch paths. */\nfunction makeAbortError(timedOut: boolean, toolName: string, policy: ToolTimeoutPolicy): ToolRuntimeError {\n const code: ToolRuntimeErrorCode = timedOut ? \"TOOL_TIMEOUT\" : \"TOOL_CANCELLED\";\n return new ToolRuntimeError(\n code,\n code === \"TOOL_TIMEOUT\"\n ? `Tool ${toolName} timed out after ${policy.timeoutMs}ms.`\n : `Tool ${toolName} was cancelled before it completed.`,\n toolName,\n policy,\n );\n}\n\nfunction combineSignals(signals: Array<AbortSignal | undefined>): {\n signal: AbortSignal;\n abort: (reason?: unknown) => void;\n cleanup: () => void;\n} {\n const controller = new AbortController();\n const listeners: Array<() => void> = [];\n\n for (const source of signals) {\n if (!source) continue;\n if (source.aborted) {\n controller.abort(source.reason);\n break;\n }\n const onAbort = () => {\n if (!controller.signal.aborted) controller.abort(source.reason);\n };\n source.addEventListener(\"abort\", onAbort, { once: true });\n listeners.push(() => source.removeEventListener(\"abort\", onAbort));\n }\n\n return {\n signal: controller.signal,\n abort: (reason?: unknown) => {\n if (!controller.signal.aborted) controller.abort(reason);\n },\n cleanup: () => {\n for (const cleanup of listeners) cleanup();\n },\n };\n}\n\nexport async function invokeToolRuntime(input: ToolInvocationRuntimeInput): Promise<string> {\n return timed(`ToolRuntime.${input.toolName}`, () => invokeToolRuntimeInner(input));\n}\n\nasync function invokeToolRuntimeInner(input: ToolInvocationRuntimeInput): Promise<string> {\n const basePolicy = getToolTimeoutPolicy(input.toolName);\n const policy = {\n ...basePolicy,\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n ...(input.maxOutputBytes !== undefined ? { maxOutputBytes: input.maxOutputBytes } : {}),\n };\n const inherited = requestContext.getStore();\n const combined = combineSignals([input.signal, inherited?.abortSignal]);\n let timedOut = false;\n const timer = setTimeout(() => {\n timedOut = true;\n combined.abort(new Error(`Tool ${input.toolName} timed out after ${policy.timeoutMs}ms`));\n }, policy.timeoutMs);\n\n let removeAbortListener = () => {};\n const abortPromise = new Promise<never>((_, reject) => {\n const onAbort = () => {\n reject(makeAbortError(timedOut, input.toolName, policy));\n };\n combined.signal.addEventListener(\"abort\", onAbort, { once: true });\n removeAbortListener = () => combined.signal.removeEventListener(\"abort\", onAbort);\n });\n\n try {\n const run = () => input.tool.handler({ context: input.context, query: input.query });\n const toolPromise = requestContext.run(\n {\n ...inherited,\n abortSignal: combined.signal,\n traceEmitter: input.traceEmitter ?? inherited?.traceEmitter,\n },\n run,\n );\n const result = await Promise.race([toolPromise, abortPromise]);\n const outputBytes = new TextEncoder().encode(result).byteLength;\n if (outputBytes > policy.maxOutputBytes) {\n throw new ToolRuntimeError(\n \"TOOL_OUTPUT_TOO_LARGE\",\n `Tool ${input.toolName} returned ${outputBytes} bytes, exceeding the ${policy.maxOutputBytes} byte limit.`,\n input.toolName,\n policy,\n );\n }\n return result;\n } catch (err) {\n if (err instanceof ToolRuntimeError) throw err;\n if (combined.signal.aborted) {\n throw makeAbortError(timedOut, input.toolName, policy);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n removeAbortListener();\n combined.cleanup();\n }\n}\n\nexport function toolRuntimeErrorToResult(err: unknown): string | null {\n if (!(err instanceof ToolRuntimeError)) return null;\n return JSON.stringify({\n success: false,\n code: err.code,\n error: err.message,\n data: {\n tool: err.toolName,\n timeoutClass: err.policy.class,\n timeoutMs: err.policy.timeoutMs,\n maxOutputBytes: err.policy.maxOutputBytes,\n },\n });\n}\n"]}
|
|
@@ -9,7 +9,7 @@ export function createUtilityTools(defineTool, deps) {
|
|
|
9
9
|
"Returns the page's text content (up to 10,000 characters) for use in subsequent tool calls.\n\n" +
|
|
10
10
|
"**When to use:**\n" +
|
|
11
11
|
"- Before create_intent: when the user shares a URL and wants to create an intent from it. Scrape first, then synthesize into a description.\n" +
|
|
12
|
-
"- Before
|
|
12
|
+
"- Before create_user_context or update_user_context: when the user shares a profile URL to update their profile from.\n" +
|
|
13
13
|
"- When the user asks about content at a URL.\n\n" +
|
|
14
14
|
"**URL format:** Bare domains work fine (e.g. 'github.com/user/repo') — protocol (https://) is added automatically.\n\n" +
|
|
15
15
|
"**Returns:** `{ url, contentLength, content }`. Content is truncated at 10,000 chars. " +
|
|
@@ -145,8 +145,8 @@ Profiles are the user's identity on the platform, used for semantic matching in
|
|
|
145
145
|
- Mirror: self-description of the person
|
|
146
146
|
- Reciprocal: what this person would look for in others
|
|
147
147
|
- Neighborhood: related community context
|
|
148
|
-
- **Onboarding flow**:
|
|
149
|
-
- **Updates**: Use
|
|
148
|
+
- **Onboarding flow**: create_user_context() → preview → create_user_context(confirm=true) → complete_onboarding()
|
|
149
|
+
- **Updates**: Use update_user_context for targeted changes, create_user_context for full regeneration.
|
|
150
150
|
|
|
151
151
|
### Profile Best Practices
|
|
152
152
|
- Richer profiles produce better opportunity matches
|
|
@@ -188,7 +188,7 @@ Discovery is the process of finding meaningful connections between users based o
|
|
|
188
188
|
workflows: `## Common Tool Workflows
|
|
189
189
|
|
|
190
190
|
### New User Setup
|
|
191
|
-
1.
|
|
191
|
+
1. create_user_context(linkedinUrl/githubUrl) → generate profile from social data
|
|
192
192
|
2. complete_onboarding() → unlock full access
|
|
193
193
|
3. read_networks() → see available communities
|
|
194
194
|
4. create_network_membership(networkId) → join a community
|
|
@@ -202,7 +202,7 @@ Discovery is the process of finding meaningful connections between users based o
|
|
|
202
202
|
|
|
203
203
|
### Making an Introduction
|
|
204
204
|
1. read_network_memberships(networkId) → find members in shared community
|
|
205
|
-
2.
|
|
205
|
+
2. read_user_contexts(userId) → get profiles of both parties
|
|
206
206
|
3. read_intents(networkId, userId) → get intents of both parties
|
|
207
207
|
4. discover_opportunities(partyUserIds=[id1,id2], entities=[...], hint="reason") → create introduction
|
|
208
208
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utility.tools.js","sourceRoot":"/","sources":["shared/agent/utility.tools.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\n\nimport { requestContext } from \"../observability/request-context.js\";\n\nimport type { DefineTool, ToolDeps } from \"./tool.helpers.js\";\nimport { success, error, normalizeUrl } from \"./tool.helpers.js\";\n\nexport function createUtilityTools(defineTool: DefineTool, deps: ToolDeps) {\n const { scraper } = deps;\n\n const scrapeUrl = defineTool({\n name: \"scrape_url\",\n description:\n \"Extracts text content from a web URL — articles, LinkedIn/GitHub profiles, documentation, project pages, etc. \" +\n \"Returns the page's text content (up to 10,000 characters) for use in subsequent tool calls.\\n\\n\" +\n \"**When to use:**\\n\" +\n \"- Before create_intent: when the user shares a URL and wants to create an intent from it. Scrape first, then synthesize into a description.\\n\" +\n \"- Before create_user_profile or update_user_profile: when the user shares a profile URL to update their profile from.\\n\" +\n \"- When the user asks about content at a URL.\\n\\n\" +\n \"**URL format:** Bare domains work fine (e.g. 'github.com/user/repo') — protocol (https://) is added automatically.\\n\\n\" +\n \"**Returns:** `{ url, contentLength, content }`. Content is truncated at 10,000 chars. \" +\n \"Returns an error if the URL is unreachable, requires login, or has no extractable text.\",\n querySchema: z.object({\n url: z.string().describe(\"The URL to extract content from. Protocol is optional — 'github.com/user/repo', 'linkedin.com/in/name', and 'https://example.com' all work.\"),\n objective: z.string().optional().describe(\"Why you're scraping — guides content extraction for better results. Examples: 'User wants to create an intent from this project page', 'User wants to update their profile from this LinkedIn page', 'Extract key information about this company'. Omit for generic text extraction.\"),\n }),\n handler: async ({ context: _context, query }) => {\n const normalizedUrl = normalizeUrl(query.url);\n if (!normalizedUrl) {\n return error(\"Invalid URL format. Please provide a valid URL (e.g. 'github.com/user/repo' or 'https://example.com').\");\n }\n\n const content = await scraper.extractUrlContent(normalizedUrl, {\n objective: query.objective?.trim() || undefined,\n signal: requestContext.getStore()?.abortSignal,\n });\n\n if (!content) {\n return error(\"Couldn't extract content from that URL. It may be blocked, require login, or have no extractable text.\");\n }\n\n const truncatedContent = content.length > 10000\n ? content.substring(0, 10000) + \"\\n\\n[Content truncated...]\"\n : content;\n\n return success({\n url: normalizedUrl,\n contentLength: content.length,\n content: truncatedContent,\n });\n },\n });\n\n const readDocs = defineTool({\n name: \"read_docs\",\n description:\n \"Returns comprehensive documentation about the Index Network protocol — entity model, workflows, tool usage guidance, and domain concepts. \" +\n \"This is the primary way for an external agent to bootstrap understanding of the system.\\n\\n\" +\n \"**When to use:** Call this FIRST when starting a new session. MCP agents MUST call read_docs(topic='mcp_agent_guide') at the start of every conversation to learn proper output formatting and workflow rules.\\n\" +\n \"Also call when you need to understand:\\n\" +\n \"- What entities exist and how they relate (intents, indexes, opportunities, profiles, contacts)\\n\" +\n \"- The discovery workflow (how intents become opportunities)\\n\" +\n \"- Which tools to call in what order for common tasks\\n\" +\n \"- Authentication and API patterns\\n\\n\" +\n \"**Returns:** Markdown documentation. Pass `topic` to get a specific section, or omit for the full reference.\\n\\n\" +\n \"**Available topics:** 'entities', 'intents', 'opportunities', 'indexes', 'profiles', 'contacts', 'discovery', 'workflows', 'authentication', 'mcp_agent_guide'\",\n querySchema: z.object({\n topic: z.string().optional().describe(\"Narrow to a specific topic: 'entities', 'intents', 'opportunities', 'indexes', 'profiles', 'contacts', 'discovery', 'workflows', or 'authentication'. Omit to get the full documentation.\"),\n }),\n handler: async ({ context: _context, query }) => {\n const topic = query.topic?.trim().toLowerCase();\n\n const sections: Record<string, string> = {\n entities: `## Entity Model & Relationships\n\n- **Users**: People on the platform. Authenticated via API key (X-API-Key header) for MCP/external agents, or session-based (Better Auth) for the web app.\n- **Profiles**: A user's identity — name, bio, skills, interests, location, social links. Generated from account data or social URLs via enrichment. One profile per user.\n- **Indexes** (also called \"networks\"): Communities or groups where members share intents and discover opportunities. Each has a title, optional prompt (purpose description), join policy (anyone or invite_only), and an owner. The user's **personal index** (isPersonal=true) stores their contacts.\n- **Index Members**: Junction between Users and Indexes. Tracks permissions (owner, member, contact), join date, auto-assign setting, and optional member prompt.\n- **Intents**: Signals of interest/need — what a user is looking for (e.g. \"Looking for a React developer in Berlin\"). Each has a description (payload), summary, confidence score (0-1), inferenceType (explicit/implicit), source tracking, and vector embedding.\n- **IntentNetworks**: Many-to-many junction between Intents and Indexes. An intent can be in multiple indexes. Has a relevancyScore (0-1) indicating how well the intent fits the index's purpose.\n- **Opportunities**: Discovered connections between users based on complementary intents within shared indexes. Have actors with roles (introducer, party), status lifecycle, match reasoning, confidence score, and presentation data.\n- **Contacts**: People in a user's personal network, stored as index members with 'contact' permission on the personal index. Can be real users or ghost users (placeholder accounts enriched from public data).\n- **Ghost Users**: Placeholder accounts created for contacts who aren't on the platform yet. Enriched with public profile data (LinkedIn, GitHub) and participate in opportunity matching.\n\n### Key Relationships\n- Users → Profiles (1:1)\n- Users → Indexes (many:many via Index Members)\n- Users → Intents (1:many, user owns intents)\n- Intents → Indexes (many:many via IntentNetworks with relevancyScore)\n- Opportunities → Users (many:many via actors with roles)\n- Opportunities → Indexes (scoped to shared index context)\n- Contacts → Personal Index (stored as members with 'contact' permission)`,\n\n intents: `## Intent Lifecycle\n\nIntents are the core unit of discovery — they represent what users are seeking and drive semantic matching.\n\n1. **Creation** (create_intent): User describes what they're looking for. The system runs inference (extracting structured intents from free text) and verification (checking specificity, speech-act type). Returns a proposal for user approval.\n2. **Confidence & Classification**: Each intent gets a confidence score (0-1), inferenceType (explicit = user stated directly, implicit = system inferred), and speech act classification (commissive, directive, assertive).\n3. **Index Assignment**: After creation, the intent is automatically evaluated against all indexes the user belongs to. The index's prompt is used as criteria. Matching indexes get linked via IntentNetworks with a relevancyScore (0-1).\n4. **Discovery Trigger**: Creating an intent triggers background opportunity detection — the system searches for other users in shared indexes whose intents complement this one.\n5. **Source Tracking**: Intents track their origin via sourceType (file, integration, link, discovery_form, enrichment) and sourceId.\n6. **Update** (update_intent): Re-processes through inference/verification, recalculates embeddings and index assignments.\n7. **Archive** (delete_intent): Soft-deletes the intent. It stops participating in discovery but is not permanently removed.\n\n### Intent Best Practices\n- Be specific: \"Looking for a senior React developer for a 3-month contract in Berlin\" > \"Need a developer\"\n- One intent per need: don't combine multiple requests into one intent\n- Update rather than delete+create to preserve history`,\n\n opportunities: `## Opportunity Lifecycle\n\nOpportunities represent discovered connections between users — potential matches worth pursuing.\n\n1. **Detection** (discover_opportunities): The opportunity graph finds users whose intents semantically complement each other within shared indexes. Uses HyDE embeddings for retrieval and an LLM evaluator for scoring.\n2. **Roles**: Each opportunity assigns roles to actors:\n - **introducer**: The person who triggered the introduction (may be the system or another user)\n - **party**: The people being connected (typically 2)\n3. **Status Flow**: draft → pending → accepted/rejected/expired\n - **draft**: Created but not sent. Only the creator/introducer sees it.\n - **pending**: Sent to the other party. They're notified and can respond.\n - **accepted**: Both parties agreed to connect.\n - **rejected**: One party declined.\n - **expired**: Timed out without response.\n4. **Creation Modes**:\n - **Discovery**: Automatic — system finds matches based on intent overlap (discover_opportunities with searchQuery)\n - **Introduction**: Manual — a user introduces two specific people (discover_opportunities with partyUserIds + entities)\n - **Direct**: One-to-one — connect with a specific person (discover_opportunities with targetUserId)\n5. **Presentation**: Each opportunity includes personalized match reasoning, confidence score, and suggested next action.\n\n### Opportunity Workflow\n1. discover_opportunities(searchQuery=\"AI engineers\") → returns draft opportunity cards\n2. update_opportunity(opportunityId, status=\"pending\") → sends to other party\n3. Other party sees opportunity → calls update_opportunity(status=\"accepted\" or \"rejected\")`,\n\n indexes: `## Index Mechanics\n\nIndexes (also called \"networks\") are communities where members share what they're looking for and the system discovers connections between them.\n\n- **Purpose prompt**: Each index has an optional prompt describing its purpose (e.g. \"AI/ML co-founders in Berlin\"). This prompt is used by the intent indexer to evaluate whether an intent belongs in this community. Indexes without prompts accept all intents (relevancyScore defaults to 1.0).\n- **Join policy**: \"anyone\" (open — any user can self-join) or \"invite_only\" (only the owner can add members).\n- **Personal index**: Each user has exactly one personal index (isPersonal=true) created on registration. It stores their contacts. Cannot be deleted, renamed, or listed publicly.\n- **Membership**: Members can see all intents in the index. The **auto-assign** setting on a membership means new intents by that user are automatically evaluated against the index.\n- **Owner permissions**: Index owners can update settings (title, prompt, joinPolicy), add/remove members, and delete the index (if sole member).\n- **Discovery scope**: Opportunities are discovered within index boundaries — the system matches intents of members who share at least one index.\n\n### Index Workflow\n1. create_network(title, prompt) → creates new community, you become owner\n2. create_network_membership(networkId, userId) → invite members\n3. Members create intents → auto-assigned to the index based on prompt\n4. discover_opportunities(networkId) → discover matches within this community`,\n\n profiles: `## Profile System\n\nProfiles are the user's identity on the platform, used for semantic matching in opportunity discovery.\n\n- **Structure**: name, bio, location, skills[], interests[], social links (LinkedIn, GitHub, Twitter, websites)\n- **Generation**: Auto-generated from account data (name, email, social links) via web enrichment. Can also be created from explicit user input (bioOrDescription).\n- **Enrichment**: The system scrapes public profiles (LinkedIn, GitHub, Twitter) to build a rich identity with skills, interests, and narrative context.\n- **Embeddings**: HyDE (Hypothetical Document Embedding) generates synthetic documents for semantic matching:\n - Mirror: self-description of the person\n - Reciprocal: what this person would look for in others\n - Neighborhood: related community context\n- **Onboarding flow**: create_user_profile() → preview → create_user_profile(confirm=true) → complete_onboarding()\n- **Updates**: Use update_user_profile for targeted changes, create_user_profile for full regeneration.\n\n### Profile Best Practices\n- Richer profiles produce better opportunity matches\n- Social links enable enrichment — encourage users to add LinkedIn/GitHub\n- Profiles are recalculated when updated, which may surface new matches`,\n\n contacts: `## Contact Management\n\nContacts are people in a user's personal network, stored as members of their personal index with 'contact' permission.\n\n- **Adding contacts**: Via import_contacts (bulk), add_contact (single email), or import_gmail_contacts (Google integration).\n- **Ghost users**: When a contact email doesn't match an existing account, a ghost user is created. Ghost users are enriched with public profile data and participate in opportunity matching — they can be discovered even before joining the platform.\n- **Personal index scope**: Pass the personal index networkId to discover_opportunities to scope discovery to just the user's contacts.\n- **Contact data**: Each contact has userId, name, email, avatar, and isGhost flag.\n\n### Contact Workflow\n1. import_contacts or import_gmail_contacts → bulk add to network\n2. list_contacts → view all contacts with userId\n3. discover_opportunities(networkId=personalIndexId) → find matches among contacts\n4. add_contact(email) → add individual contact\n5. remove_contact(contactUserId) → remove from network`,\n\n discovery: `## Discovery Mechanics\n\nDiscovery is the process of finding meaningful connections between users based on their intents and profiles.\n\n### How Discovery Works\n1. **Trigger**: Runs automatically when an intent is created, or explicitly when discover_opportunities is called.\n2. **Pipeline**: Preparation (gather user context) → Scope (determine which indexes to search) → Candidate retrieval (semantic matching via HyDE embeddings) → Evaluation (LLM scores relevance and complementarity) → Ranking → Persist as opportunities.\n3. **Semantic matching**: Uses HyDE (Hypothetical Document Embeddings) to find candidate intents that complement the source. This goes beyond keyword matching — it understands conceptual relationships.\n4. **Evaluation**: An LLM evaluator agent scores each candidate match on relevance, complementarity, and actionability. Low-scoring matches are filtered out.\n5. **Results**: Persisted as draft opportunities with roles, reasoning, and confidence scores.\n6. **Background processing**: After intent creation, a queue job continues looking for matches asynchronously.\n7. **Pagination**: Large result sets are paginated. Use continueFrom with the discoveryId to evaluate more candidates.\n\n### Discovery Best Practices\n- More specific intents produce more relevant matches\n- Richer profiles improve matching quality\n- Scope to a specific index (networkId) for more targeted results\n- After discovery returns no results, suggest creating an intent to attract future matches`,\n\n workflows: `## Common Tool Workflows\n\n### New User Setup\n1. create_user_profile(linkedinUrl/githubUrl) → generate profile from social data\n2. complete_onboarding() → unlock full access\n3. read_networks() → see available communities\n4. create_network_membership(networkId) → join a community\n5. create_intent(description) → post what you're looking for\n6. discover_opportunities(searchQuery) → find matches\n\n### Finding Connections\n1. read_networks() → list user's communities (get networkId)\n2. discover_opportunities(searchQuery, networkId) → discover matches\n3. Review opportunity cards → update_opportunity(opportunityId, status=\"pending\") to send\n\n### Making an Introduction\n1. read_network_memberships(networkId) → find members in shared community\n2. read_user_profiles(userId) → get profiles of both parties\n3. read_intents(networkId, userId) → get intents of both parties\n4. discover_opportunities(partyUserIds=[id1,id2], entities=[...], hint=\"reason\") → create introduction\n\n### Managing Contacts\n1. import_gmail_contacts() or import_contacts([...]) → add contacts\n2. list_contacts() → view network\n3. discover_opportunities(networkId=personalIndexId) → find matches among contacts\n\n### Creating a Community\n1. create_network(title, prompt) → create index\n2. create_network_membership(networkId, userId) → invite members\n3. Members create intents → auto-indexed\n4. discover_opportunities(networkId) → discover connections within community`,\n\n authentication: `## Authentication & API Access\n\n### For External AI Agents (MCP)\n- Authenticate via **X-API-Key** header with a valid API key\n- The API key is tied to a specific user account\n- All operations execute in the context of the authenticated user\n- Base URL: protocol.index.network/mcp\n\n### Key Constraints\n- Users can only read their own intents globally, or intents in indexes they belong to\n- Users can only read profiles of people in shared indexes\n- Index-scoped operations are restricted to that index\n- Personal indexes cannot be deleted or renamed\n- Only index owners can update settings, add/remove members (for invite_only indexes)\n\n### Rate Limits & Best Practices\n- Avoid unnecessary read_intents/read_networks calls — cache results within a conversation\n- Use pagination (limit/page) for large result sets\n- Call read_docs once at the start to understand the domain`,\n\n mcp_agent_guide: `## MCP Agent Integration Guide\n\n**IMPORTANT: Read this section if you are an AI agent accessing Index Network via MCP tools.**\n\n### Output Formatting\n- Tool results often contain structured JSON data (proposals, opportunities, cards). **Do NOT dump raw JSON to the user.** Parse the JSON and present information in natural language with clear formatting.\n- Some tool results contain interactive card markup (code blocks with \\`intent_proposal\\`, \\`opportunity_card\\` language tags). These are designed for the Index Network web UI. **As an MCP agent, ignore card markup.** Instead, extract the meaningful data from the JSON and present it conversationally.\n- When presenting opportunities or intents, use bullet points or short paragraphs — not raw JSON objects.\n\n### Intent Creation Workflow\n- **Always pass \\`autoApprove: true\\` when calling \\`create_intent\\`.** This persists intents directly without returning proposal cards that require manual UI approval.\n- The tool will return a list of created intents with their descriptions and confidence scores. Present these in natural language.\n- Do not tell the user to \"click on cards\" or \"approve above\" — there is no UI. Intents are created immediately with autoApprove.\n- After creating intents, proactively suggest or run discovery to find matches.\n\n### Discovery Workflow\n- After creating intents, proactively suggest running discovery: \\`discover_opportunities(searchQuery=...)\\`\n- Present discovered opportunities in natural language with the counterpart's name, match reasoning, and suggested next steps.\n- Do not reference \"cards\", \"panels\", or any web UI elements.\n\n### General MCP Agent Rules\n- You are operating via API tools, not a web interface. Never reference clicking, scrolling, cards, panels, or any visual UI elements.\n- Be proactive: if a logical next step exists (e.g., running discovery after creating intents), suggest or execute it.\n- Use \\`list_opportunities\\` to check existing matches, \\`list_negotiations\\` for ongoing negotiations.\n- Use \\`read_networks\\` to understand which communities the user belongs to before scoping operations.\n- When errors occur, provide clear technical context rather than vague \"backend issue\" messages.`,\n };\n\n if (topic) {\n const matched = Object.entries(sections).find(([key]) => key.includes(topic) || topic.includes(key));\n if (matched) {\n return success({ topic: matched[0], content: matched[1] });\n }\n // If topic not found, return all\n }\n\n const fullDoc = Object.values(sections).join(\"\\n\\n\");\n return success({ content: fullDoc });\n },\n });\n\n return [scrapeUrl, readDocs] as const;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"utility.tools.js","sourceRoot":"/","sources":["shared/agent/utility.tools.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\n\nimport { requestContext } from \"../observability/request-context.js\";\n\nimport type { DefineTool, ToolDeps } from \"./tool.helpers.js\";\nimport { success, error, normalizeUrl } from \"./tool.helpers.js\";\n\nexport function createUtilityTools(defineTool: DefineTool, deps: ToolDeps) {\n const { scraper } = deps;\n\n const scrapeUrl = defineTool({\n name: \"scrape_url\",\n description:\n \"Extracts text content from a web URL — articles, LinkedIn/GitHub profiles, documentation, project pages, etc. \" +\n \"Returns the page's text content (up to 10,000 characters) for use in subsequent tool calls.\\n\\n\" +\n \"**When to use:**\\n\" +\n \"- Before create_intent: when the user shares a URL and wants to create an intent from it. Scrape first, then synthesize into a description.\\n\" +\n \"- Before create_user_context or update_user_context: when the user shares a profile URL to update their profile from.\\n\" +\n \"- When the user asks about content at a URL.\\n\\n\" +\n \"**URL format:** Bare domains work fine (e.g. 'github.com/user/repo') — protocol (https://) is added automatically.\\n\\n\" +\n \"**Returns:** `{ url, contentLength, content }`. Content is truncated at 10,000 chars. \" +\n \"Returns an error if the URL is unreachable, requires login, or has no extractable text.\",\n querySchema: z.object({\n url: z.string().describe(\"The URL to extract content from. Protocol is optional — 'github.com/user/repo', 'linkedin.com/in/name', and 'https://example.com' all work.\"),\n objective: z.string().optional().describe(\"Why you're scraping — guides content extraction for better results. Examples: 'User wants to create an intent from this project page', 'User wants to update their profile from this LinkedIn page', 'Extract key information about this company'. Omit for generic text extraction.\"),\n }),\n handler: async ({ context: _context, query }) => {\n const normalizedUrl = normalizeUrl(query.url);\n if (!normalizedUrl) {\n return error(\"Invalid URL format. Please provide a valid URL (e.g. 'github.com/user/repo' or 'https://example.com').\");\n }\n\n const content = await scraper.extractUrlContent(normalizedUrl, {\n objective: query.objective?.trim() || undefined,\n signal: requestContext.getStore()?.abortSignal,\n });\n\n if (!content) {\n return error(\"Couldn't extract content from that URL. It may be blocked, require login, or have no extractable text.\");\n }\n\n const truncatedContent = content.length > 10000\n ? content.substring(0, 10000) + \"\\n\\n[Content truncated...]\"\n : content;\n\n return success({\n url: normalizedUrl,\n contentLength: content.length,\n content: truncatedContent,\n });\n },\n });\n\n const readDocs = defineTool({\n name: \"read_docs\",\n description:\n \"Returns comprehensive documentation about the Index Network protocol — entity model, workflows, tool usage guidance, and domain concepts. \" +\n \"This is the primary way for an external agent to bootstrap understanding of the system.\\n\\n\" +\n \"**When to use:** Call this FIRST when starting a new session. MCP agents MUST call read_docs(topic='mcp_agent_guide') at the start of every conversation to learn proper output formatting and workflow rules.\\n\" +\n \"Also call when you need to understand:\\n\" +\n \"- What entities exist and how they relate (intents, indexes, opportunities, profiles, contacts)\\n\" +\n \"- The discovery workflow (how intents become opportunities)\\n\" +\n \"- Which tools to call in what order for common tasks\\n\" +\n \"- Authentication and API patterns\\n\\n\" +\n \"**Returns:** Markdown documentation. Pass `topic` to get a specific section, or omit for the full reference.\\n\\n\" +\n \"**Available topics:** 'entities', 'intents', 'opportunities', 'indexes', 'profiles', 'contacts', 'discovery', 'workflows', 'authentication', 'mcp_agent_guide'\",\n querySchema: z.object({\n topic: z.string().optional().describe(\"Narrow to a specific topic: 'entities', 'intents', 'opportunities', 'indexes', 'profiles', 'contacts', 'discovery', 'workflows', or 'authentication'. Omit to get the full documentation.\"),\n }),\n handler: async ({ context: _context, query }) => {\n const topic = query.topic?.trim().toLowerCase();\n\n const sections: Record<string, string> = {\n entities: `## Entity Model & Relationships\n\n- **Users**: People on the platform. Authenticated via API key (X-API-Key header) for MCP/external agents, or session-based (Better Auth) for the web app.\n- **Profiles**: A user's identity — name, bio, skills, interests, location, social links. Generated from account data or social URLs via enrichment. One profile per user.\n- **Indexes** (also called \"networks\"): Communities or groups where members share intents and discover opportunities. Each has a title, optional prompt (purpose description), join policy (anyone or invite_only), and an owner. The user's **personal index** (isPersonal=true) stores their contacts.\n- **Index Members**: Junction between Users and Indexes. Tracks permissions (owner, member, contact), join date, auto-assign setting, and optional member prompt.\n- **Intents**: Signals of interest/need — what a user is looking for (e.g. \"Looking for a React developer in Berlin\"). Each has a description (payload), summary, confidence score (0-1), inferenceType (explicit/implicit), source tracking, and vector embedding.\n- **IntentNetworks**: Many-to-many junction between Intents and Indexes. An intent can be in multiple indexes. Has a relevancyScore (0-1) indicating how well the intent fits the index's purpose.\n- **Opportunities**: Discovered connections between users based on complementary intents within shared indexes. Have actors with roles (introducer, party), status lifecycle, match reasoning, confidence score, and presentation data.\n- **Contacts**: People in a user's personal network, stored as index members with 'contact' permission on the personal index. Can be real users or ghost users (placeholder accounts enriched from public data).\n- **Ghost Users**: Placeholder accounts created for contacts who aren't on the platform yet. Enriched with public profile data (LinkedIn, GitHub) and participate in opportunity matching.\n\n### Key Relationships\n- Users → Profiles (1:1)\n- Users → Indexes (many:many via Index Members)\n- Users → Intents (1:many, user owns intents)\n- Intents → Indexes (many:many via IntentNetworks with relevancyScore)\n- Opportunities → Users (many:many via actors with roles)\n- Opportunities → Indexes (scoped to shared index context)\n- Contacts → Personal Index (stored as members with 'contact' permission)`,\n\n intents: `## Intent Lifecycle\n\nIntents are the core unit of discovery — they represent what users are seeking and drive semantic matching.\n\n1. **Creation** (create_intent): User describes what they're looking for. The system runs inference (extracting structured intents from free text) and verification (checking specificity, speech-act type). Returns a proposal for user approval.\n2. **Confidence & Classification**: Each intent gets a confidence score (0-1), inferenceType (explicit = user stated directly, implicit = system inferred), and speech act classification (commissive, directive, assertive).\n3. **Index Assignment**: After creation, the intent is automatically evaluated against all indexes the user belongs to. The index's prompt is used as criteria. Matching indexes get linked via IntentNetworks with a relevancyScore (0-1).\n4. **Discovery Trigger**: Creating an intent triggers background opportunity detection — the system searches for other users in shared indexes whose intents complement this one.\n5. **Source Tracking**: Intents track their origin via sourceType (file, integration, link, discovery_form, enrichment) and sourceId.\n6. **Update** (update_intent): Re-processes through inference/verification, recalculates embeddings and index assignments.\n7. **Archive** (delete_intent): Soft-deletes the intent. It stops participating in discovery but is not permanently removed.\n\n### Intent Best Practices\n- Be specific: \"Looking for a senior React developer for a 3-month contract in Berlin\" > \"Need a developer\"\n- One intent per need: don't combine multiple requests into one intent\n- Update rather than delete+create to preserve history`,\n\n opportunities: `## Opportunity Lifecycle\n\nOpportunities represent discovered connections between users — potential matches worth pursuing.\n\n1. **Detection** (discover_opportunities): The opportunity graph finds users whose intents semantically complement each other within shared indexes. Uses HyDE embeddings for retrieval and an LLM evaluator for scoring.\n2. **Roles**: Each opportunity assigns roles to actors:\n - **introducer**: The person who triggered the introduction (may be the system or another user)\n - **party**: The people being connected (typically 2)\n3. **Status Flow**: draft → pending → accepted/rejected/expired\n - **draft**: Created but not sent. Only the creator/introducer sees it.\n - **pending**: Sent to the other party. They're notified and can respond.\n - **accepted**: Both parties agreed to connect.\n - **rejected**: One party declined.\n - **expired**: Timed out without response.\n4. **Creation Modes**:\n - **Discovery**: Automatic — system finds matches based on intent overlap (discover_opportunities with searchQuery)\n - **Introduction**: Manual — a user introduces two specific people (discover_opportunities with partyUserIds + entities)\n - **Direct**: One-to-one — connect with a specific person (discover_opportunities with targetUserId)\n5. **Presentation**: Each opportunity includes personalized match reasoning, confidence score, and suggested next action.\n\n### Opportunity Workflow\n1. discover_opportunities(searchQuery=\"AI engineers\") → returns draft opportunity cards\n2. update_opportunity(opportunityId, status=\"pending\") → sends to other party\n3. Other party sees opportunity → calls update_opportunity(status=\"accepted\" or \"rejected\")`,\n\n indexes: `## Index Mechanics\n\nIndexes (also called \"networks\") are communities where members share what they're looking for and the system discovers connections between them.\n\n- **Purpose prompt**: Each index has an optional prompt describing its purpose (e.g. \"AI/ML co-founders in Berlin\"). This prompt is used by the intent indexer to evaluate whether an intent belongs in this community. Indexes without prompts accept all intents (relevancyScore defaults to 1.0).\n- **Join policy**: \"anyone\" (open — any user can self-join) or \"invite_only\" (only the owner can add members).\n- **Personal index**: Each user has exactly one personal index (isPersonal=true) created on registration. It stores their contacts. Cannot be deleted, renamed, or listed publicly.\n- **Membership**: Members can see all intents in the index. The **auto-assign** setting on a membership means new intents by that user are automatically evaluated against the index.\n- **Owner permissions**: Index owners can update settings (title, prompt, joinPolicy), add/remove members, and delete the index (if sole member).\n- **Discovery scope**: Opportunities are discovered within index boundaries — the system matches intents of members who share at least one index.\n\n### Index Workflow\n1. create_network(title, prompt) → creates new community, you become owner\n2. create_network_membership(networkId, userId) → invite members\n3. Members create intents → auto-assigned to the index based on prompt\n4. discover_opportunities(networkId) → discover matches within this community`,\n\n profiles: `## Profile System\n\nProfiles are the user's identity on the platform, used for semantic matching in opportunity discovery.\n\n- **Structure**: name, bio, location, skills[], interests[], social links (LinkedIn, GitHub, Twitter, websites)\n- **Generation**: Auto-generated from account data (name, email, social links) via web enrichment. Can also be created from explicit user input (bioOrDescription).\n- **Enrichment**: The system scrapes public profiles (LinkedIn, GitHub, Twitter) to build a rich identity with skills, interests, and narrative context.\n- **Embeddings**: HyDE (Hypothetical Document Embedding) generates synthetic documents for semantic matching:\n - Mirror: self-description of the person\n - Reciprocal: what this person would look for in others\n - Neighborhood: related community context\n- **Onboarding flow**: create_user_context() → preview → create_user_context(confirm=true) → complete_onboarding()\n- **Updates**: Use update_user_context for targeted changes, create_user_context for full regeneration.\n\n### Profile Best Practices\n- Richer profiles produce better opportunity matches\n- Social links enable enrichment — encourage users to add LinkedIn/GitHub\n- Profiles are recalculated when updated, which may surface new matches`,\n\n contacts: `## Contact Management\n\nContacts are people in a user's personal network, stored as members of their personal index with 'contact' permission.\n\n- **Adding contacts**: Via import_contacts (bulk), add_contact (single email), or import_gmail_contacts (Google integration).\n- **Ghost users**: When a contact email doesn't match an existing account, a ghost user is created. Ghost users are enriched with public profile data and participate in opportunity matching — they can be discovered even before joining the platform.\n- **Personal index scope**: Pass the personal index networkId to discover_opportunities to scope discovery to just the user's contacts.\n- **Contact data**: Each contact has userId, name, email, avatar, and isGhost flag.\n\n### Contact Workflow\n1. import_contacts or import_gmail_contacts → bulk add to network\n2. list_contacts → view all contacts with userId\n3. discover_opportunities(networkId=personalIndexId) → find matches among contacts\n4. add_contact(email) → add individual contact\n5. remove_contact(contactUserId) → remove from network`,\n\n discovery: `## Discovery Mechanics\n\nDiscovery is the process of finding meaningful connections between users based on their intents and profiles.\n\n### How Discovery Works\n1. **Trigger**: Runs automatically when an intent is created, or explicitly when discover_opportunities is called.\n2. **Pipeline**: Preparation (gather user context) → Scope (determine which indexes to search) → Candidate retrieval (semantic matching via HyDE embeddings) → Evaluation (LLM scores relevance and complementarity) → Ranking → Persist as opportunities.\n3. **Semantic matching**: Uses HyDE (Hypothetical Document Embeddings) to find candidate intents that complement the source. This goes beyond keyword matching — it understands conceptual relationships.\n4. **Evaluation**: An LLM evaluator agent scores each candidate match on relevance, complementarity, and actionability. Low-scoring matches are filtered out.\n5. **Results**: Persisted as draft opportunities with roles, reasoning, and confidence scores.\n6. **Background processing**: After intent creation, a queue job continues looking for matches asynchronously.\n7. **Pagination**: Large result sets are paginated. Use continueFrom with the discoveryId to evaluate more candidates.\n\n### Discovery Best Practices\n- More specific intents produce more relevant matches\n- Richer profiles improve matching quality\n- Scope to a specific index (networkId) for more targeted results\n- After discovery returns no results, suggest creating an intent to attract future matches`,\n\n workflows: `## Common Tool Workflows\n\n### New User Setup\n1. create_user_context(linkedinUrl/githubUrl) → generate profile from social data\n2. complete_onboarding() → unlock full access\n3. read_networks() → see available communities\n4. create_network_membership(networkId) → join a community\n5. create_intent(description) → post what you're looking for\n6. discover_opportunities(searchQuery) → find matches\n\n### Finding Connections\n1. read_networks() → list user's communities (get networkId)\n2. discover_opportunities(searchQuery, networkId) → discover matches\n3. Review opportunity cards → update_opportunity(opportunityId, status=\"pending\") to send\n\n### Making an Introduction\n1. read_network_memberships(networkId) → find members in shared community\n2. read_user_contexts(userId) → get profiles of both parties\n3. read_intents(networkId, userId) → get intents of both parties\n4. discover_opportunities(partyUserIds=[id1,id2], entities=[...], hint=\"reason\") → create introduction\n\n### Managing Contacts\n1. import_gmail_contacts() or import_contacts([...]) → add contacts\n2. list_contacts() → view network\n3. discover_opportunities(networkId=personalIndexId) → find matches among contacts\n\n### Creating a Community\n1. create_network(title, prompt) → create index\n2. create_network_membership(networkId, userId) → invite members\n3. Members create intents → auto-indexed\n4. discover_opportunities(networkId) → discover connections within community`,\n\n authentication: `## Authentication & API Access\n\n### For External AI Agents (MCP)\n- Authenticate via **X-API-Key** header with a valid API key\n- The API key is tied to a specific user account\n- All operations execute in the context of the authenticated user\n- Base URL: protocol.index.network/mcp\n\n### Key Constraints\n- Users can only read their own intents globally, or intents in indexes they belong to\n- Users can only read profiles of people in shared indexes\n- Index-scoped operations are restricted to that index\n- Personal indexes cannot be deleted or renamed\n- Only index owners can update settings, add/remove members (for invite_only indexes)\n\n### Rate Limits & Best Practices\n- Avoid unnecessary read_intents/read_networks calls — cache results within a conversation\n- Use pagination (limit/page) for large result sets\n- Call read_docs once at the start to understand the domain`,\n\n mcp_agent_guide: `## MCP Agent Integration Guide\n\n**IMPORTANT: Read this section if you are an AI agent accessing Index Network via MCP tools.**\n\n### Output Formatting\n- Tool results often contain structured JSON data (proposals, opportunities, cards). **Do NOT dump raw JSON to the user.** Parse the JSON and present information in natural language with clear formatting.\n- Some tool results contain interactive card markup (code blocks with \\`intent_proposal\\`, \\`opportunity_card\\` language tags). These are designed for the Index Network web UI. **As an MCP agent, ignore card markup.** Instead, extract the meaningful data from the JSON and present it conversationally.\n- When presenting opportunities or intents, use bullet points or short paragraphs — not raw JSON objects.\n\n### Intent Creation Workflow\n- **Always pass \\`autoApprove: true\\` when calling \\`create_intent\\`.** This persists intents directly without returning proposal cards that require manual UI approval.\n- The tool will return a list of created intents with their descriptions and confidence scores. Present these in natural language.\n- Do not tell the user to \"click on cards\" or \"approve above\" — there is no UI. Intents are created immediately with autoApprove.\n- After creating intents, proactively suggest or run discovery to find matches.\n\n### Discovery Workflow\n- After creating intents, proactively suggest running discovery: \\`discover_opportunities(searchQuery=...)\\`\n- Present discovered opportunities in natural language with the counterpart's name, match reasoning, and suggested next steps.\n- Do not reference \"cards\", \"panels\", or any web UI elements.\n\n### General MCP Agent Rules\n- You are operating via API tools, not a web interface. Never reference clicking, scrolling, cards, panels, or any visual UI elements.\n- Be proactive: if a logical next step exists (e.g., running discovery after creating intents), suggest or execute it.\n- Use \\`list_opportunities\\` to check existing matches, \\`list_negotiations\\` for ongoing negotiations.\n- Use \\`read_networks\\` to understand which communities the user belongs to before scoping operations.\n- When errors occur, provide clear technical context rather than vague \"backend issue\" messages.`,\n };\n\n if (topic) {\n const matched = Object.entries(sections).find(([key]) => key.includes(topic) || topic.includes(key));\n if (matched) {\n return success({ topic: matched[0], content: matched[1] });\n }\n // If topic not found, return all\n }\n\n const fullDoc = Object.values(sections).join(\"\\n\\n\");\n return success({ content: fullDoc });\n },\n });\n\n return [scrapeUrl, readDocs] as const;\n}\n"]}
|
|
@@ -495,7 +495,7 @@ export interface Database {
|
|
|
495
495
|
/**
|
|
496
496
|
* Updates user account fields (name, location, socials).
|
|
497
497
|
* Merges socials with existing values (does not overwrite the whole object).
|
|
498
|
-
* Used by
|
|
498
|
+
* Used by create_user_context tool to persist user-provided info before
|
|
499
499
|
* invoking the Profile Graph in generate mode.
|
|
500
500
|
*
|
|
501
501
|
* @param userId - The unique identifier of the user
|
|
@@ -946,7 +946,7 @@ export interface Database {
|
|
|
946
946
|
*/
|
|
947
947
|
deleteProfile(userId: string): Promise<void>;
|
|
948
948
|
/**
|
|
949
|
-
* Get a user's profile including its row id (for
|
|
949
|
+
* Get a user's profile including its row id (for update_user_context validation).
|
|
950
950
|
*
|
|
951
951
|
* @param userId - The user whose profile to fetch
|
|
952
952
|
* @returns Profile with id, or null if not found
|