@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.
- package/dist/expo/{chunk-ZO7S3TBT.mjs → chunk-732ZLOLA.mjs} +11 -8
- package/dist/expo/index.cjs +45 -25
- package/dist/expo/index.d.mts +67 -0
- package/dist/expo/index.d.ts +67 -0
- package/dist/expo/index.mjs +34 -18
- package/dist/expo/{recall-OALPGL3H.mjs → recall-PIATJ336.mjs} +1 -1
- package/dist/index.d.mts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/react/{chunk-HNQ2ZEFF.mjs → chunk-6IBFFWCE.mjs} +11 -8
- package/dist/react/index.cjs +45 -25
- package/dist/react/index.d.mts +67 -0
- package/dist/react/index.d.ts +67 -0
- package/dist/react/index.mjs +34 -18
- package/dist/react/{recall-F5DBVCU2.mjs → recall-QUWN7DJ7.mjs} +1 -1
- package/dist/server/{chunk-BKB6AT7Z.mjs → chunk-7YTEE6JT.mjs} +11 -8
- package/dist/server/index.cjs +45 -25
- package/dist/server/index.d.mts +67 -0
- package/dist/server/index.d.ts +67 -0
- package/dist/server/index.mjs +34 -18
- package/dist/server/{recall-HVIEAH7J.mjs → recall-HOVWMT4Q.mjs} +1 -1
- package/package.json +1 -1
|
@@ -5155,6 +5155,9 @@ async function decomposeQuery(query, options) {
|
|
|
5155
5155
|
...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
|
|
5156
5156
|
model: options.model ?? DEFAULT_MODEL,
|
|
5157
5157
|
systemPrompt: SYSTEM_PROMPT,
|
|
5158
|
+
// The query itself rides the user turn below, so SYSTEM_PROMPT is the whole
|
|
5159
|
+
// fixed instruction — which is what lets the portal own it outright.
|
|
5160
|
+
taskType: "memory_decompose",
|
|
5158
5161
|
// Wrap the bare query in a task framing so the model treats it as
|
|
5159
5162
|
// input to classify, not as a question to answer conversationally.
|
|
5160
5163
|
// Bare-query inputs caused Anthropic models to respond with prose
|
|
@@ -6882,7 +6885,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
|
|
|
6882
6885
|
subQueries = decomp.subQueries;
|
|
6883
6886
|
}
|
|
6884
6887
|
}
|
|
6885
|
-
const { recall: recall2 } = await import("./recall-
|
|
6888
|
+
const { recall: recall2 } = await import("./recall-PIATJ336.mjs");
|
|
6886
6889
|
let recallDegraded = [];
|
|
6887
6890
|
const result = await recall2(
|
|
6888
6891
|
query,
|
|
@@ -7135,29 +7138,29 @@ async function traverseGraphLane(query, entityCtx, options = {}) {
|
|
|
7135
7138
|
var DEFAULT_REFINER_MODEL = "inclusionai/ling-2.6-flash";
|
|
7136
7139
|
var DEFAULT_REFINER_ATTEMPTS = 1;
|
|
7137
7140
|
var DEFAULT_REFINER_TOTAL_TIMEOUT_MS = 8e3;
|
|
7141
|
+
var REFINER_SYSTEM_PROMPT = `You help a memory-retrieval system decide which related topics to explore.
|
|
7142
|
+
|
|
7143
|
+
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.
|
|
7144
|
+
|
|
7145
|
+
Output strict JSON, no prose: { "expand": [<entity names to expand, verbatim from the list>] }`;
|
|
7138
7146
|
function createLlmNeighborRefiner(options) {
|
|
7139
7147
|
return {
|
|
7140
7148
|
async refine(query, candidates, limit) {
|
|
7141
7149
|
if (candidates.length === 0) return [];
|
|
7142
7150
|
const numbered = candidates.map((name, i) => `[${i + 1}] ${name}`).join("\n");
|
|
7143
|
-
const systemPrompt = `You help a memory-retrieval system decide which related topics to explore.
|
|
7144
|
-
|
|
7145
|
-
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.
|
|
7146
|
-
|
|
7147
|
-
Output strict JSON, no prose: { "expand": [<entity names to expand, verbatim from the list>] }`;
|
|
7148
7151
|
const parsed = await callPortalJsonCompletion({
|
|
7149
7152
|
...options.apiKey !== void 0 && { apiKey: options.apiKey },
|
|
7150
7153
|
...options.getToken !== void 0 && { getToken: options.getToken },
|
|
7151
7154
|
...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
|
|
7152
7155
|
taskType: "memory_graph",
|
|
7153
7156
|
model: options.model ?? DEFAULT_REFINER_MODEL,
|
|
7154
|
-
systemPrompt,
|
|
7157
|
+
systemPrompt: REFINER_SYSTEM_PROMPT,
|
|
7155
7158
|
userMessage: `Question: ${query}
|
|
7156
7159
|
|
|
7157
7160
|
Candidate topics:
|
|
7158
7161
|
${numbered}
|
|
7159
7162
|
|
|
7160
|
-
Which should be expanded
|
|
7163
|
+
Which should be expanded? Choose at most ${limit}.`,
|
|
7161
7164
|
tag: "memory/graph-refine",
|
|
7162
7165
|
maxAttempts: options.maxAttempts ?? DEFAULT_REFINER_ATTEMPTS,
|
|
7163
7166
|
totalTimeoutMs: options.totalTimeoutMs ?? DEFAULT_REFINER_TOTAL_TIMEOUT_MS,
|
package/dist/expo/index.cjs
CHANGED
|
@@ -6864,6 +6864,9 @@ async function decomposeQuery(query, options) {
|
|
|
6864
6864
|
...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
|
|
6865
6865
|
model: options.model ?? DEFAULT_MODEL2,
|
|
6866
6866
|
systemPrompt: SYSTEM_PROMPT2,
|
|
6867
|
+
// The query itself rides the user turn below, so SYSTEM_PROMPT is the whole
|
|
6868
|
+
// fixed instruction — which is what lets the portal own it outright.
|
|
6869
|
+
taskType: "memory_decompose",
|
|
6867
6870
|
// Wrap the bare query in a task framing so the model treats it as
|
|
6868
6871
|
// input to classify, not as a question to answer conversationally.
|
|
6869
6872
|
// Bare-query inputs caused Anthropic models to respond with prose
|
|
@@ -7207,24 +7210,19 @@ function createLlmNeighborRefiner(options) {
|
|
|
7207
7210
|
async refine(query, candidates, limit) {
|
|
7208
7211
|
if (candidates.length === 0) return [];
|
|
7209
7212
|
const numbered = candidates.map((name, i) => `[${i + 1}] ${name}`).join("\n");
|
|
7210
|
-
const systemPrompt = `You help a memory-retrieval system decide which related topics to explore.
|
|
7211
|
-
|
|
7212
|
-
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.
|
|
7213
|
-
|
|
7214
|
-
Output strict JSON, no prose: { "expand": [<entity names to expand, verbatim from the list>] }`;
|
|
7215
7213
|
const parsed = await callPortalJsonCompletion({
|
|
7216
7214
|
...options.apiKey !== void 0 && { apiKey: options.apiKey },
|
|
7217
7215
|
...options.getToken !== void 0 && { getToken: options.getToken },
|
|
7218
7216
|
...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
|
|
7219
7217
|
taskType: "memory_graph",
|
|
7220
7218
|
model: options.model ?? DEFAULT_REFINER_MODEL,
|
|
7221
|
-
systemPrompt,
|
|
7219
|
+
systemPrompt: REFINER_SYSTEM_PROMPT,
|
|
7222
7220
|
userMessage: `Question: ${query}
|
|
7223
7221
|
|
|
7224
7222
|
Candidate topics:
|
|
7225
7223
|
${numbered}
|
|
7226
7224
|
|
|
7227
|
-
Which should be expanded
|
|
7225
|
+
Which should be expanded? Choose at most ${limit}.`,
|
|
7228
7226
|
tag: "memory/graph-refine",
|
|
7229
7227
|
maxAttempts: options.maxAttempts ?? DEFAULT_REFINER_ATTEMPTS,
|
|
7230
7228
|
totalTimeoutMs: options.totalTimeoutMs ?? DEFAULT_REFINER_TOTAL_TIMEOUT_MS,
|
|
@@ -7245,7 +7243,7 @@ Which should be expanded?`,
|
|
|
7245
7243
|
}
|
|
7246
7244
|
};
|
|
7247
7245
|
}
|
|
7248
|
-
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;
|
|
7246
|
+
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;
|
|
7249
7247
|
var init_graphTraversal = __esm({
|
|
7250
7248
|
"src/lib/memory/graphTraversal.ts"() {
|
|
7251
7249
|
"use strict";
|
|
@@ -7264,6 +7262,11 @@ var init_graphTraversal = __esm({
|
|
|
7264
7262
|
DEFAULT_REFINER_MODEL = "inclusionai/ling-2.6-flash";
|
|
7265
7263
|
DEFAULT_REFINER_ATTEMPTS = 1;
|
|
7266
7264
|
DEFAULT_REFINER_TOTAL_TIMEOUT_MS = 8e3;
|
|
7265
|
+
REFINER_SYSTEM_PROMPT = `You help a memory-retrieval system decide which related topics to explore.
|
|
7266
|
+
|
|
7267
|
+
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.
|
|
7268
|
+
|
|
7269
|
+
Output strict JSON, no prose: { "expand": [<entity names to expand, verbatim from the list>] }`;
|
|
7267
7270
|
}
|
|
7268
7271
|
});
|
|
7269
7272
|
|
|
@@ -17740,6 +17743,7 @@ ${TRUNCATION_NOTICE}` : formatted;
|
|
|
17740
17743
|
// src/lib/memory/reflect.ts
|
|
17741
17744
|
init_clientConfig();
|
|
17742
17745
|
init_logger();
|
|
17746
|
+
init_taskType();
|
|
17743
17747
|
init_portalLlm();
|
|
17744
17748
|
init_recall();
|
|
17745
17749
|
var DEFAULT_BASE_URL2 = BASE_URL;
|
|
@@ -17785,11 +17789,13 @@ Respond with ONLY a single JSON object conforming to this JSON Schema, with no p
|
|
|
17785
17789
|
${JSON.stringify(
|
|
17786
17790
|
options.responseSchema
|
|
17787
17791
|
)}` : basePrompt;
|
|
17788
|
-
const userMessage =
|
|
17789
|
-
|
|
17790
|
-
|
|
17791
|
-
|
|
17792
|
-
|
|
17792
|
+
const userMessage = [
|
|
17793
|
+
`Question:
|
|
17794
|
+
${trimmed}`,
|
|
17795
|
+
...options.userInstructions ? [options.userInstructions] : [],
|
|
17796
|
+
`Memories (use only these as evidence):
|
|
17797
|
+
${evidence}`
|
|
17798
|
+
].join("\n\n");
|
|
17793
17799
|
const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
17794
17800
|
const fetchImpl = options.fetchFn ?? fetch;
|
|
17795
17801
|
const log = getLogger();
|
|
@@ -17803,7 +17809,13 @@ ${evidence}`;
|
|
|
17803
17809
|
try {
|
|
17804
17810
|
response = await fetchImpl(`${baseUrl}/api/v1/chat/completions`, {
|
|
17805
17811
|
method: "POST",
|
|
17806
|
-
|
|
17812
|
+
// No task type unless the caller named one — an undeclared task adds no
|
|
17813
|
+
// header at all, which is what keeps user-facing reflect() unlabelled.
|
|
17814
|
+
headers: {
|
|
17815
|
+
...authHeaders,
|
|
17816
|
+
...taskTypeHeader(options.taskType),
|
|
17817
|
+
"Content-Type": "application/json"
|
|
17818
|
+
},
|
|
17807
17819
|
body: JSON.stringify({
|
|
17808
17820
|
model,
|
|
17809
17821
|
// Modern OpenAI field; the portal reads only `max_completion_tokens`
|
|
@@ -18255,10 +18267,15 @@ async function synthesizeFacet(facet, ctx, options, prior) {
|
|
|
18255
18267
|
limit,
|
|
18256
18268
|
types: ["fact"],
|
|
18257
18269
|
maxTokens: DEFAULT_FACET_MAX_TOKENS,
|
|
18258
|
-
// Profile-facet synthesis is a background op, so it marks its own prompt
|
|
18259
|
-
// reflect() itself must
|
|
18260
|
-
// and that traffic is chat, not an internal flow. See
|
|
18261
|
-
|
|
18270
|
+
// Profile-facet synthesis is a background op, so it marks its own prompt AND
|
|
18271
|
+
// names its own task. reflect() itself must do neither — it also serves the
|
|
18272
|
+
// user's own questions, and that traffic is chat, not an internal flow. See
|
|
18273
|
+
// ../internalFlowMarker and ReflectOptions.taskType.
|
|
18274
|
+
taskType: "memory_profile_synth",
|
|
18275
|
+
systemPrompt: withInternalFlowMarker(FACET_SYSTEM_PROMPT),
|
|
18276
|
+
// The facet's label/guidance/response-fields, on the user turn — what keeps
|
|
18277
|
+
// the system half fixed and therefore server-ownable.
|
|
18278
|
+
userInstructions: buildFacetUserInstructions(facet),
|
|
18262
18279
|
responseSchema: facetResponseSchema(facet.key),
|
|
18263
18280
|
memories
|
|
18264
18281
|
});
|
|
@@ -18316,19 +18333,22 @@ function fallbackSection(facet, prior) {
|
|
|
18316
18333
|
stale: true
|
|
18317
18334
|
};
|
|
18318
18335
|
}
|
|
18319
|
-
|
|
18320
|
-
const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
|
|
18321
|
-
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.
|
|
18322
|
-
|
|
18323
|
-
Task for this section:
|
|
18324
|
-
${facet.guidance}
|
|
18336
|
+
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.
|
|
18325
18337
|
|
|
18326
18338
|
Rules:
|
|
18327
18339
|
- Ground every claim in the supplied memories \u2014 never invent, infer beyond, or embellish what they support.
|
|
18328
18340
|
- If the memories don't cover this section, return an empty summary with hasEvidence=false. Do not pad or guess.
|
|
18329
18341
|
- Write in third person about the person, in a natural voice suitable for a public profile (no "I"/"you", no name repetition).
|
|
18330
18342
|
- Be concise and specific; no preamble, hedging, or meta-commentary.
|
|
18331
|
-
- Respond as JSON
|
|
18343
|
+
- Respond as JSON in exactly the shape the user turn states, with no extra fields.`;
|
|
18344
|
+
function buildFacetUserInstructions(facet) {
|
|
18345
|
+
const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
|
|
18346
|
+
return `Section: "${facet.label}"
|
|
18347
|
+
|
|
18348
|
+
Task for this section:
|
|
18349
|
+
${facet.guidance}
|
|
18350
|
+
|
|
18351
|
+
Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false>${structuredField} }.`;
|
|
18332
18352
|
}
|
|
18333
18353
|
function extractFacetText(structured) {
|
|
18334
18354
|
if (structured && typeof structured === "object") {
|
package/dist/expo/index.d.mts
CHANGED
|
@@ -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
|
*
|
|
@@ -8222,6 +8267,28 @@ interface ReflectOptions extends RecallOptions, PortalLlmAuth {
|
|
|
8222
8267
|
maxTokens?: number;
|
|
8223
8268
|
/** Override the grounding system prompt. */
|
|
8224
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;
|
|
8225
8292
|
/** Endpoint for the answer LLM. */
|
|
8226
8293
|
baseUrl?: string;
|
|
8227
8294
|
/** Override fetch (for tests). */
|
package/dist/expo/index.d.ts
CHANGED
|
@@ -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
|
*
|
|
@@ -8222,6 +8267,28 @@ interface ReflectOptions extends RecallOptions, PortalLlmAuth {
|
|
|
8222
8267
|
maxTokens?: number;
|
|
8223
8268
|
/** Override the grounding system prompt. */
|
|
8224
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;
|
|
8225
8292
|
/** Endpoint for the answer LLM. */
|
|
8226
8293
|
baseUrl?: string;
|
|
8227
8294
|
/** Override fetch (for tests). */
|
package/dist/expo/index.mjs
CHANGED
|
@@ -132,7 +132,7 @@ import {
|
|
|
132
132
|
upsertMessageOp,
|
|
133
133
|
useEncryption,
|
|
134
134
|
vaultMemoryToStored
|
|
135
|
-
} from "./chunk-
|
|
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 =
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
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
|
-
|
|
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
|
|
8594
|
-
// and that traffic is chat, not an internal flow. See
|
|
8595
|
-
|
|
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
|
-
|
|
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
|
|
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") {
|
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" | "
|
|
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" | "
|
|
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
|
*
|
|
@@ -5439,6 +5439,9 @@ async function decomposeQuery(query, options) {
|
|
|
5439
5439
|
...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
|
|
5440
5440
|
model: options.model ?? DEFAULT_MODEL,
|
|
5441
5441
|
systemPrompt: SYSTEM_PROMPT,
|
|
5442
|
+
// The query itself rides the user turn below, so SYSTEM_PROMPT is the whole
|
|
5443
|
+
// fixed instruction — which is what lets the portal own it outright.
|
|
5444
|
+
taskType: "memory_decompose",
|
|
5442
5445
|
// Wrap the bare query in a task framing so the model treats it as
|
|
5443
5446
|
// input to classify, not as a question to answer conversationally.
|
|
5444
5447
|
// Bare-query inputs caused Anthropic models to respond with prose
|
|
@@ -7166,7 +7169,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
|
|
|
7166
7169
|
subQueries = decomp.subQueries;
|
|
7167
7170
|
}
|
|
7168
7171
|
}
|
|
7169
|
-
const { recall: recall2 } = await import("./recall-
|
|
7172
|
+
const { recall: recall2 } = await import("./recall-QUWN7DJ7.mjs");
|
|
7170
7173
|
let recallDegraded = [];
|
|
7171
7174
|
const result = await recall2(
|
|
7172
7175
|
query,
|
|
@@ -7419,29 +7422,29 @@ async function traverseGraphLane(query, entityCtx, options = {}) {
|
|
|
7419
7422
|
var DEFAULT_REFINER_MODEL = "inclusionai/ling-2.6-flash";
|
|
7420
7423
|
var DEFAULT_REFINER_ATTEMPTS = 1;
|
|
7421
7424
|
var DEFAULT_REFINER_TOTAL_TIMEOUT_MS = 8e3;
|
|
7425
|
+
var REFINER_SYSTEM_PROMPT = `You help a memory-retrieval system decide which related topics to explore.
|
|
7426
|
+
|
|
7427
|
+
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.
|
|
7428
|
+
|
|
7429
|
+
Output strict JSON, no prose: { "expand": [<entity names to expand, verbatim from the list>] }`;
|
|
7422
7430
|
function createLlmNeighborRefiner(options) {
|
|
7423
7431
|
return {
|
|
7424
7432
|
async refine(query, candidates, limit) {
|
|
7425
7433
|
if (candidates.length === 0) return [];
|
|
7426
7434
|
const numbered = candidates.map((name, i) => `[${i + 1}] ${name}`).join("\n");
|
|
7427
|
-
const systemPrompt = `You help a memory-retrieval system decide which related topics to explore.
|
|
7428
|
-
|
|
7429
|
-
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.
|
|
7430
|
-
|
|
7431
|
-
Output strict JSON, no prose: { "expand": [<entity names to expand, verbatim from the list>] }`;
|
|
7432
7435
|
const parsed = await callPortalJsonCompletion({
|
|
7433
7436
|
...options.apiKey !== void 0 && { apiKey: options.apiKey },
|
|
7434
7437
|
...options.getToken !== void 0 && { getToken: options.getToken },
|
|
7435
7438
|
...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
|
|
7436
7439
|
taskType: "memory_graph",
|
|
7437
7440
|
model: options.model ?? DEFAULT_REFINER_MODEL,
|
|
7438
|
-
systemPrompt,
|
|
7441
|
+
systemPrompt: REFINER_SYSTEM_PROMPT,
|
|
7439
7442
|
userMessage: `Question: ${query}
|
|
7440
7443
|
|
|
7441
7444
|
Candidate topics:
|
|
7442
7445
|
${numbered}
|
|
7443
7446
|
|
|
7444
|
-
Which should be expanded
|
|
7447
|
+
Which should be expanded? Choose at most ${limit}.`,
|
|
7445
7448
|
tag: "memory/graph-refine",
|
|
7446
7449
|
maxAttempts: options.maxAttempts ?? DEFAULT_REFINER_ATTEMPTS,
|
|
7447
7450
|
totalTimeoutMs: options.totalTimeoutMs ?? DEFAULT_REFINER_TOTAL_TIMEOUT_MS,
|