@anuma/sdk 6.30.2 → 6.31.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.
@@ -19,12 +19,28 @@ var noopLogger = {
19
19
  error: () => {
20
20
  }
21
21
  };
22
- var currentLogger = consoleLogger;
22
+ var LOGGER_STATE_KEY = /* @__PURE__ */ Symbol.for("@anuma/sdk/logger/v1");
23
+ var loggerGlobal = globalThis;
24
+ var fallbackLoggerState = { logger: consoleLogger };
25
+ var fallbackWarned = false;
26
+ function getLoggerState() {
27
+ try {
28
+ return loggerGlobal[LOGGER_STATE_KEY] ?? (loggerGlobal[LOGGER_STATE_KEY] = fallbackLoggerState);
29
+ } catch {
30
+ if (!fallbackWarned) {
31
+ fallbackWarned = true;
32
+ fallbackLoggerState.logger.warn(
33
+ "@anuma/sdk: the global object rejects new properties. This entrypoint keeps its own logger, so setLogger does not reach other entrypoints."
34
+ );
35
+ }
36
+ return fallbackLoggerState;
37
+ }
38
+ }
23
39
  function setLogger(logger) {
24
- currentLogger = logger;
40
+ getLoggerState().logger = logger;
25
41
  }
26
42
  function getLogger() {
27
- return currentLogger;
43
+ return getLoggerState().logger;
28
44
  }
29
45
 
30
46
  // src/lib/pii/patterns.ts
@@ -2,7 +2,7 @@ import {
2
2
  callPortalJsonCompletion,
3
3
  notifyConsolidationFallback,
4
4
  resolvePiiRedactor
5
- } from "./chunk-VD5M6QAI.mjs";
5
+ } from "./chunk-XD6WOQ5I.mjs";
6
6
  import "./chunk-GYKJOWEO.mjs";
7
7
 
8
8
  // src/lib/memory/consolidate.ts
@@ -44,13 +44,26 @@ var __decorateClass = (decorators, target, key, kind) => {
44
44
  };
45
45
 
46
46
  // src/lib/logger.ts
47
+ function getLoggerState() {
48
+ try {
49
+ return loggerGlobal[LOGGER_STATE_KEY] ?? (loggerGlobal[LOGGER_STATE_KEY] = fallbackLoggerState);
50
+ } catch {
51
+ if (!fallbackWarned) {
52
+ fallbackWarned = true;
53
+ fallbackLoggerState.logger.warn(
54
+ "@anuma/sdk: the global object rejects new properties. This entrypoint keeps its own logger, so setLogger does not reach other entrypoints."
55
+ );
56
+ }
57
+ return fallbackLoggerState;
58
+ }
59
+ }
47
60
  function setLogger(logger) {
48
- currentLogger = logger;
61
+ getLoggerState().logger = logger;
49
62
  }
50
63
  function getLogger() {
51
- return currentLogger;
64
+ return getLoggerState().logger;
52
65
  }
