@indexnetwork/protocol 3.7.0-rc.277.1 → 3.7.1-rc.279.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 (96) hide show
  1. package/dist/agent/agent.tools.js +1 -1
  2. package/dist/agent/agent.tools.js.map +1 -1
  3. package/dist/chat/chat.agent.d.ts +6 -6
  4. package/dist/chat/chat.agent.d.ts.map +1 -1
  5. package/dist/chat/chat.agent.js +10 -9
  6. package/dist/chat/chat.agent.js.map +1 -1
  7. package/dist/chat/chat.graph.d.ts.map +1 -1
  8. package/dist/chat/chat.graph.js +3 -12
  9. package/dist/chat/chat.graph.js.map +1 -1
  10. package/dist/chat/chat.interrupt.classifier.d.ts.map +1 -1
  11. package/dist/chat/chat.interrupt.classifier.js +1 -3
  12. package/dist/chat/chat.interrupt.classifier.js.map +1 -1
  13. package/dist/chat/chat.prompt.d.ts.map +1 -1
  14. package/dist/chat/chat.prompt.js +8 -7
  15. package/dist/chat/chat.prompt.js.map +1 -1
  16. package/dist/chat/chat.prompt.modules.d.ts.map +1 -1
  17. package/dist/chat/chat.prompt.modules.js +5 -0
  18. package/dist/chat/chat.prompt.modules.js.map +1 -1
  19. package/dist/chat/chat.suggester.js.map +1 -1
  20. package/dist/context/context.generator.d.ts +2 -0
  21. package/dist/context/context.generator.d.ts.map +1 -1
  22. package/dist/context/context.generator.js +8 -6
  23. package/dist/context/context.generator.js.map +1 -1
  24. package/dist/intent/intent.clarifier.d.ts +2 -0
  25. package/dist/intent/intent.clarifier.d.ts.map +1 -1
  26. package/dist/intent/intent.clarifier.js +9 -23
  27. package/dist/intent/intent.clarifier.js.map +1 -1
  28. package/dist/intent/intent.graph.d.ts.map +1 -1
  29. package/dist/intent/intent.graph.js +29 -26
  30. package/dist/intent/intent.graph.js.map +1 -1
  31. package/dist/intent/intent.tools.d.ts.map +1 -1
  32. package/dist/intent/intent.tools.js +18 -48
  33. package/dist/intent/intent.tools.js.map +1 -1
  34. package/dist/maintenance/maintenance.graph.d.ts.map +1 -1
  35. package/dist/maintenance/maintenance.graph.js +1 -2
  36. package/dist/maintenance/maintenance.graph.js.map +1 -1
  37. package/dist/mcp/mcp.server.d.ts.map +1 -1
  38. package/dist/mcp/mcp.server.js +3 -5
  39. package/dist/mcp/mcp.server.js.map +1 -1
  40. package/dist/negotiation/negotiation.graph.d.ts.map +1 -1
  41. package/dist/negotiation/negotiation.graph.js +13 -20
  42. package/dist/negotiation/negotiation.graph.js.map +1 -1
  43. package/dist/negotiation/negotiation.tools.d.ts.map +1 -1
  44. package/dist/negotiation/negotiation.tools.js +12 -12
  45. package/dist/negotiation/negotiation.tools.js.map +1 -1
  46. package/dist/network/indexer/indexer.graph.d.ts +9 -9
  47. package/dist/network/indexer/indexer.graph.d.ts.map +1 -1
  48. package/dist/network/indexer/indexer.graph.js.map +1 -1
  49. package/dist/network/network.graph.d.ts.map +1 -1
  50. package/dist/network/network.graph.js +19 -25
  51. package/dist/network/network.graph.js.map +1 -1
  52. package/dist/opportunity/feed/feed.categorizer.d.ts.map +1 -1
  53. package/dist/opportunity/feed/feed.categorizer.js +15 -20
  54. package/dist/opportunity/feed/feed.categorizer.js.map +1 -1
  55. package/dist/opportunity/feed/feed.graph.d.ts.map +1 -1
  56. package/dist/opportunity/feed/feed.graph.js +8 -10
  57. package/dist/opportunity/feed/feed.graph.js.map +1 -1
  58. package/dist/opportunity/opportunity.introducer.d.ts.map +1 -1
  59. package/dist/opportunity/opportunity.introducer.js +1 -2
  60. package/dist/opportunity/opportunity.introducer.js.map +1 -1
  61. package/dist/opportunity/opportunity.tools.d.ts.map +1 -1
  62. package/dist/opportunity/opportunity.tools.js +3 -2
  63. package/dist/opportunity/opportunity.tools.js.map +1 -1
  64. package/dist/profile/profile.enricher.d.ts +5 -7
  65. package/dist/profile/profile.enricher.d.ts.map +1 -1
  66. package/dist/profile/profile.enricher.js +8 -10
  67. package/dist/profile/profile.enricher.js.map +1 -1
  68. package/dist/profile/profile.generator.d.ts.map +1 -1
  69. package/dist/profile/profile.generator.js +1 -2
  70. package/dist/profile/profile.generator.js.map +1 -1
  71. package/dist/profile/profile.tools.js +1 -1
  72. package/dist/profile/profile.tools.js.map +1 -1
  73. package/dist/questioner/questioner.presets.d.ts.map +1 -1
  74. package/dist/questioner/questioner.presets.js +24 -38
  75. package/dist/questioner/questioner.presets.js.map +1 -1
  76. package/dist/shared/agent/tool.factory.d.ts.map +1 -1
  77. package/dist/shared/agent/tool.factory.js +2 -2
  78. package/dist/shared/agent/tool.factory.js.map +1 -1
  79. package/dist/shared/agent/tool.helpers.d.ts +10 -0
  80. package/dist/shared/agent/tool.helpers.d.ts.map +1 -1
  81. package/dist/shared/agent/tool.helpers.js +2 -1
  82. package/dist/shared/agent/tool.helpers.js.map +1 -1
  83. package/dist/shared/agent/tool.runtime.d.ts.map +1 -1
  84. package/dist/shared/agent/tool.runtime.js +20 -13
  85. package/dist/shared/agent/tool.runtime.js.map +1 -1
  86. package/dist/shared/hyde/hyde.graph.d.ts.map +1 -1
  87. package/dist/shared/hyde/hyde.graph.js +3 -2
  88. package/dist/shared/hyde/hyde.graph.js.map +1 -1
  89. package/dist/shared/hyde/hyde.strategies.d.ts +2 -1
  90. package/dist/shared/hyde/hyde.strategies.d.ts.map +1 -1
  91. package/dist/shared/hyde/hyde.strategies.js.map +1 -1
  92. package/dist/shared/observability/trace.d.ts +3 -3
  93. package/dist/shared/observability/trace.d.ts.map +1 -1
  94. package/dist/shared/observability/trace.js +19 -33
  95. package/dist/shared/observability/trace.js.map +1 -1
  96. package/package.json +1 -1
