@indexnetwork/protocol 14.3.2-rc.477.1 → 16.1.1-rc.478.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 (140) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/IMPLEMENTATION.md +50 -52
  3. package/STABILITY.md +3 -3
  4. package/dist/chat/chat.agent.js +2 -2
  5. package/dist/chat/chat.graph.d.ts +41 -1
  6. package/dist/chat/chat.graph.js +108 -127
  7. package/dist/discovery/hyde.frame.d.ts +4 -4
  8. package/dist/discovery/hyde.graph.d.ts +71 -12
  9. package/dist/discovery/hyde.graph.js +337 -335
  10. package/dist/discovery/lens.inferrer.d.ts +6 -6
  11. package/dist/enrichment/enrichment.graph.d.ts +127 -9
  12. package/dist/enrichment/enrichment.graph.js +633 -655
  13. package/dist/enrichment/enrichment.state.d.ts +1 -1
  14. package/dist/enrichment/enrichment.tools.context-read.d.ts +10 -0
  15. package/dist/enrichment/enrichment.tools.context-read.js +353 -0
  16. package/dist/enrichment/enrichment.tools.context-write.d.ts +9 -0
  17. package/dist/enrichment/enrichment.tools.context-write.js +417 -0
  18. package/dist/enrichment/enrichment.tools.d.ts +11 -1
  19. package/dist/enrichment/enrichment.tools.helpers.d.ts +139 -0
  20. package/dist/enrichment/enrichment.tools.helpers.js +234 -0
  21. package/dist/enrichment/enrichment.tools.js +16 -975
  22. package/dist/index.d.ts +27 -48
  23. package/dist/index.js +17 -27
  24. package/dist/intents/application/intent.graph.d.ts +71 -67
  25. package/dist/intents/application/intent.graph.execute.d.ts +59 -0
  26. package/dist/intents/application/intent.graph.execute.js +301 -0
  27. package/dist/intents/application/intent.graph.infer.d.ts +44 -0
  28. package/dist/intents/application/intent.graph.infer.js +97 -0
  29. package/dist/intents/application/intent.graph.js +96 -888
  30. package/dist/intents/application/intent.graph.reconcile.d.ts +71 -0
  31. package/dist/intents/application/intent.graph.reconcile.js +274 -0
  32. package/dist/intents/application/intent.graph.shared.d.ts +70 -0
  33. package/dist/intents/application/intent.graph.shared.js +153 -0
  34. package/dist/intents/domain/intent.state.d.ts +1 -1
  35. package/dist/maintenance/maintenance.graph.d.ts +66 -3
  36. package/dist/maintenance/maintenance.graph.js +155 -156
  37. package/dist/mcp/mcp.authorization-policy.d.ts +24 -28
  38. package/dist/mcp/mcp.authorization-policy.js +14 -34
  39. package/dist/mcp/mcp.server.d.ts +3 -3
  40. package/dist/mcp/mcp.server.js +12 -16
  41. package/dist/negotiations/application/negotiation.candidates.d.ts +83 -0
  42. package/dist/negotiations/application/negotiation.candidates.js +162 -0
  43. package/dist/negotiations/application/negotiation.graph.d.ts +91 -157
  44. package/dist/negotiations/application/negotiation.graph.finalize.d.ts +5 -0
  45. package/dist/negotiations/application/negotiation.graph.finalize.js +280 -0
  46. package/dist/negotiations/application/negotiation.graph.init.d.ts +54 -0
  47. package/dist/negotiations/application/negotiation.graph.init.js +227 -0
  48. package/dist/negotiations/application/negotiation.graph.js +69 -1388
  49. package/dist/negotiations/application/negotiation.graph.screen.d.ts +23 -0
  50. package/dist/negotiations/application/negotiation.graph.screen.js +108 -0
  51. package/dist/negotiations/application/negotiation.graph.shared.d.ts +75 -0
  52. package/dist/negotiations/application/negotiation.graph.shared.js +125 -0
  53. package/dist/negotiations/application/negotiation.graph.turn.d.ts +105 -0
  54. package/dist/negotiations/application/negotiation.graph.turn.js +484 -0
  55. package/dist/negotiations/domain/negotiation.state.d.ts +1 -1
  56. package/dist/negotiations/domain/negotiation.state.js +0 -1
  57. package/dist/networks/application/indexer.graph.d.ts +165 -7
  58. package/dist/networks/application/indexer.graph.js +339 -388
  59. package/dist/networks/application/indexer.state.d.ts +1 -1
  60. package/dist/networks/application/membership.graph.d.ts +83 -5
  61. package/dist/networks/application/membership.graph.js +177 -207
  62. package/dist/networks/application/network.graph.d.ts +148 -5
  63. package/dist/networks/application/network.graph.js +249 -278
  64. package/dist/networks/domain/membership.state.d.ts +1 -1
  65. package/dist/networks/domain/network.state.d.ts +1 -1
  66. package/dist/opportunities/application/delivery-card.cache.d.ts +1 -1
  67. package/dist/opportunities/application/delivery-card.cache.js +2 -2
  68. package/dist/opportunities/application/index.d.ts +2 -2
  69. package/dist/opportunities/application/index.js +2 -2
  70. package/dist/opportunities/application/opportunity.evaluator.js +1 -1
  71. package/dist/opportunities/application/opportunity.graph.d.ts +686 -473
  72. package/dist/opportunities/application/opportunity.graph.discovery-strategies.d.ts +109 -0
  73. package/dist/opportunities/application/opportunity.graph.discovery-strategies.js +451 -0
  74. package/dist/opportunities/application/opportunity.graph.discovery.d.ts +69 -0
  75. package/dist/opportunities/application/opportunity.graph.discovery.js +397 -0
  76. package/dist/opportunities/application/opportunity.graph.evaluation.d.ts +82 -0
  77. package/dist/opportunities/application/opportunity.graph.evaluation.js +608 -0
  78. package/dist/opportunities/application/opportunity.graph.js +120 -3690
  79. package/dist/opportunities/application/opportunity.graph.modes.d.ts +538 -0
  80. package/dist/opportunities/application/opportunity.graph.modes.js +538 -0
  81. package/dist/opportunities/application/opportunity.graph.negotiate.d.ts +109 -0
  82. package/dist/opportunities/application/opportunity.graph.negotiate.js +392 -0
  83. package/dist/opportunities/application/opportunity.graph.persist-node.d.ts +93 -0
  84. package/dist/opportunities/application/opportunity.graph.persist-node.js +765 -0
  85. package/dist/opportunities/application/opportunity.graph.prep.d.ts +150 -0
  86. package/dist/opportunities/application/opportunity.graph.prep.js +381 -0
  87. package/dist/opportunities/application/opportunity.graph.shared.d.ts +173 -0
  88. package/dist/opportunities/application/opportunity.graph.shared.js +199 -0
  89. package/dist/opportunities/application/opportunity.presentation.d.ts +399 -0
  90. package/dist/opportunities/application/{opportunity.presenter.js → opportunity.presentation.js} +735 -9
  91. package/dist/opportunities/application/opportunity.tools.cards.d.ts +152 -0
  92. package/dist/opportunities/application/opportunity.tools.cards.js +235 -0
  93. package/dist/opportunities/application/opportunity.tools.d.ts +8 -114
  94. package/dist/opportunities/application/opportunity.tools.js +28 -711
  95. package/dist/opportunities/application/opportunity.tools.list.d.ts +10 -0
  96. package/dist/opportunities/application/opportunity.tools.list.js +493 -0
  97. package/dist/opportunities/domain/index.d.ts +3 -3
  98. package/dist/opportunities/domain/index.js +3 -3
  99. package/dist/opportunities/domain/opportunity.state.d.ts +19 -19
  100. package/dist/opportunities/index.d.ts +6 -6
  101. package/dist/opportunities/index.js +4 -4
  102. package/dist/opportunities/ports/opportunity.tools.port.d.ts +1 -1
  103. package/dist/opportunities/radar/radar.graph.d.ts +57 -13
  104. package/dist/opportunities/radar/radar.graph.js +470 -471
  105. package/dist/premises/premise.graph.d.ts +88 -20
  106. package/dist/premises/premise.graph.js +207 -218
  107. package/dist/questions/domain/question.schema.d.ts +40 -40
  108. package/dist/shared/agent/tool.helpers.d.ts +21 -0
  109. package/dist/shared/agent/tool.registry.d.ts +4 -3
  110. package/dist/shared/agent/tool.registry.js +1 -1
  111. package/dist/shared/interfaces/database.capabilities.d.ts +100 -0
  112. package/dist/shared/interfaces/database.capabilities.js +7 -0
  113. package/dist/shared/interfaces/database.entities.d.ts +533 -0
  114. package/dist/shared/interfaces/database.entities.js +10 -0
  115. package/dist/shared/interfaces/database.identity-queries.d.ts +294 -0
  116. package/dist/shared/interfaces/database.identity-queries.js +4 -0
  117. package/dist/shared/interfaces/database.interface.d.ts +15 -2277
  118. package/dist/shared/interfaces/database.interface.js +8 -0
  119. package/dist/shared/interfaces/database.member-queries.d.ts +228 -0
  120. package/dist/shared/interfaces/database.member-queries.js +4 -0
  121. package/dist/shared/interfaces/database.negotiation.d.ts +305 -0
  122. package/dist/shared/interfaces/database.negotiation.js +26 -0
  123. package/dist/shared/interfaces/database.network-queries.d.ts +277 -0
  124. package/dist/shared/interfaces/database.network-queries.js +4 -0
  125. package/dist/shared/interfaces/database.opportunity-queries.d.ts +285 -0
  126. package/dist/shared/interfaces/database.opportunity-queries.js +4 -0
  127. package/dist/shared/interfaces/database.port.d.ts +322 -0
  128. package/dist/shared/interfaces/database.port.js +29 -0
  129. package/dist/shared/schemas/discovery-question.schema.d.ts +14 -14
  130. package/dist/shared/schemas/negotiation-digest.schema.d.ts +2 -2
  131. package/package.json +2 -15
  132. package/dist/opportunities/application/opportunity.card-presentation.d.ts +0 -44
  133. package/dist/opportunities/application/opportunity.card-presentation.js +0 -93
  134. package/dist/opportunities/application/opportunity.presenter.d.ts +0 -156
  135. package/dist/opportunities/domain/opportunity.presentation-cache.d.ts +0 -5
  136. package/dist/opportunities/domain/opportunity.presentation-cache.js +0 -11
  137. package/dist/opportunities/domain/opportunity.presentation.d.ts +0 -76
  138. package/dist/opportunities/domain/opportunity.presentation.js +0 -516
  139. package/dist/opportunities/domain/opportunity.safe-presentation.d.ts +0 -104
  140. package/dist/opportunities/domain/opportunity.safe-presentation.js +0 -102
