@indexnetwork/protocol 14.3.2-rc.477.1 → 17.0.0-rc.479.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 (184) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/IMPLEMENTATION.md +50 -52
  3. package/STABILITY.md +3 -3
  4. package/dist/chat/chat.agent.js +2 -3
  5. package/dist/chat/chat.graph.d.ts +41 -1
  6. package/dist/chat/chat.graph.js +108 -127
  7. package/dist/chat/negotiator.persona.d.ts +1 -1
  8. package/dist/chat/negotiator.persona.js +1 -4
  9. package/dist/chat/negotiator.prompt.js +1 -2
  10. package/dist/chat/onboarding.persona.js +0 -1
  11. package/dist/chat/reporter.persona.js +0 -1
  12. package/dist/chat/signal.persona.js +0 -1
  13. package/dist/contacts/application/contact.tools.d.ts +6 -9
  14. package/dist/contacts/application/contact.tools.js +11 -92
  15. package/dist/contacts/application/index.d.ts +2 -14
  16. package/dist/contacts/application/index.js +2 -13
  17. package/dist/contacts/domain/contact.types.d.ts +0 -23
  18. package/dist/contacts/domain/index.d.ts +2 -6
  19. package/dist/contacts/index.d.ts +1 -1
  20. package/dist/contacts/index.js +1 -1
  21. package/dist/contacts/ports/contact.repository.port.d.ts +5 -12
  22. package/dist/contacts/ports/contact.repository.port.js +4 -6
  23. package/dist/contacts/ports/contact.tools.port.d.ts +1 -3
  24. package/dist/contacts/ports/contact.tools.port.js +1 -2
  25. package/dist/contacts/ports/index.d.ts +1 -1
  26. package/dist/contacts/ports/index.js +1 -1
  27. package/dist/discovery/hyde.frame.d.ts +4 -4
  28. package/dist/discovery/hyde.graph.d.ts +71 -12
  29. package/dist/discovery/hyde.graph.js +337 -335
  30. package/dist/discovery/lens.inferrer.d.ts +6 -6
  31. package/dist/enrichment/enrichment.graph.d.ts +127 -9
  32. package/dist/enrichment/enrichment.graph.js +633 -655
  33. package/dist/enrichment/enrichment.state.d.ts +1 -1
  34. package/dist/enrichment/enrichment.tools.context-read.d.ts +10 -0
  35. package/dist/enrichment/enrichment.tools.context-read.js +353 -0
  36. package/dist/enrichment/enrichment.tools.context-write.d.ts +9 -0
  37. package/dist/enrichment/enrichment.tools.context-write.js +417 -0
  38. package/dist/enrichment/enrichment.tools.d.ts +11 -1
  39. package/dist/enrichment/enrichment.tools.helpers.d.ts +139 -0
  40. package/dist/enrichment/enrichment.tools.helpers.js +234 -0
  41. package/dist/enrichment/enrichment.tools.js +16 -975
  42. package/dist/index.d.ts +27 -50
  43. package/dist/index.js +17 -28
  44. package/dist/intents/application/intent.graph.d.ts +71 -67
  45. package/dist/intents/application/intent.graph.execute.d.ts +59 -0
  46. package/dist/intents/application/intent.graph.execute.js +301 -0
  47. package/dist/intents/application/intent.graph.infer.d.ts +44 -0
  48. package/dist/intents/application/intent.graph.infer.js +97 -0
  49. package/dist/intents/application/intent.graph.js +96 -888
  50. package/dist/intents/application/intent.graph.reconcile.d.ts +71 -0
  51. package/dist/intents/application/intent.graph.reconcile.js +274 -0
  52. package/dist/intents/application/intent.graph.shared.d.ts +70 -0
  53. package/dist/intents/application/intent.graph.shared.js +153 -0
  54. package/dist/intents/domain/intent.state.d.ts +1 -1
  55. package/dist/maintenance/maintenance.graph.d.ts +66 -3
  56. package/dist/maintenance/maintenance.graph.js +155 -156
  57. package/dist/mcp/mcp.authorization-policy.d.ts +24 -28
  58. package/dist/mcp/mcp.authorization-policy.js +14 -34
  59. package/dist/mcp/mcp.server.d.ts +3 -3
  60. package/dist/mcp/mcp.server.js +14 -20
  61. package/dist/negotiations/application/negotiation.candidates.d.ts +83 -0
  62. package/dist/negotiations/application/negotiation.candidates.js +162 -0
  63. package/dist/negotiations/application/negotiation.graph.d.ts +91 -157
  64. package/dist/negotiations/application/negotiation.graph.finalize.d.ts +5 -0
  65. package/dist/negotiations/application/negotiation.graph.finalize.js +280 -0
  66. package/dist/negotiations/application/negotiation.graph.init.d.ts +54 -0
  67. package/dist/negotiations/application/negotiation.graph.init.js +227 -0
  68. package/dist/negotiations/application/negotiation.graph.js +69 -1388
  69. package/dist/negotiations/application/negotiation.graph.screen.d.ts +23 -0
  70. package/dist/negotiations/application/negotiation.graph.screen.js +108 -0
  71. package/dist/negotiations/application/negotiation.graph.shared.d.ts +75 -0
  72. package/dist/negotiations/application/negotiation.graph.shared.js +125 -0
  73. package/dist/negotiations/application/negotiation.graph.turn.d.ts +105 -0
  74. package/dist/negotiations/application/negotiation.graph.turn.js +484 -0
  75. package/dist/negotiations/domain/negotiation.state.d.ts +1 -1
  76. package/dist/negotiations/domain/negotiation.state.js +0 -1
  77. package/dist/networks/application/indexer.graph.d.ts +165 -7
  78. package/dist/networks/application/indexer.graph.js +339 -388
  79. package/dist/networks/application/indexer.state.d.ts +1 -1
  80. package/dist/networks/application/membership.graph.d.ts +83 -5
  81. package/dist/networks/application/membership.graph.js +177 -207
  82. package/dist/networks/application/network.graph.d.ts +148 -5
  83. package/dist/networks/application/network.graph.js +249 -278
  84. package/dist/networks/domain/membership.state.d.ts +1 -1
  85. package/dist/networks/domain/network.state.d.ts +1 -1
  86. package/dist/opportunities/application/delivery-card.cache.d.ts +1 -1
  87. package/dist/opportunities/application/delivery-card.cache.js +2 -2
  88. package/dist/opportunities/application/index.d.ts +2 -2
  89. package/dist/opportunities/application/index.js +2 -2
  90. package/dist/opportunities/application/opportunity.evaluator.js +1 -1
  91. package/dist/opportunities/application/opportunity.graph.d.ts +686 -473
  92. package/dist/opportunities/application/opportunity.graph.discovery-strategies.d.ts +109 -0
  93. package/dist/opportunities/application/opportunity.graph.discovery-strategies.js +451 -0
  94. package/dist/opportunities/application/opportunity.graph.discovery.d.ts +69 -0
  95. package/dist/opportunities/application/opportunity.graph.discovery.js +397 -0
  96. package/dist/opportunities/application/opportunity.graph.evaluation.d.ts +82 -0
  97. package/dist/opportunities/application/opportunity.graph.evaluation.js +608 -0
  98. package/dist/opportunities/application/opportunity.graph.js +120 -3690
  99. package/dist/opportunities/application/opportunity.graph.modes.d.ts +538 -0
  100. package/dist/opportunities/application/opportunity.graph.modes.js +538 -0
  101. package/dist/opportunities/application/opportunity.graph.negotiate.d.ts +109 -0
  102. package/dist/opportunities/application/opportunity.graph.negotiate.js +392 -0
  103. package/dist/opportunities/application/opportunity.graph.persist-node.d.ts +93 -0
  104. package/dist/opportunities/application/opportunity.graph.persist-node.js +765 -0
  105. package/dist/opportunities/application/opportunity.graph.prep.d.ts +150 -0
  106. package/dist/opportunities/application/opportunity.graph.prep.js +381 -0
  107. package/dist/opportunities/application/opportunity.graph.shared.d.ts +173 -0
  108. package/dist/opportunities/application/opportunity.graph.shared.js +199 -0
  109. package/dist/opportunities/application/opportunity.presentation.d.ts +399 -0
  110. package/dist/opportunities/application/{opportunity.presenter.js → opportunity.presentation.js} +735 -9
  111. package/dist/opportunities/application/opportunity.tools.cards.d.ts +152 -0
  112. package/dist/opportunities/application/opportunity.tools.cards.js +235 -0
  113. package/dist/opportunities/application/opportunity.tools.d.ts +8 -114
  114. package/dist/opportunities/application/opportunity.tools.js +28 -711
  115. package/dist/opportunities/application/opportunity.tools.list.d.ts +10 -0
  116. package/dist/opportunities/application/opportunity.tools.list.js +493 -0
  117. package/dist/opportunities/domain/index.d.ts +3 -3
  118. package/dist/opportunities/domain/index.js +3 -3
  119. package/dist/opportunities/domain/opportunity.state.d.ts +19 -19
  120. package/dist/opportunities/index.d.ts +6 -6
  121. package/dist/opportunities/index.js +4 -4
  122. package/dist/opportunities/ports/opportunity.tools.port.d.ts +1 -1
  123. package/dist/opportunities/radar/radar.graph.d.ts +57 -13
  124. package/dist/opportunities/radar/radar.graph.js +470 -471
  125. package/dist/premises/premise.graph.d.ts +88 -20
  126. package/dist/premises/premise.graph.js +207 -218
  127. package/dist/questions/domain/question.schema.d.ts +40 -40
  128. package/dist/shared/agent/tool.factory.js +0 -8
  129. package/dist/shared/agent/tool.helpers.d.ts +21 -45
  130. package/dist/shared/agent/tool.helpers.js +1 -2
  131. package/dist/shared/agent/tool.registry.d.ts +4 -3
  132. package/dist/shared/agent/tool.registry.js +2 -4
  133. package/dist/shared/agent/tool.runtime.js +0 -2
  134. package/dist/shared/agent/utility.tools.js +9 -13
  135. package/dist/shared/interfaces/database.capabilities.d.ts +100 -0
  136. package/dist/shared/interfaces/database.capabilities.js +7 -0
  137. package/dist/shared/interfaces/database.entities.d.ts +533 -0
  138. package/dist/shared/interfaces/database.entities.js +10 -0
  139. package/dist/shared/interfaces/database.identity-queries.d.ts +294 -0
  140. package/dist/shared/interfaces/database.identity-queries.js +4 -0
  141. package/dist/shared/interfaces/database.interface.d.ts +15 -2277
  142. package/dist/shared/interfaces/database.interface.js +8 -0
  143. package/dist/shared/interfaces/database.member-queries.d.ts +221 -0
  144. package/dist/shared/interfaces/database.member-queries.js +4 -0
  145. package/dist/shared/interfaces/database.negotiation.d.ts +305 -0
  146. package/dist/shared/interfaces/database.negotiation.js +26 -0
  147. package/dist/shared/interfaces/database.network-queries.d.ts +277 -0
  148. package/dist/shared/interfaces/database.network-queries.js +4 -0
  149. package/dist/shared/interfaces/database.opportunity-queries.d.ts +285 -0
  150. package/dist/shared/interfaces/database.opportunity-queries.js +4 -0
  151. package/dist/shared/interfaces/database.port.d.ts +322 -0
  152. package/dist/shared/interfaces/database.port.js +29 -0
  153. package/dist/shared/schemas/discovery-question.schema.d.ts +14 -14
  154. package/dist/shared/schemas/negotiation-digest.schema.d.ts +2 -2
  155. package/package.json +2 -15
  156. package/dist/contacts/application/contact.inviter.d.ts +0 -49
  157. package/dist/contacts/application/contact.inviter.js +0 -64
  158. package/dist/integrations/application/index.d.ts +0 -14
  159. package/dist/integrations/application/index.js +0 -14
  160. package/dist/integrations/application/integration.tools.d.ts +0 -27
  161. package/dist/integrations/application/integration.tools.js +0 -98
  162. package/dist/integrations/domain/index.d.ts +0 -16
  163. package/dist/integrations/domain/index.js +0 -1
  164. package/dist/integrations/domain/integration.types.d.ts +0 -51
  165. package/dist/integrations/domain/integration.types.js +0 -9
  166. package/dist/integrations/index.d.ts +0 -10
  167. package/dist/integrations/index.js +0 -8
  168. package/dist/integrations/ports/index.d.ts +0 -25
  169. package/dist/integrations/ports/index.js +0 -23
  170. package/dist/integrations/ports/integration.adapter.port.d.ts +0 -62
  171. package/dist/integrations/ports/integration.adapter.port.js +0 -15
  172. package/dist/integrations/ports/integration.importer.port.d.ts +0 -31
  173. package/dist/integrations/ports/integration.importer.port.js +0 -15
  174. package/dist/integrations/ports/integration.tools.port.d.ts +0 -25
  175. package/dist/integrations/ports/integration.tools.port.js +0 -18
  176. package/dist/opportunities/application/opportunity.card-presentation.d.ts +0 -44
  177. package/dist/opportunities/application/opportunity.card-presentation.js +0 -93
  178. package/dist/opportunities/application/opportunity.presenter.d.ts +0 -156
  179. package/dist/opportunities/domain/opportunity.presentation-cache.d.ts +0 -5
  180. package/dist/opportunities/domain/opportunity.presentation-cache.js +0 -11
  181. package/dist/opportunities/domain/opportunity.presentation.d.ts +0 -76
  182. package/dist/opportunities/domain/opportunity.presentation.js +0 -516
  183. package/dist/opportunities/domain/opportunity.safe-presentation.d.ts +0 -104
  184. package/dist/opportunities/domain/opportunity.safe-presentation.js +0 -102