@@ -25,6 +25,23 @@ export class NetworkGraphFactory {
25
25
  const readNode = async (state) => {
26
26
  return timed("NetworkGraph.read", async () => {
27
27
  logger.verbose("Read indexes", { userId: state.userId, networkId: state.networkId, showAll: state.showAll });
28
+ // Shared projections for both the scoped and unscoped read paths.
29
+ const projectMembership = (m) => ({
30
+ networkId: m.networkId,
31
+ title: m.networkTitle,
32
+ prompt: m.indexPrompt,
33
+ autoAssign: m.autoAssign,
34
+ isPersonal: m.isPersonal,
35
+ joinedAt: m.joinedAt,
36
+ });
37
+ const projectOwned = (o) => ({
38
+ networkId: o.id,
39
+ title: o.title,
40
+ prompt: o.prompt,
41
+ memberCount: o.memberCount,
42
+ intentCount: o.intentCount,
43
+ joinPolicy: o.permissions.joinPolicy,
44
+ });
28
45
  try {
29
46
  const [allMemberships, ownedIndexes, publicIndexesResult] = await Promise.all([
30
47
  this.database.getNetworkMemberships(state.userId),
@@ -52,22 +69,6 @@ export class NetworkGraphFactory {
52
69
  },
53
70
  };
54
71
  }
55
- const projectMembership = (m) => ({
56
- networkId: m.networkId,
57
- title: m.networkTitle,
58
- prompt: m.indexPrompt,
59
- autoAssign: m.autoAssign,
60
- isPersonal: m.isPersonal,
61
- joinedAt: m.joinedAt,
62
- });
63
- const projectOwned = (o) => ({
64
- networkId: o.id,
65
- title: o.title,
66
- prompt: o.prompt,
67
- memberCount: o.memberCount,
68
- intentCount: o.intentCount,
69
- joinPolicy: o.permissions.joinPolicy,
70
- });
71
72
  const scopedMembership = allMemberships.find((m) => m.networkId === networkId);
72
73
  const personalMembership = scopedMembership?.isPersonal
73
74
  ? undefined
@@ -106,15 +107,8 @@ export class NetworkGraphFactory {
106
107
  }));
107
108
  return {
108
109
  readResult: {
109
- memberOf: allMemberships.map((m) => ({
110
- networkId: m.networkId,
111
- title: m.networkTitle,
112
- prompt: m.indexPrompt,
113
- autoAssign: m.autoAssign,
114
- isPersonal: m.isPersonal,
115
- joinedAt: m.joinedAt,
116
- })),
117
- owns: ownedIndexes.map((o) => ({ networkId: o.id, title: o.title, prompt: o.prompt, memberCount: o.memberCount, intentCount: o.intentCount, joinPolicy: o.permissions.joinPolicy })),
110
+ memberOf: allMemberships.map(projectMembership),
111
+ owns: ownedIndexes.map(projectOwned),
118
112
  publicNetworks,
119
113
  stats: { memberOfCount: allMemberships.length, ownsCount: ownedIndexes.length, publicNetworksCount: publicNetworks.length },
120
114
  },
@@ -1 +1 @@
1
- {"version":3,"file":"network.graph.js","sourceRoot":"/","sources":["network/network.graph.ts"],"names":[],"mappings":"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","sourcesContent":["import { StateGraph, START, END } from \"@langchain/langgraph\";\n\nimport { NetworkGraphDatabase } from \"../shared/interfaces/database.interface.js\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { timed } from \"../shared/observability/performance.js\";\n\nimport { NetworkGraphState } from \"./network.state.js\";\n\nconst logger = protocolLogger(\"NetworkGraphFactory\");\n\n/**\n * Factory class to build and compile the Index (CRUD) Graph.\n *\n * Handles create, read, update, and delete operations for indexes.\n * Membership and intent-index assignment operations are handled by\n * separate graphs (NetworkMembershipGraph and IntentNetworkGraph).\n *\n * Flow:\n * START → routerNode → {createNode | readNode | updateNode | deleteNode} → END\n */\nexport class NetworkGraphFactory {\n constructor(private database: NetworkGraphDatabase) {}\n\n public createGraph() {\n // --- NODE DEFINITIONS ---\n\n /**\n * Read Node: List indexes the user belongs to and owns.\n */\n const readNode = async (state: typeof NetworkGraphState.State) => {\n return timed(\"NetworkGraph.read\", async () => {\n logger.verbose(\"Read indexes\", { userId: state.userId, networkId: state.networkId, showAll: state.showAll });\n\n try {\n const [allMemberships, ownedIndexes, publicIndexesResult] = await Promise.all([\n this.database.getNetworkMemberships(state.userId),\n this.database.getOwnedIndexes(state.userId),\n this.database.getPublicIndexesNotJoined(state.userId),\n ]);\n\n // If index-scoped and not showAll, return just that index plus the\n // user's personal index (their contacts). The personal index is part\n // of every scope — `computeAgentIndexScope` keeps it reachable for\n // network-bound agents, and a user clicked into a community-scoped\n // chat still owns their contact list — so surfacing it here keeps\n // tools that operate on the personal index (add_contact, list_contacts,\n // import_*_contacts) discoverable. Other community memberships are\n // still hidden, and `publicNetworks` is omitted.\n const scopeToCurrentIndex = state.networkId && !state.showAll;\n if (scopeToCurrentIndex) {\n const networkId = state.networkId!;\n const isMember = await this.database.isNetworkMember(networkId, state.userId);\n if (!isMember) {\n return {\n readResult: {\n memberOf: [],\n owns: [],\n stats: { memberOfCount: 0, ownsCount: 0, scopeNote: \"Network not found or you are not a member.\" },\n },\n };\n }\n const projectMembership = (m: typeof allMemberships[number]) => ({\n networkId: m.networkId,\n title: m.networkTitle,\n prompt: m.indexPrompt,\n autoAssign: m.autoAssign,\n isPersonal: m.isPersonal,\n joinedAt: m.joinedAt,\n });\n const projectOwned = (o: typeof ownedIndexes[number]) => ({\n networkId: o.id,\n title: o.title,\n prompt: o.prompt,\n memberCount: o.memberCount,\n intentCount: o.intentCount,\n joinPolicy: o.permissions.joinPolicy,\n });\n const scopedMembership = allMemberships.find((m) => m.networkId === networkId);\n const personalMembership = scopedMembership?.isPersonal\n ? undefined\n : allMemberships.find((m) => m.isPersonal);\n const scopedOwned = ownedIndexes.find((o) => o.id === networkId);\n const personalOwned = personalMembership\n ? ownedIndexes.find((o) => o.id === personalMembership.networkId)\n : undefined;\n const memberOf = [\n ...(scopedMembership ? [projectMembership(scopedMembership)] : []),\n ...(personalMembership ? [projectMembership(personalMembership)] : []),\n ];\n const owns = [\n ...(scopedOwned ? [projectOwned(scopedOwned)] : []),\n ...(personalOwned ? [projectOwned(personalOwned)] : []),\n ];\n return {\n readResult: {\n memberOf,\n owns,\n stats: {\n memberOfCount: memberOf.length,\n ownsCount: owns.length,\n scopeNote: \"Showing current index and your personal index. Use showAll: true for all indexes.\",\n },\n },\n };\n }\n\n // Include public networks available to join\n const publicNetworks = publicIndexesResult.networks.map((idx) => ({\n networkId: idx.id,\n title: idx.title,\n prompt: idx.prompt,\n memberCount: idx.memberCount,\n owner: idx.owner,\n }));\n\n return {\n readResult: {\n memberOf: allMemberships.map((m) => ({\n networkId: m.networkId,\n title: m.networkTitle,\n prompt: m.indexPrompt,\n autoAssign: m.autoAssign,\n isPersonal: m.isPersonal,\n joinedAt: m.joinedAt,\n })),\n owns: ownedIndexes.map((o) => ({ networkId: o.id, title: o.title, prompt: o.prompt, memberCount: o.memberCount, intentCount: o.intentCount, joinPolicy: o.permissions.joinPolicy })),\n publicNetworks,\n stats: { memberOfCount: allMemberships.length, ownsCount: ownedIndexes.length, publicNetworksCount: publicNetworks.length },\n },\n };\n } catch (err) {\n logger.error(\"Read indexes failed\", { error: err });\n return { error: \"Failed to fetch network information.\" };\n }\n });\n };\n\n /**\n * Create Node: Create a new index and add user as owner.\n */\n const createNode = async (state: typeof NetworkGraphState.State) => {\n return timed(\"NetworkGraph.create\", async () => {\n logger.verbose(\"Create index\", { userId: state.userId, createInput: state.createInput });\n\n if (!state.createInput?.title?.trim()) {\n return { mutationResult: { success: false, error: \"Title is required.\" } };\n }\n\n let createdIndexId: string | undefined;\n try {\n const index = await this.database.createNetwork({\n title: state.createInput.title.trim(),\n prompt: state.createInput.prompt?.trim() || undefined,\n imageUrl: state.createInput.imageUrl ?? undefined,\n joinPolicy: state.createInput.joinPolicy,\n });\n createdIndexId = index.id;\n\n const added = await this.database.addMemberToNetwork(index.id, state.userId, 'owner');\n if (!added.success) {\n logger.error(\"addMemberToNetwork failed; cleaning up orphaned network\", { networkId: index.id });\n try { await this.database.softDeleteNetwork(index.id); } catch {}\n return { mutationResult: { success: false, error: \"Failed to set you as owner. Network was not created.\" } };\n }\n\n return {\n mutationResult: {\n success: true,\n networkId: index.id,\n title: index.title,\n message: `Network \"${index.title}\" created. You are the owner.`,\n },\n };\n } catch (err) {\n logger.error(\"Create index failed\", { error: err });\n if (createdIndexId) {\n try { await this.database.softDeleteNetwork(createdIndexId); } catch {}\n }\n return {\n mutationResult: {\n success: false,\n error: err instanceof Error ? err.message : \"Failed to create network.\",\n },\n };\n }\n });\n };\n\n /**\n * Update Node: Update index settings (owner only).\n */\n const updateNode = async (state: typeof NetworkGraphState.State) => {\n return timed(\"NetworkGraph.update\", async () => {\n const networkId = state.networkId;\n logger.verbose(\"Update index\", { userId: state.userId, networkId, updateInput: state.updateInput });\n\n if (!networkId) {\n return { mutationResult: { success: false, error: \"networkId is required for update.\" } };\n }\n\n try {\n const isOwner = await this.database.isIndexOwner(networkId, state.userId);\n if (!isOwner) {\n return { mutationResult: { success: false, error: \"You can only modify networks you own.\" } };\n }\n\n await this.database.updateIndexSettings(networkId, state.userId, state.updateInput ?? {});\n\n return {\n mutationResult: {\n success: true,\n networkId,\n message: \"Network settings updated.\",\n },\n };\n } catch (err) {\n logger.error(\"Update index failed\", { error: err });\n return { mutationResult: { success: false, error: \"Failed to update network.\" } };\n }\n });\n };\n\n /**\n * Delete Node: Soft-delete an index (owner only, sole member).\n */\n const deleteNode = async (state: typeof NetworkGraphState.State) => {\n return timed(\"NetworkGraph.delete\", async () => {\n const networkId = state.networkId;\n logger.verbose(\"Delete index\", { userId: state.userId, networkId });\n\n if (!networkId) {\n return { mutationResult: { success: false, error: \"networkId is required for delete.\" } };\n }\n\n try {\n const isOwner = await this.database.isIndexOwner(networkId, state.userId);\n if (!isOwner) {\n return { mutationResult: { success: false, error: \"You can only delete networks you own.\" } };\n }\n\n const count = await this.database.getNetworkMemberCount(networkId);\n if (count > 1) {\n return { mutationResult: { success: false, error: \"Cannot delete network with other members. Remove members first.\" } };\n }\n\n await this.database.softDeleteNetwork(networkId);\n\n return {\n mutationResult: {\n success: true,\n networkId,\n message: \"Network deleted.\",\n },\n };\n } catch (err) {\n logger.error(\"Delete index failed\", { error: err });\n return { mutationResult: { success: false, error: \"Failed to delete network.\" } };\n }\n });\n };\n\n // --- CONDITIONAL ROUTING ---\n\n const routeByMode = (state: typeof NetworkGraphState.State): string => {\n switch (state.operationMode) {\n case 'create': return 'create';\n case 'read': return 'read';\n case 'update': return 'update';\n case 'delete': return 'delete_idx';\n default: return 'read';\n }\n };\n\n // --- GRAPH ASSEMBLY ---\n\n const workflow = new StateGraph(NetworkGraphState)\n .addNode(\"read\", readNode)\n .addNode(\"create\", createNode)\n .addNode(\"update\", updateNode)\n .addNode(\"delete_idx\", deleteNode)\n .addConditionalEdges(START, routeByMode, {\n read: \"read\",\n create: \"create\",\n update: \"update\",\n delete_idx: \"delete_idx\",\n })\n .addEdge(\"read\", END)\n .addEdge(\"create\", END)\n .addEdge(\"update\", END)\n .addEdge(\"delete_idx\", END);\n\n return workflow.compile();\n }\n}\n"]}
1
+ {"version":3,"file":"network.graph.js","sourceRoot":"/","sources":["network/network.graph.ts"],"names":[],"mappings":"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","sourcesContent":["import { StateGraph, START, END } from \"@langchain/langgraph\";\n\nimport { NetworkGraphDatabase } from \"../shared/interfaces/database.interface.js\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { timed } from \"../shared/observability/performance.js\";\n\nimport { NetworkGraphState } from \"./network.state.js\";\n\nconst logger = protocolLogger(\"NetworkGraphFactory\");\n\n/**\n * Factory class to build and compile the Index (CRUD) Graph.\n *\n * Handles create, read, update, and delete operations for indexes.\n * Membership and intent-index assignment operations are handled by\n * separate graphs (NetworkMembershipGraph and IntentNetworkGraph).\n *\n * Flow:\n * START → routerNode → {createNode | readNode | updateNode | deleteNode} → END\n */\nexport class NetworkGraphFactory {\n constructor(private database: NetworkGraphDatabase) {}\n\n public createGraph() {\n // --- NODE DEFINITIONS ---\n\n /**\n * Read Node: List indexes the user belongs to and owns.\n */\n const readNode = async (state: typeof NetworkGraphState.State) => {\n return timed(\"NetworkGraph.read\", async () => {\n logger.verbose(\"Read indexes\", { userId: state.userId, networkId: state.networkId, showAll: state.showAll });\n\n // Shared projections for both the scoped and unscoped read paths.\n const projectMembership = (m: Awaited<ReturnType<typeof this.database.getNetworkMemberships>>[number]) => ({\n networkId: m.networkId,\n title: m.networkTitle,\n prompt: m.indexPrompt,\n autoAssign: m.autoAssign,\n isPersonal: m.isPersonal,\n joinedAt: m.joinedAt,\n });\n const projectOwned = (o: Awaited<ReturnType<typeof this.database.getOwnedIndexes>>[number]) => ({\n networkId: o.id,\n title: o.title,\n prompt: o.prompt,\n memberCount: o.memberCount,\n intentCount: o.intentCount,\n joinPolicy: o.permissions.joinPolicy,\n });\n\n try {\n const [allMemberships, ownedIndexes, publicIndexesResult] = await Promise.all([\n this.database.getNetworkMemberships(state.userId),\n this.database.getOwnedIndexes(state.userId),\n this.database.getPublicIndexesNotJoined(state.userId),\n ]);\n\n // If index-scoped and not showAll, return just that index plus the\n // user's personal index (their contacts). The personal index is part\n // of every scope — `computeAgentIndexScope` keeps it reachable for\n // network-bound agents, and a user clicked into a community-scoped\n // chat still owns their contact list — so surfacing it here keeps\n // tools that operate on the personal index (add_contact, list_contacts,\n // import_*_contacts) discoverable. Other community memberships are\n // still hidden, and `publicNetworks` is omitted.\n const scopeToCurrentIndex = state.networkId && !state.showAll;\n if (scopeToCurrentIndex) {\n const networkId = state.networkId!;\n const isMember = await this.database.isNetworkMember(networkId, state.userId);\n if (!isMember) {\n return {\n readResult: {\n memberOf: [],\n owns: [],\n stats: { memberOfCount: 0, ownsCount: 0, scopeNote: \"Network not found or you are not a member.\" },\n },\n };\n }\n const scopedMembership = allMemberships.find((m) => m.networkId === networkId);\n const personalMembership = scopedMembership?.isPersonal\n ? undefined\n : allMemberships.find((m) => m.isPersonal);\n const scopedOwned = ownedIndexes.find((o) => o.id === networkId);\n const personalOwned = personalMembership\n ? ownedIndexes.find((o) => o.id === personalMembership.networkId)\n : undefined;\n const memberOf = [\n ...(scopedMembership ? [projectMembership(scopedMembership)] : []),\n ...(personalMembership ? [projectMembership(personalMembership)] : []),\n ];\n const owns = [\n ...(scopedOwned ? [projectOwned(scopedOwned)] : []),\n ...(personalOwned ? [projectOwned(personalOwned)] : []),\n ];\n return {\n readResult: {\n memberOf,\n owns,\n stats: {\n memberOfCount: memberOf.length,\n ownsCount: owns.length,\n scopeNote: \"Showing current index and your personal index. Use showAll: true for all indexes.\",\n },\n },\n };\n }\n\n // Include public networks available to join\n const publicNetworks = publicIndexesResult.networks.map((idx) => ({\n networkId: idx.id,\n title: idx.title,\n prompt: idx.prompt,\n memberCount: idx.memberCount,\n owner: idx.owner,\n }));\n\n return {\n readResult: {\n memberOf: allMemberships.map(projectMembership),\n owns: ownedIndexes.map(projectOwned),\n publicNetworks,\n stats: { memberOfCount: allMemberships.length, ownsCount: ownedIndexes.length, publicNetworksCount: publicNetworks.length },\n },\n };\n } catch (err) {\n logger.error(\"Read indexes failed\", { error: err });\n return { error: \"Failed to fetch network information.\" };\n }\n });\n };\n\n /**\n * Create Node: Create a new index and add user as owner.\n */\n const createNode = async (state: typeof NetworkGraphState.State) => {\n return timed(\"NetworkGraph.create\", async () => {\n logger.verbose(\"Create index\", { userId: state.userId, createInput: state.createInput });\n\n if (!state.createInput?.title?.trim()) {\n return { mutationResult: { success: false, error: \"Title is required.\" } };\n }\n\n let createdIndexId: string | undefined;\n try {\n const index = await this.database.createNetwork({\n title: state.createInput.title.trim(),\n prompt: state.createInput.prompt?.trim() || undefined,\n imageUrl: state.createInput.imageUrl ?? undefined,\n joinPolicy: state.createInput.joinPolicy,\n });\n createdIndexId = index.id;\n\n const added = await this.database.addMemberToNetwork(index.id, state.userId, 'owner');\n if (!added.success) {\n logger.error(\"addMemberToNetwork failed; cleaning up orphaned network\", { networkId: index.id });\n try { await this.database.softDeleteNetwork(index.id); } catch {}\n return { mutationResult: { success: false, error: \"Failed to set you as owner. Network was not created.\" } };\n }\n\n return {\n mutationResult: {\n success: true,\n networkId: index.id,\n title: index.title,\n message: `Network \"${index.title}\" created. You are the owner.`,\n },\n };\n } catch (err) {\n logger.error(\"Create index failed\", { error: err });\n if (createdIndexId) {\n try { await this.database.softDeleteNetwork(createdIndexId); } catch {}\n }\n return {\n mutationResult: {\n success: false,\n error: err instanceof Error ? err.message : \"Failed to create network.\",\n },\n };\n }\n });\n };\n\n /**\n * Update Node: Update index settings (owner only).\n */\n const updateNode = async (state: typeof NetworkGraphState.State) => {\n return timed(\"NetworkGraph.update\", async () => {\n const networkId = state.networkId;\n logger.verbose(\"Update index\", { userId: state.userId, networkId, updateInput: state.updateInput });\n\n if (!networkId) {\n return { mutationResult: { success: false, error: \"networkId is required for update.\" } };\n }\n\n try {\n const isOwner = await this.database.isIndexOwner(networkId, state.userId);\n if (!isOwner) {\n return { mutationResult: { success: false, error: \"You can only modify networks you own.\" } };\n }\n\n await this.database.updateIndexSettings(networkId, state.userId, state.updateInput ?? {});\n\n return {\n mutationResult: {\n success: true,\n networkId,\n message: \"Network settings updated.\",\n },\n };\n } catch (err) {\n logger.error(\"Update index failed\", { error: err });\n return { mutationResult: { success: false, error: \"Failed to update network.\" } };\n }\n });\n };\n\n /**\n * Delete Node: Soft-delete an index (owner only, sole member).\n */\n const deleteNode = async (state: typeof NetworkGraphState.State) => {\n return timed(\"NetworkGraph.delete\", async () => {\n const networkId = state.networkId;\n logger.verbose(\"Delete index\", { userId: state.userId, networkId });\n\n if (!networkId) {\n return { mutationResult: { success: false, error: \"networkId is required for delete.\" } };\n }\n\n try {\n const isOwner = await this.database.isIndexOwner(networkId, state.userId);\n if (!isOwner) {\n return { mutationResult: { success: false, error: \"You can only delete networks you own.\" } };\n }\n\n const count = await this.database.getNetworkMemberCount(networkId);\n if (count > 1) {\n return { mutationResult: { success: false, error: \"Cannot delete network with other members. Remove members first.\" } };\n }\n\n await this.database.softDeleteNetwork(networkId);\n\n return {\n mutationResult: {\n success: true,\n networkId,\n message: \"Network deleted.\",\n },\n };\n } catch (err) {\n logger.error(\"Delete index failed\", { error: err });\n return { mutationResult: { success: false, error: \"Failed to delete network.\" } };\n }\n });\n };\n\n // --- CONDITIONAL ROUTING ---\n\n const routeByMode = (state: typeof NetworkGraphState.State): string => {\n switch (state.operationMode) {\n case 'create': return 'create';\n case 'read': return 'read';\n case 'update': return 'update';\n case 'delete': return 'delete_idx';\n default: return 'read';\n }\n };\n\n // --- GRAPH ASSEMBLY ---\n\n const workflow = new StateGraph(NetworkGraphState)\n .addNode(\"read\", readNode)\n .addNode(\"create\", createNode)\n .addNode(\"update\", updateNode)\n .addNode(\"delete_idx\", deleteNode)\n .addConditionalEdges(START, routeByMode, {\n read: \"read\",\n create: \"create\",\n update: \"update\",\n delete_idx: \"delete_idx\",\n })\n .addEdge(\"read\", END)\n .addEdge(\"create\", END)\n .addEdge(\"update\", END)\n .addEdge(\"delete_idx\", END);\n\n return workflow.compile();\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"feed.categorizer.d.ts","sourceRoot":"/","sources":["opportunity/feed/feed.categorizer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAyB3D,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,mBAAmB,EAAE,CAAC;CACjC,CAAC;AAkEF,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,KAAK,CAAiD;;IAO9D;;OAEG;IAEG,UAAU,CAAC,KAAK,EAAE,oBAAoB,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAsE5E"}
1
+ {"version":3,"file":"feed.categorizer.d.ts","sourceRoot":"/","sources":["opportunity/feed/feed.categorizer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAyB3D,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,mBAAmB,EAAE,CAAC;CACjC,CAAC;AAgFF,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,KAAK,CAAiD;;IAO9D;;OAEG;IAEG,UAAU,CAAC,KAAK,EAAE,oBAAoB,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAoD5E"}
@@ -35,6 +35,19 @@ const categorizationSchema = z.object({
35
35
  });
36
36
  const UNCATEGORIZED_SECTION_ID = 'uncategorized';
37
37
  const UNCATEGORIZED_SECTION_TITLE = 'UNCATEGORIZED OPPORTUNITIES';
38
+ /** Single-section fallback grouping every card under one generic heading. */
39
+ function buildFallbackResult(cards) {
40
+ return {
41
+ sections: [
42
+ {
43
+ id: 'opportunities',
44
+ title: 'OPPORTUNITIES',
45
+ iconName: DEFAULT_HOME_SECTION_ICON,
46
+ itemIndices: cards.map((c) => c.index),
47
+ },
48
+ ],
49
+ };
50
+ }
38
51
  /**
39
52
  * Build card summaries for the categorizer prompt.
40
53
  */
@@ -123,16 +136,7 @@ Rules:
123
136
  const parsed = categorizationSchema.safeParse(result);
124
137
  if (!parsed.success) {
125
138
  logger.warn('HomeCategorizer parse failed', { error: parsed.error });
126
- return {
127
- sections: [
128
- {
129
- id: 'opportunities',
130
- title: 'OPPORTUNITIES',
131
- iconName: DEFAULT_HOME_SECTION_ICON,
132
- itemIndices: cards.map((c) => c.index),
133
- },
134
- ],
135
- };
139
+ return buildFallbackResult(cards);
136
140
  }
137
141
  const sections = parsed.data.sections.map((s) => ({
138
142
  id: s.id,
@@ -147,16 +151,7 @@ Rules:
147
151
  catch (e) {
148
152
  const err = e instanceof Error ? { message: e.message, name: e.name } : String(e);
149
153
  logger.error('HomeCategorizer categorize failed', { error: err });
150
- return {
151
- sections: [
152
- {
153
- id: 'opportunities',
154
- title: 'OPPORTUNITIES',
155
- iconName: DEFAULT_HOME_SECTION_ICON,
156
- itemIndices: cards.map((c) => c.index),
157
- },
158
- ],
159
- };
154
+ return buildFallbackResult(cards);
160
155
  }
161
156
  }
162
157
  }
@@ -1 +1 @@
1
- {"version":3,"file":"feed.categorizer.js","sourceRoot":"/","sources":["opportunity/feed/feed.categorizer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;;;;;;;;;;AAGH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,wCAAwC,CAAC;AAC1G,OAAO,EAAE,cAAc,EAAE,MAAM,+CAA+C,CAAC;AAC/E,OAAO,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAE3E,MAAM,MAAM,GAAG,cAAc,CAAC,iBAAiB,CAAC,CAAC;AAEjD,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,QAAQ,EAAE,CAAC,CAAC,KAAK,CACf,CAAC,CAAC,MAAM,CAAC;QACP,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;QAC9D,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,QAAQ,CACP,mGAAmG,CACpG;QACH,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iDAAiD,CAAC;QAC3F,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sDAAsD,CAAC;QACrF,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,4CAA4C,CAAC;KACxF,CAAC,CACH;CACF,CAAC,CAAC;AAeH,MAAM,wBAAwB,GAAG,eAAe,CAAC;AACjD,MAAM,2BAA2B,GAAG,6BAA6B,CAAC;AAElE;;GAEG;AACH,SAAS,kBAAkB,CAAC,KAA6B;IACvD,OAAO,KAAK;SACT,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,IAAI,OAAO,CAAC;QACrC,MAAM,MAAM,GAAG,CAAC,CAAC,iBAAiB,IAAI,SAAS,CAAC;QAChD,OAAO,IAAI,CAAC,CAAC,KAAK,UAAU,IAAI,YAAY,MAAM,KAAK,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,IAAI,GAAG,CAAC;IAChH,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,SAAS,iBAAiB,CAAC,QAA+B,EAAE,QAAgB;IAC1E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IAEnC,MAAM,kBAAkB,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAClD,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAC9B,IAAI,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,QAAQ,CAAC,CAAC,CAChF,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAExB,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;YAClC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;QAED,OAAO;YACL,GAAG,OAAO;YACV,WAAW,EAAE,aAAa;SAC3B,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,QAAQ,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAClD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED,MAAM,eAAe,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,wBAAwB,CAAC,CAAC;IACtG,IAAI,eAAe,EAAE,CAAC;QACpB,eAAe,CAAC,WAAW,GAAG,KAAK,CAAC,IAAI,CACtC,IAAI,GAAG,CAAC,CAAC,GAAG,eAAe,CAAC,WAAW,EAAE,GAAG,cAAc,CAAC,CAAC,CAC7D,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACxB,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED,OAAO;QACL,GAAG,kBAAkB;QACrB;YACE,EAAE,EAAE,wBAAwB;YAC5B,KAAK,EAAE,2BAA2B;YAClC,QAAQ,EAAE,yBAAyB;YACnC,WAAW,EAAE,cAAc;SAC5B;KACF,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,oBAAoB;IAG/B;QACE,MAAM,GAAG,GAAG,WAAW,CAAC,iBAAiB,CAAC,CAAC;QAC3C,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,oBAAoB,CAAC,oBAAoB,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;IACzF,CAAC;IAED;;OAEG;IAEG,AAAN,KAAK,CAAC,UAAU,CAAC,KAA6B;QAC5C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;QAC1B,CAAC;QACD,MAAM,QAAQ,GAAG,qBAAqB,EAAE,CAAC;QACzC,MAAM,aAAa,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QAElC,MAAM,YAAY,GAAG;;;;;;;;oEAQ2C,QAAQ;oFACQ,QAAQ;;;;;;mOAMuI,CAAC;QAEhO,MAAM,WAAW,GAAG,yBAAyB,aAAa,iGAAiG,CAAC;QAE5J,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,KAAK,EAAE;gBACrD,IAAI,aAAa,CAAC,YAAY,CAAC;gBAC/B,IAAI,YAAY,CAAC,WAAW,CAAC;aAC9B,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YACtD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,MAAM,CAAC,IAAI,CAAC,8BAA8B,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;gBACrE,OAAO;oBACL,QAAQ,EAAE;wBACR;4BACE,EAAE,EAAE,eAAe;4BACnB,KAAK,EAAE,eAAe;4BACtB,QAAQ,EAAE,yBAAyB;4BACnC,WAAW,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;yBACvC;qBACF;iBACF,CAAC;YACJ,CAAC;YACD,MAAM,QAAQ,GAA0B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACvE,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,SAAS;gBACjC,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,WAAW,EAAE,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC;aAClE,CAAC,CAAC,CAAC;YACJ,MAAM,kBAAkB,GAAG,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACjE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,CAAC;QAC1C,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAClF,MAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YAClE,OAAO;gBACL,QAAQ,EAAE;oBACR;wBACE,EAAE,EAAE,eAAe;wBACnB,KAAK,EAAE,eAAe;wBACtB,QAAQ,EAAE,yBAAyB;wBACnC,WAAW,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;qBACvC;iBACF;aACF,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AAtEO;IADL,KAAK,EAAE;;;;sDAsEP","sourcesContent":["/**\n * Home Categorizer Agent\n *\n * Takes a list of presenter-produced opportunity cards and returns dynamic sections\n * with CTA-style titles and Lucide icon names. Used by the home graph after\n * generateCardText.\n */\n\nimport type { ChatOpenAI } from \"@langchain/openai\";\nimport { HumanMessage, SystemMessage } from '@langchain/core/messages';\nimport { z } from 'zod';\n\nimport type { HomeSectionProposal } from './feed.state.js';\nimport { getIconNamesForPrompt, DEFAULT_HOME_SECTION_ICON } from '../../shared/ui/lucide.icon-catalog.js';\nimport { protocolLogger } from '../../shared/observability/protocol.logger.js';\nimport { Timed } from \"../../shared/observability/performance.js\";\nimport { createModel } from \"../../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger('HomeCategorizer');\n\nconst categorizationSchema = z.object({\n sections: z.array(\n z.object({\n id: z.string().describe('Short kebab-case id for the section'),\n title: z\n .string()\n .describe(\n 'Call-to-action style section heading (e.g. REACH OUT NOW, YOUR MOVE, CONNECTIONS WAITING FOR YOU)'\n ),\n subtitle: z.string().nullable().describe('Optional one-line description; use null if none'),\n iconName: z.string().describe('Lucide icon name in kebab-case from the allowed list'),\n itemIndices: z.array(z.number()).describe('Indices of cards in this section (0-based)'),\n })\n ),\n});\n\nexport type CategorizerInputItem = {\n index: number;\n headline?: string;\n mainText: string;\n name: string;\n viewerRole?: string;\n opportunityStatus?: string;\n};\n\nexport type CategorizerResult = {\n sections: HomeSectionProposal[];\n};\n\nconst UNCATEGORIZED_SECTION_ID = 'uncategorized';\nconst UNCATEGORIZED_SECTION_TITLE = 'UNCATEGORIZED OPPORTUNITIES';\n\n/**\n * Build card summaries for the categorizer prompt.\n */\nfunction buildCardSummaries(cards: CategorizerInputItem[]): string {\n return cards\n .map((c) => {\n const role = c.viewerRole ?? 'party';\n const status = c.opportunityStatus ?? 'pending';\n return `[${c.index}] role=${role}; status=${status}; ${c.headline ?? c.mainText.slice(0, 60)}... (${c.name})`;\n })\n .join('\\n');\n}\n\nfunction reconcileSections(sections: HomeSectionProposal[], maxIndex: number): HomeSectionProposal[] {\n const assigned = new Set<number>();\n\n const normalizedSections = sections.map((section) => {\n const uniqueIndices = Array.from(\n new Set(section.itemIndices.filter((index) => index >= 0 && index <= maxIndex))\n ).sort((a, b) => a - b);\n\n for (const index of uniqueIndices) {\n assigned.add(index);\n }\n\n return {\n ...section,\n itemIndices: uniqueIndices,\n };\n });\n\n const missingIndices: number[] = [];\n for (let index = 0; index <= maxIndex; index += 1) {\n if (!assigned.has(index)) {\n missingIndices.push(index);\n }\n }\n\n if (missingIndices.length === 0) {\n return normalizedSections;\n }\n\n const fallbackSection = normalizedSections.find((section) => section.id === UNCATEGORIZED_SECTION_ID);\n if (fallbackSection) {\n fallbackSection.itemIndices = Array.from(\n new Set([...fallbackSection.itemIndices, ...missingIndices])\n ).sort((a, b) => a - b);\n return normalizedSections;\n }\n\n return [\n ...normalizedSections,\n {\n id: UNCATEGORIZED_SECTION_ID,\n title: UNCATEGORIZED_SECTION_TITLE,\n iconName: DEFAULT_HOME_SECTION_ICON,\n itemIndices: missingIndices,\n },\n ];\n}\n\nexport class HomeCategorizerAgent {\n private model: ReturnType<ChatOpenAI['withStructuredOutput']>;\n\n constructor() {\n const llm = createModel(\"homeCategorizer\");\n this.model = llm.withStructuredOutput(categorizationSchema, { name: 'home_sections' });\n }\n\n /**\n * Categorize presenter-produced cards into 1–5 sections with CTA-style titles and icons.\n */\n @Timed()\n async categorize(cards: CategorizerInputItem[]): Promise<CategorizerResult> {\n if (cards.length === 0) {\n return { sections: [] };\n }\n const iconList = getIconNamesForPrompt();\n const cardSummaries = buildCardSummaries(cards);\n const maxIndex = cards.length - 1;\n\n const systemPrompt = `You are organizing connection opportunities into dynamic sections for a user's home feed.\n\nGiven a list of opportunity cards (each with index, headline/summary, and name), group them into 1–5 sections.\n\nEach section must have:\n- id: short kebab-case identifier (e.g. waiting-for-action, your-perspective, connector)\n- title: CALL-TO-ACTION style section heading in uppercase. Write as an action or invitation, not a neutral label. Examples: REACH OUT NOW, YOUR MOVE, CONNECTIONS WAITING FOR YOU, INTROS READY TO SEND, PEOPLE YOU SHOULD MEET\n- subtitle: optional one-line description\n- iconName: exactly ONE icon from this list (use the name as-is): ${iconList}\n- itemIndices: array of card indices that belong in this section (each index 0 to ${maxIndex} at most once)\n\nRules:\n- Use only icon names from the list above.\n- Every card index must appear in exactly one section.\n- Section titles must read as calls-to-action or invitations, not passive labels.\n- If a card has role=introducer and status=pending, prioritize grouping those cards under a connector curation section with a CTA title like \"YOU'RE THE CONNECTOR THEY NEED\", \"MAKE THE INTRO\", or \"DECIDE IF IT'S A GOOD MATCH\".`;\n\n const userContent = `Cards to categorize:\\n${cardSummaries}\\n\\nOutput sections with id, title (CTA-style), subtitle (optional), iconName, and itemIndices.`;\n\n try {\n const result = await invokeWithAbortSignal(this.model, [\n new SystemMessage(systemPrompt),\n new HumanMessage(userContent),\n ]);\n const parsed = categorizationSchema.safeParse(result);\n if (!parsed.success) {\n logger.warn('HomeCategorizer parse failed', { error: parsed.error });\n return {\n sections: [\n {\n id: 'opportunities',\n title: 'OPPORTUNITIES',\n iconName: DEFAULT_HOME_SECTION_ICON,\n itemIndices: cards.map((c) => c.index),\n },\n ],\n };\n }\n const sections: HomeSectionProposal[] = parsed.data.sections.map((s) => ({\n id: s.id,\n title: s.title,\n subtitle: s.subtitle ?? undefined,\n iconName: s.iconName,\n itemIndices: s.itemIndices.filter((i) => i >= 0 && i <= maxIndex),\n }));\n const reconciledSections = reconcileSections(sections, maxIndex);\n return { sections: reconciledSections };\n } catch (e) {\n const err = e instanceof Error ? { message: e.message, name: e.name } : String(e);\n logger.error('HomeCategorizer categorize failed', { error: err });\n return {\n sections: [\n {\n id: 'opportunities',\n title: 'OPPORTUNITIES',\n iconName: DEFAULT_HOME_SECTION_ICON,\n itemIndices: cards.map((c) => c.index),\n },\n ],\n };\n }\n }\n}\n"]}
1
+ {"version":3,"file":"feed.categorizer.js","sourceRoot":"/","sources":["opportunity/feed/feed.categorizer.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Home Categorizer Agent\n *\n * Takes a list of presenter-produced opportunity cards and returns dynamic sections\n * with CTA-style titles and Lucide icon names. Used by the home graph after\n * generateCardText.\n */\n\nimport type { ChatOpenAI } from \"@langchain/openai\";\nimport { HumanMessage, SystemMessage } from '@langchain/core/messages';\nimport { z } from 'zod';\n\nimport type { HomeSectionProposal } from './feed.state.js';\nimport { getIconNamesForPrompt, DEFAULT_HOME_SECTION_ICON } from '../../shared/ui/lucide.icon-catalog.js';\nimport { protocolLogger } from '../../shared/observability/protocol.logger.js';\nimport { Timed } from \"../../shared/observability/performance.js\";\nimport { createModel } from \"../../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger('HomeCategorizer');\n\nconst categorizationSchema = z.object({\n sections: z.array(\n z.object({\n id: z.string().describe('Short kebab-case id for the section'),\n title: z\n .string()\n .describe(\n 'Call-to-action style section heading (e.g. REACH OUT NOW, YOUR MOVE, CONNECTIONS WAITING FOR YOU)'\n ),\n subtitle: z.string().nullable().describe('Optional one-line description; use null if none'),\n iconName: z.string().describe('Lucide icon name in kebab-case from the allowed list'),\n itemIndices: z.array(z.number()).describe('Indices of cards in this section (0-based)'),\n })\n ),\n});\n\nexport type CategorizerInputItem = {\n index: number;\n headline?: string;\n mainText: string;\n name: string;\n viewerRole?: string;\n opportunityStatus?: string;\n};\n\nexport type CategorizerResult = {\n sections: HomeSectionProposal[];\n};\n\nconst UNCATEGORIZED_SECTION_ID = 'uncategorized';\nconst UNCATEGORIZED_SECTION_TITLE = 'UNCATEGORIZED OPPORTUNITIES';\n\n/** Single-section fallback grouping every card under one generic heading. */\nfunction buildFallbackResult(cards: CategorizerInputItem[]): CategorizerResult {\n return {\n sections: [\n {\n id: 'opportunities',\n title: 'OPPORTUNITIES',\n iconName: DEFAULT_HOME_SECTION_ICON,\n itemIndices: cards.map((c) => c.index),\n },\n ],\n };\n}\n\n/**\n * Build card summaries for the categorizer prompt.\n */\nfunction buildCardSummaries(cards: CategorizerInputItem[]): string {\n return cards\n .map((c) => {\n const role = c.viewerRole ?? 'party';\n const status = c.opportunityStatus ?? 'pending';\n return `[${c.index}] role=${role}; status=${status}; ${c.headline ?? c.mainText.slice(0, 60)}... (${c.name})`;\n })\n .join('\\n');\n}\n\nfunction reconcileSections(sections: HomeSectionProposal[], maxIndex: number): HomeSectionProposal[] {\n const assigned = new Set<number>();\n\n const normalizedSections = sections.map((section) => {\n const uniqueIndices = Array.from(\n new Set(section.itemIndices.filter((index) => index >= 0 && index <= maxIndex))\n ).sort((a, b) => a - b);\n\n for (const index of uniqueIndices) {\n assigned.add(index);\n }\n\n return {\n ...section,\n itemIndices: uniqueIndices,\n };\n });\n\n const missingIndices: number[] = [];\n for (let index = 0; index <= maxIndex; index += 1) {\n if (!assigned.has(index)) {\n missingIndices.push(index);\n }\n }\n\n if (missingIndices.length === 0) {\n return normalizedSections;\n }\n\n const fallbackSection = normalizedSections.find((section) => section.id === UNCATEGORIZED_SECTION_ID);\n if (fallbackSection) {\n fallbackSection.itemIndices = Array.from(\n new Set([...fallbackSection.itemIndices, ...missingIndices])\n ).sort((a, b) => a - b);\n return normalizedSections;\n }\n\n return [\n ...normalizedSections,\n {\n id: UNCATEGORIZED_SECTION_ID,\n title: UNCATEGORIZED_SECTION_TITLE,\n iconName: DEFAULT_HOME_SECTION_ICON,\n itemIndices: missingIndices,\n },\n ];\n}\n\nexport class HomeCategorizerAgent {\n private model: ReturnType<ChatOpenAI['withStructuredOutput']>;\n\n constructor() {\n const llm = createModel(\"homeCategorizer\");\n this.model = llm.withStructuredOutput(categorizationSchema, { name: 'home_sections' });\n }\n\n /**\n * Categorize presenter-produced cards into 1–5 sections with CTA-style titles and icons.\n */\n @Timed()\n async categorize(cards: CategorizerInputItem[]): Promise<CategorizerResult> {\n if (cards.length === 0) {\n return { sections: [] };\n }\n const iconList = getIconNamesForPrompt();\n const cardSummaries = buildCardSummaries(cards);\n const maxIndex = cards.length - 1;\n\n const systemPrompt = `You are organizing connection opportunities into dynamic sections for a user's home feed.\n\nGiven a list of opportunity cards (each with index, headline/summary, and name), group them into 1–5 sections.\n\nEach section must have:\n- id: short kebab-case identifier (e.g. waiting-for-action, your-perspective, connector)\n- title: CALL-TO-ACTION style section heading in uppercase. Write as an action or invitation, not a neutral label. Examples: REACH OUT NOW, YOUR MOVE, CONNECTIONS WAITING FOR YOU, INTROS READY TO SEND, PEOPLE YOU SHOULD MEET\n- subtitle: optional one-line description\n- iconName: exactly ONE icon from this list (use the name as-is): ${iconList}\n- itemIndices: array of card indices that belong in this section (each index 0 to ${maxIndex} at most once)\n\nRules:\n- Use only icon names from the list above.\n- Every card index must appear in exactly one section.\n- Section titles must read as calls-to-action or invitations, not passive labels.\n- If a card has role=introducer and status=pending, prioritize grouping those cards under a connector curation section with a CTA title like \"YOU'RE THE CONNECTOR THEY NEED\", \"MAKE THE INTRO\", or \"DECIDE IF IT'S A GOOD MATCH\".`;\n\n const userContent = `Cards to categorize:\\n${cardSummaries}\\n\\nOutput sections with id, title (CTA-style), subtitle (optional), iconName, and itemIndices.`;\n\n try {\n const result = await invokeWithAbortSignal(this.model, [\n new SystemMessage(systemPrompt),\n new HumanMessage(userContent),\n ]);\n const parsed = categorizationSchema.safeParse(result);\n if (!parsed.success) {\n logger.warn('HomeCategorizer parse failed', { error: parsed.error });\n return buildFallbackResult(cards);\n }\n const sections: HomeSectionProposal[] = parsed.data.sections.map((s) => ({\n id: s.id,\n title: s.title,\n subtitle: s.subtitle ?? undefined,\n iconName: s.iconName,\n itemIndices: s.itemIndices.filter((i) => i >= 0 && i <= maxIndex),\n }));\n const reconciledSections = reconcileSections(sections, maxIndex);\n return { sections: reconciledSections };\n } catch (e) {\n const err = e instanceof Error ? { message: e.message, name: e.name } : String(e);\n logger.error('HomeCategorizer categorize failed', { error: err });\n return buildFallbackResult(cards);\n }\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"feed.graph.d.ts","sourceRoot":"/","sources":["opportunity/feed/feed.graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;AAC1G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AACnF,OAAO,EAAkB,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,mBAAmB,EAAwB,MAAM,iBAAiB,CAAC;AAOtI,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAOzE,8HAA8H;AAC9H,KAAK,WAAW,GAAG,iBAAiB,CAAC;AAErC,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8GAA8G;IAC9G,QAAQ,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,IAAI,EAAE;QAAE,kBAAkB,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,qBAAqB,EAAE,iBAAiB,EAA0B,CAAC;AAgBhF,0HAA0H;AAC1H,eAAO,MAAM,wBAAwB,EAAE,iBAAiB,EAEhC,CAAC;AAOzB;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAarF;AA4ED,qBAAa,gBAAgB;IACf,OAAO,CAAC,QAAQ;IAAe,OAAO,CAAC,KAAK;gBAApC,QAAQ,EAAE,WAAW,EAAU,KAAK,EAAE,gBAAgB;IAE1E,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwiBZ"}
1
+ {"version":3,"file":"feed.graph.d.ts","sourceRoot":"/","sources":["opportunity/feed/feed.graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;AAC1G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AACnF,OAAO,EAAkB,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,mBAAmB,EAAwB,MAAM,iBAAiB,CAAC;AAOtI,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAOzE,8HAA8H;AAC9H,KAAK,WAAW,GAAG,iBAAiB,CAAC;AAErC,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8GAA8G;IAC9G,QAAQ,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,IAAI,EAAE;QAAE,kBAAkB,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,qBAAqB,EAAE,iBAAiB,EAA0B,CAAC;AAgBhF,0HAA0H;AAC1H,eAAO,MAAM,wBAAwB,EAAE,iBAAiB,EAEhC,CAAC;AAczB;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAarF;AA4ED,qBAAa,gBAAgB;IACf,OAAO,CAAC,QAAQ;IAAe,OAAO,CAAC,KAAK;gBAApC,QAAQ,EAAE,WAAW,EAAU,KAAK,EAAE,gBAAgB;IAE1E,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgiBZ"}
@@ -42,6 +42,12 @@ const MAX_ITEMS_PER_SECTION = 20;
42
42
  const PRESENTATION_CONCURRENCY = 50;
43
43
  const MAX_REASONING_SNIPPET_LENGTH = 240;
44
44
  const HOME_CACHE_TTL = 24 * 60 * 60; // 24 hours in seconds
45
+ /** Redis key for the categorizer cache, derived from the user and the ordered opportunity-id set. */
46
+ function buildCategorizerCacheKey(userId, cards) {
47
+ const oppIds = cards.map((c) => c.opportunityId).join(',');
48
+ const hash = createHash('sha256').update(oppIds).digest('hex').slice(0, 16);
49
+ return `home:categories:${userId}:${hash}`;
50
+ }
45
51
  /**
46
52
  * Strip leading narrator name from remark when the UI already prepends "Name: " to the chip.
47
53
  * Avoids duplication like "Yankı Ekin Yüksel: Yankı Ekin Yüksel introduced you two..."
@@ -486,11 +492,7 @@ export class HomeGraphFactory {
486
492
  return { categoryCacheHit: false };
487
493
  }
488
494
  try {
489
- const oppIds = state.cards
490
- .map((c) => c.opportunityId)
491
- .join(',');
492
- const hash = createHash('sha256').update(oppIds).digest('hex').slice(0, 16);
493
- const key = `home:categories:${state.userId}:${hash}`;
495
+ const key = buildCategorizerCacheKey(state.userId, state.cards);
494
496
  const cached = await this.cache.get(key);
495
497
  if (cached) {
496
498
  logger.verbose('[HomeGraph:checkCategorizerCache] cache hit');
@@ -548,11 +550,7 @@ export class HomeGraphFactory {
548
550
  return {};
549
551
  }
550
552
  try {
551
- const oppIds = state.cards
552
- .map((c) => c.opportunityId)
553
- .join(',');
554
- const hash = createHash('sha256').update(oppIds).digest('hex').slice(0, 16);
555
- const key = `home:categories:${state.userId}:${hash}`;
553
+ const key = buildCategorizerCacheKey(state.userId, state.cards);
556
554
  await this.cache.set(key, state.sectionProposals, { ttl: HOME_CACHE_TTL });
557
555
  logger.verbose('[HomeGraph:cacheCategorizerResults] cached', {
558
556
  sectionCount: state.sectionProposals.length,