@anuma/sdk 6.14.1 → 6.15.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.
Files changed (32) hide show
  1. package/dist/client/index.cjs +13 -0
  2. package/dist/client/index.d.mts +187 -1
  3. package/dist/client/index.d.ts +187 -1
  4. package/dist/client/index.mjs +11 -0
  5. package/dist/expo/{chunk-O25LUKBK.mjs → chunk-3V3BBJLH.mjs} +66 -8
  6. package/dist/expo/{chunk-M4JSLFTQ.mjs → chunk-DIUQLXEH.mjs} +1 -1
  7. package/dist/expo/{chunk-JA7CDJCA.mjs → chunk-LUSKQQDD.mjs} +2 -2
  8. package/dist/{react/consolidate-LSDBMQDW.mjs → expo/consolidate-GTMXZZ4C.mjs} +2 -2
  9. package/dist/expo/index.cjs +68 -10
  10. package/dist/expo/index.d.mts +50 -12
  11. package/dist/expo/index.d.ts +50 -12
  12. package/dist/expo/index.mjs +5 -5
  13. package/dist/expo/{recall-JYVHWU6N.mjs → recall-GKYVPD2B.mjs} +2 -2
  14. package/dist/react/{chunk-2XLHV2XW.mjs → chunk-2GNOTRG4.mjs} +1 -1
  15. package/dist/react/{chunk-O25LUKBK.mjs → chunk-3V3BBJLH.mjs} +66 -8
  16. package/dist/react/{chunk-DNSPMVBY.mjs → chunk-PDKKPAXP.mjs} +2 -2
  17. package/dist/{expo/consolidate-S3KFA2WT.mjs → react/consolidate-Y2XWEZRQ.mjs} +2 -2
  18. package/dist/react/index.cjs +68 -10
  19. package/dist/react/index.d.mts +50 -12
  20. package/dist/react/index.d.ts +50 -12
  21. package/dist/react/index.mjs +5 -5
  22. package/dist/react/{recall-2BWBJWGG.mjs → recall-5HNAFHLL.mjs} +2 -2
  23. package/dist/server/{chunk-O7TB36P3.mjs → chunk-2QFK4L7Y.mjs} +2 -2
  24. package/dist/server/{chunk-7D3RI25K.mjs → chunk-H4G3DLDJ.mjs} +66 -8
  25. package/dist/server/{chunk-GP6YY6H5.mjs → chunk-JPLFOSB2.mjs} +1 -1
  26. package/dist/server/{consolidate-Z2JJP7LW.mjs → consolidate-CMMDTZ37.mjs} +2 -2
  27. package/dist/server/index.cjs +68 -10
  28. package/dist/server/index.d.mts +50 -12
  29. package/dist/server/index.d.ts +50 -12
  30. package/dist/server/index.mjs +5 -5
  31. package/dist/server/{recall-VHU372MK.mjs → recall-EODNEKIA.mjs} +2 -2
  32. package/package.json +2 -2
@@ -57,6 +57,30 @@ function supportsResponseFormat(model, variant = "json_object") {
57
57
  const allow = variant === "json_schema" ? RESPONSE_SCHEMA_OK : RESPONSE_FORMAT_OK;
58
58
  return model.split("/").some((seg) => allow.has(seg));
59
59
  }
60
+ var CHAT_TO_RESPONSES_FIELDS = {
61
+ max_completion_tokens: "max_output_tokens"
62
+ };
63
+ function responsesExtra(extra) {
64
+ if (!extra) return {};
65
+ const out = {};
66
+ for (const [key, value] of Object.entries(extra)) {
67
+ const target = CHAT_TO_RESPONSES_FIELDS[key] ?? key;
68
+ if (target !== key && target in extra) continue;
69
+ out[target] = value;
70
+ }
71
+ return out;
72
+ }
73
+ var FOREIGN_ENDPOINT_SUFFIX = {
74
+ chat: "/responses",
75
+ responses: "/chat/completions"
76
+ };
77
+ function assertTransportMatchesEndpoint(transport, endpoint) {
78
+ const foreign = FOREIGN_ENDPOINT_SUFFIX[transport];
79
+ if (!endpoint.endsWith(foreign)) return;
80
+ throw new Error(
81
+ `endpointOverride "${endpoint}" is the other transport's endpoint, but transport is "${transport}". The request body is built for the transport, so this sends the wrong shape and 400s without retry.`
82
+ );
83
+ }
60
84
  var RETRYABLE_HTTP = /* @__PURE__ */ new Set([408, 409, 425, 429]);
