@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
package/dist/react/index.cjs
CHANGED
|
@@ -7148,6 +7148,9 @@ async function decomposeQuery(query, options) {
|
|
|
7148
7148
|
...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
|
|
7149
7149
|
model: options.model ?? DEFAULT_MODEL2,
|
|
7150
7150
|
systemPrompt: SYSTEM_PROMPT2,
|
|
7151
|
+
// The query itself rides the user turn below, so SYSTEM_PROMPT is the whole
|
|
7152
|
+
// fixed instruction — which is what lets the portal own it outright.
|
|
7153
|
+
taskType: "memory_decompose",
|
|
7151
7154
|
// Wrap the bare query in a task framing so the model treats it as
|
|
7152
7155
|
// input to classify, not as a question to answer conversationally.
|
|
7153
7156
|
// Bare-query inputs caused Anthropic models to respond with prose
|
|
@@ -7491,24 +7494,19 @@ function createLlmNeighborRefiner(options) {
|
|
|
7491
7494
|
async refine(query, candidates, limit) {
|
|
7492
7495
|
if (candidates.length === 0) return [];
|
|
7493
7496
|
const numbered = candidates.map((name, i) => `[${i + 1}] ${name}`).join("\n");
|
|
7494
|
-
const systemPrompt = `You help a memory-retrieval system decide which related topics to explore.
|
|
7495
|
-
|
|
7496
|
-
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.
|
|
7497
|
-
|
|
7498
|
-
Output strict JSON, no prose: { "expand": [<entity names to expand, verbatim from the list>] }`;
|
|
7499
7497
|
const parsed = await callPortalJsonCompletion({
|
|
7500
7498
|
...options.apiKey !== void 0 && { apiKey: options.apiKey },
|
|
7501
7499
|
...options.getToken !== void 0 && { getToken: options.getToken },
|
|
7502
7500
|
...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
|
|
7503
7501
|
taskType: "memory_graph",
|
|
7504
7502
|
model: options.model ?? DEFAULT_REFINER_MODEL,
|
|
7505
|
-
systemPrompt,
|
|
7503
|
+
systemPrompt: REFINER_SYSTEM_PROMPT,
|
|
7506
7504
|
userMessage: `Question: ${query}
|
|
7507
7505
|
|
|
7508
7506
|
Candidate topics:
|
|
7509
7507
|
${numbered}
|
|
7510
7508
|
|
|
7511
|
-
Which should be expanded
|
|
7509
|
+
Which should be expanded? Choose at most ${limit}.`,
|
|
7512
7510
|
tag: "memory/graph-refine",
|
|
7513
7511
|
maxAttempts: options.maxAttempts ?? DEFAULT_REFINER_ATTEMPTS,
|
|
7514
7512
|
totalTimeoutMs: options.totalTimeoutMs ?? DEFAULT_REFINER_TOTAL_TIMEOUT_MS,
|
|
@@ -7529,7 +7527,7 @@ Which should be expanded?`,
|
|
|
7529
7527
|
}
|
|
7530
7528
|
};
|
|
7531
7529
|
}
|
|
7532
|
-
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;
|
|
7530
|
+
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;
|
|
7533
7531
|
var init_graphTraversal = __esm({
|
|
7534
7532
|
"src/lib/memory/graphTraversal.ts"() {
|
|
7535
7533
|
"use strict";
|
|
@@ -7548,6 +7546,11 @@ var init_graphTraversal = __esm({
|
|
|
7548
7546
|
DEFAULT_REFINER_MODEL = "inclusionai/ling-2.6-flash";
|
|
7549
7547
|
DEFAULT_REFINER_ATTEMPTS = 1;
|
|
7550
7548
|
DEFAULT_REFINER_TOTAL_TIMEOUT_MS = 8e3;
|
|
7549
|
+
REFINER_SYSTEM_PROMPT = `You help a memory-retrieval system decide which related topics to explore.
|
|
7550
|
+
|
|
7551
|
+
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.
|
|
7552
|
+
|
|
7553
|
+
Output strict JSON, no prose: { "expand": [<entity names to expand, verbatim from the list>] }`;
|
|
7551
7554
|
}
|
|
7552
7555
|
});
|
|
7553
7556
|
|
|
@@ -19038,6 +19041,7 @@ ${TRUNCATION_NOTICE}` : formatted;
|
|
|
19038
19041
|
// src/lib/memory/reflect.ts
|
|
19039
19042
|
init_clientConfig();
|
|
19040
19043
|
init_logger();
|
|
19044
|
+
init_taskType();
|
|
19041
19045
|
init_portalLlm();
|
|
19042
19046
|
init_recall();
|
|
19043
19047
|
var DEFAULT_BASE_URL2 = BASE_URL;
|
|
@@ -19083,11 +19087,13 @@ Respond with ONLY a single JSON object conforming to this JSON Schema, with no p
|
|
|
19083
19087
|
${JSON.stringify(
|
|
19084
19088
|
options.responseSchema
|
|
19085
19089
|
)}` : basePrompt;
|
|
19086
|
-
const userMessage =
|
|
19087
|
-
|
|
19088
|
-
|
|
19089
|
-
|
|
19090
|
-
|
|
19090
|
+
const userMessage = [
|
|
19091
|
+
`Question:
|
|
19092
|
+
${trimmed}`,
|
|
19093
|
+
...options.userInstructions ? [options.userInstructions] : [],
|
|
19094
|
+
`Memories (use only these as evidence):
|
|
19095
|
+
${evidence}`
|
|
19096
|
+
].join("\n\n");
|
|
19091
19097
|
const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
19092
19098
|
const fetchImpl = options.fetchFn ?? fetch;
|
|
19093
19099
|
const log = getLogger();
|
|
@@ -19101,7 +19107,13 @@ ${evidence}`;
|
|
|
19101
19107
|
try {
|
|
19102
19108
|
response = await fetchImpl(`${baseUrl}/api/v1/chat/completions`, {
|
|
19103
19109
|
method: "POST",
|
|
19104
|
-
|
|
19110
|
+
// No task type unless the caller named one — an undeclared task adds no
|
|
19111
|
+
// header at all, which is what keeps user-facing reflect() unlabelled.
|
|
19112
|
+
headers: {
|
|
19113
|
+
...authHeaders,
|
|
19114
|
+
...taskTypeHeader(options.taskType),
|
|
19115
|
+
"Content-Type": "application/json"
|
|
19116
|
+
},
|
|
19105
19117
|
body: JSON.stringify({
|
|
19106
19118
|
model,
|
|
19107
19119
|
// Modern OpenAI field; the portal reads only `max_completion_tokens`
|
|
@@ -19553,10 +19565,15 @@ async function synthesizeFacet(facet, ctx, options, prior) {
|
|
|
19553
19565
|
limit,
|
|
19554
19566
|
types: ["fact"],
|
|
19555
19567
|
maxTokens: DEFAULT_FACET_MAX_TOKENS,
|
|
19556
|
-
// Profile-facet synthesis is a background op, so it marks its own prompt
|
|
19557
|
-
// reflect() itself must
|
|
19558
|
-
// and that traffic is chat, not an internal flow. See
|
|
19559
|
-
|
|
19568
|
+
// Profile-facet synthesis is a background op, so it marks its own prompt AND
|
|
19569
|
+
// names its own task. reflect() itself must do neither — it also serves the
|
|
19570
|
+
// user's own questions, and that traffic is chat, not an internal flow. See
|
|
19571
|
+
// ../internalFlowMarker and ReflectOptions.taskType.
|
|
19572
|
+
taskType: "memory_profile_synth",
|
|
19573
|
+
systemPrompt: withInternalFlowMarker(FACET_SYSTEM_PROMPT),
|
|
19574
|
+
// The facet's label/guidance/response-fields, on the user turn — what keeps
|
|
19575
|
+
// the system half fixed and therefore server-ownable.
|
|
19576
|
+
userInstructions: buildFacetUserInstructions(facet),
|
|
19560
19577
|
responseSchema: facetResponseSchema(facet.key),
|
|
19561
19578
|
memories
|
|
19562
19579
|
});
|
|
@@ -19614,19 +19631,22 @@ function fallbackSection(facet, prior) {
|
|
|
19614
19631
|
stale: true
|
|
19615
19632
|
};
|
|
19616
19633
|
}
|
|
19617
|
-
|
|
19618
|
-
const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
|
|
19619
|
-
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.
|
|
19620
|
-
|
|
19621
|
-
Task for this section:
|
|
19622
|
-
${facet.guidance}
|
|
19634
|
+
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.
|
|
19623
19635
|
|
|
19624
19636
|
Rules:
|
|
19625
19637
|
- Ground every claim in the supplied memories \u2014 never invent, infer beyond, or embellish what they support.
|
|
19626
19638
|
- If the memories don't cover this section, return an empty summary with hasEvidence=false. Do not pad or guess.
|
|
19627
19639
|
- Write in third person about the person, in a natural voice suitable for a public profile (no "I"/"you", no name repetition).
|
|
19628
19640
|
- Be concise and specific; no preamble, hedging, or meta-commentary.
|
|
19629
|
-
- Respond as JSON
|
|
19641
|
+
- Respond as JSON in exactly the shape the user turn states, with no extra fields.`;
|
|
19642
|
+
function buildFacetUserInstructions(facet) {
|
|
19643
|
+
const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
|
|
19644
|
+
return `Section: "${facet.label}"
|
|
19645
|
+
|
|
19646
|
+
Task for this section:
|
|
19647
|
+
${facet.guidance}
|
|
19648
|
+
|
|
19649
|
+
Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false>${structuredField} }.`;
|
|
19630
19650
|
}
|
|
19631
19651
|
function extractFacetText(structured) {
|
|
19632
19652
|
if (structured && typeof structured === "object") {
|
package/dist/react/index.d.mts
CHANGED
|
@@ -6140,6 +6140,51 @@ declare function getMemoriesByEntityNamesOp(ctx: EntityOperationsContext, entity
|
|
|
6140
6140
|
*/
|
|
6141
6141
|
declare function getEntitiesByMemoryIdsOp(ctx: EntityOperationsContext, memoryIds: readonly string[]): Promise<Map<string, Set<string>>>;
|
|
6142
6142
|
|
|
6143
|
+
/**
|
|
6144
|
+
* Class-B task types — the vocabulary of the `X-Anuma-Task-Type` header.
|
|
6145
|
+
*
|
|
6146
|
+
* A Class-B flow has ONE fixed purpose and no conversation: extract memories
|
|
6147
|
+
* from a snippet, pick a topic, consolidate duplicates, classify a query. Today
|
|
6148
|
+
* each of those builds its own system prompt on the client and sends it as a
|
|
6149
|
+
* `role:system` message, which makes the prompt a suggestion rather than a
|
|
6150
|
+
* contract — a caller can reword it into "you are a general assistant" and get
|
|
6151
|
+
* general chat at internal-endpoint prices.
|
|
6152
|
+
*
|
|
6153
|
+
* The fix is that the SERVER owns the text and the client only names the task.
|
|
6154
|
+
* This header is the name. The portal allowlists it against its own copy of this
|
|
6155
|
+
* enum (ai-portal `internal/systemprompt/tasks.go`) and DROPS anything it does
|
|
6156
|
+
* not recognise, so an unknown value degrades to "no task declared" rather than
|
|
6157
|
+
* failing the request.
|
|
6158
|
+
*
|
|
6159
|
+
* ⚠ These string values are a wire contract with ai-portal. Adding one here
|
|
6160
|
+
* before the portal knows it is harmless (it is dropped); renaming or removing
|
|
6161
|
+
* one silently stops that flow being counted. Keep them in step with
|
|
6162
|
+
* `AllTaskTypes` on the portal side, and see ai-portal
|
|
6163
|
+
* `docs/BACKEND_OWNED_TASK_PROMPTS.md` for the rollout.
|
|
6164
|
+
*
|
|
6165
|
+
* ⚠ RELEASE ORDER. This union is a PUBLIC type, so a split is breaking in both
|
|
6166
|
+
* directions and has to be sequenced:
|
|
6167
|
+
*
|
|
6168
|
+
* 1. merge + publish this package;
|
|
6169
|
+
* 2. bump `@anuma/sdk` in the consumer and switch its call sites to the new
|
|
6170
|
+
* names in the same commit.
|
|
6171
|
+
*
|
|
6172
|
+
* A consumer on the OLD version cannot compile the new names, and a consumer on
|
|
6173
|
+
* the NEW version cannot compile the retired ones (`app_inspiration` is gone
|
|
6174
|
+
* here, replaced by the three `app_inspiration_*` names). Both halves of that
|
|
6175
|
+
* are type-only — the wire itself always degrades safely, because the portal
|
|
6176
|
+
* drops any value it does not recognise. Concretely: ai-memoryless-client#5782
|
|
6177
|
+
* is blocked on this publish for exactly that reason.
|
|
6178
|
+
*
|
|
6179
|
+
* Deliberately ABSENT: agent personas, `reflect()` when it answers the user, and
|
|
6180
|
+
* the app-idea chat. Those carry an open conversation with the user, so a fixed
|
|
6181
|
+
* server-owned prompt would be wrong rather than merely premature — the same
|
|
6182
|
+
* boundary {@link INTERNAL_FLOW_MARKER} draws.
|
|
6183
|
+
*
|
|
6184
|
+
* @public
|
|
6185
|
+
*/
|
|
6186
|
+
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";
|
|
6187
|
+
|
|
6143
6188
|
/**
|
|
6144
6189
|
* Shared portal LLM call for the memory pipeline.
|
|
6145
6190
|
*
|
|
@@ -9390,6 +9435,28 @@ interface ReflectOptions extends RecallOptions, PortalLlmAuth {
|
|
|
9390
9435
|
maxTokens?: number;
|
|
9391
9436
|
/** Override the grounding system prompt. */
|
|
9392
9437
|
systemPrompt?: string;
|
|
9438
|
+
/**
|
|
9439
|
+
* Extra caller instruction to carry on the USER turn, between the question and
|
|
9440
|
+
* the evidence block (see the `userMessage` assembly below). This is the slot a
|
|
9441
|
+
* background caller uses to keep its per-request data OUT of the system message
|
|
9442
|
+
* without colliding with the numbered evidence list — profile-facet synthesis
|
|
9443
|
+
* puts its section label, guidance and response-field hint here so its system
|
|
9444
|
+
* half can stay fixed and server-ownable.
|
|
9445
|
+
*/
|
|
9446
|
+
userInstructions?: string;
|
|
9447
|
+
/**
|
|
9448
|
+
* Class-B task name for the `X-Anuma-Task-Type` header, or nothing.
|
|
9449
|
+
*
|
|
9450
|
+
* Deliberately OPTIONAL and unset by default. reflect() also answers the user's
|
|
9451
|
+
* OWN question, and that traffic is chat, not an internal flow — declaring a
|
|
9452
|
+
* task type unconditionally here would put an internal-flow name on real
|
|
9453
|
+
* conversation, which is the same boundary `INTERNAL_FLOW_MARKER` draws
|
|
9454
|
+
* (reflect is deliberately unmarked; its background caller marks its own
|
|
9455
|
+
* prompt — see ../internalFlowMarker.ts). So the name is per call: only a
|
|
9456
|
+
* caller with ONE fixed purpose passes
|
|
9457
|
+
* one, and today that is profile-facet synthesis (`memory_profile_synth`).
|
|
9458
|
+
*/
|
|
9459
|
+
taskType?: TaskType;
|
|
9393
9460
|
/** Endpoint for the answer LLM. */
|
|
9394
9461
|
baseUrl?: string;
|
|
9395
9462
|
/** Override fetch (for tests). */
|
package/dist/react/index.d.ts
CHANGED
|
@@ -6140,6 +6140,51 @@ declare function getMemoriesByEntityNamesOp(ctx: EntityOperationsContext, entity
|
|
|
6140
6140
|
*/
|
|
6141
6141
|
declare function getEntitiesByMemoryIdsOp(ctx: EntityOperationsContext, memoryIds: readonly string[]): Promise<Map<string, Set<string>>>;
|
|
6142
6142
|
|
|
6143
|
+
/**
|
|
6144
|
+
* Class-B task types — the vocabulary of the `X-Anuma-Task-Type` header.
|
|
6145
|
+
*
|
|
6146
|
+
* A Class-B flow has ONE fixed purpose and no conversation: extract memories
|
|
6147
|
+
* from a snippet, pick a topic, consolidate duplicates, classify a query. Today
|
|
6148
|
+
* each of those builds its own system prompt on the client and sends it as a
|
|
6149
|
+
* `role:system` message, which makes the prompt a suggestion rather than a
|
|
6150
|
+
* contract — a caller can reword it into "you are a general assistant" and get
|
|
6151
|
+
* general chat at internal-endpoint prices.
|
|
6152
|
+
*
|
|
6153
|
+
* The fix is that the SERVER owns the text and the client only names the task.
|
|
6154
|
+
* This header is the name. The portal allowlists it against its own copy of this
|
|
6155
|
+
* enum (ai-portal `internal/systemprompt/tasks.go`) and DROPS anything it does
|
|
6156
|
+
* not recognise, so an unknown value degrades to "no task declared" rather than
|
|
6157
|
+
* failing the request.
|
|
6158
|
+
*
|
|
6159
|
+
* ⚠ These string values are a wire contract with ai-portal. Adding one here
|
|
6160
|
+
* before the portal knows it is harmless (it is dropped); renaming or removing
|
|
6161
|
+
* one silently stops that flow being counted. Keep them in step with
|
|
6162
|
+
* `AllTaskTypes` on the portal side, and see ai-portal
|
|
6163
|
+
* `docs/BACKEND_OWNED_TASK_PROMPTS.md` for the rollout.
|
|
6164
|
+
*
|
|
6165
|
+
* ⚠ RELEASE ORDER. This union is a PUBLIC type, so a split is breaking in both
|
|
6166
|
+
* directions and has to be sequenced:
|
|
6167
|
+
*
|
|
6168
|
+
* 1. merge + publish this package;
|
|
6169
|
+
* 2. bump `@anuma/sdk` in the consumer and switch its call sites to the new
|
|
6170
|
+
* names in the same commit.
|
|
6171
|
+
*
|
|
6172
|
+
* A consumer on the OLD version cannot compile the new names, and a consumer on
|
|
6173
|
+
* the NEW version cannot compile the retired ones (`app_inspiration` is gone
|
|
6174
|
+
* here, replaced by the three `app_inspiration_*` names). Both halves of that
|
|
6175
|
+
* are type-only — the wire itself always degrades safely, because the portal
|
|
6176
|
+
* drops any value it does not recognise. Concretely: ai-memoryless-client#5782
|
|
6177
|
+
* is blocked on this publish for exactly that reason.
|
|
6178
|
+
*
|
|
6179
|
+
* Deliberately ABSENT: agent personas, `reflect()` when it answers the user, and
|
|
6180
|
+
* the app-idea chat. Those carry an open conversation with the user, so a fixed
|
|
6181
|
+
* server-owned prompt would be wrong rather than merely premature — the same
|
|
6182
|
+
* boundary {@link INTERNAL_FLOW_MARKER} draws.
|
|
6183
|
+
*
|
|
6184
|
+
* @public
|
|
6185
|
+
*/
|
|
6186
|
+
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";
|
|
6187
|
+
|
|
6143
6188
|
/**
|
|
6144
6189
|
* Shared portal LLM call for the memory pipeline.
|
|
6145
6190
|
*
|
|
@@ -9390,6 +9435,28 @@ interface ReflectOptions extends RecallOptions, PortalLlmAuth {
|
|
|
9390
9435
|
maxTokens?: number;
|
|
9391
9436
|
/** Override the grounding system prompt. */
|
|
9392
9437
|
systemPrompt?: string;
|
|
9438
|
+
/**
|
|
9439
|
+
* Extra caller instruction to carry on the USER turn, between the question and
|
|
9440
|
+
* the evidence block (see the `userMessage` assembly below). This is the slot a
|
|
9441
|
+
* background caller uses to keep its per-request data OUT of the system message
|
|
9442
|
+
* without colliding with the numbered evidence list — profile-facet synthesis
|
|
9443
|
+
* puts its section label, guidance and response-field hint here so its system
|
|
9444
|
+
* half can stay fixed and server-ownable.
|
|
9445
|
+
*/
|
|
9446
|
+
userInstructions?: string;
|
|
9447
|
+
/**
|
|
9448
|
+
* Class-B task name for the `X-Anuma-Task-Type` header, or nothing.
|
|
9449
|
+
*
|
|
9450
|
+
* Deliberately OPTIONAL and unset by default. reflect() also answers the user's
|
|
9451
|
+
* OWN question, and that traffic is chat, not an internal flow — declaring a
|
|
9452
|
+
* task type unconditionally here would put an internal-flow name on real
|
|
9453
|
+
* conversation, which is the same boundary `INTERNAL_FLOW_MARKER` draws
|
|
9454
|
+
* (reflect is deliberately unmarked; its background caller marks its own
|
|
9455
|
+
* prompt — see ../internalFlowMarker.ts). So the name is per call: only a
|
|
9456
|
+
* caller with ONE fixed purpose passes
|
|
9457
|
+
* one, and today that is profile-facet synthesis (`memory_profile_synth`).
|
|
9458
|
+
*/
|
|
9459
|
+
taskType?: TaskType;
|
|
9393
9460
|
/** Endpoint for the answer LLM. */
|
|
9394
9461
|
baseUrl?: string;
|
|
9395
9462
|
/** Override fetch (for tests). */
|
package/dist/react/index.mjs
CHANGED
|
@@ -178,7 +178,7 @@ import {
|
|
|
178
178
|
updateVaultMemoryOp,
|
|
179
179
|
useEncryption,
|
|
180
180
|
vaultMemoryToStored
|
|
181
|
-
} from "./chunk-
|
|
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 =
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
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
|
-
|
|
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
|
|
9412
|
-
// and that traffic is chat, not an internal flow. See
|
|
9413
|
-
|
|
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
|
-
|
|
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
|
|
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") {
|
|
@@ -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-
|
|
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,
|
package/dist/server/index.cjs
CHANGED
|
@@ -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 =
|
|
30705
|
-
|
|
30706
|
-
|
|
30707
|
-
|
|
30708
|
-
|
|
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
|
-
|
|
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
|
|
31176
|
-
// and that traffic is chat, not an internal flow. See
|
|
31177
|
-
|
|
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
|
-
|
|
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
|
|
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") {
|