@anuma/sdk 6.11.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.
@@ -4698,6 +4698,51 @@ interface EntityOperationsContext {
4698
4698
  allowUnscopedRows?: boolean;
4699
4699
  }
4700
4700
 
4701
+ /**
4702
+ * Class-B task types — the vocabulary of the `X-Anuma-Task-Type` header.
4703
+ *
4704
+ * A Class-B flow has ONE fixed purpose and no conversation: extract memories
4705
+ * from a snippet, pick a topic, consolidate duplicates, classify a query. Today
4706
+ * each of those builds its own system prompt on the client and sends it as a
4707
+ * `role:system` message, which makes the prompt a suggestion rather than a
4708
+ * contract — a caller can reword it into "you are a general assistant" and get
4709
+ * general chat at internal-endpoint prices.
4710
+ *
4711
+ * The fix is that the SERVER owns the text and the client only names the task.
4712
+ * This header is the name. The portal allowlists it against its own copy of this
4713
+ * enum (ai-portal `internal/systemprompt/tasks.go`) and DROPS anything it does
4714
+ * not recognise, so an unknown value degrades to "no task declared" rather than
4715
+ * failing the request.
4716
+ *
4717
+ * ⚠ These string values are a wire contract with ai-portal. Adding one here
4718
+ * before the portal knows it is harmless (it is dropped); renaming or removing
4719
+ * one silently stops that flow being counted. Keep them in step with
4720
+ * `AllTaskTypes` on the portal side, and see ai-portal
4721
+ * `docs/BACKEND_OWNED_TASK_PROMPTS.md` for the rollout.
4722
+ *
4723
+ * ⚠ RELEASE ORDER. This union is a PUBLIC type, so a split is breaking in both
4724
+ * directions and has to be sequenced:
4725
+ *
4726
+ * 1. merge + publish this package;
4727
+ * 2. bump `@anuma/sdk` in the consumer and switch its call sites to the new
4728
+ * names in the same commit.
4729
+ *
4730
+ * A consumer on the OLD version cannot compile the new names, and a consumer on
4731
+ * the NEW version cannot compile the retired ones (`app_inspiration` is gone
4732
+ * here, replaced by the three `app_inspiration_*` names). Both halves of that
4733
+ * are type-only — the wire itself always degrades safely, because the portal
4734
+ * drops any value it does not recognise. Concretely: ai-memoryless-client#5782
4735
+ * is blocked on this publish for exactly that reason.
4736
+ *
4737
+ * Deliberately ABSENT: agent personas, `reflect()` when it answers the user, and
4738
+ * the app-idea chat. Those carry an open conversation with the user, so a fixed
4739
+ * server-owned prompt would be wrong rather than merely premature — the same
4740
+ * boundary {@link INTERNAL_FLOW_MARKER} draws.
4741
+ *
4742
+ * @public
4743
+ */
4744
+ 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";
4745
+
4701
4746
  /**
4702
4747
  * Shared portal LLM call for the memory pipeline.
4703
4748
  *
@@ -8799,6 +8844,28 @@ interface ReflectOptions extends RecallOptions, PortalLlmAuth {
8799
8844
  maxTokens?: number;
8800
8845
  /** Override the grounding system prompt. */
8801
8846
  systemPrompt?: string;
8847
+ /**
8848
+ * Extra caller instruction to carry on the USER turn, between the question and
8849
+ * the evidence block (see the `userMessage` assembly below). This is the slot a
8850
+ * background caller uses to keep its per-request data OUT of the system message
8851
+ * without colliding with the numbered evidence list — profile-facet synthesis
8852
+ * puts its section label, guidance and response-field hint here so its system
8853
+ * half can stay fixed and server-ownable.
8854
+ */
8855
+ userInstructions?: string;
8856
+ /**
8857
+ * Class-B task name for the `X-Anuma-Task-Type` header, or nothing.
8858
+ *
8859
+ * Deliberately OPTIONAL and unset by default. reflect() also answers the user's
8860
+ * OWN question, and that traffic is chat, not an internal flow — declaring a
8861
+ * task type unconditionally here would put an internal-flow name on real
8862
+ * conversation, which is the same boundary `INTERNAL_FLOW_MARKER` draws
8863
+ * (reflect is deliberately unmarked; its background caller marks its own
8864
+ * prompt — see ../internalFlowMarker.ts). So the name is per call: only a
8865
+ * caller with ONE fixed purpose passes
8866
+ * one, and today that is profile-facet synthesis (`memory_profile_synth`).
8867
+ */
8868
+ taskType?: TaskType;
8802
8869
  /** Endpoint for the answer LLM. */
8803
8870
  baseUrl?: string;
8804
8871
  /** Override fetch (for tests). */
@@ -4698,6 +4698,51 @@ interface EntityOperationsContext {
4698
4698
  allowUnscopedRows?: boolean;
4699
4699
  }
