@indexnetwork/protocol 4.3.3-rc.308.1 → 4.3.4-rc.310.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/IMPLEMENTATION.md +2 -2
  2. package/dist/chat/chat.agent.d.ts +1 -1
  3. package/dist/chat/chat.agent.d.ts.map +1 -1
  4. package/dist/chat/chat.agent.js +12 -2
  5. package/dist/chat/chat.agent.js.map +1 -1
  6. package/dist/chat/chat.graph.d.ts +16 -0
  7. package/dist/chat/chat.graph.d.ts.map +1 -1
  8. package/dist/chat/chat.graph.js +5 -2
  9. package/dist/chat/chat.graph.js.map +1 -1
  10. package/dist/chat/chat.prompt.d.ts.map +1 -1
  11. package/dist/chat/chat.prompt.js +44 -37
  12. package/dist/chat/chat.prompt.js.map +1 -1
  13. package/dist/chat/chat.prompt.modules.js +8 -8
  14. package/dist/chat/chat.prompt.modules.js.map +1 -1
  15. package/dist/chat/chat.state.d.ts +7 -4
  16. package/dist/chat/chat.state.d.ts.map +1 -1
  17. package/dist/chat/chat.state.js +12 -3
  18. package/dist/chat/chat.state.js.map +1 -1
  19. package/dist/chat/chat.streamer.d.ts +5 -0
  20. package/dist/chat/chat.streamer.d.ts.map +1 -1
  21. package/dist/chat/chat.streamer.js +13 -4
  22. package/dist/chat/chat.streamer.js.map +1 -1
  23. package/dist/chat/tests/chat.graph.mocks.d.ts +1 -1
  24. package/dist/chat/tests/chat.graph.mocks.js +1 -1
  25. package/dist/chat/tests/chat.graph.mocks.js.map +1 -1
  26. package/dist/contact/contact.tools.js +6 -6
  27. package/dist/contact/contact.tools.js.map +1 -1
  28. package/dist/enrichment/enrichment.tools.d.ts.map +1 -1
  29. package/dist/enrichment/enrichment.tools.js +23 -26
  30. package/dist/enrichment/enrichment.tools.js.map +1 -1
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +1 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/integration/integration.tools.js +1 -1
  36. package/dist/integration/integration.tools.js.map +1 -1
  37. package/dist/intent/intent.graph.d.ts.map +1 -1
  38. package/dist/intent/intent.graph.js +9 -9
  39. package/dist/intent/intent.graph.js.map +1 -1
  40. package/dist/intent/intent.indexer.d.ts +1 -1
  41. package/dist/intent/intent.indexer.js +4 -4
  42. package/dist/intent/intent.indexer.js.map +1 -1
  43. package/dist/intent/intent.state.d.ts +8 -8
  44. package/dist/intent/intent.state.js +8 -8
  45. package/dist/intent/intent.state.js.map +1 -1
  46. package/dist/intent/intent.tools.d.ts.map +1 -1
  47. package/dist/intent/intent.tools.js +87 -70
  48. package/dist/intent/intent.tools.js.map +1 -1
  49. package/dist/maintenance/maintenance.graph.d.ts +2 -2
  50. package/dist/maintenance/maintenance.graph.d.ts.map +1 -1
  51. package/dist/maintenance/maintenance.graph.js.map +1 -1
  52. package/dist/mcp/mcp.server.d.ts +5 -7
  53. package/dist/mcp/mcp.server.d.ts.map +1 -1
  54. package/dist/mcp/mcp.server.js +11 -20
  55. package/dist/mcp/mcp.server.js.map +1 -1
  56. package/dist/negotiation/negotiation.tools.d.ts.map +1 -1
  57. package/dist/negotiation/negotiation.tools.js +11 -7
  58. package/dist/negotiation/negotiation.tools.js.map +1 -1
  59. package/dist/network/indexer/indexer.graph.d.ts +2 -2
  60. package/dist/network/indexer/indexer.graph.d.ts.map +1 -1
  61. package/dist/network/indexer/indexer.graph.js +13 -13
  62. package/dist/network/indexer/indexer.graph.js.map +1 -1
  63. package/dist/network/indexer/indexer.state.d.ts +3 -3
  64. package/dist/network/indexer/indexer.state.d.ts.map +1 -1
  65. package/dist/network/indexer/indexer.state.js +4 -4
  66. package/dist/network/indexer/indexer.state.js.map +1 -1
  67. package/dist/network/membership/membership.graph.d.ts +1 -1
  68. package/dist/network/membership/membership.graph.js +21 -21
  69. package/dist/network/membership/membership.graph.js.map +1 -1
  70. package/dist/network/membership/membership.state.d.ts +2 -2
  71. package/dist/network/membership/membership.state.js +2 -2
  72. package/dist/network/membership/membership.state.js.map +1 -1
  73. package/dist/network/network.graph.d.ts +1 -1
  74. package/dist/network/network.graph.d.ts.map +1 -1
  75. package/dist/network/network.graph.js +13 -13
  76. package/dist/network/network.graph.js.map +1 -1
  77. package/dist/network/network.state.d.ts +3 -3
  78. package/dist/network/network.state.d.ts.map +1 -1
  79. package/dist/network/network.state.js +3 -3
  80. package/dist/network/network.state.js.map +1 -1
  81. package/dist/network/network.tools.js +68 -68
  82. package/dist/network/network.tools.js.map +1 -1
  83. package/dist/opportunity/opportunity.discover.d.ts +1 -1
  84. package/dist/opportunity/opportunity.discover.d.ts.map +1 -1
  85. package/dist/opportunity/opportunity.discover.js +1 -1
  86. package/dist/opportunity/opportunity.discover.js.map +1 -1
  87. package/dist/opportunity/opportunity.graph.js +11 -11
  88. package/dist/opportunity/opportunity.graph.js.map +1 -1
  89. package/dist/opportunity/opportunity.introducer.d.ts +3 -3
  90. package/dist/opportunity/opportunity.introducer.d.ts.map +1 -1
  91. package/dist/opportunity/opportunity.introducer.js +2 -2
  92. package/dist/opportunity/opportunity.introducer.js.map +1 -1
  93. package/dist/opportunity/opportunity.state.d.ts +1 -1
  94. package/dist/opportunity/opportunity.state.js +1 -1
  95. package/dist/opportunity/opportunity.state.js.map +1 -1
  96. package/dist/opportunity/opportunity.tools.d.ts.map +1 -1
  97. package/dist/opportunity/opportunity.tools.js +43 -47
  98. package/dist/opportunity/opportunity.tools.js.map +1 -1
  99. package/dist/premise/premise.indexer.d.ts +1 -1
  100. package/dist/premise/premise.indexer.js +5 -5
  101. package/dist/premise/premise.indexer.js.map +1 -1
  102. package/dist/questioner/questioner.tools.d.ts +1 -1
  103. package/dist/questioner/questioner.tools.d.ts.map +1 -1
  104. package/dist/questioner/questioner.tools.js +5 -6
  105. package/dist/questioner/questioner.tools.js.map +1 -1
  106. package/dist/shared/agent/tool.factory.d.ts +1 -1
  107. package/dist/shared/agent/tool.factory.d.ts.map +1 -1
  108. package/dist/shared/agent/tool.factory.js +10 -11
  109. package/dist/shared/agent/tool.factory.js.map +1 -1
  110. package/dist/shared/agent/tool.helpers.d.ts +6 -6
  111. package/dist/shared/agent/tool.helpers.d.ts.map +1 -1
  112. package/dist/shared/agent/tool.helpers.js +2 -2
  113. package/dist/shared/agent/tool.helpers.js.map +1 -1
  114. package/dist/shared/agent/tool.registry.js +1 -1
  115. package/dist/shared/agent/tool.registry.js.map +1 -1
  116. package/dist/shared/agent/tool.scope.d.ts +12 -0
  117. package/dist/shared/agent/tool.scope.d.ts.map +1 -1
  118. package/dist/shared/agent/tool.scope.js +14 -0
  119. package/dist/shared/agent/tool.scope.js.map +1 -1
  120. package/dist/shared/agent/utility.tools.js +20 -20
  121. package/dist/shared/agent/utility.tools.js.map +1 -1
  122. package/dist/shared/interfaces/auth.interface.d.ts +3 -3
  123. package/dist/shared/interfaces/auth.interface.js.map +1 -1
  124. package/dist/shared/interfaces/database.interface.d.ts +59 -59
  125. package/dist/shared/interfaces/database.interface.d.ts.map +1 -1
  126. package/dist/shared/interfaces/database.interface.js.map +1 -1
  127. package/dist/shared/interfaces/discovery-run.interface.d.ts +1 -1
  128. package/dist/shared/interfaces/discovery-run.interface.d.ts.map +1 -1
  129. package/dist/shared/interfaces/discovery-run.interface.js.map +1 -1
  130. package/dist/shared/interfaces/embedder.interface.d.ts +2 -2
  131. package/dist/shared/interfaces/embedder.interface.d.ts.map +1 -1
  132. package/dist/shared/interfaces/embedder.interface.js.map +1 -1
  133. package/dist/shared/interfaces/enrichment-run.interface.d.ts +1 -1
  134. package/dist/shared/interfaces/enrichment-run.interface.d.ts.map +1 -1
  135. package/dist/shared/interfaces/enrichment-run.interface.js.map +1 -1
  136. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"intent.tools.d.ts","sourceRoot":"/","sources":["intent/intent.tools.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AA4D5E,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,qDAorBvE"}
