@anuma/sdk 6.10.0 → 6.12.0

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.
@@ -4880,6 +4880,51 @@ interface EntityOperationsContext {
4880
4880
  allowUnscopedRows?: boolean;
4881
4881
  }
4882
4882
 
4883
+ /**
4884
+ * Class-B task types — the vocabulary of the `X-Anuma-Task-Type` header.
4885
+ *
4886
+ * A Class-B flow has ONE fixed purpose and no conversation: extract memories
4887
+ * from a snippet, pick a topic, consolidate duplicates, classify a query. Today
4888
+ * each of those builds its own system prompt on the client and sends it as a
4889
+ * `role:system` message, which makes the prompt a suggestion rather than a
4890
+ * contract — a caller can reword it into "you are a general assistant" and get
4891
+ * general chat at internal-endpoint prices.
4892
+ *
4893
+ * The fix is that the SERVER owns the text and the client only names the task.
4894
+ * This header is the name. The portal allowlists it against its own copy of this
4895
+ * enum (ai-portal `internal/systemprompt/tasks.go`) and DROPS anything it does
4896
+ * not recognise, so an unknown value degrades to "no task declared" rather than
4897
+ * failing the request.
4898
+ *
4899
+ * ⚠ These string values are a wire contract with ai-portal. Adding one here
4900
+ * before the portal knows it is harmless (it is dropped); renaming or removing
4901
+ * one silently stops that flow being counted. Keep them in step with
4902
+ * `AllTaskTypes` on the portal side, and see ai-portal
4903
+ * `docs/BACKEND_OWNED_TASK_PROMPTS.md` for the rollout.
4904
+ *
4905
+ * ⚠ RELEASE ORDER. This union is a PUBLIC type, so a split is breaking in both
4906
+ * directions and has to be sequenced:
4907
+ *
4908
+ * 1. merge + publish this package;
4909
+ * 2. bump `@anuma/sdk` in the consumer and switch its call sites to the new
4910
+ * names in the same commit.
4911
+ *
4912
+ * A consumer on the OLD version cannot compile the new names, and a consumer on
4913
+ * the NEW version cannot compile the retired ones (`app_inspiration` is gone
4914
+ * here, replaced by the three `app_inspiration_*` names). Both halves of that
4915
+ * are type-only — the wire itself always degrades safely, because the portal
4916
+ * drops any value it does not recognise. Concretely: ai-memoryless-client#5782
4917
+ * is blocked on this publish for exactly that reason.
4918
+ *
4919
+ * Deliberately ABSENT: agent personas, `reflect()` when it answers the user, and
4920
+ * the app-idea chat. Those carry an open conversation with the user, so a fixed
4921
+ * server-owned prompt would be wrong rather than merely premature — the same
4922
+ * boundary {@link INTERNAL_FLOW_MARKER} draws.
4923
+ *
4924
+ * @public
4925
+ */
4926
+ 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";
4927
+
4883
4928
  /**
4884
4929
  * Shared portal LLM call for the memory pipeline.
4885
4930
  *
@@ -5732,6 +5777,17 @@ interface RecallDiagnostics {
5732
5777
  * never the cost.
5733
5778
  */
5734
5779
  vaultRowsDecrypted?: number;
5780
+ /**
5781
+ * Rows the fact lane had to re-embed through the portal because their stored
5782
+ * vector was unusable — stale `embedding_model`, wrong dimension, or
5783
+ * unparseable. Absent when the fact lane didn't run.
5784
+ *
5785
+ * Expected to be 0 on a healthy vault: rows are embedded at write time and the
5786
+ * re-embed writes the current model back, so a persistently non-zero value
5787
+ * means the writeback is not sticking and every turn is paying for it. On the
5788
+ * LEGACY read path this batch is uncapped, so it can be the whole vault.
5789
+ */
5790
+ vaultRowsEmbedded?: number;
5735
5791
  /** Facts the fact lane returned (post-dedupe, pre-fusion). */
5736
5792
  factCount: number;
5737
5793
  /** Chunks the chunk lane returned (post-dedupe, pre-fusion). */