4700
4700
 
4701
+ /**
4702
+ * Class-B task types — the vocabulary of the `X-Anuma-Task-Type` header.
4703
+ *
4704
+ * A Class-B flow has ONE fixed purpose and no conversation: extract memories
4705
+ * from a snippet, pick a topic, consolidate duplicates, classify a query. Today
4706
+ * each of those builds its own system prompt on the client and sends it as a
4707
+ * `role:system` message, which makes the prompt a suggestion rather than a
4708
+ * contract — a caller can reword it into "you are a general assistant" and get
4709
+ * general chat at internal-endpoint prices.
4710
+ *
4711
+ * The fix is that the SERVER owns the text and the client only names the task.
4712
+ * This header is the name. The portal allowlists it against its own copy of this
4713
+ * enum (ai-portal `internal/systemprompt/tasks.go`) and DROPS anything it does
4714
+ * not recognise, so an unknown value degrades to "no task declared" rather than
4715
+ * failing the request.
4716
+ *
4717
+ * ⚠ These string values are a wire contract with ai-portal. Adding one here
4718
+ * before the portal knows it is harmless (it is dropped); renaming or removing
4719
+ * one silently stops that flow being counted. Keep them in step with
4720
+ * `AllTaskTypes` on the portal side, and see ai-portal
4721
+ * `docs/BACKEND_OWNED_TASK_PROMPTS.md` for the rollout.
4722
+ *
4723
+ * ⚠ RELEASE ORDER. This union is a PUBLIC type, so a split is breaking in both
4724
+ * directions and has to be sequenced:
4725
+ *
4726
+ * 1. merge + publish this package;
4727
+ * 2. bump `@anuma/sdk` in the consumer and switch its call sites to the new
4728
+ * names in the same commit.
4729
+ *
4730
+ * A consumer on the OLD version cannot compile the new names, and a consumer on
4731
+ * the NEW version cannot compile the retired ones (`app_inspiration` is gone
4732
+ * here, replaced by the three `app_inspiration_*` names). Both halves of that
4733
+ * are type-only — the wire itself always degrades safely, because the portal
4734
+ * drops any value it does not recognise. Concretely: ai-memoryless-client#5782
4735
+ * is blocked on this publish for exactly that reason.
4736
+ *
4737
+ * Deliberately ABSENT: agent personas, `reflect()` when it answers the user, and
4738
+ * the app-idea chat. Those carry an open conversation with the user, so a fixed
4739
+ * server-owned prompt would be wrong rather than merely premature — the same
4740
+ * boundary {@link INTERNAL_FLOW_MARKER} draws.
4741
+ *
4742
+ * @public
4743
+ */
4744
+ 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";
4745
+
4701
4746
  /**
4702
4747
  * Shared portal LLM call for the memory pipeline.
4703
4748
  *
@@ -8799,6 +8844,28 @@ interface ReflectOptions extends RecallOptions, PortalLlmAuth {
8799
8844
  maxTokens?: number;
8800
8845
  /** Override the grounding system prompt. */
8801
8846
  systemPrompt?: string;
8847
+ /**
8848
+ * Extra caller instruction to carry on the USER turn, between the question and
8849
+ * the evidence block (see the `userMessage` assembly below). This is the slot a
8850
+ * background caller uses to keep its per-request data OUT of the system message
8851
+ * without colliding with the numbered evidence list — profile-facet synthesis
8852
+ * puts its section label, guidance and response-field hint here so its system
8853
+ * half can stay fixed and server-ownable.
8854
+ */
8855
+ userInstructions?: string;
8856
+ /**
8857
+ * Class-B task name for the `X-Anuma-Task-Type` header, or nothing.
8858
+ *
8859
+ * Deliberately OPTIONAL and unset by default. reflect() also answers the user's
8860
+ * OWN question, and that traffic is chat, not an internal flow — declaring a
8861
+ * task type unconditionally here would put an internal-flow name on real
8862
+ * conversation, which is the same boundary `INTERNAL_FLOW_MARKER` draws
8863
+ * (reflect is deliberately unmarked; its background caller marks its own
8864
+ * prompt — see ../internalFlowMarker.ts). So the name is per call: only a
8865
+ * caller with ONE fixed purpose passes
8866
+ * one, and today that is profile-facet synthesis (`memory_profile_synth`).
8867
+ */
8868
+ taskType?: TaskType;
8802
8869
  /** Endpoint for the answer LLM. */
8803
8870
  baseUrl?: string;
8804
8871
  /** Override fetch (for tests). */
