@indexnetwork/protocol 4.3.4-rc.310.1 → 4.3.4-rc.312.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 (61) hide show
  1. package/dist/chat/chat.graph.d.ts +8 -8
  2. package/dist/chat/chat.prompt.d.ts.map +1 -1
  3. package/dist/chat/chat.prompt.js +18 -8
  4. package/dist/chat/chat.prompt.js.map +1 -1
  5. package/dist/chat/chat.state.d.ts +4 -3
  6. package/dist/chat/chat.state.d.ts.map +1 -1
  7. package/dist/chat/chat.state.js +2 -2
  8. package/dist/chat/chat.state.js.map +1 -1
  9. package/dist/chat/chat.streamer.d.ts +3 -2
  10. package/dist/chat/chat.streamer.d.ts.map +1 -1
  11. package/dist/chat/chat.streamer.js +2 -2
  12. package/dist/chat/chat.streamer.js.map +1 -1
  13. package/dist/intent/intent.graph.d.ts +5 -5
  14. package/dist/intent/intent.state.d.ts +2 -1
  15. package/dist/intent/intent.state.d.ts.map +1 -1
  16. package/dist/intent/intent.tools.d.ts.map +1 -1
  17. package/dist/intent/intent.tools.js +54 -2
  18. package/dist/intent/intent.tools.js.map +1 -1
  19. package/dist/opportunity/feed/feed.graph.d.ts +10 -0
  20. package/dist/opportunity/feed/feed.graph.d.ts.map +1 -1
  21. package/dist/opportunity/feed/feed.graph.js +4 -0
  22. package/dist/opportunity/feed/feed.graph.js.map +1 -1
  23. package/dist/opportunity/feed/feed.state.d.ts +2 -0
  24. package/dist/opportunity/feed/feed.state.d.ts.map +1 -1
  25. package/dist/opportunity/feed/feed.state.js +8 -0
  26. package/dist/opportunity/feed/feed.state.js.map +1 -1
  27. package/dist/opportunity/opportunity.graph.d.ts +6 -6
  28. package/dist/opportunity/opportunity.pending-questions.d.ts +5 -0
  29. package/dist/opportunity/opportunity.pending-questions.d.ts.map +1 -1
  30. package/dist/opportunity/opportunity.pending-questions.js +2 -1
  31. package/dist/opportunity/opportunity.pending-questions.js.map +1 -1
  32. package/dist/opportunity/opportunity.state.d.ts +1 -1
  33. package/dist/opportunity/opportunity.tools.d.ts.map +1 -1
  34. package/dist/opportunity/opportunity.tools.js +80 -7
  35. package/dist/opportunity/opportunity.tools.js.map +1 -1
  36. package/dist/premise/premise.graph.d.ts +4 -4
  37. package/dist/premise/premise.state.d.ts +2 -1
  38. package/dist/premise/premise.state.d.ts.map +1 -1
  39. package/dist/questioner/questioner.tools.d.ts.map +1 -1
  40. package/dist/questioner/questioner.tools.js +21 -8
  41. package/dist/questioner/questioner.tools.js.map +1 -1
  42. package/dist/shared/agent/tool.factory.d.ts.map +1 -1
  43. package/dist/shared/agent/tool.factory.js +4 -0
  44. package/dist/shared/agent/tool.factory.js.map +1 -1
  45. package/dist/shared/agent/tool.helpers.d.ts +22 -6
  46. package/dist/shared/agent/tool.helpers.d.ts.map +1 -1
  47. package/dist/shared/agent/tool.helpers.js.map +1 -1
  48. package/dist/shared/agent/tool.scope.d.ts +7 -1
  49. package/dist/shared/agent/tool.scope.d.ts.map +1 -1
  50. package/dist/shared/agent/tool.scope.js +10 -0
  51. package/dist/shared/agent/tool.scope.js.map +1 -1
  52. package/dist/shared/hyde/hyde.graph.d.ts +6 -6
  53. package/dist/shared/hyde/hyde.state.d.ts +2 -2
  54. package/dist/shared/interfaces/database.interface.d.ts +3 -0
  55. package/dist/shared/interfaces/database.interface.d.ts.map +1 -1
  56. package/dist/shared/interfaces/database.interface.js.map +1 -1
  57. package/dist/shared/interfaces/questioner.interface.d.ts +3 -0
  58. package/dist/shared/interfaces/questioner.interface.d.ts.map +1 -1
  59. package/dist/shared/interfaces/questioner.interface.js.map +1 -1
  60. package/dist/shared/schemas/network-assignment.schema.d.ts +2 -2
  61. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { requestContext } from "../shared/observability/request-context.js";
