@indexnetwork/protocol 3.13.0-rc.288.1 → 4.1.1-rc.290.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat/chat.prompt.js +25 -25
- package/dist/chat/chat.prompt.js.map +1 -1
- package/dist/chat/chat.prompt.modules.js +8 -8
- package/dist/chat/chat.prompt.modules.js.map +1 -1
- package/dist/chat/tests/chat.graph.mocks.d.ts +1 -7
- package/dist/chat/tests/chat.graph.mocks.d.ts.map +1 -1
- package/dist/chat/tests/chat.graph.mocks.js +1 -2
- package/dist/chat/tests/chat.graph.mocks.js.map +1 -1
- package/dist/contact/contact.tools.js +3 -3
- package/dist/contact/contact.tools.js.map +1 -1
- package/dist/{profile/profile.enricher.d.ts → enrichment/enrichment.enricher.d.ts} +1 -1
- package/dist/enrichment/enrichment.enricher.d.ts.map +1 -0
- package/dist/{profile/profile.enricher.js → enrichment/enrichment.enricher.js} +1 -1
- package/dist/enrichment/enrichment.enricher.js.map +1 -0
- package/dist/{profile/profile.generator.d.ts → enrichment/enrichment.generator.d.ts} +3 -3
- package/dist/enrichment/enrichment.generator.d.ts.map +1 -0
- package/dist/{profile/profile.generator.js → enrichment/enrichment.generator.js} +6 -6
- package/dist/enrichment/enrichment.generator.js.map +1 -0
- package/dist/{profile/profile.graph.d.ts → enrichment/enrichment.graph.d.ts} +61 -201
- package/dist/enrichment/enrichment.graph.d.ts.map +1 -0
- package/dist/{profile/profile.graph.js → enrichment/enrichment.graph.js} +7 -7
- package/dist/enrichment/enrichment.graph.js.map +1 -0
- package/dist/{profile/profile.state.d.ts → enrichment/enrichment.state.d.ts} +8 -32
- package/dist/enrichment/enrichment.state.d.ts.map +1 -0
- package/dist/{profile/profile.state.js → enrichment/enrichment.state.js} +2 -2
- package/dist/enrichment/enrichment.state.js.map +1 -0
- package/dist/enrichment/enrichment.tools.d.ts +3 -0
- package/dist/enrichment/enrichment.tools.d.ts.map +1 -0
- package/dist/{profile/profile.tools.js → enrichment/enrichment.tools.js} +119 -132
- package/dist/enrichment/enrichment.tools.js.map +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/intent/intent.graph.d.ts.map +1 -1
- package/dist/intent/intent.graph.js +7 -6
- package/dist/intent/intent.graph.js.map +1 -1
- package/dist/intent/intent.tools.js +5 -5
- package/dist/intent/intent.tools.js.map +1 -1
- package/dist/mcp/mcp.server.d.ts +1 -1
- package/dist/mcp/mcp.server.d.ts.map +1 -1
- package/dist/mcp/mcp.server.js +12 -4
- package/dist/mcp/mcp.server.js.map +1 -1
- package/dist/negotiation/negotiation.tools.js +1 -1
- package/dist/negotiation/negotiation.tools.js.map +1 -1
- package/dist/network/network.tools.js +2 -2
- package/dist/network/network.tools.js.map +1 -1
- package/dist/opportunity/opportunity.graph.d.ts +9 -15
- package/dist/opportunity/opportunity.graph.d.ts.map +1 -1
- package/dist/opportunity/opportunity.graph.js +12 -29
- package/dist/opportunity/opportunity.graph.js.map +1 -1
- package/dist/opportunity/opportunity.presenter.d.ts.map +1 -1
- package/dist/opportunity/opportunity.presenter.js +3 -11
- package/dist/opportunity/opportunity.presenter.js.map +1 -1
- package/dist/opportunity/opportunity.state.d.ts +4 -10
- package/dist/opportunity/opportunity.state.d.ts.map +1 -1
- package/dist/opportunity/opportunity.state.js +1 -1
- package/dist/opportunity/opportunity.state.js.map +1 -1
- package/dist/opportunity/opportunity.tools.js +8 -8
- package/dist/opportunity/opportunity.tools.js.map +1 -1
- package/dist/questioner/questioner.presets.js +1 -1
- package/dist/questioner/questioner.presets.js.map +1 -1
- package/dist/questioner/questioner.tools.d.ts +1 -1
- package/dist/questioner/questioner.tools.js +2 -2
- package/dist/questioner/questioner.tools.js.map +1 -1
- package/dist/shared/agent/tool.factory.js +6 -6
- package/dist/shared/agent/tool.factory.js.map +1 -1
- package/dist/shared/agent/tool.helpers.d.ts +8 -8
- package/dist/shared/agent/tool.helpers.d.ts.map +1 -1
- package/dist/shared/agent/tool.helpers.js.map +1 -1
- package/dist/shared/agent/tool.registry.d.ts.map +1 -1
- package/dist/shared/agent/tool.registry.js +28 -2
- package/dist/shared/agent/tool.registry.js.map +1 -1
- package/dist/shared/agent/tool.runtime.d.ts.map +1 -1
- package/dist/shared/agent/tool.runtime.js +6 -0
- package/dist/shared/agent/tool.runtime.js.map +1 -1
- package/dist/shared/agent/utility.tools.js +5 -5
- package/dist/shared/agent/utility.tools.js.map +1 -1
- package/dist/shared/hyde/hyde.graph.d.ts +6 -6
- package/dist/shared/hyde/hyde.state.d.ts +2 -2
- package/dist/shared/hyde/hyde.state.js.map +1 -1
- package/dist/shared/interfaces/database.interface.d.ts +16 -16
- package/dist/shared/interfaces/database.interface.d.ts.map +1 -1
- package/dist/shared/interfaces/database.interface.js.map +1 -1
- package/dist/shared/interfaces/{profile-run.interface.d.ts → enrichment-run.interface.d.ts} +21 -21
- package/dist/shared/interfaces/enrichment-run.interface.d.ts.map +1 -0
- package/dist/shared/interfaces/enrichment-run.interface.js +2 -0
- package/dist/shared/interfaces/enrichment-run.interface.js.map +1 -0
- package/dist/shared/schemas/discovery-question.schema.d.ts +2 -2
- package/dist/shared/schemas/identity.schema.d.ts +45 -0
- package/dist/shared/schemas/identity.schema.d.ts.map +1 -0
- package/dist/shared/schemas/identity.schema.js +20 -0
- package/dist/shared/schemas/identity.schema.js.map +1 -0
- package/dist/shared/schemas/question.schema.d.ts +4 -4
- package/dist/shared/schemas/question.schema.d.ts.map +1 -1
- package/dist/shared/schemas/question.schema.js +1 -1
- package/dist/shared/schemas/question.schema.js.map +1 -1
- package/package.json +1 -1
- package/dist/profile/profile.enricher.d.ts.map +0 -1
- package/dist/profile/profile.enricher.js.map +0 -1
- package/dist/profile/profile.generator.d.ts.map +0 -1
- package/dist/profile/profile.generator.js.map +0 -1
- package/dist/profile/profile.graph.d.ts.map +0 -1
- package/dist/profile/profile.graph.js.map +0 -1
- package/dist/profile/profile.state.d.ts.map +0 -1
- package/dist/profile/profile.state.js.map +0 -1
- package/dist/profile/profile.tools.d.ts +0 -3
- package/dist/profile/profile.tools.d.ts.map +0 -1
- package/dist/profile/profile.tools.js.map +0 -1
- package/dist/shared/interfaces/profile-run.interface.d.ts.map +0 -1
- package/dist/shared/interfaces/profile-run.interface.js +0 -2
- package/dist/shared/interfaces/profile-run.interface.js.map +0 -1
- package/dist/shared/schemas/profile.schema.d.ts +0 -100
- package/dist/shared/schemas/profile.schema.d.ts.map +0 -1
- package/dist/shared/schemas/profile.schema.js +0 -26
- package/dist/shared/schemas/profile.schema.js.map +0 -1
|
@@ -4,9 +4,9 @@ import { success, error, needsClarification, UUID_REGEX } from "../shared/agent/
|
|
|
4
4
|
import { protocolLogger } from "../shared/observability/protocol.logger.js";
|
|
5
5
|
import { socialsToEnrichmentRequest, detectSocialLabel } from "../shared/utils/social-label.js";
|
|
6
6
|
import { normalizeTelegramHandle } from "../shared/utils/telegram-handle.js";
|
|
7
|
-
import {
|
|
7
|
+
import { EnrichmentGenerator } from "./enrichment.generator.js";
|
|
8
8
|
import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
|
|
9
|
-
const logger = protocolLogger("ChatTools:
|
|
9
|
+
const logger = protocolLogger("ChatTools:Enrichment");
|
|
10
10
|
function isMeaningfulEnrichment(enrichment) {
|
|
11
11
|
return !!enrichment &&
|
|
12
12
|
enrichment.confidentMatch &&
|
|
@@ -20,7 +20,7 @@ const approvedProfileDraftSchema = z.object({
|
|
|
20
20
|
narrative: z.object({ context: z.string() }),
|
|
21
21
|
attributes: z.object({ interests: z.array(z.string()), skills: z.array(z.string()) }),
|
|
22
22
|
});
|
|
23
|
-
export function
|
|
23
|
+
export function createEnrichmentTools(defineTool, deps) {
|
|
24
24
|
const { userDb, systemDb, graphs, enricher, grantDefaultSystemPermissions, reportToolError, getUserContextText } = deps;
|
|
25
25
|
function trimToUndefined(value) {
|
|
26
26
|
const trimmed = value?.trim();
|
|
@@ -104,10 +104,10 @@ export function createProfileTools(defineTool, deps) {
|
|
|
104
104
|
return [];
|
|
105
105
|
return Object.entries(socials).map(([label, value]) => ({ label, value }));
|
|
106
106
|
}
|
|
107
|
-
async function
|
|
108
|
-
if (!context.isMcp || !deps.
|
|
107
|
+
async function enqueueEnrichmentRun(context, operation, input) {
|
|
108
|
+
if (!context.isMcp || !deps.enrichmentRuns || !deps.enrichmentRunQueue)
|
|
109
109
|
return null;
|
|
110
|
-
const run = await deps.
|
|
110
|
+
const run = await deps.enrichmentRuns.create({
|
|
111
111
|
userId: context.userId,
|
|
112
112
|
agentId: context.agentId ?? null,
|
|
113
113
|
operation,
|
|
@@ -125,11 +125,11 @@ export function createProfileTools(defineTool, deps) {
|
|
|
125
125
|
},
|
|
126
126
|
});
|
|
127
127
|
try {
|
|
128
|
-
await deps.
|
|
128
|
+
await deps.enrichmentRunQueue.enqueue(run.id);
|
|
129
129
|
}
|
|
130
130
|
catch (err) {
|
|
131
131
|
const message = err instanceof Error ? err.message : String(err);
|
|
132
|
-
await deps.
|
|
132
|
+
await deps.enrichmentRuns.markFailed(run.id, message);
|
|
133
133
|
if (err instanceof Error)
|
|
134
134
|
throw err;
|
|
135
135
|
const wrapped = new Error(`Failed to enqueue profile run: ${message}`);
|
|
@@ -203,7 +203,7 @@ export function createProfileTools(defineTool, deps) {
|
|
|
203
203
|
return;
|
|
204
204
|
const traceEmitter = requestContext.getStore()?.traceEmitter;
|
|
205
205
|
const graphStart = Date.now();
|
|
206
|
-
traceEmitter?.({ type: "graph_start", name: "
|
|
206
|
+
traceEmitter?.({ type: "graph_start", name: "enrichment" });
|
|
207
207
|
try {
|
|
208
208
|
const graphInput = {
|
|
209
209
|
userId: profile.userId,
|
|
@@ -211,7 +211,7 @@ export function createProfileTools(defineTool, deps) {
|
|
|
211
211
|
input,
|
|
212
212
|
forceUpdate: true,
|
|
213
213
|
};
|
|
214
|
-
// Always invoked as a background fire-and-forget task (see
|
|
214
|
+
// Always invoked as a background fire-and-forget task (see confirm_user_context
|
|
215
215
|
// call sites), so decomposition must outlive the originating request — invoke
|
|
216
216
|
// the graph directly and never bind the request abort signal, which would
|
|
217
217
|
// cancel it as soon as the web request completes.
|
|
@@ -223,11 +223,11 @@ export function createProfileTools(defineTool, deps) {
|
|
|
223
223
|
error: result.error,
|
|
224
224
|
});
|
|
225
225
|
reportToolError?.(err, {
|
|
226
|
-
subsystem: '
|
|
226
|
+
subsystem: 'enrichment',
|
|
227
227
|
operation: 'profile.confirm_draft_decompose',
|
|
228
|
-
toolName: '
|
|
228
|
+
toolName: 'confirm_user_context',
|
|
229
229
|
userId: profile.userId,
|
|
230
|
-
tags: { toolName: '
|
|
230
|
+
tags: { toolName: 'confirm_user_context', execution: 'background' },
|
|
231
231
|
});
|
|
232
232
|
return;
|
|
233
233
|
}
|
|
@@ -243,19 +243,19 @@ export function createProfileTools(defineTool, deps) {
|
|
|
243
243
|
error: err instanceof Error ? err.message : String(err),
|
|
244
244
|
});
|
|
245
245
|
reportToolError?.(err, {
|
|
246
|
-
subsystem: '
|
|
246
|
+
subsystem: 'enrichment',
|
|
247
247
|
operation: 'profile.confirm_draft_decompose',
|
|
248
|
-
toolName: '
|
|
248
|
+
toolName: 'confirm_user_context',
|
|
249
249
|
userId: profile.userId,
|
|
250
|
-
tags: { toolName: '
|
|
250
|
+
tags: { toolName: 'confirm_user_context', execution: 'background' },
|
|
251
251
|
});
|
|
252
252
|
}
|
|
253
253
|
finally {
|
|
254
|
-
traceEmitter?.({ type: "graph_end", name: "
|
|
254
|
+
traceEmitter?.({ type: "graph_end", name: "enrichment", durationMs: Date.now() - graphStart });
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
|
-
const
|
|
258
|
-
name: "
|
|
257
|
+
const readUserContexts = defineTool({
|
|
258
|
+
name: "read_user_contexts",
|
|
259
259
|
description: "Retrieves user profiles containing identity info (name, bio, location) plus a rich `context` paragraph (the user's synthesized identity text). " +
|
|
260
260
|
"Profiles are used for semantic matching in opportunity discovery — the richer the user's context, the better the matches.\n\n" +
|
|
261
261
|
"**Usage modes:**\n" +
|
|
@@ -265,7 +265,7 @@ export function createProfileTools(defineTool, deps) {
|
|
|
265
265
|
"- With `networkId` alone: returns thin-identity profiles of ALL members in that index (no `context`).\n" +
|
|
266
266
|
"- No parameters: returns the current user's own profile, including their `context`.\n\n" +
|
|
267
267
|
"**When to use:** Before creating introductions (need profiles of both parties), when the user asks about a person, " +
|
|
268
|
-
"or to check if a profile exists before suggesting
|
|
268
|
+
"or to check if a profile exists before suggesting create_user_context. " +
|
|
269
269
|
"MCP agents should call this with no arguments at session start to fetch the caller's profile AND onboarding status.\n\n" +
|
|
270
270
|
"**Returns:** Profile objects with name, bio, location, and (for single-user reads) a `context` paragraph. Use userId from results with other tools like read_intents(userId, networkId). " +
|
|
271
271
|
"When called for the current user (no args, or userId=self), the response also includes `onboardingComplete: boolean` and `onboardingCompletedAt?: string` — " +
|
|
@@ -324,23 +324,19 @@ export function createProfileTools(defineTool, deps) {
|
|
|
324
324
|
const profiles = await Promise.all(matched.map(async (m) => {
|
|
325
325
|
try {
|
|
326
326
|
const profile = await systemDb.getProfile(m.userId);
|
|
327
|
-
//
|
|
328
|
-
// identity text (global user_context) is fetched per-user via a userId read.
|
|
327
|
+
// Flat thin identity for list results. skills/interests are retired; the
|
|
328
|
+
// rich identity text (global user_context) is fetched per-user via a userId read.
|
|
329
329
|
return {
|
|
330
330
|
userId: m.userId,
|
|
331
331
|
name: m.name,
|
|
332
332
|
hasProfile: !!profile,
|
|
333
|
-
profile
|
|
334
|
-
? {
|
|
335
|
-
|
|
336
|
-
bio: profile.identity.bio,
|
|
337
|
-
location: profile.identity.location,
|
|
338
|
-
}
|
|
339
|
-
: undefined,
|
|
333
|
+
...(profile
|
|
334
|
+
? { bio: profile.identity.bio, location: profile.identity.location }
|
|
335
|
+
: {}),
|
|
340
336
|
};
|
|
341
337
|
}
|
|
342
338
|
catch (err) {
|
|
343
|
-
logger.warn("
|
|
339
|
+
logger.warn("read_user_contexts: getProfile failed; degrading to hasProfile=false", {
|
|
344
340
|
userId: m.userId,
|
|
345
341
|
error: err instanceof Error ? err.message : String(err),
|
|
346
342
|
});
|
|
@@ -367,19 +363,15 @@ export function createProfileTools(defineTool, deps) {
|
|
|
367
363
|
const members = await systemDb.getNetworkMembers(effectiveIndexId);
|
|
368
364
|
const profiles = await Promise.all(members.map(async (member) => {
|
|
369
365
|
const profile = await systemDb.getProfile(member.userId);
|
|
370
|
-
//
|
|
366
|
+
// Flat thin identity for roster results. skills/interests are retired; fetch a
|
|
371
367
|
// member's global user_context text via a single-user (userId) read.
|
|
372
368
|
return {
|
|
373
369
|
userId: member.userId,
|
|
374
370
|
name: member.name,
|
|
375
371
|
hasProfile: !!profile,
|
|
376
|
-
profile
|
|
377
|
-
? {
|
|
378
|
-
|
|
379
|
-
bio: profile.identity.bio,
|
|
380
|
-
location: profile.identity.location,
|
|
381
|
-
}
|
|
382
|
-
: undefined,
|
|
372
|
+
...(profile
|
|
373
|
+
? { bio: profile.identity.bio, location: profile.identity.location }
|
|
374
|
+
: {}),
|
|
383
375
|
};
|
|
384
376
|
}));
|
|
385
377
|
return success({ networkId: effectiveIndexId, memberCount: members.length, profiles });
|
|
@@ -403,12 +395,10 @@ export function createProfileTools(defineTool, deps) {
|
|
|
403
395
|
const context = getUserContextText ? await getUserContextText(targetUserId) : '';
|
|
404
396
|
return success({
|
|
405
397
|
hasProfile: true,
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
context,
|
|
411
|
-
},
|
|
398
|
+
name: profile.identity.name,
|
|
399
|
+
bio: profile.identity.bio,
|
|
400
|
+
location: profile.identity.location,
|
|
401
|
+
context,
|
|
412
402
|
});
|
|
413
403
|
}
|
|
414
404
|
return success({ hasProfile: false, message: "This user does not have a profile yet." });
|
|
@@ -416,13 +406,13 @@ export function createProfileTools(defineTool, deps) {
|
|
|
416
406
|
// --- Mode 1: No args / self → use profileGraph query (returns id for updates) ---
|
|
417
407
|
const _readProfileGraphStart = Date.now();
|
|
418
408
|
const _readProfileTraceEmitter = requestContext.getStore()?.traceEmitter;
|
|
419
|
-
_readProfileTraceEmitter?.({ type: "graph_start", name: "
|
|
409
|
+
_readProfileTraceEmitter?.({ type: "graph_start", name: "enrichment" });
|
|
420
410
|
const result = await invokeWithAbortSignal(graphs.profile, {
|
|
421
411
|
userId: context.userId,
|
|
422
412
|
operationMode: 'query',
|
|
423
413
|
});
|
|
424
414
|
const _readProfileGraphMs = Date.now() - _readProfileGraphStart;
|
|
425
|
-
_readProfileTraceEmitter?.({ type: "graph_end", name: "
|
|
415
|
+
_readProfileTraceEmitter?.({ type: "graph_end", name: "enrichment", durationMs: _readProfileGraphMs });
|
|
426
416
|
// Self-lookup includes onboarding status so MCP agents (e.g. Edge Claw)
|
|
427
417
|
// can decide whether to run the onboarding flow without depending on
|
|
428
418
|
// local-only state like a workspace BOOTSTRAP.md file.
|
|
@@ -435,29 +425,28 @@ export function createProfileTools(defineTool, deps) {
|
|
|
435
425
|
// Augment the graph's thin-identity readResult with the caller's global
|
|
436
426
|
// user_context text (the rich, profile-replacing identity paragraph).
|
|
437
427
|
const readResult = result.readResult;
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
: readResult
|
|
441
|
-
|
|
428
|
+
// Flatten identity fields up; drop the nested `profile` object (WS11).
|
|
429
|
+
const flat = readResult.hasProfile && readResult.profile
|
|
430
|
+
? { hasProfile: true, ...readResult.profile, context: getUserContextText ? await getUserContextText(context.userId) : '' }
|
|
431
|
+
: { ...readResult };
|
|
432
|
+
return success({ ...flat, ...onboardingFields, _graphTimings: [{ name: 'enrichment', durationMs: _readProfileGraphMs, agents: result.agentTimings ?? [] }] });
|
|
442
433
|
}
|
|
443
434
|
if (result.profile) {
|
|
444
435
|
return success({
|
|
445
436
|
hasProfile: true,
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
context: getUserContextText ? await getUserContextText(context.userId) : '',
|
|
451
|
-
},
|
|
437
|
+
name: result.profile.identity.name,
|
|
438
|
+
bio: result.profile.identity.bio,
|
|
439
|
+
location: result.profile.identity.location,
|
|
440
|
+
context: getUserContextText ? await getUserContextText(context.userId) : '',
|
|
452
441
|
...onboardingFields,
|
|
453
|
-
_graphTimings: [{ name: '
|
|
442
|
+
_graphTimings: [{ name: 'enrichment', durationMs: _readProfileGraphMs, agents: result.agentTimings ?? [] }],
|
|
454
443
|
});
|
|
455
444
|
}
|
|
456
445
|
return success({
|
|
457
446
|
hasProfile: false,
|
|
458
447
|
...onboardingFields,
|
|
459
448
|
message: "You don't have a profile yet. Would you like to create one? You can share your LinkedIn, GitHub, or X/Twitter profile, or just tell me about yourself.",
|
|
460
|
-
_graphTimings: [{ name: '
|
|
449
|
+
_graphTimings: [{ name: 'enrichment', durationMs: _readProfileGraphMs, agents: result.agentTimings ?? [] }],
|
|
461
450
|
});
|
|
462
451
|
},
|
|
463
452
|
});
|
|
@@ -504,11 +493,11 @@ export function createProfileTools(defineTool, deps) {
|
|
|
504
493
|
});
|
|
505
494
|
},
|
|
506
495
|
});
|
|
507
|
-
const
|
|
508
|
-
name: "
|
|
496
|
+
const previewUserContext = defineTool({
|
|
497
|
+
name: "preview_user_context",
|
|
509
498
|
description: "Builds a structured profile draft for onboarding without saving anything. Use this after recording privacy consent and before asking the user to approve the profile. " +
|
|
510
499
|
"If allowPublicLookup is false, this tool uses only explicit text, EdgeOS/event data the user allowed, and user-provided social URLs. If allowPublicLookup is true, persisted public lookup consent is required. " +
|
|
511
|
-
"In MCP contexts, starts an async profile run and returns `profileRunId`; poll
|
|
500
|
+
"In MCP contexts, starts an async profile run and returns `profileRunId`; poll get_enrichment_run until status is `succeeded`, then present its `result`." +
|
|
512
501
|
" When public lookup runs, the result includes a `publicLookup` block reporting whether a candidate identity was found (`used`, `confidentMatch`) and what it was (`identity` of name/role/location, plus `socials`), so the caller can confirm identity before saving. A candidate can be returned (`used: true`) without being confident enough to enter the draft; when no lookup runs the block is `{ used: false }`.",
|
|
513
502
|
querySchema: z.object({
|
|
514
503
|
name: z.string().optional().describe("Name explicitly provided by the user. For authenticated public lookup, the account identity is used first and this is only a fallback."),
|
|
@@ -525,12 +514,12 @@ export function createProfileTools(defineTool, deps) {
|
|
|
525
514
|
const user = await userDb.getUser();
|
|
526
515
|
if (!user)
|
|
527
516
|
return error("User not found.");
|
|
528
|
-
const profileRunId = await
|
|
517
|
+
const profileRunId = await enqueueEnrichmentRun(context, "preview_user_context", query);
|
|
529
518
|
if (profileRunId) {
|
|
530
519
|
return success({
|
|
531
520
|
status: "queued",
|
|
532
521
|
profileRunId,
|
|
533
|
-
message: `Profile preview started. Call
|
|
522
|
+
message: `Profile preview started. Call get_enrichment_run with profileRunId="${profileRunId}" until it succeeds, fails, or is cancelled.`,
|
|
534
523
|
});
|
|
535
524
|
}
|
|
536
525
|
const onboarding = user.onboarding ?? context.user.onboarding;
|
|
@@ -574,12 +563,12 @@ export function createProfileTools(defineTool, deps) {
|
|
|
574
563
|
message: "Please share a short description, allowed EdgeOS profile text, or user-provided profile links so I can draft your profile.",
|
|
575
564
|
});
|
|
576
565
|
}
|
|
577
|
-
const generated = await new
|
|
566
|
+
const generated = await new EnrichmentGenerator().invoke(input);
|
|
578
567
|
const profile = { ...generated.output, userId: context.userId };
|
|
579
568
|
return success({
|
|
580
569
|
preview: true,
|
|
581
570
|
persisted: false,
|
|
582
|
-
message: "Profile draft generated. Show this to the user and ask whether it looks right before calling
|
|
571
|
+
message: "Profile draft generated. Show this to the user and ask whether it looks right before calling confirm_user_context.",
|
|
583
572
|
profile: toProfileSummary(profile),
|
|
584
573
|
draft: profile,
|
|
585
574
|
publicLookup: enrichment
|
|
@@ -598,12 +587,12 @@ export function createProfileTools(defineTool, deps) {
|
|
|
598
587
|
});
|
|
599
588
|
},
|
|
600
589
|
});
|
|
601
|
-
const
|
|
602
|
-
name: "
|
|
603
|
-
description: "Saves an explicitly approved onboarding profile draft. Call this only after the user has seen the draft from
|
|
590
|
+
const confirmUserContext = defineTool({
|
|
591
|
+
name: "confirm_user_context",
|
|
592
|
+
description: "Saves an explicitly approved onboarding profile draft. Call this only after the user has seen the draft from preview_user_context and approved it or provided corrections. " +
|
|
604
593
|
"This path uses only the approved draft/explicit correction text and does not scrape or run public lookup.",
|
|
605
594
|
querySchema: z.object({
|
|
606
|
-
draft: approvedProfileDraftSchema.optional().describe("The structured profile draft returned by
|
|
595
|
+
draft: approvedProfileDraftSchema.optional().describe("The structured profile draft returned by preview_user_context after user approval."),
|
|
607
596
|
bioOrDescription: z.string().optional().describe("Approved correction or explicit profile text if not passing a structured draft."),
|
|
608
597
|
name: z.string().optional().describe("Approved name correction."),
|
|
609
598
|
location: z.string().optional().describe("Approved location correction."),
|
|
@@ -612,7 +601,7 @@ export function createProfileTools(defineTool, deps) {
|
|
|
612
601
|
const user = await userDb.getUser();
|
|
613
602
|
if (query.draft) {
|
|
614
603
|
const profile = { ...query.draft, userId: context.userId };
|
|
615
|
-
await userDb.saveProfile(profile);
|
|
604
|
+
await userDb.saveProfile({ userId: context.userId, identity: profile.identity, context: profile.narrative?.context ?? '' });
|
|
616
605
|
await persistApprovedProfileContext(profile, user, context.networkId);
|
|
617
606
|
const decomposeLogLabel = context.isMcp
|
|
618
607
|
? 'Approved draft premise decomposition failed'
|
|
@@ -652,7 +641,7 @@ export function createProfileTools(defineTool, deps) {
|
|
|
652
641
|
await persistApprovedProfileContext(rawProfile, user, context.networkId);
|
|
653
642
|
const _confirmTraceEmitter = requestContext.getStore()?.traceEmitter;
|
|
654
643
|
const _confirmGraphStart = Date.now();
|
|
655
|
-
_confirmTraceEmitter?.({ type: "graph_start", name: "
|
|
644
|
+
_confirmTraceEmitter?.({ type: "graph_start", name: "enrichment" });
|
|
656
645
|
graphs.profile.invoke({
|
|
657
646
|
userId: context.userId,
|
|
658
647
|
operationMode: 'write',
|
|
@@ -670,7 +659,7 @@ export function createProfileTools(defineTool, deps) {
|
|
|
670
659
|
error: err instanceof Error ? err.message : String(err),
|
|
671
660
|
})).finally(() => {
|
|
672
661
|
const _confirmGraphMs = Date.now() - _confirmGraphStart;
|
|
673
|
-
_confirmTraceEmitter?.({ type: "graph_end", name: "
|
|
662
|
+
_confirmTraceEmitter?.({ type: "graph_end", name: "enrichment", durationMs: _confirmGraphMs });
|
|
674
663
|
});
|
|
675
664
|
return success({
|
|
676
665
|
created: true,
|
|
@@ -682,10 +671,10 @@ export function createProfileTools(defineTool, deps) {
|
|
|
682
671
|
});
|
|
683
672
|
},
|
|
684
673
|
});
|
|
685
|
-
const
|
|
686
|
-
name: "
|
|
674
|
+
const createUserContext = defineTool({
|
|
675
|
+
name: "create_user_context",
|
|
687
676
|
description: "Legacy/backward-compatible tool that creates or regenerates the authenticated user's profile. AgentVillage/Hermes onboarding must use " +
|
|
688
|
-
"record_onboarding_privacy_consent →
|
|
677
|
+
"record_onboarding_privacy_consent → preview_user_context → confirm_user_context instead, so consent is recorded and the draft is shown before saving. " +
|
|
689
678
|
"Profiles are essential for discovery — they provide the semantic context used to match users with complementary intents.\n\n" +
|
|
690
679
|
"**How it works:** For generic clients, the system can enrich profile data from public web sources (LinkedIn, GitHub, Twitter) and/or explicit user input, " +
|
|
691
680
|
"then generates a structured profile with bio, skills, interests, location, and narrative context. Do not call with no arguments in consent-required onboarding flows.\n\n" +
|
|
@@ -693,7 +682,7 @@ export function createProfileTools(defineTool, deps) {
|
|
|
693
682
|
"- No args: attempts auto-generation from account data. If insufficient info, returns `missingFields` — ask the user for name/social URLs and retry.\n" +
|
|
694
683
|
"- With social URLs (linkedinUrl, githubUrl, etc.): enriches from those specific URLs.\n" +
|
|
695
684
|
"- With bioOrDescription: creates profile from explicit text only (no web scraping).\n" +
|
|
696
|
-
"- Legacy onboarding clients: first call returns a preview. AgentVillage/Hermes clients should not use this preview path; use
|
|
685
|
+
"- Legacy onboarding clients: first call returns a preview. AgentVillage/Hermes clients should not use this preview path; use preview_user_context instead because it does not persist enrichment side effects.\n\n" +
|
|
697
686
|
"**Returns:** The generated profile (name, bio, location, skills, interests) or a `needsClarification` response listing missing fields.\n\n" +
|
|
698
687
|
"**Next steps:** After profile creation, the user can create intents (create_intent) and join indexes (create_network_membership) to start discovering opportunities.",
|
|
699
688
|
querySchema: z.object({
|
|
@@ -744,13 +733,11 @@ export function createProfileTools(defineTool, deps) {
|
|
|
744
733
|
if (existingProfile) {
|
|
745
734
|
return success({
|
|
746
735
|
alreadyExists: true,
|
|
747
|
-
message: "Profile already exists. If the user confirmed it, call complete_onboarding() to finish setup. If they want changes, use
|
|
736
|
+
message: "Profile already exists. If the user confirmed it, call complete_onboarding() to finish setup. If they want changes, use create_user_context(bioOrDescription=\"...\", confirm=true).",
|
|
748
737
|
profile: {
|
|
749
738
|
name: existingProfile.identity.name,
|
|
750
739
|
bio: existingProfile.identity.bio,
|
|
751
740
|
location: existingProfile.identity.location,
|
|
752
|
-
skills: existingProfile.attributes.skills,
|
|
753
|
-
interests: existingProfile.attributes.interests,
|
|
754
741
|
},
|
|
755
742
|
});
|
|
756
743
|
}
|
|
@@ -789,7 +776,7 @@ export function createProfileTools(defineTool, deps) {
|
|
|
789
776
|
}
|
|
790
777
|
return success({
|
|
791
778
|
preview: true,
|
|
792
|
-
message: "Profile preview generated. Call
|
|
779
|
+
message: "Profile preview generated. Call create_user_context(confirm=true) to save.",
|
|
793
780
|
profile: {
|
|
794
781
|
name: enrichment.identity.name,
|
|
795
782
|
bio: enrichment.identity.bio,
|
|
@@ -820,13 +807,13 @@ export function createProfileTools(defineTool, deps) {
|
|
|
820
807
|
try {
|
|
821
808
|
const _confirmGraphStart = Date.now();
|
|
822
809
|
const _confirmTraceEmitter = requestContext.getStore()?.traceEmitter;
|
|
823
|
-
_confirmTraceEmitter?.({ type: "graph_start", name: "
|
|
810
|
+
_confirmTraceEmitter?.({ type: "graph_start", name: "enrichment" });
|
|
824
811
|
const result = await invokeWithAbortSignal(graphs.profile, {
|
|
825
812
|
userId: context.userId,
|
|
826
813
|
operationMode: 'generate',
|
|
827
814
|
});
|
|
828
815
|
const _confirmGraphMs = Date.now() - _confirmGraphStart;
|
|
829
|
-
_confirmTraceEmitter?.({ type: "graph_end", name: "
|
|
816
|
+
_confirmTraceEmitter?.({ type: "graph_end", name: "enrichment", durationMs: _confirmGraphMs });
|
|
830
817
|
if (result.error)
|
|
831
818
|
return error(result.error);
|
|
832
819
|
if (result.profile) {
|
|
@@ -840,7 +827,7 @@ export function createProfileTools(defineTool, deps) {
|
|
|
840
827
|
skills: result.profile.attributes.skills,
|
|
841
828
|
interests: result.profile.attributes.interests,
|
|
842
829
|
},
|
|
843
|
-
_graphTimings: [{ name: '
|
|
830
|
+
_graphTimings: [{ name: 'enrichment', durationMs: _confirmGraphMs, agents: result.agentTimings ?? [] }],
|
|
844
831
|
});
|
|
845
832
|
}
|
|
846
833
|
}
|
|
@@ -854,7 +841,7 @@ export function createProfileTools(defineTool, deps) {
|
|
|
854
841
|
const hasBioOrDescription = !!query.bioOrDescription?.trim();
|
|
855
842
|
if (hasBioOrDescription) {
|
|
856
843
|
// Create/update profile from user's explicit text only; do not persist to user record
|
|
857
|
-
// Include name and location in the input if provided so the
|
|
844
|
+
// Include name and location in the input if provided so the EnrichmentGenerator can use them
|
|
858
845
|
const inputParts = [];
|
|
859
846
|
if (name)
|
|
860
847
|
inputParts.push(`Name: ${name}`);
|
|
@@ -864,7 +851,7 @@ export function createProfileTools(defineTool, deps) {
|
|
|
864
851
|
const profileInput = inputParts.join('\n');
|
|
865
852
|
const _bioProfileGraphStart = Date.now();
|
|
866
853
|
const _bioProfileTraceEmitter = requestContext.getStore()?.traceEmitter;
|
|
867
|
-
_bioProfileTraceEmitter?.({ type: "graph_start", name: "
|
|
854
|
+
_bioProfileTraceEmitter?.({ type: "graph_start", name: "enrichment" });
|
|
868
855
|
const result = await invokeWithAbortSignal(graphs.profile, {
|
|
869
856
|
userId: context.userId,
|
|
870
857
|
operationMode: 'write',
|
|
@@ -872,7 +859,7 @@ export function createProfileTools(defineTool, deps) {
|
|
|
872
859
|
forceUpdate: true,
|
|
873
860
|
});
|
|
874
861
|
const _bioProfileGraphMs = Date.now() - _bioProfileGraphStart;
|
|
875
|
-
_bioProfileTraceEmitter?.({ type: "graph_end", name: "
|
|
862
|
+
_bioProfileTraceEmitter?.({ type: "graph_end", name: "enrichment", durationMs: _bioProfileGraphMs });
|
|
876
863
|
if (result.error) {
|
|
877
864
|
return error(result.error);
|
|
878
865
|
}
|
|
@@ -887,26 +874,26 @@ export function createProfileTools(defineTool, deps) {
|
|
|
887
874
|
skills: result.profile.attributes.skills,
|
|
888
875
|
interests: result.profile.attributes.interests,
|
|
889
876
|
},
|
|
890
|
-
_graphTimings: [{ name: '
|
|
877
|
+
_graphTimings: [{ name: 'enrichment', durationMs: _bioProfileGraphMs, agents: result.agentTimings ?? [] }],
|
|
891
878
|
});
|
|
892
879
|
}
|
|
893
880
|
return success({
|
|
894
881
|
created: true,
|
|
895
882
|
message: "Profile created/updated with the information you provided.",
|
|
896
|
-
_graphTimings: [{ name: '
|
|
883
|
+
_graphTimings: [{ name: 'enrichment', durationMs: _bioProfileGraphMs, agents: result.agentTimings ?? [] }],
|
|
897
884
|
});
|
|
898
885
|
}
|
|
899
886
|
// Invoke profile graph in generate mode (uses enrichUserProfile Chat API)
|
|
900
887
|
const _generateProfileGraphStart = Date.now();
|
|
901
888
|
const _generateProfileTraceEmitter = requestContext.getStore()?.traceEmitter;
|
|
902
|
-
_generateProfileTraceEmitter?.({ type: "graph_start", name: "
|
|
889
|
+
_generateProfileTraceEmitter?.({ type: "graph_start", name: "enrichment" });
|
|
903
890
|
const result = await invokeWithAbortSignal(graphs.profile, {
|
|
904
891
|
userId: context.userId,
|
|
905
892
|
operationMode: 'generate',
|
|
906
893
|
forceUpdate: true,
|
|
907
894
|
});
|
|
908
895
|
const _generateProfileGraphMs = Date.now() - _generateProfileGraphStart;
|
|
909
|
-
_generateProfileTraceEmitter?.({ type: "graph_end", name: "
|
|
896
|
+
_generateProfileTraceEmitter?.({ type: "graph_end", name: "enrichment", durationMs: _generateProfileGraphMs });
|
|
910
897
|
// If user info is insufficient, ask conversationally
|
|
911
898
|
if (result.needsUserInfo) {
|
|
912
899
|
return needsClarification({
|
|
@@ -928,14 +915,14 @@ export function createProfileTools(defineTool, deps) {
|
|
|
928
915
|
skills: result.profile.attributes.skills,
|
|
929
916
|
interests: result.profile.attributes.interests,
|
|
930
917
|
},
|
|
931
|
-
_graphTimings: [{ name: '
|
|
918
|
+
_graphTimings: [{ name: 'enrichment', durationMs: _generateProfileGraphMs, agents: result.agentTimings ?? [] }],
|
|
932
919
|
});
|
|
933
920
|
}
|
|
934
921
|
return error("Failed to create profile. Please try again.");
|
|
935
922
|
},
|
|
936
923
|
});
|
|
937
|
-
const
|
|
938
|
-
name: "
|
|
924
|
+
const updateUserContext = defineTool({
|
|
925
|
+
name: "update_user_context",
|
|
939
926
|
description: "Updates the authenticated user's existing profile using a verb-style instruction interface.\n\n" +
|
|
940
927
|
"**How to use it:**\n" +
|
|
941
928
|
"- `action`: a natural-language instruction describing what to change (e.g. \"add interests\", \"update bio\", \"remove skill\", \"set location\").\n" +
|
|
@@ -946,12 +933,12 @@ export function createProfileTools(defineTool, deps) {
|
|
|
946
933
|
"- `action=\"update bio\"`, `details=\"Product designer focused on desktop CRPG interfaces\"`\n" +
|
|
947
934
|
"- `action=\"set location\"`, `details=\"Berlin\"`\n" +
|
|
948
935
|
"- `socials={ telegram: \"@alice\" }` to silently add a reachable chat handle without regenerating the profile.\n\n" +
|
|
949
|
-
"**When to use:** When the user wants to make specific changes without regenerating the whole profile. For full profile regeneration from social URLs, use
|
|
936
|
+
"**When to use:** When the user wants to make specific changes without regenerating the whole profile. For full profile regeneration from social URLs, use create_user_context instead.\n\n" +
|
|
950
937
|
"**Important:** If the user provides a URL to update from, call scrape_url first, then pass the scraped content in `details`.\n\n" +
|
|
951
938
|
"**MCP behavior:** For MCP clients, text/profile graph updates are accepted immediately and completed in the background to avoid transport timeouts. Social-only updates still complete synchronously.\n\n" +
|
|
952
939
|
"**Returns:** Confirmation that the profile was updated or accepted for background update.",
|
|
953
940
|
querySchema: z.object({
|
|
954
|
-
profileId: z.string().optional().describe("Profile UUID from
|
|
941
|
+
profileId: z.string().optional().describe("Profile UUID from read_user_contexts. Omit to update the current user's own profile (most common usage)."),
|
|
955
942
|
action: z.string().optional().describe("Natural language description of ALL changes to make in a single call. Examples: 'update bio to focus on AI research', 'add Python and Rust to skills', 'change location to Berlin and add machine learning to interests'. Optional when only updating socials."),
|
|
956
943
|
details: z.string().optional().describe("Additional context or content to incorporate. Use this to pass scraped URL content (from scrape_url) or longer text the user provided."),
|
|
957
944
|
socials: z.record(z.string()).optional().describe("Social handles or URLs to merge into the user profile, keyed by label. Example: { telegram: '@alice', github: 'alice' }. Existing socials with other labels are preserved."),
|
|
@@ -966,28 +953,28 @@ export function createProfileTools(defineTool, deps) {
|
|
|
966
953
|
}
|
|
967
954
|
return error("Please specify what to update (e.g. action: 'update bio to X') or provide socials.");
|
|
968
955
|
}
|
|
969
|
-
const profileRunId = await
|
|
956
|
+
const profileRunId = await enqueueEnrichmentRun(context, "update_user_context", query);
|
|
970
957
|
if (profileRunId) {
|
|
971
958
|
return success({
|
|
972
959
|
status: "queued",
|
|
973
960
|
profileRunId,
|
|
974
|
-
message: `Profile update started. Call
|
|
961
|
+
message: `Profile update started. Call get_enrichment_run with profileRunId="${profileRunId}" until it succeeds, fails, or is cancelled.`,
|
|
975
962
|
});
|
|
976
963
|
}
|
|
977
964
|
// Use profileGraph query mode to validate profile existence and get id
|
|
978
965
|
const _updateQueryProfileGraphStart = Date.now();
|
|
979
966
|
const _updateQueryProfileTraceEmitter = requestContext.getStore()?.traceEmitter;
|
|
980
|
-
_updateQueryProfileTraceEmitter?.({ type: "graph_start", name: "
|
|
967
|
+
_updateQueryProfileTraceEmitter?.({ type: "graph_start", name: "enrichment" });
|
|
981
968
|
const queryResult = await invokeWithAbortSignal(graphs.profile, { userId: context.userId, operationMode: 'query' });
|
|
982
969
|
const _updateQueryProfileGraphMs = Date.now() - _updateQueryProfileGraphStart;
|
|
983
|
-
_updateQueryProfileTraceEmitter?.({ type: "graph_end", name: "
|
|
970
|
+
_updateQueryProfileTraceEmitter?.({ type: "graph_end", name: "enrichment", durationMs: _updateQueryProfileGraphMs });
|
|
984
971
|
if (!queryResult.readResult?.hasProfile && !queryResult.profile) {
|
|
985
|
-
return error("You don't have a profile yet. Use
|
|
972
|
+
return error("You don't have a profile yet. Use create_user_context first.");
|
|
986
973
|
}
|
|
987
974
|
const existingProfileId = queryResult.readResult?.profile?.id;
|
|
988
975
|
const providedProfileId = query.profileId?.trim();
|
|
989
976
|
if (providedProfileId && existingProfileId && providedProfileId !== existingProfileId) {
|
|
990
|
-
return error("Invalid profileId. Use the profile id from
|
|
977
|
+
return error("Invalid profileId. Use the profile id from read_user_contexts.");
|
|
991
978
|
}
|
|
992
979
|
if (socialUpdates.length > 0) {
|
|
993
980
|
await mergeUserSocials(socialUpdates);
|
|
@@ -995,7 +982,7 @@ export function createProfileTools(defineTool, deps) {
|
|
|
995
982
|
if (context.isMcp) {
|
|
996
983
|
const _backgroundWriteProfileGraphStart = Date.now();
|
|
997
984
|
const _backgroundWriteProfileTraceEmitter = requestContext.getStore()?.traceEmitter;
|
|
998
|
-
_backgroundWriteProfileTraceEmitter?.({ type: "graph_start", name: "
|
|
985
|
+
_backgroundWriteProfileTraceEmitter?.({ type: "graph_start", name: "enrichment" });
|
|
999
986
|
graphs.profile.invoke({
|
|
1000
987
|
userId: context.userId,
|
|
1001
988
|
operationMode: "write",
|
|
@@ -1008,11 +995,11 @@ export function createProfileTools(defineTool, deps) {
|
|
|
1008
995
|
error: writeResult.error,
|
|
1009
996
|
});
|
|
1010
997
|
reportToolError?.(new Error(writeResult.error), {
|
|
1011
|
-
subsystem: "
|
|
998
|
+
subsystem: "enrichment",
|
|
1012
999
|
operation: "profile.update_background",
|
|
1013
|
-
toolName: "
|
|
1000
|
+
toolName: "update_user_context",
|
|
1014
1001
|
userId: context.userId,
|
|
1015
|
-
tags: { toolName: "
|
|
1002
|
+
tags: { toolName: "update_user_context", execution: "background" },
|
|
1016
1003
|
context: { profileId: existingProfileId ?? providedProfileId },
|
|
1017
1004
|
});
|
|
1018
1005
|
}
|
|
@@ -1023,29 +1010,29 @@ export function createProfileTools(defineTool, deps) {
|
|
|
1023
1010
|
error: message,
|
|
1024
1011
|
});
|
|
1025
1012
|
reportToolError?.(err, {
|
|
1026
|
-
subsystem: "
|
|
1013
|
+
subsystem: "enrichment",
|
|
1027
1014
|
operation: "profile.update_background",
|
|
1028
|
-
toolName: "
|
|
1015
|
+
toolName: "update_user_context",
|
|
1029
1016
|
userId: context.userId,
|
|
1030
|
-
tags: { toolName: "
|
|
1017
|
+
tags: { toolName: "update_user_context", execution: "background" },
|
|
1031
1018
|
context: { profileId: existingProfileId ?? providedProfileId },
|
|
1032
1019
|
});
|
|
1033
1020
|
}).finally(() => {
|
|
1034
1021
|
const _backgroundWriteProfileGraphMs = Date.now() - _backgroundWriteProfileGraphStart;
|
|
1035
|
-
_backgroundWriteProfileTraceEmitter?.({ type: "graph_end", name: "
|
|
1022
|
+
_backgroundWriteProfileTraceEmitter?.({ type: "graph_end", name: "enrichment", durationMs: _backgroundWriteProfileGraphMs });
|
|
1036
1023
|
});
|
|
1037
1024
|
return success({
|
|
1038
1025
|
accepted: true,
|
|
1039
1026
|
message: "Profile update accepted. The structured profile will refresh in the background.",
|
|
1040
1027
|
_graphTimings: [
|
|
1041
|
-
{ name: '
|
|
1028
|
+
{ name: 'enrichment', durationMs: _updateQueryProfileGraphMs, agents: queryResult.agentTimings ?? [] },
|
|
1042
1029
|
],
|
|
1043
1030
|
});
|
|
1044
1031
|
}
|
|
1045
1032
|
// Execute update directly
|
|
1046
1033
|
const _updateWriteProfileGraphStart = Date.now();
|
|
1047
1034
|
const _updateWriteProfileTraceEmitter = requestContext.getStore()?.traceEmitter;
|
|
1048
|
-
_updateWriteProfileTraceEmitter?.({ type: "graph_start", name: "
|
|
1035
|
+
_updateWriteProfileTraceEmitter?.({ type: "graph_start", name: "enrichment" });
|
|
1049
1036
|
const _writeResult = await invokeWithAbortSignal(graphs.profile, {
|
|
1050
1037
|
userId: context.userId,
|
|
1051
1038
|
operationMode: "write",
|
|
@@ -1053,31 +1040,31 @@ export function createProfileTools(defineTool, deps) {
|
|
|
1053
1040
|
forceUpdate: true,
|
|
1054
1041
|
});
|
|
1055
1042
|
const _updateWriteProfileGraphMs = Date.now() - _updateWriteProfileGraphStart;
|
|
1056
|
-
_updateWriteProfileTraceEmitter?.({ type: "graph_end", name: "
|
|
1043
|
+
_updateWriteProfileTraceEmitter?.({ type: "graph_end", name: "enrichment", durationMs: _updateWriteProfileGraphMs });
|
|
1057
1044
|
if (_writeResult.error) {
|
|
1058
1045
|
return error(_writeResult.error);
|
|
1059
1046
|
}
|
|
1060
1047
|
return success({
|
|
1061
1048
|
message: "Profile updated.",
|
|
1062
1049
|
_graphTimings: [
|
|
1063
|
-
{ name: '
|
|
1064
|
-
{ name: '
|
|
1050
|
+
{ name: 'enrichment', durationMs: _updateQueryProfileGraphMs, agents: queryResult.agentTimings ?? [] },
|
|
1051
|
+
{ name: 'enrichment', durationMs: _updateWriteProfileGraphMs, agents: _writeResult.agentTimings ?? [] },
|
|
1065
1052
|
],
|
|
1066
1053
|
});
|
|
1067
1054
|
},
|
|
1068
1055
|
});
|
|
1069
|
-
const
|
|
1070
|
-
name: "
|
|
1071
|
-
description: "Checks the status of an async profile preview/update run started by
|
|
1056
|
+
const getEnrichmentRun = defineTool({
|
|
1057
|
+
name: "get_enrichment_run",
|
|
1058
|
+
description: "Checks the status of an async profile preview/update run started by preview_user_context or update_user_context in MCP contexts. " +
|
|
1072
1059
|
"Poll this tool with the profileRunId until status is succeeded, failed, or cancelled. When succeeded, present the result to the user.",
|
|
1073
1060
|
querySchema: z.object({
|
|
1074
|
-
profileRunId: z.string().describe("Profile run ID returned by
|
|
1061
|
+
profileRunId: z.string().describe("Profile run ID returned by preview_user_context or update_user_context."),
|
|
1075
1062
|
}),
|
|
1076
1063
|
handler: async ({ context, query }) => {
|
|
1077
|
-
if (!deps.
|
|
1064
|
+
if (!deps.enrichmentRuns) {
|
|
1078
1065
|
return error("Profile run polling is not available in this environment.");
|
|
1079
1066
|
}
|
|
1080
|
-
const run = await deps.
|
|
1067
|
+
const run = await deps.enrichmentRuns.get(query.profileRunId, context.userId);
|
|
1081
1068
|
if (!run)
|
|
1082
1069
|
return error("Profile run not found.");
|
|
1083
1070
|
return success({
|
|
@@ -1093,18 +1080,18 @@ export function createProfileTools(defineTool, deps) {
|
|
|
1093
1080
|
});
|
|
1094
1081
|
},
|
|
1095
1082
|
});
|
|
1096
|
-
const
|
|
1097
|
-
name: "
|
|
1083
|
+
const cancelEnrichmentRun = defineTool({
|
|
1084
|
+
name: "cancel_enrichment_run",
|
|
1098
1085
|
description: "Requests cancellation for an async profile run. If the queued job has not started, it is removed and marked cancelled. " +
|
|
1099
1086
|
"If already running, the worker observes the cancellation request and aborts where supported.",
|
|
1100
1087
|
querySchema: z.object({
|
|
1101
|
-
profileRunId: z.string().describe("Profile run ID returned by
|
|
1088
|
+
profileRunId: z.string().describe("Profile run ID returned by preview_user_context or update_user_context."),
|
|
1102
1089
|
}),
|
|
1103
1090
|
handler: async ({ context, query }) => {
|
|
1104
|
-
if (!deps.
|
|
1091
|
+
if (!deps.enrichmentRuns || !deps.enrichmentRunQueue) {
|
|
1105
1092
|
return error("Profile run cancellation is not available in this environment.");
|
|
1106
1093
|
}
|
|
1107
|
-
const existing = await deps.
|
|
1094
|
+
const existing = await deps.enrichmentRuns.get(query.profileRunId, context.userId);
|
|
1108
1095
|
if (!existing)
|
|
1109
1096
|
return error("Profile run not found.");
|
|
1110
1097
|
if (!["queued", "running"].includes(existing.status)) {
|
|
@@ -1114,14 +1101,14 @@ export function createProfileTools(defineTool, deps) {
|
|
|
1114
1101
|
message: `Profile run is already ${existing.status}.`,
|
|
1115
1102
|
});
|
|
1116
1103
|
}
|
|
1117
|
-
const run = await deps.
|
|
1104
|
+
const run = await deps.enrichmentRuns.requestCancel(query.profileRunId, context.userId);
|
|
1118
1105
|
if (!run)
|
|
1119
1106
|
return error("Profile run not found or cannot be cancelled.");
|
|
1120
|
-
const removed = await deps.
|
|
1107
|
+
const removed = await deps.enrichmentRunQueue.cancel(run.id);
|
|
1121
1108
|
if (removed) {
|
|
1122
|
-
await deps.
|
|
1109
|
+
await deps.enrichmentRuns.markCancelled(run.id, "cancelled before worker start");
|
|
1123
1110
|
}
|
|
1124
|
-
const updated = await deps.
|
|
1111
|
+
const updated = await deps.enrichmentRuns.get(run.id, context.userId);
|
|
1125
1112
|
return success({
|
|
1126
1113
|
profileRunId: run.id,
|
|
1127
1114
|
status: updated?.status ?? run.status,
|
|
@@ -1138,7 +1125,7 @@ export function createProfileTools(defineTool, deps) {
|
|
|
1138
1125
|
"**Prerequisites:** The user must have a confirmed profile AND at least one active intent/signal. The profile must be shown to the user and explicitly approved " +
|
|
1139
1126
|
"(said 'yes', 'looks good', 'that's right', or similar). The first signal must be persisted before this tool is called; MCP/onboarding agents should call create_intent(..., autoApprove=true).\n\n" +
|
|
1140
1127
|
"**What happens:** Validates that the confirmed profile and first active intent exist, then sets completedAt timestamp on the user's onboarding record.\n\n" +
|
|
1141
|
-
"**Workflow:**
|
|
1128
|
+
"**Workflow:** create_user_context() -> user confirms preview -> create_user_context(confirm=true) -> create_intent(..., autoApprove=true) -> complete_onboarding()\n\n" +
|
|
1142
1129
|
"**Returns:** Confirmation that onboarding is complete. No parameters needed.",
|
|
1143
1130
|
querySchema: z.object({}),
|
|
1144
1131
|
handler: async ({ context }) => {
|
|
@@ -1176,6 +1163,6 @@ export function createProfileTools(defineTool, deps) {
|
|
|
1176
1163
|
return success({ message: "Onboarding complete." });
|
|
1177
1164
|
},
|
|
1178
1165
|
});
|
|
1179
|
-
return [
|
|
1166
|
+
return [readUserContexts, recordOnboardingPrivacyConsent, previewUserContext, confirmUserContext, createUserContext, updateUserContext, getEnrichmentRun, cancelEnrichmentRun, completeOnboarding];
|
|
1180
1167
|
}
|
|
1181
|
-
//# sourceMappingURL=
|
|
1168
|
+
//# sourceMappingURL=enrichment.tools.js.map
|