@indexnetwork/protocol 0.16.0-rc.39.1 → 0.17.0-rc.41.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/README.md +0 -2
- package/dist/agent/agent.tools.d.ts +1 -1
- package/dist/agent/agent.tools.d.ts.map +1 -1
- package/dist/agent/agent.tools.js +5 -132
- package/dist/agent/agent.tools.js.map +1 -1
- package/dist/chat/chat.agent.d.ts +7 -0
- package/dist/chat/chat.agent.d.ts.map +1 -1
- package/dist/chat/chat.agent.js +16 -2
- package/dist/chat/chat.agent.js.map +1 -1
- package/dist/negotiation/negotiation.graph.d.ts +12 -0
- package/dist/negotiation/negotiation.graph.d.ts.map +1 -1
- package/dist/negotiation/negotiation.graph.js +27 -5
- package/dist/negotiation/negotiation.graph.js.map +1 -1
- package/dist/opportunity/opportunity.discover.d.ts +19 -0
- package/dist/opportunity/opportunity.discover.d.ts.map +1 -1
- package/dist/opportunity/opportunity.discover.js +25 -2
- package/dist/opportunity/opportunity.discover.js.map +1 -1
- package/dist/opportunity/opportunity.enricher.d.ts +20 -1
- package/dist/opportunity/opportunity.enricher.d.ts.map +1 -1
- package/dist/opportunity/opportunity.enricher.js +15 -1
- package/dist/opportunity/opportunity.enricher.js.map +1 -1
- package/dist/opportunity/opportunity.graph.d.ts +57 -0
- package/dist/opportunity/opportunity.graph.d.ts.map +1 -1
- package/dist/opportunity/opportunity.graph.js +92 -5
- package/dist/opportunity/opportunity.graph.js.map +1 -1
- package/dist/opportunity/opportunity.state.d.ts +65 -0
- package/dist/opportunity/opportunity.state.d.ts.map +1 -1
- package/dist/opportunity/opportunity.state.js +50 -0
- package/dist/opportunity/opportunity.state.js.map +1 -1
- package/dist/opportunity/opportunity.tools.d.ts.map +1 -1
- package/dist/opportunity/opportunity.tools.js +15 -0
- package/dist/opportunity/opportunity.tools.js.map +1 -1
- package/dist/shared/agent/tool.helpers.d.ts +3 -3
- package/dist/shared/agent/tool.helpers.js +3 -3
- package/dist/shared/interfaces/agent.interface.d.ts +2 -2
- package/dist/shared/interfaces/agent.interface.d.ts.map +1 -1
- package/dist/shared/interfaces/database.interface.d.ts +13 -3
- package/dist/shared/interfaces/database.interface.d.ts.map +1 -1
- package/dist/shared/observability/request-context.d.ts +30 -1
- package/dist/shared/observability/request-context.d.ts.map +1 -1
- package/dist/shared/observability/request-context.js.map +1 -1
- package/package.json +1 -1
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { StateGraph, START, END } from '@langchain/langgraph';
|
|
15
15
|
import { OpportunityGraphState, } from './opportunity.state.js';
|
|
16
|
+
import { resolveInitialStatus } from './opportunity.state.js';
|
|
16
17
|
import { OpportunityEvaluator, } from './opportunity.evaluator.js';
|
|
17
18
|
import { IntentIndexer } from '../intent/intent.indexer.js';
|
|
18
19
|
import { getModelName } from '../shared/agent/model.config.js';
|
|
@@ -1564,21 +1565,69 @@ export class OpportunityGraphFactory {
|
|
|
1564
1565
|
? (await Promise.all(uniqueIndexIds.map((networkId) => this.agentDispatcher.hasPersonalAgent(discoveryUserId, { action: 'manage:negotiations', scopeType: 'network', scopeId: networkId }).catch(() => false)))).every(Boolean)
|
|
1565
1566
|
: false)
|
|
1566
1567
|
: false;
|
|
1568
|
+
// Orchestrator (chat-driven a2h) fan-out uses a tight 60s park window —
|
|
1569
|
+
// the user is watching the stream, so we cannot afford the 5-min ambient
|
|
1570
|
+
// budget. Ambient keeps its heartbeat-aware long/short split.
|
|
1571
|
+
const ORCHESTRATOR_PARK_WINDOW_MS = 60000;
|
|
1572
|
+
const isOrchestrator = state.trigger === 'orchestrator';
|
|
1567
1573
|
const useLongTimeout = !isChatPath || hasPersonalAgent;
|
|
1568
|
-
const timeoutMs =
|
|
1574
|
+
const timeoutMs = isOrchestrator
|
|
1575
|
+
? ORCHESTRATOR_PARK_WINDOW_MS
|
|
1576
|
+
: useLongTimeout ? AMBIENT_PARK_WINDOW_MS : 30000;
|
|
1569
1577
|
logger.info('negotiateNode timeout decision', {
|
|
1570
1578
|
discoveryUserId,
|
|
1579
|
+
trigger: state.trigger,
|
|
1571
1580
|
isChatPath,
|
|
1581
|
+
isOrchestrator,
|
|
1572
1582
|
hasDispatcher: !!this.agentDispatcher,
|
|
1573
1583
|
hasPersonalAgent,
|
|
1574
1584
|
useLongTimeout,
|
|
1575
1585
|
timeoutMs,
|
|
1576
1586
|
candidateCount: candidates.length,
|
|
1577
1587
|
});
|
|
1588
|
+
// Orchestrator-only: per-candidate streaming hook. Each accepted
|
|
1589
|
+
// negotiation flips the opp from 'pending' (negotiation finalize's
|
|
1590
|
+
// default) to 'draft' (chat-only surface) and pushes an
|
|
1591
|
+
// `opportunity_draft_ready` event so the frontend can render it
|
|
1592
|
+
// inline as soon as it resolves, rather than waiting for the full
|
|
1593
|
+
// fan-out. Abort (e.g. user closed the chat) suppresses both the
|
|
1594
|
+
// status flip and the event — the in-flight negotiation finishes
|
|
1595
|
+
// naturally but its card never reaches the user.
|
|
1596
|
+
const onCandidateResolved = isOrchestrator
|
|
1597
|
+
? async ({ candidate, accepted }) => {
|
|
1598
|
+
const abortSignal = requestContext.getStore()?.abortSignal;
|
|
1599
|
+
if (abortSignal?.aborted)
|
|
1600
|
+
return;
|
|
1601
|
+
if (!accepted || !candidate.opportunityId)
|
|
1602
|
+
return;
|
|
1603
|
+
// Only emit after a successful status flip — the frontend keys
|
|
1604
|
+
// cards off `opportunity.status === 'draft'`, so emitting a row
|
|
1605
|
+
// with its pre-flip status would render inconsistently. If the
|
|
1606
|
+
// flip fails we log and drop the event; the negotiation result
|
|
1607
|
+
// is still captured in acceptedResults for the final summary.
|
|
1608
|
+
const updated = await this.database
|
|
1609
|
+
.updateOpportunityStatus(candidate.opportunityId, 'draft')
|
|
1610
|
+
.catch((err) => {
|
|
1611
|
+
logger.warn('[Graph:Negotiate] failed to flip opp to draft; suppressing draft-ready event', {
|
|
1612
|
+
opportunityId: candidate.opportunityId,
|
|
1613
|
+
error: err,
|
|
1614
|
+
});
|
|
1615
|
+
return null;
|
|
1616
|
+
});
|
|
1617
|
+
if (!updated || abortSignal?.aborted)
|
|
1618
|
+
return;
|
|
1619
|
+
traceEmitter?.({
|
|
1620
|
+
type: 'opportunity_draft_ready',
|
|
1621
|
+
opportunityId: candidate.opportunityId,
|
|
1622
|
+
opportunity: updated,
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
: undefined;
|
|
1578
1626
|
const acceptedResults = await negotiateCandidates(this.negotiationGraph, sourceUser, candidates, { networkId: '', prompt: '' }, // base context, overridden per-candidate below
|
|
1579
1627
|
{ maxTurns, traceEmitter: traceEmitter ?? undefined,
|
|
1580
1628
|
indexContextOverrides: indexContextMap,
|
|
1581
|
-
timeoutMs
|
|
1629
|
+
timeoutMs,
|
|
1630
|
+
...(onCandidateResolved && { onCandidateResolved }) });
|
|
1582
1631
|
// No filtering: every candidate's outcome (accept/reject/stalled) was applied to its
|
|
1583
1632
|
// opportunity row by the negotiation graph's finalize node via the opportunityId we
|
|
1584
1633
|
// passed. state.opportunities stays as it was at persist time; DB has the new statuses.
|
|
@@ -1874,9 +1923,11 @@ export class OpportunityGraphFactory {
|
|
|
1874
1923
|
const persistNode = withNodeTrace("opportunity-persist", async (state) => {
|
|
1875
1924
|
return timed("OpportunityGraph.persist", async () => {
|
|
1876
1925
|
const startTime = Date.now();
|
|
1926
|
+
const initialStatus = resolveInitialStatus(state.trigger, state.options.initialStatus);
|
|
1877
1927
|
logger.verbose('[Graph:Persist] Starting persistence (dedup-v2)', {
|
|
1878
1928
|
opportunitiesToCreate: state.evaluatedOpportunities.length,
|
|
1879
|
-
|
|
1929
|
+
trigger: state.trigger,
|
|
1930
|
+
initialStatus,
|
|
1880
1931
|
});
|
|
1881
1932
|
if (state.evaluatedOpportunities.length === 0) {
|
|
1882
1933
|
logger.verbose('[Graph:Persist] No opportunities to persist');
|
|
@@ -1887,7 +1938,6 @@ export class OpportunityGraphFactory {
|
|
|
1887
1938
|
const reactivatedOpportunities = [];
|
|
1888
1939
|
const existingBetweenActors = [];
|
|
1889
1940
|
const now = new Date().toISOString();
|
|
1890
|
-
const initialStatus = state.options.initialStatus ?? 'pending';
|
|
1891
1941
|
// Only skip 'draft' (chat-only) opportunities during dedup.
|
|
1892
1942
|
// 'latent' must NOT be skipped — background discovery creates latent opportunities,
|
|
1893
1943
|
// and excluding them causes the same user pair to get duplicate opportunities
|
|
@@ -1896,6 +1946,41 @@ export class OpportunityGraphFactory {
|
|
|
1896
1946
|
const introducerUserForOnBehalf = state.onBehalfOfUserId
|
|
1897
1947
|
? await this.database.getUser(state.userId)
|
|
1898
1948
|
: null;
|
|
1949
|
+
// Orchestrator-only: collect already-accepted pairs so Task 7's
|
|
1950
|
+
// create_opportunities tool can tell the LLM "these pairs are
|
|
1951
|
+
// already connected, surface the existing chat rather than
|
|
1952
|
+
// creating a new draft". Runs in parallel across unique
|
|
1953
|
+
// counterparties (a single evaluator pass can return multiple
|
|
1954
|
+
// opps per counterparty; we only hit the DB once per pair).
|
|
1955
|
+
// Failures are swallowed — the per-pair query is best-effort.
|
|
1956
|
+
const dedupAlreadyAccepted = [];
|
|
1957
|
+
if (state.trigger === 'orchestrator') {
|
|
1958
|
+
// Use the same viewer-resolution as evaluation/negotiate/persist
|
|
1959
|
+
// on-behalf branches so an introducer-driven orchestrator run
|
|
1960
|
+
// queries accepted opps between the *target* user and the
|
|
1961
|
+
// counterparty, not between the introducer and the counterparty.
|
|
1962
|
+
const dedupUserId = (state.onBehalfOfUserId ?? state.userId);
|
|
1963
|
+
const uniqueCounterparts = new Set();
|
|
1964
|
+
for (const evaluated of state.evaluatedOpportunities) {
|
|
1965
|
+
const candidateUserId = evaluated.actors.find(a => a.userId !== dedupUserId)?.userId;
|
|
1966
|
+
if (candidateUserId)
|
|
1967
|
+
uniqueCounterparts.add(candidateUserId);
|
|
1968
|
+
}
|
|
1969
|
+
const lookups = await Promise.all([...uniqueCounterparts].map(async (counterpartyUserId) => {
|
|
1970
|
+
const accepted = await this.database
|
|
1971
|
+
.getAcceptedOpportunitiesBetweenActors(dedupUserId, counterpartyUserId)
|
|
1972
|
+
.catch((err) => {
|
|
1973
|
+
logger.warn('[Graph:Persist] getAcceptedOpportunitiesBetweenActors failed', {
|
|
1974
|
+
userId: dedupUserId,
|
|
1975
|
+
counterpartyUserId,
|
|
1976
|
+
error: err,
|
|
1977
|
+
});
|
|
1978
|
+
return [];
|
|
1979
|
+
});
|
|
1980
|
+
return accepted.map((opp) => ({ opportunityId: opp.id, counterpartyUserId }));
|
|
1981
|
+
}));
|
|
1982
|
+
dedupAlreadyAccepted.push(...lookups.flat());
|
|
1983
|
+
}
|
|
1899
1984
|
for (const evaluated of state.evaluatedOpportunities) {
|
|
1900
1985
|
const indexIdForActors = state.networkId ?? evaluated.actors[0]?.networkId;
|
|
1901
1986
|
let actors;
|
|
@@ -2176,13 +2261,15 @@ export class OpportunityGraphFactory {
|
|
|
2176
2261
|
return {
|
|
2177
2262
|
opportunities: allOpportunities,
|
|
2178
2263
|
existingBetweenActors,
|
|
2264
|
+
dedupAlreadyAccepted,
|
|
2179
2265
|
trace: [{
|
|
2180
2266
|
node: "persist",
|
|
2181
|
-
detail: `Created ${createdList.length}, reactivated ${reactivatedOpportunities.length}, ${existingBetweenActors.length} existing skipped`,
|
|
2267
|
+
detail: `Created ${createdList.length}, reactivated ${reactivatedOpportunities.length}, ${existingBetweenActors.length} existing skipped, ${dedupAlreadyAccepted.length} already-accepted pair(s)`,
|
|
2182
2268
|
data: {
|
|
2183
2269
|
created: createdList.length,
|
|
2184
2270
|
reactivated: reactivatedOpportunities.length,
|
|
2185
2271
|
existingSkipped: existingBetweenActors.length,
|
|
2272
|
+
alreadyAccepted: dedupAlreadyAccepted.length,
|
|
2186
2273
|
totalOutput: allOpportunities.length,
|
|
2187
2274
|
durationMs: Date.now() - startTime,
|
|
2188
2275
|
},
|