@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.
- package/CHANGELOG.md +107 -0
- package/IMPLEMENTATION.md +50 -52
- package/STABILITY.md +3 -3
- package/dist/chat/chat.agent.js +2 -2
- package/dist/chat/chat.graph.d.ts +41 -1
- package/dist/chat/chat.graph.js +108 -127
- package/dist/discovery/hyde.frame.d.ts +4 -4
- package/dist/discovery/hyde.graph.d.ts +71 -12
- package/dist/discovery/hyde.graph.js +337 -335
- package/dist/discovery/lens.inferrer.d.ts +6 -6
- package/dist/enrichment/enrichment.graph.d.ts +127 -9
- package/dist/enrichment/enrichment.graph.js +633 -655
- package/dist/enrichment/enrichment.state.d.ts +1 -1
- package/dist/enrichment/enrichment.tools.context-read.d.ts +10 -0
- package/dist/enrichment/enrichment.tools.context-read.js +353 -0
- package/dist/enrichment/enrichment.tools.context-write.d.ts +9 -0
- package/dist/enrichment/enrichment.tools.context-write.js +417 -0
- package/dist/enrichment/enrichment.tools.d.ts +11 -1
- package/dist/enrichment/enrichment.tools.helpers.d.ts +139 -0
- package/dist/enrichment/enrichment.tools.helpers.js +234 -0
- package/dist/enrichment/enrichment.tools.js +16 -975
- package/dist/index.d.ts +27 -48
- package/dist/index.js +17 -27
- package/dist/intents/application/intent.graph.d.ts +71 -67
- package/dist/intents/application/intent.graph.execute.d.ts +59 -0
- package/dist/intents/application/intent.graph.execute.js +301 -0
- package/dist/intents/application/intent.graph.infer.d.ts +44 -0
- package/dist/intents/application/intent.graph.infer.js +97 -0
- package/dist/intents/application/intent.graph.js +96 -888
- package/dist/intents/application/intent.graph.reconcile.d.ts +71 -0
- package/dist/intents/application/intent.graph.reconcile.js +274 -0
- package/dist/intents/application/intent.graph.shared.d.ts +70 -0
- package/dist/intents/application/intent.graph.shared.js +153 -0
- package/dist/intents/domain/intent.state.d.ts +1 -1
- package/dist/maintenance/maintenance.graph.d.ts +66 -3
- package/dist/maintenance/maintenance.graph.js +155 -156
- package/dist/mcp/mcp.authorization-policy.d.ts +24 -28
- package/dist/mcp/mcp.authorization-policy.js +14 -34
- package/dist/mcp/mcp.server.d.ts +3 -3
- package/dist/mcp/mcp.server.js +12 -16
- package/dist/negotiations/application/negotiation.candidates.d.ts +83 -0
- package/dist/negotiations/application/negotiation.candidates.js +162 -0
- package/dist/negotiations/application/negotiation.graph.d.ts +91 -157
- package/dist/negotiations/application/negotiation.graph.finalize.d.ts +5 -0
- package/dist/negotiations/application/negotiation.graph.finalize.js +280 -0
- package/dist/negotiations/application/negotiation.graph.init.d.ts +54 -0
- package/dist/negotiations/application/negotiation.graph.init.js +227 -0
- package/dist/negotiations/application/negotiation.graph.js +69 -1388
- package/dist/negotiations/application/negotiation.graph.screen.d.ts +23 -0
- package/dist/negotiations/application/negotiation.graph.screen.js +108 -0
- package/dist/negotiations/application/negotiation.graph.shared.d.ts +75 -0
- package/dist/negotiations/application/negotiation.graph.shared.js +125 -0
- package/dist/negotiations/application/negotiation.graph.turn.d.ts +105 -0
- package/dist/negotiations/application/negotiation.graph.turn.js +484 -0
- package/dist/negotiations/domain/negotiation.state.d.ts +1 -1
- package/dist/negotiations/domain/negotiation.state.js +0 -1
- package/dist/networks/application/indexer.graph.d.ts +165 -7
- package/dist/networks/application/indexer.graph.js +339 -388
- package/dist/networks/application/indexer.state.d.ts +1 -1
- package/dist/networks/application/membership.graph.d.ts +83 -5
- package/dist/networks/application/membership.graph.js +177 -207
- package/dist/networks/application/network.graph.d.ts +148 -5
- package/dist/networks/application/network.graph.js +249 -278
- package/dist/networks/domain/membership.state.d.ts +1 -1
- package/dist/networks/domain/network.state.d.ts +1 -1
- package/dist/opportunities/application/delivery-card.cache.d.ts +1 -1
- package/dist/opportunities/application/delivery-card.cache.js +2 -2
- package/dist/opportunities/application/index.d.ts +2 -2
- package/dist/opportunities/application/index.js +2 -2
- package/dist/opportunities/application/opportunity.evaluator.js +1 -1
- package/dist/opportunities/application/opportunity.graph.d.ts +686 -473
- package/dist/opportunities/application/opportunity.graph.discovery-strategies.d.ts +109 -0
- package/dist/opportunities/application/opportunity.graph.discovery-strategies.js +451 -0
- package/dist/opportunities/application/opportunity.graph.discovery.d.ts +69 -0
- package/dist/opportunities/application/opportunity.graph.discovery.js +397 -0
- package/dist/opportunities/application/opportunity.graph.evaluation.d.ts +82 -0
- package/dist/opportunities/application/opportunity.graph.evaluation.js +608 -0
- package/dist/opportunities/application/opportunity.graph.js +120 -3690
- package/dist/opportunities/application/opportunity.graph.modes.d.ts +538 -0
- package/dist/opportunities/application/opportunity.graph.modes.js +538 -0
- package/dist/opportunities/application/opportunity.graph.negotiate.d.ts +109 -0
- package/dist/opportunities/application/opportunity.graph.negotiate.js +392 -0
- package/dist/opportunities/application/opportunity.graph.persist-node.d.ts +93 -0
- package/dist/opportunities/application/opportunity.graph.persist-node.js +765 -0
- package/dist/opportunities/application/opportunity.graph.prep.d.ts +150 -0
- package/dist/opportunities/application/opportunity.graph.prep.js +381 -0
- package/dist/opportunities/application/opportunity.graph.shared.d.ts +173 -0
- package/dist/opportunities/application/opportunity.graph.shared.js +199 -0
- package/dist/opportunities/application/opportunity.presentation.d.ts +399 -0
- package/dist/opportunities/application/{opportunity.presenter.js → opportunity.presentation.js} +735 -9
- package/dist/opportunities/application/opportunity.tools.cards.d.ts +152 -0
- package/dist/opportunities/application/opportunity.tools.cards.js +235 -0
- package/dist/opportunities/application/opportunity.tools.d.ts +8 -114
- package/dist/opportunities/application/opportunity.tools.js +28 -711
- package/dist/opportunities/application/opportunity.tools.list.d.ts +10 -0
- package/dist/opportunities/application/opportunity.tools.list.js +493 -0
- package/dist/opportunities/domain/index.d.ts +3 -3
- package/dist/opportunities/domain/index.js +3 -3
- package/dist/opportunities/domain/opportunity.state.d.ts +19 -19
- package/dist/opportunities/index.d.ts +6 -6
- package/dist/opportunities/index.js +4 -4
- package/dist/opportunities/ports/opportunity.tools.port.d.ts +1 -1
- package/dist/opportunities/radar/radar.graph.d.ts +57 -13
- package/dist/opportunities/radar/radar.graph.js +470 -471
- package/dist/premises/premise.graph.d.ts +88 -20
- package/dist/premises/premise.graph.js +207 -218
- package/dist/questions/domain/question.schema.d.ts +40 -40
- package/dist/shared/agent/tool.helpers.d.ts +21 -0
- package/dist/shared/agent/tool.registry.d.ts +4 -3
- package/dist/shared/agent/tool.registry.js +1 -1
- package/dist/shared/interfaces/database.capabilities.d.ts +100 -0
- package/dist/shared/interfaces/database.capabilities.js +7 -0
- package/dist/shared/interfaces/database.entities.d.ts +533 -0
- package/dist/shared/interfaces/database.entities.js +10 -0
- package/dist/shared/interfaces/database.identity-queries.d.ts +294 -0
- package/dist/shared/interfaces/database.identity-queries.js +4 -0
- package/dist/shared/interfaces/database.interface.d.ts +15 -2277
- package/dist/shared/interfaces/database.interface.js +8 -0
- package/dist/shared/interfaces/database.member-queries.d.ts +228 -0
- package/dist/shared/interfaces/database.member-queries.js +4 -0
- package/dist/shared/interfaces/database.negotiation.d.ts +305 -0
- package/dist/shared/interfaces/database.negotiation.js +26 -0
- package/dist/shared/interfaces/database.network-queries.d.ts +277 -0
- package/dist/shared/interfaces/database.network-queries.js +4 -0
- package/dist/shared/interfaces/database.opportunity-queries.d.ts +285 -0
- package/dist/shared/interfaces/database.opportunity-queries.js +4 -0
- package/dist/shared/interfaces/database.port.d.ts +322 -0
- package/dist/shared/interfaces/database.port.js +29 -0
- package/dist/shared/schemas/discovery-question.schema.d.ts +14 -14
- package/dist/shared/schemas/negotiation-digest.schema.d.ts +2 -2
- package/package.json +2 -15
- package/dist/opportunities/application/opportunity.card-presentation.d.ts +0 -44
- package/dist/opportunities/application/opportunity.card-presentation.js +0 -93
- package/dist/opportunities/application/opportunity.presenter.d.ts +0 -156
- package/dist/opportunities/domain/opportunity.presentation-cache.d.ts +0 -5
- package/dist/opportunities/domain/opportunity.presentation-cache.js +0 -11
- package/dist/opportunities/domain/opportunity.presentation.d.ts +0 -76
- package/dist/opportunities/domain/opportunity.presentation.js +0 -516
- package/dist/opportunities/domain/opportunity.safe-presentation.d.ts +0 -104
- package/dist/opportunities/domain/opportunity.safe-presentation.js +0 -102
package/dist/mcp/mcp.server.js
CHANGED
|
@@ -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
|
|
230
|
-
* chat
|
|
231
|
-
*
|
|
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
|
|
339
|
-
//
|
|
340
|
-
//
|
|
341
|
-
//
|
|
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
|
-
//
|
|
377
|
-
//
|
|
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 = (
|
|
429
|
-
const message =
|
|
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
|
|
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
|
+
}
|