@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,6 +1,6 @@
1
1
  import {
2
2
  getLogger
3
- } from "./chunk-O25LUKBK.mjs";
3
+ } from "./chunk-3V3BBJLH.mjs";
4
4
 
5
5
  // src/lib/pii/patterns.ts
6
6
  function luhnCheck(digits) {
@@ -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
@@ -6885,7 +6885,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
6885
6885
  subQueries = decomp.subQueries;
6886
6886
  }
6887
6887
  }
6888
- const { recall: recall2 } = await import("./recall-JYVHWU6N.mjs");
6888
+ const { recall: recall2 } = await import("./recall-GKYVPD2B.mjs");
6889
6889
  let recallDegraded = [];
6890
6890
  const result = await recall2(
6891
6891
  query,
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  notifyConsolidationFallback,
3
3
  resolvePiiRedactor
4
- } from "./chunk-2XLHV2XW.mjs";
4
+ } from "./chunk-DIUQLXEH.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
@@ -6145,6 +6145,23 @@ function supportsResponseFormat(model, variant = "json_object") {
6145
6145
  const allow = variant === "json_schema" ? RESPONSE_SCHEMA_OK : RESPONSE_FORMAT_OK;
6146
6146
  return model.split("/").some((seg) => allow.has(seg));
6147
6147
  }
6148
+ function responsesExtra(extra) {
6149
+ if (!extra) return {};
6150
+ const out = {};
6151
+ for (const [key, value] of Object.entries(extra)) {
6152
+ const target = CHAT_TO_RESPONSES_FIELDS[key] ?? key;
6153
+ if (target !== key && target in extra) continue;
6154
+ out[target] = value;
6155
+ }
6156
+ return out;
6157
+ }
6158
+ function assertTransportMatchesEndpoint(transport, endpoint) {
6159
+ const foreign = FOREIGN_ENDPOINT_SUFFIX[transport];
6160
+ if (!endpoint.endsWith(foreign)) return;
6161
+ throw new Error(
6162
+ `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.`
6163
+ );
6164
+ }
6148
6165
  function isRetryableStatus(status) {
6149
6166
  return status >= 500 || RETRYABLE_HTTP.has(status);
6150
6167
  }
@@ -6185,13 +6202,14 @@ async function resolvePortalAuthHeaders(auth, tag) {
6185
6202
  }