@@ -5744,6 +5800,37 @@ interface RecallDiagnostics {
5744
5800
  prep: number;
5745
5801
  /** Vault fact-lane search (`searchVaultMemoriesWithSize`). */
5746
5802
  factLane: number;
5803
+ /**
5804
+ * The cross-encoder's share of {@link factLane} — wall-clock spent inside
5805
+ * `rerankPairs`. Billed even when the CE threw partway through, because a
5806
+ * rerank that burned three seconds and then failed still cost them.
5807
+ *
5808
+ * A SUBSET of `factLane`, not a sibling: read `factLane - rerank` for
5809
+ * everything else the lane did (query embed, vault read, fused ranking).
5810
+ * 0 when the CE did not run; `reranked` is what distinguishes that from a
5811
+ * rerank that cost nothing.
5812
+ *
5813
+ * Exists because #845 spent three rounds arguing about which stage inside
5814
+ * the fact lane dominated — whole-vault read, then admission window, then
5815
+ * the CE — with one aggregate number for all of them. Every hypothesis was
5816
+ * an inference; this makes the question a query.
5817
+ */
5818
+ rerank: number;
5819
+ /**
5820
+ * The query embed's share of {@link factLane} — one portal round trip.
5821
+ *
5822
+ * Also a SUBSET of `factLane`. Together with `rerank` this makes the lane
5823
+ * decomposable: `factLane - queryEmbed - rerank` is the local work (vault
5824
+ * read, decrypt, BM25/cosine fusion), and each of the three implies a
5825
+ * completely different fix.
5826
+ *
5827
+ * 0 when the lane returned before embedding, which includes every empty
5828
+ * vault — `prepareVaultCandidates` short-circuits first. That is why the
5829
+ * fast `vault_size = 0` population never established a baseline for this
5830
+ * cost, and why the ~850ms floor on the smallest NON-empty vaults had no
5831
+ * attributable owner.
5832
+ */
5833
+ queryEmbed: number;
5747
5834
  /** Chunk-lane search (`searchChunksOp`). */
5748
5835
  chunkLane: number;
5749
5836
  /** Cross-lane RRF fusion + provenance dedup after both lanes. */
@@ -8180,6 +8267,28 @@ interface ReflectOptions extends RecallOptions, PortalLlmAuth {
8180
8267
  maxTokens?: number;
8181
8268
  /** Override the grounding system prompt. */
8182
8269
  systemPrompt?: string;
8270
+ /**
8271
+ * Extra caller instruction to carry on the USER turn, between the question and
8272
+ * the evidence block (see the `userMessage` assembly below). This is the slot a
8273
+ * background caller uses to keep its per-request data OUT of the system message
8274
+ * without colliding with the numbered evidence list — profile-facet synthesis
8275
+ * puts its section label, guidance and response-field hint here so its system
8276
+ * half can stay fixed and server-ownable.
8277
+ */
8278
+ userInstructions?: string;
8279
+ /**
8280
+ * Class-B task name for the `X-Anuma-Task-Type` header, or nothing.
8281
+ *
8282
+ * Deliberately OPTIONAL and unset by default. reflect() also answers the user's
8283
+ * OWN question, and that traffic is chat, not an internal flow — declaring a
8284
+ * task type unconditionally here would put an internal-flow name on real
8285
+ * conversation, which is the same boundary `INTERNAL_FLOW_MARKER` draws
8286
+ * (reflect is deliberately unmarked; its background caller marks its own
8287
+ * prompt — see ../internalFlowMarker.ts). So the name is per call: only a
8288
+ * caller with ONE fixed purpose passes
8289
+ * one, and today that is profile-facet synthesis (`memory_profile_synth`).
8290
+ */
8291
+ taskType?: TaskType;
8183
8292
  /** Endpoint for the answer LLM. */
8184
8293
  baseUrl?: string;
8185
8294
  /** Override fetch (for tests). */
@@ -132,7 +132,7 @@ import {
132
132
  upsertMessageOp,
133
133
  useEncryption,
134
134
  vaultMemoryToStored
135
- } from "./chunk-4M56MKBV.mjs";
135
+ } from "./chunk-732ZLOLA.mjs";
136
136
  import {
137
137
  PII_PATTERNS,
138
138
  PiiRedactor,
@@ -8131,11 +8131,13 @@ Respond with ONLY a single JSON object conforming to this JSON Schema, with no p
8131
8131
  ${JSON.stringify(
8132
8132
  options.responseSchema
8133
8133
  )}` : basePrompt;
8134
- const userMessage = `Question:
8135
- ${trimmed}
8136
-
8137
- Memories (use only these as evidence):
8138
- ${evidence}`;
8134
+ const userMessage = [
8135
+ `Question:
8136
+ ${trimmed}`,
8137
+ ...options.userInstructions ? [options.userInstructions] : [],
8138
+ `Memories (use only these as evidence):
8139
+ ${evidence}`
8140
+ ].join("\n\n");
8139
8141
  const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
8140
8142
  const fetchImpl = options.fetchFn ?? fetch;
8141
8143
  const log = getLogger();
@@ -8149,7 +8151,13 @@ ${evidence}`;
8149
8151
  try {
8150
8152
  response = await fetchImpl(`${baseUrl}/api/v1/chat/completions`, {
8151
8153
  method: "POST",
8152
- headers: { ...authHeaders, "Content-Type": "application/json" },
8154
+ // No task type unless the caller named one — an undeclared task adds no
8155
+ // header at all, which is what keeps user-facing reflect() unlabelled.
8156
+ headers: {
8157
+ ...authHeaders,
8158
+ ...taskTypeHeader(options.taskType),
8159
+ "Content-Type": "application/json"
8160
+ },
8153
8161
  body: JSON.stringify({
8154
8162
  model,
8155
8163
  // Modern OpenAI field; the portal reads only `max_completion_tokens`
@@ -8589,10 +8597,15 @@ async function synthesizeFacet(facet, ctx, options, prior) {
8589
8597
  limit,
8590
8598
  types: ["fact"],
8591
8599
  maxTokens: DEFAULT_FACET_MAX_TOKENS,
8592
- // Profile-facet synthesis is a background op, so it marks its own prompt.
8593
- // reflect() itself must NOT mark — it also serves the user's own questions,
8594
- // and that traffic is chat, not an internal flow. See ../internalFlowMarker.
8595
- systemPrompt: withInternalFlowMarker(buildFacetSystemPrompt(facet)),
8600
+ // Profile-facet synthesis is a background op, so it marks its own prompt AND
8601
+ // names its own task. reflect() itself must do neither — it also serves the
8602
+ // user's own questions, and that traffic is chat, not an internal flow. See
8603
+ // ../internalFlowMarker and ReflectOptions.taskType.
8604
+ taskType: "memory_profile_synth",
8605
+ systemPrompt: withInternalFlowMarker(FACET_SYSTEM_PROMPT),
8606
+ // The facet's label/guidance/response-fields, on the user turn — what keeps
8607
+ // the system half fixed and therefore server-ownable.
8608
+ userInstructions: buildFacetUserInstructions(facet),
8596
8609
  responseSchema: facetResponseSchema(facet.key),
8597
8610
  memories
8598
8611
  });
@@ -8650,19 +8663,22 @@ function fallbackSection(facet, prior) {
8650
8663
  stale: true
8651
8664
  };
8652
8665
  }
8653
- function buildFacetSystemPrompt(facet) {
8654
- const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
8655
- 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.
8656
-
8657
- Task for this section:
8658
- ${facet.guidance}
8666
+ 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.
8659
8667
 
8660
8668
  Rules:
8661
8669
  - Ground every claim in the supplied memories \u2014 never invent, infer beyond, or embellish what they support.
8662
8670
  - If the memories don't cover this section, return an empty summary with hasEvidence=false. Do not pad or guess.
8663
8671
  - Write in third person about the person, in a natural voice suitable for a public profile (no "I"/"you", no name repetition).
8664
8672
  - Be concise and specific; no preamble, hedging, or meta-commentary.
8665
- - Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false>${structuredField} }.`;
8673
+ - Respond as JSON in exactly the shape the user turn states, with no extra fields.`;
8674
+ function buildFacetUserInstructions(facet) {
8675
+ const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
8676
+ return `Section: "${facet.label}"
8677
+
8678
+ Task for this section:
8679
+ ${facet.guidance}
8680
+
8681
+ Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false>${structuredField} }.`;
8666
8682
  }
8667
8683
  function extractFacetText(structured) {
8668
8684
  if (structured && typeof structured === "object") {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-4M56MKBV.mjs";
3
+ } from "./chunk-732ZLOLA.mjs";
4
4
  import "./chunk-CJ32HO6Q.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
package/dist/index.d.mts CHANGED
@@ -8916,6 +8916,20 @@ declare function decryptConversationTitle(encryptedTitle: string, address: strin
8916
8916
  * `AllTaskTypes` on the portal side, and see ai-portal
8917
8917
  * `docs/BACKEND_OWNED_TASK_PROMPTS.md` for the rollout.
8918
8918
  *
8919
+ * ⚠ RELEASE ORDER. This union is a PUBLIC type, so a split is breaking in both
8920
+ * directions and has to be sequenced:
8921
+ *
8922
+ * 1. merge + publish this package;
8923
+ * 2. bump `@anuma/sdk` in the consumer and switch its call sites to the new
8924
+ * names in the same commit.
8925
+ *
8926
+ * A consumer on the OLD version cannot compile the new names, and a consumer on
8927
+ * the NEW version cannot compile the retired ones (`app_inspiration` is gone
8928
+ * here, replaced by the three `app_inspiration_*` names). Both halves of that
8929
+ * are type-only — the wire itself always degrades safely, because the portal
8930
+ * drops any value it does not recognise. Concretely: ai-memoryless-client#5782
8931
+ * is blocked on this publish for exactly that reason.
8932
+ *
8919
8933
  * Deliberately ABSENT: agent personas, `reflect()` when it answers the user, and
8920
8934
  * the app-idea chat. Those carry an open conversation with the user, so a fixed
8921
8935
  * server-owned prompt would be wrong rather than merely premature — the same
@@ -8923,7 +8937,7 @@ declare function decryptConversationTitle(encryptedTitle: string, address: strin
8923
8937
  *
8924
8938
  * @public
8925
8939
  */
8926
- 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" | "spotlight_image" | "slide_image" | "media_video" | "media_audio" | "media_bg_removal" | "media_image_preset" | "tool_app_builder" | "tool_document_builder" | "tool_slides" | "connector_guidance" | "cf_task" | "xbot_intent" | "xbot_council" | "scheduled_task";
8940
+ 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";
8927
8941
  /**
8928
8942
  * HTTP header naming the Class-B task a request performs.
8929
8943
  *
package/dist/index.d.ts CHANGED
@@ -8916,6 +8916,20 @@ declare function decryptConversationTitle(encryptedTitle: string, address: strin
8916
8916
  * `AllTaskTypes` on the portal side, and see ai-portal
8917
8917
  * `docs/BACKEND_OWNED_TASK_PROMPTS.md` for the rollout.
8918
8918
  *
8919
+ * ⚠ RELEASE ORDER. This union is a PUBLIC type, so a split is breaking in both
8920
+ * directions and has to be sequenced:
8921
+ *
8922
+ * 1. merge + publish this package;
8923
+ * 2. bump `@anuma/sdk` in the consumer and switch its call sites to the new
8924
+ * names in the same commit.
8925
+ *
8926
+ * A consumer on the OLD version cannot compile the new names, and a consumer on
8927
+ * the NEW version cannot compile the retired ones (`app_inspiration` is gone
8928
+ * here, replaced by the three `app_inspiration_*` names). Both halves of that
8929
+ * are type-only — the wire itself always degrades safely, because the portal
8930
+ * drops any value it does not recognise. Concretely: ai-memoryless-client#5782
8931
+ * is blocked on this publish for exactly that reason.
8932
+ *
8919
8933
  * Deliberately ABSENT: agent personas, `reflect()` when it answers the user, and
8920
8934
  * the app-idea chat. Those carry an open conversation with the user, so a fixed
8921
8935
  * server-owned prompt would be wrong rather than merely premature — the same
@@ -8923,7 +8937,7 @@ declare function decryptConversationTitle(encryptedTitle: string, address: strin
8923
8937
  *
8924
8938
  * @public
8925
8939
  */
8926
- 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" | "spotlight_image" | "slide_image" | "media_video" | "media_audio" | "media_bg_removal" | "media_image_preset" | "tool_app_builder" | "tool_document_builder" | "tool_slides" | "connector_guidance" | "cf_task" | "xbot_intent" | "xbot_council" | "scheduled_task";
8940
+ 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";
8927
8941
  /**
8928
8942
  * HTTP header naming the Class-B task a request performs.
8929
8943
  *