@@ -226,9 +226,9 @@ export const applyNetworkScopeToContext = (context, networkScopeId) => {
226
226
  };
227
227
  export { ONBOARDING_ALLOWED } from './mcp.authorization-policy.js';
228
228
  /**
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.
229
+ * Builds the onboarding gate message for REST Tool API callers. Condensed
230
+ * from the chat onboarding flow into a 6-step tool-error guide. MCP does
231
+ * not use this gate.
232
232
  */
233
233
  export function buildMcpOnboardingMessage(ctx) {
234
234
  const nameStep = ctx.hasName
@@ -335,10 +335,10 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory, policyOpt
335
335
  });
336
336
  }
337
337
  };
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.
338
+ // Both snapshots are scoped to this MCP server/connection. Permission
339
+ // changes therefore apply only after the caller reconnects or refreshes
340
+ // its session. They are never shared with the static tool metadata cache,
341
+ // so a different server/principal cannot inherit decisions.
342
342
  let authenticatedRequest;
343
343
  let resolvedRequest;
344
344
  const extractAuthInput = (httpReq) => ({
@@ -373,11 +373,10 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory, policyOpt
373
373
  return {
374
374
  identity,
375
375
  agent,
376
- // Onboarding can only reduce access, so this snapshot is sufficient to
377
- // reject forged/hidden calls before resolving chat context.
376
+ // This snapshot is sufficient to reject forged/hidden calls before
377
+ // resolving chat context.
378
378
  preliminarySubject: resolveMcpCapabilitySubject({
379
379
  identity,
380
- isOnboarding: false,
381
380
  agent,
382
381
  }),
383
382
  };
@@ -410,7 +409,6 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory, policyOpt
410
409
  applyNetworkScopeToContext(context, authenticated.identity.networkScopeId);
411
410
  const subject = resolveMcpCapabilitySubject({
412
411
  identity: authenticated.identity,
413
- isOnboarding: context.isOnboarding,
414
412
  agent: authenticated.agent,
415
413
  });
416
414
  // Bind the typed resolved caller context so tools with
@@ -425,10 +423,8 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory, policyOpt
425
423
  })();
426
424
  return resolvedRequest;
427
425
  };
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.';
426
+ const capabilityDeniedResult = (_decision) => {
427
+ const message = 'This capability is not available to the authenticated principal. Reconnect or refresh the session after an administrator changes permissions.';
432
428
  return {
433
429
  content: [{
434
430
  type: 'text',
@@ -482,7 +478,7 @@ export function createMcpServer(deps, authResolver, scopedDepsFactory, policyOpt
482
478
  subject: resolved.subject,
483
479
  decision,
484
480
  });
485
- return capabilityDeniedResult(decision, resolved.context);
481
+ return capabilityDeniedResult(decision);
486
482
  }
487
483
  const { identity, context } = resolved;
488
484
  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
+ }