6186
6203
  async function callPortalJsonCompletion(req) {
6187
6204
  const log = getLogger();
6188
- let endpoint = "/api/v1/chat/completions";
6205
+ let endpoint = req.transport === "responses" ? "/api/v1/responses" : "/api/v1/chat/completions";
6189
6206
  if (req.endpointOverride !== void 0) {
6190
6207
  const overrideValidation = validateEndpointOverride(req.endpointOverride);
6191
6208
  if (!overrideValidation.valid) {
6192
6209
  throw new Error(overrideValidation.message);
6193
6210
  }
6194
6211
  endpoint = overrideValidation.endpoint;
6212
+ assertTransportMatchesEndpoint(req.transport ?? "chat", endpoint);
6195
6213
  }
6196
6214
  const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
6197
6215
  const startedAt = Date.now();
@@ -6253,8 +6271,8 @@ async function attemptPortalJson(req, endpoint) {
6253
6271
  const authHeaders = await resolvePortalAuthHeaders(req, req.tag);
6254
6272
  if (authHeaders === null)
6255
6273
  return { kind: "terminal", code: "auth-unavailable", reason: "auth unavailable (no token)" };
6256
- const isAnthropic = req.model.startsWith("anthropic/");
6257
- const prefill = isAnthropic ? "{" : "";
6274
+ const usesPrefill = req.transport !== "responses" && req.model.startsWith("anthropic/");
6275
+ const prefill = usesPrefill ? "{" : "";
6258
6276
  const messages = [
6259
6277
  // Every caller of this helper is a first-party BACKGROUND op (fact extraction,
6260
6278
  // topic sweep, consolidation, classifiers, graph traversal, query
@@ -6273,13 +6291,34 @@ async function attemptPortalJson(req, endpoint) {
6273
6291
  const supportsJsonObjectFormat = supportsResponseFormat(req.model);
6274
6292
  const controller = new AbortController();
6275
6293
  const timer = setTimeout(() => controller.abort(), timeoutMs);
6276
- const requestBody = {
6294
+ const requestBody = req.transport === "responses" ? {
6295
+ model: req.model,
6296
+ // Same system+user pair under the Responses-API field name. No prefill
6297
+ // entry to strip: `usesPrefill` is false on this transport, so it was
6298
+ // never pushed and the response-side restore is disarmed with it. An
6299
+ // earlier version pushed it and sliced it back off here, which left
6300
+ // `prefill` truthy and the restore still armed — the fix belonged at
6301
+ // the decision, not at the two symptoms.
6302
+ input: messages,
6303
+ ...req.reasoning && { reasoning: req.reasoning },
6304
+ // NO response_format. The Responses API spells structured output
6305
+ // differently (`text.format`), and it is unverified against this
6306
+ // portal — so this transport relies on the same strict-JSON system
6307
+ // prompt plus `extractJsonCandidate` that every response_format-
6308
+ // rejecting model already relies on. Verified end to end on dev:
6309
+ // luna returns clean parseable JSON here without it. Add the field
6310
+ // only with a per-provider check, exactly as RESPONSE_FORMAT_OK was
6311
+ // earned.
6312
+ ...responsesExtra(req.extra)
6313
+ } : {
6277
6314
  model: req.model,
6278
6315
  messages,
6279
6316
  ...supportsJsonObjectFormat && { response_format: { type: "json_object" } },
6280
6317
  ...req.extra
6281
6318
  };
6282
- if (!supportsJsonObjectFormat) delete requestBody.response_format;
6319
+ if (req.transport === "responses" || !supportsJsonObjectFormat) {
6320
+ delete requestBody.response_format;
6321
+ }
6283
6322
  let response;
6284
6323
  try {
6285
6324
  response = await fetchImpl(`${baseUrl}${endpoint}`, {
@@ -6409,13 +6448,15 @@ function logPortalUsage(body, tag) {
6409
6448
  const root = asRecord(body);
6410
6449
  if (!root) return;
6411
6450
  const usage = asRecord(root.usage);
6412
- const promptTokens = asCount(usage?.prompt_tokens);
6413
- const completionTokens = asCount(usage?.completion_tokens);
6451
+ const promptTokens = asCount(usage?.prompt_tokens) ?? asCount(usage?.input_tokens);
6452
+ const completionTokens = asCount(usage?.completion_tokens) ?? asCount(usage?.output_tokens);
6414
6453
  if (promptTokens === void 0 && completionTokens === void 0) return;
6415
- const cachedTokens = asCount(asRecord(root.portal)?.cached_tokens) ?? asCount(asRecord(usage?.prompt_tokens_details)?.cached_tokens) ?? 0;
6454
+ 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;
6455
+ const reasoningTokens = asCount(asRecord(usage?.output_tokens_details)?.reasoning_tokens) ?? asCount(asRecord(usage?.completion_tokens_details)?.reasoning_tokens);
6416
6456
  const parts = [];
6417
6457
  if (promptTokens !== void 0) parts.push(`prompt=${promptTokens}`);
6418
6458
  if (completionTokens !== void 0) parts.push(`completion=${completionTokens}`);
6459
+ if (reasoningTokens !== void 0) parts.push(`reasoning=${reasoningTokens}`);
6419
6460
  parts.push(`cached=${cachedTokens}`);
6420
6461
  getLogger().debug(`[${tag}] usage ${parts.join(" ")}`);
6421
6462
  } catch {
@@ -6423,13 +6464,23 @@ function logPortalUsage(body, tag) {
6423
6464
  }
6424
6465
  function extractCompletionContent(body) {
6425
6466
  if (typeof body !== "object" || body === null) return null;
6467
+ const responsesText = extractResponsesContent(body);
6468
+ if (responsesText !== null) return responsesText;
6426
6469
  const choices = body.choices;
6427
6470
  if (!Array.isArray(choices) || choices.length === 0) return null;
6428
6471
  const first = choices[0];
6429
6472
  const content = first?.message?.content;
6430
6473
  return typeof content === "string" ? content : null;
6431
6474
  }
6432
- var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, RETRYABLE_HTTP;
6475
+ function extractResponsesContent(body) {
6476
+ const root = asRecord(body);
6477
+ if (!root) return null;
6478
+ if (typeof root.output_text === "string" && root.output_text !== "") return root.output_text;
6479
+ if (!Array.isArray(root.output)) return null;
6480
+ 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("");
6481
+ return text12;
6482
+ }
6483
+ var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, CHAT_TO_RESPONSES_FIELDS, FOREIGN_ENDPOINT_SUFFIX, RETRYABLE_HTTP;
6433
6484
  var init_portalLlm = __esm({
6434
6485
  "src/lib/memory/portalLlm.ts"() {
6435
6486
  "use strict";
@@ -6441,6 +6492,13 @@ var init_portalLlm = __esm({
6441
6492
  RESPONSE_FORMAT_OK = /* @__PURE__ */ new Set(["openai", "inclusionai", "deepseek"]);
6442
6493
  RESPONSE_SCHEMA_OK = /* @__PURE__ */ new Set(["openai"]);
6443
6494
  JSON_CONTRACT_REMINDER = "Output ONLY the strict JSON object requested. No prose, no explanation, no markdown.";
6495
+ CHAT_TO_RESPONSES_FIELDS = {
6496
+ max_completion_tokens: "max_output_tokens"
6497
+ };
6498
+ FOREIGN_ENDPOINT_SUFFIX = {
6499
+ chat: "/responses",
6500
+ responses: "/chat/completions"
6501
+ };
6444
6502
  RETRYABLE_HTTP = /* @__PURE__ */ new Set([408, 409, 425, 429]);
6445
6503
  }
6446
6504
  });
@@ -15438,7 +15496,7 @@ async function ingestPublishedPhotoMemoriesOp(ctx, rows) {
15438
15496
  ({ row, content }) => ctx.vaultMemoryCollection.prepareCreate((record) => {
15439
15497
  record._raw.id = row.memoryId;
15440
15498
  record._setRaw("content", content);
15441
- record._setRaw("scope", "private");
15499
+ record._setRaw("scope", "shared");
15442
15500
  record._setRaw("folder_id", null);
15443
15501
  record._setRaw("user_id", ctx.userId ?? null);
15444
15502
  record._setRaw("is_deleted", false);
@@ -6335,15 +6335,26 @@ declare function extractAndRetain(messages: AutoExtractMessage[], retainCtx: Ret
6335
6335
  }>;
6336
6336
 
6337
6337
  /**
6338
- * People Nearby cross-user visibility. ORTHOGONAL to `scope` (model access):
6339
- * - `private`: local-only (default; null column grandfathered as private)
6340
- * - `public`: embedding + plaintext may be published (compatibility matching,
6341
- * profile display, discovery answers, digital-twin prompts)
6338
+ * LEGACY. This column no longer decides anything.
6339
+ *
6340
+ * `scope === 'shared'` is now the single publication axis: it means the memory
6341
+ * is on the People Nearby profile, its text goes to the nearby server, and it
6342
+ * may inform what strangers see. `scope: 'private'` means it is not. There is
6343
+ * no second control, and scope no longer gates model access either — every
6344
+ * model tier reads every memory. Do NOT reason about publication from this
6345
+ * type; read `scope`.
6346
+ *
6347
+ * The column survives in the schema and is still stamped on writes so that
6348
+ * stored rows stay readable by builds that predate the change. Nothing on a
6349
+ * current build reads it to make a decision.
6350
+ *
6351
+ * Values, kept for tolerance of already-stored data:
6352
+ * - `private`: local-only (a null column reads as private)
6353
+ * - `public`: the row was published
6342
6354
  *
6343
6355
  * TWO TIERS ONLY (decided 2026-07-27). An earlier design had a middle
6344
6356
  * `matchable` tier that published a memory's embedding while keeping its text
6345
- * on-device; it was dropped, so a memory either never leaves the device or is
6346
- * published with its content. Any unrecognised stored value — including a
6357
+ * on-device; it was dropped. Any unrecognised stored value — including a
6347
6358
  * `matchable` row written by a pre-release build — reads as `private`, which is
6348
6359
  * the fail-safe direction: it un-publishes rather than exposes.
6349
6360
  */
@@ -6997,12 +7008,16 @@ declare function updateVaultMemoryEmbeddingOp(ctx: VaultMemoryOperationsContext,
6997
7008
  * candidate to be reconciled against the vault — it is an already-decided
6998
7009
  * fact the server has published.
6999
7010
  *
7000
- * The rows are written `visibility: "public"` because they genuinely ARE
7001
- * published — the reconciler reads local visibility as the user's intent, and
7002
- * anything else would make it immediately revoke a memory the user never turned
7003
- * off. They carry `source: SOURCE_PHOTO`, which is what exempts them from
7004
- * decay's auto-archive; see the note there for why archiving one would silently
7005
- * unpublish it.
7011
+ * The rows are written `scope: "shared"` because they genuinely ARE published.
7012
+ * `scope === 'shared'` is the publication axis — the single one. The reconciler
7013
+ * reads local scope as the user's intent, and it builds its `local` set from a
7014
+ * vault read narrowed to `{scopes: ['shared']}`. A row stamped anything else
7015
+ * never enters that read, so it is never adopted into the publish ledger, and
7016
+ * `toRevoke` is computed only over ids the ledger holds. The user's off-toggle
7017
+ * would then do nothing server-side while nearby kept serving the memory. The
7018
+ * `visibility` column is legacy: nothing reads it any more. They carry
7019
+ * `source: SOURCE_PHOTO`, which is what exempts them from decay's auto-archive;
7020
+ * see the note there for why archiving one would silently unpublish it.
7006
7021
  *
7007
7022
  * No embedding is computed here. `searchTool`'s un-embedded lane embeds any row
7008
7023
  * lacking a vector at search time, uses it in that same search and persists it
@@ -7050,6 +7065,29 @@ interface PhotoIngestResult {
7050
7065
  * class of bug as publishing a version the user has since changed. Re-ingesting
7051
7066
  * changed text is a follow-up that needs a merge decision, not a clobber.
7052
7067
  *
7068
+ * That skip is also why this op does NOT repair a row an older build stamped
7069
+ * `scope: "private"`. Such a row is present, so ingest leaves it alone, and it
7070
+ * stays outside the published-set read. The user's switch cannot revoke it.
7071
+ * Repair belongs in a one-time pass at upgrade. Do not add it here, for two
7072
+ * reasons:
7073
+ *
7074
+ * - **The two states are indistinguishable from this op.** After this build a
7075
+ * `photo:` row with `scope: "private"` is also exactly what the user's own
7076
+ * off-toggle produces. The toggle calls `updateVaultMemoryOp`, which writes
7077
+ * `scope` and never touches `visibility`, so the mis-stamped row and the
7078
+ * turned-off row carry identical columns. No test here can tell them apart.
7079
+ *
7080
+ * - **A re-stamp here would revert the off-toggle.** The pass runs ingest
7081
+ * BEFORE it reads the vault, so a row re-stamped `shared` re-enters the
7082
+ * desired set on that same pass and never reaches `toRevoke`. The switch
7083
+ * would spring back to "on" and nearby would keep serving the memory. That
7084
+ * is worse than the bug it repairs.
7085
+ *
7086
+ * A one-time pass does not have the ambiguity. Before this build the switch
7087
+ * already read "off" for these rows, because it reads `scope`, so no user can
7088
+ * have turned one off. Every `private` photo row at that instant came from the
7089
+ * old stamp.
7090
+ *
7053
7091
  * Non-`photo:` ids are ignored: everything else in the published set is a
7054
7092
  * client-published memory that by definition already lives in this vault (or in
7055
7093
  * another device's, which is not ours to recreate).
@@ -6335,15 +6335,26 @@ declare function extractAndRetain(messages: AutoExtractMessage[], retainCtx: Ret
6335
6335
  }>;
6336
6336
 
6337
6337
  /**
6338
- * People Nearby cross-user visibility. ORTHOGONAL to `scope` (model access):
6339
- * - `private`: local-only (default; null column grandfathered as private)
6340
- * - `public`: embedding + plaintext may be published (compatibility matching,
6341
- * profile display, discovery answers, digital-twin prompts)
6338
+ * LEGACY. This column no longer decides anything.
6339
+ *
6340
+ * `scope === 'shared'` is now the single publication axis: it means the memory
6341
+ * is on the People Nearby profile, its text goes to the nearby server, and it
6342
+ * may inform what strangers see. `scope: 'private'` means it is not. There is
6343
+ * no second control, and scope no longer gates model access either — every
6344
+ * model tier reads every memory. Do NOT reason about publication from this
6345
+ * type; read `scope`.
6346
+ *
6347
+ * The column survives in the schema and is still stamped on writes so that
6348
+ * stored rows stay readable by builds that predate the change. Nothing on a
6349
+ * current build reads it to make a decision.
6350
+ *
6351
+ * Values, kept for tolerance of already-stored data:
6352
+ * - `private`: local-only (a null column reads as private)
6353
+ * - `public`: the row was published
6342
6354
  *
6343
6355
  * TWO TIERS ONLY (decided 2026-07-27). An earlier design had a middle
6344
6356
  * `matchable` tier that published a memory's embedding while keeping its text
6345
- * on-device; it was dropped, so a memory either never leaves the device or is
6346
- * published with its content. Any unrecognised stored value — including a
6357
+ * on-device; it was dropped. Any unrecognised stored value — including a
6347
6358
  * `matchable` row written by a pre-release build — reads as `private`, which is
6348
6359
  * the fail-safe direction: it un-publishes rather than exposes.
6349
6360
  */
@@ -6997,12 +7008,16 @@ declare function updateVaultMemoryEmbeddingOp(ctx: VaultMemoryOperationsContext,
6997
7008
  * candidate to be reconciled against the vault — it is an already-decided
6998
7009
  * fact the server has published.
6999
7010
  *
7000
- * The rows are written `visibility: "public"` because they genuinely ARE
7001
- * published — the reconciler reads local visibility as the user's intent, and
7002
- * anything else would make it immediately revoke a memory the user never turned
7003
- * off. They carry `source: SOURCE_PHOTO`, which is what exempts them from
7004
- * decay's auto-archive; see the note there for why archiving one would silently
7005
- * unpublish it.
7011
+ * The rows are written `scope: "shared"` because they genuinely ARE published.
7012
+ * `scope === 'shared'` is the publication axis — the single one. The reconciler
7013
+ * reads local scope as the user's intent, and it builds its `local` set from a
7014
+ * vault read narrowed to `{scopes: ['shared']}`. A row stamped anything else
7015
+ * never enters that read, so it is never adopted into the publish ledger, and
7016
+ * `toRevoke` is computed only over ids the ledger holds. The user's off-toggle
7017
+ * would then do nothing server-side while nearby kept serving the memory. The
7018
+ * `visibility` column is legacy: nothing reads it any more. They carry
7019
+ * `source: SOURCE_PHOTO`, which is what exempts them from decay's auto-archive;
7020
+ * see the note there for why archiving one would silently unpublish it.
7006
7021
  *
7007
7022
  * No embedding is computed here. `searchTool`'s un-embedded lane embeds any row
7008
7023
  * lacking a vector at search time, uses it in that same search and persists it
@@ -7050,6 +7065,29 @@ interface PhotoIngestResult {
7050
7065
  * class of bug as publishing a version the user has since changed. Re-ingesting
7051
7066
  * changed text is a follow-up that needs a merge decision, not a clobber.
7052
7067
  *
7068
+ * That skip is also why this op does NOT repair a row an older build stamped
7069
+ * `scope: "private"`. Such a row is present, so ingest leaves it alone, and it
7070
+ * stays outside the published-set read. The user's switch cannot revoke it.
7071
+ * Repair belongs in a one-time pass at upgrade. Do not add it here, for two
7072
+ * reasons:
7073
+ *
7074
+ * - **The two states are indistinguishable from this op.** After this build a
7075
+ * `photo:` row with `scope: "private"` is also exactly what the user's own
7076
+ * off-toggle produces. The toggle calls `updateVaultMemoryOp`, which writes
7077
+ * `scope` and never touches `visibility`, so the mis-stamped row and the
7078
+ * turned-off row carry identical columns. No test here can tell them apart.
7079
+ *
7080
+ * - **A re-stamp here would revert the off-toggle.** The pass runs ingest
7081
+ * BEFORE it reads the vault, so a row re-stamped `shared` re-enters the
7082
+ * desired set on that same pass and never reaches `toRevoke`. The switch
7083
+ * would spring back to "on" and nearby would keep serving the memory. That
7084
+ * is worse than the bug it repairs.
7085
+ *
7086
+ * A one-time pass does not have the ambiguity. Before this build the switch
7087
+ * already read "off" for these rows, because it reads `scope`, so no user can
7088
+ * have turned one off. Every `private` photo row at that instant came from the
7089
+ * old stamp.
7090
+ *
7053
7091
  * Non-`photo:` ids are ignored: everything else in the published set is a
7054
7092
  * client-published memory that by definition already lives in this vault (or in
7055
7093
  * another device's, which is not ours to recreate).
@@ -132,7 +132,7 @@ import {
132
132
  upsertMessageOp,
133
133
  useEncryption,
134
134
  vaultMemoryToStored
135
- } from "./chunk-JA7CDJCA.mjs";
135
+ } from "./chunk-LUSKQQDD.mjs";
136
136
  import {
137
137
  PII_PATTERNS,
138
138
  PiiRedactor,
@@ -140,7 +140,7 @@ import {
140
140
  isPiiRedactor,
141
141
  notifyConsolidationFallback,
142
142
  resolvePiiRedactor
143
- } from "./chunk-M4JSLFTQ.mjs";
143
+ } from "./chunk-DIUQLXEH.mjs";
144
144
  import {
145
145
  INTERNAL_FLOW_MARKER,
146
146
  callPortalJsonCompletion,
@@ -154,7 +154,7 @@ import {
154
154
  taskTypeHeader,
155
155
  validateEndpointOverride,
156
156
  withInternalFlowMarker
157
- } from "./chunk-O25LUKBK.mjs";
157
+ } from "./chunk-3V3BBJLH.mjs";
158
158
  import {
159
159
  BASE_URL,
160
160
  MCP_R2_DOMAIN,
@@ -5819,7 +5819,7 @@ async function ingestPublishedPhotoMemoriesOp(ctx, rows) {
5819
5819
  ({ row, content }) => ctx.vaultMemoryCollection.prepareCreate((record) => {
5820
5820
  record._raw.id = row.memoryId;
5821
5821
  record._setRaw("content", content);
5822
- record._setRaw("scope", "private");
5822
+ record._setRaw("scope", "shared");
5823
5823
  record._setRaw("folder_id", null);
5824
5824
  record._setRaw("user_id", ctx.userId ?? null);
5825
5825
  record._setRaw("is_deleted", false);
@@ -6868,7 +6868,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
6868
6868
  content: m.content,
6869
6869
  similarity: m.similarity
6870
6870
  }));
6871
- const { consolidateMemory: doConsolidate } = await import("./consolidate-S3KFA2WT.mjs");
6871
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-GTMXZZ4C.mjs");
6872
6872
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
6873
6873
  if (decision.action === "create") return null;
6874
6874
  if (decision.action === "supersede" && decision.content) {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-JA7CDJCA.mjs";
4
- import "./chunk-O25LUKBK.mjs";
3
+ } from "./chunk-LUSKQQDD.mjs";
4
+ import "./chunk-3V3BBJLH.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
7
7
  recall
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getLogger
3
- } from "./chunk-O25LUKBK.mjs";
3
+ } from "./chunk-3V3BBJLH.mjs";
4
4
 
5
5
  // src/lib/pii/patterns.ts
6
6
  function luhnCheck(digits) {