@indexnetwork/protocol 17.0.1-rc.480.1 → 19.0.0-rc.482.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/IMPLEMENTATION.md +46 -3
  3. package/STABILITY.md +5 -1
  4. package/dist/contacts/application/contact.tools.js +1 -3
  5. package/dist/contacts/domain/contact.types.d.ts +0 -2
  6. package/dist/contexts/application/index.d.ts +0 -1
  7. package/dist/contexts/application/index.js +0 -1
  8. package/dist/enrichment/enrichment.graph.js +1 -36
  9. package/dist/index.d.ts +2 -9
  10. package/dist/index.js +4 -6
  11. package/dist/intents/{application → graph}/intent.graph.d.ts +26 -26
  12. package/dist/intents/{application → graph}/intent.graph.execute.d.ts +1 -1
  13. package/dist/intents/{application → graph}/intent.graph.execute.js +1 -1
  14. package/dist/intents/{application → graph}/intent.graph.js +4 -4
  15. package/dist/intents/{application → graph}/intent.graph.reconcile.d.ts +2 -2
  16. package/dist/intents/{application → graph}/intent.graph.shared.d.ts +5 -5
  17. package/dist/intents/{application → graph}/intent.graph.shared.js +1 -1
  18. package/dist/intents/{domain/intent.state.d.ts → graph/intent.graph.state.d.ts} +3 -3
  19. package/dist/intents/{application/intent.clarifier.js → intent.clarifier.js} +5 -5
  20. package/dist/intents/{application/intent.indexer.js → intent.indexer.js} +4 -4
  21. package/dist/intents/{application/intent.inferrer.js → intent.inferrer.js} +4 -4
  22. package/dist/intents/intent.module.d.ts +633 -0
  23. package/dist/intents/intent.module.js +103 -0
  24. package/dist/intents/{domain/intent.proposal.d.ts → intent.proposal.d.ts} +2 -0
  25. package/dist/intents/{domain/intent.proposal.js → intent.proposal.js} +2 -0
  26. package/dist/intents/{application/intent.reconciler.js → intent.reconciler.js} +4 -4
  27. package/dist/intents/{application/intent.tools.d.ts → intent.tools.d.ts} +6 -3
  28. package/dist/intents/{application/intent.tools.js → intent.tools.js} +6 -7
  29. package/dist/intents/{application/intent.verifier.js → intent.verifier.js} +4 -4
  30. package/dist/networks/application/indexer.graph.d.ts +7 -7
  31. package/dist/networks/application/indexer.graph.js +2 -2
  32. package/dist/networks/ports/index.d.ts +4 -2
  33. package/dist/networks/ports/index.js +1 -5
  34. package/dist/opportunities/application/opportunity.graph.prep.js +3 -3
  35. package/dist/opportunities/application/opportunity.tools.cards.d.ts +1 -2
  36. package/dist/opportunities/application/opportunity.tools.cards.js +1 -2
  37. package/dist/opportunities/application/opportunity.tools.list.js +0 -4
  38. package/dist/opportunities/radar/radar.graph.js +0 -4
  39. package/dist/opportunities/radar/radar.state.d.ts +0 -2
  40. package/dist/shared/agent/tool.factory.js +10 -6
  41. package/dist/shared/agent/tool.helpers.d.ts +2 -2
  42. package/dist/shared/agent/tool.registry.js +2 -2
  43. package/dist/shared/agent/utility.tools.js +1 -2
  44. package/dist/shared/interfaces/database.capabilities.d.ts +2 -2
  45. package/dist/shared/interfaces/database.entities.d.ts +0 -3
  46. package/dist/shared/interfaces/database.identity-queries.d.ts +0 -28
  47. package/dist/shared/interfaces/database.member-queries.d.ts +0 -2
  48. package/package.json +1 -1
  49. package/dist/enrichment/enrichment.enricher.d.ts +0 -18
  50. package/dist/enrichment/enrichment.enricher.js +0 -30
  51. package/dist/intents/application/index.d.ts +0 -21
  52. package/dist/intents/application/index.js +0 -26
  53. package/dist/intents/domain/index.d.ts +0 -9
  54. package/dist/intents/domain/index.js +0 -11
  55. package/dist/intents/domain/signal.specificity.d.ts +0 -2
  56. package/dist/intents/domain/signal.specificity.js +0 -2
  57. package/dist/intents/index.d.ts +0 -18
  58. package/dist/intents/index.js +0 -14
  59. package/dist/intents/ports/index.d.ts +0 -15
  60. package/dist/intents/ports/index.js +0 -13
  61. package/dist/intents/ports/signals.tools.port.d.ts +0 -5
  62. package/dist/intents/ports/signals.tools.port.js +0 -1
  63. /package/dist/intents/{application → graph}/intent.graph.infer.d.ts +0 -0
  64. /package/dist/intents/{application → graph}/intent.graph.infer.js +0 -0
  65. /package/dist/intents/{application → graph}/intent.graph.reconcile.js +0 -0
  66. /package/dist/intents/{domain/intent.state.js → graph/intent.graph.state.js} +0 -0
  67. /package/dist/intents/{application → intake}/intake.orchestrator.d.ts +0 -0
  68. /package/dist/intents/{application → intake}/intake.orchestrator.js +0 -0
  69. /package/dist/intents/{application → intake}/intake.pack.generator.d.ts +0 -0
  70. /package/dist/intents/{application → intake}/intake.pack.generator.js +0 -0
  71. /package/dist/intents/{application/intent.clarifier.d.ts → intent.clarifier.d.ts} +0 -0
  72. /package/dist/intents/{application/intent.indexer.d.ts → intent.indexer.d.ts} +0 -0
  73. /package/dist/intents/{application/intent.inferrer.d.ts → intent.inferrer.d.ts} +0 -0
  74. /package/dist/intents/{application/intent.reconciler.d.ts → intent.reconciler.d.ts} +0 -0
  75. /package/dist/intents/{application/intent.verifier.d.ts → intent.verifier.d.ts} +0 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * intents — the capability's single public surface.
