@indexnetwork/protocol 4.3.4-rc.309.1 → 4.3.4-rc.310.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/IMPLEMENTATION.md +2 -2
  2. package/dist/chat/chat.agent.d.ts +1 -1
  3. package/dist/chat/chat.agent.js +1 -1
  4. package/dist/chat/chat.agent.js.map +1 -1
  5. package/dist/chat/chat.prompt.js +30 -30
  6. package/dist/chat/chat.prompt.js.map +1 -1
  7. package/dist/chat/chat.prompt.modules.js +8 -8
  8. package/dist/chat/chat.prompt.modules.js.map +1 -1
  9. package/dist/chat/chat.state.d.ts +1 -1
  10. package/dist/chat/chat.state.d.ts.map +1 -1
  11. package/dist/chat/chat.state.js.map +1 -1
  12. package/dist/chat/tests/chat.graph.mocks.d.ts +1 -1
  13. package/dist/chat/tests/chat.graph.mocks.js +1 -1
  14. package/dist/chat/tests/chat.graph.mocks.js.map +1 -1
  15. package/dist/contact/contact.tools.js +6 -6
  16. package/dist/contact/contact.tools.js.map +1 -1
  17. package/dist/enrichment/enrichment.tools.js +7 -7
  18. package/dist/enrichment/enrichment.tools.js.map +1 -1
  19. package/dist/integration/integration.tools.js +1 -1
  20. package/dist/integration/integration.tools.js.map +1 -1
  21. package/dist/intent/intent.graph.d.ts.map +1 -1
  22. package/dist/intent/intent.graph.js +9 -9
  23. package/dist/intent/intent.graph.js.map +1 -1
  24. package/dist/intent/intent.indexer.d.ts +1 -1
  25. package/dist/intent/intent.indexer.js +4 -4
  26. package/dist/intent/intent.indexer.js.map +1 -1
  27. package/dist/intent/intent.state.d.ts +7 -7
  28. package/dist/intent/intent.state.js +7 -7
  29. package/dist/intent/intent.state.js.map +1 -1
  30. package/dist/intent/intent.tools.js +26 -26
  31. package/dist/intent/intent.tools.js.map +1 -1
  32. package/dist/maintenance/maintenance.graph.d.ts +2 -2
  33. package/dist/maintenance/maintenance.graph.d.ts.map +1 -1
  34. package/dist/maintenance/maintenance.graph.js.map +1 -1
  35. package/dist/mcp/mcp.server.d.ts +2 -2
  36. package/dist/mcp/mcp.server.js +4 -4
  37. package/dist/mcp/mcp.server.js.map +1 -1
  38. package/dist/network/indexer/indexer.graph.d.ts +2 -2
  39. package/dist/network/indexer/indexer.graph.d.ts.map +1 -1
  40. package/dist/network/indexer/indexer.graph.js +13 -13
  41. package/dist/network/indexer/indexer.graph.js.map +1 -1
  42. package/dist/network/indexer/indexer.state.d.ts +3 -3
  43. package/dist/network/indexer/indexer.state.d.ts.map +1 -1
  44. package/dist/network/indexer/indexer.state.js +4 -4
  45. package/dist/network/indexer/indexer.state.js.map +1 -1
  46. package/dist/network/membership/membership.graph.d.ts +1 -1
  47. package/dist/network/membership/membership.graph.js +21 -21
  48. package/dist/network/membership/membership.graph.js.map +1 -1
  49. package/dist/network/membership/membership.state.d.ts +2 -2
  50. package/dist/network/membership/membership.state.js +2 -2
  51. package/dist/network/membership/membership.state.js.map +1 -1
  52. package/dist/network/network.graph.d.ts +1 -1
  53. package/dist/network/network.graph.d.ts.map +1 -1
  54. package/dist/network/network.graph.js +13 -13
  55. package/dist/network/network.graph.js.map +1 -1
  56. package/dist/network/network.state.d.ts +3 -3
  57. package/dist/network/network.state.d.ts.map +1 -1
  58. package/dist/network/network.state.js +3 -3
  59. package/dist/network/network.state.js.map +1 -1
  60. package/dist/network/network.tools.d.ts.map +1 -1
  61. package/dist/network/network.tools.js +95 -107
  62. package/dist/network/network.tools.js.map +1 -1
  63. package/dist/opportunity/opportunity.discover.d.ts +1 -1
  64. package/dist/opportunity/opportunity.discover.d.ts.map +1 -1
  65. package/dist/opportunity/opportunity.discover.js +1 -1
  66. package/dist/opportunity/opportunity.discover.js.map +1 -1
  67. package/dist/opportunity/opportunity.graph.js +10 -10
  68. package/dist/opportunity/opportunity.graph.js.map +1 -1
  69. package/dist/opportunity/opportunity.introducer.d.ts +3 -3
  70. package/dist/opportunity/opportunity.introducer.d.ts.map +1 -1
  71. package/dist/opportunity/opportunity.introducer.js +2 -2
  72. package/dist/opportunity/opportunity.introducer.js.map +1 -1
  73. package/dist/opportunity/opportunity.state.d.ts +1 -1
  74. package/dist/opportunity/opportunity.state.js +1 -1
  75. package/dist/opportunity/opportunity.state.js.map +1 -1
  76. package/dist/opportunity/opportunity.tools.js +18 -18
  77. package/dist/opportunity/opportunity.tools.js.map +1 -1
  78. package/dist/premise/premise.indexer.d.ts +1 -1
  79. package/dist/premise/premise.indexer.js +5 -5
  80. package/dist/premise/premise.indexer.js.map +1 -1
  81. package/dist/questioner/questioner.tools.js +1 -1
  82. package/dist/questioner/questioner.tools.js.map +1 -1
  83. package/dist/shared/agent/tool.factory.d.ts +1 -1
  84. package/dist/shared/agent/tool.factory.js +1 -1
  85. package/dist/shared/agent/tool.factory.js.map +1 -1
  86. package/dist/shared/agent/tool.helpers.d.ts +6 -6
  87. package/dist/shared/agent/tool.helpers.d.ts.map +1 -1
  88. package/dist/shared/agent/tool.helpers.js +2 -2
  89. package/dist/shared/agent/tool.helpers.js.map +1 -1
  90. package/dist/shared/agent/tool.scope.js +1 -1
  91. package/dist/shared/agent/tool.scope.js.map +1 -1
  92. package/dist/shared/agent/utility.tools.js +20 -20
  93. package/dist/shared/agent/utility.tools.js.map +1 -1
  94. package/dist/shared/interfaces/database.interface.d.ts +59 -59
  95. package/dist/shared/interfaces/database.interface.d.ts.map +1 -1
  96. package/dist/shared/interfaces/database.interface.js.map +1 -1
  97. package/dist/shared/interfaces/embedder.interface.d.ts +2 -2
  98. package/dist/shared/interfaces/embedder.interface.d.ts.map +1 -1
  99. package/dist/shared/interfaces/embedder.interface.js.map +1 -1
  100. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import { Annotation } from "@langchain/langgraph";
