@indexnetwork/protocol 4.0.0-rc.289.1 → 4.1.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 (46) hide show
  1. package/dist/chat/chat.prompt.js +21 -21
  2. package/dist/chat/chat.prompt.js.map +1 -1
  3. package/dist/chat/chat.prompt.modules.js +8 -8
  4. package/dist/chat/chat.prompt.modules.js.map +1 -1
  5. package/dist/contact/contact.tools.js +3 -3
  6. package/dist/contact/contact.tools.js.map +1 -1
  7. package/dist/enrichment/enrichment.graph.js +1 -1
  8. package/dist/enrichment/enrichment.graph.js.map +1 -1
  9. package/dist/enrichment/enrichment.tools.js +47 -47
  10. package/dist/enrichment/enrichment.tools.js.map +1 -1
  11. package/dist/intent/intent.graph.d.ts.map +1 -1
  12. package/dist/intent/intent.graph.js +7 -6
  13. package/dist/intent/intent.graph.js.map +1 -1
  14. package/dist/intent/intent.tools.js +2 -2
  15. package/dist/intent/intent.tools.js.map +1 -1
  16. package/dist/mcp/mcp.server.d.ts +1 -1
  17. package/dist/mcp/mcp.server.d.ts.map +1 -1
  18. package/dist/mcp/mcp.server.js +12 -4
  19. package/dist/mcp/mcp.server.js.map +1 -1
  20. package/dist/negotiation/negotiation.tools.js +1 -1
  21. package/dist/negotiation/negotiation.tools.js.map +1 -1
  22. package/dist/network/network.tools.js +2 -2
  23. package/dist/network/network.tools.js.map +1 -1
  24. package/dist/opportunity/opportunity.graph.d.ts +8 -8
  25. package/dist/opportunity/opportunity.graph.js +7 -7
  26. package/dist/opportunity/opportunity.graph.js.map +1 -1
  27. package/dist/opportunity/opportunity.state.d.ts +2 -2
  28. package/dist/opportunity/opportunity.state.d.ts.map +1 -1
  29. package/dist/opportunity/opportunity.state.js +1 -1
  30. package/dist/opportunity/opportunity.state.js.map +1 -1
  31. package/dist/opportunity/opportunity.tools.js +8 -8
  32. package/dist/opportunity/opportunity.tools.js.map +1 -1
  33. package/dist/shared/agent/tool.registry.d.ts.map +1 -1
  34. package/dist/shared/agent/tool.registry.js +26 -0
  35. package/dist/shared/agent/tool.registry.js.map +1 -1
  36. package/dist/shared/agent/tool.runtime.d.ts.map +1 -1
  37. package/dist/shared/agent/tool.runtime.js +6 -0
  38. package/dist/shared/agent/tool.runtime.js.map +1 -1
  39. package/dist/shared/agent/utility.tools.js +5 -5
  40. package/dist/shared/agent/utility.tools.js.map +1 -1
  41. package/dist/shared/interfaces/database.interface.d.ts +2 -2
  42. package/dist/shared/interfaces/database.interface.js.map +1 -1
  43. package/dist/shared/interfaces/enrichment-run.interface.d.ts +1 -1
  44. package/dist/shared/interfaces/enrichment-run.interface.d.ts.map +1 -1
  45. package/dist/shared/interfaces/enrichment-run.interface.js.map +1 -1
  46. package/package.json +1 -1
@@ -6,7 +6,7 @@ import { socialsToEnrichmentRequest, detectSocialLabel } from "../shared/utils/s
6
6
  import { normalizeTelegramHandle } from "../shared/utils/telegram-handle.js";
7
7
  import { EnrichmentGenerator } from "./enrichment.generator.js";
8
8
  import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
