@anuma/sdk 6.11.0 → 6.12.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.
@@ -178,7 +178,7 @@ import {
178
178
  updateVaultMemoryOp,
179
179
  useEncryption,
180
180
  vaultMemoryToStored
181
- } from "./chunk-HNQ2ZEFF.mjs";
181
+ } from "./chunk-6IBFFWCE.mjs";
182
182
  import {
183
183
  PiiRedactor,
184
184
  createStreamingDeAnonymizer,
@@ -8949,11 +8949,13 @@ Respond with ONLY a single JSON object conforming to this JSON Schema, with no p
8949
8949
  ${JSON.stringify(
8950
8950
  options.responseSchema
8951
8951
  )}` : basePrompt;
8952
- const userMessage = `Question:
8953
- ${trimmed}
8954
-
8955
- Memories (use only these as evidence):
8956
- ${evidence}`;
8952
+ const userMessage = [
8953
+ `Question:
8954
+ ${trimmed}`,
8955
+ ...options.userInstructions ? [options.userInstructions] : [],
8956
+ `Memories (use only these as evidence):
8957
+ ${evidence}`
8958
+ ].join("\n\n");
8957
8959
  const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
8958
8960
  const fetchImpl = options.fetchFn ?? fetch;
8959
8961
  const log = getLogger();
@@ -8967,7 +8969,13 @@ ${evidence}`;
8967
8969
  try {
8968
8970
  response = await fetchImpl(`${baseUrl}/api/v1/chat/completions`, {
8969
8971
  method: "POST",
8970
- headers: { ...authHeaders, "Content-Type": "application/json" },
8972
+ // No task type unless the caller named one — an undeclared task adds no
8973
+ // header at all, which is what keeps user-facing reflect() unlabelled.
8974
+ headers: {
8975
+ ...authHeaders,
8976
+ ...taskTypeHeader(options.taskType),
8977
+ "Content-Type": "application/json"
8978
+ },
8971
8979
  body: JSON.stringify({
8972
8980
  model,
8973
8981
  // Modern OpenAI field; the portal reads only `max_completion_tokens`
@@ -9407,10 +9415,15 @@ async function synthesizeFacet(facet, ctx, options, prior) {
9407
9415
  limit,
9408
9416
  types: ["fact"],
9409
9417
  maxTokens: DEFAULT_FACET_MAX_TOKENS,
9410
- // Profile-facet synthesis is a background op, so it marks its own prompt.
9411
- // reflect() itself must NOT mark — it also serves the user's own questions,
9412
- // and that traffic is chat, not an internal flow. See ../internalFlowMarker.
9413
- systemPrompt: withInternalFlowMarker(buildFacetSystemPrompt(facet)),
9418
+ // Profile-facet synthesis is a background op, so it marks its own prompt AND
9419
+ // names its own task. reflect() itself must do neither — it also serves the
9420
+ // user's own questions, and that traffic is chat, not an internal flow. See
9421
+ // ../internalFlowMarker and ReflectOptions.taskType.
9422
+ taskType: "memory_profile_synth",
9423
+ systemPrompt: withInternalFlowMarker(FACET_SYSTEM_PROMPT),
9424
+ // The facet's label/guidance/response-fields, on the user turn — what keeps
9425
+ // the system half fixed and therefore server-ownable.
9426
+ userInstructions: buildFacetUserInstructions(facet),
9414
9427
  responseSchema: facetResponseSchema(facet.key),
9415
9428
  memories
9416
9429
  });
@@ -9468,19 +9481,22 @@ function fallbackSection(facet, prior) {
9468
9481
  stale: true
9469
9482
  };
9470
9483
  }
9471
- function buildFacetSystemPrompt(facet) {
9472
- const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
9473
- return `You are writing the "${facet.label}" section of a person's shareable profile, using their private memories (supplied as evidence) as the only source of truth.
9474
-
9475
- Task for this section:
9476
- ${facet.guidance}
9484
+ var FACET_SYSTEM_PROMPT = `You are writing one section of a person's shareable profile, using their private memories (supplied as evidence) as the only source of truth. The user turn names the section, states the task for it, and gives the exact JSON shape to respond in.
9477
9485
 
9478
9486
  Rules:
9479
9487
  - Ground every claim in the supplied memories \u2014 never invent, infer beyond, or embellish what they support.
9480
9488
  - If the memories don't cover this section, return an empty summary with hasEvidence=false. Do not pad or guess.
9481
9489
  - Write in third person about the person, in a natural voice suitable for a public profile (no "I"/"you", no name repetition).
9482
9490
  - Be concise and specific; no preamble, hedging, or meta-commentary.
9483
- - Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false>${structuredField} }.`;
9491
+ - Respond as JSON in exactly the shape the user turn states, with no extra fields.`;
9492
+ function buildFacetUserInstructions(facet) {
9493
+ const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
9494
+ return `Section: "${facet.label}"
9495
+
9496
+ Task for this section:
9497
+ ${facet.guidance}
9498
+
9499
+ Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false>${structuredField} }.`;
9484
9500
  }
9485
9501
  function extractFacetText(structured) {
9486
9502
  if (structured && typeof structured === "object") {
@@ -11483,6 +11499,20 @@ function clientToolToCompletionsFormat(tool) {
11483
11499
  }
11484
11500
  var TOOL_SEARCH_TOOL_TYPE = "tool_search_tool_regex_20251119";
11485
11501
  var TOOL_SEARCH_TOOL_NAME = "tool_search_tool_regex";
11502
+ function resolveDeferredServerTools(allServerTools, serverToolsFilter, config) {
11503
+ const allowed = serverToolsFilter === void 0 || typeof serverToolsFilter === "function" ? allServerTools : filterServerTools(allServerTools, [...serverToolsFilter]);
11504
+ const excluded = new Set(config.excludeTools ?? []);
11505
+ if (typeof serverToolsFilter === "function") {
11506
+ for (const name of serverToolsFilter.excludeTools ?? []) excluded.add(name);
11507
+ }
11508
+ if (excluded.size === 0) return allowed;
11509
+ return allowed.filter((tool) => !excluded.has(tool.name));
11510
+ }
11511
+ function deferFormattingConfig(serverToolsFilter, config) {
11512
+ if (!config?.enabled) return config;
11513
+ const isExplicitList = serverToolsFilter !== void 0 && typeof serverToolsFilter !== "function";
11514
+ return isExplicitList ? void 0 : config;
11515
+ }
11486
11516
  function byNameAscending(a, b) {
11487
11517
  if (a.name < b.name) return -1;
11488
11518
  if (a.name > b.name) return 1;
@@ -11809,7 +11839,7 @@ function createServerToolsFilter(options = {}) {
11809
11839
  const exclude = new Set(options.excludeTools ?? []);
11810
11840
  const sets = options.toolSets ?? [];
11811
11841
  const matchOpts = options.matchOptions;
11812
- return (embeddings, tools) => {
11842
+ const filter = (embeddings, tools) => {
11813
11843
  const matches = findMatchingTools(embeddings, tools, matchOpts);
11814
11844
  if (matches.length === 0) return [];
11815
11845
  const matchedNames = new Set(matches.map((m) => m.tool.name));
@@ -11827,6 +11857,10 @@ function createServerToolsFilter(options = {}) {
11827
11857
  for (const name of exclude) finalNames.delete(name);
11828
11858
  return [...finalNames];
11829
11859
  };
11860
+ return Object.defineProperty(filter, "excludeTools", {
11861
+ value: Object.freeze([...exclude]),
11862
+ enumerable: false
11863
+ });
11830
11864
  }
11831
11865
  var DEFAULT_EXCLUDED_SERVER_TOOLS = [
11832
11866
  "AnumaVisionMCP-anuma_analyze_image",
@@ -11908,7 +11942,8 @@ async function selectServerToolsForPrompt(options) {
11908
11942
  return [];
11909
11943
  }
11910
11944
  if (allServerTools.length === 0) return [];
11911
- if (deferLoading?.enabled) return allServerTools;
11945
+ if (deferLoading?.enabled)
11946
+ return resolveDeferredServerTools(allServerTools, serverToolsFilter, deferLoading);
11912
11947
  if (typeof serverToolsFilter === "function") {
11913
11948
  if (prompt.length < MIN_CONTENT_LENGTH_FOR_TOOLS) return [];
11914
11949
  let promptEmbedding;
@@ -12335,7 +12370,8 @@ async function previewToolSelection(options) {
12335
12370
  cache: serverToolsConfig?.cache
12336
12371
  });
12337
12372
  const allow = new Set(serverToolsFilter);
12338
- gatedServerNames = allServerTools.filter((t) => allow.has(t.name)).map((t) => t.name);
12373
+ const gated = allServerTools.filter((t) => allow.has(t.name));
12374
+ gatedServerNames = (serverToolsConfig?.deferLoading?.enabled ? resolveDeferredServerTools(gated, serverToolsFilter, serverToolsConfig.deferLoading) : gated).map((t) => t.name);
12339
12375
  } catch {
12340
12376
  }
12341
12377
  }
@@ -12384,7 +12420,11 @@ async function previewToolSelection(options) {
12384
12420
  cache: serverToolsConfig?.cache
12385
12421
  });
12386
12422
  if (serverToolsConfig?.deferLoading?.enabled) {
12387
- serverToolNames = allServerTools.map((t) => t.name);
12423
+ serverToolNames = resolveDeferredServerTools(
12424
+ allServerTools,
12425
+ serverToolsFilter,
12426
+ serverToolsConfig.deferLoading
12427
+ ).map((t) => t.name);
12388
12428
  } else if (typeof serverToolsFilter === "function") {
12389
12429
  serverToolNames = serverToolsFilter(promptEmbedding, allServerTools);
12390
12430
  } else {
@@ -13400,7 +13440,11 @@ function useChatStorage(options) {
13400
13440
  cache: serverToolsConfig?.cache
13401
13441
  });
13402
13442
  if (serverToolsConfig?.deferLoading?.enabled) {
13403
- filteredServerTools2 = allServerTools;
13443
+ filteredServerTools2 = resolveDeferredServerTools(
13444
+ allServerTools,
13445
+ serverToolsFilter,
13446
+ serverToolsConfig.deferLoading
13447
+ );
13404
13448
  } else if (isServerToolsFunction2) {
13405
13449
  if (skipStorageEmbeddings) {
13406
13450
  const toolNames = serverToolsFilter(skipStorageEmbeddings, allServerTools);
@@ -13441,7 +13485,7 @@ function useChatStorage(options) {
13441
13485
  filteredServerTools2,
13442
13486
  filteredClientTools2,
13443
13487
  effectiveApiType2,
13444
- serverToolsConfig?.deferLoading
13488
+ deferFormattingConfig(serverToolsFilter, serverToolsConfig?.deferLoading)
13445
13489
  );
13446
13490
  }
13447
13491
  if (onToolSelection) {
@@ -13800,7 +13844,11 @@ function useChatStorage(options) {
13800
13844
  if (allServerTools) {
13801
13845
  try {
13802
13846
  if (serverToolsConfig?.deferLoading?.enabled && effectiveApiType === "responses") {
13803
- filteredServerTools = allServerTools;
13847
+ filteredServerTools = resolveDeferredServerTools(
13848
+ allServerTools,
13849
+ serverToolsFilter,
13850
+ serverToolsConfig.deferLoading
13851
+ );
13804
13852
  } else if (isServerToolsFunction) {
13805
13853
  if (userMessageEmbeddings) {
13806
13854
  const toolNames = serverToolsFilter(userMessageEmbeddings, allServerTools);
@@ -13880,7 +13928,7 @@ function useChatStorage(options) {
13880
13928
  filteredServerTools,
13881
13929
  filteredClientTools,
13882
13930
  effectiveApiType,
13883
- serverToolsConfig?.deferLoading
13931
+ deferFormattingConfig(serverToolsFilter, serverToolsConfig?.deferLoading)
13884
13932
  );
13885
13933
  }
13886
13934
  if (onToolSelection) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-HNQ2ZEFF.mjs";
3
+ } from "./chunk-6IBFFWCE.mjs";
4
4
  import "./chunk-CJ32HO6Q.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
@@ -19193,6 +19193,9 @@ async function decomposeQuery(query, options) {
19193
19193
  ...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
19194
19194
  model: options.model ?? DEFAULT_MODEL,
19195
19195
  systemPrompt: SYSTEM_PROMPT,
19196
+ // The query itself rides the user turn below, so SYSTEM_PROMPT is the whole
19197
+ // fixed instruction — which is what lets the portal own it outright.
19198
+ taskType: "memory_decompose",
19196
19199
  // Wrap the bare query in a task framing so the model treats it as
19197
19200
  // input to classify, not as a question to answer conversationally.
19198
19201
  // Bare-query inputs caused Anthropic models to respond with prose
@@ -20920,7 +20923,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
20920
20923
  subQueries = decomp.subQueries;
20921
20924
  }
20922
20925
  }
20923
- const { recall: recall2 } = await import("./recall-HVIEAH7J.mjs");
20926
+ const { recall: recall2 } = await import("./recall-HOVWMT4Q.mjs");
20924
20927
  let recallDegraded = [];
20925
20928
  const result = await recall2(
20926
20929
  query,
@@ -21173,29 +21176,29 @@ async function traverseGraphLane(query, entityCtx, options = {}) {
21173
21176
  var DEFAULT_REFINER_MODEL = "inclusionai/ling-2.6-flash";
21174
21177
  var DEFAULT_REFINER_ATTEMPTS = 1;
21175
21178
  var DEFAULT_REFINER_TOTAL_TIMEOUT_MS = 8e3;
21179
+ var REFINER_SYSTEM_PROMPT = `You help a memory-retrieval system decide which related topics to explore.
21180
+
21181
+ Given a user's question and a numbered list of candidate topics/entities linked to memories found so far, pick the ones most likely to lead to memories that help ANSWER the question. Never return more than the maximum the user turn asks for. Prefer topics semantically related to the question; ignore incidental ones.
21182
+
21183
+ Output strict JSON, no prose: { "expand": [<entity names to expand, verbatim from the list>] }`;
21176
21184
  function createLlmNeighborRefiner(options) {
21177
21185
  return {
21178
21186
  async refine(query, candidates, limit) {
21179
21187
  if (candidates.length === 0) return [];
21180
21188
  const numbered = candidates.map((name, i) => `[${i + 1}] ${name}`).join("\n");
21181
- const systemPrompt = `You help a memory-retrieval system decide which related topics to explore.
21182
-
21183
- Given a user's question and a numbered list of candidate topics/entities linked to memories found so far, pick the ones most likely to lead to memories that help ANSWER the question. Choose at most ${limit}. Prefer topics semantically related to the question; ignore incidental ones.
21184
-
21185
- Output strict JSON, no prose: { "expand": [<entity names to expand, verbatim from the list>] }`;
21186
21189
  const parsed = await callPortalJsonCompletion({
21187
21190
  ...options.apiKey !== void 0 && { apiKey: options.apiKey },
21188
21191
  ...options.getToken !== void 0 && { getToken: options.getToken },
21189
21192
  ...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
21190
21193
  taskType: "memory_graph",
21191
21194
  model: options.model ?? DEFAULT_REFINER_MODEL,
21192
- systemPrompt,
21195
+ systemPrompt: REFINER_SYSTEM_PROMPT,
21193
21196
  userMessage: `Question: ${query}
21194
21197
 
21195
21198
  Candidate topics:
21196
21199
  ${numbered}
21197
21200
 
21198
- Which should be expanded?`,
21201
+ Which should be expanded? Choose at most ${limit}.`,
21199
21202
  tag: "memory/graph-refine",
21200
21203
  maxAttempts: options.maxAttempts ?? DEFAULT_REFINER_ATTEMPTS,
21201
21204
  totalTimeoutMs: options.totalTimeoutMs ?? DEFAULT_REFINER_TOTAL_TIMEOUT_MS,
@@ -20915,6 +20915,9 @@ async function decomposeQuery(query, options) {
20915
20915
  ...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
20916
20916
  model: options.model ?? DEFAULT_MODEL,
20917
20917
  systemPrompt: SYSTEM_PROMPT,
20918
+ // The query itself rides the user turn below, so SYSTEM_PROMPT is the whole
20919
+ // fixed instruction — which is what lets the portal own it outright.
20920
+ taskType: "memory_decompose",
20918
20921
  // Wrap the bare query in a task framing so the model treats it as
20919
20922
  // input to classify, not as a question to answer conversationally.
20920
20923
  // Bare-query inputs caused Anthropic models to respond with prose
@@ -21220,24 +21223,19 @@ function createLlmNeighborRefiner(options) {
21220
21223
  async refine(query, candidates, limit) {
21221
21224
  if (candidates.length === 0) return [];
21222
21225
  const numbered = candidates.map((name, i) => `[${i + 1}] ${name}`).join("\n");
21223
- const systemPrompt = `You help a memory-retrieval system decide which related topics to explore.
21224
-
21225
- Given a user's question and a numbered list of candidate topics/entities linked to memories found so far, pick the ones most likely to lead to memories that help ANSWER the question. Choose at most ${limit}. Prefer topics semantically related to the question; ignore incidental ones.
21226
-
21227
- Output strict JSON, no prose: { "expand": [<entity names to expand, verbatim from the list>] }`;
21228
21226
  const parsed = await callPortalJsonCompletion({
21229
21227
  ...options.apiKey !== void 0 && { apiKey: options.apiKey },
21230
21228
  ...options.getToken !== void 0 && { getToken: options.getToken },
21231
21229
  ...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
21232
21230
  taskType: "memory_graph",
21233
21231
  model: options.model ?? DEFAULT_REFINER_MODEL,
21234
- systemPrompt,
21232
+ systemPrompt: REFINER_SYSTEM_PROMPT,
21235
21233
  userMessage: `Question: ${query}
21236
21234
 
21237
21235
  Candidate topics:
21238
21236
  ${numbered}
21239
21237
 
21240
- Which should be expanded?`,
21238
+ Which should be expanded? Choose at most ${limit}.`,
21241
21239
  tag: "memory/graph-refine",
21242
21240
  maxAttempts: options.maxAttempts ?? DEFAULT_REFINER_ATTEMPTS,
21243
21241
  totalTimeoutMs: options.totalTimeoutMs ?? DEFAULT_REFINER_TOTAL_TIMEOUT_MS,
@@ -21258,7 +21256,7 @@ Which should be expanded?`,
21258
21256
  }
21259
21257
  };
21260
21258
  }
21261
- var MAX_HOPS, ENTITY_FANOUT, MIN_REFINER_CANDIDATES, MAX_REFINER_CANDIDATES, NODE_BUDGET, VAULT_SIZE_HOP_CAP, DEFAULT_REFINER_MODEL, DEFAULT_REFINER_ATTEMPTS, DEFAULT_REFINER_TOTAL_TIMEOUT_MS;
21259
+ var MAX_HOPS, ENTITY_FANOUT, MIN_REFINER_CANDIDATES, MAX_REFINER_CANDIDATES, NODE_BUDGET, VAULT_SIZE_HOP_CAP, DEFAULT_REFINER_MODEL, DEFAULT_REFINER_ATTEMPTS, DEFAULT_REFINER_TOTAL_TIMEOUT_MS, REFINER_SYSTEM_PROMPT;
21262
21260
  var init_graphTraversal = __esm({
21263
21261
  "src/lib/memory/graphTraversal.ts"() {
21264
21262
  "use strict";
@@ -21277,6 +21275,11 @@ var init_graphTraversal = __esm({
21277
21275
  DEFAULT_REFINER_MODEL = "inclusionai/ling-2.6-flash";
21278
21276
  DEFAULT_REFINER_ATTEMPTS = 1;
21279
21277
  DEFAULT_REFINER_TOTAL_TIMEOUT_MS = 8e3;
21278
+ REFINER_SYSTEM_PROMPT = `You help a memory-retrieval system decide which related topics to explore.
21279
+
21280
+ Given a user's question and a numbered list of candidate topics/entities linked to memories found so far, pick the ones most likely to lead to memories that help ANSWER the question. Never return more than the maximum the user turn asks for. Prefer topics semantically related to the question; ignore incidental ones.
21281
+
21282
+ Output strict JSON, no prose: { "expand": [<entity names to expand, verbatim from the list>] }`;
21280
21283
  }
21281
21284
  });
21282
21285
 
@@ -30656,6 +30659,7 @@ ${TRUNCATION_NOTICE}` : formatted;
30656
30659
  // src/lib/memory/reflect.ts
30657
30660
  init_clientConfig();
30658
30661
  init_logger();
30662
+ init_taskType();
30659
30663
  init_portalLlm();
30660
30664
  init_recall();
30661
30665
  var DEFAULT_BASE_URL2 = BASE_URL;
@@ -30701,11 +30705,13 @@ Respond with ONLY a single JSON object conforming to this JSON Schema, with no p
30701
30705
  ${JSON.stringify(
30702
30706
  options.responseSchema
30703
30707
  )}` : basePrompt;
30704
- const userMessage = `Question:
30705
- ${trimmed}
30706
-
30707
- Memories (use only these as evidence):
30708
- ${evidence}`;
30708
+ const userMessage = [
30709
+ `Question:
30710
+ ${trimmed}`,
30711
+ ...options.userInstructions ? [options.userInstructions] : [],
30712
+ `Memories (use only these as evidence):
30713
+ ${evidence}`
30714
+ ].join("\n\n");
30709
30715
  const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
30710
30716
  const fetchImpl = options.fetchFn ?? fetch;
30711
30717
  const log = getLogger();
@@ -30719,7 +30725,13 @@ ${evidence}`;
30719
30725
  try {
30720
30726
  response = await fetchImpl(`${baseUrl}/api/v1/chat/completions`, {
30721
30727
  method: "POST",
30722
- headers: { ...authHeaders, "Content-Type": "application/json" },
30728
+ // No task type unless the caller named one — an undeclared task adds no
30729
+ // header at all, which is what keeps user-facing reflect() unlabelled.
30730
+ headers: {
30731
+ ...authHeaders,
30732
+ ...taskTypeHeader(options.taskType),
30733
+ "Content-Type": "application/json"
30734
+ },
30723
30735
  body: JSON.stringify({
30724
30736
  model,
30725
30737
  // Modern OpenAI field; the portal reads only `max_completion_tokens`
@@ -31171,10 +31183,15 @@ async function synthesizeFacet(facet, ctx, options, prior) {
31171
31183
  limit,
31172
31184
  types: ["fact"],
31173
31185
  maxTokens: DEFAULT_FACET_MAX_TOKENS,
31174
- // Profile-facet synthesis is a background op, so it marks its own prompt.
31175
- // reflect() itself must NOT mark — it also serves the user's own questions,
31176
- // and that traffic is chat, not an internal flow. See ../internalFlowMarker.
31177
- systemPrompt: withInternalFlowMarker(buildFacetSystemPrompt(facet)),
31186
+ // Profile-facet synthesis is a background op, so it marks its own prompt AND
31187
+ // names its own task. reflect() itself must do neither — it also serves the
31188
+ // user's own questions, and that traffic is chat, not an internal flow. See
31189
+ // ../internalFlowMarker and ReflectOptions.taskType.
31190
+ taskType: "memory_profile_synth",
31191
+ systemPrompt: withInternalFlowMarker(FACET_SYSTEM_PROMPT),
31192
+ // The facet's label/guidance/response-fields, on the user turn — what keeps
31193
+ // the system half fixed and therefore server-ownable.
31194
+ userInstructions: buildFacetUserInstructions(facet),
31178
31195
  responseSchema: facetResponseSchema(facet.key),
31179
31196
  memories
31180
31197
  });
@@ -31232,19 +31249,22 @@ function fallbackSection(facet, prior) {
31232
31249
  stale: true
31233
31250
  };
31234
31251
  }
31235
- function buildFacetSystemPrompt(facet) {
31236
- const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
31237
- return `You are writing the "${facet.label}" section of a person's shareable profile, using their private memories (supplied as evidence) as the only source of truth.
31238
-
31239
- Task for this section:
31240
- ${facet.guidance}
31252
+ var FACET_SYSTEM_PROMPT = `You are writing one section of a person's shareable profile, using their private memories (supplied as evidence) as the only source of truth. The user turn names the section, states the task for it, and gives the exact JSON shape to respond in.
31241
31253
 
31242
31254
  Rules:
31243
31255
  - Ground every claim in the supplied memories \u2014 never invent, infer beyond, or embellish what they support.
31244
31256
  - If the memories don't cover this section, return an empty summary with hasEvidence=false. Do not pad or guess.
31245
31257
  - Write in third person about the person, in a natural voice suitable for a public profile (no "I"/"you", no name repetition).
31246
31258
  - Be concise and specific; no preamble, hedging, or meta-commentary.
31247
- - Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false>${structuredField} }.`;
31259
+ - Respond as JSON in exactly the shape the user turn states, with no extra fields.`;
31260
+ function buildFacetUserInstructions(facet) {
31261
+ const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
31262
+ return `Section: "${facet.label}"
31263
+
31264
+ Task for this section:
31265
+ ${facet.guidance}
31266
+
31267
+ Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false>${structuredField} }.`;
31248
31268
  }
31249
31269
  function extractFacetText(structured) {
31250
31270
  if (structured && typeof structured === "object") {
@@ -32048,6 +32068,15 @@ function filterServerTools(serverTools, includeNames) {
32048
32068
  const includeSet = new Set(includeNames);
32049
32069
  return serverTools.filter((tool) => includeSet.has(tool.name));
32050
32070
  }
32071
+ function resolveDeferredServerTools(allServerTools, serverToolsFilter, config) {
32072
+ const allowed = serverToolsFilter === void 0 || typeof serverToolsFilter === "function" ? allServerTools : filterServerTools(allServerTools, [...serverToolsFilter]);
32073
+ const excluded = new Set(config.excludeTools ?? []);
32074
+ if (typeof serverToolsFilter === "function") {
32075
+ for (const name of serverToolsFilter.excludeTools ?? []) excluded.add(name);
32076
+ }
32077
+ if (excluded.size === 0) return allowed;
32078
+ return allowed.filter((tool) => !excluded.has(tool.name));
32079
+ }
32051
32080
  var DEFAULT_TOOL_MATCH_OPTIONS = {
32052
32081
  limit: 5,
32053
32082
  minSimilarity: 0.3,
@@ -32324,7 +32353,7 @@ function createServerToolsFilter(options = {}) {
32324
32353
  const exclude = new Set(options.excludeTools ?? []);
32325
32354
  const sets = options.toolSets ?? [];
32326
32355
  const matchOpts = options.matchOptions;
32327
- return (embeddings, tools) => {
32356
+ const filter = (embeddings, tools) => {
32328
32357
  const matches = findMatchingTools(embeddings, tools, matchOpts);
32329
32358
  if (matches.length === 0) return [];
32330
32359
  const matchedNames = new Set(matches.map((m) => m.tool.name));
@@ -32342,6 +32371,10 @@ function createServerToolsFilter(options = {}) {
32342
32371
  for (const name of exclude) finalNames.delete(name);
32343
32372
  return [...finalNames];
32344
32373
  };
32374
+ return Object.defineProperty(filter, "excludeTools", {
32375
+ value: Object.freeze([...exclude]),
32376
+ enumerable: false
32377
+ });
32345
32378
  }
32346
32379
  var DEFAULT_EXCLUDED_SERVER_TOOLS = [
32347
32380
  "AnumaVisionMCP-anuma_analyze_image",
@@ -32423,7 +32456,8 @@ async function selectServerToolsForPrompt(options) {
32423
32456
  return [];
32424
32457
  }
32425
32458
  if (allServerTools.length === 0) return [];
32426
- if (deferLoading?.enabled) return allServerTools;
32459
+ if (deferLoading?.enabled)
32460
+ return resolveDeferredServerTools(allServerTools, serverToolsFilter, deferLoading);
32427
32461
  if (typeof serverToolsFilter === "function") {
32428
32462
  if (prompt.length < MIN_CONTENT_LENGTH_FOR_TOOLS) return [];
32429
32463
  let promptEmbedding;
@@ -3013,6 +3013,22 @@ interface DeferLoadingConfig {
3013
3013
  enabled: boolean;
3014
3014
  /** Server-tool names to keep non-deferred (besides the tool-search tool), in priority order. */
3015
3015
  hotToolNames: readonly string[];
3016
+ /**
3017
+ * Server-tool names to drop entirely while defer-loading is on.
3018
+ *
3019
+ * Defer widens the per-turn selection to the whole catalog, which also bypasses the `excludeTools`
3020
+ * baked into a caller's semantic filter. A filter built by {@link createServerToolsFilter} tags itself
3021
+ * with its own exclusions and those are honoured automatically — so most callers need nothing here.
3022
+ *
3023
+ * Set this when the filter's tag can't be seen: a hand-written filter, a filter wrapped in a plain
3024
+ * closure (the wrapper drops the tag), or a static-array `serverToolsFilter`. Anything excluded
3025
+ * UNCONDITIONALLY (a tool the app replaces with its own UI, a capability the model already has
3026
+ * natively) belongs in one of the two places, or defer quietly hands it back to the model.
3027
+ *
3028
+ * This list is UNIONED with the filter's own tag, never a replacement for it — so adding one name
3029
+ * here cannot re-admit the tools the filter already excludes.
3030
+ */
3031
+ excludeTools?: readonly string[];
3016
3032
  }
3017
3033
  /**
3018
3034
  * Result of tool matching with similarity score
@@ -3192,7 +3208,7 @@ interface CreateServerToolsFilterOptions {
3192
3208
  * });
3193
3209
  * ```
3194
3210
  */
3195
- declare function createServerToolsFilter(options?: CreateServerToolsFilterOptions): (embeddings: number[] | number[][], tools: ServerTool[]) => string[];
3211
+ declare function createServerToolsFilter(options?: CreateServerToolsFilterOptions): ServerToolsFilterFunction;
3196
3212
  /**
3197
3213
  * Default exclusions baked into `defaultServerToolsFilter`.
3198
3214
  *
@@ -3258,13 +3274,21 @@ declare const SERVER_TOOL_DEPENDENCY_SETS: ToolSet[];
3258
3274
  * If you need to customize (extra excludes, different limits, opt into
3259
3275
  * tool-set expansion), call `createServerToolsFilter` directly.
3260
3276
  */
3261
- declare const defaultServerToolsFilter: (embeddings: number[] | number[][], tools: ServerTool[]) => string[];
3277
+ declare const defaultServerToolsFilter: ServerToolsFilterFunction;
3262
3278
  /**
3263
3279
  * Type for a server-tools filter — a function that takes prompt embeddings
3264
3280
  * and the full server tool catalog and returns the names of tools to keep.
3265
3281
  * Matches `useChatStorage`'s `serverTools` callback signature.
3266
3282
  */
3267
- type ServerToolsFilterFunction = (embeddings: number[] | number[][], tools: ServerTool[]) => string[];
3283
+ type ServerToolsFilterFunction = ((embeddings: number[] | number[][], tools: ServerTool[]) => string[]) & {
3284
+ /**
3285
+ * The unconditional exclusions this filter applies, exposed by
3286
+ * {@link createServerToolsFilter} so defer-loading can honour them without the caller
3287
+ * repeating the list — see {@link resolveDeferredServerTools}. Absent on a hand-written
3288
+ * filter, or on one wrapped in a plain closure.
3289
+ */
3290
+ readonly excludeTools?: readonly string[];
3291
+ };
3268
3292
  /**
3269
3293
  * Options for `selectServerToolsForPrompt`.
3270
3294
  */
@@ -3292,9 +3316,10 @@ interface SelectServerToolsForPromptOptions {
3292
3316
  */
3293
3317
  cache?: ToolsCacheBackend;
3294
3318
  /**
3295
- * Phase 3 defer-loading. When `enabled`, this helper returns the FULL catalog (skipping semantic/
3296
- * static filtering) to mirror useChatStorage's responses send path, which swaps in the full catalog
3297
- * for mergeTools + tool-search. Omit/disabled → today's filtered selection.
3319
+ * Phase 3 defer-loading. When `enabled`, this helper skips SEMANTIC filtering to mirror
3320
+ * useChatStorage's responses send path, which hands the catalog to mergeTools + tool-search. The
3321
+ * caller's unconditional constraints still apply — an explicit static array, and exclusions (see
3322
+ * {@link resolveDeferredServerTools}). Omit/disabled → today's filtered selection.
3298
3323
  */
3299
3324
  deferLoading?: DeferLoadingConfig;
3300
3325
  }
@@ -4698,6 +4723,51 @@ interface EntityOperationsContext {
4698
4723
  allowUnscopedRows?: boolean;
4699
4724
  }
4700
4725
 
4726
+ /**
4727
+ * Class-B task types — the vocabulary of the `X-Anuma-Task-Type` header.
4728
+ *
4729
+ * A Class-B flow has ONE fixed purpose and no conversation: extract memories
4730
+ * from a snippet, pick a topic, consolidate duplicates, classify a query. Today
4731
+ * each of those builds its own system prompt on the client and sends it as a
4732
+ * `role:system` message, which makes the prompt a suggestion rather than a
4733
+ * contract — a caller can reword it into "you are a general assistant" and get
4734
+ * general chat at internal-endpoint prices.
4735
+ *
4736
+ * The fix is that the SERVER owns the text and the client only names the task.
4737
+ * This header is the name. The portal allowlists it against its own copy of this
4738
+ * enum (ai-portal `internal/systemprompt/tasks.go`) and DROPS anything it does
4739
+ * not recognise, so an unknown value degrades to "no task declared" rather than
4740
+ * failing the request.
4741
+ *
4742
+ * ⚠ These string values are a wire contract with ai-portal. Adding one here
4743
+ * before the portal knows it is harmless (it is dropped); renaming or removing
4744
+ * one silently stops that flow being counted. Keep them in step with
4745
+ * `AllTaskTypes` on the portal side, and see ai-portal
4746
+ * `docs/BACKEND_OWNED_TASK_PROMPTS.md` for the rollout.
4747
+ *
4748
+ * ⚠ RELEASE ORDER. This union is a PUBLIC type, so a split is breaking in both
4749
+ * directions and has to be sequenced:
4750
+ *
4751
+ * 1. merge + publish this package;
4752
+ * 2. bump `@anuma/sdk` in the consumer and switch its call sites to the new
4753
+ * names in the same commit.
4754
+ *
4755
+ * A consumer on the OLD version cannot compile the new names, and a consumer on
4756
+ * the NEW version cannot compile the retired ones (`app_inspiration` is gone
4757
+ * here, replaced by the three `app_inspiration_*` names). Both halves of that
4758
+ * are type-only — the wire itself always degrades safely, because the portal
4759
+ * drops any value it does not recognise. Concretely: ai-memoryless-client#5782
4760
+ * is blocked on this publish for exactly that reason.
4761
+ *
4762
+ * Deliberately ABSENT: agent personas, `reflect()` when it answers the user, and
4763
+ * the app-idea chat. Those carry an open conversation with the user, so a fixed
4764
+ * server-owned prompt would be wrong rather than merely premature — the same
4765
+ * boundary {@link INTERNAL_FLOW_MARKER} draws.
4766
+ *
4767
+ * @public
4768
+ */
4769
+ type TaskType = "title" | "commit_message" | "memory_extract" | "memory_dedup" | "memory_decay" | "memory_injection_check" | "memory_verify_support" | "memory_autosort" | "memory_topic" | "memory_consolidate" | "memory_decompose" | "memory_profile_synth" | "memory_graph" | "folder_context" | "selection_memory" | "summarize" | "style_analysis" | "classify_search" | "app_inspiration_generate" | "app_inspiration_trends" | "app_inspiration_remix" | "spotlight_image" | "slide_image" | "slide_video" | "slide_music" | "slide_sfx" | "media_video" | "media_audio" | "media_bg_removal" | "media_image_preset" | "tool_app_builder" | "tool_document_builder" | "tool_slides" | "tool_slides_image_gen" | "connector_guidance" | "cf_task" | "xbot_intent" | "xbot_council" | "scheduled_task";
4770
+
4701
4771
  /**
4702
4772
  * Shared portal LLM call for the memory pipeline.
4703
4773
  *
@@ -8799,6 +8869,28 @@ interface ReflectOptions extends RecallOptions, PortalLlmAuth {
8799
8869
  maxTokens?: number;
8800
8870
  /** Override the grounding system prompt. */
8801
8871
  systemPrompt?: string;
8872
+ /**
8873
+ * Extra caller instruction to carry on the USER turn, between the question and
8874
+ * the evidence block (see the `userMessage` assembly below). This is the slot a
8875
+ * background caller uses to keep its per-request data OUT of the system message
8876
+ * without colliding with the numbered evidence list — profile-facet synthesis
8877
+ * puts its section label, guidance and response-field hint here so its system
8878
+ * half can stay fixed and server-ownable.
8879
+ */
8880
+ userInstructions?: string;
8881
+ /**
8882
+ * Class-B task name for the `X-Anuma-Task-Type` header, or nothing.
8883
+ *
8884
+ * Deliberately OPTIONAL and unset by default. reflect() also answers the user's
8885
+ * OWN question, and that traffic is chat, not an internal flow — declaring a
8886
+ * task type unconditionally here would put an internal-flow name on real
8887
+ * conversation, which is the same boundary `INTERNAL_FLOW_MARKER` draws
8888
+ * (reflect is deliberately unmarked; its background caller marks its own
8889
+ * prompt — see ../internalFlowMarker.ts). So the name is per call: only a
8890
+ * caller with ONE fixed purpose passes
8891
+ * one, and today that is profile-facet synthesis (`memory_profile_synth`).
8892
+ */
8893
+ taskType?: TaskType;
8802
8894
  /** Endpoint for the answer LLM. */
8803
8895
  baseUrl?: string;
8804
8896
  /** Override fetch (for tests). */