3
+ *
4
+ * Everything the rest of the package (and every host) may reach lives on the
5
+ * {@link Intents} class. The directories beside this file are private
6
+ * implementation, grouped by what they do rather than by layer:
7
+ *
8
+ * graph/ the lifecycle graph — prep, infer, verify, reconcile, execute
9
+ * intake/ the guided first-signal interview
10
+ * intent.inferrer an utterance into candidate signals
11
+ * intent.reconciler candidate signals into create/update/expire actions
12
+ * intent.verifier felicity and entropy verdicts
13
+ * intent.clarifier the clarification path when a signal is underspecified
14
+ * intent.indexer scoring one signal against one network
15
+ * intent.proposal the persisted proposal record and description normalization
16
+ * intent.tools the agent-facing tool definitions
17
+ *
18
+ * Only the two multi-file stages keep a directory. Nothing outside `intents/`
19
+ * imports any of it; the layout may change freely as long as this class keeps
20
+ * its shape.
21
+ */
22
+ import { IntentGraphFactory } from "./graph/intent.graph.js";
23
+ import { IntentIndexer } from "./intent.indexer.js";
24
+ import { FALLBACK_WHO_QUESTION, SignalIntakeOrchestrator } from "./intake/intake.orchestrator.js";
25
+ import { SignalIntakePackGenerator } from "./intake/intake.pack.generator.js";
26
+ import { normalizeIntentDescription } from "./intent.proposal.js";
27
+ import { createIntentTools } from "./intent.tools.js";
28
+ import { SemanticVerifier } from "./intent.verifier.js";
29
+ /**
30
+ * The intents capability.
31
+ *
32
+ * One instance is cheap: the model-backed collaborators behind each method are
33
+ * constructed on first use and then reused, so a host can hold a single
34
+ * `Intents` and call only the parts it needs without paying for the rest.
35
+ */
36
+ export class Intents {
37
+ constructor(deps = {}) {
38
+ this.deps = deps;
39
+ }
40
+ // ── Lifecycle graph ─────────────────────────────────────────────────────────
41
+ /**
42
+ * Build the intent lifecycle graph — load, infer, verify, reconcile, execute.
43
+ *
44
+ * @throws If the instance was constructed without a `database`.
45
+ */
46
+ createGraph() {
47
+ const { database, embedder, queue, questionerEnqueue, agents } = this.deps;
48
+ if (!database) {
49
+ throw new Error("Intents.createGraph() requires a `database` dependency.");
50
+ }
51
+ return new IntentGraphFactory(database, embedder, queue, questionerEnqueue, agents).createGraph();
52
+ }
53
+ // ── Verification ────────────────────────────────────────────────────────────
54
+ /**
55
+ * Verify one utterance against the speaker's profile — felicity conditions,
56
+ * speech-act classification, semantic entropy, and specificity.
57
+ *
58
+ * @param content - The raw utterance.
59
+ * @param profileContext - The speaker's profile, serialized as JSON.
60
+ */
61
+ async verifyIntent(content, profileContext) {
62
+ this.verifier ?? (this.verifier = new SemanticVerifier());
63
+ return this.verifier.invoke(content, profileContext);
64
+ }
65
+ // ── Indexing ────────────────────────────────────────────────────────────────
66
+ /**
67
+ * Score how well one signal fits one network, from the network's purpose and
68
+ * the member's sharing preferences.
69
+ *
70
+ * @returns Scores and reasoning, or `null` when the model call fails.
71
+ */
72
+ async indexIntent(intent, indexPrompt, memberPrompt, sourceName, networkContext) {
73
+ this.indexer ?? (this.indexer = new IntentIndexer());
74
+ return this.indexer.invoke(intent, indexPrompt, memberPrompt, sourceName, networkContext);
75
+ }
76
+ // ── Guided intake ───────────────────────────────────────────────────────────
77
+ /** Generate a participant's intake brief and round-1 question. */
78
+ async generateIntakePack(input) {
79
+ this.packGenerator ?? (this.packGenerator = new SignalIntakePackGenerator());
80
+ return this.packGenerator.generate(input);
81
+ }
82
+ /** Plan and write the next intake follow-up questions. */
83
+ async generateIntakeFollowUps(input) {
84
+ this.orchestrator ?? (this.orchestrator = new SignalIntakeOrchestrator());
85
+ return this.orchestrator.generateFollowUps(input);
86
+ }
87
+ /** Turn answered intake rounds into a signal description and card summary. */
88
+ async synthesizeIntake(input) {
89
+ this.orchestrator ?? (this.orchestrator = new SignalIntakeOrchestrator());
90
+ return this.orchestrator.synthesize(input);
91
+ }
92
+ /** Normalize a signal description to its persisted form. */
93
+ static normalizeDescription(description) {
94
+ return normalizeIntentDescription(description);
95
+ }
96
+ /** Register the agent-facing intent tools against a tool definer. */
97
+ static createTools(defineTool, deps) {
98
+ return createIntentTools(defineTool, deps);
99
+ }
100
+ }
101
+ // ── Stateless surface ───────────────────────────────────────────────────────
102
+ /** The static round-1 question used when pack generation is unavailable. */
103
+ Intents.FALLBACK_INTAKE_QUESTION = FALLBACK_WHO_QUESTION;
@@ -1,3 +1,5 @@
1
+ /** Default user-facing warning for broad attributive intent proposals. */
2
+ export declare const DEFAULT_SPECIFICITY_WARNING = "This signal is broad and may produce many weak matches. Add a more concrete role, outcome, location, timeframe, domain, or specific need to get better recommendations.";
1
3
  /** Verifier output retained verbatim across the host persistence boundary. */
