@indexnetwork/protocol 18.0.0-rc.481.1 → 20.0.0-rc.483.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 +54 -0
- package/IMPLEMENTATION.md +27 -10
- package/dist/contacts/application/contact.tools.js +1 -3
- package/dist/contacts/domain/contact.types.d.ts +0 -2
- package/dist/contexts/application/index.d.ts +0 -1
- package/dist/contexts/application/index.js +0 -1
- package/dist/enrichment/enrichment.graph.js +1 -36
- package/dist/index.d.ts +2 -3
- package/dist/index.js +4 -3
- package/dist/networks/{application/indexer.graph.d.ts → indexer.graph.d.ts} +11 -7
- package/dist/networks/{application/indexer.graph.js → indexer.graph.js} +6 -6
- package/dist/networks/{application/indexer.state.d.ts → indexer.state.d.ts} +7 -17
- package/dist/networks/{application/indexer.state.js → indexer.state.js} +6 -10
- package/dist/networks/{application/membership.graph.d.ts → membership.graph.d.ts} +2 -4
- package/dist/networks/{application/membership.graph.js → membership.graph.js} +3 -3
- package/dist/networks/{domain/membership.state.d.ts → membership.state.d.ts} +0 -2
- package/dist/networks/{domain/membership.state.js → membership.state.js} +0 -2
- package/dist/networks/{application/network.graph.d.ts → network.graph.d.ts} +2 -4
- package/dist/networks/{application/network.graph.js → network.graph.js} +3 -3
- package/dist/networks/network.module.d.ts +1314 -0
- package/dist/networks/network.module.js +83 -0
- package/dist/networks/{application/network.recommender.d.ts → network.recommender.d.ts} +0 -2
- package/dist/networks/{application/network.recommender.js → network.recommender.js} +4 -6
- package/dist/networks/{domain/network.state.d.ts → network.state.d.ts} +0 -2
- package/dist/networks/{domain/network.state.js → network.state.js} +0 -2
- package/dist/networks/{application/network.tools.d.ts → network.tools.d.ts} +5 -4
- package/dist/networks/{application/network.tools.js → network.tools.js} +5 -7
- package/dist/opportunities/application/opportunity.tools.cards.d.ts +1 -2
- package/dist/opportunities/application/opportunity.tools.cards.js +1 -2
- package/dist/opportunities/application/opportunity.tools.list.js +0 -4
- package/dist/opportunities/radar/radar.graph.js +0 -4
- package/dist/opportunities/radar/radar.state.d.ts +0 -2
- package/dist/shared/agent/tool.factory.js +6 -6
- package/dist/shared/agent/tool.registry.js +2 -2
- package/dist/shared/agent/utility.tools.js +1 -2
- package/dist/shared/interfaces/database.capabilities.d.ts +2 -2
- package/dist/shared/interfaces/database.entities.d.ts +0 -3
- package/dist/shared/interfaces/database.identity-queries.d.ts +0 -28
- package/dist/shared/interfaces/database.member-queries.d.ts +0 -2
- package/package.json +1 -1
- package/dist/enrichment/enrichment.enricher.d.ts +0 -18
- package/dist/enrichment/enrichment.enricher.js +0 -30
- package/dist/networks/application/index.d.ts +0 -45
- package/dist/networks/application/index.js +0 -50
- package/dist/networks/domain/index.d.ts +0 -29
- package/dist/networks/domain/index.js +0 -31
- package/dist/networks/index.d.ts +0 -9
- package/dist/networks/index.js +0 -8
- package/dist/networks/ports/communities.tools.port.d.ts +0 -5
- package/dist/networks/ports/communities.tools.port.js +0 -1
- package/dist/networks/ports/index.d.ts +0 -27
- package/dist/networks/ports/index.js +0 -23
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* networks — the capability's single public surface.
|
|
3
|
+
*
|
|
4
|
+
* Everything the rest of the package (and every host) may reach lives on the
|
|
5
|
+
* {@link Networks} class. The files beside this one are private implementation,
|
|
6
|
+
* named for what they do rather than for the layer they sit in:
|
|
7
|
+
*
|
|
8
|
+
* network.graph the community lifecycle graph — create, read, update, delete
|
|
9
|
+
* network.state that graph's channel state
|
|
10
|
+
* membership.graph the roster graph — add, list, remove members
|
|
11
|
+
* membership.state that graph's channel state
|
|
12
|
+
* indexer.graph signal↔community assignment, direct or model-evaluated
|
|
13
|
+
* indexer.state that graph's channel state
|
|
14
|
+
* network.recommender ranking public communities during onboarding
|
|
15
|
+
* network.tools the agent-facing tool definitions
|
|
16
|
+
*
|
|
17
|
+
* No directories: every stage here is one or two files, so a folder per stage
|
|
18
|
+
* would only add a hop. Nothing outside `networks/` imports any of it; the
|
|
19
|
+
* layout may change freely as long as this class keeps its shape.
|
|
20
|
+
*/
|
|
21
|
+
import { IntentNetworkGraphFactory } from "./indexer.graph.js";
|
|
22
|
+
import { NetworkMembershipGraphFactory } from "./membership.graph.js";
|
|
23
|
+
import { NetworkGraphFactory } from "./network.graph.js";
|
|
24
|
+
import { createNetworkTools } from "./network.tools.js";
|
|
25
|
+
/**
|
|
26
|
+
* The networks capability.
|
|
27
|
+
*
|
|
28
|
+
* One instance is cheap: it holds its dependencies and compiles a graph only
|
|
29
|
+
* when asked, so a host can keep a single `Networks` and build just the graphs
|
|
30
|
+
* it serves.
|
|
31
|
+
*/
|
|
32
|
+
export class Networks {
|
|
33
|
+
constructor(deps = {}) {
|
|
34
|
+
this.deps = deps;
|
|
35
|
+
}
|
|
36
|
+
// ── Community lifecycle ─────────────────────────────────────────────────────
|
|
37
|
+
/**
|
|
38
|
+
* Build the community lifecycle graph — create, read, update, delete.
|
|
39
|
+
*
|
|
40
|
+
* @throws If the instance was constructed without a `database`.
|
|
41
|
+
*/
|
|
42
|
+
createGraph() {
|
|
43
|
+
return new NetworkGraphFactory(this.database("createGraph")).createGraph();
|
|
44
|
+
}
|
|
45
|
+
// ── Roster ──────────────────────────────────────────────────────────────────
|
|
46
|
+
/**
|
|
47
|
+
* Build the membership graph — add, list, and remove members, under the
|
|
48
|
+
* community's join policy and owner authority.
|
|
49
|
+
*
|
|
50
|
+
* @throws If the instance was constructed without a `database`.
|
|
51
|
+
*/
|
|
52
|
+
createMembershipGraph() {
|
|
53
|
+
return new NetworkMembershipGraphFactory(this.database("createMembershipGraph")).createGraph();
|
|
54
|
+
}
|
|
55
|
+
// ── Signal assignment ───────────────────────────────────────────────────────
|
|
56
|
+
/**
|
|
57
|
+
* Build the signal↔community assignment graph — link a signal to a community
|
|
58
|
+
* directly or after model evaluation, and unlink it.
|
|
59
|
+
*
|
|
60
|
+
* @throws If the instance was constructed without a `database` or an `indexer`.
|
|
61
|
+
*/
|
|
62
|
+
createAssignmentGraph() {
|
|
63
|
+
const { indexer } = this.deps;
|
|
64
|
+
if (!indexer) {
|
|
65
|
+
throw new Error("Networks.createAssignmentGraph() requires an `indexer` dependency.");
|
|
66
|
+
}
|
|
67
|
+
return new IntentNetworkGraphFactory(this.database("createAssignmentGraph"), indexer).createGraph();
|
|
68
|
+
}
|
|
69
|
+
// ── Stateless surface ───────────────────────────────────────────────────────
|
|
70
|
+
/** Register the agent-facing community tools against a tool definer. */
|
|
71
|
+
static createTools(defineTool, deps) {
|
|
72
|
+
return createNetworkTools(defineTool, deps);
|
|
73
|
+
}
|
|
74
|
+
// ── Internals ───────────────────────────────────────────────────────────────
|
|
75
|
+
/** The database, or the error naming the method that needed it. */
|
|
76
|
+
database(method) {
|
|
77
|
+
const { database } = this.deps;
|
|
78
|
+
if (!database) {
|
|
79
|
+
throw new Error(`Networks.${method}() requires a \`database\` dependency.`);
|
|
80
|
+
}
|
|
81
|
+
return database;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -28,8 +28,6 @@ export interface NetworkRecommenderInput {
|
|
|
28
28
|
* (not at module level) so that importing this module does not require
|
|
29
29
|
* OPENROUTER_API_KEY to be set — tests that import communities tools without a
|
|
30
30
|
* live LLM env are unaffected.
|
|
31
|
-
*
|
|
32
|
-
* IND-546: canonical home — previously network/network.recommender.ts.
|
|
33
31
|
*/
|
|
34
32
|
export declare class NetworkRecommender {
|
|
35
33
|
private model;
|
|
@@ -9,10 +9,10 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
};
|
|
10
10
|
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
|
|
11
11
|
import { z } from "zod";
|
|
12
|
-
import { log } from "
|
|
13
|
-
import { Timed } from "
|
|
14
|
-
import { createStructuredModel } from "
|
|
15
|
-
import { invokeWithAbortSignal } from "
|
|
12
|
+
import { log } from "../shared/observability/log.js";
|
|
13
|
+
import { Timed } from "../shared/observability/performance.js";
|
|
14
|
+
import { createStructuredModel } from "../shared/agent/model.config.js";
|
|
15
|
+
import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
|
|
16
16
|
// ─── Response schema ───────────────────────────────────────────────────────────
|
|
17
17
|
export const NetworkRecommenderOutputSchema = z.object({
|
|
18
18
|
rankedNetworkIds: z
|
|
@@ -57,8 +57,6 @@ OUTPUT RULES:
|
|
|
57
57
|
* (not at module level) so that importing this module does not require
|
|
58
58
|
* OPENROUTER_API_KEY to be set — tests that import communities tools without a
|
|
59
59
|
* live LLM env are unaffected.
|
|
60
|
-
*
|
|
61
|
-
* IND-546: canonical home — previously network/network.recommender.ts.
|
|
62
60
|
*/
|
|
63
61
|
export class NetworkRecommender {
|
|
64
62
|
constructor() {
|
|
@@ -10,8 +10,6 @@
|
|
|
10
10
|
* When the chat is network-scoped (`networkId` is set) and `showAll` is false,
|
|
11
11
|
* readNode surfaces only the focused network plus the user's personal network
|
|
12
12
|
* (contacts). Setting `showAll: true` bypasses the restriction (admin use).
|
|
13
|
-
*
|
|
14
|
-
* IND-546: canonical home — previously network/network.state.ts.
|
|
15
13
|
*/
|
|
16
14
|
export declare const NetworkGraphState: import("@langchain/langgraph").AnnotationRoot<{
|
|
17
15
|
/** User performing the action. Always required. */
|
|
@@ -11,8 +11,6 @@ import { Annotation } from "@langchain/langgraph";
|
|
|
11
11
|
* When the chat is network-scoped (`networkId` is set) and `showAll` is false,
|
|
12
12
|
* readNode surfaces only the focused network plus the user's personal network
|
|
13
13
|
* (contacts). Setting `showAll: true` bypasses the restriction (admin use).
|
|
14
|
-
*
|
|
15
|
-
* IND-546: canonical home — previously network/network.state.ts.
|
|
16
14
|
*/
|
|
17
15
|
export const NetworkGraphState = Annotation.Root({
|
|
18
16
|
// --- Core Inputs (from ChatGraph via ToolContext) ---
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import type { DefineTool } from "
|
|
2
|
-
|
|
1
|
+
import type { DefineTool, ToolRegistryCompositionDeps } from "../shared/agent/tool.helpers.js";
|
|
2
|
+
/** Host capabilities consumed by community discovery and membership tools. */
|
|
3
|
+
export type NetworkToolDeps = Pick<ToolRegistryCompositionDeps, "userDb" | "systemDb" | "getUserContextText" | "networkRanker" | "reportToolError"> & {
|
|
4
|
+
graphs: Pick<ToolRegistryCompositionDeps["graphs"], "index" | "networkMembership">;
|
|
5
|
+
};
|
|
3
6
|
/**
|
|
4
7
|
* Creates all community (network) lifecycle and membership tools.
|
|
5
8
|
*
|
|
@@ -29,7 +32,5 @@ import type { NetworkToolDeps } from "../ports/communities.tools.port.js";
|
|
|
29
32
|
* `context.isOnboarding` is true and public networks are available. Ranking is
|
|
30
33
|
* performed by `NetworkRecommender` (ambient LLM agent) or a `deps.networkRanker`
|
|
31
34
|
* override, with graceful degradation if ranking fails.
|
|
32
|
-
*
|
|
33
|
-
* IND-546: canonical home — previously network/network.tools.ts.
|
|
34
35
|
*/
|
|
35
36
|
export declare function createNetworkTools(defineTool: DefineTool, deps: NetworkToolDeps): readonly [any, any, any, any, any, any, any];
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { requestContext } from "
|
|
3
|
-
import { log } from "
|
|
4
|
-
import { renderNetworkContext } from "
|
|
5
|
-
import { success, error, UUID_REGEX } from "
|
|
6
|
-
import { focusedNetworkId } from "
|
|
2
|
+
import { requestContext } from "../shared/observability/request-context.js";
|
|
3
|
+
import { log } from "../shared/observability/log.js";
|
|
4
|
+
import { renderNetworkContext } from "../shared/network/metadata.renderer.js";
|
|
5
|
+
import { success, error, UUID_REGEX } from "../shared/agent/tool.helpers.js";
|
|
6
|
+
import { focusedNetworkId } from "../shared/agent/tool.scope.js";
|
|
7
7
|
import { NetworkRecommender } from "./network.recommender.js";
|
|
8
8
|
/**
|
|
9
9
|
* Resolves the community this caller is hard-bound to, if any.
|
|
@@ -52,8 +52,6 @@ const logger = log.protocol.from("ChatTools:Network");
|
|
|
52
52
|
* `context.isOnboarding` is true and public networks are available. Ranking is
|
|
53
53
|
* performed by `NetworkRecommender` (ambient LLM agent) or a `deps.networkRanker`
|
|
54
54
|
* override, with graceful degradation if ranking fails.
|
|
55
|
-
*
|
|
56
|
-
* IND-546: canonical home — previously network/network.tools.ts.
|
|
57
55
|
*/
|
|
58
56
|
export function createNetworkTools(defineTool, deps) {
|
|
59
57
|
const { graphs, userDb, systemDb } = deps;
|
|
@@ -111,7 +111,7 @@ export declare const CHAT_DISPLAY_LIMIT = 6;
|
|
|
111
111
|
*
|
|
112
112
|
* Exported for use in tests (opportunity.tools.spec.ts).
|
|
113
113
|
*/
|
|
114
|
-
export declare function buildMinimalOpportunityCard(opp: Opportunity, viewerId: string, counterpartUserId: string, counterpartName: string, counterpartAvatar: string | null, introducerName?: string | null, introducerAvatar?: string | null, viewerName?: string, secondPartyName?: string, secondPartyAvatar?: string | null, secondPartyUserId?: string
|
|
114
|
+
export declare function buildMinimalOpportunityCard(opp: Opportunity, viewerId: string, counterpartUserId: string, counterpartName: string, counterpartAvatar: string | null, introducerName?: string | null, introducerAvatar?: string | null, viewerName?: string, secondPartyName?: string, secondPartyAvatar?: string | null, secondPartyUserId?: string): {
|
|
115
115
|
opportunityId: string;
|
|
116
116
|
userId: string;
|
|
117
117
|
name: string;
|
|
@@ -131,7 +131,6 @@ export declare function buildMinimalOpportunityCard(opp: Opportunity, viewerId:
|
|
|
131
131
|
viewerRole: string;
|
|
132
132
|
score: number | undefined;
|
|
133
133
|
status: string;
|
|
134
|
-
isGhost: boolean;
|
|
135
134
|
secondParty?: {
|
|
136
135
|
name: string;
|
|
137
136
|
avatar?: string | null;
|
|
@@ -172,7 +172,7 @@ export const CHAT_DISPLAY_LIMIT = 6;
|
|
|
172
172
|
*
|
|
173
173
|
* Exported for use in tests (opportunity.tools.spec.ts).
|
|
174
174
|
*/
|
|
175
|
-
export function buildMinimalOpportunityCard(opp, viewerId, counterpartUserId, counterpartName, counterpartAvatar, introducerName, introducerAvatar, viewerName, secondPartyName, secondPartyAvatar, secondPartyUserId
|
|
175
|
+
export function buildMinimalOpportunityCard(opp, viewerId, counterpartUserId, counterpartName, counterpartAvatar, introducerName, introducerAvatar, viewerName, secondPartyName, secondPartyAvatar, secondPartyUserId) {
|
|
176
176
|
const viewerActor = opp.actors.find((a) => a.userId === viewerId);
|
|
177
177
|
const viewerRole = viewerActor?.role ?? "party";
|
|
178
178
|
const introducerActor = opp.actors.find((a) => a.role === "introducer" && a.userId !== viewerId);
|
|
@@ -218,7 +218,6 @@ export function buildMinimalOpportunityCard(opp, viewerId, counterpartUserId, co
|
|
|
218
218
|
viewerRole,
|
|
219
219
|
score,
|
|
220
220
|
status: opp.status ?? "latent",
|
|
221
|
-
isGhost: isCounterpartGhost ?? false,
|
|
222
221
|
...(viewerIsIntroducer && secondPartyName
|
|
223
222
|
? {
|
|
224
223
|
secondParty: {
|
|
@@ -212,7 +212,6 @@ export function createListOpportunitiesTool(defineTool, deps) {
|
|
|
212
212
|
: undefined;
|
|
213
213
|
const viewerActor = opp.actors.find((a) => a.userId === context.userId);
|
|
214
214
|
const viewerRole = viewerActor?.role ?? "party";
|
|
215
|
-
const isCounterpartGhost = counterpartUser?.isGhost ?? false;
|
|
216
215
|
try {
|
|
217
216
|
// Load the negotiation context alongside presenter context so
|
|
218
217
|
// the digest copy can explain *why* the opportunity surfaced
|
|
@@ -271,7 +270,6 @@ export function createListOpportunitiesTool(defineTool, deps) {
|
|
|
271
270
|
? opp.interpretation.confidence
|
|
272
271
|
: undefined,
|
|
273
272
|
status: opp.status,
|
|
274
|
-
isGhost: isCounterpartGhost,
|
|
275
273
|
...(redeliveryIds.has(opp.id) ? { redelivery: true } : {}),
|
|
276
274
|
...(viewerIsIntroducerHere && secondPartyNameForHeadline
|
|
277
275
|
? {
|
|
@@ -371,7 +369,6 @@ export function createListOpportunitiesTool(defineTool, deps) {
|
|
|
371
369
|
: undefined;
|
|
372
370
|
const viewerActor = opp.actors.find((a) => a.userId === context.userId);
|
|
373
371
|
const viewerRole = viewerActor?.role ?? "party";
|
|
374
|
-
const isCounterpartGhost = counterpartUser?.isGhost ?? false;
|
|
375
372
|
const [ctx, negotiationContext] = await Promise.all([
|
|
376
373
|
gatherOpportunityPresenterContext(presenterDb, opp, context.userId, counterpartUserId),
|
|
377
374
|
loadNegotiationContext(deps.negotiationDatabase, opp.id, opp.status),
|
|
@@ -417,7 +414,6 @@ export function createListOpportunitiesTool(defineTool, deps) {
|
|
|
417
414
|
? opp.interpretation.confidence
|
|
418
415
|
: undefined,
|
|
419
416
|
status: opp.status,
|
|
420
|
-
isGhost: isCounterpartGhost,
|
|
421
417
|
...(viewerIsIntroducerHere && secondPartyNameForHeadline
|
|
422
418
|
? {
|
|
423
419
|
secondParty: {
|
|
@@ -478,7 +478,6 @@ export async function generateCardTextNode(state, deps) {
|
|
|
478
478
|
};
|
|
479
479
|
}
|
|
480
480
|
}
|
|
481
|
-
const isCounterpartGhost = otherUser?.isGhost ?? false;
|
|
482
481
|
// Skeleton presentation: return an identity-only card without the
|
|
483
482
|
// deps.presenter LLM or negotiation-context load. Name resolution and
|
|
484
483
|
// the unresolvable-counterpart drop above still apply, so the card
|
|
@@ -496,7 +495,6 @@ export async function generateCardTextNode(state, deps) {
|
|
|
496
495
|
secondaryActionLabel: SECONDARY_ACTION_LABEL,
|
|
497
496
|
mutualIntentsLabel: isIntroducer ? 'Connector match' : 'Shared interests',
|
|
498
497
|
viewerRole,
|
|
499
|
-
isGhost: isCounterpartGhost,
|
|
500
498
|
...(secondPartyData ? { secondParty: secondPartyData } : {}),
|
|
501
499
|
presentationPending: true,
|
|
502
500
|
_cardIndex: cardIndex,
|
|
@@ -520,7 +518,6 @@ export async function generateCardTextNode(state, deps) {
|
|
|
520
518
|
? { name: 'You', text: 'Worth a look.', userId: state.userId }
|
|
521
519
|
: { name: 'Index', text: 'Worth a look.' },
|
|
522
520
|
viewerRole,
|
|
523
|
-
isGhost: isCounterpartGhost,
|
|
524
521
|
...(secondPartyData ? { secondParty: secondPartyData } : {}),
|
|
525
522
|
_presentationFallback: true,
|
|
526
523
|
_cardIndex: cardIndex,
|
|
@@ -580,7 +577,6 @@ export async function generateCardTextNode(state, deps) {
|
|
|
580
577
|
mutualIntentsLabel: presentation.mutualIntentsLabel,
|
|
581
578
|
narratorChip,
|
|
582
579
|
viewerRole,
|
|
583
|
-
isGhost: isCounterpartGhost,
|
|
584
580
|
...(secondPartyData ? { secondParty: secondPartyData } : {}),
|
|
585
581
|
_cardIndex: cardIndex,
|
|
586
582
|
};
|
|
@@ -27,8 +27,6 @@ export interface RadarCardItem {
|
|
|
27
27
|
};
|
|
28
28
|
/** Viewer's role in this opportunity (e.g. 'introducer', 'party', 'agent', 'patient', 'peer'). */
|
|
29
29
|
viewerRole?: string;
|
|
30
|
-
/** Whether the counterpart is a ghost (not yet onboarded) user. */
|
|
31
|
-
isGhost?: boolean;
|
|
32
30
|
/** Template-only explanation for a pool-answer demotion (never evaluator reasoning). */
|
|
33
31
|
deprioritizedReason?: string;
|
|
34
32
|
/** Second party in introducer arrow layout. Present when viewerRole is 'introducer'. */
|
|
@@ -5,7 +5,7 @@ import { OpportunityGraphFactory } from "../../opportunities/index.js";
|
|
|
5
5
|
import { HydeGraphFactory } from "../../discovery/hyde.graph.js";
|
|
6
6
|
import { HydeGenerator } from "../../discovery/hyde.generator.js";
|
|
7
7
|
import { LensInferrer } from "../../discovery/lens.inferrer.js";
|
|
8
|
-
import {
|
|
8
|
+
import { Networks } from "../../networks/network.module.js";
|
|
9
9
|
import { NegotiationGraphFactory } from "../../negotiations/index.js";
|
|
10
10
|
import { PremiseGraphFactory } from "../../premises/premise.graph.js";
|
|
11
11
|
import { protocolLogger } from "../observability/protocol.logger.js";
|
|
@@ -13,7 +13,6 @@ import { resolveChatContext, error, redactSensitiveFields } from "./tool.helpers
|
|
|
13
13
|
import { deriveAllowedNetworkIds, focusedIntentId, scopeFromNetworkId } from "./tool.scope.js";
|
|
14
14
|
import { invokeToolRuntime, toolRuntimeErrorToResult } from "./tool.runtime.js";
|
|
15
15
|
import { createEnrichmentTools } from "../../enrichment/enrichment.tools.js";
|
|
16
|
-
import { createNetworkTools } from "../../networks/index.js";
|
|
17
16
|
import { createOpportunityTools } from "../../opportunities/index.js";
|
|
18
17
|
import { createUtilityTools } from "./utility.tools.js";
|
|
19
18
|
import { createContactTools } from "../../contacts/index.js";
|
|
@@ -133,9 +132,10 @@ export async function createChatTools(deps, preResolvedContext) {
|
|
|
133
132
|
const opportunityGraph = new OpportunityGraphFactory(database, embedder, compiledHydeGraph, undefined, // evaluator (default)
|
|
134
133
|
undefined, // queueNotification
|
|
135
134
|
negotiationGraph, deps.agentDispatcher, deps.queueNegotiateExisting, deps.stampNewbornOpportunities).createGraph();
|
|
136
|
-
const
|
|
137
|
-
const
|
|
138
|
-
const
|
|
135
|
+
const networks = new Networks({ database, indexer: intents });
|
|
136
|
+
const networkGraph = networks.createGraph();
|
|
137
|
+
const networkMembershipGraph = networks.createMembershipGraph();
|
|
138
|
+
const intentNetworkGraph = networks.createAssignmentGraph();
|
|
139
139
|
// ─── Create context-bound databases ────────────────────────────────────────
|
|
140
140
|
// Use injected instances when provided (e.g. tests). Otherwise create from the same
|
|
141
141
|
// database used for graphs so that scope checks (e.g. ensureScopedMembership, opportunity
|
|
@@ -197,7 +197,7 @@ export async function createChatTools(deps, preResolvedContext) {
|
|
|
197
197
|
const intentToolsForChat = focusedIntentId(resolvedContext)
|
|
198
198
|
? intentTools.filter((candidate) => candidate.name !== "create_intent")
|
|
199
199
|
: intentTools;
|
|
200
|
-
const networkTools =
|
|
200
|
+
const networkTools = Networks.createTools(defineTool, toolDeps);
|
|
201
201
|
const opportunityTools = createOpportunityTools(defineTool, toolDeps);
|
|
202
202
|
const utilityTools = createUtilityTools(defineTool, toolDeps);
|
|
203
203
|
const contactTools = createContactTools(defineTool, toolDeps);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { error, redactSensitiveFields } from './tool.helpers.js';
|
|
2
2
|
import { createEnrichmentTools } from '../../enrichment/enrichment.tools.js';
|
|
3
3
|
import { Intents } from '../../intents/intent.module.js';
|
|
4
|
-
import {
|
|
4
|
+
import { Networks } from '../../networks/network.module.js';
|
|
5
5
|
import { createOpportunityTools } from '../../opportunities/index.js';
|
|
6
6
|
import { createUtilityTools } from './utility.tools.js';
|
|
7
7
|
import { createContactTools } from '../../contacts/index.js';
|
|
@@ -54,7 +54,7 @@ export function createToolRegistry(deps, options = {}) {
|
|
|
54
54
|
const dt = defineTool;
|
|
55
55
|
createEnrichmentTools(dt, deps, { surface: isMcpSurface ? 'mcp' : 'rest' });
|
|
56
56
|
Intents.createTools(dt, deps);
|
|
57
|
-
|
|
57
|
+
Networks.createTools(dt, deps);
|
|
58
58
|
createOpportunityTools(dt, deps);
|
|
59
59
|
// Utility tools always register read_docs + read_activity_summary; on the
|
|
60
60
|
// MCP surface scrape_url is omitted and read_docs guidance is sanitized
|
|
@@ -119,8 +119,7 @@ Contacts are people in a user's personal network, stored as members of their per
|
|
|
119
119
|
- **Intents**: Signals of interest/need — what a user is looking for (e.g. "Looking for a React developer in Berlin"). Each has a description (payload), summary, confidence score (0-1), inferenceType (explicit/implicit), source tracking, and vector embedding.
|
|
120
120
|
- **IntentNetworks**: Many-to-many junction between Intents and Indexes. An intent can be in multiple indexes. Has a relevancyScore (0-1) indicating how well the intent fits the index's purpose.
|
|
121
121
|
- **Opportunities**: Discovered connections between users based on complementary intents within shared networks. Have actors with roles (introducer, party), status lifecycle, match reasoning, confidence score, and presentation data.
|
|
122
|
-
- **Contacts**: People in a user's personal network, stored as network members with 'contact' permission on the personal network.
|
|
123
|
-
- **Ghost Users**: Placeholder accounts created for contacts who aren't on the platform yet. Enriched with public profile data (LinkedIn, GitHub) and participate in opportunity matching.
|
|
122
|
+
- **Contacts**: People in a user's personal network, stored as network members with 'contact' permission on the personal network. Established by accepting an opportunity, which adds both people to each other's personal network. Always real accounts.
|
|
124
123
|
|
|
125
124
|
### Key Relationships
|
|
126
125
|
- Users → Profiles (1:1)
|
|
@@ -12,7 +12,7 @@ import type { Database } from './database.port.js';
|
|
|
12
12
|
*
|
|
13
13
|
* Access layer: Primarily UserDatabase (user's own profile)
|
|
14
14
|
*/
|
|
15
|
-
export type EnrichmentGraphDatabase = Pick<Database, 'getProfile' | 'getUser' | 'updateUser' | 'saveProfile' | 'getProfileByUserId' | '
|
|
15
|
+
export type EnrichmentGraphDatabase = Pick<Database, 'getProfile' | 'getUser' | 'updateUser' | 'saveProfile' | 'getProfileByUserId' | 'getUserSocials' | 'setUserSocials' | 'getPremisesForUser' | 'getUserContext'> & {
|
|
16
16
|
/**
|
|
17
17
|
* Optional premise retraction support. When present, write-mode input that
|
|
18
18
|
* disavows existing premises ("remove X", "I have nothing to do with Y")
|
|
@@ -37,7 +37,7 @@ export type PremiseGraphDatabase = Pick<Database, 'createPremise' | 'getPremise'
|
|
|
37
37
|
*
|
|
38
38
|
* Access layer: Both UserDatabase + SystemDatabase (orchestrates all operations)
|
|
39
39
|
*/
|
|
40
|
-
export type ChatGraphCompositeDatabase = Pick<Database, 'getProfile' | 'getActiveIntents' | 'getActiveIntentsAcrossIndexes' | 'getIntentsInIndexForMember' | 'getUser' | 'updateUser' | 'getUserSocials' | 'setUserSocials' | 'saveProfile' | '
|
|
40
|
+
export type ChatGraphCompositeDatabase = Pick<Database, 'getProfile' | 'getActiveIntents' | 'getActiveIntentsAcrossIndexes' | 'getIntentsInIndexForMember' | 'getUser' | 'updateUser' | 'getUserSocials' | 'setUserSocials' | 'saveProfile' | 'createIntent' | 'updateIntent' | 'archiveIntent' | 'createOpportunity' | 'createOpportunityIfNetworkEligible' | 'createOpportunityAndExpireIdsIfNetworkEligible' | 'persistIntentScopedOpportunityIfNetworkEligible' | 'updateOpportunityStatusIfNetworkEligible' | 'getOpportunity' | 'getOpportunitiesByIds' | 'opportunityExistsBetweenActors' | 'findOpportunitiesByActors' | 'getOpportunitiesForUser' | 'updateOpportunityStatus' | 'compensateTasklessNegotiatingOpportunity' | 'updateOpportunityActorApproval' | 'stampOpportunityActorAction' | 'getOrCreateDM' | 'getHydeDocument' | 'getHydeDocumentsForSource' | 'saveHydeDocument' | 'getIntent' | 'getPublicIndexesNotJoined' | 'getUserIndexIds' | 'getAssignmentNetworkMembershipsForUser' | 'getAssignmentNetworkIdsForUser' | 'getNetworkMemberships' | 'getNetworkMembership' | 'getActiveNetworkMembershipPairs' | 'getNetwork' | 'getNetworkWithPermissions' | 'getIntentForIndexing' | 'getNetworkMemberContext' | 'getNetworkAssignmentContext' | 'isIntentAssignedToIndex' | 'assignIntentToNetwork' | 'assignIntentToNetworkIfMember' | 'unassignIntentFromIndex' | 'getNetworkIdsForIntent' | 'getIntentIndexScores' | 'getPersonalIndexesForContact' | 'getOwnedIndexes' | 'isIndexOwner' | 'isNetworkMember' | 'getNetworkMembersForOwner' | 'getNetworkMembersForMember' | 'getMembersFromUserIndexes' | 'getNetworkIntentsForOwner' | 'getNetworkIntentsForMember' | 'updateIndexSettings' | 'softDeleteNetwork' | 'deleteProfile' | 'getProfileByUserId' | 'createNetwork' | 'getNetworkMemberCount' | 'addMemberToNetwork' | 'removeMemberFromIndex' | 'getPremisesForUser' | 'getPremisesForUserInNetworks' | 'createPremise' | 'getPremise' | 'updatePremise' | 'assignPremiseToNetwork' | 'getPremiseNetworks' | 'searchPremisesBySimilarity' | 'searchPremisesBySimilarityBatch' | 'getUserContext' | 'getUserContexts' | 'searchIntentsByContextEmbedding' | 'searchUserContextsBySimilarity'> & Pick<NegotiationQueries, 'getNegotiationTaskForOpportunity'>;
|
|
41
41
|
/**
|
|
42
42
|
* Database interface for Opportunity Graph operations.
|
|
43
43
|
* Includes prep/scope (network membership, intents, index details), persist (create, dedupe),
|
|
@@ -121,7 +121,6 @@ export interface UserRecord {
|
|
|
121
121
|
location?: string | null;
|
|
122
122
|
socials: UserSocial[];
|
|
123
123
|
onboarding?: OnboardingState | null;
|
|
124
|
-
isGhost?: boolean;
|
|
125
124
|
deletedAt?: Date | null;
|
|
126
125
|
}
|
|
127
126
|
/**
|
|
@@ -402,8 +401,6 @@ export interface IndexMemberDetails {
|
|
|
402
401
|
joinedAt: Date;
|
|
403
402
|
/** Count of their intents in this network */
|
|
404
403
|
intentCount: number;
|
|
405
|
-
/** Whether this user is a ghost (not yet onboarded) */
|
|
406
|
-
isGhost?: boolean;
|
|
407
404
|
}
|
|
408
405
|
/**
|
|
409
406
|
* Intent details visible to network owners.
|
|
@@ -44,34 +44,6 @@ export interface DatabaseIdentityQueries {
|
|
|
44
44
|
label: string;
|
|
45
45
|
value: string;
|
|
46
46
|
}[]): Promise<void>;
|
|
47
|
-
/**
|
|
48
|
-
* Soft-delete a ghost user and all their contact memberships.
|
|
49
|
-
* Used when enrichment determines the entity is not a real person.
|
|
50
|
-
* @param userId - The ghost user to soft-delete
|
|
51
|
-
* @returns true if the user was soft-deleted
|
|
52
|
-
*/
|
|
53
|
-
softDeleteGhost(userId: string): Promise<boolean>;
|
|
54
|
-
/**
|
|
55
|
-
* Find an existing user that matches the given social handles.
|
|
56
|
-
* Checks LinkedIn, GitHub, and Twitter/X handles (case-insensitive, exact match).
|
|
57
|
-
* Excludes the given userId and soft-deleted users.
|
|
58
|
-
* Prefers real users over ghosts; among ghosts, returns the oldest.
|
|
59
|
-
* @param userId - The ghost user being enriched (excluded from results)
|
|
60
|
-
* @param socials - Enriched social handles to match against
|
|
61
|
-
* @returns The matching user's id, or null if no match
|
|
62
|
-
*/
|
|
63
|
-
findDuplicateUser(userId: string, socials: UserSocial[]): Promise<{
|
|
64
|
-
id: string;
|
|
65
|
-
} | null>;
|
|
66
|
-
/**
|
|
67
|
-
* Merge a ghost user (source) into a target user.
|
|
68
|
-
* Re-points all data (intents, opportunities, memberships, etc.) from source to target,
|
|
69
|
-
* deletes ghost-only records (profile, sessions, etc.), and soft-deletes the source user.
|
|
70
|
-
* Runs in a single transaction.
|
|
71
|
-
* @param sourceId - The ghost user to merge away
|
|
72
|
-
* @param targetId - The user to merge into
|
|
73
|
-
*/
|
|
74
|
-
mergeGhostUser(sourceId: string, targetId: string): Promise<void>;
|
|
75
47
|
/**
|
|
76
48
|
* Retrieves all active (non-archived) intents for a user.
|
|
77
49
|
* Used to populate the `activeIntents` field in the Intent Graph state
|
|
@@ -35,7 +35,6 @@ export interface DatabaseMemberQueries {
|
|
|
35
35
|
name: string;
|
|
36
36
|
email: string;
|
|
37
37
|
avatar: string | null;
|
|
38
|
-
isGhost: boolean;
|
|
39
38
|
};
|
|
40
39
|
}>>;
|
|
41
40
|
/** Clear a reverse opt-out (reactivate soft-deleted contact membership in another user's personal network). */
|
|
@@ -55,7 +54,6 @@ export interface DatabaseMemberQueries {
|
|
|
55
54
|
id: string;
|
|
56
55
|
name: string;
|
|
57
56
|
email: string;
|
|
58
|
-
isGhost: boolean;
|
|
59
57
|
} | null>;
|
|
60
58
|
createPremise(input: {
|
|
61
59
|
userId: string;
|
package/package.json
CHANGED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Returns true when the enriched name is meaningfully better than the email local-part.
|
|
3
|
-
* A name that is empty, contains '@', or case-insensitively matches the prefix is NOT meaningful.
|
|
4
|
-
*/
|
|
5
|
-
export declare function isEnrichedNameMeaningful(email: string, enrichedName: string): boolean;
|
|
6
|
-
/**
|
|
7
|
-
* Decides whether to set {@link users.name} from Parallel `enrichment.identity.name` for ghost users.
|
|
8
|
-
* @remarks Real users (Google login, etc.) are never touched. Ghost users always get
|
|
9
|
-
* their name enriched when Parallel returns a non-empty name that isn't an email.
|
|
10
|
-
* @param user - Current user row (must include `email`, `name`, `isGhost`)
|
|
11
|
-
* @param enrichedName - `enrichment.identity.name` from Parallel (may be untrimmed)
|
|
12
|
-
* @returns True if `users.name` should be updated to the enriched full name
|
|
13
|
-
*/
|
|
14
|
-
export declare function shouldEnrichGhostDisplayNameFromParallel(user: {
|
|
15
|
-
name: string;
|
|
16
|
-
email: string;
|
|
17
|
-
isGhost?: boolean | null;
|
|
18
|
-
}, enrichedName: string): boolean;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Returns true when the enriched name is meaningfully better than the email local-part.
|
|
3
|
-
* A name that is empty, contains '@', or case-insensitively matches the prefix is NOT meaningful.
|
|
4
|
-
*/
|
|
5
|
-
export function isEnrichedNameMeaningful(email, enrichedName) {
|
|
6
|
-
const trimmed = enrichedName.trim();
|
|
7
|
-
if (!trimmed || trimmed.includes('@'))
|
|
8
|
-
return false;
|
|
9
|
-
const localPart = email.split('@')[0].toLowerCase();
|
|
10
|
-
return trimmed.toLowerCase() !== localPart;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Decides whether to set {@link users.name} from Parallel `enrichment.identity.name` for ghost users.
|
|
14
|
-
* @remarks Real users (Google login, etc.) are never touched. Ghost users always get
|
|
15
|
-
* their name enriched when Parallel returns a non-empty name that isn't an email.
|
|
16
|
-
* @param user - Current user row (must include `email`, `name`, `isGhost`)
|
|
17
|
-
* @param enrichedName - `enrichment.identity.name` from Parallel (may be untrimmed)
|
|
18
|
-
* @returns True if `users.name` should be updated to the enriched full name
|
|
19
|
-
*/
|
|
20
|
-
export function shouldEnrichGhostDisplayNameFromParallel(user, enrichedName) {
|
|
21
|
-
if (!user.isGhost)
|
|
22
|
-
return false;
|
|
23
|
-
const trimmed = enrichedName.trim();
|
|
24
|
-
if (!trimmed || trimmed.includes("@"))
|
|
25
|
-
return false;
|
|
26
|
-
// Skip only if exactly the same (case-sensitive)
|
|
27
|
-
if (user.name.trim() === trimmed)
|
|
28
|
-
return false;
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* networks/application — orchestrators, factories, tools, and agents.
|
|
3
|
-
*
|
|
4
|
-
* Re-exports the orchestration tier of the communities capability: graph
|
|
5
|
-
* factories (LangGraph compilations), tool factories, and agent classes.
|
|
6
|
-
*
|
|
7
|
-
* Boundary: application-layer only. Imports from networks/domain,
|
|
8
|
-
* networks/ports, and shared/ infrastructure — never from runtime/,
|
|
9
|
-
* host implementations, or capability facades (except as injected ports).
|
|
10
|
-
*
|
|
11
|
-
* ## Foreground use cases (participant-directed)
|
|
12
|
-
*
|
|
13
|
-
* - {@link NetworkGraphFactory} — compiles the network lifecycle graph
|
|
14
|
-
* (create, read, update, delete networks).
|
|
15
|
-
* - {@link NetworkMembershipGraphFactory} — compiles the membership graph
|
|
16
|
-
* (add, list, remove members; enforces join-policy and owner authority).
|
|
17
|
-
* - {@link IntentNetworkGraphFactory} — compiles the signal assignment graph
|
|
18
|
-
* (direct or LLM-evaluated intent–network linking; unassign).
|
|
19
|
-
* IntentIndexer is injected from the signals public facade.
|
|
20
|
-
* - {@link createNetworkTools} — foreground tool factory; accepts compiled graphs
|
|
21
|
-
* and host deps through communities.tools.port and produces LangChain-compatible
|
|
22
|
-
* tool arrays for the tool registry.
|
|
23
|
-
*
|
|
24
|
-
* ## Ambient use case (ranking / recommendation)
|
|
25
|
-
*
|
|
26
|
-
* - {@link NetworkRecommender} — LLM-based community ranking used during onboarding
|
|
27
|
-
* (step 6) to surface the most relevant public communities for a user. Lazy-
|
|
28
|
-
* instantiated inside createNetworkTools to avoid requiring OPENROUTER_API_KEY
|
|
29
|
-
* at import time.
|
|
30
|
-
*
|
|
31
|
-
* ## Application-internal (not in public surface)
|
|
32
|
-
*
|
|
33
|
-
* - {@link IntentNetworkGraphState}, {@link AssignmentResult}, etc. — graph execution
|
|
34
|
-
* state types used within indexer.graph.ts; available here for consumers that
|
|
35
|
-
* need the application-layer state shape.
|
|
36
|
-
*
|
|
37
|
-
* IND-546: canonical application home for communities capability previously spread
|
|
38
|
-
* across network/, network/membership/, and network/indexer/.
|
|
39
|
-
*/
|
|
40
|
-
export { NetworkGraphFactory } from "./network.graph.js";
|
|
41
|
-
export { NetworkMembershipGraphFactory } from "./membership.graph.js";
|
|
42
|
-
export { IntentNetworkGraphFactory } from "./indexer.graph.js";
|
|
43
|
-
export { IntentNetworkGraphState, type AssignmentResult, type IntentForIndexing, type IndexMemberContext, } from "./indexer.state.js";
|
|
44
|
-
export { NetworkRecommender, NetworkRecommenderOutputSchema, type NetworkRecommenderOutput, type NetworkRecommenderInput, type NetworkRecommenderNetwork, } from "./network.recommender.js";
|
|
45
|
-
export { createNetworkTools } from "./network.tools.js";
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* networks/application — orchestrators, factories, tools, and agents.
|
|
3
|
-
*
|
|
4
|
-
* Re-exports the orchestration tier of the communities capability: graph
|
|
5
|
-
* factories (LangGraph compilations), tool factories, and agent classes.
|
|
6
|
-
*
|
|
7
|
-
* Boundary: application-layer only. Imports from networks/domain,
|
|
8
|
-
* networks/ports, and shared/ infrastructure — never from runtime/,
|
|
9
|
-
* host implementations, or capability facades (except as injected ports).
|
|
10
|
-
*
|
|
11
|
-
* ## Foreground use cases (participant-directed)
|
|
12
|
-
*
|
|
13
|
-
* - {@link NetworkGraphFactory} — compiles the network lifecycle graph
|
|
14
|
-
* (create, read, update, delete networks).
|
|
15
|
-
* - {@link NetworkMembershipGraphFactory} — compiles the membership graph
|
|
16
|
-
* (add, list, remove members; enforces join-policy and owner authority).
|
|
17
|
-
* - {@link IntentNetworkGraphFactory} — compiles the signal assignment graph
|
|
18
|
-
* (direct or LLM-evaluated intent–network linking; unassign).
|
|
19
|
-
* IntentIndexer is injected from the signals public facade.
|
|
20
|
-
* - {@link createNetworkTools} — foreground tool factory; accepts compiled graphs
|
|
21
|
-
* and host deps through communities.tools.port and produces LangChain-compatible
|
|
22
|
-
* tool arrays for the tool registry.
|
|
23
|
-
*
|
|
24
|
-
* ## Ambient use case (ranking / recommendation)
|
|
25
|
-
*
|
|
26
|
-
* - {@link NetworkRecommender} — LLM-based community ranking used during onboarding
|
|
27
|
-
* (step 6) to surface the most relevant public communities for a user. Lazy-
|
|
28
|
-
* instantiated inside createNetworkTools to avoid requiring OPENROUTER_API_KEY
|
|
29
|
-
* at import time.
|
|
30
|
-
*
|
|
31
|
-
* ## Application-internal (not in public surface)
|
|
32
|
-
*
|
|
33
|
-
* - {@link IntentNetworkGraphState}, {@link AssignmentResult}, etc. — graph execution
|
|
34
|
-
* state types used within indexer.graph.ts; available here for consumers that
|
|
35
|
-
* need the application-layer state shape.
|
|
36
|
-
*
|
|
37
|
-
* IND-546: canonical application home for communities capability previously spread
|
|
38
|
-
* across network/, network/membership/, and network/indexer/.
|
|
39
|
-
*/
|
|
40
|
-
// ── Network lifecycle graph ───────────────────────────────────────────────────
|
|
41
|
-
export { NetworkGraphFactory } from "./network.graph.js";
|
|
42
|
-
// ── Membership graph ──────────────────────────────────────────────────────────
|
|
43
|
-
export { NetworkMembershipGraphFactory } from "./membership.graph.js";
|
|
44
|
-
// ── Signal-assignment (indexer) graph + state ─────────────────────────────────
|
|
45
|
-
export { IntentNetworkGraphFactory } from "./indexer.graph.js";
|
|
46
|
-
export { IntentNetworkGraphState, } from "./indexer.state.js";
|
|
47
|
-
// ── Community ranking agent ───────────────────────────────────────────────────
|
|
48
|
-
export { NetworkRecommender, NetworkRecommenderOutputSchema, } from "./network.recommender.js";
|
|
49
|
-
// ── Tool factory (foreground adapter entry point) ─────────────────────────────
|
|
50
|
-
export { createNetworkTools } from "./network.tools.js";
|