61
85
  function isRetryableStatus(status) {
62
86
  return status >= 500 || RETRYABLE_HTTP.has(status);
@@ -98,13 +122,14 @@ async function resolvePortalAuthHeaders(auth, tag) {
98
122
  }
99
123
  async function callPortalJsonCompletion(req) {
100
124
  const log = getLogger();
101
- let endpoint = "/api/v1/chat/completions";
125
+ let endpoint = req.transport === "responses" ? "/api/v1/responses" : "/api/v1/chat/completions";
102
126
  if (req.endpointOverride !== void 0) {
103
127
  const overrideValidation = validateEndpointOverride(req.endpointOverride);
104
128
  if (!overrideValidation.valid) {
105
129
  throw new Error(overrideValidation.message);
106
130
  }
107
131
  endpoint = overrideValidation.endpoint;
132
+ assertTransportMatchesEndpoint(req.transport ?? "chat", endpoint);
108
133
  }
109
134
  const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
110
135
  const startedAt = Date.now();
@@ -166,8 +191,8 @@ async function attemptPortalJson(req, endpoint) {
166
191
  const authHeaders = await resolvePortalAuthHeaders(req, req.tag);
167
192
  if (authHeaders === null)
168
193
  return { kind: "terminal", code: "auth-unavailable", reason: "auth unavailable (no token)" };
169
- const isAnthropic = req.model.startsWith("anthropic/");
170
- const prefill = isAnthropic ? "{" : "";
194
+ const usesPrefill = req.transport !== "responses" && req.model.startsWith("anthropic/");
195
+ const prefill = usesPrefill ? "{" : "";
171
196
  const messages = [
172
197
  // Every caller of this helper is a first-party BACKGROUND op (fact extraction,
173
198
  // topic sweep, consolidation, classifiers, graph traversal, query
@@ -186,13 +211,34 @@ async function attemptPortalJson(req, endpoint) {
186
211
  const supportsJsonObjectFormat = supportsResponseFormat(req.model);
187
212
  const controller = new AbortController();
188
213
  const timer = setTimeout(() => controller.abort(), timeoutMs);
189
- const requestBody = {
214
+ const requestBody = req.transport === "responses" ? {
215
+ model: req.model,
216
+ // Same system+user pair under the Responses-API field name. No prefill
217
+ // entry to strip: `usesPrefill` is false on this transport, so it was
218
+ // never pushed and the response-side restore is disarmed with it. An
219
+ // earlier version pushed it and sliced it back off here, which left
220
+ // `prefill` truthy and the restore still armed — the fix belonged at
221
+ // the decision, not at the two symptoms.
222
+ input: messages,
223
+ ...req.reasoning && { reasoning: req.reasoning },
224
+ // NO response_format. The Responses API spells structured output
225
+ // differently (`text.format`), and it is unverified against this
226
+ // portal — so this transport relies on the same strict-JSON system
227
+ // prompt plus `extractJsonCandidate` that every response_format-
228
+ // rejecting model already relies on. Verified end to end on dev:
229
+ // luna returns clean parseable JSON here without it. Add the field
230
+ // only with a per-provider check, exactly as RESPONSE_FORMAT_OK was
231
+ // earned.
232
+ ...responsesExtra(req.extra)
233
+ } : {
190
234
  model: req.model,
191
235
  messages,
192
236
  ...supportsJsonObjectFormat && { response_format: { type: "json_object" } },
193
237
  ...req.extra
194
238
  };
195
- if (!supportsJsonObjectFormat) delete requestBody.response_format;
239
+ if (req.transport === "responses" || !supportsJsonObjectFormat) {
240
+ delete requestBody.response_format;
241
+ }
196
242
  let response;
197
243
  try {
198
244
  response = await fetchImpl(`${baseUrl}${endpoint}`, {
@@ -322,13 +368,15 @@ function logPortalUsage(body, tag) {
322
368
  const root = asRecord(body);
323
369
  if (!root) return;
324
370
  const usage = asRecord(root.usage);
325
- const promptTokens = asCount(usage?.prompt_tokens);
326
- const completionTokens = asCount(usage?.completion_tokens);
371
+ const promptTokens = asCount(usage?.prompt_tokens) ?? asCount(usage?.input_tokens);
372
+ const completionTokens = asCount(usage?.completion_tokens) ?? asCount(usage?.output_tokens);
327
373
  if (promptTokens === void 0 && completionTokens === void 0) return;
328
- const cachedTokens = asCount(asRecord(root.portal)?.cached_tokens) ?? asCount(asRecord(usage?.prompt_tokens_details)?.cached_tokens) ?? 0;
374
+ const cachedTokens = asCount(asRecord(root.portal)?.cached_tokens) ?? asCount(asRecord(usage?.prompt_tokens_details)?.cached_tokens) ?? asCount(asRecord(usage?.input_tokens_details)?.cached_tokens) ?? 0;
375
+ const reasoningTokens = asCount(asRecord(usage?.output_tokens_details)?.reasoning_tokens) ?? asCount(asRecord(usage?.completion_tokens_details)?.reasoning_tokens);
329
376
  const parts = [];
330
377
  if (promptTokens !== void 0) parts.push(`prompt=${promptTokens}`);
331
378
  if (completionTokens !== void 0) parts.push(`completion=${completionTokens}`);
379
+ if (reasoningTokens !== void 0) parts.push(`reasoning=${reasoningTokens}`);
332
380
  parts.push(`cached=${cachedTokens}`);
333
381
  getLogger().debug(`[${tag}] usage ${parts.join(" ")}`);
334
382
  } catch {
@@ -336,12 +384,22 @@ function logPortalUsage(body, tag) {
336
384
  }
337
385
  function extractCompletionContent(body) {
338
386
  if (typeof body !== "object" || body === null) return null;
387
+ const responsesText = extractResponsesContent(body);
388
+ if (responsesText !== null) return responsesText;
339
389
  const choices = body.choices;
340
390
  if (!Array.isArray(choices) || choices.length === 0) return null;
341
391
  const first = choices[0];
342
392
  const content = first?.message?.content;
343
393
  return typeof content === "string" ? content : null;
344
394
  }
395
+ function extractResponsesContent(body) {
396
+ const root = asRecord(body);
397
+ if (!root) return null;
398
+ if (typeof root.output_text === "string" && root.output_text !== "") return root.output_text;
399
+ if (!Array.isArray(root.output)) return null;
400
+ const text = root.output.filter((item) => asRecord(item)?.type === "message").flatMap((item) => Array.isArray(item.content) ? item.content : []).map((part) => asRecord(part)?.text).filter((t) => typeof t === "string").join("");
401
+ return text;
402
+ }
345
403
 
346
404
  export {
347
405
  getLogger,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getLogger
3
- } from "./chunk-7D3RI25K.mjs";
3
+ } from "./chunk-H4G3DLDJ.mjs";
4
4
 
5
5
  // src/lib/pii/patterns.ts
6
6
  function luhnCheck(digits) {
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  notifyConsolidationFallback,
3
3
  resolvePiiRedactor
4
- } from "./chunk-GP6YY6H5.mjs";
4
+ } from "./chunk-JPLFOSB2.mjs";
5
5
  import {
6
6
  callPortalJsonCompletion
7
- } from "./chunk-7D3RI25K.mjs";
7
+ } from "./chunk-H4G3DLDJ.mjs";
8
8
  import "./chunk-3RPGXXPZ.mjs";
9
9
 
10
10
  // src/lib/memory/consolidate.ts
@@ -20611,6 +20611,23 @@ function supportsResponseFormat(model, variant = "json_object") {
20611
20611
  const allow = variant === "json_schema" ? RESPONSE_SCHEMA_OK : RESPONSE_FORMAT_OK;
20612
20612
  return model.split("/").some((seg) => allow.has(seg));
20613
20613
  }
20614
+ function responsesExtra(extra) {
20615
+ if (!extra) return {};
20616
+ const out = {};
20617
+ for (const [key, value] of Object.entries(extra)) {
20618
+ const target = CHAT_TO_RESPONSES_FIELDS[key] ?? key;
20619
+ if (target !== key && target in extra) continue;
20620
+ out[target] = value;
20621
+ }
20622
+ return out;
20623
+ }
20624
+ function assertTransportMatchesEndpoint(transport, endpoint) {
20625
+ const foreign = FOREIGN_ENDPOINT_SUFFIX[transport];
20626
+ if (!endpoint.endsWith(foreign)) return;
20627
+ throw new Error(
20628
+ `endpointOverride "${endpoint}" is the other transport's endpoint, but transport is "${transport}". The request body is built for the transport, so this sends the wrong shape and 400s without retry.`
20629
+ );
20630
+ }
20614
20631
  function isRetryableStatus(status) {
20615
20632
  return status >= 500 || RETRYABLE_HTTP.has(status);
20616
20633
  }
@@ -20651,13 +20668,14 @@ async function resolvePortalAuthHeaders(auth, tag) {
20651
20668
  }
20652
20669
  async function callPortalJsonCompletion(req) {
20653
20670
  const log = getLogger();
20654
- let endpoint = "/api/v1/chat/completions";
20671
+ let endpoint = req.transport === "responses" ? "/api/v1/responses" : "/api/v1/chat/completions";
20655
20672
  if (req.endpointOverride !== void 0) {
20656
20673
  const overrideValidation = validateEndpointOverride(req.endpointOverride);
20657
20674
  if (!overrideValidation.valid) {
20658
20675
  throw new Error(overrideValidation.message);
20659
20676
  }
20660
20677
  endpoint = overrideValidation.endpoint;
20678
+ assertTransportMatchesEndpoint(req.transport ?? "chat", endpoint);
20661
20679
  }
20662
20680
  const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
20663
20681
  const startedAt = Date.now();
@@ -20719,8 +20737,8 @@ async function attemptPortalJson(req, endpoint) {
20719
20737
  const authHeaders = await resolvePortalAuthHeaders(req, req.tag);
20720
20738
  if (authHeaders === null)
20721
20739
  return { kind: "terminal", code: "auth-unavailable", reason: "auth unavailable (no token)" };
20722
- const isAnthropic = req.model.startsWith("anthropic/");
20723
- const prefill = isAnthropic ? "{" : "";
20740
+ const usesPrefill = req.transport !== "responses" && req.model.startsWith("anthropic/");
20741
+ const prefill = usesPrefill ? "{" : "";
20724
20742
  const messages = [
20725
20743
  // Every caller of this helper is a first-party BACKGROUND op (fact extraction,
20726
20744
  // topic sweep, consolidation, classifiers, graph traversal, query
@@ -20739,13 +20757,34 @@ async function attemptPortalJson(req, endpoint) {
20739
20757
  const supportsJsonObjectFormat = supportsResponseFormat(req.model);
20740
20758
  const controller = new AbortController();
20741
20759
  const timer = setTimeout(() => controller.abort(), timeoutMs);
20742
- const requestBody = {
20760
+ const requestBody = req.transport === "responses" ? {
20761
+ model: req.model,
20762
+ // Same system+user pair under the Responses-API field name. No prefill
20763
+ // entry to strip: `usesPrefill` is false on this transport, so it was
20764
+ // never pushed and the response-side restore is disarmed with it. An
20765
+ // earlier version pushed it and sliced it back off here, which left
20766
+ // `prefill` truthy and the restore still armed — the fix belonged at
20767
+ // the decision, not at the two symptoms.
20768
+ input: messages,
20769
+ ...req.reasoning && { reasoning: req.reasoning },
20770
+ // NO response_format. The Responses API spells structured output
20771
+ // differently (`text.format`), and it is unverified against this
20772
+ // portal — so this transport relies on the same strict-JSON system
20773
+ // prompt plus `extractJsonCandidate` that every response_format-
20774
+ // rejecting model already relies on. Verified end to end on dev:
20775
+ // luna returns clean parseable JSON here without it. Add the field
20776
+ // only with a per-provider check, exactly as RESPONSE_FORMAT_OK was
20777
+ // earned.
20778
+ ...responsesExtra(req.extra)
20779
+ } : {
20743
20780
  model: req.model,
20744
20781
  messages,
20745
20782
  ...supportsJsonObjectFormat && { response_format: { type: "json_object" } },
20746
20783
  ...req.extra
20747
20784
  };
20748
- if (!supportsJsonObjectFormat) delete requestBody.response_format;
20785
+ if (req.transport === "responses" || !supportsJsonObjectFormat) {
20786
+ delete requestBody.response_format;
20787
+ }
20749
20788
  let response;
20750
20789
  try {
20751
20790
  response = await fetchImpl(`${baseUrl}${endpoint}`, {
@@ -20875,13 +20914,15 @@ function logPortalUsage(body, tag) {
20875
20914
  const root = asRecord(body);
20876
20915
  if (!root) return;
20877
20916
  const usage = asRecord(root.usage);
20878
- const promptTokens = asCount(usage?.prompt_tokens);
20879
- const completionTokens = asCount(usage?.completion_tokens);
20917
+ const promptTokens = asCount(usage?.prompt_tokens) ?? asCount(usage?.input_tokens);
20918
+ const completionTokens = asCount(usage?.completion_tokens) ?? asCount(usage?.output_tokens);
20880
20919
  if (promptTokens === void 0 && completionTokens === void 0) return;
20881
- const cachedTokens = asCount(asRecord(root.portal)?.cached_tokens) ?? asCount(asRecord(usage?.prompt_tokens_details)?.cached_tokens) ?? 0;
20920
+ const cachedTokens = asCount(asRecord(root.portal)?.cached_tokens) ?? asCount(asRecord(usage?.prompt_tokens_details)?.cached_tokens) ?? asCount(asRecord(usage?.input_tokens_details)?.cached_tokens) ?? 0;
20921
+ const reasoningTokens = asCount(asRecord(usage?.output_tokens_details)?.reasoning_tokens) ?? asCount(asRecord(usage?.completion_tokens_details)?.reasoning_tokens);
20882
20922
  const parts = [];
20883
20923
  if (promptTokens !== void 0) parts.push(`prompt=${promptTokens}`);
20884
20924
  if (completionTokens !== void 0) parts.push(`completion=${completionTokens}`);
20925
+ if (reasoningTokens !== void 0) parts.push(`reasoning=${reasoningTokens}`);
20885
20926
  parts.push(`cached=${cachedTokens}`);
20886
20927
  getLogger().debug(`[${tag}] usage ${parts.join(" ")}`);
20887
20928
  } catch {
@@ -20889,13 +20930,23 @@ function logPortalUsage(body, tag) {
20889
20930
  }
20890
20931
  function extractCompletionContent(body) {
20891
20932
  if (typeof body !== "object" || body === null) return null;
20933
+ const responsesText = extractResponsesContent(body);
20934
+ if (responsesText !== null) return responsesText;
20892
20935
  const choices = body.choices;
20893
20936
  if (!Array.isArray(choices) || choices.length === 0) return null;
20894
20937
  const first = choices[0];
20895
20938
  const content = first?.message?.content;
20896
20939
  return typeof content === "string" ? content : null;
20897
20940
  }
20898
- var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, RETRYABLE_HTTP;
20941
+ function extractResponsesContent(body) {
20942
+ const root = asRecord(body);
20943
+ if (!root) return null;
20944
+ if (typeof root.output_text === "string" && root.output_text !== "") return root.output_text;
20945
+ if (!Array.isArray(root.output)) return null;
20946
+ const text12 = root.output.filter((item) => asRecord(item)?.type === "message").flatMap((item) => Array.isArray(item.content) ? item.content : []).map((part) => asRecord(part)?.text).filter((t) => typeof t === "string").join("");
20947
+ return text12;
20948
+ }
20949
+ var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, CHAT_TO_RESPONSES_FIELDS, FOREIGN_ENDPOINT_SUFFIX, RETRYABLE_HTTP;
20899
20950
  var init_portalLlm = __esm({
20900
20951
  "src/lib/memory/portalLlm.ts"() {
20901
20952
  "use strict";
@@ -20907,6 +20958,13 @@ var init_portalLlm = __esm({
20907
20958
  RESPONSE_FORMAT_OK = /* @__PURE__ */ new Set(["openai", "inclusionai", "deepseek"]);
20908
20959
  RESPONSE_SCHEMA_OK = /* @__PURE__ */ new Set(["openai"]);
20909
20960
  JSON_CONTRACT_REMINDER = "Output ONLY the strict JSON object requested. No prose, no explanation, no markdown.";
20961
+ CHAT_TO_RESPONSES_FIELDS = {
20962
+ max_completion_tokens: "max_output_tokens"
20963
+ };
20964
+ FOREIGN_ENDPOINT_SUFFIX = {
20965
+ chat: "/responses",
20966
+ responses: "/chat/completions"
20967
+ };
20910
20968
  RETRYABLE_HTTP = /* @__PURE__ */ new Set([408, 409, 425, 429]);
20911
20969
  }
20912
20970
  });
@@ -27426,7 +27484,7 @@ async function ingestPublishedPhotoMemoriesOp(ctx, rows) {
27426
27484
  ({ row, content }) => ctx.vaultMemoryCollection.prepareCreate((record) => {
27427
27485
  record._raw.id = row.memoryId;
27428
27486
  record._setRaw("content", content);
27429
- record._setRaw("scope", "private");
27487
+ record._setRaw("scope", "shared");
27430
27488
  record._setRaw("folder_id", null);
27431
27489
  record._setRaw("user_id", ctx.userId ?? null);
27432
27490
  record._setRaw("is_deleted", false);
@@ -6253,15 +6253,26 @@ declare function extractAndRetain(messages: AutoExtractMessage[], retainCtx: Ret
6253
6253
  }>;
6254
6254
 
6255
6255
  /**
6256
- * People Nearby cross-user visibility. ORTHOGONAL to `scope` (model access):
6257
- * - `private`: local-only (default; null column grandfathered as private)
6258
- * - `public`: embedding + plaintext may be published (compatibility matching,
6259
- * profile display, discovery answers, digital-twin prompts)
6256
+ * LEGACY. This column no longer decides anything.
6257
+ *
6258
+ * `scope === 'shared'` is now the single publication axis: it means the memory
6259
+ * is on the People Nearby profile, its text goes to the nearby server, and it
6260
+ * may inform what strangers see. `scope: 'private'` means it is not. There is
6261
+ * no second control, and scope no longer gates model access either — every
6262
+ * model tier reads every memory. Do NOT reason about publication from this
6263
+ * type; read `scope`.
6264
+ *
6265
+ * The column survives in the schema and is still stamped on writes so that
6266
+ * stored rows stay readable by builds that predate the change. Nothing on a
6267
+ * current build reads it to make a decision.
6268
+ *
6269
+ * Values, kept for tolerance of already-stored data:
6270
+ * - `private`: local-only (a null column reads as private)
6271
+ * - `public`: the row was published
6260
6272
  *
6261
6273
  * TWO TIERS ONLY (decided 2026-07-27). An earlier design had a middle
6262
6274
  * `matchable` tier that published a memory's embedding while keeping its text
6263
- * on-device; it was dropped, so a memory either never leaves the device or is
6264
- * published with its content. Any unrecognised stored value — including a
6275
+ * on-device; it was dropped. Any unrecognised stored value — including a
6265
6276
  * `matchable` row written by a pre-release build — reads as `private`, which is
6266
6277
  * the fail-safe direction: it un-publishes rather than exposes.
6267
6278
  */
@@ -6853,12 +6864,16 @@ declare function updateVaultMemoryEmbeddingOp(ctx: VaultMemoryOperationsContext,
6853
6864
  * candidate to be reconciled against the vault — it is an already-decided
6854
6865
  * fact the server has published.
6855
6866
  *
6856
- * The rows are written `visibility: "public"` because they genuinely ARE
6857
- * published — the reconciler reads local visibility as the user's intent, and
6858
- * anything else would make it immediately revoke a memory the user never turned
6859
- * off. They carry `source: SOURCE_PHOTO`, which is what exempts them from
6860
- * decay's auto-archive; see the note there for why archiving one would silently
6861
- * unpublish it.
6867
+ * The rows are written `scope: "shared"` because they genuinely ARE published.
6868
+ * `scope === 'shared'` is the publication axis — the single one. The reconciler
6869
+ * reads local scope as the user's intent, and it builds its `local` set from a
6870
+ * vault read narrowed to `{scopes: ['shared']}`. A row stamped anything else
6871
+ * never enters that read, so it is never adopted into the publish ledger, and
6872
+ * `toRevoke` is computed only over ids the ledger holds. The user's off-toggle
6873
+ * would then do nothing server-side while nearby kept serving the memory. The
6874
+ * `visibility` column is legacy: nothing reads it any more. They carry
6875
+ * `source: SOURCE_PHOTO`, which is what exempts them from decay's auto-archive;
6876
+ * see the note there for why archiving one would silently unpublish it.
6862
6877
  *
6863
6878
  * No embedding is computed here. `searchTool`'s un-embedded lane embeds any row
6864
6879
  * lacking a vector at search time, uses it in that same search and persists it
@@ -6906,6 +6921,29 @@ interface PhotoIngestResult {
6906
6921
  * class of bug as publishing a version the user has since changed. Re-ingesting
6907
6922
  * changed text is a follow-up that needs a merge decision, not a clobber.
6908
6923
  *
6924
+ * That skip is also why this op does NOT repair a row an older build stamped
6925
+ * `scope: "private"`. Such a row is present, so ingest leaves it alone, and it
6926
+ * stays outside the published-set read. The user's switch cannot revoke it.
6927
+ * Repair belongs in a one-time pass at upgrade. Do not add it here, for two
6928
+ * reasons:
6929
+ *
6930
+ * - **The two states are indistinguishable from this op.** After this build a
6931
+ * `photo:` row with `scope: "private"` is also exactly what the user's own
6932
+ * off-toggle produces. The toggle calls `updateVaultMemoryOp`, which writes
6933
+ * `scope` and never touches `visibility`, so the mis-stamped row and the
6934
+ * turned-off row carry identical columns. No test here can tell them apart.
6935
+ *
6936
+ * - **A re-stamp here would revert the off-toggle.** The pass runs ingest
6937
+ * BEFORE it reads the vault, so a row re-stamped `shared` re-enters the
6938
+ * desired set on that same pass and never reaches `toRevoke`. The switch
6939
+ * would spring back to "on" and nearby would keep serving the memory. That
6940
+ * is worse than the bug it repairs.
6941
+ *
6942
+ * A one-time pass does not have the ambiguity. Before this build the switch
6943
+ * already read "off" for these rows, because it reads `scope`, so no user can
6944
+ * have turned one off. Every `private` photo row at that instant came from the
6945
+ * old stamp.
6946
+ *
6909
6947
  * Non-`photo:` ids are ignored: everything else in the published set is a
6910
6948
  * client-published memory that by definition already lives in this vault (or in
6911
6949
  * another device's, which is not ours to recreate).
@@ -6253,15 +6253,26 @@ declare function extractAndRetain(messages: AutoExtractMessage[], retainCtx: Ret
6253
6253
  }>;
6254
6254
 
6255
6255
  /**
6256
- * People Nearby cross-user visibility. ORTHOGONAL to `scope` (model access):
6257
- * - `private`: local-only (default; null column grandfathered as private)
6258
- * - `public`: embedding + plaintext may be published (compatibility matching,
6259
- * profile display, discovery answers, digital-twin prompts)
6256
+ * LEGACY. This column no longer decides anything.
6257
+ *
6258
+ * `scope === 'shared'` is now the single publication axis: it means the memory
6259
+ * is on the People Nearby profile, its text goes to the nearby server, and it
6260
+ * may inform what strangers see. `scope: 'private'` means it is not. There is
6261
+ * no second control, and scope no longer gates model access either — every
6262
+ * model tier reads every memory. Do NOT reason about publication from this
6263
+ * type; read `scope`.
6264
+ *
6265
+ * The column survives in the schema and is still stamped on writes so that
6266
+ * stored rows stay readable by builds that predate the change. Nothing on a
6267
+ * current build reads it to make a decision.
6268
+ *
6269
+ * Values, kept for tolerance of already-stored data:
6270
+ * - `private`: local-only (a null column reads as private)
6271
+ * - `public`: the row was published
6260
6272
  *
6261
6273
  * TWO TIERS ONLY (decided 2026-07-27). An earlier design had a middle
6262
6274
  * `matchable` tier that published a memory's embedding while keeping its text
6263
- * on-device; it was dropped, so a memory either never leaves the device or is
6264
- * published with its content. Any unrecognised stored value — including a
6275
+ * on-device; it was dropped. Any unrecognised stored value — including a
6265
6276
  * `matchable` row written by a pre-release build — reads as `private`, which is
6266
6277
  * the fail-safe direction: it un-publishes rather than exposes.
6267
6278
  */
@@ -6853,12 +6864,16 @@ declare function updateVaultMemoryEmbeddingOp(ctx: VaultMemoryOperationsContext,
6853
6864
  * candidate to be reconciled against the vault — it is an already-decided
6854
6865
  * fact the server has published.
6855
6866
  *
6856
- * The rows are written `visibility: "public"` because they genuinely ARE
6857
- * published — the reconciler reads local visibility as the user's intent, and
6858
- * anything else would make it immediately revoke a memory the user never turned
6859
- * off. They carry `source: SOURCE_PHOTO`, which is what exempts them from
6860
- * decay's auto-archive; see the note there for why archiving one would silently
6861
- * unpublish it.
6867
+ * The rows are written `scope: "shared"` because they genuinely ARE published.
6868
+ * `scope === 'shared'` is the publication axis — the single one. The reconciler
6869
+ * reads local scope as the user's intent, and it builds its `local` set from a
6870
+ * vault read narrowed to `{scopes: ['shared']}`. A row stamped anything else
6871
+ * never enters that read, so it is never adopted into the publish ledger, and
6872
+ * `toRevoke` is computed only over ids the ledger holds. The user's off-toggle
6873
+ * would then do nothing server-side while nearby kept serving the memory. The
6874
+ * `visibility` column is legacy: nothing reads it any more. They carry
6875
+ * `source: SOURCE_PHOTO`, which is what exempts them from decay's auto-archive;
6876
+ * see the note there for why archiving one would silently unpublish it.
6862
6877
  *
6863
6878
  * No embedding is computed here. `searchTool`'s un-embedded lane embeds any row
6864
6879
  * lacking a vector at search time, uses it in that same search and persists it
@@ -6906,6 +6921,29 @@ interface PhotoIngestResult {
6906
6921
  * class of bug as publishing a version the user has since changed. Re-ingesting
6907
6922
  * changed text is a follow-up that needs a merge decision, not a clobber.
6908
6923
  *
6924
+ * That skip is also why this op does NOT repair a row an older build stamped
6925
+ * `scope: "private"`. Such a row is present, so ingest leaves it alone, and it
6926
+ * stays outside the published-set read. The user's switch cannot revoke it.
6927
+ * Repair belongs in a one-time pass at upgrade. Do not add it here, for two
6928
+ * reasons:
6929
+ *
6930
+ * - **The two states are indistinguishable from this op.** After this build a
6931
+ * `photo:` row with `scope: "private"` is also exactly what the user's own
6932
+ * off-toggle produces. The toggle calls `updateVaultMemoryOp`, which writes
6933
+ * `scope` and never touches `visibility`, so the mis-stamped row and the
6934
+ * turned-off row carry identical columns. No test here can tell them apart.
6935
+ *
6936
+ * - **A re-stamp here would revert the off-toggle.** The pass runs ingest
6937
+ * BEFORE it reads the vault, so a row re-stamped `shared` re-enters the
6938
+ * desired set on that same pass and never reaches `toRevoke`. The switch
6939
+ * would spring back to "on" and nearby would keep serving the memory. That
6940
+ * is worse than the bug it repairs.
6941
+ *
6942
+ * A one-time pass does not have the ambiguity. Before this build the switch
6943
+ * already read "off" for these rows, because it reads `scope`, so no user can
6944
+ * have turned one off. Every `private` photo row at that instant came from the
6945
+ * old stamp.
6946
+ *
6909
6947
  * Non-`photo:` ids are ignored: everything else in the published set is a
6910
6948
  * client-published memory that by definition already lives in this vault (or in
6911
6949
  * another device's, which is not ours to recreate).
@@ -141,12 +141,12 @@ import {
141
141
  updateVaultMemoryOp,
142
142
  upsertMessageOp,
143
143
  vaultMemoryToStored
144
- } from "./chunk-O7TB36P3.mjs";
144
+ } from "./chunk-2QFK4L7Y.mjs";
145
145
  import {
146
146
  createStreamingDeAnonymizer,
147
147
  notifyConsolidationFallback,
148
148
  resolvePiiRedactor
149
- } from "./chunk-GP6YY6H5.mjs";
149
+ } from "./chunk-JPLFOSB2.mjs";
150
150
  import {
151
151
  INTERNAL_FLOW_MARKER,
152
152
  callPortalJsonCompletion,
@@ -157,7 +157,7 @@ import {
157
157
  taskTypeHeader,
158
158
  validateEndpointOverride,
159
159
  withInternalFlowMarker
160
- } from "./chunk-7D3RI25K.mjs";
160
+ } from "./chunk-H4G3DLDJ.mjs";
161
161
  import {
162
162
  BASE_URL,
163
163
  __commonJS,
@@ -3736,7 +3736,7 @@ async function ingestPublishedPhotoMemoriesOp(ctx, rows) {
3736
3736
  ({ row, content }) => ctx.vaultMemoryCollection.prepareCreate((record) => {
3737
3737
  record._raw.id = row.memoryId;
3738
3738
  record._setRaw("content", content);
3739
- record._setRaw("scope", "private");
3739
+ record._setRaw("scope", "shared");
3740
3740
  record._setRaw("folder_id", null);
3741
3741
  record._setRaw("user_id", ctx.userId ?? null);
3742
3742
  record._setRaw("is_deleted", false);
@@ -5700,7 +5700,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
5700
5700
  content: m.content,
5701
5701
  similarity: m.similarity
5702
5702
  }));
5703
- const { consolidateMemory: doConsolidate } = await import("./consolidate-Z2JJP7LW.mjs");
5703
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-CMMDTZ37.mjs");
5704
5704
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
5705
5705
  if (decision.action === "create") return null;
5706
5706
  if (decision.action === "supersede" && decision.content) {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-O7TB36P3.mjs";
4
- import "./chunk-7D3RI25K.mjs";
3
+ } from "./chunk-2QFK4L7Y.mjs";
4
+ import "./chunk-H4G3DLDJ.mjs";
5
5
  import "./chunk-3RPGXXPZ.mjs";
6
6
  export {
7
7
  recall
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anuma/sdk",
3
- "version": "6.14.1",
3
+ "version": "6.15.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -160,7 +160,7 @@
160
160
  "homepage": "https://github.com/anuma-ai/sdk#readme",
161
161
  "packageManager": "pnpm@10.23.0",
162
162
  "dependencies": {
163
- "@anuma/portal": "1.0.0-next.20260811055217",
163
+ "@anuma/portal": "1.0.0-next.20260815023541",
164
164
  "@babel/parser": "^7.29.2",
165
165
  "@babel/types": "^7.29.0",
166
166
  "ai": "6.0.202",