9
- const logger = protocolLogger("ChatTools:Profile");
9
+ const logger = protocolLogger("ChatTools:Enrichment");
10
10
  function isMeaningfulEnrichment(enrichment) {
11
11
  return !!enrichment &&
12
12
  enrichment.confidentMatch &&
@@ -211,7 +211,7 @@ export function createEnrichmentTools(defineTool, deps) {
211
211
  input,
212
212
  forceUpdate: true,
213
213
  };
214
- // Always invoked as a background fire-and-forget task (see confirm_user_profile
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.
@@ -225,9 +225,9 @@ export function createEnrichmentTools(defineTool, deps) {
225
225
  reportToolError?.(err, {
226
226
  subsystem: 'enrichment',
227
227
  operation: 'profile.confirm_draft_decompose',
228
- toolName: 'confirm_user_profile',
228
+ toolName: 'confirm_user_context',
229
229
  userId: profile.userId,
230
- tags: { toolName: 'confirm_user_profile', execution: 'background' },
230
+ tags: { toolName: 'confirm_user_context', execution: 'background' },
231
231
  });
232
232
  return;
233
233
  }
@@ -245,17 +245,17 @@ export function createEnrichmentTools(defineTool, deps) {
245
245
  reportToolError?.(err, {
246
246
  subsystem: 'enrichment',
247
247
  operation: 'profile.confirm_draft_decompose',
248
- toolName: 'confirm_user_profile',
248
+ toolName: 'confirm_user_context',
249
249
  userId: profile.userId,
250
- tags: { toolName: 'confirm_user_profile', execution: 'background' },
250
+ tags: { toolName: 'confirm_user_context', execution: 'background' },
251
251
  });
252
252
  }
253
253
  finally {
254
254
  traceEmitter?.({ type: "graph_end", name: "enrichment", durationMs: Date.now() - graphStart });
255
255
  }
256
256
  }
