@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
@@ -23,9 +23,8 @@ const mcpToolMetadataCache = new Map();
23
23
  * the cached metadata set is automatically invalidated.
24
24
  */
25
25
  export function getMcpToolMetadataCacheKey(deps) {
26
- // CONTACTS_ENABLED deliberately does NOT shape the MCP registry: contact and
27
- // Gmail-import tools are omitted from the MCP surface entirely (IND-596), so
28
- // the flag can never change the MCP tool set.
26
+ // Contact tools are omitted from the MCP surface entirely (IND-596), so no
27
+ // contacts-related input can change the MCP tool set.
29
28
  return [
30
29
  `chat:${deps.chatSession ? '1' : '0'}`,
31
30
  `agent:${deps.agentDatabase ? '1' : '0'}`,
@@ -226,9 +225,9 @@ export const applyNetworkScopeToContext = (context, networkScopeId) => {
226
225
  };
227
226
  export { ONBOARDING_ALLOWED } from './mcp.authorization-policy.js';
228
227
  /**
229
- * Builds the onboarding gate message for MCP callers. Condensed from the
230
- * chat orchestrator's flow (chat.prompt.ts buildOnboarding) into a
231
- * 6-step tool-error guide suited for non-interactive MCP clients.
228
+ * Builds the onboarding gate message for REST Tool API callers. Condensed
229
+ * from the chat onboarding flow into a 6-step tool-error guide. MCP does
230
+ * not use this gate.
232
231
  */
233
232
  export function buildMcpOnboardingMessage(ctx) {
234
233
  const nameStep = ctx.hasName
@@ -335,10 +334,10 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory, policyOpt
335
334
  });
336
335
  }
337
336
  };
338
- // Both snapshots are scoped to this MCP server/connection. Permission and
339
- // onboarding changes therefore apply only after the caller reconnects or
340
- // refreshes its session. They are never shared with the static tool metadata
341
- // cache, so a different server/principal cannot inherit decisions.
337
+ // Both snapshots are scoped to this MCP server/connection. Permission
338
+ // changes therefore apply only after the caller reconnects or refreshes
339
+ // its session. They are never shared with the static tool metadata cache,
340
+ // so a different server/principal cannot inherit decisions.
342
341
  let authenticatedRequest;
343
342
  let resolvedRequest;
344
343
  const extractAuthInput = (httpReq) => ({
@@ -373,11 +372,10 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory, policyOpt
373
372
  return {
374
373
  identity,
375
374
  agent,
376
- // Onboarding can only reduce access, so this snapshot is sufficient to
377
- // reject forged/hidden calls before resolving chat context.
375
+ // This snapshot is sufficient to reject forged/hidden calls before
376
+ // resolving chat context.
378
377
  preliminarySubject: resolveMcpCapabilitySubject({
379
378
  identity,
380
- isOnboarding: false,
381
379
  agent,
382
380
  }),
383
381
  };
@@ -392,7 +390,6 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory, policyOpt
392
390
  const context = await resolveChatContext({
393
391
  database: deps.database,
394
392
  userId: authenticated.identity.userId,
395
- contactsEnabled: deps.contactsEnabled,
396
393
  });
397
394
  context.isMcp = true;
398
395
  if (authenticated.identity.agentId) {
@@ -410,7 +407,6 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory, policyOpt
410
407
  applyNetworkScopeToContext(context, authenticated.identity.networkScopeId);
411
408
  const subject = resolveMcpCapabilitySubject({
412
409
  identity: authenticated.identity,
413
- isOnboarding: context.isOnboarding,
414
410
  agent: authenticated.agent,
415
411
  });
416
412
  // Bind the typed resolved caller context so tools with
@@ -425,10 +421,8 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory, policyOpt
425
421
  })();
426
422
  return resolvedRequest;
427
423
  };
