@indexnetwork/protocol 14.3.2-rc.477.1 → 16.1.1-rc.478.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/IMPLEMENTATION.md +50 -52
  3. package/STABILITY.md +3 -3
  4. package/dist/chat/chat.agent.js +2 -2
  5. package/dist/chat/chat.graph.d.ts +41 -1
  6. package/dist/chat/chat.graph.js +108 -127
  7. package/dist/discovery/hyde.frame.d.ts +4 -4
  8. package/dist/discovery/hyde.graph.d.ts +71 -12
  9. package/dist/discovery/hyde.graph.js +337 -335
  10. package/dist/discovery/lens.inferrer.d.ts +6 -6
  11. package/dist/enrichment/enrichment.graph.d.ts +127 -9
  12. package/dist/enrichment/enrichment.graph.js +633 -655
  13. package/dist/enrichment/enrichment.state.d.ts +1 -1
  14. package/dist/enrichment/enrichment.tools.context-read.d.ts +10 -0
  15. package/dist/enrichment/enrichment.tools.context-read.js +353 -0
  16. package/dist/enrichment/enrichment.tools.context-write.d.ts +9 -0
  17. package/dist/enrichment/enrichment.tools.context-write.js +417 -0
  18. package/dist/enrichment/enrichment.tools.d.ts +11 -1
  19. package/dist/enrichment/enrichment.tools.helpers.d.ts +139 -0
  20. package/dist/enrichment/enrichment.tools.helpers.js +234 -0
  21. package/dist/enrichment/enrichment.tools.js +16 -975
  22. package/dist/index.d.ts +27 -48
  23. package/dist/index.js +17 -27
  24. package/dist/intents/application/intent.graph.d.ts +71 -67
  25. package/dist/intents/application/intent.graph.execute.d.ts +59 -0
  26. package/dist/intents/application/intent.graph.execute.js +301 -0
  27. package/dist/intents/application/intent.graph.infer.d.ts +44 -0
  28. package/dist/intents/application/intent.graph.infer.js +97 -0
  29. package/dist/intents/application/intent.graph.js +96 -888
  30. package/dist/intents/application/intent.graph.reconcile.d.ts +71 -0
  31. package/dist/intents/application/intent.graph.reconcile.js +274 -0
  32. package/dist/intents/application/intent.graph.shared.d.ts +70 -0
  33. package/dist/intents/application/intent.graph.shared.js +153 -0
  34. package/dist/intents/domain/intent.state.d.ts +1 -1
  35. package/dist/maintenance/maintenance.graph.d.ts +66 -3
  36. package/dist/maintenance/maintenance.graph.js +155 -156
  37. package/dist/mcp/mcp.authorization-policy.d.ts +24 -28
  38. package/dist/mcp/mcp.authorization-policy.js +14 -34
  39. package/dist/mcp/mcp.server.d.ts +3 -3
  40. package/dist/mcp/mcp.server.js +12 -16
  41. package/dist/negotiations/application/negotiation.candidates.d.ts +83 -0
  42. package/dist/negotiations/application/negotiation.candidates.js +162 -0
  43. package/dist/negotiations/application/negotiation.graph.d.ts +91 -157
  44. package/dist/negotiations/application/negotiation.graph.finalize.d.ts +5 -0
  45. package/dist/negotiations/application/negotiation.graph.finalize.js +280 -0
  46. package/dist/negotiations/application/negotiation.graph.init.d.ts +54 -0
  47. package/dist/negotiations/application/negotiation.graph.init.js +227 -0
  48. package/dist/negotiations/application/negotiation.graph.js +69 -1388
  49. package/dist/negotiations/application/negotiation.graph.screen.d.ts +23 -0
  50. package/dist/negotiations/application/negotiation.graph.screen.js +108 -0
  51. package/dist/negotiations/application/negotiation.graph.shared.d.ts +75 -0
  52. package/dist/negotiations/application/negotiation.graph.shared.js +125 -0
  53. package/dist/negotiations/application/negotiation.graph.turn.d.ts +105 -0
  54. package/dist/negotiations/application/negotiation.graph.turn.js +484 -0
  55. package/dist/negotiations/domain/negotiation.state.d.ts +1 -1
  56. package/dist/negotiations/domain/negotiation.state.js +0 -1
  57. package/dist/networks/application/indexer.graph.d.ts +165 -7
  58. package/dist/networks/application/indexer.graph.js +339 -388
  59. package/dist/networks/application/indexer.state.d.ts +1 -1
  60. package/dist/networks/application/membership.graph.d.ts +83 -5
  61. package/dist/networks/application/membership.graph.js +177 -207
  62. package/dist/networks/application/network.graph.d.ts +148 -5
  63. package/dist/networks/application/network.graph.js +249 -278
  64. package/dist/networks/domain/membership.state.d.ts +1 -1
  65. package/dist/networks/domain/network.state.d.ts +1 -1
  66. package/dist/opportunities/application/delivery-card.cache.d.ts +1 -1
  67. package/dist/opportunities/application/delivery-card.cache.js +2 -2
  68. package/dist/opportunities/application/index.d.ts +2 -2
  69. package/dist/opportunities/application/index.js +2 -2
  70. package/dist/opportunities/application/opportunity.evaluator.js +1 -1
  71. package/dist/opportunities/application/opportunity.graph.d.ts +686 -473
  72. package/dist/opportunities/application/opportunity.graph.discovery-strategies.d.ts +109 -0
  73. package/dist/opportunities/application/opportunity.graph.discovery-strategies.js +451 -0
  74. package/dist/opportunities/application/opportunity.graph.discovery.d.ts +69 -0
  75. package/dist/opportunities/application/opportunity.graph.discovery.js +397 -0
  76. package/dist/opportunities/application/opportunity.graph.evaluation.d.ts +82 -0
  77. package/dist/opportunities/application/opportunity.graph.evaluation.js +608 -0
  78. package/dist/opportunities/application/opportunity.graph.js +120 -3690
  79. package/dist/opportunities/application/opportunity.graph.modes.d.ts +538 -0
  80. package/dist/opportunities/application/opportunity.graph.modes.js +538 -0
  81. package/dist/opportunities/application/opportunity.graph.negotiate.d.ts +109 -0
  82. package/dist/opportunities/application/opportunity.graph.negotiate.js +392 -0
  83. package/dist/opportunities/application/opportunity.graph.persist-node.d.ts +93 -0
  84. package/dist/opportunities/application/opportunity.graph.persist-node.js +765 -0
  85. package/dist/opportunities/application/opportunity.graph.prep.d.ts +150 -0
  86. package/dist/opportunities/application/opportunity.graph.prep.js +381 -0
  87. package/dist/opportunities/application/opportunity.graph.shared.d.ts +173 -0
  88. package/dist/opportunities/application/opportunity.graph.shared.js +199 -0
  89. package/dist/opportunities/application/opportunity.presentation.d.ts +399 -0
  90. package/dist/opportunities/application/{opportunity.presenter.js → opportunity.presentation.js} +735 -9
  91. package/dist/opportunities/application/opportunity.tools.cards.d.ts +152 -0
  92. package/dist/opportunities/application/opportunity.tools.cards.js +235 -0
  93. package/dist/opportunities/application/opportunity.tools.d.ts +8 -114
  94. package/dist/opportunities/application/opportunity.tools.js +28 -711
  95. package/dist/opportunities/application/opportunity.tools.list.d.ts +10 -0
  96. package/dist/opportunities/application/opportunity.tools.list.js +493 -0
  97. package/dist/opportunities/domain/index.d.ts +3 -3
  98. package/dist/opportunities/domain/index.js +3 -3
  99. package/dist/opportunities/domain/opportunity.state.d.ts +19 -19
  100. package/dist/opportunities/index.d.ts +6 -6
  101. package/dist/opportunities/index.js +4 -4
  102. package/dist/opportunities/ports/opportunity.tools.port.d.ts +1 -1
  103. package/dist/opportunities/radar/radar.graph.d.ts +57 -13
  104. package/dist/opportunities/radar/radar.graph.js +470 -471
  105. package/dist/premises/premise.graph.d.ts +88 -20
  106. package/dist/premises/premise.graph.js +207 -218
  107. package/dist/questions/domain/question.schema.d.ts +40 -40
  108. package/dist/shared/agent/tool.helpers.d.ts +21 -0
  109. package/dist/shared/agent/tool.registry.d.ts +4 -3
  110. package/dist/shared/agent/tool.registry.js +1 -1
  111. package/dist/shared/interfaces/database.capabilities.d.ts +100 -0
  112. package/dist/shared/interfaces/database.capabilities.js +7 -0
  113. package/dist/shared/interfaces/database.entities.d.ts +533 -0
  114. package/dist/shared/interfaces/database.entities.js +10 -0
  115. package/dist/shared/interfaces/database.identity-queries.d.ts +294 -0
  116. package/dist/shared/interfaces/database.identity-queries.js +4 -0
  117. package/dist/shared/interfaces/database.interface.d.ts +15 -2277
  118. package/dist/shared/interfaces/database.interface.js +8 -0
  119. package/dist/shared/interfaces/database.member-queries.d.ts +228 -0
  120. package/dist/shared/interfaces/database.member-queries.js +4 -0
  121. package/dist/shared/interfaces/database.negotiation.d.ts +305 -0
  122. package/dist/shared/interfaces/database.negotiation.js +26 -0
  123. package/dist/shared/interfaces/database.network-queries.d.ts +277 -0
  124. package/dist/shared/interfaces/database.network-queries.js +4 -0
  125. package/dist/shared/interfaces/database.opportunity-queries.d.ts +285 -0
  126. package/dist/shared/interfaces/database.opportunity-queries.js +4 -0
  127. package/dist/shared/interfaces/database.port.d.ts +322 -0
  128. package/dist/shared/interfaces/database.port.js +29 -0
  129. package/dist/shared/schemas/discovery-question.schema.d.ts +14 -14
  130. package/dist/shared/schemas/negotiation-digest.schema.d.ts +2 -2
  131. package/package.json +2 -15
  132. package/dist/opportunities/application/opportunity.card-presentation.d.ts +0 -44
  133. package/dist/opportunities/application/opportunity.card-presentation.js +0 -93
  134. package/dist/opportunities/application/opportunity.presenter.d.ts +0 -156
  135. package/dist/opportunities/domain/opportunity.presentation-cache.d.ts +0 -5
  136. package/dist/opportunities/domain/opportunity.presentation-cache.js +0 -11
  137. package/dist/opportunities/domain/opportunity.presentation.d.ts +0 -76
  138. package/dist/opportunities/domain/opportunity.presentation.js +0 -516
  139. package/dist/opportunities/domain/opportunity.safe-presentation.d.ts +0 -104
  140. package/dist/opportunities/domain/opportunity.safe-presentation.js +0 -102
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Intent graph, stages 2-3: verify inferred signals, then reconcile against what exists.
3
+ */
4
+ import { VerifiedIntent, type IntentValidationFailure } from "../domain/intent.state.js";
5
+ import type { NormalizedIntentAction } from "./intent.reconciler.js";
6
+ import type { DebugMetaAgent } from "../../agents/index.js";
7
+ import { type IntentGraphDeps, type IntentState } from "./intent.graph.shared.js";
8
+ /**
9
+ * Node 2: Verification (Map-Reduce / Parallel)
10
+ * Verifies each inferred intent in parallel.
11
+ * Phase 4: Can be skipped for delete operations and updates with no new intents.
12
+ */
13
+ export declare function verificationNode(state: IntentState, deps: IntentGraphDeps): Promise<{
14
+ verifiedIntents: never[];
15
+ agentTimings: never[];
16
+ validationFailures?: undefined;
17
+ trace?: undefined;
18
+ } | {
19
+ verifiedIntents: VerifiedIntent[];
20
+ validationFailures: IntentValidationFailure[];
21
+ agentTimings: DebugMetaAgent[];
22
+ trace: {
23
+ node: string;
24
+ detail: string;
25
+ data: {
26
+ clarity: number;
27
+ authority: number;
28
+ sincerity: number;
29
+ entropy: number | undefined;
30
+ referentialBreadth: "narrow" | "moderate" | "broad" | undefined;
31
+ missingSelectionalConstraints: ("location" | "role" | "outcome" | "timeframe" | "domain" | "concrete_need")[] | undefined;
32
+ specificityWarning: string | undefined;
33
+ classification: "COMMISSIVE" | "DIRECTIVE" | "ASSERTIVE" | "EXPRESSIVE" | "DECLARATION" | "UNKNOWN" | undefined;
34
+ score: number | undefined;
35
+ } | undefined;
36
+ }[];
37
+ }>;
38
+ /**
39
+ * Node 3: Reconciliation
40
+ * Decides on final actions (Create, Update, Expire).
41
+ * Phase 4: Handles delete operations directly without LLM reconciliation.
42
+ */
43
+ export declare function reconciliationNode(state: IntentState, deps: IntentGraphDeps): Promise<{
44
+ actions: {
45
+ type: "expire";
46
+ id: string;
47
+ reasoning: string;
48
+ }[];
49
+ agentTimings: DebugMetaAgent[];
50
+ trace: {
51
+ node: string;
52
+ detail: string;
53
+ }[];
54
+ validationFailures?: undefined;
55
+ } | {
56
+ actions: NormalizedIntentAction[];
57
+ validationFailures: IntentValidationFailure[];
58
+ agentTimings: DebugMetaAgent[];
59
+ trace: {
60
+ node: string;
61
+ detail: string;
62
+ }[];
63
+ } | {
64
+ actions: NormalizedIntentAction[];
65
+ agentTimings: DebugMetaAgent[];
66
+ trace: {
67
+ node: string;
68
+ detail: string;
69
+ }[];
70
+ validationFailures?: undefined;
71
+ }>;
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Intent graph, stages 2-3: verify inferred signals, then reconcile against what exists.
3
+ */
4
+ import { timed } from "../../shared/observability/performance.js";
5
+ import { requestContext } from "../../shared/observability/request-context.js";
6
+ import { buildExplicitUpdateActions, enforceIntentActionBoundary, enrichVagueIntentWithContext, getSpecificityWarning, isVague, logger } from "./intent.graph.shared.js";
7
+ /**
8
+ * Node 2: Verification (Map-Reduce / Parallel)
9
+ * Verifies each inferred intent in parallel.
10
+ * Phase 4: Can be skipped for delete operations and updates with no new intents.
11
+ */
12
+ export async function verificationNode(state, deps) {
13
+ return timed("IntentGraph.verification", async () => {
14
+ const intents = state.inferredIntents;
15
+ logger.verbose("Starting verification", {
16
+ operationMode: state.operationMode,
17
+ intentCount: intents.length
18
+ });
19
+ if (intents.length === 0) {
20
+ logger.verbose("No intents to verify");
21
+ return { verifiedIntents: [], agentTimings: [] };
22
+ }
23
+ logger.verbose('Verifying intents in parallel', { count: intents.length });
24
+ const agentTimingsAccum = [];
25
+ // Parallel Execution
26
+ const verificationResults = await Promise.all(intents.map(async (intent) => {
27
+ try {
28
+ let description = intent.description;
29
+ const _traceEmitterVerifier = requestContext.getStore()?.traceEmitter;
30
+ const verifierStart1 = Date.now();
31
+ _traceEmitterVerifier?.({ type: "agent_start", name: "intent-verifier" });
32
+ let verdict = await deps.verifier.invoke(description, state.userProfile);
33
+ agentTimingsAccum.push({ name: 'intent.verifier', durationMs: Date.now() - verifierStart1 });
34
+ _traceEmitterVerifier?.({ type: "agent_end", name: "intent-verifier", durationMs: Date.now() - verifierStart1, summary: `Verified: ${verdict.classification}` });
35
+ if (isVague(description, verdict.semantic_entropy, verdict.felicity_scores.clarity)) {
36
+ // Role-hint enrichment for vague job intents reads the global
37
+ // user_context paragraph instead of the structured profile fields.
38
+ const roleHintContext = (await deps.database.getUserContext(state.userId, null))?.text ?? '';
39
+ const enrichedDescription = enrichVagueIntentWithContext(description, roleHintContext);
40
+ if (enrichedDescription !== description) {
41
+ logger.verbose("Enriched vague intent using profile context", {
42
+ before: description,
43
+ after: enrichedDescription,
44
+ });
45
+ const _traceEmitterVerifier2 = requestContext.getStore()?.traceEmitter;
46
+ const verifierStart2 = Date.now();
47
+ _traceEmitterVerifier2?.({ type: "agent_start", name: "intent-verifier" });
48
+ const enrichedVerdict = await deps.verifier.invoke(enrichedDescription, state.userProfile);
49
+ agentTimingsAccum.push({ name: 'intent.verifier', durationMs: Date.now() - verifierStart2 });
50
+ _traceEmitterVerifier2?.({ type: "agent_end", name: "intent-verifier", durationMs: Date.now() - verifierStart2, summary: `Verified (enriched): ${enrichedVerdict.classification}` });
51
+ const becameClear = enrichedVerdict.semantic_entropy < verdict.semantic_entropy ||
52
+ enrichedVerdict.felicity_scores.clarity > verdict.felicity_scores.clarity;
53
+ if (becameClear) {
54
+ description = enrichedDescription;
55
+ verdict = enrichedVerdict;
56
+ }
57
+ }
58
+ }
59
+ // Filter Logic: Must be a Commissive, Directive, or Declaration
60
+ const VALID_TYPES = ['COMMISSIVE', 'DIRECTIVE', 'DECLARATION'];
61
+ if (!VALID_TYPES.includes(verdict.classification)) {
62
+ logger.warn('Dropping intent', { description, classification: verdict.classification });
63
+ return {
64
+ failure: {
65
+ category: 'non_actionable',
66
+ classification: verdict.classification,
67
+ referentialBreadth: verdict.referential_breadth,
68
+ message: `Description was classified as ${verdict.classification}, not an actionable goal.`,
69
+ },
70
+ };
71
+ }
72
+ if (isVague(description, verdict.semantic_entropy, verdict.felicity_scores.clarity)) {
73
+ logger.warn('Dropping vague intent after verification', {
74
+ description,
75
+ entropy: verdict.semantic_entropy,
76
+ clarity: verdict.felicity_scores.clarity,
77
+ });
78
+ return {
79
+ failure: {
80
+ category: 'vague_or_invalid',
81
+ classification: verdict.classification,
82
+ referentialBreadth: verdict.referential_breadth,
83
+ message: 'Description failed clarity or semantic-entropy requirements.',
84
+ },
85
+ };
86
+ }
87
+ if (state.operationMode === 'create' && verdict.referential_breadth === 'broad') {
88
+ logger.warn('Dropping broad attributive intent before persistence', {
89
+ description,
90
+ referentialBreadth: verdict.referential_breadth,
91
+ missingSelectionalConstraints: verdict.missing_selectional_constraints,
92
+ warning: getSpecificityWarning(verdict),
93
+ });
94
+ return {
95
+ failure: {
96
+ category: 'vague_or_invalid',
97
+ classification: verdict.classification,
98
+ referentialBreadth: verdict.referential_breadth,
99
+ message: getSpecificityWarning(verdict),
100
+ },
101
+ };
102
+ }
103
+ // Calculate Score
104
+ const score = Math.min(verdict.felicity_scores.authority, verdict.felicity_scores.sincerity, verdict.felicity_scores.clarity);
105
+ // Return enriched intent
106
+ return {
107
+ intent: {
108
+ ...intent,
109
+ description,
110
+ verification: verdict,
111
+ score,
112
+ },
113
+ };
114
+ }
115
+ catch (e) {
116
+ logger.error('Error verifying intent', { description: intent.description, error: e });
117
+ return {
118
+ failure: {
119
+ category: 'verification_failure',
120
+ message: e instanceof Error ? e.message : 'Intent verification failed unexpectedly.',
121
+ },
122
+ };
123
+ }
124
+ }));
125
+ const verified = verificationResults.flatMap((result) => 'intent' in result ? [result.intent] : []);
126
+ const validationFailures = verificationResults.flatMap((result) => 'failure' in result ? [result.failure] : []);
127
+ logger.verbose(`Verification complete`, {
128
+ passed: verified.length,
129
+ total: intents.length,
130
+ operationMode: state.operationMode
131
+ });
132
+ // Build trace entries with Felicity scores for each verified intent
133
+ const traceEntries = verified.map(v => {
134
+ const fs = v.verification?.felicity_scores;
135
+ const entropy = v.verification?.semantic_entropy;
136
+ const classification = v.verification?.classification;
137
+ const referentialBreadth = v.verification?.referential_breadth;
138
+ return {
139
+ node: "verification",
140
+ detail: `"${v.description.slice(0, 40)}${v.description.length > 40 ? '...' : ''}" → ${classification}${referentialBreadth ? ` (${referentialBreadth} referential breadth)` : ''}`,
141
+ data: fs ? {
142
+ clarity: fs.clarity,
143
+ authority: fs.authority,
144
+ sincerity: fs.sincerity,
145
+ entropy: entropy != null ? Math.round(entropy * 100) / 100 : undefined,
146
+ referentialBreadth,
147
+ missingSelectionalConstraints: v.verification?.missing_selectional_constraints,
148
+ specificityWarning: v.verification?.specificity_warning ?? undefined,
149
+ classification,
150
+ score: v.score,
151
+ } : undefined,
152
+ };
153
+ });
154
+ // Add summary trace if some intents were filtered out
155
+ const dropped = intents.length - verified.length;
156
+ if (dropped > 0) {
157
+ traceEntries.unshift({
158
+ node: "verification",
159
+ detail: `Verified ${verified.length}/${intents.length} (${dropped} filtered as invalid)`,
160
+ data: undefined,
161
+ });
162
+ }
163
+ else if (verified.length > 0) {
164
+ traceEntries.unshift({
165
+ node: "verification",
166
+ detail: `Verified ${verified.length} intent(s)`,
167
+ data: undefined,
168
+ });
169
+ }
170
+ return { verifiedIntents: verified, validationFailures, agentTimings: agentTimingsAccum, trace: traceEntries };
171
+ });
172
+ }
173
+ /**
174
+ * Node 3: Reconciliation
175
+ * Decides on final actions (Create, Update, Expire).
176
+ * Phase 4: Handles delete operations directly without LLM reconciliation.
177
+ */
178
+ export async function reconciliationNode(state, deps) {
179
+ return timed("IntentGraph.reconciliation", async () => {
180
+ logger.verbose("Starting reconciliation", {
181
+ operationMode: state.operationMode,
182
+ verifiedIntentCount: state.verifiedIntents.length,
183
+ targetIntentIds: state.targetIntentIds
184
+ });
185
+ const agentTimingsAccum = [];
186
+ // Phase 4: Handle delete operations directly
187
+ if (state.operationMode === 'delete') {
188
+ if (!state.targetIntentIds || state.targetIntentIds.length === 0) {
189
+ logger.warn("Delete mode with no target IDs");
190
+ return {
191
+ actions: [],
192
+ agentTimings: agentTimingsAccum,
193
+ trace: [{ node: "reconciler", detail: "Delete mode with no target IDs" }],
194
+ };
195
+ }
196
+ logger.verbose("Delete mode - generating expire actions", {
197
+ targetIds: state.targetIntentIds
198
+ });
199
+ const actions = state.targetIntentIds.map(id => ({
200
+ type: 'expire',
201
+ id,
202
+ reasoning: 'User requested deletion'
203
+ }));
204
+ return {
205
+ actions,
206
+ agentTimings: agentTimingsAccum,
207
+ trace: [{
208
+ node: "reconciler",
209
+ detail: `Actions: expire=${actions.length}`,
210
+ }],
211
+ };
212
+ }
213
+ // Standard reconciliation for create/update operations
214
+ const candidates = state.verifiedIntents;
215
+ if (candidates.length === 0) {
216
+ logger.verbose("No verified intents to reconcile");
217
+ return {
218
+ actions: [],
219
+ agentTimings: agentTimingsAccum,
220
+ trace: [{ node: "reconciler", detail: "No intents to reconcile" }],
221
+ };
222
+ }
223
+ if (state.operationMode === 'update') {
224
+ const explicitUpdate = buildExplicitUpdateActions(state.targetIntentIds, state.activeIntentIds, candidates);
225
+ return {
226
+ actions: explicitUpdate.actions,
227
+ validationFailures: explicitUpdate.failure
228
+ ? [...state.validationFailures, explicitUpdate.failure]
229
+ : state.validationFailures,
230
+ agentTimings: agentTimingsAccum,
231
+ trace: [{
232
+ node: 'reconciler',
233
+ detail: explicitUpdate.failure
234
+ ? `Explicit update rejected: ${explicitUpdate.failure.category}`
235
+ : `Explicit update bound to target ${state.targetIntentIds?.[0]}`,
236
+ }],
237
+ };
238
+ }
239
+ // Format candidates for the Reconciler Prompt
240
+ const formattedCandidates = candidates.map(c => `- [${c.type.toUpperCase()}] "${c.description}" (Confidence: ${c.confidence}, Score: ${c.score})\n` +
241
+ ` Reasoning: ${c.reasoning}\n` +
242
+ ` Verification: ${c.verification?.classification} (Flags: ${c.verification?.flags.join(', ') || 'None'})`).join('\n');
243
+ logger.verbose("Invoking reconciler agent", {
244
+ candidateCount: candidates.length,
245
+ operationMode: state.operationMode
246
+ });
247
+ const _traceEmitterReconciler = requestContext.getStore()?.traceEmitter;
248
+ const reconcilerStart = Date.now();
249
+ _traceEmitterReconciler?.({ type: "agent_start", name: "intent-reconciler" });
250
+ const result = await deps.reconciler.invoke(formattedCandidates, state.activeIntents);
251
+ agentTimingsAccum.push({ name: 'intent.reconciler', durationMs: Date.now() - reconcilerStart });
252
+ _traceEmitterReconciler?.({ type: "agent_end", name: "intent-reconciler", durationMs: Date.now() - reconcilerStart, summary: `Reconciled ${result.actions.length} action(s)` });
253
+ const actions = enforceIntentActionBoundary(state.operationMode, state.targetIntentIds, result.actions);
254
+ logger.verbose("Reconciliation complete", {
255
+ actionCount: actions.length,
256
+ droppedActionCount: result.actions.length - actions.length,
257
+ operationMode: state.operationMode
258
+ });
259
+ // Count actions by type after enforcing the operation boundary.
260
+ const counts = { create: 0, update: 0, expire: 0 };
261
+ for (const a of actions) {
262
+ if (a.type in counts)
263
+ counts[a.type]++;
264
+ }
265
+ return {
266
+ actions,
267
+ agentTimings: agentTimingsAccum,
268
+ trace: [{
269
+ node: "reconciler",
270
+ detail: `Actions: create=${counts.create}, update=${counts.update}, expire=${counts.expire}`,
271
+ }],
272
+ };
273
+ });
274
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Shared vocabulary for the intent graph's nodes.
3
+ *
4
+ * The nodes were closures inside `IntentGraphFactory.createGraph()`; they are
5
+ * top-level functions now, each taking an explicit {@link IntentGraphDeps}.
6
+ * This module owns the bag, the logger, and the pure helpers.
7
+ */
8
+ import { IntentGraphState, VerifiedIntent, type IntentValidationFailure } from "../domain/intent.state.js";
9
+ import { ExplicitIntentInferrer } from "./intent.inferrer.js";
10
+ import { SemanticVerifier } from "./intent.verifier.js";
11
+ import { IntentReconciler } from "./intent.reconciler.js";
12
+ import type { NormalizedIntentAction } from "./intent.reconciler.js";
13
+ import { IntentGraphDatabase } from "../../shared/interfaces/database.interface.js";
14
+ import type { EmbeddingGenerator } from "../../shared/interfaces/embedder.interface.js";
15
+ import type { IntentGraphQueue } from "../../shared/interfaces/queue.interface.js";
16
+ import type { QuestionerEnqueueFn } from "../../questions/index.js";
17
+ /** The graph's channel state, as every node sees it. */
18
+ export type IntentState = typeof IntentGraphState.State;
19
+ /** Everything the intent nodes reach for. Composed once by the factory. */
20
+ export interface IntentGraphDeps {
21
+ database: IntentGraphDatabase;
22
+ embedder?: EmbeddingGenerator;
23
+ intentQueue?: IntentGraphQueue;
24
+ questionerEnqueue?: QuestionerEnqueueFn;
25
+ inferrer: Pick<ExplicitIntentInferrer, 'invoke'>;
26
+ verifier: Pick<SemanticVerifier, 'invoke'>;
27
+ reconciler: Pick<IntentReconciler, 'invoke'>;
28
+ }
29
+ export declare const logger: import("../../shared/observability/log.js").LoggerWithSource;
30
+ /**
31
+ * Enforce write-mode constraints on reconciler output before any action can
32
+ * reach persistence. Update mode is deliberately fail-closed: only updates
33
+ * whose id is one of the caller-provided targets survive.
34
+ */
35
+ export declare function enforceIntentActionBoundary(operationMode: 'create' | 'update' | 'delete' | 'read' | 'propose', targetIntentIds: string[] | undefined, actions: NormalizedIntentAction[]): NormalizedIntentAction[];
36
+ /**
37
+ * Build the only action permitted for an explicit update. This path is
38
+ * intentionally deterministic: semantic reconciliation may shape create
39
+ * operations, but it may not redirect an update away from its supplied target.
40
+ */
41
+ export declare function buildExplicitUpdateActions(targetIntentIds: string[] | undefined, activeIntentIds: string[], candidates: VerifiedIntent[]): {
42
+ actions: NormalizedIntentAction[];
43
+ failure?: IntentValidationFailure;
44
+ };
45
+ export declare const MAX_PERMISSIBLE_ENTROPY = 0.75;
46
+ export declare const MIN_CLEAR_INTENT_SCORE = 40;
47
+ export declare const GENERIC_JOB_PHRASE: RegExp;
48
+ export declare const inferRoleFromContextText: (text: string) => string | null;
49
+ /**
50
+ * Derive a job-role qualifier from the user's global user_context paragraph
51
+ * (the identity text that replaced the legacy profile projection). Role is
52
+ * inferred from the free text; the old structured skills/interests extraction
53
+ * is gone with the removed profile fields.
54
+ */
55
+ export declare const buildJobQualifierFromContext: (contextText: string) => string | null;
56
+ export declare const enrichVagueIntentWithContext: (description: string, userContext: string) => string;
57
+ export declare const isVague: (description: string, entropy: number, clarity: number) => boolean;
58
+ export declare const getSpecificityWarning: (verdict: {
59
+ specificity_warning?: string | null;
60
+ }) => string;
61
+ export declare const toSpeechActType: (classification?: string) => "COMMISSIVE" | "DIRECTIVE" | null;
62
+ /**
63
+ * Factory class to build and compile the Intent Processing Graph.
64
+
65
+ /**
66
+ * Generate a flat embedding for an intent payload, swallowing failures so
67
+ * persistence can continue without an embedding. `intentId` is logging-only
68
+ * (present for updates, absent for creates).
69
+ */
70
+ export declare function generateIntentEmbedding(deps: IntentGraphDeps, sanitizedPayload: string, intentId?: string): Promise<number[] | undefined>;
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Shared vocabulary for the intent graph's nodes.
3
+ *
4
+ * The nodes were closures inside `IntentGraphFactory.createGraph()`; they are
5
+ * top-level functions now, each taking an explicit {@link IntentGraphDeps}.
6
+ * This module owns the bag, the logger, and the pure helpers.
7
+ */
8
+ import { DEFAULT_SPECIFICITY_WARNING } from "../domain/signal.specificity.js";
9
+ import { getAbortSignalConfig } from "../../shared/agent/model-signal.js";
10
+ import { protocolLogger } from "../../shared/observability/protocol.logger.js";
11
+ export const logger = protocolLogger("IntentGraphFactory");
12
+ /**
13
+ * Enforce write-mode constraints on reconciler output before any action can
14
+ * reach persistence. Update mode is deliberately fail-closed: only updates
15
+ * whose id is one of the caller-provided targets survive.
16
+ */
17
+ export function enforceIntentActionBoundary(operationMode, targetIntentIds, actions) {
18
+ if (operationMode !== 'update')
19
+ return actions;
20
+ const targets = new Set(targetIntentIds ?? []);
21
+ return actions.filter((action) => action.type === 'update' && targets.has(action.id));
22
+ }
23
+ /**
24
+ * Build the only action permitted for an explicit update. This path is
25
+ * intentionally deterministic: semantic reconciliation may shape create
26
+ * operations, but it may not redirect an update away from its supplied target.
27
+ */
28
+ export function buildExplicitUpdateActions(targetIntentIds, activeIntentIds, candidates) {
29
+ if (targetIntentIds?.length !== 1 || !activeIntentIds.includes(targetIntentIds[0])) {
30
+ return {
31
+ actions: [],
32
+ failure: {
33
+ category: 'update_target_boundary',
34
+ message: 'Explicit update requires exactly one active intent owned by the caller.',
35
+ },
36
+ };
37
+ }
38
+ if (candidates.length !== 1) {
39
+ return {
40
+ actions: [],
41
+ failure: {
42
+ category: 'reconciliation_boundary',
43
+ message: 'Explicit update must resolve to exactly one verified intent.',
44
+ },
45
+ };
46
+ }
47
+ const candidate = candidates[0];
48
+ return {
49
+ actions: [{
50
+ type: 'update',
51
+ id: targetIntentIds[0],
52
+ payload: candidate.description,
53
+ score: candidate.score ?? null,
54
+ reasoning: candidate.reasoning ?? 'Explicit user-confirmed update',
55
+ intentMode: candidate.verification?.referential_anchor ? 'REFERENTIAL' : 'ATTRIBUTIVE',
56
+ }],
57
+ };
58
+ }
59
+ export const MAX_PERMISSIBLE_ENTROPY = 0.75;
60
+ export const MIN_CLEAR_INTENT_SCORE = 40;
61
+ export const GENERIC_JOB_PHRASE = /\b(?:a|any|some)\s+job\b/i;
62
+ export const inferRoleFromContextText = (text) => {
63
+ const normalized = text.toLowerCase();
64
+ if (/\b(engineer|developer)\b/.test(normalized))
65
+ return "software engineering";
66
+ if (/\b(designer|ux|ui)\b/.test(normalized))
67
+ return "product design";
68
+ if (/\b(marketing|marketer|growth)\b/.test(normalized))
69
+ return "marketing";
70
+ if (/\b(product manager|product)\b/.test(normalized))
71
+ return "product management";
72
+ if (/\b(data scientist|machine learning|ml|ai)\b/.test(normalized))
73
+ return "AI/ML";
74
+ if (/\b(sales|account executive|business development)\b/.test(normalized))
75
+ return "sales";
76
+ return null;
77
+ };
78
+ /**
79
+ * Derive a job-role qualifier from the user's global user_context paragraph
80
+ * (the identity text that replaced the legacy profile projection). Role is
81
+ * inferred from the free text; the old structured skills/interests extraction
82
+ * is gone with the removed profile fields.
83
+ */
84
+ export const buildJobQualifierFromContext = (contextText) => {
85
+ const roleHint = inferRoleFromContextText(contextText ?? "");
86
+ return roleHint ? `${roleHint} role` : null;
87
+ };
88
+ export const enrichVagueIntentWithContext = (description, userContext) => {
89
+ const trimmed = description?.trim();
90
+ if (!trimmed)
91
+ return description;
92
+ const isGenericJobRequest = GENERIC_JOB_PHRASE.test(trimmed) ||
93
+ /\b(?:find|get|look(?:ing)?\s+for|want)\s+(?:to\s+)?(?:find\s+)?job\b/i.test(trimmed);
94
+ if (!isGenericJobRequest)
95
+ return description;
96
+ const qualifier = buildJobQualifierFromContext(userContext);
97
+ if (!qualifier)
98
+ return description;
99
+ const enriched = trimmed
100
+ .replace(/\ba job\b/i, `a ${qualifier}`)
101
+ .replace(/\bjob\b/i, `${qualifier}`)
102
+ .replace(/\s{2,}/g, " ")
103
+ .trim();
104
+ return enriched.length > 0 ? enriched : description;
105
+ };
106
+ export const isVague = (description, entropy, clarity) => {
107
+ if (GENERIC_JOB_PHRASE.test(description))
108
+ return true;
109
+ if (entropy > MAX_PERMISSIBLE_ENTROPY)
110
+ return true;
111
+ if (clarity < MIN_CLEAR_INTENT_SCORE)
112
+ return true;
113
+ return false;
114
+ };
115
+ export const getSpecificityWarning = (verdict) => {
116
+ const warning = verdict.specificity_warning?.trim();
117
+ return warning && warning.length > 0 ? warning : DEFAULT_SPECIFICITY_WARNING;
118
+ };
119
+ export const toSpeechActType = (classification) => {
120
+ if (classification === "COMMISSIVE" || classification === "DIRECTIVE")
121
+ return classification;
122
+ return null;
123
+ };
124
+ /**
125
+ * Factory class to build and compile the Intent Processing Graph.
126
+
127
+ /**
128
+ * Generate a flat embedding for an intent payload, swallowing failures so
129
+ * persistence can continue without an embedding. `intentId` is logging-only
130
+ * (present for updates, absent for creates).
131
+ */
132
+ export async function generateIntentEmbedding(deps, sanitizedPayload, intentId) {
133
+ if (!deps.embedder)
134
+ return undefined;
135
+ try {
136
+ const embedding = await deps.embedder.generate(sanitizedPayload, undefined, getAbortSignalConfig());
137
+ const flatEmbedding = Array.isArray(embedding?.[0])
138
+ ? embedding[0]
139
+ : embedding;
140
+ logger.verbose("Generated embedding for intent", {
141
+ ...(intentId ? { intentId } : {}),
142
+ dimensions: flatEmbedding?.length,
143
+ });
144
+ return flatEmbedding;
145
+ }
146
+ catch (embErr) {
147
+ logger.error("Failed to generate embedding for intent (continuing without)", {
148
+ ...(intentId ? { intentId } : {}),
149
+ error: embErr,
150
+ });
151
+ return undefined;
152
+ }
153
+ }
@@ -82,7 +82,7 @@ export declare const IntentGraphState: import("@langchain/langgraph").Annotation
82
82
  *
83
83
  * Defaults to 'create' for backward compatibility.
84
84
  */
85
- operationMode: import("@langchain/langgraph").BaseChannel<"delete" | "propose" | "update" | "create" | "read", "delete" | "propose" | "update" | "create" | "read" | import("@langchain/langgraph").OverwriteValue<"delete" | "propose" | "update" | "create" | "read">, unknown>;
85
+ operationMode: import("@langchain/langgraph").BaseChannel<"update" | "propose" | "create" | "delete" | "read", "update" | "propose" | "create" | "delete" | "read" | import("@langchain/langgraph").OverwriteValue<"update" | "propose" | "create" | "delete" | "read">, unknown>;
86
86
  /**
87
87
  * For update/delete operations, specifies which intent IDs to target.
88
88
  * Optional - used when modifying or removing specific intents.
@@ -1,3 +1,4 @@
1
+ import { MaintenanceGraphState } from './maintenance.state.js';
1
2
  /** Database methods needed by the maintenance graph (includes introducer discovery). */
2
3
  export interface MaintenanceGraphDatabase {
3
4
  getOpportunitiesForUser(userId: string, options?: {
@@ -43,14 +44,25 @@ export interface MaintenanceGraphQueue {
43
44
  jobId?: string;
44
45
  }): Promise<unknown>;
45
46
  }
47
+ /**
48
+ * Factory for the Maintenance Graph.
49
+ * Accepts database, cache, and queue dependencies via constructor injection.
50
+ */
51
+ /** The graph's channel state, as every node sees it. */
52
+ export type MaintenanceState = typeof MaintenanceGraphState.State;
53
+ /** Everything the maintenance nodes reach for. */
54
+ export interface MaintenanceGraphDeps {
55
+ database: MaintenanceGraphDatabase;
56
+ cache: MaintenanceGraphCache;
57
+ queue: MaintenanceGraphQueue;
58
+ }
46
59
  /**
47
60
  * Factory for the Maintenance Graph.
48
61
  * Accepts database, cache, and queue dependencies via constructor injection.
49
62
  */
50
63
  export declare class MaintenanceGraphFactory {
51
- private database;
52
- private cache;
53
- private queue;
64
+ /** Resolved dependency bag shared by every node. */
65
+ readonly deps: MaintenanceGraphDeps;
54
66
  constructor(database: MaintenanceGraphDatabase, cache: MaintenanceGraphCache, queue: MaintenanceGraphQueue);
55
67
  /** Compile and return the maintenance graph. */
56
68
  createGraph(): import("@langchain/langgraph").CompiledStateGraph<{
@@ -174,3 +186,54 @@ export declare class MaintenanceGraphFactory {
174
186
  logMaintenance: {};
175
187
  }, unknown, unknown, []>;
176
188
  }
189
+ export declare function loadCurrentRadarNode(state: MaintenanceState, deps: MaintenanceGraphDeps): Promise<{
190
+ error: string;
191
+ currentOpportunities?: undefined;
192
+ expiredCount?: undefined;
193
+ activeIntents?: undefined;
194
+ lastRediscoveryAt?: undefined;
195
+ } | {
196
+ currentOpportunities: {
197
+ [key: string]: unknown;
198
+ id: string;
199
+ actors: Array<{
200
+ userId: string;
201
+ role: string;
202
+ }>;
203
+ status: string;
204
+ }[];
205
+ expiredCount: number;
206
+ activeIntents: {
207
+ id: string;
208
+ payload: string;
209
+ }[];
210
+ lastRediscoveryAt: number | null;
211
+ error?: undefined;
212
+ }>;
213
+ export declare function scoreRadarHealthNode(state: MaintenanceState): Promise<{
214
+ healthResult?: undefined;
215
+ connectorFlowCount?: undefined;
216
+ error?: undefined;
217
+ } | {
218
+ healthResult: import("../opportunities/radar/radar.health.js").RadarHealthResult;
219
+ connectorFlowCount: number;
220
+ error?: undefined;
221
+ } | {
222
+ error: string;
223
+ healthResult?: undefined;
224
+ connectorFlowCount?: undefined;
225
+ }>;
226
+ export declare function shouldRediscover(state: MaintenanceState): string;
227
+ export declare function rediscoverNode(state: MaintenanceState, deps: MaintenanceGraphDeps): Promise<{
228
+ rediscoveryJobsEnqueued: number;
229
+ error?: undefined;
230
+ } | {
231
+ error: string;
232
+ rediscoveryJobsEnqueued?: undefined;
233
+ }>;
234
+ export declare function introducerDiscoveryNode(state: MaintenanceState, deps: MaintenanceGraphDeps): Promise<{
235
+ introducerDiscoveryJobsEnqueued?: undefined;
236
+ } | {
237
+ introducerDiscoveryJobsEnqueued: number;
238
+ }>;
239
+ export declare function logMaintenanceNode(state: MaintenanceState): Promise<{}>;