@indexnetwork/protocol 6.14.0-rc.406.1 → 8.0.1-rc.417.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 +307 -0
- package/IMPLEMENTATION.md +7 -4
- package/dist/capabilities/opportunities.facade.d.ts +4 -0
- package/dist/capabilities/opportunities.facade.js +4 -0
- package/dist/capabilities/signals.tools.port.d.ts +1 -1
- package/dist/chat/chat-streaming.types.d.ts +4 -5
- package/dist/chat/chat.agent.d.ts +0 -19
- package/dist/chat/chat.agent.js +1 -70
- package/dist/chat/chat.persona.d.ts +0 -6
- package/dist/chat/chat.persona.js +0 -1
- package/dist/chat/chat.prompt.js +10 -10
- package/dist/chat/chat.prompt.modules.js +0 -104
- package/dist/chat/chat.tools.js +11 -7
- package/dist/chat/negotiator.persona.js +0 -1
- package/dist/chat/onboarding.persona.js +0 -1
- package/dist/chat/reporter.persona.d.ts +1 -1
- package/dist/chat/reporter.persona.js +1 -2
- package/dist/chat/reporter.prompt.js +5 -5
- package/dist/chat/signal.persona.js +0 -1
- package/dist/communities/application/network.tools.js +48 -27
- package/dist/contacts/application/contact.tools.js +9 -9
- package/dist/index.d.ts +9 -2
- package/dist/index.js +5 -0
- package/dist/maintenance/maintenance.graph.js +7 -0
- package/dist/mcp/mcp.authorization-policy.d.ts +250 -0
- package/dist/mcp/mcp.authorization-policy.js +522 -0
- package/dist/mcp/mcp.server.d.ts +6 -29
- package/dist/mcp/mcp.server.js +202 -228
- package/dist/negotiation/application/negotiation.agent.js +29 -5
- package/dist/negotiation/application/negotiation.graph.d.ts +2 -5
- package/dist/negotiation/application/negotiation.graph.js +53 -23
- package/dist/negotiation/domain/index.d.ts +3 -0
- package/dist/negotiation/domain/index.js +3 -0
- package/dist/negotiation/domain/negotiation.deadlock.d.ts +7 -1
- package/dist/negotiation/domain/negotiation.deadlock.js +48 -1
- package/dist/negotiation/domain/negotiation.stance.contracts.d.ts +85 -0
- package/dist/negotiation/domain/negotiation.stance.contracts.js +140 -0
- package/dist/negotiation/domain/negotiation.state.d.ts +2 -3
- package/dist/negotiation/domain/negotiation.state.js +2 -3
- package/dist/negotiation/public/index.d.ts +2 -0
- package/dist/negotiation/public/index.js +2 -0
- package/dist/opportunity/application/index.d.ts +0 -3
- package/dist/opportunity/application/index.js +0 -3
- package/dist/opportunity/application/opportunity.graph.d.ts +0 -43
- package/dist/opportunity/application/opportunity.graph.js +7 -113
- package/dist/opportunity/application/opportunity.introducer-feature.d.ts +2 -0
- package/dist/opportunity/application/opportunity.introducer-feature.js +4 -0
- package/dist/opportunity/application/opportunity.owner-approval.d.ts +118 -0
- package/dist/opportunity/application/opportunity.owner-approval.js +34 -0
- package/dist/opportunity/application/opportunity.owner-provenance.d.ts +6 -0
- package/dist/opportunity/application/opportunity.owner-provenance.js +28 -0
- package/dist/opportunity/application/opportunity.tools.d.ts +3 -3
- package/dist/opportunity/application/opportunity.tools.js +77 -870
- package/dist/opportunity/discriminator/discriminator.shadow.js +17 -10
- package/dist/opportunity/discriminator/discriminator.types.d.ts +2 -0
- package/dist/opportunity/domain/index.d.ts +0 -2
- package/dist/opportunity/domain/index.js +0 -2
- package/dist/opportunity/domain/opportunity.state.d.ts +2 -70
- package/dist/opportunity/domain/opportunity.state.js +2 -55
- package/dist/opportunity/opportunity.owner-approval.d.ts +5 -0
- package/dist/opportunity/opportunity.owner-approval.js +5 -0
- package/dist/opportunity/ports/opportunity.tools.port.d.ts +12 -1
- package/dist/opportunity/public/index.d.ts +3 -3
- package/dist/opportunity/public/index.js +2 -1
- package/dist/participant-agents/application/agent.tools.d.ts +1 -1
- package/dist/participant-agents/application/agent.tools.js +44 -6
- package/dist/participant-agents/application/index.d.ts +5 -3
- package/dist/participant-agents/application/index.js +5 -3
- package/dist/questions/application/question.tools.js +31 -2
- package/dist/runtime/foreground/composition/tool.factory.d.ts +2 -1
- package/dist/runtime/foreground/composition/tool.factory.js +10 -2
- package/dist/runtime/foreground/composition/tool.registry.d.ts +16 -2
- package/dist/runtime/foreground/composition/tool.registry.js +41 -38
- package/dist/shared/agent/activity-projection.d.ts +271 -0
- package/dist/shared/agent/activity-projection.js +248 -0
- package/dist/shared/agent/canonical-guidance.d.ts +30 -0
- package/dist/shared/agent/canonical-guidance.js +249 -0
- package/dist/shared/agent/tool.helpers.d.ts +13 -17
- package/dist/shared/agent/tool.runtime.js +1 -3
- package/dist/shared/agent/utility.tools.d.ts +11 -1
- package/dist/shared/agent/utility.tools.js +126 -90
- package/dist/shared/interfaces/auth.interface.d.ts +2 -8
- package/dist/shared/interfaces/chat-session.interface.js +5 -0
- package/dist/shared/interfaces/database.interface.d.ts +12 -5
- package/dist/shared/observability/request-context.d.ts +4 -40
- package/dist/shared/schemas/mcp-auth.schema.d.ts +87 -15
- package/dist/shared/schemas/mcp-auth.schema.js +49 -1
- package/dist/signals/application/intent.graph.js +4 -15
- package/dist/signals/application/intent.tools.js +34 -14
- package/dist/signals/domain/intent.proposal.d.ts +35 -0
- package/dist/signals/domain/intent.proposal.js +12 -0
- package/package.json +10 -9
- package/dist/opportunity/application/opportunity.discover.d.ts +0 -215
- package/dist/opportunity/application/opportunity.discover.js +0 -793
- package/dist/opportunity/application/opportunity.discovery-continuation-finalization.d.ts +0 -34
- package/dist/opportunity/application/opportunity.discovery-continuation-finalization.js +0 -74
- package/dist/opportunity/application/opportunity.discovery-mcp-lifecycle-finalization.d.ts +0 -44
- package/dist/opportunity/application/opportunity.discovery-mcp-lifecycle-finalization.js +0 -92
- package/dist/opportunity/domain/opportunity.discovery-run-coalescing.d.ts +0 -33
- package/dist/opportunity/domain/opportunity.discovery-run-coalescing.js +0 -33
- package/dist/opportunity/domain/opportunity.discovery.contracts.d.ts +0 -45
- package/dist/opportunity/domain/opportunity.discovery.contracts.js +0 -1
- package/dist/opportunity/opportunity.discover.d.ts +0 -5
- package/dist/opportunity/opportunity.discover.js +0 -5
- package/dist/opportunity/opportunity.discovery-continuation-finalization.d.ts +0 -5
- package/dist/opportunity/opportunity.discovery-continuation-finalization.js +0 -5
- package/dist/opportunity/opportunity.discovery-mcp-lifecycle-finalization.d.ts +0 -5
- package/dist/opportunity/opportunity.discovery-mcp-lifecycle-finalization.js +0 -5
- package/dist/opportunity/opportunity.discovery-run-coalescing.d.ts +0 -5
- package/dist/opportunity/opportunity.discovery-run-coalescing.js +0 -5
- package/dist/opportunity/opportunity.discovery.contracts.d.ts +0 -5
- package/dist/opportunity/opportunity.discovery.contracts.js +0 -5
- package/dist/shared/interfaces/discovery-run.interface.d.ts +0 -70
- package/dist/shared/interfaces/discovery-run.interface.js +0 -1
|
@@ -125,11 +125,6 @@ export declare class OpportunityGraphFactory {
|
|
|
125
125
|
targetUserId: Id<"users"> | undefined;
|
|
126
126
|
onBehalfOfUserId: Id<"users"> | undefined;
|
|
127
127
|
options: import("../domain/opportunity.state.js").OpportunityGraphOptions;
|
|
128
|
-
trigger: import("../domain/opportunity.state.js").OpportunityTrigger;
|
|
129
|
-
dedupAlreadyAccepted: {
|
|
130
|
-
opportunityId: string;
|
|
131
|
-
counterpartyUserId: string;
|
|
132
|
-
}[];
|
|
133
128
|
operationMode: "delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing";
|
|
134
129
|
introductionEntities: EvaluatorEntity[];
|
|
135
130
|
introductionHint: string | undefined;
|
|
@@ -239,14 +234,6 @@ export declare class OpportunityGraphFactory {
|
|
|
239
234
|
targetUserId?: Id<"users"> | import("@langchain/langgraph").OverwriteValue<Id<"users"> | undefined> | undefined;
|
|
240
235
|
onBehalfOfUserId?: Id<"users"> | import("@langchain/langgraph").OverwriteValue<Id<"users"> | undefined> | undefined;
|
|
241
236
|
options?: import("../domain/opportunity.state.js").OpportunityGraphOptions | import("@langchain/langgraph").OverwriteValue<import("../domain/opportunity.state.js").OpportunityGraphOptions> | undefined;
|
|
242
|
-
trigger?: import("../domain/opportunity.state.js").OpportunityTrigger | import("@langchain/langgraph").OverwriteValue<import("../domain/opportunity.state.js").OpportunityTrigger> | undefined;
|
|
243
|
-
dedupAlreadyAccepted?: {
|
|
244
|
-
opportunityId: string;
|
|
245
|
-
counterpartyUserId: string;
|
|
246
|
-
}[] | import("@langchain/langgraph").OverwriteValue<{
|
|
247
|
-
opportunityId: string;
|
|
248
|
-
counterpartyUserId: string;
|
|
249
|
-
}[]> | undefined;
|
|
250
237
|
operationMode?: "delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | import("@langchain/langgraph").OverwriteValue<"delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing"> | undefined;
|
|
251
238
|
introductionEntities?: EvaluatorEntity[] | import("@langchain/langgraph").OverwriteValue<EvaluatorEntity[]> | undefined;
|
|
252
239
|
introductionHint?: string | import("@langchain/langgraph").OverwriteValue<string | undefined> | undefined;
|
|
@@ -420,17 +407,6 @@ export declare class OpportunityGraphFactory {
|
|
|
420
407
|
targetUserId: import("@langchain/langgraph").BaseChannel<Id<"users"> | undefined, Id<"users"> | import("@langchain/langgraph").OverwriteValue<Id<"users"> | undefined> | undefined, unknown>;
|
|
421
408
|
onBehalfOfUserId: import("@langchain/langgraph").BaseChannel<Id<"users"> | undefined, Id<"users"> | import("@langchain/langgraph").OverwriteValue<Id<"users"> | undefined> | undefined, unknown>;
|
|
422
409
|
options: import("@langchain/langgraph").BaseChannel<import("../domain/opportunity.state.js").OpportunityGraphOptions, import("../domain/opportunity.state.js").OpportunityGraphOptions | import("@langchain/langgraph").OverwriteValue<import("../domain/opportunity.state.js").OpportunityGraphOptions>, unknown>;
|
|
423
|
-
trigger: import("@langchain/langgraph").BaseChannel<import("../domain/opportunity.state.js").OpportunityTrigger, import("../domain/opportunity.state.js").OpportunityTrigger | import("@langchain/langgraph").OverwriteValue<import("../domain/opportunity.state.js").OpportunityTrigger>, unknown>;
|
|
424
|
-
dedupAlreadyAccepted: import("@langchain/langgraph").BaseChannel<{
|
|
425
|
-
opportunityId: string;
|
|
426
|
-
counterpartyUserId: string;
|
|
427
|
-
}[], {
|
|
428
|
-
opportunityId: string;
|
|
429
|
-
counterpartyUserId: string;
|
|
430
|
-
}[] | import("@langchain/langgraph").OverwriteValue<{
|
|
431
|
-
opportunityId: string;
|
|
432
|
-
counterpartyUserId: string;
|
|
433
|
-
}[]>, unknown>;
|
|
434
410
|
operationMode: import("@langchain/langgraph").BaseChannel<"delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing", "delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | import("@langchain/langgraph").OverwriteValue<"delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing">, unknown>;
|
|
435
411
|
introductionEntities: import("@langchain/langgraph").BaseChannel<EvaluatorEntity[], EvaluatorEntity[] | import("@langchain/langgraph").OverwriteValue<EvaluatorEntity[]>, unknown>;
|
|
436
412
|
introductionHint: import("@langchain/langgraph").BaseChannel<string | undefined, string | import("@langchain/langgraph").OverwriteValue<string | undefined> | undefined, unknown>;
|
|
@@ -668,17 +644,6 @@ export declare class OpportunityGraphFactory {
|
|
|
668
644
|
targetUserId: import("@langchain/langgraph").BaseChannel<Id<"users"> | undefined, Id<"users"> | import("@langchain/langgraph").OverwriteValue<Id<"users"> | undefined> | undefined, unknown>;
|
|
669
645
|
onBehalfOfUserId: import("@langchain/langgraph").BaseChannel<Id<"users"> | undefined, Id<"users"> | import("@langchain/langgraph").OverwriteValue<Id<"users"> | undefined> | undefined, unknown>;
|
|
670
646
|
options: import("@langchain/langgraph").BaseChannel<import("../domain/opportunity.state.js").OpportunityGraphOptions, import("../domain/opportunity.state.js").OpportunityGraphOptions | import("@langchain/langgraph").OverwriteValue<import("../domain/opportunity.state.js").OpportunityGraphOptions>, unknown>;
|
|
671
|
-
trigger: import("@langchain/langgraph").BaseChannel<import("../domain/opportunity.state.js").OpportunityTrigger, import("../domain/opportunity.state.js").OpportunityTrigger | import("@langchain/langgraph").OverwriteValue<import("../domain/opportunity.state.js").OpportunityTrigger>, unknown>;
|
|
672
|
-
dedupAlreadyAccepted: import("@langchain/langgraph").BaseChannel<{
|
|
673
|
-
opportunityId: string;
|
|
674
|
-
counterpartyUserId: string;
|
|
675
|
-
}[], {
|
|
676
|
-
opportunityId: string;
|
|
677
|
-
counterpartyUserId: string;
|
|
678
|
-
}[] | import("@langchain/langgraph").OverwriteValue<{
|
|
679
|
-
opportunityId: string;
|
|
680
|
-
counterpartyUserId: string;
|
|
681
|
-
}[]>, unknown>;
|
|
682
647
|
operationMode: import("@langchain/langgraph").BaseChannel<"delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing", "delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing" | import("@langchain/langgraph").OverwriteValue<"delete" | "send" | "update" | "approve_introduction" | "create" | "create_introduction" | "continue_discovery" | "read" | "negotiate_existing">, unknown>;
|
|
683
648
|
introductionEntities: import("@langchain/langgraph").BaseChannel<EvaluatorEntity[], EvaluatorEntity[] | import("@langchain/langgraph").OverwriteValue<EvaluatorEntity[]>, unknown>;
|
|
684
649
|
introductionHint: import("@langchain/langgraph").BaseChannel<string | undefined, string | import("@langchain/langgraph").OverwriteValue<string | undefined> | undefined, unknown>;
|
|
@@ -1220,14 +1185,12 @@ export declare class OpportunityGraphFactory {
|
|
|
1220
1185
|
finalAtomicConflictCount: number;
|
|
1221
1186
|
};
|
|
1222
1187
|
existingBetweenActors?: undefined;
|
|
1223
|
-
dedupAlreadyAccepted?: undefined;
|
|
1224
1188
|
trace?: undefined;
|
|
1225
1189
|
error?: undefined;
|
|
1226
1190
|
} | {
|
|
1227
1191
|
opportunities: never[];
|
|
1228
1192
|
persistenceOutcome?: undefined;
|
|
1229
1193
|
existingBetweenActors?: undefined;
|
|
1230
|
-
dedupAlreadyAccepted?: undefined;
|
|
1231
1194
|
trace?: undefined;
|
|
1232
1195
|
error?: undefined;
|
|
1233
1196
|
} | {
|
|
@@ -1240,10 +1203,6 @@ export declare class OpportunityGraphFactory {
|
|
|
1240
1203
|
reason?: "same_trigger_recent_duplicate" | "pair_active_negotiation" | "final_atomic_conflict";
|
|
1241
1204
|
existingTriggerIntentId?: string;
|
|
1242
1205
|
}[];
|
|
1243
|
-
dedupAlreadyAccepted: {
|
|
1244
|
-
opportunityId: string;
|
|
1245
|
-
counterpartyUserId: string;
|
|
1246
|
-
}[];
|
|
1247
1206
|
persistenceOutcome: {
|
|
1248
1207
|
evaluatedCount: number;
|
|
1249
1208
|
createdCount: number;
|
|
@@ -1260,7 +1219,6 @@ export declare class OpportunityGraphFactory {
|
|
|
1260
1219
|
created: number;
|
|
1261
1220
|
reactivated: number;
|
|
1262
1221
|
existingSkipped: number;
|
|
1263
|
-
alreadyAccepted: number;
|
|
1264
1222
|
totalOutput: number;
|
|
1265
1223
|
persistenceOutcome: {
|
|
1266
1224
|
evaluatedCount: number;
|
|
@@ -1287,7 +1245,6 @@ export declare class OpportunityGraphFactory {
|
|
|
1287
1245
|
};
|
|
1288
1246
|
}[];
|
|
1289
1247
|
persistenceOutcome?: undefined;
|
|
1290
|
-
dedupAlreadyAccepted?: undefined;
|
|
1291
1248
|
};
|
|
1292
1249
|
read: {
|
|
1293
1250
|
readResult: {
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { StateGraph, START, END } from '@langchain/langgraph';
|
|
15
15
|
import { OpportunityGraphState } from '../domain/opportunity.state.js';
|
|
16
|
-
import { resolveInitialStatus } from '../domain/opportunity.state.js';
|
|
17
16
|
import { OpportunityEvaluator } from './opportunity.evaluator.js';
|
|
18
17
|
import { IntentIndexer } from '../../capabilities/signals.indexing.facade.js';
|
|
19
18
|
import { getModelName } from '../../shared/agent/model.config.js';
|
|
@@ -2028,34 +2027,19 @@ export class OpportunityGraphFactory {
|
|
|
2028
2027
|
? (await Promise.all(uniqueIndexIds.map((networkId) => this.agentDispatcher.hasExternalAgent(discoveryUserId, { action: 'manage:negotiations', scopeType: 'network', scopeId: networkId }).catch(() => false)))).every(Boolean)
|
|
2029
2028
|
: false)
|
|
2030
2029
|
: false;
|
|
2031
|
-
// Orchestrator (chat-driven a2h) fan-out uses a tight 60s park window —
|
|
2032
|
-
// the user is watching the stream, so we cannot afford the 5-min ambient
|
|
2033
|
-
// budget. Ambient keeps its heartbeat-aware long/short split.
|
|
2034
|
-
const ORCHESTRATOR_PARK_WINDOW_MS = 60000;
|
|
2035
|
-
const isOrchestrator = state.trigger === 'orchestrator';
|
|
2036
2030
|
const useLongTimeout = !isChatPath || hasExternalAgent;
|
|
2037
|
-
const timeoutMs =
|
|
2038
|
-
? ORCHESTRATOR_PARK_WINDOW_MS
|
|
2039
|
-
: useLongTimeout ? AMBIENT_PARK_WINDOW_MS : 30000;
|
|
2031
|
+
const timeoutMs = useLongTimeout ? AMBIENT_PARK_WINDOW_MS : 30000;
|
|
2040
2032
|
logger.info('negotiateNode timeout decision', {
|
|
2041
2033
|
discoveryUserId,
|
|
2042
|
-
trigger: state.trigger,
|
|
2043
2034
|
isChatPath,
|
|
2044
|
-
isOrchestrator,
|
|
2045
2035
|
hasDispatcher: !!this.agentDispatcher,
|
|
2046
2036
|
hasExternalAgent,
|
|
2047
2037
|
useLongTimeout,
|
|
2048
2038
|
timeoutMs,
|
|
2049
2039
|
candidateCount: candidates.length,
|
|
2050
2040
|
});
|
|
2051
|
-
// Per-candidate hook
|
|
2052
|
-
//
|
|
2053
|
-
// trigger: flips the opp from 'pending' to 'draft' and pushes an
|
|
2054
|
-
// `opportunity_draft_ready` event so the frontend can render it
|
|
2055
|
-
// inline as soon as it resolves, rather than waiting for the full
|
|
2056
|
-
// fan-out. Abort (e.g. user closed the chat) suppresses both the
|
|
2057
|
-
// status flip and the event — the in-flight negotiation finishes
|
|
2058
|
-
// naturally but its card never reaches the user.
|
|
2041
|
+
// Per-candidate hook accumulates negotiation resolutions for discovery
|
|
2042
|
+
// question generation, preserving candidate-list order below.
|
|
2059
2043
|
// Build a stable order index so that resolutions accumulated via the
|
|
2060
2044
|
// per-candidate async hook can be re-sorted to candidate-list order
|
|
2061
2045
|
// before being handed to buildQuestionPrompt. Without this the LLM
|
|
@@ -2064,7 +2048,7 @@ export class OpportunityGraphFactory {
|
|
|
2064
2048
|
candidates.forEach((c, i) => candidateOrderById.set(c.userId, i));
|
|
2065
2049
|
const resolutions = [];
|
|
2066
2050
|
const resolvedOpportunityIds = new Set();
|
|
2067
|
-
const onCandidateResolved = async ({ candidate,
|
|
2051
|
+
const onCandidateResolved = async ({ candidate, turns, outcome }) => {
|
|
2068
2052
|
if (candidate.opportunityId)
|
|
2069
2053
|
resolvedOpportunityIds.add(candidate.opportunityId);
|
|
2070
2054
|
resolutions.push({
|
|
@@ -2085,62 +2069,11 @@ export class OpportunityGraphFactory {
|
|
|
2085
2069
|
if (candidate.opportunityId) {
|
|
2086
2070
|
await compensateTasklessNegotiatingOpportunity(candidate.opportunityId);
|
|
2087
2071
|
}
|
|
2088
|
-
if (state.trigger !== 'orchestrator')
|
|
2089
|
-
return;
|
|
2090
|
-
// ─── orchestrator streaming body ───
|
|
2091
|
-
const abortSignal = requestContext.getStore()?.abortSignal;
|
|
2092
|
-
if (abortSignal?.aborted)
|
|
2093
|
-
return;
|
|
2094
|
-
if (!accepted || !candidate.opportunityId)
|
|
2095
|
-
return;
|
|
2096
|
-
// Only emit after a successful status flip — the frontend keys
|
|
2097
|
-
// cards off `opportunity.status === 'draft'`, so emitting a row
|
|
2098
|
-
// with its pre-flip status would render inconsistently. If the
|
|
2099
|
-
// flip fails we log and drop the event; the negotiation result
|
|
2100
|
-
// is still captured in acceptedResults for the final summary.
|
|
2101
|
-
const updated = await this.database
|
|
2102
|
-
.updateOpportunityStatus(candidate.opportunityId, 'draft')
|
|
2103
|
-
.catch((err) => {
|
|
2104
|
-
negotiateLog.warn('failed to flip opp to draft; suppressing draft-ready event', {
|
|
2105
|
-
opportunityId: candidate.opportunityId,
|
|
2106
|
-
error: err,
|
|
2107
|
-
});
|
|
2108
|
-
return null;
|
|
2109
|
-
});
|
|
2110
|
-
if (!updated || abortSignal?.aborted)
|
|
2111
|
-
return;
|
|
2112
|
-
const counterpartName = candidate.candidateUser.profile?.name ?? '';
|
|
2113
|
-
const viewerName = sourceUser.profile.name;
|
|
2114
|
-
const rawReasoning = updated.interpretation?.reasoning ?? '';
|
|
2115
|
-
const personalizedSummary = safeFallbackSummary(rawReasoning, {
|
|
2116
|
-
counterpartName,
|
|
2117
|
-
viewerName,
|
|
2118
|
-
emptyText: 'A suggested connection.',
|
|
2119
|
-
});
|
|
2120
|
-
traceEmitter?.({
|
|
2121
|
-
type: 'opportunity_draft_ready',
|
|
2122
|
-
opportunityId: candidate.opportunityId,
|
|
2123
|
-
opportunity: {
|
|
2124
|
-
...updated,
|
|
2125
|
-
interpretation: {
|
|
2126
|
-
...updated.interpretation,
|
|
2127
|
-
reasoning: personalizedSummary,
|
|
2128
|
-
},
|
|
2129
|
-
},
|
|
2130
|
-
personalizedSummary,
|
|
2131
|
-
counterparty: {
|
|
2132
|
-
userId: candidate.candidateUser.id,
|
|
2133
|
-
...(candidate.candidateUser.profile?.name
|
|
2134
|
-
? { name: candidate.candidateUser.profile.name }
|
|
2135
|
-
: {}),
|
|
2136
|
-
},
|
|
2137
|
-
});
|
|
2138
2072
|
};
|
|
2139
2073
|
const negotiationWork = negotiateCandidates(this.negotiationGraph, sourceUser, candidates, { networkId: '', prompt: '' }, // base context, overridden per-candidate below
|
|
2140
2074
|
{ maxTurns, traceEmitter: traceEmitter ?? undefined,
|
|
2141
2075
|
indexContextOverrides: indexContextMap,
|
|
2142
2076
|
timeoutMs,
|
|
2143
|
-
trigger: state.trigger === 'orchestrator' ? 'orchestrator' : 'ambient',
|
|
2144
2077
|
// v2 initiator stamp: every fresh-discovery origin resolves to the
|
|
2145
2078
|
// discovery user — querying user (chat/tool), intent owner
|
|
2146
2079
|
// (from-intent), enriched user (from-enrichment/discovery-run), or
|
|
@@ -2525,10 +2458,9 @@ export class OpportunityGraphFactory {
|
|
|
2525
2458
|
const persistNode = withNodeTrace("opportunity-persist", async (state) => {
|
|
2526
2459
|
return timed("OpportunityGraph.persist", async () => {
|
|
2527
2460
|
const startTime = Date.now();
|
|
2528
|
-
const initialStatus =
|
|
2461
|
+
const initialStatus = state.options.initialStatus ?? 'pending';
|
|
2529
2462
|
persistLog.verbose('Starting persistence (dedup-v2)', {
|
|
2530
2463
|
opportunitiesToCreate: state.evaluatedOpportunities.length,
|
|
2531
|
-
trigger: state.trigger,
|
|
2532
2464
|
initialStatus,
|
|
2533
2465
|
});
|
|
2534
2466
|
if (state.evaluatedOpportunities.length === 0) {
|
|
@@ -2593,41 +2525,6 @@ export class OpportunityGraphFactory {
|
|
|
2593
2525
|
const introducerUserForOnBehalf = state.onBehalfOfUserId
|
|
2594
2526
|
? await this.database.getUser(state.userId)
|
|
2595
2527
|
: null;
|
|
2596
|
-
// Orchestrator-only: collect already-accepted pairs so Task 7's
|
|
2597
|
-
// discover_opportunities tool can tell the LLM "these pairs are
|
|
2598
|
-
// already connected, surface the existing chat rather than
|
|
2599
|
-
// creating a new draft". Runs in parallel across unique
|
|
2600
|
-
// counterparties (a single evaluator pass can return multiple
|
|
2601
|
-
// opps per counterparty; we only hit the DB once per pair).
|
|
2602
|
-
// Failures are swallowed — the per-pair query is best-effort.
|
|
2603
|
-
const dedupAlreadyAccepted = [];
|
|
2604
|
-
if (state.trigger === 'orchestrator') {
|
|
2605
|
-
// Use the same viewer-resolution as evaluation/negotiate/persist
|
|
2606
|
-
// on-behalf branches so an introducer-driven orchestrator run
|
|
2607
|
-
// queries accepted opps between the *target* user and the
|
|
2608
|
-
// counterparty, not between the introducer and the counterparty.
|
|
2609
|
-
const dedupUserId = (state.onBehalfOfUserId ?? state.userId);
|
|
2610
|
-
const uniqueCounterparts = new Set();
|
|
2611
|
-
for (const evaluated of evaluatedToPersist) {
|
|
2612
|
-
const candidateUserId = evaluated.actors.find(a => a.userId !== dedupUserId)?.userId;
|
|
2613
|
-
if (candidateUserId)
|
|
2614
|
-
uniqueCounterparts.add(candidateUserId);
|
|
2615
|
-
}
|
|
2616
|
-
const lookups = await Promise.all([...uniqueCounterparts].map(async (counterpartyUserId) => {
|
|
2617
|
-
const accepted = await this.database
|
|
2618
|
-
.findOpportunitiesByActors([dedupUserId, counterpartyUserId], { includeIntroducers: true, statuses: ['accepted'] })
|
|
2619
|
-
.catch((err) => {
|
|
2620
|
-
persistLog.warn('findOpportunitiesByActors (sibling-accept) failed', {
|
|
2621
|
-
userId: dedupUserId,
|
|
2622
|
-
counterpartyUserId,
|
|
2623
|
-
error: err,
|
|
2624
|
-
});
|
|
2625
|
-
return [];
|
|
2626
|
-
});
|
|
2627
|
-
return accepted.map((opp) => ({ opportunityId: opp.id, counterpartyUserId }));
|
|
2628
|
-
}));
|
|
2629
|
-
dedupAlreadyAccepted.push(...lookups.flat());
|
|
2630
|
-
}
|
|
2631
2528
|
for (const evaluated of evaluatedToPersist) {
|
|
2632
2529
|
const indexIdForActors = state.networkId ?? evaluated.actors[0]?.networkId;
|
|
2633
2530
|
let actors;
|
|
@@ -3233,16 +3130,14 @@ export class OpportunityGraphFactory {
|
|
|
3233
3130
|
return {
|
|
3234
3131
|
opportunities: allOpportunities,
|
|
3235
3132
|
existingBetweenActors,
|
|
3236
|
-
dedupAlreadyAccepted,
|
|
3237
3133
|
persistenceOutcome,
|
|
3238
3134
|
trace: [{
|
|
3239
3135
|
node: "persist",
|
|
3240
|
-
detail: `Created ${createdList.length}, reactivated ${reactivatedOpportunities.length}, ${existingBetweenActors.length} existing skipped
|
|
3136
|
+
detail: `Created ${createdList.length}, reactivated ${reactivatedOpportunities.length}, ${existingBetweenActors.length} existing skipped`,
|
|
3241
3137
|
data: {
|
|
3242
3138
|
created: createdList.length,
|
|
3243
3139
|
reactivated: reactivatedOpportunities.length,
|
|
3244
3140
|
existingSkipped: existingBetweenActors.length,
|
|
3245
|
-
alreadyAccepted: dedupAlreadyAccepted.length,
|
|
3246
3141
|
totalOutput: allOpportunities.length,
|
|
3247
3142
|
persistenceOutcome,
|
|
3248
3143
|
durationMs: Date.now() - startTime,
|
|
@@ -3308,7 +3203,7 @@ export class OpportunityGraphFactory {
|
|
|
3308
3203
|
return {
|
|
3309
3204
|
readResult: {
|
|
3310
3205
|
count: 0,
|
|
3311
|
-
message: 'You have no opportunities yet. Use
|
|
3206
|
+
message: 'You have no opportunities yet. Create or refine an approved signal; matching runs in the background. Use list_opportunities later to review persisted results.',
|
|
3312
3207
|
opportunities: [],
|
|
3313
3208
|
},
|
|
3314
3209
|
};
|
|
@@ -3494,7 +3389,6 @@ export class OpportunityGraphFactory {
|
|
|
3494
3389
|
maxTurns: Number(process.env.NEGOTIATION_MAX_TURNS_AMBIENT) || 6,
|
|
3495
3390
|
indexContextOverrides,
|
|
3496
3391
|
timeoutMs: AMBIENT_PARK_WINDOW_MS,
|
|
3497
|
-
trigger: 'ambient',
|
|
3498
3392
|
...(execution ? {
|
|
3499
3393
|
resumeFromTaskId: execution.taskId,
|
|
3500
3394
|
continuationSettlementId: execution.settlementId,
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IND-593: protocol-owned owner-approval authority port.
|
|
3
|
+
*
|
|
4
|
+
* Every registered MCP-agent opportunity state change (send/accept/reject)
|
|
5
|
+
* requires an explicit owner-issued, fresh, atomically single-use proof bound
|
|
6
|
+
* to the exact opportunity, target action, owner principal, acting agent, and
|
|
7
|
+
* current server-derived interaction. The protocol owns this verifier/consumer
|
|
8
|
+
* contract and the fail-closed boundary in `update_opportunity`; the host
|
|
9
|
+
* injects the concrete authority (challenge store + signed-proof issuance).
|
|
10
|
+
*
|
|
11
|
+
* Direct authenticated-owner interactions (REST/chat/CLI) traverse the same
|
|
12
|
+
* boundary through `attestOwnerInteraction`: the host attests the
|
|
13
|
+
* server-derived principal and interaction provenance of the already
|
|
14
|
+
* authenticated owner session. Caller-controlled identity or proof-binding
|
|
15
|
+
* fields are never trusted by either path — the binding below is always
|
|
16
|
+
* derived from the resolved tool context.
|
|
17
|
+
*
|
|
18
|
+
* A2A negotiation approvals, agent self-acknowledgment
|
|
19
|
+
* (`acknowledgedUptakeQuestionIds`), and server-generated advisory/challenge
|
|
20
|
+
* values are explicitly NOT substitutes for owner authorization.
|
|
21
|
+
*/
|
|
22
|
+
/** Owner-gated opportunity actions. `expired` is a system transition and is not gated. */
|
|
23
|
+
export type OpportunityOwnerAction = 'send' | 'accept' | 'reject';
|
|
24
|
+
/** Maps an update_opportunity target status to its owner-gated action, or null when ungated. */
|
|
25
|
+
export declare function opportunityOwnerActionForStatus(status: string): OpportunityOwnerAction | null;
|
|
26
|
+
/**
|
|
27
|
+
* Server-derived proof binding. Built exclusively from the resolved tool
|
|
28
|
+
* context and validated tool input — never from caller-supplied binding
|
|
29
|
+
* fields. `agentId` is present exactly when a registered agent acts.
|
|
30
|
+
*/
|
|
31
|
+
export interface OpportunityOwnerApprovalBinding {
|
|
32
|
+
opportunityId: string;
|
|
33
|
+
action: OpportunityOwnerAction;
|
|
34
|
+
ownerId: string;
|
|
35
|
+
agentId?: string;
|
|
36
|
+
}
|
|
37
|
+
/** Server-derived transport surface of the current interaction. */
|
|
38
|
+
export type OpportunityOwnerInteractionSurface = 'mcp' | 'chat' | 'rest';
|
|
39
|
+
/**
|
|
40
|
+
* Trusted interaction/surface provenance for a non-agent tool call (IND-593
|
|
41
|
+
* Batch B). Derived exclusively from the resolved server context — the host
|
|
42
|
+
* composition binds `isSessionAuth` from the authenticated request identity,
|
|
43
|
+
* and the surface follows the composition root (MCP server, chat factory,
|
|
44
|
+
* REST tool service). Tool arguments and caller-supplied fields can never
|
|
45
|
+
* populate it. The host authority attests ONLY a genuine direct owner
|
|
46
|
+
* session (`sessionAuthenticated` on a non-chat surface); chat/CLI/H2A/A2A
|
|
47
|
+
* and other mediated surfaces fail closed with `untrusted_provenance`.
|
|
48
|
+
*/
|
|
49
|
+
export interface OpportunityOwnerInteractionProvenance {
|
|
50
|
+
surface: OpportunityOwnerInteractionSurface;
|
|
51
|
+
/** True only when the host bound an authenticated owner session (never an API key or a mediated orchestrator) to this exact request. */
|
|
52
|
+
sessionAuthenticated: boolean;
|
|
53
|
+
}
|
|
54
|
+
/** Attestation input: the server-derived binding plus trusted provenance. */
|
|
55
|
+
export type OpportunityOwnerApprovalAttestation = OpportunityOwnerApprovalBinding & {
|
|
56
|
+
provenance: OpportunityOwnerInteractionProvenance;
|
|
57
|
+
};
|
|
58
|
+
/** Stable, testable denial reasons for the fail-closed owner-approval boundary. */
|
|
59
|
+
export type OpportunityOwnerApprovalDenialReason =
|
|
60
|
+
/** No proof presented (or no authority wired); a fresh interaction challenge is attached. */
|
|
61
|
+
'missing'
|
|
62
|
+
/** Proof fails authenticity/structure checks (bad signature, unknown interaction, foreign token). */
|
|
63
|
+
| 'forged'
|
|
64
|
+
/** Proof or underlying challenge has expired. */
|
|
65
|
+
| 'stale'
|
|
66
|
+
/** Proof is not bound to an exact opportunity/action/owner/agent/interaction. */
|
|
67
|
+
| 'generic'
|
|
68
|
+
/** Proof binding mismatches the current server-derived binding. */
|
|
69
|
+
| 'wrong_owner' | 'wrong_agent' | 'wrong_action' | 'wrong_opportunity'
|
|
70
|
+
/** Proof was already consumed; single-use is atomic. */
|
|
71
|
+
| 'replayed'
|
|
72
|
+
/** The interaction's server-derived provenance cannot mint or attest owner authority; an owner-issued proof is required. */
|
|
73
|
+
| 'untrusted_provenance'
|
|
74
|
+
/** The authority's store or configuration failed; nothing is admitted. */
|
|
75
|
+
| 'unavailable';
|
|
76
|
+
/**
|
|
77
|
+
* Fresh, server-derived interaction challenge returned with a `missing`
|
|
78
|
+
* denial. The owner explicitly approves this exact interaction (bound to the
|
|
79
|
+
* full server-derived binding) to obtain a one-time proof.
|
|
80
|
+
*/
|
|
81
|
+
export interface OpportunityOwnerApprovalChallenge {
|
|
82
|
+
interactionId: string;
|
|
83
|
+
expiresAt: string;
|
|
84
|
+
}
|
|
85
|
+
export type OpportunityOwnerApprovalVerdict = {
|
|
86
|
+
kind: 'admitted';
|
|
87
|
+
} | {
|
|
88
|
+
kind: 'denied';
|
|
89
|
+
reason: OpportunityOwnerApprovalDenialReason;
|
|
90
|
+
challenge?: OpportunityOwnerApprovalChallenge;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Authoritative owner-proof verifier/consumer, injected by the host.
|
|
94
|
+
* Implementations MUST fail closed and MUST make consumption atomically
|
|
95
|
+
* single-use: a denied verification never consumes a proof; a successful one
|
|
96
|
+
* consumes it exactly once, even under concurrent calls.
|
|
97
|
+
*/
|
|
98
|
+
export interface OpportunityOwnerApprovalAuthority {
|
|
99
|
+
/**
|
|
100
|
+
* Verify and atomically consume an agent-presented proof against the
|
|
101
|
+
* server-derived binding. When `proof` is undefined the implementation
|
|
102
|
+
* registers the current interaction as a fresh challenge and denies with
|
|
103
|
+
* reason `missing`, attaching the challenge so the agent can relay it to the
|
|
104
|
+
* owner for explicit approval.
|
|
105
|
+
*/
|
|
106
|
+
consumeAgentProof(proof: string | undefined, binding: OpportunityOwnerApprovalBinding & {
|
|
107
|
+
agentId: string;
|
|
108
|
+
}): Promise<OpportunityOwnerApprovalVerdict>;
|
|
109
|
+
/**
|
|
110
|
+
* Traverse the same boundary for a non-agent interaction. The host attests
|
|
111
|
+
* ONLY a genuine direct authenticated-owner session, judged from the
|
|
112
|
+
* server-derived principal and interaction/surface provenance — this is
|
|
113
|
+
* authoritative host derivation, not a bypass. Mediated surfaces
|
|
114
|
+
* (chat/CLI/H2A/A2A) and absent or malformed provenance fail closed with
|
|
115
|
+
* `untrusted_provenance`.
|
|
116
|
+
*/
|
|
117
|
+
attestOwnerInteraction(binding: OpportunityOwnerApprovalAttestation): Promise<OpportunityOwnerApprovalVerdict>;
|
|
118
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IND-593: protocol-owned owner-approval authority port.
|
|
3
|
+
*
|
|
4
|
+
* Every registered MCP-agent opportunity state change (send/accept/reject)
|
|
5
|
+
* requires an explicit owner-issued, fresh, atomically single-use proof bound
|
|
6
|
+
* to the exact opportunity, target action, owner principal, acting agent, and
|
|
7
|
+
* current server-derived interaction. The protocol owns this verifier/consumer
|
|
8
|
+
* contract and the fail-closed boundary in `update_opportunity`; the host
|
|
9
|
+
* injects the concrete authority (challenge store + signed-proof issuance).
|
|
10
|
+
*
|
|
11
|
+
* Direct authenticated-owner interactions (REST/chat/CLI) traverse the same
|
|
12
|
+
* boundary through `attestOwnerInteraction`: the host attests the
|
|
13
|
+
* server-derived principal and interaction provenance of the already
|
|
14
|
+
* authenticated owner session. Caller-controlled identity or proof-binding
|
|
15
|
+
* fields are never trusted by either path — the binding below is always
|
|
16
|
+
* derived from the resolved tool context.
|
|
17
|
+
*
|
|
18
|
+
* A2A negotiation approvals, agent self-acknowledgment
|
|
19
|
+
* (`acknowledgedUptakeQuestionIds`), and server-generated advisory/challenge
|
|
20
|
+
* values are explicitly NOT substitutes for owner authorization.
|
|
21
|
+
*/
|
|
22
|
+
/** Maps an update_opportunity target status to its owner-gated action, or null when ungated. */
|
|
23
|
+
export function opportunityOwnerActionForStatus(status) {
|
|
24
|
+
switch (status) {
|
|
25
|
+
case 'pending':
|
|
26
|
+
return 'send';
|
|
27
|
+
case 'accepted':
|
|
28
|
+
return 'accept';
|
|
29
|
+
case 'rejected':
|
|
30
|
+
return 'reject';
|
|
31
|
+
default:
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ResolvedToolContext } from "../../shared/agent/tool.helpers.js";
|
|
2
|
+
import type { OpportunityOwnerInteractionProvenance } from "./opportunity.owner-approval.js";
|
|
3
|
+
/** Bind authoritative host provenance to one resolved tool context. */
|
|
4
|
+
export declare function bindOwnerApprovalProvenance(context: ResolvedToolContext, provenance: OpportunityOwnerInteractionProvenance): void;
|
|
5
|
+
/** Read host-bound provenance without accepting any caller-supplied surrogate. */
|
|
6
|
+
export declare function ownerApprovalProvenanceFor(context: ResolvedToolContext): OpportunityOwnerInteractionProvenance | undefined;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability-local extension of the resolved tool context for IND-593.
|
|
3
|
+
*
|
|
4
|
+
* The host is the only writer: it binds provenance after authentication and
|
|
5
|
+
* before the registry invokes a tool. Tool input schemas never contain this
|
|
6
|
+
* field, so a caller cannot forge a direct-owner interaction through args or
|
|
7
|
+
* generic context data. Keeping this extension here avoids changing the shared
|
|
8
|
+
* helper that is part of the negotiation/question architecture cycle.
|
|
9
|
+
*/
|
|
10
|
+
const OWNER_APPROVAL_PROVENANCE = Symbol("ownerApprovalProvenance");
|
|
11
|
+
function hasOwnerApprovalProvenance(context) {
|
|
12
|
+
return OWNER_APPROVAL_PROVENANCE in context;
|
|
13
|
+
}
|
|
14
|
+
/** Bind authoritative host provenance to one resolved tool context. */
|
|
15
|
+
export function bindOwnerApprovalProvenance(context, provenance) {
|
|
16
|
+
Object.defineProperty(context, OWNER_APPROVAL_PROVENANCE, {
|
|
17
|
+
value: Object.freeze({ ...provenance }),
|
|
18
|
+
enumerable: false,
|
|
19
|
+
configurable: false,
|
|
20
|
+
writable: false,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/** Read host-bound provenance without accepting any caller-supplied surrogate. */
|
|
24
|
+
export function ownerApprovalProvenanceFor(context) {
|
|
25
|
+
return hasOwnerApprovalProvenance(context)
|
|
26
|
+
? context[OWNER_APPROVAL_PROVENANCE]
|
|
27
|
+
: undefined;
|
|
28
|
+
}
|
|
@@ -13,14 +13,14 @@ import type { ConnectLinkKind } from "../../shared/interfaces/connect-link.inter
|
|
|
13
13
|
* flips the opp to accepted and opens the chat with the counterpart.
|
|
14
14
|
* - `approve_introduction` — draft or latent opp where viewer is an unapproved
|
|
15
15
|
* introducer. Clicking flips approved=true and triggers negotiation. The
|
|
16
|
-
* `draft` case
|
|
16
|
+
* `draft` case represents a pending introduction; the `latent`
|
|
17
17
|
* case comes from background-discovered connector-flow cards surfaced in
|
|
18
18
|
* `list_opportunities`. In both, status remains pre-send and the `/c/<code>`
|
|
19
19
|
* link is the only MCP path to approve.
|
|
20
20
|
* - `outreach` — accepted opp where viewer is a non-introducer party.
|
|
21
21
|
* Clicking opens the existing chat (no state change).
|
|
22
22
|
* - `send_direct` — draft or latent opp where viewer is a non-introducer
|
|
23
|
-
* party. Issued
|
|
23
|
+
* party. Issued for a direct opportunity without an introducer
|
|
24
24
|
* mode: the match has already passed evaluation, the row exists in
|
|
25
25
|
* draft state, and the sender just needs to release it. Clicking flips
|
|
26
26
|
* the opp straight to accepted and opens the chat with a greeting —
|
|
@@ -128,4 +128,4 @@ export declare function buildMinimalOpportunityCard(opp: Opportunity, viewerId:
|
|
|
128
128
|
* failures and drop permanent ones instead of re-spamming the ledger.
|
|
129
129
|
*/
|
|
130
130
|
export type ConfirmDeliveryErrorCode = "unauthenticated" | "ledger_unavailable" | "invalid_opportunity_id" | "opportunity_not_found" | "not_authorized" | "confirm_failed";
|
|
131
|
-
export declare function createOpportunityTools(defineTool: DefineTool, deps: OpportunityToolDeps): readonly [any, any, any
|
|
131
|
+
export declare function createOpportunityTools(defineTool: DefineTool, deps: OpportunityToolDeps): readonly [any, any, any];
|