428
- const capabilityDeniedResult = (decision, context) => {
429
- const message = decision.reason === 'onboarding_required' && context
430
- ? buildMcpOnboardingMessage(context)
431
- : 'This capability is not available to the authenticated principal. Reconnect or refresh the session after an administrator changes permissions.';
424
+ const capabilityDeniedResult = (_decision) => {
425
+ const message = 'This capability is not available to the authenticated principal. Reconnect or refresh the session after an administrator changes permissions.';
432
426
  return {
433
427
  content: [{
434
428
  type: 'text',
@@ -482,7 +476,7 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory, policyOpt
482
476
  subject: resolved.subject,
483
477
  decision,
484
478
  });
485
- return capabilityDeniedResult(decision, resolved.context);
479
+ return capabilityDeniedResult(decision);
486
480
  }
487
481
  const { identity, context } = resolved;
488
482
  const { userId, agentId } = identity;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Fan-out driver: run one bilateral negotiation per candidate.
3
+ *
4
+ * Split out of `negotiation.graph.ts`, which held the graph, its four nodes and
5
+ * this driver in one file. Import from `negotiation.graph.js` — it re-exports
6
+ * everything here.
7
+ */
8
+ import type { TraceEmitter } from "../../shared/observability/request-context.js";
9
+ import type { OpportunityStatus } from "../../shared/interfaces/database.interface.js";
10
+ import { type NegotiationTurn, type NegotiationOutcome, type UserNegotiationContext, type NegotiationGraphLike } from "../domain/negotiation.state.js";
11
+ export interface NegotiationCandidate {
12
+ userId: string;
13
+ /** Exact opportunity-bound source and candidate intent IDs. */
14
+ sourceIntentId?: string;
15
+ candidateIntentId?: string;
16
+ /** Per-opportunity source context when its exact actor intent differs across a fan-out. */
17
+ sourceUser?: UserNegotiationContext;
18
+ reasoning: string;
19
+ valencyRole: string;
20
+ networkId?: string;
21
+ candidateUser: UserNegotiationContext;
22
+ /** The explicit search query that triggered discovery (if any). */
23
+ discoveryQuery?: string;
24
+ /**
25
+ * ID of the opportunity this negotiation is for. When set, the negotiation
26
+ * graph's finalize node updates the opportunity's status based on the outcome
27
+ * (`accept` → 'pending', `reject` → 'rejected', otherwise → 'stalled').
28
+ */
29
+ opportunityId?: string;
30
+ /** Exact persisted lifecycle state claimed by this negotiation attempt. */
31
+ opportunityStatus?: OpportunityStatus;
32
+ opportunityUpdatedAt?: Date;
33
+ }
34
+ export interface NegotiationResult {
35
+ userId: string;
36
+ agreedRoles: NegotiationOutcome["agreedRoles"];
37
+ reasoning: string;
38
+ turnCount: number;
39
+ }
40
+ /**
41
+ * Per-candidate resolution hook — fires as each negotiation settles, before
42
+ * Promise.all aggregates. Awaited so the caller can run async work before the
43
+ * next settle.
44
+ *
45
+ * `turns` and `outcome` are passed through from the underlying negotiation
46
+ * graph so consumers can build per-candidate decision-question inputs without
47
+ * re-walking trace events or DB artifacts. Both are present on every
48
+ * resolution (accepted, rejected, stalled, error); error paths receive a
49
+ * synthesized `outcome` with `hasOpportunity: false`.
50
+ */
51
+ export type OnNegotiationResolved = (entry: {
52
+ candidate: NegotiationCandidate;
53
+ accepted: NegotiationResult | null;
54
+ turns: NegotiationTurn[];
55
+ outcome: NegotiationOutcome;
56
+ continuationReceipt?: import('../../shared/interfaces/database.interface.js').NegotiationContinuationReceipt;
57
+ }) => Promise<void>;
58
+ /**
59
+ * Runs bilateral negotiation for each candidate in parallel.
60
+ * @returns Only candidates that produced an opportunity
61
+ */
62
+ export declare function negotiateCandidates(negotiationGraph: NegotiationGraphLike, sourceUser: UserNegotiationContext, candidates: NegotiationCandidate[], indexContext: {
63
+ networkId: string;
64
+ prompt: string;
65
+ }, opts?: {
66
+ maxTurns?: number;
67
+ traceEmitter?: TraceEmitter;
68
+ indexContextOverrides?: Map<string, string>;
69
+ timeoutMs?: number;
70
+ onCandidateResolved?: OnNegotiationResolved;
71
+ /**
72
+ * Initiator seat for every candidate session in this fan-out (v2 stamp).
73
+ * Passed through to the negotiation graph, which may still override it by
74
+ * inheriting from a prior task on the same opportunities/conversation.
75
+ */
76
+ initiatorUserId?: string;
77
+ /** Exact settled task to resume; only the durable run-existing path sets this. */
78
+ resumeFromTaskId?: string;
79
+ /** Deterministic settlement key paired with resumeFromTaskId. */
80
+ continuationSettlementId?: string;
81
+ /** Current durable lease/fence for this exact continuation successor. */
82
+ continuationExecution?: import('../../shared/interfaces/database.interface.js').NegotiationContinuationExecution;
83
+ }): Promise<NegotiationResult[]>;
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Fan-out driver: run one bilateral negotiation per candidate.
3
+ *
4
+ * Split out of `negotiation.graph.ts`, which held the graph, its four nodes and
5
+ * this driver in one file. Import from `negotiation.graph.js` — it re-exports
6
+ * everything here.
7
+ */
8
+ import { invokeWithAbortSignal } from "../../shared/agent/model-signal.js";
9
+ import { negotiateCandidatesLog, turnsFromMessages } from "./negotiation.graph.shared.js";
10
+ /**
11
+ * Runs bilateral negotiation for each candidate in parallel.
12
+ * @returns Only candidates that produced an opportunity
13
+ */
14
+ export async function negotiateCandidates(negotiationGraph, sourceUser, candidates, indexContext, opts) {
15
+ const { maxTurns, traceEmitter, indexContextOverrides, timeoutMs, onCandidateResolved, initiatorUserId, resumeFromTaskId, continuationSettlementId, continuationExecution, } = opts ?? {};
16
+ // Local helper to emit events whose shape is wider than the declared
17
+ // `TraceEmitter` union (mirrors the cast used in chat.agent at the relay sink
18
+ // and inside turn/finalize nodes above).
19
+ const emitWide = (event) => traceEmitter?.(event);
20
+ const results = await Promise.all(candidates.map(async (candidate) => {
21
+ const start = Date.now();
22
+ const candidateSourceUser = candidate.sourceUser ?? sourceUser;
23
+ if (candidate.opportunityId) {
24
+ const candidateName = candidate.candidateUser?.profile?.name;
25
+ emitWide({
26
+ type: "negotiation_session_start",
27
+ opportunityId: candidate.opportunityId,
28
+ negotiationConversationId: "", // filled in on session_end
29
+ sourceUserId: candidateSourceUser.id,
30
+ candidateUserId: candidate.userId,
31
+ initiatorUserId: initiatorUserId ?? candidateSourceUser.id,
32
+ ...(candidateName && { candidateName }),
33
+ startedAt: start,
34
+ });
35
+ }
36
+ traceEmitter?.({ type: "agent_start", name: "Negotiating candidate" });
37
+ try {
38
+ const candidateIndexContext = candidate.networkId
39
+ ? { networkId: candidate.networkId, prompt: indexContextOverrides?.get(candidate.networkId) ?? '' }
40
+ : indexContext;
41
+ const result = await invokeWithAbortSignal(negotiationGraph, {
42
+ sourceUser: candidateSourceUser,
43
+ candidateUser: candidate.candidateUser,
44
+ ...(candidate.sourceIntentId && { sourceIntentId: candidate.sourceIntentId }),
45
+ ...(candidate.candidateIntentId && { candidateIntentId: candidate.candidateIntentId }),
46
+ indexContext: candidateIndexContext,
47
+ seedAssessment: {
48
+ reasoning: candidate.reasoning,
49
+ valencyRole: candidate.valencyRole,
50
+ },
51
+ ...(candidate.discoveryQuery && { discoveryQuery: candidate.discoveryQuery }),
52
+ ...(candidate.opportunityId && { opportunityId: candidate.opportunityId }),
53
+ ...(candidate.opportunityStatus && { opportunityStatus: candidate.opportunityStatus }),
54
+ ...(candidate.opportunityUpdatedAt && { opportunityUpdatedAt: candidate.opportunityUpdatedAt }),
55
+ ...(initiatorUserId && { initiatorUserId }),
56
+ ...(resumeFromTaskId && continuationSettlementId && continuationExecution ? {
57
+ resumeFromTaskId,
58
+ continuationSettlementId,
59
+ continuationExecution,
60
+ } : {}),
61
+ ...(maxTurns !== undefined && { maxTurns }),
62
+ ...(timeoutMs !== undefined && { timeoutMs }),
63
+ });
64
+ const durationMs = Date.now() - start;
65
+ const outcome = result.outcome;
66
+ const hasOpportunity = outcome?.hasOpportunity === true;
67
+ const isContinuation = result.isContinuation ?? false;
68
+ const priorTurnCount = result.priorTurnCount ?? 0;
69
+ const turnFlow = (result.messages ?? [])
70
+ .map((m) => {
71
+ const dataPart = m.parts?.find((p) => p.kind === "data");
72
+ if (!dataPart?.data)
73
+ return null;
74
+ const turn = dataPart.data;
75
+ return turn.action ?? "unknown";
76
+ })
77
+ .filter(Boolean)
78
+ .join(" → ");
79
+ const statusTag = hasOpportunity ? "✓ opportunity" : "✗ rejected";
80
+ traceEmitter?.({ type: "agent_end", name: "Negotiating candidate", durationMs, summary: `${candidate.userId}: ${turnFlow} ${statusTag}` });
81
+ if (candidate.opportunityId) {
82
+ emitWide({
83
+ type: "negotiation_session_end",
84
+ opportunityId: candidate.opportunityId,
85
+ negotiationConversationId: result.conversationId ?? "",
86
+ durationMs: Date.now() - start,
87
+ isContinuation,
88
+ turnsAdded: outcome?.turnCount ?? 0,
89
+ priorTurnCount,
90
+ });
91
+ }
92
+ const accepted = hasOpportunity && outcome
93
+ ? {
94
+ userId: candidate.userId,
95
+ agreedRoles: outcome.agreedRoles,
96
+ reasoning: outcome.reasoning,
97
+ turnCount: outcome.turnCount,
98
+ }
99
+ : null;
100
+ if (onCandidateResolved) {
101
+ const turnHistory = turnsFromMessages(result.messages ?? []);
102
+ const resolvedOutcome = result.outcome ?? {
103
+ hasOpportunity: false,
104
+ agreedRoles: [],
105
+ reasoning: "no outcome returned by negotiation graph",
106
+ turnCount: turnHistory.length,
107
+ };
108
+ try {
109
+ await onCandidateResolved({
110
+ candidate,
111
+ accepted,
112
+ turns: turnHistory,
113
+ outcome: resolvedOutcome,
114
+ ...(result.continuationReceipt ? { continuationReceipt: result.continuationReceipt } : {}),
115
+ });
116
+ }
117
+ catch (hookErr) {
118
+ // Hook failures must not sink the candidate result — the aggregate
119
+ // return remains useful to the caller.
120
+ negotiateCandidatesLog.error("onCandidateResolved hook threw", {
121
+ candidateUserId: candidate.userId,
122
+ error: hookErr,
123
+ });
124
+ }
125
+ }
126
+ return accepted;
127
+ }
128
+ catch (err) {
129
+ const durationMs = Date.now() - start;
130
+ traceEmitter?.({ type: "agent_end", name: "Negotiating candidate", durationMs, summary: `${candidate.userId}: error` });
131
+ if (candidate.opportunityId) {
132
+ emitWide({
133
+ type: "negotiation_session_end",
134
+ opportunityId: candidate.opportunityId,
135
+ negotiationConversationId: "",
136
+ durationMs: Date.now() - start,
137
+ });
138
+ }
139
+ negotiateCandidatesLog.error("Negotiation failed", { candidateUserId: candidate.userId, error: err });
140
+ if (onCandidateResolved) {
141
+ try {
142
+ await onCandidateResolved({
143
+ candidate,
144
+ accepted: null,
145
+ turns: [],
146
+ outcome: {
147
+ hasOpportunity: false,
148
+ agreedRoles: [],
149
+ reasoning: err instanceof Error ? err.message : String(err),
150
+ turnCount: 0,
151
+ },
152
+ });
153
+ }
154
+ catch {
155
+ // ignore hook failure on error path
156
+ }
157
+ }
158
+ return null;
159
+ }
160
+ }));
161
+ return results.filter((r) => r !== null);
162
+ }