3
3
  import { success, error, UUID_REGEX } from "../shared/agent/tool.helpers.js";
4
- import { deriveDiscoveryNetworkIds, focusedNetworkId, focusedNetworkLabel } from "../shared/agent/tool.scope.js";
4
+ import { deriveDiscoveryNetworkIds, focusedIntentId, focusedNetworkId, focusedNetworkLabel } from "../shared/agent/tool.scope.js";
5
5
  import { MINIMAL_MAIN_TEXT_MAX_CHARS, getPrimaryActionLabel, SECONDARY_ACTION_LABEL } from "./opportunity.labels.js";
6
6
  import { viewerCentricCardSummary, narratorRemarkFromReasoning, stripUuids } from "./opportunity.presentation.js";
7
7
  import { runDiscoverFromQuery, continueDiscovery } from "./opportunity.discover.js";
@@ -177,6 +177,13 @@ const UPDATE_OPPORTUNITY_BLOCKED_STATUSES = new Set([
177
177
  "expired",
178
178
  "negotiating",
179
179
  ]);
180
+ function matchesSelectedIntentScope(opportunity, viewerId, scope) {
181
+ if (scope?.scopeType !== 'intent' || !scope.scopeId)
182
+ return true;
183
+ if (opportunity.detection?.triggeredBy === scope.scopeId)
184
+ return true;
185
+ return opportunity.actors.some((actor) => actor.userId === viewerId && actor.intent === scope.scopeId);
186
+ }
180
187
  /**
181
188
  * Maximum number of opportunity cards to show per chat response.
182
189
  * Sized for `selectByComposition` to fill both feed buckets — up to 3
@@ -515,7 +522,7 @@ export function createOpportunityTools(defineTool, deps) {
515
522
  "'complementary skills for a startup'). Helps the evaluator produce better match reasoning."),
516
523
  }),
517
524
  handler: async ({ context, query }) => {
518
- const scopedNetworkId = focusedNetworkId(context);
525
+ const scopedNetworkId = focusedNetworkId(context) ?? context.networkId?.trim();
519
526
  const scopedIndexLabel = focusedNetworkLabel(context);
520
527
  // Strict scope enforcement: when chat is network-scoped, only allow that index
521
528
  if (scopedNetworkId &&
@@ -566,7 +573,7 @@ export function createOpportunityTools(defineTool, deps) {
566
573
  userId: context.userId,
567
574
  userName: context.userName,
568
575
  userEmail: context.userEmail,
569
- ...(scopedNetworkId ? { scopeType: 'network', scopeId: scopedNetworkId } : {}),
576
+ ...(context.scopeType && context.scopeId ? { scopeType: context.scopeType, scopeId: context.scopeId } : scopedNetworkId ? { scopeType: 'network', scopeId: scopedNetworkId } : {}),
570
577
  ...(context.indexName ? { indexName: context.indexName } : {}),
571
578
  ...(context.sessionId ? { sessionId: context.sessionId } : {}),
572
579
  ...(context.agentId ? { agentId: context.agentId } : {}),
@@ -895,10 +902,15 @@ export function createOpportunityTools(defineTool, deps) {
895
902
  const toolDebugSteps = [
896
903
  { step: "resolve_index_scope", detail: `${indexScope.length} index(es)` },
897
904
  ];
898
- const triggerIntentId = query.intentId?.trim() || undefined;
899
- if (triggerIntentId != null && !UUID_REGEX.test(triggerIntentId)) {
905
+ const contextIntentId = focusedIntentId(context);
906
+ const requestedIntentId = query.intentId?.trim() || undefined;
907
+ if (requestedIntentId != null && !UUID_REGEX.test(requestedIntentId)) {
900
908
  return error("Invalid intent ID format.");
901
909
  }
910
+ if (contextIntentId && requestedIntentId && requestedIntentId !== contextIntentId) {
911
+ return error("This chat is scoped to a different intent.");
912
+ }
913
+ const triggerIntentId = contextIntentId ?? requestedIntentId;
902
914
  if (query.introTargetUserId?.trim() && query.introTargetUserId.trim() === context.userId) {
903
915
  return error("You cannot discover introductions for yourself. Try regular discovery instead.");
904
916
  }
@@ -965,6 +977,7 @@ export function createOpportunityTools(defineTool, deps) {
965
977
  userId: context.userId,
966
978
  sourceType: 'discovery',
967
979
  ...(context.scopeType === 'network' && context.scopeId ? { networkId: context.scopeId } : {}),
980
+ ...(contextIntentId ? { scopeType: 'intent', scopeId: contextIntentId } : {}),
968
981
  surfacedQuestionIds: new Set(), // Dedup handled at chat.agent level
969
982
  });
970
983
  const pendingQuestions = pendingQuestionResult.questions;
@@ -1301,13 +1314,21 @@ export function createOpportunityTools(defineTool, deps) {
1301
1314
  .string()
1302
1315
  .optional()
1303
1316
  .describe("Network UUID to filter opportunities to a specific community. Get from read_networks. Defaults to the scoped network in network-scoped chats. Omit to see opportunities across all networks."),
1317
+ scopeType: z
1318
+ .enum(['intent'])
1319
+ .optional()
1320
+ .describe("Optional selected scope type. Use 'intent' to narrow listed opportunities to a selected intent."),
1321
+ scopeId: z
1322
+ .string()
1323
+ .optional()
1324
+ .describe("Selected intent UUID when scopeType is 'intent'. Ignored only when absent."),
1304
1325
  includeDigestMarkers: z
1305
1326
  .boolean()
1306
1327
  .optional()
1307
1328
  .describe("Internal scheduled-digest mode only. When true, includes hidden delivery markers so the digest send pass can confirm only edited-in opportunities."),
1308
1329
  }),
1309
1330
  handler: async ({ context, query }) => {
1310
- const scopedNetworkId = focusedNetworkId(context);
1331
+ const scopedNetworkId = focusedNetworkId(context) ?? context.networkId?.trim();
1311
1332
  const scopedIndexLabel = focusedNetworkLabel(context);
1312
1333
  // Strict scope enforcement: when chat is network-scoped, only allow that index
1313
1334
  if (scopedNetworkId &&
@@ -1319,6 +1340,25 @@ export function createOpportunityTools(defineTool, deps) {
1319
1340
  if (effectiveIndexId && !UUID_REGEX.test(effectiveIndexId)) {
1320
1341
  return error("Invalid network ID format.");
1321
1342
  }
1343
+ const contextIntentId = focusedIntentId(context);
1344
+ const rawScopeId = query.scopeId?.trim() || undefined;
1345
+ if (query.scopeType === 'intent' && !rawScopeId) {
1346
+ return error("scopeId required when scopeType is intent.");
1347
+ }
1348
+ if (!query.scopeType && rawScopeId) {
1349
+ return error("scopeType=intent required when scopeId is provided.");
1350
+ }
1351
+ if (rawScopeId && !UUID_REGEX.test(rawScopeId)) {
1352
+ return error("Invalid scope ID format.");
1353
+ }
1354
+ if (contextIntentId && rawScopeId && contextIntentId !== rawScopeId) {
1355
+ return error("This chat is scoped to a different intent.");
1356
+ }
1357
+ const effectiveIntentScope = contextIntentId
1358
+ ? { scopeType: 'intent', scopeId: contextIntentId }
1359
+ : query.scopeType === 'intent' && rawScopeId
1360
+ ? { scopeType: 'intent', scopeId: rawScopeId }
1361
+ : {};
1322
1362
  // The MCP/chat surface exposes actionable opportunities.
1323
1363
  // `latent` is included so the introducer-as-viewer can see their unapproved
1324
1364
  // connector-flow cards ("do you know someone for X?"). Other latent visibility
@@ -1331,6 +1371,7 @@ export function createOpportunityTools(defineTool, deps) {
1331
1371
  // sort order can fill the whole window and starve the other section.
1332
1372
  const fetched = await database.getOpportunitiesForUser(context.userId, {
1333
1373
  networkId: effectiveIndexId,
1374
+ ...effectiveIntentScope,
1334
1375
  statuses,
1335
1376
  limit: CHAT_FETCH_LIMIT,
1336
1377
  });
@@ -1399,8 +1440,10 @@ export function createOpportunityTools(defineTool, deps) {
1399
1440
  if (isDigestMode && deduped.length > 0) {
1400
1441
  const acceptedCounterpartIds = new Set();
1401
1442
  try {
1443
+ // effectiveIntentScope scopes the statuses: ["accepted"] suppression fetch.
1402
1444
  const acceptedOpps = await database.getOpportunitiesForUser(context.userId, {
1403
1445
  ...(effectiveIndexId ? { networkId: effectiveIndexId } : {}),
1446
+ ...effectiveIntentScope,
1404
1447
  statuses: ["accepted"],
1405
1448
  limit: ACCEPTED_SUPPRESSION_FETCH_LIMIT,
1406
1449
  });
@@ -1789,12 +1832,39 @@ export function createOpportunityTools(defineTool, deps) {
1789
1832
  .enum(["pending", "accepted", "rejected", "expired"])
1790
1833
  .describe("New status: 'pending' = send the draft to the other party, 'accepted' = accept the connection, " +
1791
1834
  "'rejected' = decline, 'expired' = mark as timed out."),
1835
+ scopeType: z
1836
+ .enum(['intent'])
1837
+ .optional()
1838
+ .describe("Optional selected scope type. Use 'intent' to require this opportunity to belong to a selected intent."),
1839
+ scopeId: z
1840
+ .string()
1841
+ .optional()
1842
+ .describe("Selected intent UUID when scopeType is 'intent'. Must match the chat's focused intent when one exists."),
1792
1843
  }),
1793
1844
  handler: async ({ context, query }) => {
1794
1845
  const opportunityId = query.opportunityId?.trim();
1795
1846
  if (!opportunityId || !UUID_REGEX.test(opportunityId)) {
1796
1847
  return error("Valid opportunityId required.");
1797
1848
  }
1849
+ const contextIntentId = focusedIntentId(context);
1850
+ const rawScopeId = query.scopeId?.trim() || undefined;
1851
+ if (query.scopeType === 'intent' && !rawScopeId) {
1852
+ return error("scopeId required when scopeType is intent.");
1853
+ }
1854
+ if (!query.scopeType && rawScopeId) {
1855
+ return error("scopeType=intent required when scopeId is provided.");
1856
+ }
1857
+ if (rawScopeId && !UUID_REGEX.test(rawScopeId)) {
1858
+ return error("Invalid scope ID format.");
1859
+ }
1860
+ if (contextIntentId && rawScopeId && contextIntentId !== rawScopeId) {
1861
+ return error("This chat is scoped to a different intent.");
1862
+ }
1863
+ const effectiveIntentScope = contextIntentId
1864
+ ? { scopeType: 'intent', scopeId: contextIntentId }
1865
+ : query.scopeType === 'intent' && rawScopeId
1866
+ ? { scopeType: 'intent', scopeId: rawScopeId }
1867
+ : {};
1798
1868
  // Always fetch the opportunity — needed for actor guard and state machine
1799
1869
  const opportunity = await systemDb.getOpportunity(opportunityId);
1800
1870
  if (!opportunity) {
@@ -1816,13 +1886,16 @@ export function createOpportunityTools(defineTool, deps) {
1816
1886
  // Mirrors the per-actor filter in getOpportunitiesForUser — relying on
1817
1887
  // a focus-scope id or any-actor matches would let a counterpart's network
1818
1888
  // presence shadow a viewer whose own actor is elsewhere.
1819
- const scopedNetworkId = focusedNetworkId(context);
1889
+ const scopedNetworkId = focusedNetworkId(context) ?? context.networkId?.trim();
1820
1890
  if (scopedNetworkId) {
1821
1891
  const callerOnBoundNetwork = opportunity.actors?.some((a) => a.userId === context.userId && a.networkId === scopedNetworkId);
1822
1892
  if (!callerOnBoundNetwork) {
1823
1893
  return error("Opportunity not found.");
1824
1894
  }
1825
1895
  }
1896
+ if (!matchesSelectedIntentScope(opportunity, context.userId, effectiveIntentScope)) {
1897
+ return error("Opportunity not found.");
1898
+ }
1826
1899
  const isSend = query.status === "pending";
1827
1900
  const _updateGraphStart = Date.now();
1828
1901
  const _updateTraceEmitter = requestContext.getStore()?.traceEmitter;