@indexnetwork/protocol 14.3.2-rc.477.1 → 17.0.0-rc.479.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 (184) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/IMPLEMENTATION.md +50 -52
  3. package/STABILITY.md +3 -3
  4. package/dist/chat/chat.agent.js +2 -3
  5. package/dist/chat/chat.graph.d.ts +41 -1
  6. package/dist/chat/chat.graph.js +108 -127
  7. package/dist/chat/negotiator.persona.d.ts +1 -1
  8. package/dist/chat/negotiator.persona.js +1 -4
  9. package/dist/chat/negotiator.prompt.js +1 -2
  10. package/dist/chat/onboarding.persona.js +0 -1
  11. package/dist/chat/reporter.persona.js +0 -1
  12. package/dist/chat/signal.persona.js +0 -1
  13. package/dist/contacts/application/contact.tools.d.ts +6 -9
  14. package/dist/contacts/application/contact.tools.js +11 -92
  15. package/dist/contacts/application/index.d.ts +2 -14
  16. package/dist/contacts/application/index.js +2 -13
  17. package/dist/contacts/domain/contact.types.d.ts +0 -23
  18. package/dist/contacts/domain/index.d.ts +2 -6
  19. package/dist/contacts/index.d.ts +1 -1
  20. package/dist/contacts/index.js +1 -1
  21. package/dist/contacts/ports/contact.repository.port.d.ts +5 -12
  22. package/dist/contacts/ports/contact.repository.port.js +4 -6
  23. package/dist/contacts/ports/contact.tools.port.d.ts +1 -3
  24. package/dist/contacts/ports/contact.tools.port.js +1 -2
  25. package/dist/contacts/ports/index.d.ts +1 -1
  26. package/dist/contacts/ports/index.js +1 -1
  27. package/dist/discovery/hyde.frame.d.ts +4 -4
  28. package/dist/discovery/hyde.graph.d.ts +71 -12
  29. package/dist/discovery/hyde.graph.js +337 -335
  30. package/dist/discovery/lens.inferrer.d.ts +6 -6
  31. package/dist/enrichment/enrichment.graph.d.ts +127 -9
  32. package/dist/enrichment/enrichment.graph.js +633 -655
  33. package/dist/enrichment/enrichment.state.d.ts +1 -1
  34. package/dist/enrichment/enrichment.tools.context-read.d.ts +10 -0
  35. package/dist/enrichment/enrichment.tools.context-read.js +353 -0
  36. package/dist/enrichment/enrichment.tools.context-write.d.ts +9 -0
  37. package/dist/enrichment/enrichment.tools.context-write.js +417 -0
  38. package/dist/enrichment/enrichment.tools.d.ts +11 -1
  39. package/dist/enrichment/enrichment.tools.helpers.d.ts +139 -0
  40. package/dist/enrichment/enrichment.tools.helpers.js +234 -0
  41. package/dist/enrichment/enrichment.tools.js +16 -975
  42. package/dist/index.d.ts +27 -50
  43. package/dist/index.js +17 -28
  44. package/dist/intents/application/intent.graph.d.ts +71 -67
  45. package/dist/intents/application/intent.graph.execute.d.ts +59 -0
  46. package/dist/intents/application/intent.graph.execute.js +301 -0
  47. package/dist/intents/application/intent.graph.infer.d.ts +44 -0
  48. package/dist/intents/application/intent.graph.infer.js +97 -0
  49. package/dist/intents/application/intent.graph.js +96 -888
  50. package/dist/intents/application/intent.graph.reconcile.d.ts +71 -0
  51. package/dist/intents/application/intent.graph.reconcile.js +274 -0
  52. package/dist/intents/application/intent.graph.shared.d.ts +70 -0
  53. package/dist/intents/application/intent.graph.shared.js +153 -0
  54. package/dist/intents/domain/intent.state.d.ts +1 -1
  55. package/dist/maintenance/maintenance.graph.d.ts +66 -3
  56. package/dist/maintenance/maintenance.graph.js +155 -156
  57. package/dist/mcp/mcp.authorization-policy.d.ts +24 -28
  58. package/dist/mcp/mcp.authorization-policy.js +14 -34
  59. package/dist/mcp/mcp.server.d.ts +3 -3
  60. package/dist/mcp/mcp.server.js +14 -20
  61. package/dist/negotiations/application/negotiation.candidates.d.ts +83 -0
  62. package/dist/negotiations/application/negotiation.candidates.js +162 -0
  63. package/dist/negotiations/application/negotiation.graph.d.ts +91 -157
  64. package/dist/negotiations/application/negotiation.graph.finalize.d.ts +5 -0
  65. package/dist/negotiations/application/negotiation.graph.finalize.js +280 -0
  66. package/dist/negotiations/application/negotiation.graph.init.d.ts +54 -0
  67. package/dist/negotiations/application/negotiation.graph.init.js +227 -0
  68. package/dist/negotiations/application/negotiation.graph.js +69 -1388
  69. package/dist/negotiations/application/negotiation.graph.screen.d.ts +23 -0
  70. package/dist/negotiations/application/negotiation.graph.screen.js +108 -0
  71. package/dist/negotiations/application/negotiation.graph.shared.d.ts +75 -0
  72. package/dist/negotiations/application/negotiation.graph.shared.js +125 -0
  73. package/dist/negotiations/application/negotiation.graph.turn.d.ts +105 -0
  74. package/dist/negotiations/application/negotiation.graph.turn.js +484 -0
  75. package/dist/negotiations/domain/negotiation.state.d.ts +1 -1
  76. package/dist/negotiations/domain/negotiation.state.js +0 -1
  77. package/dist/networks/application/indexer.graph.d.ts +165 -7
  78. package/dist/networks/application/indexer.graph.js +339 -388
  79. package/dist/networks/application/indexer.state.d.ts +1 -1
  80. package/dist/networks/application/membership.graph.d.ts +83 -5
  81. package/dist/networks/application/membership.graph.js +177 -207
  82. package/dist/networks/application/network.graph.d.ts +148 -5
  83. package/dist/networks/application/network.graph.js +249 -278
  84. package/dist/networks/domain/membership.state.d.ts +1 -1
  85. package/dist/networks/domain/network.state.d.ts +1 -1
  86. package/dist/opportunities/application/delivery-card.cache.d.ts +1 -1
  87. package/dist/opportunities/application/delivery-card.cache.js +2 -2
  88. package/dist/opportunities/application/index.d.ts +2 -2
  89. package/dist/opportunities/application/index.js +2 -2
  90. package/dist/opportunities/application/opportunity.evaluator.js +1 -1
  91. package/dist/opportunities/application/opportunity.graph.d.ts +686 -473
  92. package/dist/opportunities/application/opportunity.graph.discovery-strategies.d.ts +109 -0
  93. package/dist/opportunities/application/opportunity.graph.discovery-strategies.js +451 -0
  94. package/dist/opportunities/application/opportunity.graph.discovery.d.ts +69 -0
  95. package/dist/opportunities/application/opportunity.graph.discovery.js +397 -0
  96. package/dist/opportunities/application/opportunity.graph.evaluation.d.ts +82 -0
  97. package/dist/opportunities/application/opportunity.graph.evaluation.js +608 -0
  98. package/dist/opportunities/application/opportunity.graph.js +120 -3690
  99. package/dist/opportunities/application/opportunity.graph.modes.d.ts +538 -0
  100. package/dist/opportunities/application/opportunity.graph.modes.js +538 -0
  101. package/dist/opportunities/application/opportunity.graph.negotiate.d.ts +109 -0
  102. package/dist/opportunities/application/opportunity.graph.negotiate.js +392 -0
  103. package/dist/opportunities/application/opportunity.graph.persist-node.d.ts +93 -0
  104. package/dist/opportunities/application/opportunity.graph.persist-node.js +765 -0
  105. package/dist/opportunities/application/opportunity.graph.prep.d.ts +150 -0
  106. package/dist/opportunities/application/opportunity.graph.prep.js +381 -0
  107. package/dist/opportunities/application/opportunity.graph.shared.d.ts +173 -0
  108. package/dist/opportunities/application/opportunity.graph.shared.js +199 -0
  109. package/dist/opportunities/application/opportunity.presentation.d.ts +399 -0
  110. package/dist/opportunities/application/{opportunity.presenter.js → opportunity.presentation.js} +735 -9
  111. package/dist/opportunities/application/opportunity.tools.cards.d.ts +152 -0
  112. package/dist/opportunities/application/opportunity.tools.cards.js +235 -0
  113. package/dist/opportunities/application/opportunity.tools.d.ts +8 -114
  114. package/dist/opportunities/application/opportunity.tools.js +28 -711
  115. package/dist/opportunities/application/opportunity.tools.list.d.ts +10 -0
  116. package/dist/opportunities/application/opportunity.tools.list.js +493 -0
  117. package/dist/opportunities/domain/index.d.ts +3 -3
  118. package/dist/opportunities/domain/index.js +3 -3
  119. package/dist/opportunities/domain/opportunity.state.d.ts +19 -19
  120. package/dist/opportunities/index.d.ts +6 -6
  121. package/dist/opportunities/index.js +4 -4
  122. package/dist/opportunities/ports/opportunity.tools.port.d.ts +1 -1
  123. package/dist/opportunities/radar/radar.graph.d.ts +57 -13
  124. package/dist/opportunities/radar/radar.graph.js +470 -471
  125. package/dist/premises/premise.graph.d.ts +88 -20
  126. package/dist/premises/premise.graph.js +207 -218
  127. package/dist/questions/domain/question.schema.d.ts +40 -40
  128. package/dist/shared/agent/tool.factory.js +0 -8
  129. package/dist/shared/agent/tool.helpers.d.ts +21 -45
  130. package/dist/shared/agent/tool.helpers.js +1 -2
  131. package/dist/shared/agent/tool.registry.d.ts +4 -3
  132. package/dist/shared/agent/tool.registry.js +2 -4
  133. package/dist/shared/agent/tool.runtime.js +0 -2
  134. package/dist/shared/agent/utility.tools.js +9 -13
  135. package/dist/shared/interfaces/database.capabilities.d.ts +100 -0
  136. package/dist/shared/interfaces/database.capabilities.js +7 -0
  137. package/dist/shared/interfaces/database.entities.d.ts +533 -0
  138. package/dist/shared/interfaces/database.entities.js +10 -0
  139. package/dist/shared/interfaces/database.identity-queries.d.ts +294 -0
  140. package/dist/shared/interfaces/database.identity-queries.js +4 -0
  141. package/dist/shared/interfaces/database.interface.d.ts +15 -2277
  142. package/dist/shared/interfaces/database.interface.js +8 -0
  143. package/dist/shared/interfaces/database.member-queries.d.ts +221 -0
  144. package/dist/shared/interfaces/database.member-queries.js +4 -0
  145. package/dist/shared/interfaces/database.negotiation.d.ts +305 -0
  146. package/dist/shared/interfaces/database.negotiation.js +26 -0
  147. package/dist/shared/interfaces/database.network-queries.d.ts +277 -0
  148. package/dist/shared/interfaces/database.network-queries.js +4 -0
  149. package/dist/shared/interfaces/database.opportunity-queries.d.ts +285 -0
  150. package/dist/shared/interfaces/database.opportunity-queries.js +4 -0
  151. package/dist/shared/interfaces/database.port.d.ts +322 -0
  152. package/dist/shared/interfaces/database.port.js +29 -0
  153. package/dist/shared/schemas/discovery-question.schema.d.ts +14 -14
  154. package/dist/shared/schemas/negotiation-digest.schema.d.ts +2 -2
  155. package/package.json +2 -15
  156. package/dist/contacts/application/contact.inviter.d.ts +0 -49
  157. package/dist/contacts/application/contact.inviter.js +0 -64
  158. package/dist/integrations/application/index.d.ts +0 -14
  159. package/dist/integrations/application/index.js +0 -14
  160. package/dist/integrations/application/integration.tools.d.ts +0 -27
  161. package/dist/integrations/application/integration.tools.js +0 -98
  162. package/dist/integrations/domain/index.d.ts +0 -16
  163. package/dist/integrations/domain/index.js +0 -1
  164. package/dist/integrations/domain/integration.types.d.ts +0 -51
  165. package/dist/integrations/domain/integration.types.js +0 -9
  166. package/dist/integrations/index.d.ts +0 -10
  167. package/dist/integrations/index.js +0 -8
  168. package/dist/integrations/ports/index.d.ts +0 -25
  169. package/dist/integrations/ports/index.js +0 -23
  170. package/dist/integrations/ports/integration.adapter.port.d.ts +0 -62
  171. package/dist/integrations/ports/integration.adapter.port.js +0 -15
  172. package/dist/integrations/ports/integration.importer.port.d.ts +0 -31
  173. package/dist/integrations/ports/integration.importer.port.js +0 -15
  174. package/dist/integrations/ports/integration.tools.port.d.ts +0 -25
  175. package/dist/integrations/ports/integration.tools.port.js +0 -18
  176. package/dist/opportunities/application/opportunity.card-presentation.d.ts +0 -44
  177. package/dist/opportunities/application/opportunity.card-presentation.js +0 -93
  178. package/dist/opportunities/application/opportunity.presenter.d.ts +0 -156
  179. package/dist/opportunities/domain/opportunity.presentation-cache.d.ts +0 -5
  180. package/dist/opportunities/domain/opportunity.presentation-cache.js +0 -11
  181. package/dist/opportunities/domain/opportunity.presentation.d.ts +0 -76
  182. package/dist/opportunities/domain/opportunity.presentation.js +0 -516
  183. package/dist/opportunities/domain/opportunity.safe-presentation.d.ts +0 -104
  184. package/dist/opportunities/domain/opportunity.safe-presentation.js +0 -102
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Intent graph, stage 4 and the read fast path.
3
+ */
4
+ import { ExecutionResult } from "../domain/intent.state.js";
5
+ import { type IntentGraphDeps, type IntentState } from "./intent.graph.shared.js";
6
+ /**
7
+ * Node 4: Executor
8
+ * Executes reconciler actions against the database.
9
+ */
10
+ export declare function executorNode(state: IntentState, deps: IntentGraphDeps): Promise<{
11
+ executionResults: ExecutionResult[];
12
+ }>;
13
+ /**
14
+ * Node: Query
15
+ * Fast-path read node — fetches intents from DB based on scope.
16
+ * Handles: global user intents, network-scoped (all or filtered by user).
17
+ * No LLM calls; no inference/verification/reconciliation.
18
+ */
19
+ export declare function queryNode(state: IntentState, deps: IntentGraphDeps): Promise<{
20
+ readResult: {
21
+ count: number;
22
+ intents: never[];
23
+ message: string;
24
+ networkId?: undefined;
25
+ };
26
+ } | {
27
+ readResult: {
28
+ count: number;
29
+ intents: {
30
+ id: string;
31
+ description: string;
32
+ summary: string | null;
33
+ createdAt: Date;
34
+ }[];
35
+ message?: undefined;
36
+ networkId?: undefined;
37
+ };
38
+ } | {
39
+ readResult: {
40
+ count: number;
41
+ intents: never[];
42
+ message: string;
43
+ networkId: string;
44
+ };
45
+ } | {
46
+ readResult: {
47
+ count: number;
48
+ networkId: string;
49
+ intents: {
50
+ id: string;
51
+ description: string;
52
+ summary: string | null;
53
+ createdAt: Date;
54
+ userId: string;
55
+ userName: string | null;
56
+ }[];
57
+ message?: undefined;
58
+ };
59
+ }>;
@@ -0,0 +1,301 @@
1
+ /**
2
+ * Intent graph, stage 4 and the read fast path.
3
+ */
4
+ import { normalizeIntentDescription } from "../domain/intent.proposal.js";
5
+ import { timed } from "../../shared/observability/performance.js";
6
+ import { enforceIntentActionBoundary, generateIntentEmbedding, logger, toSpeechActType } from "./intent.graph.shared.js";
7
+ /**
8
+ * Node 4: Executor
9
+ * Executes reconciler actions against the database.
10
+ */
11
+ export async function executorNode(state, deps) {
12
+ return timed("IntentGraph.executor", async () => {
13
+ const actions = enforceIntentActionBoundary(state.operationMode, state.targetIntentIds, state.actions ?? []);
14
+ if (actions.length === 0) {
15
+ return { executionResults: [] };
16
+ }
17
+ logger.verbose('Executing actions', { count: actions.length });
18
+ const results = [];
19
+ const scopeEnvelope = state.scopeType && state.scopeId
20
+ ? { scopeType: state.scopeType, scopeId: state.scopeId }
21
+ : {};
22
+ const verifiedIntentByPayload = new Map();
23
+ for (const verifiedIntent of state.verifiedIntents) {
24
+ verifiedIntentByPayload.set(verifiedIntent.description, verifiedIntent);
25
+ verifiedIntentByPayload.set(normalizeIntentDescription(verifiedIntent.description), verifiedIntent);
26
+ }
27
+ for (const action of actions) {
28
+ const actionType = action.type.toLowerCase();
29
+ try {
30
+ if (actionType === 'create') {
31
+ const createAction = action;
32
+ const sanitizedPayload = normalizeIntentDescription(createAction.payload);
33
+ const matchedVerifiedIntent = verifiedIntentByPayload.get(createAction.payload) ||
34
+ verifiedIntentByPayload.get(sanitizedPayload);
35
+ // Generate embedding for the intent payload
36
+ const flatEmbedding = await generateIntentEmbedding(deps, sanitizedPayload);
37
+ const created = await deps.database.createIntent({
38
+ userId: state.userId,
39
+ payload: sanitizedPayload,
40
+ confidence: createAction.score ? createAction.score / 100 : 1.0,
41
+ inferenceType: 'explicit',
42
+ sourceType: 'discovery_form',
43
+ embedding: flatEmbedding,
44
+ semanticEntropy: createAction.semanticEntropy ??
45
+ matchedVerifiedIntent?.verification?.semantic_entropy ??
46
+ null,
47
+ referentialAnchor: createAction.referentialAnchor ??
48
+ matchedVerifiedIntent?.verification?.referential_anchor ??
49
+ null,
50
+ felicityAuthority: matchedVerifiedIntent?.verification?.felicity_scores.authority ?? null,
51
+ felicitySincerity: matchedVerifiedIntent?.verification?.felicity_scores.sincerity ?? null,
52
+ felicityClarity: matchedVerifiedIntent?.verification?.felicity_scores.clarity ?? null,
53
+ intentMode: createAction.intentMode ?? null,
54
+ speechActType: toSpeechActType(matchedVerifiedIntent?.verification?.classification),
55
+ });
56
+ results.push({ actionType: 'create', success: true, intentId: created.id, payload: sanitizedPayload });
57
+ logger.verbose('Created intent', { intentId: created.id });
58
+ deps.intentQueue?.addGenerateHydeJob({
59
+ intentId: created.id,
60
+ userId: state.userId,
61
+ ...scopeEnvelope,
62
+ }).catch((err) => logger.error('Failed to enqueue intent HyDE job', { intentId: created.id, error: err }));
63
+ if (deps.questionerEnqueue) {
64
+ const userContext = (await deps.database.getUserContext(state.userId, null))?.text ?? '';
65
+ deps.questionerEnqueue({
66
+ mode: 'intent',
67
+ userId: state.userId,
68
+ sourceType: 'intent',
69
+ sourceId: created.id,
70
+ ...scopeEnvelope,
71
+ context: {
72
+ intentId: created.id,
73
+ payload: sanitizedPayload,
74
+ userContext,
75
+ },
76
+ }).catch((err) => logger.error('Failed to enqueue intent question generation', { intentId: created.id, error: err }));
77
+ }
78
+ }
79
+ else if (actionType === 'update') {
80
+ const updateAction = action;
81
+ const sanitizedPayload = normalizeIntentDescription(updateAction.payload);
82
+ const matchedVerifiedIntent = verifiedIntentByPayload.get(updateAction.payload) ||
83
+ verifiedIntentByPayload.get(sanitizedPayload);
84
+ // Regenerate embedding for the updated payload
85
+ const flatEmbedding = await generateIntentEmbedding(deps, sanitizedPayload, updateAction.id);
86
+ const updated = await deps.database.updateIntent(updateAction.id, {
87
+ payload: sanitizedPayload,
88
+ embedding: flatEmbedding,
89
+ semanticEntropy: matchedVerifiedIntent?.verification?.semantic_entropy ??
90
+ null,
91
+ referentialAnchor: matchedVerifiedIntent?.verification?.referential_anchor ??
92
+ null,
93
+ felicityAuthority: matchedVerifiedIntent?.verification?.felicity_scores.authority ?? null,
94
+ felicitySincerity: matchedVerifiedIntent?.verification?.felicity_scores.sincerity ?? null,
95
+ felicityClarity: matchedVerifiedIntent?.verification?.felicity_scores.clarity ?? null,
96
+ intentMode: updateAction.intentMode ?? null,
97
+ speechActType: toSpeechActType(matchedVerifiedIntent?.verification?.classification),
98
+ ...(state.expectedIntentFingerprint !== undefined ? {
99
+ expectedIntentFingerprint: state.expectedIntentFingerprint,
100
+ expectedIntentUserId: state.userId,
101
+ } : {}),
102
+ });
103
+ results.push({
104
+ actionType: 'update',
105
+ success: !!updated,
106
+ intentId: updateAction.id,
107
+ payload: sanitizedPayload,
108
+ error: updated ? undefined : 'Intent not found'
109
+ });
110
+ logger.verbose('Updated intent', { intentId: updateAction.id });
111
+ if (updated) {
112
+ deps.intentQueue?.addGenerateHydeJob({
113
+ intentId: updateAction.id,
114
+ userId: state.userId,
115
+ ...scopeEnvelope,
116
+ }).catch((err) => logger.error('Failed to enqueue intent HyDE job', { intentId: updateAction.id, error: err }));
117
+ }
118
+ }
119
+ else if (actionType === 'expire') {
120
+ const expireAction = action;
121
+ const result = await deps.database.archiveIntent(expireAction.id);
122
+ results.push({
123
+ actionType: 'expire',
124
+ success: result.success,
125
+ intentId: expireAction.id,
126
+ error: result.error
127
+ });
128
+ logger.verbose('Archived intent', { intentId: expireAction.id });
129
+ if (result.success) {
130
+ deps.intentQueue?.addDeleteHydeJob({ intentId: expireAction.id }).catch((err) => logger.error('Failed to enqueue intent HyDE delete job', { intentId: expireAction.id, error: err }));
131
+ }
132
+ }
133
+ }
134
+ catch (error) {
135
+ logger.error('Failed to execute action', { actionType: action.type, error });
136
+ results.push({
137
+ actionType,
138
+ success: false,
139
+ intentId: 'id' in action ? action.id : undefined,
140
+ error: error instanceof Error ? error.message : 'Unknown error'
141
+ });
142
+ }
143
+ }
144
+ return { executionResults: results };
145
+ });
146
+ }
147
+ /**
148
+ * Node: Query
149
+ * Fast-path read node — fetches intents from DB based on scope.
150
+ * Handles: global user intents, network-scoped (all or filtered by user).
151
+ * No LLM calls; no inference/verification/reconciliation.
152
+ */
153
+ export async function queryNode(state, deps) {
154
+ return timed("IntentGraph.query", async () => {
155
+ logger.verbose("Starting query (read mode)", {
156
+ userId: state.userId,
157
+ networkId: state.networkId,
158
+ queryUserId: state.queryUserId,
159
+ allUserIntents: state.allUserIntents,
160
+ });
161
+ try {
162
+ // Scope-aware default: caller's intents across all reachable networks.
163
+ // Triggered when the tool layer passed indexScope and did not pick a
164
+ // specific networkId or queryUserId — i.e. "my intents" in a chat
165
+ // where the agent's reach is more than one index.
166
+ if (!state.queryUserId &&
167
+ !state.networkId &&
168
+ state.indexScope &&
169
+ state.indexScope.length > 0) {
170
+ const intents = await deps.database.getActiveIntentsAcrossIndexes(state.userId, state.indexScope);
171
+ if (intents.length === 0) {
172
+ return {
173
+ readResult: {
174
+ count: 0,
175
+ intents: [],
176
+ message: "You don't have any active intents yet. Share what you're looking for.",
177
+ },
178
+ };
179
+ }
180
+ return {
181
+ readResult: {
182
+ count: intents.length,
183
+ intents: intents.map((i) => ({
184
+ id: i.id,
185
+ description: i.payload,
186
+ summary: i.summary,
187
+ createdAt: i.createdAt,
188
+ })),
189
+ },
190
+ };
191
+ }
192
+ // When allUserIntents is true, ignore network scope and return all
193
+ const effectiveIndexId = state.allUserIntents ? undefined : state.networkId;
194
+ if (effectiveIndexId) {
195
+ // Verify membership
196
+ const isMember = await deps.database.isNetworkMember(effectiveIndexId, state.userId);
197
+ if (!isMember) {
198
+ return {
199
+ readResult: {
200
+ count: 0,
201
+ intents: [],
202
+ message: "Index not found or you are not a member.",
203
+ },
204
+ };
205
+ }
206
+ // Network-scoped read
207
+ if (!state.queryUserId) {
208
+ // All intents in the index (any member can see)
209
+ const intents = await deps.database.getNetworkIntentsForMember(effectiveIndexId, state.userId, { limit: 50, offset: 0 });
210
+ if (intents.length === 0) {
211
+ return {
212
+ readResult: {
213
+ count: 0,
214
+ intents: [],
215
+ message: "No intents in this network yet.",
216
+ networkId: effectiveIndexId,
217
+ },
218
+ };
219
+ }
220
+ return {
221
+ readResult: {
222
+ count: intents.length,
223
+ networkId: effectiveIndexId,
224
+ intents: intents.map((i) => ({
225
+ id: i.id,
226
+ description: i.payload,
227
+ summary: i.summary,
228
+ createdAt: i.createdAt,
229
+ userId: i.userId,
230
+ userName: i.userName,
231
+ })),
232
+ },
233
+ };
234
+ }
235
+ // Specific user's intents in the index
236
+ const effectiveUserId = state.queryUserId;
237
+ const intents = await deps.database.getIntentsInIndexForMember(effectiveUserId, effectiveIndexId);
238
+ if (intents.length === 0) {
239
+ return {
240
+ readResult: {
241
+ count: 0,
242
+ intents: [],
243
+ message: effectiveUserId === state.userId
244
+ ? "You don't have any intents in this network yet."
245
+ : "No intents for that user in this network.",
246
+ networkId: effectiveIndexId,
247
+ },
248
+ };
249
+ }
250
+ const user = await deps.database.getUser(effectiveUserId);
251
+ const userName = user?.name ?? null;
252
+ return {
253
+ readResult: {
254
+ count: intents.length,
255
+ networkId: effectiveIndexId,
256
+ intents: intents.map((i) => ({
257
+ id: i.id,
258
+ description: i.payload,
259
+ summary: i.summary,
260
+ createdAt: i.createdAt,
261
+ userId: effectiveUserId,
262
+ userName,
263
+ })),
264
+ },
265
+ };
266
+ }
267
+ // Global (no network scope): return user's own active intents
268
+ const intents = await deps.database.getActiveIntents(state.userId);
269
+ if (intents.length === 0) {
270
+ return {
271
+ readResult: {
272
+ count: 0,
273
+ intents: [],
274
+ message: "You don't have any active intents yet. Share what you're looking for.",
275
+ },
276
+ };
277
+ }
278
+ return {
279
+ readResult: {
280
+ count: intents.length,
281
+ intents: intents.map((i) => ({
282
+ id: i.id,
283
+ description: i.payload,
284
+ summary: i.summary,
285
+ createdAt: i.createdAt,
286
+ })),
287
+ },
288
+ };
289
+ }
290
+ catch (err) {
291
+ logger.error("Query node failed", { error: err });
292
+ return {
293
+ readResult: {
294
+ count: 0,
295
+ intents: [],
296
+ message: "Failed to fetch intents. Please try again.",
297
+ },
298
+ };
299
+ }
300
+ });
301
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Intent graph, stages 0-1: load the user's signals, then infer new ones.
3
+ */
4
+ import type { DebugMetaAgent } from "../../agents/index.js";
5
+ import { type IntentGraphDeps, type IntentState } from "./intent.graph.shared.js";
6
+ /**
7
+ * Node 0: Prep
8
+ * Always fetches ALL of the user's active intents from the DB via getActiveIntents(userId).
9
+ * This ensures reconciliation can detect duplicates and modifications globally,
10
+ * regardless of network scope.
11
+ */
12
+ export declare function prepNode(state: IntentState, deps: IntentGraphDeps): Promise<{
13
+ error: string;
14
+ activeIntents?: undefined;
15
+ activeIntentIds?: undefined;
16
+ trace?: undefined;
17
+ } | {
18
+ activeIntents: string;
19
+ activeIntentIds: string[];
20
+ trace: {
21
+ node: string;
22
+ detail: string;
23
+ }[];
24
+ error?: undefined;
25
+ }>;
26
+ /**
27
+ * Node 1: Inference
28
+ * Extracts intents from raw content.
29
+ * Phase 4: Uses operation mode to control behavior and determine if node should execute.
30
+ * Phase 5: Passes conversation context for anaphoric resolution.
31
+ */
32
+ export declare function inferenceNode(state: IntentState, deps: IntentGraphDeps): Promise<{
33
+ inferredIntents: {
34
+ reasoning: string;
35
+ type: "goal" | "tombstone";
36
+ confidence: "low" | "medium" | "high";
37
+ description: string;
38
+ }[];
39
+ agentTimings: DebugMetaAgent[];
40
+ trace: {
41
+ node: string;
42
+ detail: string;
43
+ }[];
44
+ }>;
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Intent graph, stages 0-1: load the user's signals, then infer new ones.
3
+ */
4
+ import { timed } from "../../shared/observability/performance.js";
5
+ import { requestContext } from "../../shared/observability/request-context.js";
6
+ import { logger } from "./intent.graph.shared.js";
7
+ /**
8
+ * Node 0: Prep
9
+ * Always fetches ALL of the user's active intents from the DB via getActiveIntents(userId).
10
+ * This ensures reconciliation can detect duplicates and modifications globally,
11
+ * regardless of network scope.
12
+ */
13
+ export async function prepNode(state, deps) {
14
+ return timed("IntentGraph.prep", async () => {
15
+ logger.verbose("Starting preparation phase", {
16
+ operationMode: state.operationMode,
17
+ hasContent: !!state.inputContent,
18
+ targetIntentIds: state.targetIntentIds,
19
+ networkId: state.networkId,
20
+ });
21
+ // Gate: write operations require an existing profile
22
+ if (state.operationMode !== 'read') {
23
+ const profile = await deps.database.getProfile(state.userId);
24
+ if (!profile) {
25
+ const msg = "You need to create a profile before creating intents. Please set up your profile first.";
26
+ logger.error("Prep failed: no profile for user", { userId: state.userId });
27
+ return { error: msg };
28
+ }
29
+ }
30
+ const activeIntents = await deps.database.getActiveIntents(state.userId);
31
+ const formattedActiveIntents = activeIntents
32
+ .map(i => `ID: ${i.id}, Description: ${i.payload}, Summary: ${i.summary || 'N/A'}`)
33
+ .join('\n') || "No active intents.";
34
+ logger.verbose("Fetched active intents", {
35
+ count: activeIntents.length,
36
+ operationMode: state.operationMode
37
+ });
38
+ return {
39
+ activeIntents: formattedActiveIntents,
40
+ activeIntentIds: activeIntents.map((intent) => intent.id),
41
+ trace: [{
42
+ node: "prep",
43
+ detail: `Fetched ${activeIntents.length} active intent(s)`,
44
+ }],
45
+ };
46
+ });
47
+ }
48
+ /**
49
+ * Node 1: Inference
50
+ * Extracts intents from raw content.
51
+ * Phase 4: Uses operation mode to control behavior and determine if node should execute.
52
+ * Phase 5: Passes conversation context for anaphoric resolution.
53
+ */
54
+ export async function inferenceNode(state, deps) {
55
+ return timed("IntentGraph.inference", async () => {
56
+ logger.verbose("Starting inference", {
57
+ operationMode: state.operationMode,
58
+ hasContent: !!state.inputContent,
59
+ contentPreview: state.inputContent?.substring(0, 50),
60
+ hasConversationContext: !!state.conversationContext,
61
+ conversationMessagesCount: state.conversationContext?.length || 0
62
+ });
63
+ const agentTimingsAccum = [];
64
+ // Phase 4: Control profile fallback based on operation mode
65
+ // Only allow for create operations without explicit content
66
+ const allowProfileFallback = state.operationMode === 'create' && !state.inputContent;
67
+ // Cast operationMode: 'read' and 'propose' map to 'create' for the deps.inferrer
68
+ // (inference node is never called in read mode; propose behaves like create for inference)
69
+ const inferrerMode = (state.operationMode === 'read' || state.operationMode === 'propose') ? 'create' : state.operationMode;
70
+ const _traceEmitterInferrer = requestContext.getStore()?.traceEmitter;
71
+ const inferrerStart = Date.now();
72
+ _traceEmitterInferrer?.({ type: "agent_start", name: "intent-inferrer" });
73
+ const result = await deps.inferrer.invoke(state.inputContent || null, state.userProfile, {
74
+ allowProfileFallback,
75
+ operationMode: inferrerMode,
76
+ conversationContext: state.conversationContext // Phase 5: Pass conversation history
77
+ });
78
+ agentTimingsAccum.push({ name: 'intent.inferrer', durationMs: Date.now() - inferrerStart });
79
+ _traceEmitterInferrer?.({ type: "agent_end", name: "intent-inferrer", durationMs: Date.now() - inferrerStart, summary: result.intents.length > 0 ? `Extracted ${result.intents.length} intent(s)` : "intent-inferrer completed" });
80
+ logger.verbose("Inference complete", {
81
+ inferredCount: result.intents.length,
82
+ operationMode: state.operationMode
83
+ });
84
+ const descriptions = result.intents.map(i => i.description).slice(0, 3);
85
+ const truncated = result.intents.length > 3 ? `... +${result.intents.length - 3} more` : "";
86
+ return {
87
+ inferredIntents: result.intents,
88
+ agentTimings: agentTimingsAccum,
89
+ trace: [{
90
+ node: "inference",
91
+ detail: result.intents.length === 0
92
+ ? "No intents extracted"
93
+ : `Extracted ${result.intents.length}: ${descriptions.map(d => `"${d.slice(0, 50)}${d.length > 50 ? '...' : ''}"`).join(", ")}${truncated}`,
94
+ }],
95
+ };
96
+ });
97
+ }