1
+ {"version":3,"file":"intent.tools.d.ts","sourceRoot":"/","sources":["intent/intent.tools.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AA8D5E,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,qDAssBvE"}
@@ -4,6 +4,7 @@ import { protocolLogger } from "../shared/observability/protocol.logger.js";
4
4
  import { traceGraph } from "../shared/observability/trace.js";
5
5
  import { success, error, UUID_REGEX } from "../shared/agent/tool.helpers.js";
6
6
  import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
7
+ import { deriveAllowedNetworkIds, focusedNetworkId, focusedNetworkLabel } from "../shared/agent/tool.scope.js";
7
8
  const logger = protocolLogger("ChatTools:Intent");
8
9
  /**
9
10
  * Sanitize JSON string for use inside a markdown code fence (```). Escapes backticks
@@ -12,13 +13,14 @@ const logger = protocolLogger("ChatTools:Intent");
12
13
  function sanitizeJsonForCodeFence(json) {
13
14
  return json.replace(/`/g, "\\u0060");
14
15
  }
15
- /** When context is index-scoped, verifies the caller is still a member of that index. Returns error message or null. */
16
+ /** When context is network-scoped, verifies the caller is still a member of that index. Returns error message or null. */
16
17
  async function ensureScopedMembership(context, systemDb) {
17
- if (!context.networkId)
18
+ const scopedNetworkId = focusedNetworkId(context);
19
+ if (!scopedNetworkId)
18
20
  return null;
19
- const isMember = await systemDb.isNetworkMember(context.networkId, context.userId);
21
+ const isMember = await systemDb.isNetworkMember(scopedNetworkId, context.userId);
20
22
  if (!isMember) {
21
- return `This chat is scoped to ${context.indexName ?? 'this index'}. You are no longer a member of this community.`;
23
+ return `This chat is scoped to ${focusedNetworkLabel(context)}. You are no longer a member of this community.`;
22
24
  }
23
25
  return null;
24
26
  }
@@ -60,17 +62,17 @@ export function createIntentTools(defineTool, deps) {
60
62
  description: "Retrieves intents (signals of interest/need, e.g. 'Looking for a React developer in Berlin'). " +
61
63
  "Intents are the core unit of discovery — they represent what users are seeking and drive semantic matching for opportunities.\n\n" +
62
64
  "**Usage modes:**\n" +
63
- "- No parameters: returns the **caller's own** active intents. In an index-scoped chat the result is clamped to the reachable indexes (the bound index plus the user's personal index). In an unscoped chat the result spans all of the user's active intents. There is no implicit default to the scoped index — to browse the bound community's intents, pass `networkId` explicitly.\n" +
65
+ "- No parameters: returns the **caller's own** active intents. In an network-scoped chat the result is clamped to the reachable networks (the bound network plus the user's personal network). In an unscoped chat the result spans all of the user's active intents. There is no implicit default to the scoped network — to browse the bound community's intents, pass `networkId` explicitly.\n" +
64
66
  "- With networkId: returns **all members'** intents in that index (community browse path). Add userId to filter to one member.\n" +
65
- "- With userId in an index-scoped chat: reads that member's intents in the bound index. The target user must be a member of that index.\n" +
66
- "- With userId in an unscoped chat: only works for the current user (cannot read another user's global intents without an index scope).\n\n" +
67
- "**Workflow:** To explore what members of an index are looking for, first call read_network_memberships(networkId) to list members, " +
67
+ "- With userId in an network-scoped chat: reads that member's intents in the bound network. The target user must be a member of that index.\n" +
68
+ "- With userId in an unscoped chat: only works for the current user (cannot read another user's global intents without an network scope).\n\n" +
69
+ "**Workflow:** To explore what members of a network are looking for, first call read_network_memberships(networkId) to list members, " +
68
70
  "then read_intents(networkId) to see all intents in that community. " +
69
71
  "Each intent includes: id, description (payload), summary, confidence (0-1), inferenceType (explicit/implicit), status, and linked indexes.\n\n" +
70
72
  "**Returns:** Paginated list of intents with count. Use the intent IDs in subsequent calls to update_intent, delete_intent, or create_intent_index.",
71
73
  querySchema: z.object({
72
- networkId: z.string().optional().describe("Index UUID — filters intents to this index (community browse path: returns all members' intents). There is no implicit default in index-scoped chats; omit to get caller-owned intents across the reachable indexes, or pass the scoped index UUID to browse community members. Get index IDs from read_networks."),
73
- userId: z.string().optional().describe("User ID — filters to this user's intents. In an index-scoped chat, this reads that member's intents in the bound index (no networkId required). In an unscoped chat, only the current user is allowed without networkId; cross-user reads require an index scope. Omit for caller-owned intents."),
74
+ networkId: z.string().optional().describe("Network UUID — filters intents to this network (community browse path: returns all members' intents). There is no implicit default in network-scoped chats; omit to get caller-owned intents across the reachable networks, or pass the scoped network UUID to browse community members. Get network IDs from read_networks."),
75
+ userId: z.string().optional().describe("User ID — filters to this user's intents. In an network-scoped chat, this reads that member's intents in the bound network (no networkId required). In an unscoped chat, only the current user is allowed without networkId; cross-user reads require an network scope. Omit for caller-owned intents."),
74
76
  limit: z.number().int().min(1).max(100).optional().describe("Page size (1-100). Defaults to returning all results if omitted."),
75
77
  page: z.number().int().min(1).optional().describe("Page number (1-based). Only used when limit is also provided."),
76
78
  }),
@@ -79,6 +81,8 @@ export function createIntentTools(defineTool, deps) {
79
81
  if (scopeErr)
80
82
  return error(scopeErr);
81
83
  // Distinguish "explicit network browse" from "implicit scope-aware read"
84
+ const scopedNetworkId = focusedNetworkId(context);
85
+ const scopedIndexLabel = focusedNetworkLabel(context);
82
86
  const explicitNetworkId = query.networkId?.trim();
83
87
  const explicitUserId = query.userId?.trim();
84
88
  // Validate explicit networkId format
@@ -86,25 +90,24 @@ export function createIntentTools(defineTool, deps) {
86
90
  return error("Invalid network ID format.");
87
91
  }
88
92
  // Strict scope enforcement: in a scoped chat, the only allowed explicit
89
- // networkId is context.networkId itself. The chat's focus is the bound
90
- // network; cross-network browse must happen in a separate (unscoped)
91
- // chat or a chat scoped to that other network.
92
- if (context.networkId && explicitNetworkId && explicitNetworkId !== context.networkId) {
93
- return error(`This chat is scoped to ${context.indexName ?? 'this index'}. You can only read intents from this community.`);
94
- }
95
- // Cross-user read in scoped chat: target user must be a member of the scoped index
96
- if (context.networkId && explicitUserId && explicitUserId !== context.userId) {
97
- const isInScopedIndex = await deps.systemDb.isNetworkMember(context.networkId, explicitUserId);
93
+ // networkId is the envelope's scoped network. Cross-network browse must
94
+ // happen in a separate unscoped chat or a chat scoped to that network.
95
+ if (scopedNetworkId && explicitNetworkId && explicitNetworkId !== scopedNetworkId) {
96
+ return error(`This chat is scoped to ${scopedIndexLabel}. You can only read intents from this community.`);
97
+ }
98
+ // Cross-user read in scoped chat: target user must be a member of the scoped network
99
+ if (scopedNetworkId && explicitUserId && explicitUserId !== context.userId) {
100
+ const isInScopedIndex = await deps.systemDb.isNetworkMember(scopedNetworkId, explicitUserId);
98
101
  if (!isInScopedIndex) {
99
- return error(`This chat is scoped to ${context.indexName ?? 'this index'}. You can only read intents from members of this community.`);
102
+ return error(`This chat is scoped to ${scopedIndexLabel}. You can only read intents from members of this community.`);
100
103
  }
101
104
  }
102
- // Cross-user global read is disallowed without an index scope
103
- if (!explicitNetworkId && !context.networkId && explicitUserId && explicitUserId !== context.userId) {
105
+ // Cross-user global read is disallowed without an network scope
106
+ if (!explicitNetworkId && !scopedNetworkId && explicitUserId && explicitUserId !== context.userId) {
104
107
  return error("Cannot read another user's global intents. Use networkId to scope to a shared network.");
105
108
  }
106
109
  // Membership check for explicit cross-network reads in unscoped chats
107
- if (!context.networkId && explicitNetworkId) {
110
+ if (!scopedNetworkId && explicitNetworkId) {
108
111
  const callerIsMember = await deps.systemDb.isNetworkMember(explicitNetworkId, context.userId);
109
112
  if (!callerIsMember) {
110
113
  return error("You can only read intents from indexes you are a member of.");
@@ -126,10 +129,10 @@ export function createIntentTools(defineTool, deps) {
126
129
  if (explicitUserId)
127
130
  graphInput.queryUserId = explicitUserId;
128
131
  }
129
- else if (explicitUserId && context.networkId) {
132
+ else if (explicitUserId && scopedNetworkId) {
130
133
  // Scoped chat + userId: implicit network is the chat's bound network.
131
134
  // Membership of the target user was verified above.
132
- graphInput.networkId = context.networkId;
135
+ graphInput.networkId = scopedNetworkId;
133
136
  graphInput.queryUserId = explicitUserId;
134
137
  }
135
138
  else if (explicitUserId) {
@@ -137,9 +140,13 @@ export function createIntentTools(defineTool, deps) {
137
140
  graphInput.queryUserId = explicitUserId;
138
141
  graphInput.allUserIntents = true;
139
142
  }
140
- else if (context.indexScope && context.indexScope.length > 0 && context.networkId) {
141
- // Scoped chat, implicit read: caller-only across reachable indexes.
142
- graphInput.indexScope = context.indexScope;
143
+ else if (scopedNetworkId) {
144
+ // Scoped chat, implicit read: caller-only across reachable networks.
145
+ graphInput.indexScope = deriveAllowedNetworkIds({
146
+ memberships: context.userNetworks,
147
+ scopeType: 'network',
148
+ scopeId: scopedNetworkId,
149
+ });
143
150
  }
144
151
  else {
145
152
  // Unscoped, implicit read: caller's global intents.
@@ -199,7 +206,7 @@ export function createIntentTools(defineTool, deps) {
199
206
  "to create_user_context are handled by that tool directly — do not scrape first.",
200
207
  querySchema: z.object({
201
208
  description: z.string().describe("A clear, specific description of what the user is looking for. Should be concept-based, not a raw URL. If the user shared a URL, scrape it first with scrape_url and pass the synthesized content here. Vague descriptions will be rejected — include what kind, what for, and/or timeframe."),
202
- networkId: z.string().optional().describe("Index UUID to link the intent to upon creation. Defaults to the scoped index in index-scoped chats. Get index IDs from read_networks. If omitted, the system auto-assigns to relevant indexes based on their prompts."),
209
+ networkId: z.string().optional().describe("Network UUID to link the intent to upon creation. Defaults to the scoped network in network-scoped chats. Get network IDs from read_networks. If omitted, the system auto-assigns to relevant networks based on their prompts."),
203
210
  autoApprove: z.boolean().optional().describe("When true, automatically persists all verified intents without returning proposal cards for manual approval. MCP agents SHOULD set this to true since there is no UI for card-based approval. Web chat agents should omit or set to false to get interactive proposal cards."),
204
211
  }),
205
212
  handler: async ({ context, query }) => {
@@ -209,13 +216,15 @@ export function createIntentTools(defineTool, deps) {
209
216
  if (!query.description?.trim()) {
210
217
  return error("Description is required.");
211
218
  }
219
+ const scopedNetworkId = focusedNetworkId(context);
220
+ const scopedIndexLabel = focusedNetworkLabel(context);
212
221
  // Strict scope enforcement
213
- if (context.networkId && query.networkId?.trim() && query.networkId.trim() !== context.networkId) {
214
- return error(`This chat is scoped to ${context.indexName ?? 'this index'}. You can only create intents in this community.`);
222
+ if (scopedNetworkId && query.networkId?.trim() && query.networkId.trim() !== scopedNetworkId) {
223
+ return error(`This chat is scoped to ${scopedIndexLabel}. You can only create intents in this community.`);
215
224
  }
216
- const effectiveIndexId = context.networkId || query.networkId?.trim() || undefined;
217
- const scopeEnvelope = context.scopeType && context.scopeId
218
- ? { scopeType: context.scopeType, scopeId: context.scopeId }
225
+ const effectiveIndexId = scopedNetworkId || query.networkId?.trim() || undefined;
226
+ const scopeEnvelope = scopedNetworkId
227
+ ? { scopeType: 'network', scopeId: scopedNetworkId }
219
228
  : {};
220
229
  // Fetch profile (the intent graph needs it for inference)
221
230
  const _profileGraphStart1 = Date.now();
@@ -375,12 +384,14 @@ export function createIntentTools(defineTool, deps) {
375
384
  if (intent.archivedAt) {
376
385
  return error("This intent is archived and cannot be updated. Create a new intent instead.");
377
386
  }
378
- // Strict scope enforcement: when chat is index-scoped, verify intent is linked to that index
379
- if (context.networkId) {
387
+ const scopedNetworkId = focusedNetworkId(context);
388
+ const scopedIndexLabel = focusedNetworkLabel(context);
389
+ // Strict scope enforcement: when chat is network-scoped, verify intent is linked to that index
390
+ if (scopedNetworkId) {
380
391
  const db = deps.userDb;
381
392
  const intentNetworks = await db.getNetworkIdsForIntent(intentId);
382
- if (!intentNetworks.includes(context.networkId)) {
383
- return error(`This chat is scoped to ${context.indexName ?? 'this index'}. You can only update intents linked to this community.`);
393
+ if (!intentNetworks.includes(scopedNetworkId)) {
394
+ return error(`This chat is scoped to ${scopedIndexLabel}. You can only update intents linked to this community.`);
384
395
  }
385
396
  }
386
397
  const _profileGraphStart2 = Date.now();
@@ -396,8 +407,7 @@ export function createIntentTools(defineTool, deps) {
396
407
  operationMode: 'update',
397
408
  inputContent: query.description,
398
409
  targetIntentIds: [intentId],
399
- ...(context.networkId && { networkId: context.networkId }),
400
- ...(context.scopeType && context.scopeId ? { scopeType: context.scopeType, scopeId: context.scopeId } : {}),
410
+ ...(scopedNetworkId && { networkId: scopedNetworkId, scopeType: 'network', scopeId: scopedNetworkId }),
401
411
  }));
402
412
  const _intentGraphMs2 = Date.now() - _intentGraphStart2;
403
413
  if (result.executionResults?.some((r) => !r.success)) {
@@ -440,12 +450,14 @@ export function createIntentTools(defineTool, deps) {
440
450
  if (!intent || intent.userId !== context.userId) {
441
451
  return error("Intent not found or you can only delete your own intents.");
442
452
  }
443
- // Strict scope enforcement: when chat is index-scoped, verify intent is linked to that index
444
- if (context.networkId) {
453
+ const scopedNetworkId = focusedNetworkId(context);
454
+ const scopedIndexLabel = focusedNetworkLabel(context);
455
+ // Strict scope enforcement: when chat is network-scoped, verify intent is linked to that index
456
+ if (scopedNetworkId) {
445
457
  const db = deps.userDb;
446
458
  const intentNetworks = await db.getNetworkIdsForIntent(intentId);
447
- if (!intentNetworks.includes(context.networkId)) {
448
- return error(`This chat is scoped to ${context.indexName ?? 'this index'}. You can only delete intents linked to this community.`);
459
+ if (!intentNetworks.includes(scopedNetworkId)) {
460
+ return error(`This chat is scoped to ${scopedIndexLabel}. You can only delete intents linked to this community.`);
449
461
  }
450
462
  }
451
463
  const _deleteIntentGraphStart = Date.now();
@@ -454,8 +466,7 @@ export function createIntentTools(defineTool, deps) {
454
466
  userProfile: "",
455
467
  operationMode: 'delete',
456
468
  targetIntentIds: [intentId],
457
- ...(context.networkId && { networkId: context.networkId }),
458
- ...(context.scopeType && context.scopeId ? { scopeType: context.scopeType, scopeId: context.scopeId } : {}),
469
+ ...(scopedNetworkId && { networkId: scopedNetworkId, scopeType: 'network', scopeId: scopedNetworkId }),
459
470
  }));
460
471
  const _deleteIntentGraphMs = Date.now() - _deleteIntentGraphStart;
461
472
  if (result.executionResults?.some((r) => !r.success)) {
@@ -472,27 +483,29 @@ export function createIntentTools(defineTool, deps) {
472
483
  // ─────────────────────────────────────────────────────────────────────────────
473
484
  const createIntentIndex = defineTool({
474
485
  name: "create_intent_index",
475
- description: "Manually links an intent to an index (community), making it visible to other members and eligible for opportunity discovery within that index. " +
486
+ description: "Manually links an intent to a network (community), making it visible to other members and eligible for opportunity discovery within that index. " +
476
487
  "Normally intents are auto-assigned to relevant indexes on creation, but use this to explicitly add an intent to an additional index.\n\n" +
477
488
  "**When to use:** When the user wants to share an existing intent with a specific community they belong to, " +
478
- "or when auto-assignment missed an index the user considers relevant.\n\n" +
489
+ "or when auto-assignment missed a network the user considers relevant.\n\n" +
479
490
  "**Returns:** Confirmation that the link was created. The intent will now appear in that index's intent list and participate in discovery within that community.",
480
491
  querySchema: z.object({
481
492
  intentId: z.string().describe("The UUID of the intent to link. Get this from read_intents results."),
482
- networkId: z.string().optional().describe("The UUID of the index to link the intent to. Get this from read_networks. Defaults to the scoped index in index-scoped chats."),
493
+ networkId: z.string().optional().describe("The UUID of the network to link the intent to. Get this from read_networks. Defaults to the scoped network in network-scoped chats."),
483
494
  }),
484
495
  handler: async ({ context, query }) => {
485
496
  const scopeErr = await ensureScopedMembership(context, deps.systemDb);
486
497
  if (scopeErr)
487
498
  return error(scopeErr);
499
+ const scopedNetworkId = focusedNetworkId(context);
500
+ const scopedIndexLabel = focusedNetworkLabel(context);
488
501
  const intentId = query.intentId?.trim() ?? "";
489
- const networkId = query.networkId?.trim() || context.networkId || "";
502
+ const networkId = query.networkId?.trim() || scopedNetworkId || "";
490
503
  if (!UUID_REGEX.test(intentId) || !UUID_REGEX.test(networkId)) {
491
504
  return error("Invalid ID format. Both must be UUIDs.");
492
505
  }
493
- // Strict scope enforcement: when chat is index-scoped, only allow linking to that index
494
- if (context.networkId && networkId !== context.networkId) {
495
- return error(`This chat is scoped to ${context.indexName ?? 'this index'}. You can only link intents to this community.`);
506
+ // Strict scope enforcement: when chat is network-scoped, only allow linking to that index
507
+ if (scopedNetworkId && networkId !== scopedNetworkId) {
508
+ return error(`This chat is scoped to ${scopedIndexLabel}. You can only link intents to this community.`);
496
509
  }
497
510
  const _createIntentIndexGraphStart = Date.now();
498
511
  const result = await traceGraph("intent_network", () => invokeWithAbortSignal(graphs.intentIndex, {
@@ -524,21 +537,23 @@ export function createIntentTools(defineTool, deps) {
524
537
  "**Usage modes:**\n" +
525
538
  "- With networkId: lists all intents linked to that index. Add userId to filter to one member's intents in that index.\n" +
526
539
  "- With intentId + networkId: checks whether a specific intent is linked to a specific index.\n" +
527
- "- intentId alone requires a networkId (the system won't reveal all indexes an intent is in).\n\n" +
540
+ "- intentId alone requires a networkId (the system won't reveal all networks an intent is in).\n\n" +
528
541
  "**When to use:** To audit which intents are active in a community, verify an intent's index assignment before unlinking, " +
529
542
  "or check if a newly created intent was auto-assigned to the expected index.\n\n" +
530
- "**Returns:** List of intent-index links with relevancy scores (0-1, how well the intent fits the index's purpose).",
543
+ "**Returns:** List of intent-network links with relevancy scores (0-1, how well the intent fits the network's purpose).",
531
544
  querySchema: z.object({
532
545
  intentId: z.string().optional().describe("Intent UUID — check if this specific intent is linked to the specified index. Must be combined with networkId."),
533
- networkId: z.string().optional().describe("Index UUID — list all intents linked to this index. Get this from read_networks. Defaults to scoped index in index-scoped chats."),
546
+ networkId: z.string().optional().describe("Network UUID — list all intents linked to this network. Get this from read_networks. Defaults to scoped network in network-scoped chats."),
534
547
  userId: z.string().optional().describe("Filter results to this user's intents within the specified index. Omit to see all members' intents."),
535
548
  }),
536
549
  handler: async ({ context, query }) => {
537
550
  const scopeErr = await ensureScopedMembership(context, deps.systemDb);
538
551
  if (scopeErr)
539
552
  return error(scopeErr);
553
+ const scopedNetworkId = focusedNetworkId(context);
554
+ const scopedIndexLabel = focusedNetworkLabel(context);
540
555
  const intentId = query.intentId?.trim() || undefined;
541
- let networkId = query.networkId?.trim() || context.networkId || undefined;
556
+ let networkId = query.networkId?.trim() || scopedNetworkId || undefined;
542
557
  const queryUserId = query.userId?.trim() || undefined;
543
558
  if (intentId && !UUID_REGEX.test(intentId)) {
544
559
  return error("Invalid intent ID format.");
@@ -549,18 +564,18 @@ export function createIntentTools(defineTool, deps) {
549
564
  if (!intentId && !networkId) {
550
565
  return error("Provide networkId or intentId.");
551
566
  }
552
- // Strict scope enforcement: when chat is index-scoped, only allow querying that index
553
- if (context.networkId && networkId && networkId !== context.networkId) {
554
- return error(`This chat is scoped to ${context.indexName ?? 'this index'}. You can only read intent links from this community.`);
567
+ // Strict scope enforcement: when chat is network-scoped, only allow querying that index
568
+ if (scopedNetworkId && networkId && networkId !== scopedNetworkId) {
569
+ return error(`This chat is scoped to ${scopedIndexLabel}. You can only read intent links from this community.`);
555
570
  }
556
571
  // When only intentId is provided, enforce scope - don't reveal all linked indexes
557
572
  if (intentId && !networkId) {
558
- if (context.networkId) {
559
- // When scoped, only check if intent is linked to the scoped index
560
- networkId = context.networkId;
573
+ if (scopedNetworkId) {
574
+ // When scoped, only check if intent is linked to the scoped network
575
+ networkId = scopedNetworkId;
561
576
  }
562
577
  else {
563
- // When unscoped, still don't reveal all indexes - require explicit networkId
578
+ // When unscoped, still don't reveal all networks - require explicit networkId
564
579
  return error("Please provide a networkId to check if the intent is linked to a specific network. Listing all linked networks is not supported.");
565
580
  }
566
581
  }
@@ -584,27 +599,29 @@ export function createIntentTools(defineTool, deps) {
584
599
  });
585
600
  const deleteIntentIndex = defineTool({
586
601
  name: "delete_intent_index",
587
- description: "Removes the link between an intent and an index. The intent itself is NOT deleted — it just stops being visible in that community " +
602
+ description: "Removes the link between an intent and a network. The intent itself is NOT deleted — it just stops being visible in that community " +
588
603
  "and no longer participates in opportunity discovery within that index. The intent may still be linked to other indexes.\n\n" +
589
604
  "**When to use:** When the user wants to withdraw an intent from a specific community without archiving it entirely. " +
590
605
  "Use read_intent_indexes first to verify the link exists.\n\n" +
591
606
  "**Returns:** Confirmation that the link was removed. To fully remove an intent, use delete_intent instead.",
592
607
  querySchema: z.object({
593
608
  intentId: z.string().describe("The UUID of the intent to unlink. Get this from read_intents or read_intent_indexes."),
594
- networkId: z.string().optional().describe("The UUID of the index to unlink from. Get this from read_networks. Defaults to the scoped index in index-scoped chats."),
609
+ networkId: z.string().optional().describe("The UUID of the network to unlink from. Get this from read_networks. Defaults to the scoped network in network-scoped chats."),
595
610
  }),
596
611
  handler: async ({ context, query }) => {
597
612
  const scopeErr = await ensureScopedMembership(context, deps.systemDb);
598
613
  if (scopeErr)
599
614
  return error(scopeErr);
615
+ const scopedNetworkId = focusedNetworkId(context);
616
+ const scopedIndexLabel = focusedNetworkLabel(context);
600
617
  const intentId = query.intentId?.trim() ?? "";
601
- const networkId = query.networkId?.trim() || context.networkId || "";
618
+ const networkId = query.networkId?.trim() || scopedNetworkId || "";
602
619
  if (!UUID_REGEX.test(intentId) || !UUID_REGEX.test(networkId)) {
603
620
  return error("Invalid ID format. Both must be UUIDs.");
604
621
  }
605
- // Strict scope enforcement: when chat is index-scoped, only allow unlinking from that index
606
- if (context.networkId && networkId !== context.networkId) {
607
- return error(`This chat is scoped to ${context.indexName ?? 'this index'}. You can only unlink intents from this community.`);
622
+ // Strict scope enforcement: when chat is network-scoped, only allow unlinking from that index
623
+ if (scopedNetworkId && networkId !== scopedNetworkId) {
624
+ return error(`This chat is scoped to ${scopedIndexLabel}. You can only unlink intents from this community.`);
608
625
  }
609
626
  const _deleteIntentIndexGraphStart = Date.now();
610
627
  const result = await traceGraph("intent_network", () => invokeWithAbortSignal(graphs.intentIndex, {