2
2
  /**
3
- * Index Graph State.
3
+ * Network Graph State.
4
4
  * Handles CRUD operations for indexes (communities).
5
5
  *
6
6
  * Flow:
@@ -10,7 +10,7 @@ export const NetworkGraphState = Annotation.Root({
10
10
  // --- Core Inputs (from ChatGraph via ToolContext) ---
11
11
  /** User performing the action. Always required. */
12
12
  userId: (Annotation),
13
- /** Target index ID. Required for read/update/delete. From ChatGraph or tool arg. */
13
+ /** Target network ID. Required for read/update/delete. From ChatGraph or tool arg. */
14
14
  networkId: Annotation({
15
15
  reducer: (_, next) => next,
16
16
  default: () => undefined,
@@ -31,7 +31,7 @@ export const NetworkGraphState = Annotation.Root({
31
31
  reducer: (_, next) => next,
32
32
  default: () => undefined,
33
33
  }),
34
- /** When true and index-scoped, read returns all user indexes (not just scoped one). */
34
+ /** When true and network-scoped, read returns all user indexes (not just scoped one). */
35
35
  showAll: Annotation({
36
36
  reducer: (_, next) => next,
37
37
  default: () => false,
@@ -1 +1 @@
1
- {"version":3,"file":"network.state.js","sourceRoot":"/","sources":["network/network.state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,UAAU,CAAC,IAAI,CAAC;IAC/C,uDAAuD;IAEvD,mDAAmD;IACnD,MAAM,EAAE,CAAA,UAAkB,CAAA;IAE1B,oFAAoF;IACpF,SAAS,EAAE,UAAU,CAAqB;QACxC,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;QAC1B,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS;KACzB,CAAC;IAEF,sBAAsB;IACtB,aAAa,EAAE,UAAU,CAA0C;QACjE,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,IAAI;QACrC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAe;KAC/B,CAAC;IAEF,+BAA+B;IAE/B,4CAA4C;IAC5C,WAAW,EAAE,UAAU,CAKR;QACb,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;QAC1B,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS;KACzB,CAAC;IAEF,yCAAyC;IACzC,WAAW,EAAE,UAAU,CAMR;QACb,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;QAC1B,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS;KACzB,CAAC;IAEF,uFAAuF;IACvF,OAAO,EAAE,UAAU,CAAU;QAC3B,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;QAC1B,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK;KACrB,CAAC;IAEF,kBAAkB;IAElB,4BAA4B;IAC5B,UAAU,EAAE,UAAU,CA8BP;QACb,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;QAC1B,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS;KACzB,CAAC;IAEF,6CAA6C;IAC7C,cAAc,EAAE,UAAU,CAMX;QACb,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;QAC1B,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS;KACzB,CAAC;IAEF,iDAAiD;IACjD,KAAK,EAAE,UAAU,CAAgB;QAC/B,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;QAC1B,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI;KACpB,CAAC;CACH,CAAC,CAAC","sourcesContent":["import { Annotation } from \"@langchain/langgraph\";\n\n/**\n * Index Graph State.\n * Handles CRUD operations for indexes (communities).\n *\n * Flow:\n * START → routerNode → {createNode | readNode | updateNode | deleteNode} → END\n */\nexport const NetworkGraphState = Annotation.Root({\n // --- Core Inputs (from ChatGraph via ToolContext) ---\n\n /** User performing the action. Always required. */\n userId: Annotation<string>,\n\n /** Target index ID. Required for read/update/delete. From ChatGraph or tool arg. */\n networkId: Annotation<string | undefined>({\n reducer: (_, next) => next,\n default: () => undefined,\n }),\n\n /** Operation mode. */\n operationMode: Annotation<'create' | 'read' | 'update' | 'delete'>({\n reducer: (curr, next) => next ?? curr,\n default: () => 'read' as const,\n }),\n\n // --- Mode-Specific Inputs ---\n\n /** For create mode: index creation data. */\n createInput: Annotation<{\n title: string;\n prompt?: string;\n imageUrl?: string | null;\n joinPolicy?: 'anyone' | 'invite_only';\n } | undefined>({\n reducer: (_, next) => next,\n default: () => undefined,\n }),\n\n /** For update mode: fields to update. */\n updateInput: Annotation<{\n title?: string;\n prompt?: string | null;\n imageUrl?: string | null;\n joinPolicy?: 'anyone' | 'invite_only';\n allowGuestVibeCheck?: boolean;\n } | undefined>({\n reducer: (_, next) => next,\n default: () => undefined,\n }),\n\n /** When true and index-scoped, read returns all user indexes (not just scoped one). */\n showAll: Annotation<boolean>({\n reducer: (_, next) => next,\n default: () => false,\n }),\n\n // --- Outputs ---\n\n /** Output for read mode. */\n readResult: Annotation<{\n memberOf: Array<{\n networkId: string;\n title: string;\n prompt: string | null;\n autoAssign: boolean;\n isPersonal: boolean;\n joinedAt: Date;\n }>;\n owns: Array<{\n networkId: string;\n title: string;\n prompt: string | null;\n memberCount: number;\n intentCount: number;\n joinPolicy: string;\n }>;\n publicNetworks?: Array<{\n networkId: string;\n title: string;\n prompt: string | null;\n memberCount: number;\n owner: { name: string; avatar: string | null } | null;\n }>;\n stats: {\n memberOfCount: number;\n ownsCount: number;\n publicNetworksCount?: number;\n scopeNote?: string;\n };\n } | undefined>({\n reducer: (_, next) => next,\n default: () => undefined,\n }),\n\n /** Output for create/update/delete modes. */\n mutationResult: Annotation<{\n success: boolean;\n networkId?: string;\n title?: string;\n message?: string;\n error?: string;\n } | undefined>({\n reducer: (_, next) => next,\n default: () => undefined,\n }),\n\n /** Error message if graph could not complete. */\n error: Annotation<string | null>({\n reducer: (_, next) => next,\n default: () => null,\n }),\n});\n"]}
1
+ {"version":3,"file":"network.state.js","sourceRoot":"/","sources":["network/network.state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,UAAU,CAAC,IAAI,CAAC;IAC/C,uDAAuD;IAEvD,mDAAmD;IACnD,MAAM,EAAE,CAAA,UAAkB,CAAA;IAE1B,sFAAsF;IACtF,SAAS,EAAE,UAAU,CAAqB;QACxC,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;QAC1B,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS;KACzB,CAAC;IAEF,sBAAsB;IACtB,aAAa,EAAE,UAAU,CAA0C;QACjE,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,IAAI;QACrC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAe;KAC/B,CAAC;IAEF,+BAA+B;IAE/B,4CAA4C;IAC5C,WAAW,EAAE,UAAU,CAKR;QACb,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;QAC1B,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS;KACzB,CAAC;IAEF,yCAAyC;IACzC,WAAW,EAAE,UAAU,CAMR;QACb,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;QAC1B,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS;KACzB,CAAC;IAEF,yFAAyF;IACzF,OAAO,EAAE,UAAU,CAAU;QAC3B,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;QAC1B,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK;KACrB,CAAC;IAEF,kBAAkB;IAElB,4BAA4B;IAC5B,UAAU,EAAE,UAAU,CA8BP;QACb,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;QAC1B,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS;KACzB,CAAC;IAEF,6CAA6C;IAC7C,cAAc,EAAE,UAAU,CAMX;QACb,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;QAC1B,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS;KACzB,CAAC;IAEF,iDAAiD;IACjD,KAAK,EAAE,UAAU,CAAgB;QAC/B,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;QAC1B,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI;KACpB,CAAC;CACH,CAAC,CAAC","sourcesContent":["import { Annotation } from \"@langchain/langgraph\";\n\n/**\n * Network Graph State.\n * Handles CRUD operations for indexes (communities).\n *\n * Flow:\n * START → routerNode → {createNode | readNode | updateNode | deleteNode} → END\n */\nexport const NetworkGraphState = Annotation.Root({\n // --- Core Inputs (from ChatGraph via ToolContext) ---\n\n /** User performing the action. Always required. */\n userId: Annotation<string>,\n\n /** Target network ID. Required for read/update/delete. From ChatGraph or tool arg. */\n networkId: Annotation<string | undefined>({\n reducer: (_, next) => next,\n default: () => undefined,\n }),\n\n /** Operation mode. */\n operationMode: Annotation<'create' | 'read' | 'update' | 'delete'>({\n reducer: (curr, next) => next ?? curr,\n default: () => 'read' as const,\n }),\n\n // --- Mode-Specific Inputs ---\n\n /** For create mode: index creation data. */\n createInput: Annotation<{\n title: string;\n prompt?: string;\n imageUrl?: string | null;\n joinPolicy?: 'anyone' | 'invite_only';\n } | undefined>({\n reducer: (_, next) => next,\n default: () => undefined,\n }),\n\n /** For update mode: fields to update. */\n updateInput: Annotation<{\n title?: string;\n prompt?: string | null;\n imageUrl?: string | null;\n joinPolicy?: 'anyone' | 'invite_only';\n allowGuestVibeCheck?: boolean;\n } | undefined>({\n reducer: (_, next) => next,\n default: () => undefined,\n }),\n\n /** When true and network-scoped, read returns all user indexes (not just scoped one). */\n showAll: Annotation<boolean>({\n reducer: (_, next) => next,\n default: () => false,\n }),\n\n // --- Outputs ---\n\n /** Output for read mode. */\n readResult: Annotation<{\n memberOf: Array<{\n networkId: string;\n title: string;\n prompt: string | null;\n autoAssign: boolean;\n isPersonal: boolean;\n joinedAt: Date;\n }>;\n owns: Array<{\n networkId: string;\n title: string;\n prompt: string | null;\n memberCount: number;\n intentCount: number;\n joinPolicy: string;\n }>;\n publicNetworks?: Array<{\n networkId: string;\n title: string;\n prompt: string | null;\n memberCount: number;\n owner: { name: string; avatar: string | null } | null;\n }>;\n stats: {\n memberOfCount: number;\n ownsCount: number;\n publicNetworksCount?: number;\n scopeNote?: string;\n };\n } | undefined>({\n reducer: (_, next) => next,\n default: () => undefined,\n }),\n\n /** Output for create/update/delete modes. */\n mutationResult: Annotation<{\n success: boolean;\n networkId?: string;\n title?: string;\n message?: string;\n error?: string;\n } | undefined>({\n reducer: (_, next) => next,\n default: () => undefined,\n }),\n\n /** Error message if graph could not complete. */\n error: Annotation<string | null>({\n reducer: (_, next) => next,\n default: () => null,\n }),\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"network.tools.d.ts","sourceRoot":"/","sources":["network/network.tools.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAS5E,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,gDA2nBxE"}
1
+ {"version":3,"file":"network.tools.d.ts","sourceRoot":"/","sources":["network/network.tools.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAQ5E,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,gDA+mBxE"}
@@ -2,7 +2,6 @@ import { z } from "zod";
2
2
  import { requestContext } from "../shared/observability/request-context.js";
3
3
  import { renderNetworkContext } from '../shared/network/metadata.renderer.js';
4
4
  import { success, error, UUID_REGEX } from "../shared/agent/tool.helpers.js";
5
- import { focusedNetworkId, focusedNetworkLabel } from "../shared/agent/tool.scope.js";
6
5
  import { NetworkRecommender } from "./network.recommender.js";
7
6
  // Lazy singleton — only instantiated on first onboarding ranking call so that
8
7
  // importing this module does not require OPENROUTER_API_KEY at load time.
@@ -26,7 +25,7 @@ export function createNetworkTools(defineTool, deps) {
26
25
  "**Returns:** Up to three lists — `memberOf` (networks the user joined), `owns` (networks the user created), and `publicNetworks` " +
27
26
  "(publicly joinable communities the user is not yet a member of). Entries in `memberOf` include `isPersonal` set to `true` for the user's " +
28
27
  "personal network.\n\n" +
29
- "**Note:** In index-scoped chats, only the scoped network is returned. During onboarding, `orderedNetworkIds` " +
28
+ "**Note:** In network-scoped chats, only the scoped network is returned. During onboarding, `orderedNetworkIds` " +
30
29
  "may be returned alongside `publicNetworks` \u2014 a ranked array of network IDs ordered by relevance to the user's profile (omitted when ranking is unavailable or fails).",
31
30
  querySchema: z.object({
32
31
  userId: z.string().optional().describe("Must be the current user's ID or omitted. Cannot list another user's indexes."),
@@ -35,14 +34,12 @@ export function createNetworkTools(defineTool, deps) {
35
34
  if (query.userId && query.userId.trim() !== context.userId) {
36
35
  return error("You can only list your own indexes. Omit userId to see the current user's indexes.");
37
36
  }
38
- const scopedNetworkId = focusedNetworkId(context);
39
- const scopedIndexLabel = focusedNetworkLabel(context);
40
37
  const _readIndexGraphStart = Date.now();
41
38
  const _readIndexTraceEmitter = requestContext.getStore()?.traceEmitter;
42
39
  _readIndexTraceEmitter?.({ type: "graph_start", name: "index" });
43
40
  const result = await graphs.index.invoke({
44
41
  userId: context.userId,
45
- networkId: scopedNetworkId,
42
+ networkId: context.networkId || undefined,
46
43
  operationMode: 'read',
47
44
  showAll: false, // Never allow bypass - strict scope enforcement
48
45
  });
@@ -60,13 +57,13 @@ export function createNetworkTools(defineTool, deps) {
60
57
  ...(Array.isArray(rr.publicNetworks) ? { publicNetworks: enrichWithContext(rr.publicNetworks) } : {}),
61
58
  };
62
59
  // When scoped, add clear metadata so model knows results are limited
63
- if (scopedNetworkId) {
60
+ if (context.networkId) {
64
61
  return success({
65
62
  ...enriched,
66
63
  scopeRestriction: {
67
64
  isScoped: true,
68
- scopedToIndex: scopedIndexLabel,
69
- message: `Results are limited to "${scopedIndexLabel}" because this chat is scoped to that community. The user may belong to other communities not shown here.`,
65
+ scopedToIndex: context.indexName ?? context.networkId,
66
+ message: `Results are limited to "${context.indexName ?? 'this network'}" because this chat is scoped to that community. The user may belong to other communities not shown here.`,
70
67
  },
71
68
  _graphTimings: [{ name: 'index', durationMs: _readIndexGraphMs, agents: result.agentTimings ?? [] }],
72
69
  });
@@ -143,41 +140,39 @@ export function createNetworkTools(defineTool, deps) {
143
140
  });
144
141
  const readIndexMemberships = defineTool({
145
142
  name: "read_network_memberships",
146
- description: "Reads index membership information — who is in which community. Essential for understanding the social graph before " +
143
+ description: "Reads network membership information — who is in which community. Essential for understanding the social graph before " +
147
144
  "creating introductions or exploring intents.\n\n" +
148
145
  "**Usage modes:**\n" +
149
146
  "- With `networkId` only: lists ALL members of that index — returns userId, name, avatar, permissions (owner/member), intentCount, and joinedAt. " +
150
147
  "Use this to see who's in a community before browsing their intents or creating introductions.\n" +
151
- "- With `userId` only (or omit for self): lists all indexes that user belongs to — returns networkId, networkTitle, permissions, joinedAt.\n" +
148
+ "- With `userId` only (or omit for self): lists all networks that user belongs to — returns networkId, networkTitle, permissions, joinedAt.\n" +
152
149
  "- With both `networkId` and `userId`: checks whether that specific user is a member of that specific index (returns isMember boolean).\n\n" +
153
- "**When to use:** Before creating introductions (need to verify shared index membership), to explore community members, " +
150
+ "**When to use:** Before creating introductions (need to verify shared network membership), to explore community members, " +
154
151
  "or to check if a user belongs to a specific index.\n\n" +
155
152
  "**Returns:** Member list with user details, or membership list with index details, or a membership check result.\n\n" +
156
- "**Personal index semantics.** The personal index (`isPersonal: true` on the membership) is the user's " +
153
+ "**Personal network semantics.** The personal network (`isPersonal: true` on the membership) is the user's " +
157
154
  "contact list — members of that index are the user's contacts. For another user, this tool only reveals the " +
158
155
  "indexes you already share with them.\n\n" +
159
156
  "**Shared-context pattern.** To find overlap with another user: (1) omit `userId` to read your own " +
160
- "memberships, (2) call this tool with the other person's actual `userId` to get the shared indexes, " +
157
+ "memberships, (2) call this tool with the other person's actual `userId` to get the shared networks, " +
161
158
  "(3) call read_intents for each shared network to see what each is looking for there, (4) call " +
162
159
  "read_user_contexts for the other party. That sequence gives you enough to decide whether to propose a " +
163
160
  "direct connection or an introduction.",
164
161
  querySchema: z.object({
165
- networkId: z.string().optional().describe("Index UUID — lists all members of this index. Get from read_networks. In index-scoped chats, only the scoped index can be queried."),
166
- userId: z.string().optional().describe("User ID — lists that user's index memberships. Omit to get the current user's memberships. When combined with networkId, checks if this user is in that specific index."),
162
+ networkId: z.string().optional().describe("Network UUID — lists all members of this network. Get from read_networks. In network-scoped chats, only the scoped index can be queried."),
163
+ userId: z.string().optional().describe("User ID — lists that user's network memberships. Omit to get the current user's memberships. When combined with networkId, checks if this user is in that specific index."),
167
164
  }),
168
165
  handler: async ({ context, query }) => {
169
- const scopedNetworkId = focusedNetworkId(context);
170
- const scopedIndexLabel = focusedNetworkLabel(context);
171
166
  const networkId = query.networkId?.trim() || undefined;
172
167
  const userId = query.userId?.trim() || undefined;
173
168
  if (networkId && !UUID_REGEX.test(networkId)) {
174
- return error("Invalid index ID format. Use the exact UUID from read_networks.");
169
+ return error("Invalid network ID format. Use the exact UUID from read_networks.");
175
170
  }
176
171
  // Mode 1: list members of an index
177
172
  if (networkId && !userId) {
178
- // Enforce strict scope: when chat is index-scoped, only allow querying that index
179
- if (scopedNetworkId && networkId !== scopedNetworkId) {
180
- return error(`This chat is scoped to ${scopedIndexLabel}. You can only query members of this index.`);
173
+ // Enforce strict scope: when chat is network-scoped, only allow querying that index
174
+ if (context.networkId && networkId !== context.networkId) {
175
+ return error(`This chat is scoped to ${context.indexName ?? 'this network'}. You can only query members of this network.`);
181
176
  }
182
177
  const _readMembersGraphStart = Date.now();
183
178
  const _readMembersTraceEmitter = requestContext.getStore()?.traceEmitter;
@@ -195,19 +190,19 @@ export function createNetworkTools(defineTool, deps) {
195
190
  if (result.readResult) {
196
191
  return success({ ...result.readResult, _graphTimings: [{ name: 'network_membership', durationMs: _readMembersGraphMs, agents: result.agentTimings ?? [] }] });
197
192
  }
198
- return error("Failed to fetch index members.");
193
+ return error("Failed to fetch network members.");
199
194
  }
200
195
  // Mode 2: list a user's memberships (indexes they belong to)
201
196
  const targetUserId = userId || context.userId;
202
- // Use userDb for own memberships, but systemDb access is implicit through shared index scope
197
+ // Use userDb for own memberships, but systemDb access is implicit through shared network scope
203
198
  let memberships;
204
199
  if (targetUserId !== context.userId) {
205
- // Cross-user access: systemDb will validate shared index membership
200
+ // Cross-user access: systemDb will validate shared network membership
206
201
  const callerMemberships = await userDb.getNetworkMemberships();
207
202
  if (networkId) {
208
- // Strict scope enforcement: when chat is index-scoped, only allow querying that index
209
- if (scopedNetworkId && networkId !== scopedNetworkId) {
210
- return error(`This chat is scoped to ${scopedIndexLabel}. You can only query membership in this community.`);
203
+ // Strict scope enforcement: when chat is network-scoped, only allow querying that index
204
+ if (context.networkId && networkId !== context.networkId) {
205
+ return error(`This chat is scoped to ${context.indexName ?? 'this network'}. You can only query membership in this community.`);
211
206
  }
212
207
  const callerInIndex = callerMemberships.some((m) => m.networkId === networkId);
213
208
  if (!callerInIndex) {
@@ -218,37 +213,37 @@ export function createNetworkTools(defineTool, deps) {
218
213
  if (isMember) {
219
214
  return success({ isMember: true, userId: targetUserId, networkId });
220
215
  }
221
- return success({ isMember: false, userId: targetUserId, networkId, message: "User is not a member of this index." });
216
+ return success({ isMember: false, userId: targetUserId, networkId, message: "User is not a member of this network." });
222
217
  }
223
218
  else {
224
- // Strict scope enforcement: when chat is index-scoped, only check the scoped index
225
- if (scopedNetworkId) {
226
- const isMember = await systemDb.isNetworkMember(scopedNetworkId, targetUserId);
219
+ // Strict scope enforcement: when chat is network-scoped, only check the scoped index
220
+ if (context.networkId) {
221
+ const isMember = await systemDb.isNetworkMember(context.networkId, targetUserId);
227
222
  if (isMember) {
228
223
  return success({
229
224
  isMember: true,
230
225
  userId: targetUserId,
231
- networkId: scopedNetworkId,
226
+ networkId: context.networkId,
232
227
  scopeRestriction: {
233
228
  isScoped: true,
234
- scopedToIndex: scopedIndexLabel,
235
- message: `This chat is scoped to "${scopedIndexLabel}". Only membership in this community is shown.`,
229
+ scopedToIndex: context.indexName ?? context.networkId,
230
+ message: `This chat is scoped to "${context.indexName ?? 'this network'}". Only membership in this community is shown.`,
236
231
  },
237
232
  });
238
233
  }
239
234
  return success({
240
235
  isMember: false,
241
236
  userId: targetUserId,
242
- networkId: scopedNetworkId,
237
+ networkId: context.networkId,
243
238
  message: "User is not a member of this community.",
244
239
  scopeRestriction: {
245
240
  isScoped: true,
246
- scopedToIndex: scopedIndexLabel,
247
- message: `This chat is scoped to "${scopedIndexLabel}". Only membership in this community was checked.`,
241
+ scopedToIndex: context.indexName ?? context.networkId,
242
+ message: `This chat is scoped to "${context.indexName ?? 'this network'}". Only membership in this community was checked.`,
248
243
  },
249
244
  });
250
245
  }
251
- // Unscoped chat: show overlap with shared indexes (intersection of caller and target memberships)
246
+ // Unscoped chat: show overlap with shared networks (intersection of caller and target memberships)
252
247
  const sharedIndexes = [];
253
248
  for (const m of callerMemberships) {
254
249
  if (await systemDb.isNetworkMember(m.networkId, targetUserId)) {
@@ -266,25 +261,26 @@ export function createNetworkTools(defineTool, deps) {
266
261
  networkId: m.networkId,
267
262
  networkTitle: m.networkTitle,
268
263
  })),
269
- note: "Only showing shared indexes.",
264
+ note: "Only showing shared networks.",
270
265
  });
271
266
  }
272
267
  }
273
268
  else {
274
269
  // Own memberships - use userDb
275
270
  memberships = await userDb.getNetworkMemberships();
276
- // The scope envelope's network id is the focus of the scoped chat — it filters
277
- // what we show, not what we can reach. Concrete reach is derived separately.
278
- // Strict scope enforcement: when chat is index-scoped, only return the scoped index membership
279
- if (scopedNetworkId && !networkId) {
280
- memberships = memberships.filter((m) => m.networkId === scopedNetworkId);
271
+ // NOTE: context.networkId here is the *focus* of the scoped chat — it filters
272
+ // what we show, not what we can reach. indexScope is the reach; the bound
273
+ // network is one element of it. See IND-306 for the equivalent in read_intents.
274
+ // Strict scope enforcement: when chat is network-scoped, only return the scoped network membership
275
+ if (context.networkId && !networkId) {
276
+ memberships = memberships.filter((m) => m.networkId === context.networkId);
281
277
  }
282
278
  }
283
279
  // If both networkId and userId: filter to that specific membership
284
280
  if (networkId) {
285
- // Strict scope enforcement: when chat is index-scoped, only allow querying that index
286
- if (scopedNetworkId && networkId !== scopedNetworkId) {
287
- return error(`This chat is scoped to ${scopedIndexLabel}. You can only query membership in this community.`);
281
+ // Strict scope enforcement: when chat is network-scoped, only allow querying that index
282
+ if (context.networkId && networkId !== context.networkId) {
283
+ return error(`This chat is scoped to ${context.indexName ?? 'this network'}. You can only query membership in this community.`);
288
284
  }
289
285
  const callerMemberships = await userDb.getNetworkMemberships();
290
286
  const callerInIndex = targetUserId === context.userId ||
@@ -294,7 +290,7 @@ export function createNetworkTools(defineTool, deps) {
294
290
  }
295
291
  const match = memberships.find((m) => m.networkId === networkId);
296
292
  if (!match) {
297
- return success({ isMember: false, userId: targetUserId, networkId, message: "User is not a member of this index." });
293
+ return success({ isMember: false, userId: targetUserId, networkId, message: "User is not a member of this network." });
298
294
  }
299
295
  return success({
300
296
  isMember: true,
@@ -306,7 +302,7 @@ export function createNetworkTools(defineTool, deps) {
306
302
  });
307
303
  }
308
304
  // When scoped, add clear metadata so model knows results are limited
309
- if (scopedNetworkId && targetUserId === context.userId) {
305
+ if (context.networkId && targetUserId === context.userId) {
310
306
  return success({
311
307
  userId: targetUserId,
312
308
  count: memberships.length,
@@ -318,8 +314,8 @@ export function createNetworkTools(defineTool, deps) {
318
314
  })),
319
315
  scopeRestriction: {
320
316
  isScoped: true,
321
- scopedToIndex: scopedIndexLabel,
322
- message: `Results are limited to "${scopedIndexLabel}" because this chat is scoped to that community. The user may belong to other communities not shown here.`,
317
+ scopedToIndex: context.indexName ?? context.networkId,
318
+ message: `Results are limited to "${context.indexName ?? 'this network'}" because this chat is scoped to that community. The user may belong to other communities not shown here.`,
323
319
  },
324
320
  });
325
321
  }
@@ -344,28 +340,26 @@ export function createNetworkTools(defineTool, deps) {
344
340
  }).strict();
345
341
  const updateNetwork = defineTool({
346
342
  name: "update_network",
347
- description: "Updates settings of an existing index (community). Only the index owner can perform updates.\n\n" +
343
+ description: "Updates settings of an existing network (community). Only the network owner can perform updates.\n\n" +
348
344
  "**Updatable fields:** title (display name), prompt (purpose description used for intent auto-assignment), " +
349
345
  "imageUrl (community avatar), joinPolicy ('anyone' for open or 'invite_only'), allowGuestVibeCheck (allow non-members to preview).\n\n" +
350
- "**When to use:** When an index owner wants to change their community's settings — e.g. update the purpose description, " +
346
+ "**When to use:** When a network owner wants to change their community's settings — e.g. update the purpose description, " +
351
347
  "change from invite-only to open, or update the community image.\n\n" +
352
- "**Important:** Changing the prompt affects how future intents are evaluated for auto-assignment to this index. " +
353
- "Existing intent-index links are not re-evaluated automatically.\n\n" +
348
+ "**Important:** Changing the prompt affects how future intents are evaluated for auto-assignment to this network. " +
349
+ "Existing intent-network links are not re-evaluated automatically.\n\n" +
354
350
  "**Returns:** Confirmation with the list of settings that were updated.",
355
351
  querySchema: z.object({
356
- networkId: z.string().optional().describe("Index UUID to update. Get from read_networks. Defaults to the scoped index in index-scoped chats."),
352
+ networkId: z.string().optional().describe("Network UUID to update. Get from read_networks. Defaults to the scoped index in network-scoped chats."),
357
353
  settings: updateNetworkSettingsSchema.describe("Object with fields to update. All fields are optional — only include the ones to change. title: display name. prompt: purpose description (used for intent auto-assignment). imageUrl: community image URL (null to remove). joinPolicy: 'anyone' or 'invite_only'. allowGuestVibeCheck: boolean."),
358
354
  }),
359
355
  handler: async ({ context, query }) => {
360
- const scopedNetworkId = focusedNetworkId(context);
361
- const scopedIndexLabel = focusedNetworkLabel(context);
362
- const effectiveIndexId = (query.networkId?.trim() || scopedNetworkId) ?? null;
356
+ const effectiveIndexId = (query.networkId?.trim() || context.networkId) ?? null;
363
357
  if (!effectiveIndexId || !UUID_REGEX.test(effectiveIndexId)) {
364
358
  return error("Valid networkId required.");
365
359
  }
366
- // Strict scope enforcement: when chat is index-scoped, only allow updating that index
367
- if (scopedNetworkId && effectiveIndexId !== scopedNetworkId) {
368
- return error(`This chat is scoped to ${scopedIndexLabel}. You can only update this community's settings.`);
360
+ // Strict scope enforcement: when chat is network-scoped, only allow updating that index
361
+ if (context.networkId && effectiveIndexId !== context.networkId) {
362
+ return error(`This chat is scoped to ${context.indexName ?? 'this network'}. You can only update this community's settings.`);
369
363
  }
370
364
  const _updateNetworkGraphStart = Date.now();
371
365
  const _updateNetworkTraceEmitter = requestContext.getStore()?.traceEmitter;
@@ -386,15 +380,15 @@ export function createNetworkTools(defineTool, deps) {
386
380
  });
387
381
  const createNetwork = defineTool({
388
382
  name: "create_network",
389
- description: "Creates a new index (community/group). The authenticated user becomes the owner with full control over settings and membership.\n\n" +
390
- "**What is an index?** A shared space where members post intents (what they're looking for) and the system discovers opportunities " +
391
- "(complementary matches) between members. The index's prompt guides what kinds of intents belong.\n\n" +
383
+ description: "Creates a new network (community/group). The authenticated user becomes the owner with full control over settings and membership.\n\n" +
384
+ "**What is a network?** A shared space where members post intents (what they're looking for) and the system discovers opportunities " +
385
+ "(complementary matches) between members. The network's prompt guides what kinds of intents belong.\n\n" +
392
386
  "**When to use:** When the user wants to create a new community — e.g. a professional network, interest group, or project team.\n\n" +
393
- "**Returns:** The new index's networkId (UUID) and title. Use the networkId to add members (create_network_membership), " +
387
+ "**Returns:** The new network's networkId (UUID) and title. Use the networkId to add members (create_network_membership), " +
394
388
  "link intents (create_intent_index), or run discovery (discover_opportunities with networkId).",
395
389
  querySchema: z.object({
396
- title: z.string().describe("Display name of the index (e.g. 'AI Founders Berlin', 'Design Co-op'). Required."),
397
- prompt: z.string().optional().describe("Description of what this community is about (e.g. 'Early-stage AI/ML founders in Berlin looking for co-founders, advisors, and investors'). Used by the system to evaluate which intents belong in this index. Highly recommended for better auto-assignment."),
390
+ title: z.string().describe("Display name of the network (e.g. 'AI Founders Berlin', 'Design Co-op'). Required."),
391
+ prompt: z.string().optional().describe("Description of what this community is about (e.g. 'Early-stage AI/ML founders in Berlin looking for co-founders, advisors, and investors'). Used by the system to evaluate which intents belong in this network. Highly recommended for better auto-assignment."),
398
392
  imageUrl: z.string().url().optional().describe("URL for the community's avatar/image. Optional."),
399
393
  joinPolicy: z.enum(['anyone', 'invite_only']).optional().describe("'anyone' = open (any user can self-join), 'invite_only' = only the owner can add members. Defaults to 'invite_only'."),
400
394
  }),
@@ -427,31 +421,29 @@ export function createNetworkTools(defineTool, deps) {
427
421
  _graphTimings: [{ name: 'index', durationMs: _createNetworkGraphMs, agents: result.agentTimings ?? [] }],
428
422
  });
429
423
  }
430
- return error(result.mutationResult.error || "Failed to create index.");
424
+ return error(result.mutationResult.error || "Failed to create network.");
431
425
  }
432
- return error("Failed to create index.");
426
+ return error("Failed to create network.");
433
427
  },
434
428
  });
435
429
  const deleteNetwork = defineTool({
436
430
  name: "delete_network",
437
- description: "Permanently deletes an index (community). Only the owner can delete, and the index must have no other members " +
438
- "(remove all members first with delete_network_membership). Personal indexes cannot be deleted.\n\n" +
439
- "**When to use:** When the owner wants to disband a community. This is irreversible — all intent-index links to this index are removed.\n\n" +
431
+ description: "Permanently deletes a network (community). Only the owner can delete, and the network must have no other members " +
432
+ "(remove all members first with delete_network_membership). Personal networks cannot be deleted.\n\n" +
433
+ "**When to use:** When the owner wants to disband a community. This is irreversible — all intent-network links to this network are removed.\n\n" +
440
434
  "**Prerequisites:** Must be the owner. Must be the sole remaining member (remove others first).\n\n" +
441
- "**Returns:** Confirmation that the index was deleted.",
435
+ "**Returns:** Confirmation that the network was deleted.",
442
436
  querySchema: z.object({
443
- networkId: z.string().optional().describe("Index UUID to delete. Get from read_networks. Defaults to the scoped index in index-scoped chats. Cannot be a personal index."),
437
+ networkId: z.string().optional().describe("Network UUID to delete. Get from read_networks. Defaults to the scoped index in network-scoped chats. Cannot be a personal network."),
444
438
  }),
445
439
  handler: async ({ context, query }) => {
446
- const scopedNetworkId = focusedNetworkId(context);
447
- const scopedIndexLabel = focusedNetworkLabel(context);
448
- const networkId = query.networkId?.trim() || scopedNetworkId;
440
+ const networkId = query.networkId?.trim() || context.networkId;
449
441
  if (!networkId || !UUID_REGEX.test(networkId)) {
450
442
  return error("Valid networkId required.");
451
443
  }
452
- // Strict scope enforcement: when chat is index-scoped, only allow deleting that index
453
- if (scopedNetworkId && networkId !== scopedNetworkId) {
454
- return error(`This chat is scoped to ${scopedIndexLabel}. You can only delete this community.`);
444
+ // Strict scope enforcement: when chat is network-scoped, only allow deleting that index
445
+ if (context.networkId && networkId !== context.networkId) {
446
+ return error(`This chat is scoped to ${context.indexName ?? 'this network'}. You can only delete this community.`);
455
447
  }
456
448
  const _deleteNetworkGraphStart = Date.now();
457
449
  const _deleteNetworkTraceEmitter = requestContext.getStore()?.traceEmitter;
@@ -464,36 +456,34 @@ export function createNetworkTools(defineTool, deps) {
464
456
  const _deleteNetworkGraphMs = Date.now() - _deleteNetworkGraphStart;
465
457
  _deleteNetworkTraceEmitter?.({ type: "graph_end", name: "index", durationMs: _deleteNetworkGraphMs });
466
458
  if (result.mutationResult && !result.mutationResult.success) {
467
- return error(result.mutationResult.error || "Failed to delete index.");
459
+ return error(result.mutationResult.error || "Failed to delete network.");
468
460
  }
469
461
  return success({ message: "Network deleted.", _graphTimings: [{ name: 'index', durationMs: _deleteNetworkGraphMs, agents: result.agentTimings ?? [] }] });
470
462
  },
471
463
  });
472
464
  const createNetworkMembership = defineTool({
473
465
  name: "create_network_membership",
474
- description: "Adds a user as a member of an index (community). Membership enables the user to post intents in the index and be discovered " +
466
+ description: "Adds a user as a member of a network (community). Membership enables the user to post intents in the network and be discovered " +
475
467
  "by other members through opportunity matching.\n\n" +
476
468
  "**Usage modes:**\n" +
477
- "- Omit userId: self-join (only works for indexes with joinPolicy 'anyone').\n" +
478
- "- With userId: add another user (only the index owner can do this for 'invite_only' indexes).\n\n" +
479
- "**When to use:** When the user wants to join an open community, or when an index owner wants to invite someone.\n\n" +
469
+ "- Omit userId: self-join (only works for networks with joinPolicy 'anyone').\n" +
470
+ "- With userId: add another user (only the network owner can do this for 'invite_only' indexes).\n\n" +
471
+ "**When to use:** When the user wants to join an open community, or when a network owner wants to invite someone.\n\n" +
480
472
  "**Returns:** Confirmation that the member was added (or a note that they were already a member). " +
481
- "After joining, the user's existing intents with autoAssign=true may be evaluated against the new index.",
473
+ "After joining, the user's existing intents with autoAssign=true may be evaluated against the new network.",
482
474
  querySchema: z.object({
483
- userId: z.string().optional().describe("User ID to add as a member. Omit to join the index yourself. Get user IDs from read_user_contexts(query=name) or read_network_memberships."),
484
- networkId: z.string().optional().describe("Index UUID to add the member to. Get from read_networks. Defaults to the scoped index in index-scoped chats."),
475
+ userId: z.string().optional().describe("User ID to add as a member. Omit to join the network yourself. Get user IDs from read_user_contexts(query=name) or read_network_memberships."),
476
+ networkId: z.string().optional().describe("Network UUID to add the member to. Get from read_networks. Defaults to the scoped index in network-scoped chats."),
485
477
  }),
486
478
  handler: async ({ context, query }) => {
487
- const scopedNetworkId = focusedNetworkId(context);
488
- const scopedIndexLabel = focusedNetworkLabel(context);
489
- const networkId = query.networkId?.trim() || scopedNetworkId;
479
+ const networkId = query.networkId?.trim() || context.networkId;
490
480
  const targetUserId = query.userId?.trim() || context.userId;
491
481
  if (!networkId || !UUID_REGEX.test(networkId)) {
492
- return error("Invalid index ID format. Use the exact UUID from read_networks.");
482
+ return error("Invalid network ID format. Use the exact UUID from read_networks.");
493
483
  }
494
- // Strict scope enforcement: when chat is index-scoped, only allow adding to that index
495
- if (scopedNetworkId && networkId !== scopedNetworkId) {
496
- return error(`This chat is scoped to ${scopedIndexLabel}. You can only add members to this community.`);
484
+ // Strict scope enforcement: when chat is network-scoped, only allow adding to that index
485
+ if (context.networkId && networkId !== context.networkId) {
486
+ return error(`This chat is scoped to ${context.indexName ?? 'this network'}. You can only add members to this community.`);
497
487
  }
498
488
  const _createMembershipGraphStart = Date.now();
499
489
  const _createMembershipTraceEmitter = requestContext.getStore()?.traceEmitter;
@@ -522,20 +512,18 @@ export function createNetworkTools(defineTool, deps) {
522
512
  });
523
513
  const deleteNetworkMembership = defineTool({
524
514
  name: "delete_network_membership",
525
- description: "Removes a user from an index (community). After removal, the user's intents are unlinked from this index " +
515
+ description: "Removes a user from a network (community). After removal, the user's intents are unlinked from this network " +
526
516
  "and they can no longer participate in opportunity discovery within it.\n\n" +
527
- "**Permissions:** Only the index owner can remove members. The owner themselves cannot be removed (delete the index instead).\n\n" +
528
- "**When to use:** When an index owner wants to remove a member from the community. " +
517
+ "**Permissions:** Only the network owner can remove members. The owner themselves cannot be removed (delete the network instead).\n\n" +
518
+ "**When to use:** When a network owner wants to remove a member from the community. " +
529
519
  "Use read_network_memberships(networkId) first to get the userId of the member to remove.\n\n" +
530
520
  "**Returns:** Confirmation that the member was removed.",
531
521
  querySchema: z.object({
532
- userId: z.string().describe("User ID of the member to remove. Get from read_network_memberships(networkId). Cannot be the index owner."),
533
- networkId: z.string().optional().describe("Index UUID to remove the member from. Get from read_networks. Defaults to the scoped index in index-scoped chats."),
522
+ userId: z.string().describe("User ID of the member to remove. Get from read_network_memberships(networkId). Cannot be the network owner."),
523
+ networkId: z.string().optional().describe("Network UUID to remove the member from. Get from read_networks. Defaults to the scoped index in network-scoped chats."),
534
524
  }),
535
525
  handler: async ({ context, query }) => {
536
- const scopedNetworkId = focusedNetworkId(context);
537
- const scopedIndexLabel = focusedNetworkLabel(context);
538
- const networkId = query.networkId?.trim() || scopedNetworkId;
526
+ const networkId = query.networkId?.trim() || context.networkId;
539
527
  const targetUserId = query.userId?.trim();
540
528
  if (!networkId || !UUID_REGEX.test(networkId)) {
541
529
  return error("Valid networkId required. Use the exact UUID from read_networks.");
@@ -543,9 +531,9 @@ export function createNetworkTools(defineTool, deps) {
543
531
  if (!targetUserId) {
544
532
  return error("userId is required.");
545
533
  }
546
- // Strict scope enforcement: when chat is index-scoped, only allow that index
547
- if (scopedNetworkId && networkId !== scopedNetworkId) {
548
- return error(`This chat is scoped to ${scopedIndexLabel}. You can only manage members of this community.`);
534
+ // Strict scope enforcement: when chat is network-scoped, only allow that index
535
+ if (context.networkId && networkId !== context.networkId) {
536
+ return error(`This chat is scoped to ${context.indexName ?? 'this network'}. You can only manage members of this community.`);
549
537
  }
550
538
  const _deleteMembershipGraphStart = Date.now();
551
539
  const _deleteMembershipTraceEmitter = requestContext.getStore()?.traceEmitter;