53
- var consoleLogger, noopLogger, currentLogger;
66
+ var consoleLogger, noopLogger, LOGGER_STATE_KEY, loggerGlobal, fallbackLoggerState, fallbackWarned;
54
67
  var init_logger = __esm({
55
68
  "src/lib/logger.ts"() {
56
69
  "use strict";
@@ -70,7 +83,10 @@ var init_logger = __esm({
70
83
  error: () => {
71
84
  }
72
85
  };
73
- currentLogger = consoleLogger;
86
+ LOGGER_STATE_KEY = /* @__PURE__ */ Symbol.for("@anuma/sdk/logger/v1");
87
+ loggerGlobal = globalThis;
88
+ fallbackLoggerState = { logger: consoleLogger };
89
+ fallbackWarned = false;
74
90
  }
75
91
  });
76
92
 
@@ -3734,6 +3750,9 @@ async function updateMessageEmbeddingOp(ctx, uniqueId, vector, embeddingModel) {
3734
3750
  await ctx.database.write(async () => {
3735
3751
  await message.update((msg) => {
3736
3752
  msg._setRaw("vector", JSON.stringify(vector));
3753
+ if (msg.origin === "chunks_discarded" && vector.length > 0) {
3754
+ msg._setRaw("origin", "message");
3755
+ }
3737
3756
  msg._setRaw("embedding_model", embeddingModel);
3738
3757
  });
3739
3758
  });
@@ -3750,6 +3769,9 @@ async function updateMessageChunksOp(ctx, uniqueId, chunks, embeddingModel) {
3750
3769
  await ctx.database.write(async () => {
3751
3770
  await message.update((msg) => {
3752
3771
  msg._setRaw("chunks", JSON.stringify(storedChunks));
3772
+ if (msg.origin === "chunks_discarded" && storedChunks.length > 0 && storedChunks.every((chunk) => chunk.vector.length > 0)) {
3773
+ msg._setRaw("origin", "message");
3774
+ }
3753
3775
  msg._setRaw("embedding_model", embeddingModel);
3754
3776
  });
3755
3777
  });
@@ -5974,6 +5996,9 @@ function clientToolToResponsesFormat(tool) {
5974
5996
  ...toolConfig.removeAfterExecution !== void 0 && {
5975
5997
  removeAfterExecution: toolConfig.removeAfterExecution
5976
5998
  },
5999
+ ...toolConfig.removeAfterResult !== void 0 && {
6000
+ removeAfterResult: toolConfig.removeAfterResult
6001
+ },
5977
6002
  ...toolConfig.executorTimeout !== void 0 && {
5978
6003
  executorTimeout: toolConfig.executorTimeout
5979
6004
  },
@@ -6007,6 +6032,9 @@ function clientToolToCompletionsFormat(tool) {
6007
6032
  ...toolConfig.removeAfterExecution !== void 0 && {
6008
6033
  removeAfterExecution: toolConfig.removeAfterExecution
6009
6034
  },
6035
+ ...toolConfig.removeAfterResult !== void 0 && {
6036
+ removeAfterResult: toolConfig.removeAfterResult
6037
+ },
6010
6038
  ...toolConfig.executorTimeout !== void 0 && {
6011
6039
  executorTimeout: toolConfig.executorTimeout
6012
6040
  },
@@ -6941,6 +6969,7 @@ function toolsToApiFormat(tools, apiType) {
6941
6969
  executor: _executor,
6942
6970
  skipContinuation: _skipContinuation,
6943
6971
  removeAfterExecution: _removeAfterExecution,
6972
+ removeAfterResult: _removeAfterResult,
6944
6973
  executorTimeout: _executorTimeout,
6945
6974
  dependsOn: _dependsOn,
6946
6975
  deAnonymizeArgs: _deAnonymizeArgs,
@@ -9385,9 +9414,11 @@ Executing tool: ${toolInfo}
9385
9414
  }
9386
9415
  if (tools && apiTools) {
9387
9416
  const successfullyExecutedNames = /* @__PURE__ */ new Set();
9417
+ const successfulResults = [];
9388
9418
  for (const r of executionResults) {
9389
9419
  if (!r.error && !isToolErrorResult(r.result) && "name" in r && r.name) {
9390
9420
  successfullyExecutedNames.add(r.name);
9421
+ successfulResults.push(r.result);
9391
9422
  }
9392
9423
  }
9393
9424
  if (successfullyExecutedNames.size > 0) {
@@ -9396,7 +9427,15 @@ Executing tool: ${toolInfo}
9396
9427
  const tc = t;
9397
9428
  const func = tc.function;
9398
9429
  const toolName = typeof func?.name === "string" ? func.name : typeof tc.name === "string" ? tc.name : void 0;
9399
- if (tc.removeAfterExecution === true && toolName && successfullyExecutedNames.has(toolName)) {
9430
+ if (!toolName) continue;
9431
+ const removeAfterResult = tc.removeAfterResult;
9432
+ if (tc.removeAfterExecution === true && successfullyExecutedNames.has(toolName) || typeof removeAfterResult === "function" && successfulResults.some((result) => {
9433
+ try {
9434
+ return removeAfterResult(result);
9435
+ } catch {
9436
+ return false;
9437
+ }
9438
+ })) {
9400
9439
  toolsToRemove.add(toolName);
9401
9440
  }
9402
9441
  }
@@ -13237,7 +13276,7 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
13237
13276
  const id = raw.id;
13238
13277
  const topics = parseTopics(raw.topics);
13239
13278
  const linked = linkedNames.get(id);
13240
- if (topics !== null && topics.length > 0 && linksDivergeFromTopics(topics, linked ?? /* @__PURE__ */ new Set())) {
13279
+ if (topics !== null && linksDivergeFromTopics(topics, linked ?? /* @__PURE__ */ new Set())) {
13241
13280
  topicsToRelinkAll.push(id);
13242
13281
  continue;
13243
13282
  }
@@ -13331,18 +13370,20 @@ async function relinkMemoryTopicsOp(ctx, memoryIds) {
13331
13370
  }
13332
13371
  const relinked = [];
13333
13372
  for (const id of Array.from(new Set(memoryIds))) {
13334
- let record;
13335
13373
  try {
13336
- record = await ctx.vaultMemoryCollection.find(id);
13337
- } catch {
13338
- continue;
13339
- }
13340
- if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) continue;
13341
- const topics = parseTopics(record.topics);
13342
- if (topics === null || topics.length === 0) continue;
13343
- try {
13344
- await relinkMemoryEntitiesFromTopicsOp(entityCtx, id, topics);
13345
- relinked.push(id);
13374
+ await ctx.database.write(async (writer) => {
13375
+ let record;
13376
+ try {
13377
+ record = await ctx.vaultMemoryCollection.find(id);
13378
+ } catch {
13379
+ return;
13380
+ }
13381
+ if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return;
13382
+ const topics = parseTopics(record.topics);
13383
+ if (topics === null) return;
13384
+ await writer.callWriter(() => relinkMemoryEntitiesFromTopicsOp(entityCtx, id, topics));
13385
+ relinked.push(id);
13386
+ });
13346
13387
  } catch (err) {
13347
13388
  getLogger().warn("[memory/topics] relink failed", err);
13348
13389
  }
@@ -21397,14 +21438,14 @@ async function autoFilterClientTools(clientTools, promptEmbeddings, cache, embed
21397
21438
  const alwaysInclude = clientTools.filter(isAlwaysIncluded);
21398
21439
  const filterCandidates = clientTools.filter((t) => !isAlwaysIncluded(t));
21399
21440
  if (filterCandidates.length === 0) {
21400
- return { tools: clientTools, activatedSetNames: /* @__PURE__ */ new Set() };
21441
+ return { tools: clientTools, activatedSetNames: /* @__PURE__ */ new Set(), matchedSetNames: /* @__PURE__ */ new Set() };
21401
21442
  }
21402
21443
  if (!promptEmbeddings && noEmbeddingsReason === "error") {
21403
- return { tools: clientTools, activatedSetNames: /* @__PURE__ */ new Set() };
21444
+ return { tools: clientTools, activatedSetNames: /* @__PURE__ */ new Set(), matchedSetNames: /* @__PURE__ */ new Set() };
21404
21445
  }
21405
21446
  if (!promptEmbeddings) {
21406
21447
  if (activeToolSets.length === 0) {
21407
- return { tools: [], activatedSetNames: /* @__PURE__ */ new Set() };
21448
+ return { tools: [], activatedSetNames: /* @__PURE__ */ new Set(), matchedSetNames: /* @__PURE__ */ new Set() };
21408
21449
  }
21409
21450
  const allSets = extraToolSets.length > 0 ? [...BUILT_IN_TOOL_SETS, ...extraToolSets] : BUILT_IN_TOOL_SETS;
21410
21451
  const activeSets = allSets.filter((s) => activeToolSets.includes(s.name));
@@ -21414,7 +21455,8 @@ async function autoFilterClientTools(clientTools, promptEmbeddings, cache, embed
21414
21455
  ...alwaysInclude,
21415
21456
  ...filterCandidates.filter((t) => stickyMembers.has(getToolName2(t)))
21416
21457
  ],
21417
- activatedSetNames: new Set(activeSets.map((s) => s.name))
21458
+ activatedSetNames: new Set(activeSets.map((s) => s.name)),
21459
+ matchedSetNames: /* @__PURE__ */ new Set()
21418
21460
  };
21419
21461
  }
21420
21462
  const toolsNeedingEmbeddings = [];
@@ -21432,7 +21474,7 @@ async function autoFilterClientTools(clientTools, promptEmbeddings, cache, embed
21432
21474
  cache.set(toolsNeedingEmbeddings[i].name, embeddings[i]);
21433
21475
  }
21434
21476
  } catch {
21435
- return { tools: clientTools, activatedSetNames: /* @__PURE__ */ new Set() };
21477
+ return { tools: clientTools, activatedSetNames: /* @__PURE__ */ new Set(), matchedSetNames: /* @__PURE__ */ new Set() };
21436
21478
  }
21437
21479
  }
21438
21480
  const pseudoServerTools = [];
@@ -21478,16 +21520,171 @@ async function autoFilterClientTools(clientTools, promptEmbeddings, cache, embed
21478
21520
  activeSetNames
21479
21521
  );
21480
21522
  const activatedSetNames = activatedToolSetNames(scores, toolSets, activeSetNames);
21523
+ const matchedSetNames = activatedToolSetNames(scores, toolSets);
21481
21524
  if (finalNames.size === 0) {
21482
- return { tools: alwaysInclude, activatedSetNames };
21525
+ return { tools: alwaysInclude, activatedSetNames, matchedSetNames };
21483
21526
  }
21484
21527
  const filtered = filterCandidates.filter((t) => {
21485
21528
  const name = getToolName2(t);
21486
21529
  return name && finalNames.has(name);
21487
21530
  });
21488
- return { tools: [...alwaysInclude, ...filtered], activatedSetNames };
21531
+ return { tools: [...alwaysInclude, ...filtered], activatedSetNames, matchedSetNames };
21532
+ }
21533
+
21534
+ // src/lib/tools/toolCatalog.ts
21535
+ var TOOL_CATALOG = {
21536
+ // Gmail (src/tools/gmail.ts)
21537
+ gmail_search_messages: { label: "Search emails", provider: "gmail", connector: "Gmail" },
21538
+ gmail_get_message: { label: "Read an email", provider: "gmail", connector: "Gmail" },
21539
+ gmail_create_draft: { label: "Draft a reply", provider: "gmail", connector: "Gmail" },
21540
+ gmail_send_message: { label: "Send an email", provider: "gmail", connector: "Gmail" },
21541
+ // Google Calendar (src/tools/googleCalendar.ts)
21542
+ google_calendar_list_events: {
21543
+ label: "Search calendar events",
21544
+ provider: "gcalendar",
21545
+ connector: "Calendar"
21546
+ },
21547
+ google_calendar_create_event: {
21548
+ label: "Create calendar event",
21549
+ provider: "gcalendar",
21550
+ connector: "Calendar"
21551
+ },
21552
+ google_calendar_update_event: {
21553
+ label: "Update calendar event",
21554
+ provider: "gcalendar",
21555
+ connector: "Calendar"
21556
+ },
21557
+ // Google Drive (src/tools/googleDrive.ts)
21558
+ google_drive_search: { label: "Search files", provider: "gdrive", connector: "Drive" },
21559
+ google_drive_list_recent: { label: "List recent files", provider: "gdrive", connector: "Drive" },
21560
+ google_drive_get_content: { label: "Read file content", provider: "gdrive", connector: "Drive" },
21561
+ google_drive_create_file: {
21562
+ label: "Create a file in Drive",
21563
+ provider: "gdrive",
21564
+ connector: "Drive"
21565
+ },
21566
+ google_drive_update_file: {
21567
+ label: "Update a Drive file",
21568
+ provider: "gdrive",
21569
+ connector: "Drive"
21570
+ },
21571
+ // Notion (src/tools/notion.ts)
21572
+ "notion-search": { label: "Search", provider: "notion", connector: "Notion" },
21573
+ "notion-fetch": { label: "Read page", provider: "notion", connector: "Notion" },
21574
+ "notion-create-pages": { label: "Create page", provider: "notion", connector: "Notion" },
21575
+ "notion-update-page": { label: "Update page", provider: "notion", connector: "Notion" },
21576
+ "notion-move-pages": { label: "Move pages", provider: "notion", connector: "Notion" },
21577
+ "notion-duplicate-page": { label: "Duplicate page", provider: "notion", connector: "Notion" },
21578
+ "notion-create-database": { label: "Create database", provider: "notion", connector: "Notion" },
21579
+ "notion-update-data-source": {
21580
+ label: "Update data source",
21581
+ provider: "notion",
21582
+ connector: "Notion"
21583
+ },
21584
+ "notion-create-comment": { label: "Add a comment", provider: "notion", connector: "Notion" },
21585
+ "notion-get-comments": { label: "Read comments", provider: "notion", connector: "Notion" },
21586
+ "notion-get-users": {
21587
+ label: "List workspace members",
21588
+ provider: "notion",
21589
+ connector: "Notion"
21590
+ },
21591
+ "notion-get-teams": { label: "List teamspaces", provider: "notion", connector: "Notion" },
21592
+ // GitHub (src/tools/github.ts)
21593
+ github_api: { label: "Use GitHub", provider: "github", connector: "GitHub" },
21594
+ github_get_authenticated_user: {
21595
+ label: "Read GitHub profile",
21596
+ provider: "github",
21597
+ connector: "GitHub"
21598
+ },
21599
+ // X / Twitter (src/tools/x.ts)
21600
+ x_get_me: { label: "Get my X profile", provider: "x", connector: "X" },
21601
+ x_get_my_posts: { label: "Read my recent posts", provider: "x", connector: "X" },
21602
+ // Dropbox (src/tools/dropbox.ts)
21603
+ dropbox_list_folders: { label: "List files", provider: "dropbox", connector: "Dropbox" },
21604
+ dropbox_get_file_content: {
21605
+ label: "Read file content",
21606
+ provider: "dropbox",
21607
+ connector: "Dropbox"
21608
+ },
21609
+ dropbox_search: { label: "Search files", provider: "dropbox", connector: "Dropbox" },
21610
+ // Slack (src/tools/slack.ts)
21611
+ slack_get_me: { label: "Get my Slack profile", provider: "slack", connector: "Slack" },
21612
+ slack_list_channels: { label: "List channels", provider: "slack", connector: "Slack" },
21613
+ slack_list_dms: { label: "List direct messages", provider: "slack", connector: "Slack" },
21614
+ slack_search_messages: { label: "Search messages", provider: "slack", connector: "Slack" },
21615
+ slack_list_users: { label: "List members", provider: "slack", connector: "Slack" },
21616
+ slack_get_channel_history: {
21617
+ label: "Read channel history",
21618
+ provider: "slack",
21619
+ connector: "Slack"
21620
+ },
21621
+ slack_get_thread_replies: {
21622
+ label: "Read thread replies",
21623
+ provider: "slack",
21624
+ connector: "Slack"
21625
+ },
21626
+ slack_post_message: { label: "Post a message", provider: "slack", connector: "Slack" }
21627
+ };
21628
+
21629
+ // src/lib/tools/selection/activeToolSets.ts
21630
+ function mergeActiveToolSets(current, derived) {
21631
+ if (derived.length === 0) return current;
21632
+ const merged = Array.from(/* @__PURE__ */ new Set([...current, ...derived]));
21633
+ return merged.length === current.length ? current : merged;
21489
21634
  }
21490
21635
 
21636
+ // src/lib/tools/selection/recentToolSets.ts
21637
+ var RECENT_TOOL_SET_TURNS = 2;
21638
+ var RECENT_TOOL_SET_CONVERSATION_LIMIT = 50;
21639
+ var CARRYABLE_TOOL_SETS = new Set(
21640
+ BUILT_IN_TOOL_SETS.filter(
21641
+ (s) => s.anchors.length > 0 && s.anchors.every((a) => a in TOOL_CATALOG)
21642
+ ).map((s) => s.name)
21643
+ );
21644
+ var recentToolSets = /* @__PURE__ */ new WeakMap();
21645
+ function touch(conversations, conversationId) {
21646
+ const turns = conversations.get(conversationId);
21647
+ if (!turns) return void 0;
21648
+ conversations.delete(conversationId);
21649
+ conversations.set(conversationId, turns);
21650
+ return turns;
21651
+ }
21652
+ function carriedToolSets(database, conversationId) {
21653
+ if (!conversationId) return [];
21654
+ const conversations = recentToolSets.get(database);
21655
+ if (!conversations) return [];
21656
+ return [...touch(conversations, conversationId)?.keys() ?? []];
21657
+ }
21658
+ function recordToolSetTurn(database, conversationId, matched) {
21659
+ if (!conversationId) return;
21660
+ let conversations = recentToolSets.get(database);
21661
+ if (!conversations) {
21662
+ conversations = /* @__PURE__ */ new Map();
21663
+ recentToolSets.set(database, conversations);
21664
+ }
21665
+ const turns = touch(conversations, conversationId) ?? /* @__PURE__ */ new Map();
21666
+ for (const [name, left] of turns) {
21667
+ if (left > 1) turns.set(name, left - 1);
21668
+ else turns.delete(name);
21669
+ }
21670
+ for (const name of matched) {
21671
+ if (CARRYABLE_TOOL_SETS.has(name)) turns.set(name, RECENT_TOOL_SET_TURNS);
21672
+ }
21673
+ if (turns.size === 0) {
21674
+ conversations.delete(conversationId);
21675
+ return;
21676
+ }
21677
+ conversations.set(conversationId, turns);
21678
+ if (conversations.size > RECENT_TOOL_SET_CONVERSATION_LIMIT) {
21679
+ const oldest = conversations.keys().next().value;
21680
+ if (oldest !== void 0) conversations.delete(oldest);
21681
+ }
21682
+ }
21683
+ function resetRecentToolSets() {
21684
+ recentToolSets = /* @__PURE__ */ new WeakMap();
21685
+ }
21686
+ onClearAllEncryptionState(resetRecentToolSets);
21687
+
21491
21688
  // src/expo/useChatStorage.ts
21492
21689
  var CONVERSATION_REDACTOR_LIMIT = 50;
21493
21690
  var NO_CONVERSATION_KEY = "__no_conversation__";
@@ -22367,6 +22564,7 @@ function useChatStorage(options) {
22367
22564
  }
22368
22565
  let narrowedClientTools2 = clientTools;
22369
22566
  let clientActivatedSetNames2;
22567
+ let matchedToolSets2 = /* @__PURE__ */ new Set();
22370
22568
  if (clientTools?.length) {
22371
22569
  try {
22372
22570
  if (!skipUserEmbedding && getTokenRef.current && messageContent.length >= MIN_CONTENT_LENGTH_FOR_TOOLS) {
@@ -22387,17 +22585,25 @@ function useChatStorage(options) {
22387
22585
  narrowedClientTools2 = clientTools.filter((t) => keep.has(getToolName2(t)));
22388
22586
  }
22389
22587
  } else if (getTokenRef.current) {
22390
- const { tools: autoTools, activatedSetNames } = await autoFilterClientTools(
22588
+ const {
22589
+ tools: autoTools,
22590
+ activatedSetNames,
22591
+ matchedSetNames
22592
+ } = await autoFilterClientTools(
22391
22593
  clientTools,
22392
22594
  skipUserEmbedding ?? null,
22393
22595
  clientToolFilterCache,
22394
22596
  { getToken: getTokenRef.current, baseUrl, model: embeddingModel },
22395
22597
  extraToolSets ?? [],
22396
- activeToolSetsRef.current ?? [],
22598
+ mergeActiveToolSets(
22599
+ activeToolSetsRef.current ?? [],
22600
+ carriedToolSets(database, currentConversationId)
22601
+ ),
22397
22602
  skipEmbeddingFailed ? "error" : "short-prompt"
22398
22603
  );
22399
22604
  narrowedClientTools2 = autoTools;
22400
22605
  clientActivatedSetNames2 = activatedSetNames;
22606
+ matchedToolSets2 = matchedSetNames;
22401
22607
  }
22402
22608
  } catch (error) {
22403
22609
  getLogger().warn("[useChatStorage] client tool filtering failed (skipStorage):", error);
@@ -22449,6 +22655,7 @@ function useChatStorage(options) {
22449
22655
  headers
22450
22656
  });
22451
22657
  if ("detached" in result2 && result2.detached) {
22658
+ recordToolSetTurn(database, currentConversationId, matchedToolSets2);
22452
22659
  return {
22453
22660
  data: result2.data,
22454
22661
  error: result2.error,
@@ -22462,6 +22669,7 @@ function useChatStorage(options) {
22462
22669
  error: result2.error || "Unknown error"
22463
22670
  };
22464
22671
  }
22672
+ recordToolSetTurn(database, currentConversationId, matchedToolSets2);
22465
22673
  const skipRefreshGetToken = getTokenRef.current;
22466
22674
  const skipRefreshData = result2.data;
22467
22675
  if (skipRefreshGetToken) {
@@ -22677,6 +22885,7 @@ function useChatStorage(options) {
22677
22885
  }
22678
22886
  let narrowedClientTools = clientTools;
22679
22887
  let clientActivatedSetNames;
22888
+ let matchedToolSets = /* @__PURE__ */ new Set();
22680
22889
  if (clientTools?.length) {
22681
22890
  try {
22682
22891
  if (!userMessageEmbedding && getTokenRef.current && contentForStorage.length >= MIN_CONTENT_LENGTH_FOR_TOOLS) {
@@ -22693,17 +22902,25 @@ function useChatStorage(options) {
22693
22902
  narrowedClientTools = clientTools.filter((t) => keep.has(getToolName2(t)));
22694
22903
  }
22695
22904
  } else if (getTokenRef.current) {
22696
- const { tools: autoTools, activatedSetNames } = await autoFilterClientTools(
22905
+ const {
22906
+ tools: autoTools,
22907
+ activatedSetNames,
22908
+ matchedSetNames
22909
+ } = await autoFilterClientTools(
22697
22910
  clientTools,
22698
22911
  userMessageEmbedding ?? null,
22699
22912
  clientToolFilterCache,
22700
22913
  { getToken: getTokenRef.current, baseUrl, model: embeddingModel },
22701
22914
  extraToolSets ?? [],
22702
- activeToolSetsRef.current ?? [],
22915
+ mergeActiveToolSets(
22916
+ activeToolSetsRef.current ?? [],
22917
+ carriedToolSets(database, convId)
22918
+ ),
22703
22919
  userMessageEmbeddingFailed ? "error" : "short-prompt"
22704
22920
  );
22705
22921
  narrowedClientTools = autoTools;
22706
22922
  clientActivatedSetNames = activatedSetNames;
22923
+ matchedToolSets = matchedSetNames;
22707
22924
  }
22708
22925
  mergedTools = filteredServerTools.length > 0 || (narrowedClientTools?.length ?? 0) > 0 ? mergeTools(
22709
22926
  filteredServerTools,
@@ -22790,6 +23007,7 @@ function useChatStorage(options) {
22790
23007
  const responseDuration = (Date.now() - startTime) / 1e3;
22791
23008
  const detachedResult = result;
22792
23009
  if (detachedResult.detached) {
23010
+ recordToolSetTurn(database, convId, matchedToolSets);
22793
23011
  const rowId = effectiveAssistantUniqueId ?? `msg_${(0, import_uuid5.v7)()}`;
22794
23012
  pendingResumeRef.current = {
22795
23013
  handle: detachedResult.resume ?? null,
@@ -22858,6 +23076,7 @@ function useChatStorage(options) {
22858
23076
  ],
22859
23077
  usage: void 0
22860
23078
  };
23079
+ recordToolSetTurn(database, convId, matchedToolSets);
22861
23080
  return {
22862
23081
  data: responseData2,
22863
23082
  error: null,
@@ -22896,6 +23115,7 @@ function useChatStorage(options) {
22896
23115
  userMessage: { ...storedUserMessage, error: errorMessage }
22897
23116
  };
22898
23117
  }
23118
+ recordToolSetTurn(database, convId, matchedToolSets);
22899
23119
  const responseData = result.data;
22900
23120
  let assistantContent = "";
22901
23121
  let thinkingContent;
@@ -2209,6 +2209,21 @@ type ToolConfig = LlmapiChatCompletionTool & {
2209
2209
  * execution fails, allowing the model to retry.
2210
2210
  */
2211
2211
  removeAfterExecution?: boolean;
2212
+ /**
2213
+ * Remove this tool for the rest of the turn once ANY tool returns a
2214
+ * successful result this predicate accepts — its own or another's. Errors
2215
+ * never count, as for `removeAfterExecution`.
2216
+ *
2217
+ * Only results of tools executed on this client are offered; tools the
2218
+ * portal runs server-side never reach the predicate. A predicate that throws
2219
+ * is treated as not matching.
2220
+ *
2221
+ * For a family of tools that has to leave together: when one of them
2222
+ * declines an out-of-scope request, the model otherwise works through its
2223
+ * siblings round after round until the round cap. Give every member the same
2224
+ * predicate and the first refusal removes the whole family.
2225
+ */
2226
+ removeAfterResult?: (result: unknown) => boolean;
2212
2227
  /**
2213
2228
  * Timeout in milliseconds for this tool's executor.
2214
2229
  * Overrides the default 30-second timeout.
@@ -3512,6 +3527,7 @@ type MessageFeedback = "like" | "dislike" | null;
3512
3527
  * - `tool_result`: the hidden `[Tool Execution Results]` row built from a
3513
3528
  * turn's auto-executed tool results. Skipped by the embedding sweep (see
3514
3529
  * `memoryEngine/embeddings`) AND hidden from the transcript.
3530
+ * - `message`: an ordinary message with an index after explicit repair.
3515
3531
  * - `chunks_discarded`: an ordinary, still-rendered message whose chunk vectors
3516
3532
  * were built over `enc:v3:` ciphertext (sdk#864) and have been discarded
3517
3533
  * rather than re-embedded at the user's own expense (client#5618). Skipped by
@@ -3524,7 +3540,7 @@ type MessageFeedback = "like" | "dislike" | null;
3524
3540
  * encrypted flag would be unreadable exactly where it matters and the skip
3525
3541
  * would fail open.
3526
3542
  */
3527
- type MessageOrigin = "tool_result" | "chunks_discarded";
3543
+ type MessageOrigin = "tool_result" | "chunks_discarded" | "message";
3528
3544
  /**
3529
3545
  * Metadata for files attached to messages.
3530
3546
  *
@@ -7299,7 +7315,8 @@ declare function stampTopicsExtractedAtOp(ctx: VaultMemoryOperationsContext, mem
7299
7315
  * `topics_user_managed` in particular is left exactly as it arrived, so the
7300
7316
  * autotagger stays off a curated memory whose links this just restored.
7301
7317
  *
7302
- * Skips deleted, foreign-user, and record-less rows. Returns the ids relinked.
7318
+ * Skips deleted rows, foreign-user rows, and rows with null topics.
7319
+ * Empty topics remove stale links. Returns the relinked IDs.
7303
7320
  */
7304
7321
  declare function relinkMemoryTopicsOp(ctx: VaultMemoryOperationsContext, memoryIds: readonly string[]): Promise<string[]>;
7305
7322
  /**
@@ -9756,14 +9773,10 @@ declare function generateEmbeddings(texts: string[], options: EmbeddingOptions):
9756
9773
  * re-index. Off by default, so no background pass can spend a user's credits
9757
9774
  * without being asked to.
9758
9775
  *
9759
- * That flag opens the gate for one call and nothing more. It does not heal the
9760
- * row: a message that re-indexes successfully still carries `chunks_discarded`
9761
- * afterwards, so the next pass skips it again unless that pass sets the flag
9762
- * too. An embedding-model migration is where this bites. It reaches this gate
9763
- * before it can do anything, so the repaired rows stay on the old model, and
9764
- * `searchChunksOp` already drops stale-model vectors — they go quiet with
9765
- * nothing telling them apart from rows that were never repaired. Clearing the
9766
- * marker on a successful re-index is the real fix, tracked in #879.
9776
+ * A successful repair replaces the marker with `message` in the index write.
9777
+ * The new marker preserves ordinary message provenance during chat replay.
9778
+ * Later model migrations can then include the repaired row without this flag.
9779
+ * Failed embedding requests and empty index data do not clear the marker.
9767
9780
  *
9768
9781
  * `satisfies MessageOrigin` so the constant and the union cannot drift apart
9769
9782
  * silently — the column's type is the union, and a typo here would otherwise
@@ -9800,9 +9813,8 @@ declare function embedAllMessages(ctx: StorageOperationsContext, options: Embedd
9800
9813
  * to an explicit user action, never to a background pass. Opens that marker
9801
9814
  * only — `tool_result` rows stay excluded.
9802
9815
  *
9803
- * Per call, not a state change: a row that re-indexes successfully keeps its
9804
- * marker, so a later pass that omits this flag skips it again. See
9805
- * {@link CHUNKS_DISCARDED_ORIGIN}.
9816
+ * A successful repair clears the marker. Later model migrations include
9817
+ * the repaired row without this flag. See {@link CHUNKS_DISCARDED_ORIGIN}.
9806
9818
  */
9807
9819
  reembedDiscarded?: boolean;
9808
9820
  }): Promise<number>;
@@ -9860,9 +9872,8 @@ declare function chunkAndEmbedAllMessages(ctx: StorageOperationsContext, options
9860
9872
  * chunks nor vector, so it never reaches that check and falls straight to the
9861
9873
  * origin gate.
9862
9874
  *
9863
- * Per call, not a state change: a row that re-indexes successfully keeps its
9864
- * marker, so a later pass that omits this flag skips it again. See
9865
- * {@link CHUNKS_DISCARDED_ORIGIN}.
9875
+ * A successful repair clears the marker. Later model migrations include
9876
+ * the repaired row without this flag. See {@link CHUNKS_DISCARDED_ORIGIN}.
9866
9877
  */
9867
9878
  reembedDiscarded?: boolean;
9868
9879
  }): Promise<number>;