@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
@@ -70,6 +70,30 @@ function supportsResponseFormat(model, variant = "json_object") {
70
70
  const allow = variant === "json_schema" ? RESPONSE_SCHEMA_OK : RESPONSE_FORMAT_OK;
71
71
  return model.split("/").some((seg) => allow.has(seg));
72
72
  }
73
+ var CHAT_TO_RESPONSES_FIELDS = {
74
+ max_completion_tokens: "max_output_tokens"
75
+ };
76
+ function responsesExtra(extra) {
77
+ if (!extra) return {};
78
+ const out = {};
79
+ for (const [key, value] of Object.entries(extra)) {
80
+ const target = CHAT_TO_RESPONSES_FIELDS[key] ?? key;
81
+ if (target !== key && target in extra) continue;
82
+ out[target] = value;
83
+ }
84
+ return out;
85
+ }
86
+ var FOREIGN_ENDPOINT_SUFFIX = {
87
+ chat: "/responses",
88
+ responses: "/chat/completions"
89
+ };
90
+ function assertTransportMatchesEndpoint(transport, endpoint) {
91
+ const foreign = FOREIGN_ENDPOINT_SUFFIX[transport];
92
+ if (!endpoint.endsWith(foreign)) return;
93
+ throw new Error(
94
+ `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.`
95
+ );
96
+ }
73
97
  var RETRYABLE_HTTP = /* @__PURE__ */ new Set([408, 409, 425, 429]);