@@ -141,7 +141,7 @@ import {
141
141
  updateVaultMemoryOp,
142
142
  upsertMessageOp,
143
143
  vaultMemoryToStored
144
- } from "./chunk-BKB6AT7Z.mjs";
144
+ } from "./chunk-7YTEE6JT.mjs";
145
145
  import {
146
146
  createStreamingDeAnonymizer,
147
147
  notifyConsolidationFallback,
@@ -6963,11 +6963,13 @@ Respond with ONLY a single JSON object conforming to this JSON Schema, with no p
6963
6963
  ${JSON.stringify(
6964
6964
  options.responseSchema
6965
6965
  )}` : basePrompt;
6966
- const userMessage = `Question:
6967
- ${trimmed}
6968
-
6969
- Memories (use only these as evidence):
6970
- ${evidence}`;
6966
+ const userMessage = [
6967
+ `Question:
6968
+ ${trimmed}`,
6969
+ ...options.userInstructions ? [options.userInstructions] : [],
6970
+ `Memories (use only these as evidence):
6971
+ ${evidence}`
6972
+ ].join("\n\n");
6971
6973
  const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
6972
6974
  const fetchImpl = options.fetchFn ?? fetch;
6973
6975
  const log = getLogger();
@@ -6981,7 +6983,13 @@ ${evidence}`;
6981
6983
  try {
6982
6984
  response = await fetchImpl(`${baseUrl}/api/v1/chat/completions`, {
6983
6985
  method: "POST",
6984
- headers: { ...authHeaders, "Content-Type": "application/json" },
6986
+ // No task type unless the caller named one — an undeclared task adds no
6987
+ // header at all, which is what keeps user-facing reflect() unlabelled.
6988
+ headers: {
6989
+ ...authHeaders,
6990
+ ...taskTypeHeader(options.taskType),
6991
+ "Content-Type": "application/json"
6992
+ },
6985
6993
  body: JSON.stringify({
6986
6994
  model,
6987
6995
  // Modern OpenAI field; the portal reads only `max_completion_tokens`
@@ -7421,10 +7429,15 @@ async function synthesizeFacet(facet, ctx, options, prior) {
7421
7429
  limit,
7422
7430
  types: ["fact"],
7423
7431
  maxTokens: DEFAULT_FACET_MAX_TOKENS,
7424
- // Profile-facet synthesis is a background op, so it marks its own prompt.
7425
- // reflect() itself must NOT mark — it also serves the user's own questions,
7426
- // and that traffic is chat, not an internal flow. See ../internalFlowMarker.
7427
- systemPrompt: withInternalFlowMarker(buildFacetSystemPrompt(facet)),
7432
+ // Profile-facet synthesis is a background op, so it marks its own prompt AND
7433
+ // names its own task. reflect() itself must do neither — it also serves the
7434
+ // user's own questions, and that traffic is chat, not an internal flow. See
7435
+ // ../internalFlowMarker and ReflectOptions.taskType.
7436
+ taskType: "memory_profile_synth",
7437
+ systemPrompt: withInternalFlowMarker(FACET_SYSTEM_PROMPT),
7438
+ // The facet's label/guidance/response-fields, on the user turn — what keeps
7439
+ // the system half fixed and therefore server-ownable.
7440
+ userInstructions: buildFacetUserInstructions(facet),
7428
7441
  responseSchema: facetResponseSchema(facet.key),
7429
7442
  memories
7430
7443
  });
@@ -7482,19 +7495,22 @@ function fallbackSection(facet, prior) {
7482
7495
  stale: true
7483
7496
  };
7484
7497
  }
7485
- function buildFacetSystemPrompt(facet) {
7486
- const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
7487
- 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.
7488
-
7489
- Task for this section:
7490
- ${facet.guidance}
7498
+ 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.
7491
7499
 
7492
7500
  Rules:
7493
7501
  - Ground every claim in the supplied memories \u2014 never invent, infer beyond, or embellish what they support.
7494
7502
  - If the memories don't cover this section, return an empty summary with hasEvidence=false. Do not pad or guess.
7495
7503
  - Write in third person about the person, in a natural voice suitable for a public profile (no "I"/"you", no name repetition).
7496
7504
  - Be concise and specific; no preamble, hedging, or meta-commentary.
7497
- - Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false>${structuredField} }.`;
7505
+ - Respond as JSON in exactly the shape the user turn states, with no extra fields.`;
7506
+ function buildFacetUserInstructions(facet) {
7507
+ const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
7508
+ return `Section: "${facet.label}"
7509
+
7510
+ Task for this section:
7511
+ ${facet.guidance}
7512
+
7513
+ Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false>${structuredField} }.`;
7498
7514
  }
7499
7515
  function extractFacetText(structured) {
7500
7516
  if (structured && typeof structured === "object") {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-BKB6AT7Z.mjs";
3
+ } from "./chunk-7YTEE6JT.mjs";
4
4
  import "./chunk-KF5OU4IN.mjs";
5
5
  import "./chunk-3RPGXXPZ.mjs";
6
6
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anuma/sdk",
3
- "version": "6.11.0",
3
+ "version": "6.12.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",