257
- const readUserProfiles = defineTool({
258
- name: "read_user_profiles",
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 createEnrichmentTools(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 create_user_profile. " +
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` — " +
@@ -336,7 +336,7 @@ export function createEnrichmentTools(defineTool, deps) {
336
336
  };
337
337
  }
338
338
  catch (err) {
339
- logger.warn("read_user_profiles: getProfile failed; degrading to hasProfile=false", {
339
+ logger.warn("read_user_contexts: getProfile failed; degrading to hasProfile=false", {
340
340
  userId: m.userId,
341
341
  error: err instanceof Error ? err.message : String(err),
342
342
  });
@@ -493,11 +493,11 @@ export function createEnrichmentTools(defineTool, deps) {
493
493
  });
494
494
  },
495
495
  });
496
- const previewUserProfile = defineTool({
497
- name: "preview_user_profile",
496
+ const previewUserContext = defineTool({
497
+ name: "preview_user_context",
498
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. " +
499
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. " +
500
- "In MCP contexts, starts an async profile run and returns `profileRunId`; poll get_profile_run until status is `succeeded`, then present its `result`." +
500
+ "In MCP contexts, starts an async profile run and returns `profileRunId`; poll get_enrichment_run until status is `succeeded`, then present its `result`." +
501
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 }`.",
502
502
  querySchema: z.object({
503
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."),
@@ -514,12 +514,12 @@ export function createEnrichmentTools(defineTool, deps) {
514
514
  const user = await userDb.getUser();
515
515
  if (!user)
516
516
  return error("User not found.");
517
- const profileRunId = await enqueueEnrichmentRun(context, "preview_user_profile", query);
517
+ const profileRunId = await enqueueEnrichmentRun(context, "preview_user_context", query);
518
518
  if (profileRunId) {
519
519
  return success({
520
520
  status: "queued",
521
521
  profileRunId,
522
- message: `Profile preview started. Call get_profile_run with profileRunId="${profileRunId}" until it succeeds, fails, or is cancelled.`,
522
+ message: `Profile preview started. Call get_enrichment_run with profileRunId="${profileRunId}" until it succeeds, fails, or is cancelled.`,
523
523
  });
524
524
  }
525
525
  const onboarding = user.onboarding ?? context.user.onboarding;
@@ -568,7 +568,7 @@ export function createEnrichmentTools(defineTool, deps) {
568
568
  return success({
569
569
  preview: true,
570
570
  persisted: false,
571
- message: "Profile draft generated. Show this to the user and ask whether it looks right before calling confirm_user_profile.",
571
+ message: "Profile draft generated. Show this to the user and ask whether it looks right before calling confirm_user_context.",
572
572
  profile: toProfileSummary(profile),
573
573
  draft: profile,
574
574
  publicLookup: enrichment
@@ -587,12 +587,12 @@ export function createEnrichmentTools(defineTool, deps) {
587
587
  });
588
588
  },
589
589
  });
590
- const confirmUserProfile = defineTool({
591
- name: "confirm_user_profile",
592
- description: "Saves an explicitly approved onboarding profile draft. Call this only after the user has seen the draft from preview_user_profile and approved it or provided corrections. " +
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. " +
593
593
  "This path uses only the approved draft/explicit correction text and does not scrape or run public lookup.",
594
594
  querySchema: z.object({
595
- draft: approvedProfileDraftSchema.optional().describe("The structured profile draft returned by preview_user_profile after user approval."),
595
+ draft: approvedProfileDraftSchema.optional().describe("The structured profile draft returned by preview_user_context after user approval."),
596
596
  bioOrDescription: z.string().optional().describe("Approved correction or explicit profile text if not passing a structured draft."),
597
597
  name: z.string().optional().describe("Approved name correction."),
598
598
  location: z.string().optional().describe("Approved location correction."),
@@ -671,10 +671,10 @@ export function createEnrichmentTools(defineTool, deps) {
671
671
  });
672
672
  },
673
673
  });
674
- const createUserProfile = defineTool({
675
- name: "create_user_profile",
674
+ const createUserContext = defineTool({
675
+ name: "create_user_context",
676
676
  description: "Legacy/backward-compatible tool that creates or regenerates the authenticated user's profile. AgentVillage/Hermes onboarding must use " +
677
- "record_onboarding_privacy_consent → preview_user_profileconfirm_user_profile instead, so consent is recorded and the draft is shown before saving. " +
677
+ "record_onboarding_privacy_consent → preview_user_contextconfirm_user_context instead, so consent is recorded and the draft is shown before saving. " +
678
678
  "Profiles are essential for discovery — they provide the semantic context used to match users with complementary intents.\n\n" +
679
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, " +
680
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" +
@@ -682,7 +682,7 @@ export function createEnrichmentTools(defineTool, deps) {
682
682
  "- No args: attempts auto-generation from account data. If insufficient info, returns `missingFields` — ask the user for name/social URLs and retry.\n" +
683
683
  "- With social URLs (linkedinUrl, githubUrl, etc.): enriches from those specific URLs.\n" +
684
684
  "- With bioOrDescription: creates profile from explicit text only (no web scraping).\n" +
685
- "- Legacy onboarding clients: first call returns a preview. AgentVillage/Hermes clients should not use this preview path; use preview_user_profile instead because it does not persist enrichment side effects.\n\n" +
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" +
686
686
  "**Returns:** The generated profile (name, bio, location, skills, interests) or a `needsClarification` response listing missing fields.\n\n" +
687
687
  "**Next steps:** After profile creation, the user can create intents (create_intent) and join indexes (create_network_membership) to start discovering opportunities.",
688
688
  querySchema: z.object({
@@ -733,7 +733,7 @@ export function createEnrichmentTools(defineTool, deps) {
733
733
  if (existingProfile) {
734
734
  return success({
735
735
  alreadyExists: true,
736
- message: "Profile already exists. If the user confirmed it, call complete_onboarding() to finish setup. If they want changes, use create_user_profile(bioOrDescription=\"...\", confirm=true).",
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).",
737
737
  profile: {
738
738
  name: existingProfile.identity.name,
739
739
  bio: existingProfile.identity.bio,
@@ -776,7 +776,7 @@ export function createEnrichmentTools(defineTool, deps) {
776
776
  }
777
777
  return success({
778
778
  preview: true,
779
- message: "Profile preview generated. Call create_user_profile(confirm=true) to save.",
779
+ message: "Profile preview generated. Call create_user_context(confirm=true) to save.",
780
780
  profile: {
781
781
  name: enrichment.identity.name,
782
782
  bio: enrichment.identity.bio,
@@ -921,8 +921,8 @@ export function createEnrichmentTools(defineTool, deps) {
921
921
  return error("Failed to create profile. Please try again.");
922
922
  },
923
923
  });
924
- const updateUserProfile = defineTool({
925
- name: "update_user_profile",
924
+ const updateUserContext = defineTool({
925
+ name: "update_user_context",
926
926
  description: "Updates the authenticated user's existing profile using a verb-style instruction interface.\n\n" +
927
927
  "**How to use it:**\n" +
928
928
  "- `action`: a natural-language instruction describing what to change (e.g. \"add interests\", \"update bio\", \"remove skill\", \"set location\").\n" +
@@ -933,12 +933,12 @@ export function createEnrichmentTools(defineTool, deps) {
933
933
  "- `action=\"update bio\"`, `details=\"Product designer focused on desktop CRPG interfaces\"`\n" +
934
934
  "- `action=\"set location\"`, `details=\"Berlin\"`\n" +
935
935
  "- `socials={ telegram: \"@alice\" }` to silently add a reachable chat handle without regenerating the profile.\n\n" +
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_profile instead.\n\n" +
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" +
937
937
  "**Important:** If the user provides a URL to update from, call scrape_url first, then pass the scraped content in `details`.\n\n" +
938
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" +
939
939
  "**Returns:** Confirmation that the profile was updated or accepted for background update.",
940
940
  querySchema: z.object({
941
- profileId: z.string().optional().describe("Profile UUID from read_user_profiles. Omit to update the current user's own profile (most common usage)."),
941
+ profileId: z.string().optional().describe("Profile UUID from read_user_contexts. Omit to update the current user's own profile (most common usage)."),
942
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."),
943
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."),
944
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."),
@@ -953,12 +953,12 @@ export function createEnrichmentTools(defineTool, deps) {
953
953
  }
954
954
  return error("Please specify what to update (e.g. action: 'update bio to X') or provide socials.");
955
955
  }
956
- const profileRunId = await enqueueEnrichmentRun(context, "update_user_profile", query);
956
+ const profileRunId = await enqueueEnrichmentRun(context, "update_user_context", query);
957
957
  if (profileRunId) {
958
958
  return success({
959
959
  status: "queued",
960
960
  profileRunId,
961
- message: `Profile update started. Call get_profile_run with profileRunId="${profileRunId}" until it succeeds, fails, or is cancelled.`,
961
+ message: `Profile update started. Call get_enrichment_run with profileRunId="${profileRunId}" until it succeeds, fails, or is cancelled.`,
962
962
  });
963
963
  }
964
964
  // Use profileGraph query mode to validate profile existence and get id
@@ -969,12 +969,12 @@ export function createEnrichmentTools(defineTool, deps) {
969
969
  const _updateQueryProfileGraphMs = Date.now() - _updateQueryProfileGraphStart;
970
970
  _updateQueryProfileTraceEmitter?.({ type: "graph_end", name: "enrichment", durationMs: _updateQueryProfileGraphMs });
971
971
  if (!queryResult.readResult?.hasProfile && !queryResult.profile) {
972
- return error("You don't have a profile yet. Use create_user_profile first.");
972
+ return error("You don't have a profile yet. Use create_user_context first.");
973
973
  }
974
974
  const existingProfileId = queryResult.readResult?.profile?.id;
975
975
  const providedProfileId = query.profileId?.trim();
976
976
  if (providedProfileId && existingProfileId && providedProfileId !== existingProfileId) {
977
- return error("Invalid profileId. Use the profile id from read_user_profiles.");
977
+ return error("Invalid profileId. Use the profile id from read_user_contexts.");
978
978
  }
979
979
  if (socialUpdates.length > 0) {
980
980
  await mergeUserSocials(socialUpdates);
@@ -997,9 +997,9 @@ export function createEnrichmentTools(defineTool, deps) {
997
997
  reportToolError?.(new Error(writeResult.error), {
998
998
  subsystem: "enrichment",
999
999
  operation: "profile.update_background",
1000
- toolName: "update_user_profile",
1000
+ toolName: "update_user_context",
1001
1001
  userId: context.userId,
1002
- tags: { toolName: "update_user_profile", execution: "background" },
1002
+ tags: { toolName: "update_user_context", execution: "background" },
1003
1003
  context: { profileId: existingProfileId ?? providedProfileId },
1004
1004
  });
1005
1005
  }
@@ -1012,9 +1012,9 @@ export function createEnrichmentTools(defineTool, deps) {
1012
1012
  reportToolError?.(err, {
1013
1013
  subsystem: "enrichment",
1014
1014
  operation: "profile.update_background",
1015
- toolName: "update_user_profile",
1015
+ toolName: "update_user_context",
1016
1016
  userId: context.userId,
1017
- tags: { toolName: "update_user_profile", execution: "background" },
1017
+ tags: { toolName: "update_user_context", execution: "background" },
1018
1018
  context: { profileId: existingProfileId ?? providedProfileId },
1019
1019
  });
1020
1020
  }).finally(() => {
@@ -1053,12 +1053,12 @@ export function createEnrichmentTools(defineTool, deps) {
1053
1053
  });
1054
1054
  },
1055
1055
  });
1056
- const getProfileRun = defineTool({
1057
- name: "get_profile_run",
1058
- description: "Checks the status of an async profile preview/update run started by preview_user_profile or update_user_profile in MCP contexts. " +
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. " +
1059
1059
  "Poll this tool with the profileRunId until status is succeeded, failed, or cancelled. When succeeded, present the result to the user.",
1060
1060
  querySchema: z.object({
1061
- profileRunId: z.string().describe("Profile run ID returned by preview_user_profile or update_user_profile."),
1061
+ profileRunId: z.string().describe("Profile run ID returned by preview_user_context or update_user_context."),
1062
1062
  }),
1063
1063
  handler: async ({ context, query }) => {
1064
1064
  if (!deps.enrichmentRuns) {
@@ -1080,12 +1080,12 @@ export function createEnrichmentTools(defineTool, deps) {
1080
1080
  });
1081
1081
  },
1082
1082
  });
1083
- const cancelProfileRun = defineTool({
1084
- name: "cancel_profile_run",
1083
+ const cancelEnrichmentRun = defineTool({
1084
+ name: "cancel_enrichment_run",
1085
1085
  description: "Requests cancellation for an async profile run. If the queued job has not started, it is removed and marked cancelled. " +
1086
1086
  "If already running, the worker observes the cancellation request and aborts where supported.",
1087
1087
  querySchema: z.object({
1088
- profileRunId: z.string().describe("Profile run ID returned by preview_user_profile or update_user_profile."),
1088
+ profileRunId: z.string().describe("Profile run ID returned by preview_user_context or update_user_context."),
1089
1089
  }),
1090
1090
  handler: async ({ context, query }) => {
1091
1091
  if (!deps.enrichmentRuns || !deps.enrichmentRunQueue) {
@@ -1125,7 +1125,7 @@ export function createEnrichmentTools(defineTool, deps) {
1125
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 " +
1126
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" +
1127
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" +
1128
- "**Workflow:** create_user_profile() -> user confirms preview -> create_user_profile(confirm=true) -> create_intent(..., autoApprove=true) -> complete_onboarding()\n\n" +
1128
+ "**Workflow:** create_user_context() -> user confirms preview -> create_user_context(confirm=true) -> create_intent(..., autoApprove=true) -> complete_onboarding()\n\n" +
1129
1129
  "**Returns:** Confirmation that onboarding is complete. No parameters needed.",
1130
1130
  querySchema: z.object({}),
1131
1131
  handler: async ({ context }) => {
@@ -1163,6 +1163,6 @@ export function createEnrichmentTools(defineTool, deps) {
1163
1163
  return success({ message: "Onboarding complete." });
1164
1164
  },
1165
1165
  });
1166
- return [readUserProfiles, recordOnboardingPrivacyConsent, previewUserProfile, confirmUserProfile, createUserProfile, updateUserProfile, getProfileRun, cancelProfileRun, completeOnboarding];
1166
+ return [readUserContexts, recordOnboardingPrivacyConsent, previewUserContext, confirmUserContext, createUserContext, updateUserContext, getEnrichmentRun, cancelEnrichmentRun, completeOnboarding];
1167
1167
  }
1168
1168
  //# sourceMappingURL=enrichment.tools.js.map