74
98
  function isRetryableStatus(status) {
75
99
  return status >= 500 || RETRYABLE_HTTP.has(status);
@@ -111,13 +135,14 @@ async function resolvePortalAuthHeaders(auth, tag) {
111
135
  }
112
136
  async function callPortalJsonCompletion(req) {
113
137
  const log = getLogger();
114
- let endpoint = "/api/v1/chat/completions";
138
+ let endpoint = req.transport === "responses" ? "/api/v1/responses" : "/api/v1/chat/completions";
115
139
  if (req.endpointOverride !== void 0) {
116
140
  const overrideValidation = validateEndpointOverride(req.endpointOverride);
117
141
  if (!overrideValidation.valid) {
118
142
  throw new Error(overrideValidation.message);
119
143
  }
120
144
  endpoint = overrideValidation.endpoint;
145
+ assertTransportMatchesEndpoint(req.transport ?? "chat", endpoint);
121
146
  }
122
147
  const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
123
148
  const startedAt = Date.now();
@@ -179,8 +204,8 @@ async function attemptPortalJson(req, endpoint) {
179
204
  const authHeaders = await resolvePortalAuthHeaders(req, req.tag);
180
205
  if (authHeaders === null)
181
206
  return { kind: "terminal", code: "auth-unavailable", reason: "auth unavailable (no token)" };
182
- const isAnthropic = req.model.startsWith("anthropic/");
183
- const prefill = isAnthropic ? "{" : "";
207
+ const usesPrefill = req.transport !== "responses" && req.model.startsWith("anthropic/");
208
+ const prefill = usesPrefill ? "{" : "";
184
209
  const messages = [
185
210
  // Every caller of this helper is a first-party BACKGROUND op (fact extraction,
186
211
  // topic sweep, consolidation, classifiers, graph traversal, query
@@ -199,13 +224,34 @@ async function attemptPortalJson(req, endpoint) {
199
224
  const supportsJsonObjectFormat = supportsResponseFormat(req.model);
200
225
  const controller = new AbortController();
201
226
  const timer = setTimeout(() => controller.abort(), timeoutMs);
202
- const requestBody = {
227
+ const requestBody = req.transport === "responses" ? {
228
+ model: req.model,
229
+ // Same system+user pair under the Responses-API field name. No prefill
230
+ // entry to strip: `usesPrefill` is false on this transport, so it was
231
+ // never pushed and the response-side restore is disarmed with it. An
232
+ // earlier version pushed it and sliced it back off here, which left
233
+ // `prefill` truthy and the restore still armed — the fix belonged at
234
+ // the decision, not at the two symptoms.
235
+ input: messages,
236
+ ...req.reasoning && { reasoning: req.reasoning },
237
+ // NO response_format. The Responses API spells structured output
238
+ // differently (`text.format`), and it is unverified against this
239
+ // portal — so this transport relies on the same strict-JSON system
240
+ // prompt plus `extractJsonCandidate` that every response_format-
241
+ // rejecting model already relies on. Verified end to end on dev:
242
+ // luna returns clean parseable JSON here without it. Add the field
243
+ // only with a per-provider check, exactly as RESPONSE_FORMAT_OK was
244
+ // earned.
245
+ ...responsesExtra(req.extra)
246
+ } : {
203
247
  model: req.model,
204
248
  messages,
205
249
  ...supportsJsonObjectFormat && { response_format: { type: "json_object" } },
206
250
  ...req.extra
207
251
  };
208
- if (!supportsJsonObjectFormat) delete requestBody.response_format;
252
+ if (req.transport === "responses" || !supportsJsonObjectFormat) {
253
+ delete requestBody.response_format;
254
+ }
209
255
  let response;
210
256
  try {
211
257
  response = await fetchImpl(`${baseUrl}${endpoint}`, {
@@ -335,13 +381,15 @@ function logPortalUsage(body, tag) {
335
381
  const root = asRecord(body);
336
382
  if (!root) return;
337
383
  const usage = asRecord(root.usage);
338
- const promptTokens = asCount(usage?.prompt_tokens);
339
- const completionTokens = asCount(usage?.completion_tokens);
384
+ const promptTokens = asCount(usage?.prompt_tokens) ?? asCount(usage?.input_tokens);
385
+ const completionTokens = asCount(usage?.completion_tokens) ?? asCount(usage?.output_tokens);
340
386
  if (promptTokens === void 0 && completionTokens === void 0) return;
341
- const cachedTokens = asCount(asRecord(root.portal)?.cached_tokens) ?? asCount(asRecord(usage?.prompt_tokens_details)?.cached_tokens) ?? 0;
387
+ 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;
388
+ const reasoningTokens = asCount(asRecord(usage?.output_tokens_details)?.reasoning_tokens) ?? asCount(asRecord(usage?.completion_tokens_details)?.reasoning_tokens);
342
389
  const parts = [];
343
390
  if (promptTokens !== void 0) parts.push(`prompt=${promptTokens}`);
344
391
  if (completionTokens !== void 0) parts.push(`completion=${completionTokens}`);
392
+ if (reasoningTokens !== void 0) parts.push(`reasoning=${reasoningTokens}`);
345
393
  parts.push(`cached=${cachedTokens}`);
346
394
  getLogger().debug(`[${tag}] usage ${parts.join(" ")}`);
347
395
  } catch {
@@ -349,12 +397,22 @@ function logPortalUsage(body, tag) {
349
397
  }
350
398
  function extractCompletionContent(body) {
351
399
  if (typeof body !== "object" || body === null) return null;
400
+ const responsesText = extractResponsesContent(body);
401
+ if (responsesText !== null) return responsesText;
352
402
  const choices = body.choices;
353
403
  if (!Array.isArray(choices) || choices.length === 0) return null;
354
404
  const first = choices[0];
355
405
  const content = first?.message?.content;
356
406
  return typeof content === "string" ? content : null;
357
407
  }
408
+ function extractResponsesContent(body) {
409
+ const root = asRecord(body);
410
+ if (!root) return null;
411
+ if (typeof root.output_text === "string" && root.output_text !== "") return root.output_text;
412
+ if (!Array.isArray(root.output)) return null;
413
+ 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("");
414
+ return text;
415
+ }
358
416
 
359
417
  export {
360
418
  consoleLogger,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  callPortalJsonCompletion,
3
3
  getLogger
4
- } from "./chunk-O25LUKBK.mjs";
4
+ } from "./chunk-3V3BBJLH.mjs";
5
5
  import {
6
6
  BASE_URL,
7
7
  createClientConfig
@@ -7169,7 +7169,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
7169
7169
  subQueries = decomp.subQueries;
7170
7170
  }
7171
7171
  }
7172
- const { recall: recall2 } = await import("./recall-2BWBJWGG.mjs");
7172
+ const { recall: recall2 } = await import("./recall-5HNAFHLL.mjs");
7173
7173
  let recallDegraded = [];
7174
7174
  const result = await recall2(
7175
7175
  query,
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  notifyConsolidationFallback,
3
3
  resolvePiiRedactor
4
- } from "./chunk-M4JSLFTQ.mjs";
4
+ } from "./chunk-2GNOTRG4.mjs";
5
5
  import {
6
6
  callPortalJsonCompletion
7
- } from "./chunk-O25LUKBK.mjs";
7
+ } from "./chunk-3V3BBJLH.mjs";
8
8
  import "./chunk-GYKJOWEO.mjs";
9
9
 
10
10
  // src/lib/memory/consolidate.ts
@@ -6429,6 +6429,23 @@ function supportsResponseFormat(model, variant = "json_object") {
6429
6429
  const allow = variant === "json_schema" ? RESPONSE_SCHEMA_OK : RESPONSE_FORMAT_OK;
6430
6430
  return model.split("/").some((seg) => allow.has(seg));
6431
6431
  }
6432
+ function responsesExtra(extra) {
6433
+ if (!extra) return {};
6434
+ const out = {};
6435
+ for (const [key, value] of Object.entries(extra)) {
6436
+ const target = CHAT_TO_RESPONSES_FIELDS[key] ?? key;
6437
+ if (target !== key && target in extra) continue;
6438
+ out[target] = value;
6439
+ }
6440
+ return out;
6441
+ }
6442
+ function assertTransportMatchesEndpoint(transport, endpoint) {
6443
+ const foreign = FOREIGN_ENDPOINT_SUFFIX[transport];
6444
+ if (!endpoint.endsWith(foreign)) return;
6445
+ throw new Error(
6446
+ `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.`
6447
+ );
6448
+ }
6432
6449
  function isRetryableStatus(status) {
6433
6450
  return status >= 500 || RETRYABLE_HTTP.has(status);
6434
6451
  }
@@ -6469,13 +6486,14 @@ async function resolvePortalAuthHeaders(auth, tag) {
6469
6486
  }
6470
6487
  async function callPortalJsonCompletion(req) {
6471
6488
  const log = getLogger();
6472
- let endpoint = "/api/v1/chat/completions";
6489
+ let endpoint = req.transport === "responses" ? "/api/v1/responses" : "/api/v1/chat/completions";
6473
6490
  if (req.endpointOverride !== void 0) {
6474
6491
  const overrideValidation = validateEndpointOverride(req.endpointOverride);
6475
6492
  if (!overrideValidation.valid) {
6476
6493
  throw new Error(overrideValidation.message);
6477
6494
  }
6478
6495
  endpoint = overrideValidation.endpoint;
6496
+ assertTransportMatchesEndpoint(req.transport ?? "chat", endpoint);
6479
6497
  }
6480
6498
  const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
6481
6499
  const startedAt = Date.now();
@@ -6537,8 +6555,8 @@ async function attemptPortalJson(req, endpoint) {
6537
6555
  const authHeaders = await resolvePortalAuthHeaders(req, req.tag);
6538
6556
  if (authHeaders === null)
6539
6557
  return { kind: "terminal", code: "auth-unavailable", reason: "auth unavailable (no token)" };
6540
- const isAnthropic = req.model.startsWith("anthropic/");
6541
- const prefill = isAnthropic ? "{" : "";
6558
+ const usesPrefill = req.transport !== "responses" && req.model.startsWith("anthropic/");
6559
+ const prefill = usesPrefill ? "{" : "";
6542
6560
  const messages = [
6543
6561
  // Every caller of this helper is a first-party BACKGROUND op (fact extraction,
6544
6562
  // topic sweep, consolidation, classifiers, graph traversal, query
@@ -6557,13 +6575,34 @@ async function attemptPortalJson(req, endpoint) {
6557
6575
  const supportsJsonObjectFormat = supportsResponseFormat(req.model);
6558
6576
  const controller = new AbortController();
6559
6577
  const timer = setTimeout(() => controller.abort(), timeoutMs);
6560
- const requestBody = {
6578
+ const requestBody = req.transport === "responses" ? {
6579
+ model: req.model,
6580
+ // Same system+user pair under the Responses-API field name. No prefill
6581
+ // entry to strip: `usesPrefill` is false on this transport, so it was
6582
+ // never pushed and the response-side restore is disarmed with it. An
6583
+ // earlier version pushed it and sliced it back off here, which left
6584
+ // `prefill` truthy and the restore still armed — the fix belonged at
6585
+ // the decision, not at the two symptoms.
6586
+ input: messages,
6587
+ ...req.reasoning && { reasoning: req.reasoning },
6588
+ // NO response_format. The Responses API spells structured output
6589
+ // differently (`text.format`), and it is unverified against this
6590
+ // portal — so this transport relies on the same strict-JSON system
6591
+ // prompt plus `extractJsonCandidate` that every response_format-
6592
+ // rejecting model already relies on. Verified end to end on dev:
6593
+ // luna returns clean parseable JSON here without it. Add the field
6594
+ // only with a per-provider check, exactly as RESPONSE_FORMAT_OK was
6595
+ // earned.
6596
+ ...responsesExtra(req.extra)
6597
+ } : {
6561
6598
  model: req.model,
6562
6599
  messages,
6563
6600
  ...supportsJsonObjectFormat && { response_format: { type: "json_object" } },
6564
6601
  ...req.extra
6565
6602
  };
6566
- if (!supportsJsonObjectFormat) delete requestBody.response_format;
6603
+ if (req.transport === "responses" || !supportsJsonObjectFormat) {
6604
+ delete requestBody.response_format;
6605
+ }
6567
6606
  let response;
6568
6607
  try {
6569
6608
  response = await fetchImpl(`${baseUrl}${endpoint}`, {
@@ -6693,13 +6732,15 @@ function logPortalUsage(body, tag) {
6693
6732
  const root = asRecord(body);
6694
6733
  if (!root) return;
6695
6734
  const usage = asRecord(root.usage);
6696
- const promptTokens = asCount(usage?.prompt_tokens);
6697
- const completionTokens = asCount(usage?.completion_tokens);
6735
+ const promptTokens = asCount(usage?.prompt_tokens) ?? asCount(usage?.input_tokens);
6736
+ const completionTokens = asCount(usage?.completion_tokens) ?? asCount(usage?.output_tokens);
6698
6737
  if (promptTokens === void 0 && completionTokens === void 0) return;
6699
- const cachedTokens = asCount(asRecord(root.portal)?.cached_tokens) ?? asCount(asRecord(usage?.prompt_tokens_details)?.cached_tokens) ?? 0;
6738
+ 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;
6739
+ const reasoningTokens = asCount(asRecord(usage?.output_tokens_details)?.reasoning_tokens) ?? asCount(asRecord(usage?.completion_tokens_details)?.reasoning_tokens);
6700
6740
  const parts = [];
6701
6741
  if (promptTokens !== void 0) parts.push(`prompt=${promptTokens}`);
6702
6742
  if (completionTokens !== void 0) parts.push(`completion=${completionTokens}`);
6743
+ if (reasoningTokens !== void 0) parts.push(`reasoning=${reasoningTokens}`);
6703
6744
  parts.push(`cached=${cachedTokens}`);
6704
6745
  getLogger().debug(`[${tag}] usage ${parts.join(" ")}`);
6705
6746
  } catch {
@@ -6707,13 +6748,23 @@ function logPortalUsage(body, tag) {
6707
6748
  }
6708
6749
  function extractCompletionContent(body) {
6709
6750
  if (typeof body !== "object" || body === null) return null;
6751
+ const responsesText = extractResponsesContent(body);
6752
+ if (responsesText !== null) return responsesText;
6710
6753
  const choices = body.choices;
6711
6754
  if (!Array.isArray(choices) || choices.length === 0) return null;
6712
6755
  const first = choices[0];
6713
6756
  const content = first?.message?.content;
6714
6757
  return typeof content === "string" ? content : null;
6715
6758
  }
6716
- var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, RETRYABLE_HTTP;
6759
+ function extractResponsesContent(body) {
6760
+ const root = asRecord(body);
6761
+ if (!root) return null;
6762
+ if (typeof root.output_text === "string" && root.output_text !== "") return root.output_text;
6763
+ if (!Array.isArray(root.output)) return null;
6764
+ 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("");
6765
+ return text12;
6766
+ }
6767
+ var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, CHAT_TO_RESPONSES_FIELDS, FOREIGN_ENDPOINT_SUFFIX, RETRYABLE_HTTP;
6717
6768
  var init_portalLlm = __esm({
6718
6769
  "src/lib/memory/portalLlm.ts"() {
6719
6770
  "use strict";
@@ -6725,6 +6776,13 @@ var init_portalLlm = __esm({
6725
6776
  RESPONSE_FORMAT_OK = /* @__PURE__ */ new Set(["openai", "inclusionai", "deepseek"]);
6726
6777
  RESPONSE_SCHEMA_OK = /* @__PURE__ */ new Set(["openai"]);
6727
6778
  JSON_CONTRACT_REMINDER = "Output ONLY the strict JSON object requested. No prose, no explanation, no markdown.";
6779
+ CHAT_TO_RESPONSES_FIELDS = {
6780
+ max_completion_tokens: "max_output_tokens"
6781
+ };
6782
+ FOREIGN_ENDPOINT_SUFFIX = {
6783
+ chat: "/responses",
6784
+ responses: "/chat/completions"
6785
+ };
6728
6786
  RETRYABLE_HTTP = /* @__PURE__ */ new Set([408, 409, 425, 429]);
6729
6787
  }
6730
6788
  });
@@ -17076,7 +17134,7 @@ async function ingestPublishedPhotoMemoriesOp(ctx, rows) {
17076
17134
  ({ row, content }) => ctx.vaultMemoryCollection.prepareCreate((record) => {
17077
17135
  record._raw.id = row.memoryId;
17078
17136
  record._setRaw("content", content);
17079
- record._setRaw("scope", "private");
17137
+ record._setRaw("scope", "shared");
17080
17138
  record._setRaw("folder_id", null);
17081
17139
  record._setRaw("user_id", ctx.userId ?? null);
17082
17140
  record._setRaw("is_deleted", false);
@@ -7604,15 +7604,26 @@ declare function extractAndRetain(messages: AutoExtractMessage[], retainCtx: Ret
7604
7604
  }>;
7605
7605
 
7606
7606
  /**
7607
- * People Nearby cross-user visibility. ORTHOGONAL to `scope` (model access):
7608
- * - `private`: local-only (default; null column grandfathered as private)
7609
- * - `public`: embedding + plaintext may be published (compatibility matching,
7610
- * profile display, discovery answers, digital-twin prompts)
7607
+ * LEGACY. This column no longer decides anything.
7608
+ *
7609
+ * `scope === 'shared'` is now the single publication axis: it means the memory
7610
+ * is on the People Nearby profile, its text goes to the nearby server, and it
7611
+ * may inform what strangers see. `scope: 'private'` means it is not. There is
7612
+ * no second control, and scope no longer gates model access either — every
7613
+ * model tier reads every memory. Do NOT reason about publication from this
7614
+ * type; read `scope`.
7615
+ *
7616
+ * The column survives in the schema and is still stamped on writes so that
7617
+ * stored rows stay readable by builds that predate the change. Nothing on a
7618
+ * current build reads it to make a decision.
7619
+ *
7620
+ * Values, kept for tolerance of already-stored data:
7621
+ * - `private`: local-only (a null column reads as private)
7622
+ * - `public`: the row was published
7611
7623
  *
7612
7624
  * TWO TIERS ONLY (decided 2026-07-27). An earlier design had a middle
7613
7625
  * `matchable` tier that published a memory's embedding while keeping its text
7614
- * on-device; it was dropped, so a memory either never leaves the device or is
7615
- * published with its content. Any unrecognised stored value — including a
7626
+ * on-device; it was dropped. Any unrecognised stored value — including a
7616
7627
  * `matchable` row written by a pre-release build — reads as `private`, which is
7617
7628
  * the fail-safe direction: it un-publishes rather than exposes.
7618
7629
  */
@@ -8352,12 +8363,16 @@ declare function updateVaultMemoryEmbeddingOp(ctx: VaultMemoryOperationsContext,
8352
8363
  * candidate to be reconciled against the vault — it is an already-decided
8353
8364
  * fact the server has published.
8354
8365
  *
8355
- * The rows are written `visibility: "public"` because they genuinely ARE
8356
- * published — the reconciler reads local visibility as the user's intent, and
8357
- * anything else would make it immediately revoke a memory the user never turned
8358
- * off. They carry `source: SOURCE_PHOTO`, which is what exempts them from
8359
- * decay's auto-archive; see the note there for why archiving one would silently
8360
- * unpublish it.
8366
+ * The rows are written `scope: "shared"` because they genuinely ARE published.
8367
+ * `scope === 'shared'` is the publication axis — the single one. The reconciler
8368
+ * reads local scope as the user's intent, and it builds its `local` set from a
8369
+ * vault read narrowed to `{scopes: ['shared']}`. A row stamped anything else
8370
+ * never enters that read, so it is never adopted into the publish ledger, and
8371
+ * `toRevoke` is computed only over ids the ledger holds. The user's off-toggle
8372
+ * would then do nothing server-side while nearby kept serving the memory. The
8373
+ * `visibility` column is legacy: nothing reads it any more. They carry
8374
+ * `source: SOURCE_PHOTO`, which is what exempts them from decay's auto-archive;
8375
+ * see the note there for why archiving one would silently unpublish it.
8361
8376
  *
8362
8377
  * No embedding is computed here. `searchTool`'s un-embedded lane embeds any row
8363
8378
  * lacking a vector at search time, uses it in that same search and persists it
@@ -8405,6 +8420,29 @@ interface PhotoIngestResult {
8405
8420
  * class of bug as publishing a version the user has since changed. Re-ingesting
8406
8421
  * changed text is a follow-up that needs a merge decision, not a clobber.
8407
8422
  *
8423
+ * That skip is also why this op does NOT repair a row an older build stamped
8424
+ * `scope: "private"`. Such a row is present, so ingest leaves it alone, and it
8425
+ * stays outside the published-set read. The user's switch cannot revoke it.
8426
+ * Repair belongs in a one-time pass at upgrade. Do not add it here, for two
8427
+ * reasons:
8428
+ *
8429
+ * - **The two states are indistinguishable from this op.** After this build a
8430
+ * `photo:` row with `scope: "private"` is also exactly what the user's own
8431
+ * off-toggle produces. The toggle calls `updateVaultMemoryOp`, which writes
8432
+ * `scope` and never touches `visibility`, so the mis-stamped row and the
8433
+ * turned-off row carry identical columns. No test here can tell them apart.
8434
+ *
8435
+ * - **A re-stamp here would revert the off-toggle.** The pass runs ingest
8436
+ * BEFORE it reads the vault, so a row re-stamped `shared` re-enters the
8437
+ * desired set on that same pass and never reaches `toRevoke`. The switch
8438
+ * would spring back to "on" and nearby would keep serving the memory. That
8439
+ * is worse than the bug it repairs.
8440
+ *
8441
+ * A one-time pass does not have the ambiguity. Before this build the switch
8442
+ * already read "off" for these rows, because it reads `scope`, so no user can
8443
+ * have turned one off. Every `private` photo row at that instant came from the
8444
+ * old stamp.
8445
+ *
8408
8446
  * Non-`photo:` ids are ignored: everything else in the published set is a
8409
8447
  * client-published memory that by definition already lives in this vault (or in
8410
8448
  * another device's, which is not ours to recreate).
@@ -7604,15 +7604,26 @@ declare function extractAndRetain(messages: AutoExtractMessage[], retainCtx: Ret
7604
7604
  }>;
7605
7605
 
7606
7606
  /**
7607
- * People Nearby cross-user visibility. ORTHOGONAL to `scope` (model access):
7608
- * - `private`: local-only (default; null column grandfathered as private)
7609
- * - `public`: embedding + plaintext may be published (compatibility matching,
7610
- * profile display, discovery answers, digital-twin prompts)
7607
+ * LEGACY. This column no longer decides anything.
7608
+ *
7609
+ * `scope === 'shared'` is now the single publication axis: it means the memory
7610
+ * is on the People Nearby profile, its text goes to the nearby server, and it
7611
+ * may inform what strangers see. `scope: 'private'` means it is not. There is
7612
+ * no second control, and scope no longer gates model access either — every
7613
+ * model tier reads every memory. Do NOT reason about publication from this
7614
+ * type; read `scope`.
7615
+ *
7616
+ * The column survives in the schema and is still stamped on writes so that
7617
+ * stored rows stay readable by builds that predate the change. Nothing on a
7618
+ * current build reads it to make a decision.
7619
+ *
7620
+ * Values, kept for tolerance of already-stored data:
7621
+ * - `private`: local-only (a null column reads as private)
7622
+ * - `public`: the row was published
7611
7623
  *
7612
7624
  * TWO TIERS ONLY (decided 2026-07-27). An earlier design had a middle
7613
7625
  * `matchable` tier that published a memory's embedding while keeping its text
7614
- * on-device; it was dropped, so a memory either never leaves the device or is
7615
- * published with its content. Any unrecognised stored value — including a
7626
+ * on-device; it was dropped. Any unrecognised stored value — including a
7616
7627
  * `matchable` row written by a pre-release build — reads as `private`, which is
7617
7628
  * the fail-safe direction: it un-publishes rather than exposes.
7618
7629
  */
@@ -8352,12 +8363,16 @@ declare function updateVaultMemoryEmbeddingOp(ctx: VaultMemoryOperationsContext,
8352
8363
  * candidate to be reconciled against the vault — it is an already-decided
8353
8364
  * fact the server has published.
8354
8365
  *
8355
- * The rows are written `visibility: "public"` because they genuinely ARE
8356
- * published — the reconciler reads local visibility as the user's intent, and
8357
- * anything else would make it immediately revoke a memory the user never turned
8358
- * off. They carry `source: SOURCE_PHOTO`, which is what exempts them from
8359
- * decay's auto-archive; see the note there for why archiving one would silently
8360
- * unpublish it.
8366
+ * The rows are written `scope: "shared"` because they genuinely ARE published.
8367
+ * `scope === 'shared'` is the publication axis — the single one. The reconciler
8368
+ * reads local scope as the user's intent, and it builds its `local` set from a
8369
+ * vault read narrowed to `{scopes: ['shared']}`. A row stamped anything else
8370
+ * never enters that read, so it is never adopted into the publish ledger, and
8371
+ * `toRevoke` is computed only over ids the ledger holds. The user's off-toggle
8372
+ * would then do nothing server-side while nearby kept serving the memory. The
8373
+ * `visibility` column is legacy: nothing reads it any more. They carry
8374
+ * `source: SOURCE_PHOTO`, which is what exempts them from decay's auto-archive;
8375
+ * see the note there for why archiving one would silently unpublish it.
8361
8376
  *
8362
8377
  * No embedding is computed here. `searchTool`'s un-embedded lane embeds any row
8363
8378
  * lacking a vector at search time, uses it in that same search and persists it
@@ -8405,6 +8420,29 @@ interface PhotoIngestResult {
8405
8420
  * class of bug as publishing a version the user has since changed. Re-ingesting
8406
8421
  * changed text is a follow-up that needs a merge decision, not a clobber.
8407
8422
  *
8423
+ * That skip is also why this op does NOT repair a row an older build stamped
8424
+ * `scope: "private"`. Such a row is present, so ingest leaves it alone, and it
8425
+ * stays outside the published-set read. The user's switch cannot revoke it.
8426
+ * Repair belongs in a one-time pass at upgrade. Do not add it here, for two
8427
+ * reasons:
8428
+ *
8429
+ * - **The two states are indistinguishable from this op.** After this build a
8430
+ * `photo:` row with `scope: "private"` is also exactly what the user's own
8431
+ * off-toggle produces. The toggle calls `updateVaultMemoryOp`, which writes
8432
+ * `scope` and never touches `visibility`, so the mis-stamped row and the
8433
+ * turned-off row carry identical columns. No test here can tell them apart.
8434
+ *
8435
+ * - **A re-stamp here would revert the off-toggle.** The pass runs ingest
8436
+ * BEFORE it reads the vault, so a row re-stamped `shared` re-enters the
8437
+ * desired set on that same pass and never reaches `toRevoke`. The switch
8438
+ * would spring back to "on" and nearby would keep serving the memory. That
8439
+ * is worse than the bug it repairs.
8440
+ *
8441
+ * A one-time pass does not have the ambiguity. Before this build the switch
8442
+ * already read "off" for these rows, because it reads `scope`, so no user can
8443
+ * have turned one off. Every `private` photo row at that instant came from the
8444
+ * old stamp.
8445
+ *
8408
8446
  * Non-`photo:` ids are ignored: everything else in the published set is a
8409
8447
  * client-published memory that by definition already lives in this vault (or in
8410
8448
  * another device's, which is not ours to recreate).
@@ -178,14 +178,14 @@ import {
178
178
  updateVaultMemoryOp,
179
179
  useEncryption,
180
180
  vaultMemoryToStored
181
- } from "./chunk-DNSPMVBY.mjs";
181
+ } from "./chunk-PDKKPAXP.mjs";
182
182
  import {
183
183
  PiiRedactor,
184
184
  createStreamingDeAnonymizer,
185
185
  isPiiRedactor,
186
186
  notifyConsolidationFallback,
187
187
  resolvePiiRedactor
188
- } from "./chunk-2XLHV2XW.mjs";
188
+ } from "./chunk-2GNOTRG4.mjs";
189
189
  import {
190
190
  INTERNAL_FLOW_MARKER,
191
191
  callPortalJsonCompletion,
@@ -199,7 +199,7 @@ import {
199
199
  taskTypeHeader,
200
200
  validateEndpointOverride,
201
201
  withInternalFlowMarker
202
- } from "./chunk-O25LUKBK.mjs";
202
+ } from "./chunk-3V3BBJLH.mjs";
203
203
  import {
204
204
  BASE_URL,
205
205
  MCP_R2_DOMAIN,
@@ -6976,7 +6976,7 @@ async function ingestPublishedPhotoMemoriesOp(ctx, rows) {
6976
6976
  ({ row, content }) => ctx.vaultMemoryCollection.prepareCreate((record) => {
6977
6977
  record._raw.id = row.memoryId;
6978
6978
  record._setRaw("content", content);
6979
- record._setRaw("scope", "private");
6979
+ record._setRaw("scope", "shared");
6980
6980
  record._setRaw("folder_id", null);
6981
6981
  record._setRaw("user_id", ctx.userId ?? null);
6982
6982
  record._setRaw("is_deleted", false);
@@ -7686,7 +7686,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
7686
7686
  content: m.content,
7687
7687
  similarity: m.similarity
7688
7688
  }));
7689
- const { consolidateMemory: doConsolidate } = await import("./consolidate-LSDBMQDW.mjs");
7689
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-Y2XWEZRQ.mjs");
7690
7690
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
7691
7691
  if (decision.action === "create") return null;
7692
7692
  if (decision.action === "supersede" && decision.content) {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-DNSPMVBY.mjs";
4
- import "./chunk-O25LUKBK.mjs";
3
+ } from "./chunk-PDKKPAXP.mjs";
4
+ import "./chunk-3V3BBJLH.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
7
7
  recall
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  callPortalJsonCompletion,
3
3
  getLogger
4
- } from "./chunk-7D3RI25K.mjs";
4
+ } from "./chunk-H4G3DLDJ.mjs";
5
5
  import {
6
6
  BASE_URL,
7
7
  __commonJS,
@@ -20923,7 +20923,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
20923
20923
  subQueries = decomp.subQueries;
20924
20924
  }
20925
20925
  }
20926
- const { recall: recall2 } = await import("./recall-VHU372MK.mjs");
20926
+ const { recall: recall2 } = await import("./recall-EODNEKIA.mjs");
20927
20927
  let recallDegraded = [];
20928
20928
  const result = await recall2(
20929
20929
  query,