2
4
  export interface IntentProposalVerifierOutput {
3
5
  reasoning: string;
@@ -1,3 +1,5 @@
1
+ /** Default user-facing warning for broad attributive intent proposals. */
2
+ export const DEFAULT_SPECIFICITY_WARNING = "This signal is broad and may produce many weak matches. Add a more concrete role, outcome, location, timeframe, domain, or specific need to get better recommendations.";
1
3
  /** Normalize intent text exactly as the direct graph write path does. */
2
4
  export function normalizeIntentDescription(description) {
3
5
  if (!description || typeof description !== "string")
@@ -10,10 +10,10 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  import { HumanMessage, SystemMessage } from "@langchain/core/messages";
11
11
  import { tool } from "@langchain/core/tools";
12
12
  import { z } from "zod";
13
- import { protocolLogger } from "../../shared/observability/protocol.logger.js";
14
- import { Timed } from "../../shared/observability/performance.js";
15
- import { createStructuredModel } from "../../shared/agent/model.config.js";
16
- import { invokeWithAbortSignal } from "../../shared/agent/model-signal.js";
13
+ import { protocolLogger } from "../shared/observability/protocol.logger.js";
14
+ import { Timed } from "../shared/observability/performance.js";
15
+ import { createStructuredModel } from "../shared/agent/model.config.js";
16
+ import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
17
17
  const logger = protocolLogger("IntentReconciler");
18
18
  const invokeLog = protocolLogger("IntentReconciler:invoke");
19
19
  const CreateActionTypeSchema = z.union([z.literal("create"), z.literal("CREATE")]);
@@ -1,7 +1,10 @@
1
1
  import { IntentClarifier } from "./intent.clarifier.js";
2
- import type { ExecutionResult, IntentValidationFailure } from "../domain/intent.state.js";
3
- import type { DefineTool } from "../../shared/agent/tool.helpers.js";
4
- import type { IntentToolDeps } from "../ports/signals.tools.port.js";
2
+ import type { ExecutionResult, IntentValidationFailure } from "./graph/intent.graph.state.js";
3
+ import type { DefineTool, ToolRegistryCompositionDeps } from "../shared/agent/tool.helpers.js";
4
+ /** Host capabilities consumed by signal and intent tools. */
5
+ export type IntentToolDeps = Pick<ToolRegistryCompositionDeps, "userDb" | "systemDb"> & Pick<ToolRegistryCompositionDeps, "intentProposalStore"> & {
6
+ graphs: Pick<ToolRegistryCompositionDeps["graphs"], "intent" | "intentIndex" | "profile">;
7
+ };
5
8
  type IntentClarifierLike = Pick<IntentClarifier, "invoke">;
6
9
  /** Replace the lazy clarifier in deterministic tool tests. */
7
10
  export declare function setIntentClarifierForTesting(clarifier: IntentClarifierLike | null): void;
@@ -1,12 +1,11 @@
1
1
  import { z } from "zod";
2
2
  import { IntentClarifier } from "./intent.clarifier.js";
3
- import { DEFAULT_SPECIFICITY_WARNING } from "../domain/signal.specificity.js";
4
- import { normalizeIntentDescription } from "../domain/intent.proposal.js";
5
- import { protocolLogger } from "../../shared/observability/protocol.logger.js";
6
- import { traceGraph } from "../../shared/observability/trace.js";
7
- import { success, error, UUID_REGEX } from "../../shared/agent/tool.helpers.js";
8
- import { invokeWithAbortSignal } from "../../shared/agent/model-signal.js";
9
- import { deriveAllowedNetworkIds, focusedIntentId, focusedNetworkId, focusedNetworkLabel } from "../../shared/agent/tool.scope.js";
3
+ import { DEFAULT_SPECIFICITY_WARNING, normalizeIntentDescription } from "./intent.proposal.js";
4
+ import { protocolLogger } from "../shared/observability/protocol.logger.js";
5
+ import { traceGraph } from "../shared/observability/trace.js";
6
+ import { success, error, UUID_REGEX } from "../shared/agent/tool.helpers.js";
7
+ import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
8
+ import { deriveAllowedNetworkIds, focusedIntentId, focusedNetworkId, focusedNetworkLabel } from "../shared/agent/tool.scope.js";
10
9
  const logger = protocolLogger("ChatTools:Intent");
11
10
  let intentClarifier;
12
11
  function getIntentClarifier() {
@@ -10,10 +10,10 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  import { HumanMessage, SystemMessage } from "@langchain/core/messages";
11
11
  import { tool } from "@langchain/core/tools";
12
12
  import { z } from "zod";
13
- import { protocolLogger } from "../../shared/observability/protocol.logger.js";
14
- import { Timed } from "../../shared/observability/performance.js";
15
- import { createStructuredModel } from "../../shared/agent/model.config.js";
16
- import { invokeWithAbortSignal } from "../../shared/agent/model-signal.js";
13
+ import { protocolLogger } from "../shared/observability/protocol.logger.js";
14
+ import { Timed } from "../shared/observability/performance.js";
15
+ import { createStructuredModel } from "../shared/agent/model.config.js";
16
+ import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
17
17
  const logger = protocolLogger("SemanticVerifier");
18
18
  const invokeLog = protocolLogger("SemanticVerifier:invoke");
19
19
  const referentialBreadthSchema = z.enum(["narrow", "moderate", "broad"]);
@@ -1,6 +1,6 @@
1
1
  import type { IntentNetworkGraphDatabase } from "../ports/index.js";
2
2
  import type { DebugMetaAgent } from "../../agents/index.js";
3
- import type { IntentIndexer } from "../ports/index.js";
3
+ import type { IntentNetworkIndexer } from "../ports/index.js";
4
4
  import { IntentNetworkGraphState, type AssignmentResult } from "./indexer.state.js";
5
5
  /**
6
6
  * Factory class to build and compile the Intent–Network (indexer) Graph.
@@ -12,9 +12,9 @@ import { IntentNetworkGraphState, type AssignmentResult } from "./indexer.state.
12
12
  *
13
13
  * ## Signal assignment policy
14
14
  *
15
- * The `IntentIndexer` is injected at construction time from the signals public
16
- * facade via the communities ports layer — this factory never imports signals
17
- * application internals directly.
15
+ * The indexer is injected at construction time via the communities ports layer,
16
+ * which narrows the intents module to the one method used here — this factory
17
+ * never reaches intents internals directly.
18
18
  *
19
19
  * Two assignment paths:
20
20
  * 1. Direct (`skipEvaluation: true`):
@@ -23,7 +23,7 @@ import { IntentNetworkGraphState, type AssignmentResult } from "./indexer.state.
23
23
  * - Loads intent + network context (indexPrompt, memberPrompt).
24
24
  * - No-prompt fast path: if both prompts are absent, assigns with
25
25
  * `mode: automatic, promptPresence: 'none'` without calling the LLM.
26
- * - Otherwise invokes IntentIndexer to get indexScore + memberScore + reasoning.
26
+ * - Otherwise invokes the intents indexer to get indexScore + memberScore + reasoning.
27
27
  * - Applies `buildNetworkAssignmentDecision` to produce the threshold/metadata.
28
28
  * - Only writes the link when the decision's `assigned` flag is true.
29
29
  *
@@ -49,12 +49,12 @@ export type IntentNetworkState = typeof IntentNetworkGraphState.State;
49
49
  /** Everything the intent-network nodes reach for. */
50
50
  export interface IntentNetworkGraphDeps {
51
51
  database: IntentNetworkGraphDatabase;
52
- intentNetworker: IntentIndexer;
52
+ intentNetworker: IntentNetworkIndexer;
53
53
  }
54
54
  export declare class IntentNetworkGraphFactory {
55
55
  /** Resolved dependency bag shared by every node. */
56
56
  readonly deps: IntentNetworkGraphDeps;
57
- constructor(database: IntentNetworkGraphDatabase, intentNetworker: IntentIndexer);
57
+ constructor(database: IntentNetworkGraphDatabase, intentNetworker: IntentNetworkIndexer);
58
58
  createGraph(): import("@langchain/langgraph").CompiledStateGraph<{
59
59
  userId: string;
60
60
  networkId: string | undefined;
@@ -141,13 +141,13 @@ export async function assignNode(state, deps) {
141
141
  const sourceName = intentForIndexing.sourceType
142
142
  ? `${intentForIndexing.sourceType}:${intentForIndexing.sourceId ?? ""}`
143
143
  : undefined;
144
- // Run IntentIndexer evaluation (injected from signals public facade via ports)
144
+ // Score the signal against the network (intents module, injected via ports)
145
145
  const _traceEmitter = requestContext.getStore()?.traceEmitter;
146
146
  const _indexerStart = Date.now();
147
147
  _traceEmitter?.({ type: "agent_start", name: "intent-networker" });
148
148
  let result = null;
149
149
  try {
150
- result = await deps.intentNetworker.evaluate(intentForIndexing.payload, indexPrompt, memberPrompt, sourceName, renderedContext);
150
+ result = await deps.intentNetworker.indexIntent(intentForIndexing.payload, indexPrompt, memberPrompt, sourceName, renderedContext);
151
151
  }
152
152
  finally {
153
153
  const _indexerMs = Date.now() - _indexerStart;
@@ -21,5 +21,7 @@
21
21
  * IND-546: explicit port layer for communities; signals consumed via capabilities/signals.facade.ts.
22
22
  */
23
23
  export type { NetworkGraphDatabase, NetworkMembershipGraphDatabase, IntentNetworkGraphDatabase, } from "../../shared/interfaces/database.interface.js";
24
- export { IntentIndexer } from "../../intents/index.js";
25
- export type { IntentIndexerOutput } from "../../intents/index.js";
24
+ import type { Intents } from "../../intents/intent.module.js";
25
+ /** The one intents method communities calls: score a signal against a network. */
26
+ export type IntentNetworkIndexer = Pick<Intents, "indexIntent">;
27
+ export type { IntentIndexerOutput } from "../../intents/intent.module.js";
@@ -20,8 +20,4 @@
20
20
  *
21
21
  * IND-546: explicit port layer for communities; signals consumed via capabilities/signals.facade.ts.
22
22
  */
23
- // ── Signal-assignment port ────────────────────────────────────────────────────
24
- // Re-exported from the signals capability facade so that application-layer code
25
- // (indexer.graph.ts, indexer.state.ts) imports from the communities port, not
26
- // directly from the signals facade or its implementation.
27
- export { IntentIndexer } from "../../intents/index.js";
23
+ export {};
@@ -4,7 +4,7 @@
4
4
  * Each stage is a top-level function taking the graph state and an explicit
5
5
  * dependency bag. `opportunity.graph.ts` wires them into the StateGraph.
6
6
  */
7
- import { IntentIndexer } from '../../intents/index.js';
7
+ import { Intents } from '../../intents/intent.module.js';
8
8
  import { withCallLogging } from '../../shared/observability/protocol.logger.js';
9
9
  import { timed } from '../../shared/observability/performance.js';
10
10
  import { requestContext } from '../../shared/observability/request-context.js';
@@ -200,7 +200,7 @@ export async function scopeNode(state, deps) {
200
200
  else if (state.searchQuery?.trim()) {
201
201
  // Chat path: score query against target indexes in parallel
202
202
  try {
203
- const indexer = new IntentIndexer();
203
+ const indexer = new Intents();
204
204
  const scopeAgentTimings = [];
205
205
  const scorableIndexes = targetNetworks.filter(ti => ti.title !== 'Unknown');
206
206
  const scoringPromises = scorableIndexes.map(async (ti) => {
@@ -213,7 +213,7 @@ export async function scopeNode(state, deps) {
213
213
  traceEmitter?.({ type: "agent_start", name: "intent-networker" });
214
214
  let result = null;
215
215
  try {
216
- result = await indexer.invoke(state.searchQuery, ctx?.indexPrompt ?? null, ctx?.memberPrompt ?? null);
216
+ result = await indexer.indexIntent(state.searchQuery, ctx?.indexPrompt ?? null, ctx?.memberPrompt ?? null);
217
217
  }
218
218
  catch {
219
219
  return { networkId: ti.networkId, score: 1.0 };
@@ -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, isCounterpartGhost?: boolean): {
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, isCounterpartGhost) {
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'. */
@@ -1,12 +1,11 @@
1
1
  import { tool } from "@langchain/core/tools";
2
- import { IntentGraphFactory } from "../../intents/application/intent.graph.js";
2
+ import { Intents } from "../../intents/intent.module.js";
3
3
  import { EnrichmentGraphFactory } from "../../enrichment/enrichment.graph.js";
4
4
  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
8
  import { NetworkGraphFactory, NetworkMembershipGraphFactory, IntentNetworkGraphFactory } from "../../networks/index.js";
9
- import { IntentIndexer } from "../../intents/application/intent.indexer.js";
10
9
  import { NegotiationGraphFactory } from "../../negotiations/index.js";
11
10
  import { PremiseGraphFactory } from "../../premises/premise.graph.js";
12
11
  import { protocolLogger } from "../observability/protocol.logger.js";
@@ -14,7 +13,6 @@ import { resolveChatContext, error, redactSensitiveFields } from "./tool.helpers
14
13
  import { deriveAllowedNetworkIds, focusedIntentId, scopeFromNetworkId } from "./tool.scope.js";
15
14
  import { invokeToolRuntime, toolRuntimeErrorToResult } from "./tool.runtime.js";
16
15
  import { createEnrichmentTools } from "../../enrichment/enrichment.tools.js";
17
- import { createIntentTools } from "../../intents/application/intent.tools.js";
18
16
  import { createNetworkTools } from "../../networks/index.js";
19
17
  import { createOpportunityTools } from "../../opportunities/index.js";
20
18
  import { createUtilityTools } from "./utility.tools.js";
@@ -116,7 +114,13 @@ export async function createChatTools(deps, preResolvedContext) {
116
114
  ...(resolvedContext.sessionId && !input.conversationId ? { conversationId: resolvedContext.sessionId } : {}),
117
115
  })
118
116
  : undefined;
119
- const intentGraph = new IntentGraphFactory(database, embedder, deps.intentQueue, sessionAwareEnqueue).createGraph();
117
+ const intents = new Intents({
118
+ database,
119
+ embedder,
120
+ queue: deps.intentQueue,
121
+ questionerEnqueue: sessionAwareEnqueue,
122
+ });
123
+ const intentGraph = intents.createGraph();
120
124
  const premiseGraph = new PremiseGraphFactory(database, embedder).createGraph();
121
125
  const profileGraph = new EnrichmentGraphFactory(database, scraper, deps.enricher, premiseGraph).createGraph();
122
126
  const hydeCache = deps.hydeCache;
@@ -131,7 +135,7 @@ export async function createChatTools(deps, preResolvedContext) {
131
135
  negotiationGraph, deps.agentDispatcher, deps.queueNegotiateExisting, deps.stampNewbornOpportunities).createGraph();
132
136
  const networkGraph = new NetworkGraphFactory(database).createGraph();
133
137
  const networkMembershipGraph = new NetworkMembershipGraphFactory(database).createGraph();
134
- const intentNetworkGraph = new IntentNetworkGraphFactory(database, new IntentIndexer()).createGraph();
138
+ const intentNetworkGraph = new IntentNetworkGraphFactory(database, intents).createGraph();
135
139
  // ─── Create context-bound databases ────────────────────────────────────────
136
140
  // Use injected instances when provided (e.g. tests). Otherwise create from the same
137
141
  // database used for graphs so that scope checks (e.g. ensureScopedMembership, opportunity
@@ -186,7 +190,7 @@ export async function createChatTools(deps, preResolvedContext) {
186
190
  };
187
191
  // ─── Create domain tools ──────────────────────────────────────────────────
188
192
  const profileTools = createEnrichmentTools(defineTool, toolDeps);
189
- const intentTools = createIntentTools(defineTool, toolDeps);
193
+ const intentTools = Intents.createTools(defineTool, toolDeps);
190
194
  // An intent-scoped conversation exists to refine its selected signal. Keep
191
195
  // creation out of the model-visible registry; the update handler separately
192
196
  // clamps writes to the exact scoped intent as a runtime backstop.
@@ -187,7 +187,7 @@ interface ToolContextBindings {
187
187
  /** Optional durable persistence for reporter cleanup-action proposals. */
188
188
  actionProposalStore?: import('../../chat/reporter.action.contracts.js').AgentActionProposalStore;
189
189
  /** Durable host persistence for verified intent proposals shown in chat. */
190
- intentProposalStore?: import('../../intents/domain/intent.proposal.js').IntentProposalStore;
190
+ intentProposalStore?: import('../../intents/intent.proposal.js').IntentProposalStore;
191
191
  /**
192
192
  * Host bridge for the negotiator persona's `remember`/`forget` memory
193
193
  * tools (P5.4). Injected by the composition root only when negotiator
@@ -344,7 +344,7 @@ interface ToolDepsBindings {
344
344
  /** Context-bound database for LLM/system operations on cross-user resources within shared networks. */
345
345
  systemDb: SystemDatabase;
346
346
  /** Durable host persistence for verified intent proposals shown in chat. */
347
- intentProposalStore?: import('../../intents/domain/intent.proposal.js').IntentProposalStore;
347
+ intentProposalStore?: import('../../intents/intent.proposal.js').IntentProposalStore;
348
348
  scraper: Scraper;
349
349
  embedder: import('../interfaces/embedder.interface.js').Embedder;
350
350
  cache: Cache;
@@ -1,6 +1,6 @@
1
1
  import { error, redactSensitiveFields } from './tool.helpers.js';
2
2
  import { createEnrichmentTools } from '../../enrichment/enrichment.tools.js';
3
- import { createIntentTools } from '../../intents/application/intent.tools.js';
3
+ import { Intents } from '../../intents/intent.module.js';
4
4
  import { createNetworkTools } from '../../networks/index.js';
5
5
  import { createOpportunityTools } from '../../opportunities/index.js';
6
6
  import { createUtilityTools } from './utility.tools.js';
@@ -53,7 +53,7 @@ export function createToolRegistry(deps, options = {}) {
53
53
  // The local defineTool is compatible with DefineTool (which returns any).
54
54
  const dt = defineTool;
55
55
  createEnrichmentTools(dt, deps, { surface: isMcpSurface ? 'mcp' : 'rest' });
56
- createIntentTools(dt, deps);
56
+ Intents.createTools(dt, deps);
57
57
  createNetworkTools(dt, deps);
58
58
  createOpportunityTools(dt, deps);
59
59
  // Utility tools always register read_docs + read_activity_summary; on the
@@ -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. Can be real users or ghost users (placeholder accounts enriched from public data).
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' | 'softDeleteGhost' | 'findDuplicateUser' | 'mergeGhostUser' | 'getUserSocials' | 'setUserSocials' | 'getPremisesForUser' | 'getUserContext'> & {
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' | 'softDeleteGhost' | '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' | 'findDuplicateUser' | 'mergeGhostUser' | 'getPremisesForUser' | 'getPremisesForUserInNetworks' | 'createPremise' | 'getPremise' | 'updatePremise' | 'assignPremiseToNetwork' | 'getPremiseNetworks' | 'searchPremisesBySimilarity' | 'searchPremisesBySimilarityBatch' | 'getUserContext' | 'getUserContexts' | 'searchIntentsByContextEmbedding' | 'searchUserContextsBySimilarity'> & Pick<NegotiationQueries, 'getNegotiationTaskForOpportunity'>;
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;