@@ -19,24 +19,6 @@ function isRetriableError(err) {
19
19
  lower.includes("etimedout"));
20
20
  }
21
21
  const RETRY_DELAY_MS = 800;
22
- // ══════════════════════════════════════════════════════════════════════════════
23
- // CHAT GRAPH FACTORY (Agent Loop Architecture)
24
- // ══════════════════════════════════════════════════════════════════════════════
25
- /**
26
- * Factory class to build and compile the Chat Graph.
27
- *
28
- * Architecture: ReAct-Style Agent Loop
29
- *
30
- * The graph contains a single node that runs an agent loop:
31
- * 1. Agent receives messages (conversation + tool results)
32
- * 2. Agent decides: call tools OR respond to user
33
- * 3. If tools called → execute → add results → loop back
34
- * 4. If response → exit loop → stream to user
35
- *
36
- * This replaces the previous 17-node conditional routing architecture
37
- * with a flexible, LLM-driven approach that can handle multi-step
38
- * reasoning and self-correction.
39
- */
40
22
  export class ChatGraphFactory {
41
23
  constructor(database, embedder, scraper, chatSession, protocolDeps,
42
24
  /** Persona driving this graph's agent loop. Required — there is no default. */
@@ -139,68 +121,107 @@ export class ChatGraphFactory {
139
121
  * @returns Uncompiled StateGraph
140
122
  */
141
123
  buildGraph() {
142
- const database = this.database;
143
- const embedder = this.embedder;
144
- const scraper = this.scraper;
145
- const protocolDeps = this.protocolDeps;
146
- const persona = this.persona;
124
+ const deps = {
125
+ database: this.database,
126
+ embedder: this.embedder,
127
+ scraper: this.scraper,
128
+ protocolDeps: this.protocolDeps,
129
+ persona: this.persona,
130
+ };
147
131
  // ─────────────────────────────────────────────────────────────────────────
148
- // AGENT LOOP NODE
132
+ // GRAPH ASSEMBLY
149
133
  // ─────────────────────────────────────────────────────────────────────────
150
- /**
151
- * The main agent loop node.
152
- * Runs a ReAct-style agent that calls tools until it decides to respond.
153
- *
154
- * Uses `agent.streamRun()` + `config.writer` so that text tokens and
155
- * tool-activity events are pushed into the graph's custom stream in
156
- * real-time rather than batched at the end.
157
- */
158
- const agentLoopNode = async (state, config) => {
159
- return timed("ChatGraph.agentLoop", async () => {
160
- logger.verbose("Agent loop starting", {
134
+ const workflow = new StateGraph(ChatGraphState)
135
+ .addNode("agent_loop", (state, config) => agentLoopNode(state, config, deps))
136
+ .addEdge(START, "agent_loop")
137
+ .addEdge("agent_loop", END);
138
+ logger.verbose("Graph built successfully (agent loop architecture)");
139
+ return workflow;
140
+ }
141
+ }
142
+ /**
143
+ * The main agent loop node.
144
+ * Runs a ReAct-style agent that calls tools until it decides to respond.
145
+ *
146
+ * Uses `agent.streamRun()` + `config.writer` so that text tokens and
147
+ * tool-activity events are pushed into the graph's custom stream in
148
+ * real-time rather than batched at the end.
149
+ */
150
+ export async function agentLoopNode(state, config, deps) {
151
+ return timed("ChatGraph.agentLoop", async () => {
152
+ logger.verbose("Agent loop starting", {
153
+ userId: state.userId,
154
+ messageCount: state.messages.length,
155
+ currentIteration: state.iterationCount
156
+ });
157
+ const runLoop = async () => {
158
+ const legacyNetworkId = state.networkId;
159
+ const scopeType = state.scopeType ?? (legacyNetworkId ? 'network' : undefined);
160
+ const scopeId = state.scopeId ?? legacyNetworkId;
161
+ const agent = await ChatAgent.create({
162
+ ...deps.protocolDeps,
163
+ userId: state.userId,
164
+ database: deps.database,
165
+ embedder: deps.embedder,
166
+ scraper: deps.scraper,
167
+ ...(legacyNetworkId ? { networkId: legacyNetworkId } : {}),
168
+ ...(scopeType && scopeId ? { scopeType, scopeId } : {}),
169
+ sessionId: state.sessionId,
170
+ }, deps.persona);
171
+ // Direct streaming writer - emit events immediately instead of buffering
172
+ const directWriter = (data) => {
173
+ try {
174
+ config.writer?.(data);
175
+ }
176
+ catch {
177
+ /* swallow if writer is gone */
178
+ }
179
+ };
180
+ // Get signal from configurable (passed by streamer via graph.stream() config)
181
+ const signal = config.configurable?.signal;
182
+ const result = await agent.streamRun(state.messages, directWriter, signal);
183
+ return result;
184
+ };
185
+ try {
186
+ const result = await runLoop();
187
+ logger.debug("Agent streamRun result", {
188
+ responseText: result.responseText,
189
+ iterationCount: result.iterationCount,
190
+ messageCount: result.messages.length,
191
+ });
192
+ logger.verbose("Agent loop complete", {
193
+ userId: state.userId,
194
+ iterations: result.iterationCount,
195
+ responseLength: result.responseText.length
196
+ });
197
+ return {
198
+ messages: result.messages,
199
+ responseText: result.responseText,
200
+ iterationCount: result.iterationCount,
201
+ shouldContinue: false,
202
+ debugMeta: result.debugMeta,
203
+ };
204
+ }
205
+ catch (error) {
206
+ if (isRetriableError(error)) {
207
+ const signal = config.configurable?.signal;
208
+ if (signal?.aborted) {
209
+ return {
210
+ error: "Request aborted",
211
+ responseText: "",
212
+ shouldContinue: false,
213
+ };
214
+ }
215
+ logger.warn("Agent loop failed with retriable error, retrying once", {
161
216
  userId: state.userId,
162
- messageCount: state.messages.length,
163
- currentIteration: state.iterationCount
217
+ error: error instanceof Error ? error.message : String(error)
164
218
  });
165
- const runLoop = async () => {
166
- const legacyNetworkId = state.networkId;
167
- const scopeType = state.scopeType ?? (legacyNetworkId ? 'network' : undefined);
168
- const scopeId = state.scopeId ?? legacyNetworkId;
169
- const agent = await ChatAgent.create({
170
- ...protocolDeps,
171
- userId: state.userId,
172
- database,
173
- embedder,
174
- scraper,
175
- ...(legacyNetworkId ? { networkId: legacyNetworkId } : {}),
176
- ...(scopeType && scopeId ? { scopeType, scopeId } : {}),
177
- sessionId: state.sessionId,
178
- }, persona);
179
- // Direct streaming writer - emit events immediately instead of buffering
180
- const directWriter = (data) => {
181
- try {
182
- config.writer?.(data);
183
- }
184
- catch {
185
- /* swallow if writer is gone */
186
- }
187
- };
188
- // Get signal from configurable (passed by streamer via graph.stream() config)
189
- const signal = config.configurable?.signal;
190
- const result = await agent.streamRun(state.messages, directWriter, signal);
191
- return result;
192
- };
219
+ await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
193
220
  try {
194
221
  const result = await runLoop();
195
- logger.debug("Agent streamRun result", {
196
- responseText: result.responseText,
197
- iterationCount: result.iterationCount,
198
- messageCount: result.messages.length,
199
- });
200
- logger.verbose("Agent loop complete", {
222
+ logger.verbose("Agent loop complete after retry", {
201
223
  userId: state.userId,
202
224
  iterations: result.iterationCount,
203
- responseLength: result.responseText.length
204
225
  });
205
226
  return {
206
227
  messages: result.messages,
@@ -210,67 +231,27 @@ export class ChatGraphFactory {
210
231
  debugMeta: result.debugMeta,
211
232
  };
212
233
  }
213
- catch (error) {
214
- if (isRetriableError(error)) {
215
- const signal = config.configurable?.signal;
216
- if (signal?.aborted) {
217
- return {
218
- error: "Request aborted",
219
- responseText: "",
220
- shouldContinue: false,
221
- };
222
- }
223
- logger.warn("Agent loop failed with retriable error, retrying once", {
224
- userId: state.userId,
225
- error: error instanceof Error ? error.message : String(error)
226
- });
227
- await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
228
- try {
229
- const result = await runLoop();
230
- logger.verbose("Agent loop complete after retry", {
231
- userId: state.userId,
232
- iterations: result.iterationCount,
233
- });
234
- return {
235
- messages: result.messages,
236
- responseText: result.responseText,
237
- iterationCount: result.iterationCount,
238
- shouldContinue: false,
239
- debugMeta: result.debugMeta,
240
- };
241
- }
242
- catch (retryError) {
243
- logger.error("Agent loop failed on retry", {
244
- userId: state.userId,
245
- error: retryError instanceof Error ? retryError.message : String(retryError)
246
- });
247
- return {
248
- error: retryError instanceof Error ? retryError.message : "Agent loop failed",
249
- responseText: "I apologize, but I encountered an issue processing your request. Please try again.",
250
- shouldContinue: false
251
- };
252
- }
253
- }
254
- logger.error("Agent loop failed", {
234
+ catch (retryError) {
235
+ logger.error("Agent loop failed on retry", {
255
236
  userId: state.userId,
256
- error: error instanceof Error ? error.message : String(error)
237
+ error: retryError instanceof Error ? retryError.message : String(retryError)
257
238
  });
258
239
  return {
259
- error: error instanceof Error ? error.message : "Agent loop failed",
240
+ error: retryError instanceof Error ? retryError.message : "Agent loop failed",
260
241
  responseText: "I apologize, but I encountered an issue processing your request. Please try again.",
261
242
  shouldContinue: false
262
243
  };
263
244
  }
245
+ }
246
+ logger.error("Agent loop failed", {
247
+ userId: state.userId,
248
+ error: error instanceof Error ? error.message : String(error)
264
249
  });
265
- };
266
- // ─────────────────────────────────────────────────────────────────────────
267
- // GRAPH ASSEMBLY
268
- // ─────────────────────────────────────────────────────────────────────────
269
- const workflow = new StateGraph(ChatGraphState)
270
- .addNode("agent_loop", agentLoopNode)
271
- .addEdge(START, "agent_loop")
272
- .addEdge("agent_loop", END);
273
- logger.verbose("Graph built successfully (agent loop architecture)");
274
- return workflow;
275
- }
250
+ return {
251
+ error: error instanceof Error ? error.message : "Agent loop failed",
252
+ responseText: "I apologize, but I encountered an issue processing your request. Please try again.",
253
+ shouldContinue: false
254
+ };
255
+ }
256
+ });
276
257
  }
@@ -25,7 +25,7 @@ export declare const NEGOTIATOR_PERSONA_ID = "negotiator";
25
25
  * - agent management (`register/update/delete_agent`, permission grants),
26
26
  * - `confirm_opportunity_delivery` (OpenClaw ledger write, never chat).
27
27
  */
28
- export declare const NEGOTIATOR_TOOL_NAMES: readonly ["list_negotiations", "get_negotiation", "respond_to_negotiation", "list_opportunities", "update_opportunity", "read_pending_questions", "answer_pending_question", "read_intents", "create_intent", "update_intent", "delete_intent", "search_intents", "read_intent_indexes", "create_intent_index", "delete_intent_index", "read_user_contexts", "create_user_context", "update_user_context", "confirm_user_context", "preview_user_context", "read_premises", "create_premise", "update_premise", "retract_premise", "read_networks", "read_network_memberships", "create_network_membership", "delete_network_membership", "list_contacts", "search_contacts", "add_contact", "remove_contact", "import_contacts", "import_gmail_contacts", "scrape_url"];
28
+ export declare const NEGOTIATOR_TOOL_NAMES: readonly ["list_negotiations", "get_negotiation", "respond_to_negotiation", "list_opportunities", "update_opportunity", "read_pending_questions", "answer_pending_question", "read_intents", "create_intent", "update_intent", "delete_intent", "search_intents", "read_intent_indexes", "create_intent_index", "delete_intent_index", "read_user_contexts", "create_user_context", "update_user_context", "confirm_user_context", "preview_user_context", "read_premises", "create_premise", "update_premise", "retract_premise", "read_networks", "read_network_memberships", "create_network_membership", "delete_network_membership", "list_contacts", "search_contacts", "remove_contact", "scrape_url"];
29
29
  /**
30
30
  * Filters a full chat-tool registry down to the negotiator allowlist.
31
31
  * Exported separately so the scoping rule is unit-testable without
@@ -73,13 +73,10 @@ export const NEGOTIATOR_TOOL_NAMES = [
73
73
  "read_network_memberships",
74
74
  "create_network_membership",
75
75
  "delete_network_membership",
76
- // Contacts — dormant while CONTACTS_ENABLED=false (P4.5)
76
+ // Contacts — read/remove only; contacts arise from accepted opportunities
77
77
  "list_contacts",
78
78
  "search_contacts",
79
- "add_contact",
80
79
  "remove_contact",
81
- "import_contacts",
82
- "import_gmail_contacts",
83
80
  // Utility — create_intent's own contract instructs scraping pasted URLs
84
81
  // before synthesizing a signal description (P4.5)
85
82
  "scrape_url",
@@ -145,8 +145,7 @@ ${profileContext}
145
145
  | **read_premises** / **create_premise** / **update_premise** / **retract_premise** | ... | The client's premises (facts they've established) |
146
146
  | **read_networks** / **read_network_memberships** | — | The client's communities and memberships |
147
147
  | **create_network_membership** / **delete_network_membership** | networkId | Join/leave a community on instruction |
148
- | **list_contacts** / **search_contacts** / **add_contact** / **remove_contact** | ... | The client's contacts |
149
- | **import_contacts** / **import_gmail_contacts** | ... | Bulk contact import on instruction |
148
+ | **list_contacts** / **search_contacts** / **remove_contact** | ... | The client's contacts |
150
149
  | **scrape_url** | url, objective | Read a link the client pasted (e.g. before drafting a signal from it) |${memoryToolsRows}
151
150
 
152
151
  ## Grounding rules
@@ -56,7 +56,6 @@ export async function createOnboardingTools(deps, preResolvedContext) {
56
56
  userId: deps.userId,
57
57
  networkId: explicitScope.scopeType === "network" ? explicitScope.scopeId : deps.networkId,
58
58
  sessionId: deps.sessionId,
59
- contactsEnabled: deps.contactsEnabled,
60
59
  });
61
60
  if (explicitScope.scopeType && explicitScope.scopeId) {
62
61
  resolvedContext.scopeType = explicitScope.scopeType;
@@ -179,7 +179,6 @@ export async function createReporterTools(deps, preResolvedContext) {
179
179
  userId: deps.userId,
180
180
  networkId: explicitScope.scopeType === "network" ? explicitScope.scopeId : deps.networkId,
181
181
  sessionId: deps.sessionId,
182
- contactsEnabled: deps.contactsEnabled,
183
182
  actionToolsEnabled: deps.actionToolsEnabled,
184
183
  });
185
184
  if (explicitScope.scopeType && explicitScope.scopeId) {
@@ -289,7 +289,6 @@ export async function createSignalTools(deps, preResolvedContext) {
289
289
  userId: deps.userId,
290
290
  networkId: explicitScope.scopeType === "network" ? explicitScope.scopeId : deps.networkId,
291
291
  sessionId: deps.sessionId,
292
- contactsEnabled: deps.contactsEnabled,
293
292
  });
294
293
  if (explicitScope.scopeType && explicitScope.scopeId) {
295
294
  resolvedContext.scopeType = explicitScope.scopeType;
@@ -1,20 +1,17 @@
1
1
  /**
2
2
  * contacts/application — createContactTools canonical implementation.
3
3
  *
4
- * Creates contact management tools for the chat agent. Enables importing,
5
- * listing, adding, removing, and searching the user's personal network.
4
+ * Creates contact management tools for the chat agent. Enables listing,
5
+ * removing, and searching the user's personal network.
6
6
  *
7
- * Feature gating: import_contacts and add_contact are gated behind the
8
- * CONTACTS_ENABLED flag (deps.contactsEnabled). List, remove, and search
9
- * tools are always registered so existing contacts stay manageable.
10
- *
11
- * IND-549: moved from contact/contact.tools.ts into the canonical
12
- * contacts/application layer.
7
+ * Contacts are established by accepting an opportunity opportunity accept
8
+ * and start-chat write the mutual `contact` memberships. There is no import or
9
+ * manual-add path, and no ghost users: every contact is a real account.
13
10
  */
14
11
  import type { DefineTool } from '../../shared/agent/tool.helpers.js';
15
12
  import type { ContactToolDeps } from '../ports/index.js';
16
13
  /**
17
14
  * Creates contact management tools for the chat agent.
18
- * Enables importing, listing, and managing the user's network.
15
+ * Enables listing, searching, and removing entries in the user's network.
19
16
  */
20
17
  export declare function createContactTools(defineTool: DefineTool, deps: ContactToolDeps): any[];
@@ -1,15 +1,12 @@
1
1
  /**
2
2
  * contacts/application — createContactTools canonical implementation.
3
3
  *
4
- * Creates contact management tools for the chat agent. Enables importing,
5
- * listing, adding, removing, and searching the user's personal network.
4
+ * Creates contact management tools for the chat agent. Enables listing,
5
+ * removing, and searching the user's personal network.
6
6
  *
7
- * Feature gating: import_contacts and add_contact are gated behind the
8
- * CONTACTS_ENABLED flag (deps.contactsEnabled). List, remove, and search
9
- * tools are always registered so existing contacts stay manageable.
10
- *
11
- * IND-549: moved from contact/contact.tools.ts into the canonical
12
- * contacts/application layer.
7
+ * Contacts are established by accepting an opportunity opportunity accept
8
+ * and start-chat write the mutual `contact` memberships. There is no import or
9
+ * manual-add path, and no ghost users: every contact is a real account.
13
10
  */
14
11
  import { z } from 'zod';
15
12
  import { success, error } from '../../shared/agent/tool.helpers.js';
@@ -17,58 +14,18 @@ import { protocolLogger } from '../../shared/observability/protocol.logger.js';
17
14
  const logger = protocolLogger('ChatTools:Contact');
18
15
  /**
19
16
  * Creates contact management tools for the chat agent.
20
- * Enables importing, listing, and managing the user's network.
17
+ * Enables listing, searching, and removing entries in the user's network.
21
18
  */
22
19
  export function createContactTools(defineTool, deps) {
23
20
  const { contactService } = deps;
24
- // Contact import / manual-add create ghost users. These are gated behind the
25
- // CONTACTS_ENABLED flag (injected as deps.contactsEnabled). Read/remove/search
26
- // tools below are always available so existing contacts stay manageable.
27
- const contactsEnabled = deps.contactsEnabled === true;
28
- // Only register when enabled: in the registry path defineTool registers as a
29
- // side effect, so the call itself must be gated, not just the returned array.
30
- const import_contacts = contactsEnabled ? defineTool({
31
- name: 'import_contacts',
32
- description: "Bulk-imports contacts into the authenticated user's personal network (personal network). Contacts become members of the user's " +
33
- "personal network with 'contact' permission, making their approved signals eligible for background matching.\n\n" +
34
- "**What happens:** Each contact is matched by email. If the email belongs to an existing user, they're linked directly. " +
35
- "If not, a 'ghost user' is created — a placeholder account enriched with public profile data (from LinkedIn, GitHub, etc.) " +
36
- "that participates in opportunity matching even before the person joins the platform.\n\n" +
37
- "**When to use:** When the user provides a list of contacts to add (from CSV, manual input, or any source other than Gmail). " +
38
- "For Gmail specifically, use import_gmail_contacts instead.\n\n" +
39
- "**Returns:** Import statistics: imported (total processed), skipped (invalid), newContacts (ghost users created), " +
40
- "existingContacts (already in network). Use list_contacts to see all contacts after import.",
41
- querySchema: z.object({
42
- contacts: z.array(z.object({
43
- name: z.string().describe('Full name of the contact (e.g. "Jane Smith")'),
44
- email: z.string().describe('Email address — used as the unique identifier for matching existing users'),
45
- })).describe('Array of contact objects to import. Each must have name and email. Duplicates (by email) are skipped.'),
46
- }),
47
- handler: async ({ context, query }) => {
48
- try {
49
- const result = await contactService.importContacts(context.userId, query.contacts);
50
- return success({
51
- message: `Imported ${result.imported} contacts to your network.`,
52
- imported: result.imported,
53
- skipped: result.skipped,
54
- newContacts: result.newContacts,
55
- existingContacts: result.existingContacts,
56
- });
57
- }
58
- catch (err) {
59
- logger.error('Failed to import contacts', { err });
60
- return error('Failed to import contacts. Please try again.');
61
- }
62
- },
63
- }) : null;
64
21
  const list_contacts = defineTool({
65
22
  name: 'list_contacts',
66
- description: "Lists all contacts in the authenticated user's personal network. Contacts are people the user has added " +
67
- "(via import_contacts, add_contact, or import_gmail_contacts) stored as members of their personal network.\n\n" +
23
+ description: "Lists all contacts in the authenticated user's personal network. Contacts are people the user has " +
24
+ "accepted an opportunity with, stored as members of their personal network.\n\n" +
68
25
  "**When to use:** To see who's in the user's network, find a contact's userId for other operations, " +
69
26
  "or check if a specific person is already a contact.\n\n" +
70
- "**Returns:** Array of contacts, each with userId (use with read_user_contexts), name, email, avatar URL, and isGhost " +
71
- "(true = no account yet, profile enriched from public data). Use read_user_contexts(userId) to get the full profile. " +
27
+ "**Returns:** Array of contacts, each with userId (use with read_user_contexts), name, email, and avatar URL. " +
28
+ "Use read_user_contexts(userId) to get the full profile. " +
72
29
  "Approved signals are matched in the background; list_opportunities only reviews persisted results.",
73
30
  querySchema: z.object({
74
31
  limit: z.number().optional().describe('Maximum number of contacts to return. Omit to return all contacts. Use for large networks to paginate results.'),
@@ -96,38 +53,6 @@ export function createContactTools(defineTool, deps) {
96
53
  }
97
54
  },
98
55
  });
99
- const add_contact = contactsEnabled ? defineTool({
100
- name: 'add_contact',
101
- description: "Adds a single contact to the authenticated user's personal network by email address. " +
102
- "For bulk imports, use import_contacts instead.\n\n" +
103
- "**What happens:** Looks up the email. If an account exists, links that user as a contact. " +
104
- "If not, creates a ghost user (placeholder enriched with public profile data) and adds them. " +
105
- "Their approved signals can then become eligible for background matching within the user's personal network.\n\n" +
106
- "**When to use:** When the user wants to add a specific person (e.g. 'add john@example.com to my network').\n\n" +
107
- "**Returns:** Confirmation with the contact's userId and whether a new ghost user was created (isNewGhost). " +
108
- "Background matching may create persisted opportunities from approved signals; list_opportunities only reviews those results.",
109
- querySchema: z.object({
110
- email: z.string().describe('Email address of the person to add. Used as unique identifier — if already a contact, the operation is idempotent.'),
111
- name: z.string().optional().describe('Full name of the contact. Optional — if omitted, the email prefix is used as name. Provide when known for better profile enrichment.'),
112
- }),
113
- handler: async ({ context, query }) => {
114
- try {
115
- const result = await contactService.addContact(context.userId, query.email, { name: query.name, restore: true });
116
- return success({
117
- added: true,
118
- message: result.isNew
119
- ? `Added ${query.name || query.email} to your network. Their profile is being enriched.`
120
- : `Added ${query.name || query.email} to your network.`,
121
- userId: result.userId,
122
- isNewGhost: result.isNew,
123
- });
124
- }
125
- catch (err) {
126
- logger.error('Failed to add contact', { err });
127
- return error('Failed to add contact. Please try again.');
128
- }
129
- },
130
- }) : null;
131
56
  const remove_contact = defineTool({
132
57
  name: 'remove_contact',
133
58
  description: "Removes a contact from the authenticated user's personal network. The contact relationship is deleted — " +
@@ -181,11 +106,5 @@ export function createContactTools(defineTool, deps) {
181
106
  }
182
107
  },
183
108
  });
184
- return [
185
- ...(import_contacts ? [import_contacts] : []),
186
- ...(add_contact ? [add_contact] : []),
187
- list_contacts,
188
- remove_contact,
189
- search_contacts,
190
- ];
109
+ return [list_contacts, remove_contact, search_contacts];
191
110
  }
@@ -4,19 +4,7 @@
4
4
  * ## Exports
5
5
  *
6
6
  * ### Tool factory
7
- * - `createContactTools` — creates contact management MCP tools (import,
8
- * list, add, remove, search). Write-path tools are gated behind
9
- * deps.contactsEnabled; read/remove/search are always registered.
10
- *
11
- * ### Invite generator
12
- * - `generateInviteMessage` — LLM-based invite message generator for ghost
13
- * users; produces short, contextual messages referencing the opportunity.
14
- *
15
- * IND-549: canonical application layer for the contacts capability.
16
- * Legacy paths:
17
- * - contact/contact.tools.ts → thin compatibility shim pointing here
18
- * - contact/contact.inviter.ts → thin compatibility shim pointing here
7
+ * - `createContactTools` — creates contact management MCP tools
8
+ * (list, remove, search).
19
9
  */
20
10
  export { createContactTools } from "./contact.tools.js";
21
- export { generateInviteMessage } from "./contact.inviter.js";
22
- export type { InviteInput, InviteOutput } from "./contact.inviter.js";
@@ -4,18 +4,7 @@
4
4
  * ## Exports
5
5
  *
6
6
  * ### Tool factory
7
- * - `createContactTools` — creates contact management MCP tools (import,
8
- * list, add, remove, search). Write-path tools are gated behind
9
- * deps.contactsEnabled; read/remove/search are always registered.
10
- *
11
- * ### Invite generator
12
- * - `generateInviteMessage` — LLM-based invite message generator for ghost
13
- * users; produces short, contextual messages referencing the opportunity.
14
- *
15
- * IND-549: canonical application layer for the contacts capability.
16
- * Legacy paths:
17
- * - contact/contact.tools.ts → thin compatibility shim pointing here
18
- * - contact/contact.inviter.ts → thin compatibility shim pointing here
7
+ * - `createContactTools` — creates contact management MCP tools
8
+ * (list, remove, search).
19
9
  */
20
10
  export { createContactTools } from "./contact.tools.js";
21
- export { generateInviteMessage } from "./contact.inviter.js";
@@ -5,29 +5,6 @@
5
5
  *
6
6
  * IND-549: canonical domain layer for the contacts capability.
7
7
  */
8
- /** Input for importing a single contact. */
9
- export interface ContactInput {
10
- name: string;
11
- email: string;
12
- }
13
- /** Result of adding a single contact. */
14
- export interface ContactResult {
15
- userId: string;
16
- isNew: boolean;
17
- isGhost: boolean;
18
- }
19
- /** Result of importing contacts in bulk. */
20
- export interface ContactImportResult {
21
- imported: number;
22
- skipped: number;
23
- newContacts: number;
24
- existingContacts: number;
25
- details: Array<{
26
- email: string;
27
- userId: string;
28
- isNew: boolean;
29
- }>;
30
- }
31
8
  /** Contact with user details, as returned by listContacts. */
32
9
  export interface ContactEntry {
33
10
  userId: string;
@@ -1,16 +1,12 @@
1
1
  /**
2
2
  * contacts/domain — pure contact entity value types and constants.
3
3
  *
4
- * Contains ContactInput, ContactResult, ContactImportResult, ContactEntry,
5
- * and ContactSearchResult.
4
+ * Contains ContactEntry and ContactSearchResult.
6
5
  *
7
6
  * ## What does NOT live here
8
7
  *
9
8
  * - ContactServiceAdapter: persistence port — lives in contacts/ports.
10
9
  * - ContactToolDeps: tool host port — lives in contacts/ports.
11
10
  * - createContactTools: application layer — lives in contacts/application.
12
- * - generateInviteMessage: application layer — lives in contacts/application.
13
- *
14
- * IND-549: canonical domain layer for the contacts capability.
15
11
  */
16
- export type { ContactInput, ContactResult, ContactImportResult, ContactEntry, ContactSearchResult, } from "./contact.types.js";
12
+ export type { ContactEntry, ContactSearchResult, } from "./contact.types.js";
@@ -5,5 +5,5 @@
5
5
  * Supersedes the capabilities/contacts.facade.ts + contacts/public/ pair; the
6
6
  * export list is the union of the facades it replaces, so the contract is unchanged.
7
7
  */
8
- export { createContactTools, generateInviteMessage, } from "./application/index.js";
8
+ export { createContactTools } from "./application/index.js";
9
9
  export type { ContactServiceAdapter, ContactToolDeps, } from "./ports/index.js";
@@ -5,4 +5,4 @@
5
5
  * Supersedes the capabilities/contacts.facade.ts + contacts/public/ pair; the
6
6
  * export list is the union of the facades it replaces, so the contract is unchanged.
7
7
  */
8
- export { createContactTools, generateInviteMessage, } from "./application/index.js";
8
+ export { createContactTools } from "./application/index.js";