@anuma/sdk 6.14.0 → 6.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/expo/{chunk-732ZLOLA.mjs → chunk-JA7CDJCA.mjs} +2 -2
- package/dist/expo/{chunk-OUFITMDL.mjs → chunk-M4JSLFTQ.mjs} +1 -1
- package/dist/{react/chunk-CJ32HO6Q.mjs → expo/chunk-O25LUKBK.mjs} +11 -2
- package/dist/{react/consolidate-5TP3J6BY.mjs → expo/consolidate-S3KFA2WT.mjs} +2 -2
- package/dist/expo/index.cjs +13 -4
- package/dist/expo/index.d.mts +50 -12
- package/dist/expo/index.d.ts +50 -12
- package/dist/expo/index.mjs +5 -5
- package/dist/expo/{recall-PIATJ336.mjs → recall-JYVHWU6N.mjs} +2 -2
- package/dist/react/{chunk-DY5VGC6K.mjs → chunk-2XLHV2XW.mjs} +1 -1
- package/dist/react/{chunk-6IBFFWCE.mjs → chunk-DNSPMVBY.mjs} +2 -2
- package/dist/{expo/chunk-CJ32HO6Q.mjs → react/chunk-O25LUKBK.mjs} +11 -2
- package/dist/{expo/consolidate-4BMNVOL4.mjs → react/consolidate-LSDBMQDW.mjs} +2 -2
- package/dist/react/index.cjs +13 -4
- package/dist/react/index.d.mts +50 -12
- package/dist/react/index.d.ts +50 -12
- package/dist/react/index.mjs +5 -5
- package/dist/react/{recall-QUWN7DJ7.mjs → recall-2BWBJWGG.mjs} +2 -2
- package/dist/server/{chunk-KF5OU4IN.mjs → chunk-7D3RI25K.mjs} +11 -2
- package/dist/server/{chunk-MEQ2PAEX.mjs → chunk-GP6YY6H5.mjs} +1 -1
- package/dist/server/{chunk-7YTEE6JT.mjs → chunk-O7TB36P3.mjs} +2 -2
- package/dist/server/{consolidate-QDWEYFTZ.mjs → consolidate-Z2JJP7LW.mjs} +2 -2
- package/dist/server/index.cjs +13 -4
- package/dist/server/index.d.mts +50 -12
- package/dist/server/index.d.ts +50 -12
- package/dist/server/index.mjs +5 -5
- package/dist/server/{recall-HOVWMT4Q.mjs → recall-VHU372MK.mjs} +2 -2
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
callPortalJsonCompletion,
|
|
3
3
|
getLogger
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-O25LUKBK.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-
|
|
6888
|
+
const { recall: recall2 } = await import("./recall-JYVHWU6N.mjs");
|
|
6889
6889
|
let recallDegraded = [];
|
|
6890
6890
|
const result = await recall2(
|
|
6891
6891
|
query,
|
|
@@ -65,6 +65,7 @@ function defaultBaseUrl() {
|
|
|
65
65
|
}
|
|
66
66
|
var RESPONSE_FORMAT_OK = /* @__PURE__ */ new Set(["openai", "inclusionai", "deepseek"]);
|
|
67
67
|
var RESPONSE_SCHEMA_OK = /* @__PURE__ */ new Set(["openai"]);
|
|
68
|
+
var JSON_CONTRACT_REMINDER = "Output ONLY the strict JSON object requested. No prose, no explanation, no markdown.";
|
|
68
69
|
function supportsResponseFormat(model, variant = "json_object") {
|
|
69
70
|
const allow = variant === "json_schema" ? RESPONSE_SCHEMA_OK : RESPONSE_FORMAT_OK;
|
|
70
71
|
return model.split("/").some((seg) => allow.has(seg));
|
|
@@ -122,6 +123,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
122
123
|
const startedAt = Date.now();
|
|
123
124
|
const overBudget = () => req.totalTimeoutMs !== void 0 && Date.now() - startedAt >= req.totalTimeoutMs;
|
|
124
125
|
let lastFailure;
|
|
126
|
+
let reinforce = false;
|
|
125
127
|
const giveUp = () => {
|
|
126
128
|
req.onFailure?.(lastFailure ?? { reason: "network", attempts: maxAttempts });
|
|
127
129
|
return null;
|
|
@@ -137,8 +139,9 @@ async function callPortalJsonCompletion(req) {
|
|
|
137
139
|
timeoutMs: Math.max(
|
|
138
140
|
1,
|
|
139
141
|
Math.min(req.timeoutMs ?? 6e4, req.totalTimeoutMs - (Date.now() - startedAt))
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
+
),
|
|
143
|
+
...reinforce && { reinforceJsonContract: true }
|
|
144
|
+
} : reinforce ? { ...req, reinforceJsonContract: true } : req;
|
|
142
145
|
const outcome = await attemptPortalJson(attemptReq, endpoint);
|
|
143
146
|
if (outcome.kind === "ok") return outcome.value;
|
|
144
147
|
lastFailure = {
|
|
@@ -146,6 +149,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
146
149
|
...outcome.httpStatus !== void 0 && { httpStatus: outcome.httpStatus },
|
|
147
150
|
attempts: attempt
|
|
148
151
|
};
|
|
152
|
+
if (outcome.code === "invalid-json" || outcome.code === "null-completion") reinforce = true;
|
|
149
153
|
if (outcome.kind === "terminal") {
|
|
150
154
|
log.warn(`[${req.tag}] ${outcome.reason}`);
|
|
151
155
|
return giveUp();
|
|
@@ -184,6 +188,11 @@ async function attemptPortalJson(req, endpoint) {
|
|
|
184
188
|
// keeps the portal's detector reading them as genuine rather than markerless.
|
|
185
189
|
// See ../internalFlowMarker.
|
|
186
190
|
{ role: "system", content: withInternalFlowMarker(req.systemPrompt) },
|
|
191
|
+
// Retry-only. Kept as a SEPARATE message rather than appended to the system
|
|
192
|
+
// prompt so the marked prompt above stays byte-identical across attempts —
|
|
193
|
+
// the portal's internal-flow detector reads that one, and rewriting it on a
|
|
194
|
+
// retry would change what the detector sees for reasons unrelated to auth.
|
|
195
|
+
...req.reinforceJsonContract ? [{ role: "system", content: JSON_CONTRACT_REMINDER }] : [],
|
|
187
196
|
{ role: "user", content: req.userMessage }
|
|
188
197
|
];
|
|
189
198
|
if (prefill) messages.push({ role: "assistant", content: prefill });
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
notifyConsolidationFallback,
|
|
3
3
|
resolvePiiRedactor
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-M4JSLFTQ.mjs";
|
|
5
5
|
import {
|
|
6
6
|
callPortalJsonCompletion
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-O25LUKBK.mjs";
|
|
8
8
|
import "./chunk-GYKJOWEO.mjs";
|
|
9
9
|
|
|
10
10
|
// src/lib/memory/consolidate.ts
|
package/dist/expo/index.cjs
CHANGED
|
@@ -6197,6 +6197,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
6197
6197
|
const startedAt = Date.now();
|
|
6198
6198
|
const overBudget = () => req.totalTimeoutMs !== void 0 && Date.now() - startedAt >= req.totalTimeoutMs;
|
|
6199
6199
|
let lastFailure;
|
|
6200
|
+
let reinforce = false;
|
|
6200
6201
|
const giveUp = () => {
|
|
6201
6202
|
req.onFailure?.(lastFailure ?? { reason: "network", attempts: maxAttempts });
|
|
6202
6203
|
return null;
|
|
@@ -6212,8 +6213,9 @@ async function callPortalJsonCompletion(req) {
|
|
|
6212
6213
|
timeoutMs: Math.max(
|
|
6213
6214
|
1,
|
|
6214
6215
|
Math.min(req.timeoutMs ?? 6e4, req.totalTimeoutMs - (Date.now() - startedAt))
|
|
6215
|
-
)
|
|
6216
|
-
|
|
6216
|
+
),
|
|
6217
|
+
...reinforce && { reinforceJsonContract: true }
|
|
6218
|
+
} : reinforce ? { ...req, reinforceJsonContract: true } : req;
|
|
6217
6219
|
const outcome = await attemptPortalJson(attemptReq, endpoint);
|
|
6218
6220
|
if (outcome.kind === "ok") return outcome.value;
|
|
6219
6221
|
lastFailure = {
|
|
@@ -6221,6 +6223,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
6221
6223
|
...outcome.httpStatus !== void 0 && { httpStatus: outcome.httpStatus },
|
|
6222
6224
|
attempts: attempt
|
|
6223
6225
|
};
|
|
6226
|
+
if (outcome.code === "invalid-json" || outcome.code === "null-completion") reinforce = true;
|
|
6224
6227
|
if (outcome.kind === "terminal") {
|
|
6225
6228
|
log.warn(`[${req.tag}] ${outcome.reason}`);
|
|
6226
6229
|
return giveUp();
|
|
@@ -6259,6 +6262,11 @@ async function attemptPortalJson(req, endpoint) {
|
|
|
6259
6262
|
// keeps the portal's detector reading them as genuine rather than markerless.
|
|
6260
6263
|
// See ../internalFlowMarker.
|
|
6261
6264
|
{ role: "system", content: withInternalFlowMarker(req.systemPrompt) },
|
|
6265
|
+
// Retry-only. Kept as a SEPARATE message rather than appended to the system
|
|
6266
|
+
// prompt so the marked prompt above stays byte-identical across attempts —
|
|
6267
|
+
// the portal's internal-flow detector reads that one, and rewriting it on a
|
|
6268
|
+
// retry would change what the detector sees for reasons unrelated to auth.
|
|
6269
|
+
...req.reinforceJsonContract ? [{ role: "system", content: JSON_CONTRACT_REMINDER }] : [],
|
|
6262
6270
|
{ role: "user", content: req.userMessage }
|
|
6263
6271
|
];
|
|
6264
6272
|
if (prefill) messages.push({ role: "assistant", content: prefill });
|
|
@@ -6421,7 +6429,7 @@ function extractCompletionContent(body) {
|
|
|
6421
6429
|
const content = first?.message?.content;
|
|
6422
6430
|
return typeof content === "string" ? content : null;
|
|
6423
6431
|
}
|
|
6424
|
-
var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, RETRYABLE_HTTP;
|
|
6432
|
+
var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, RETRYABLE_HTTP;
|
|
6425
6433
|
var init_portalLlm = __esm({
|
|
6426
6434
|
"src/lib/memory/portalLlm.ts"() {
|
|
6427
6435
|
"use strict";
|
|
@@ -6432,6 +6440,7 @@ var init_portalLlm = __esm({
|
|
|
6432
6440
|
init_taskType();
|
|
6433
6441
|
RESPONSE_FORMAT_OK = /* @__PURE__ */ new Set(["openai", "inclusionai", "deepseek"]);
|
|
6434
6442
|
RESPONSE_SCHEMA_OK = /* @__PURE__ */ new Set(["openai"]);
|
|
6443
|
+
JSON_CONTRACT_REMINDER = "Output ONLY the strict JSON object requested. No prose, no explanation, no markdown.";
|
|
6435
6444
|
RETRYABLE_HTTP = /* @__PURE__ */ new Set([408, 409, 425, 429]);
|
|
6436
6445
|
}
|
|
6437
6446
|
});
|
|
@@ -15429,7 +15438,7 @@ async function ingestPublishedPhotoMemoriesOp(ctx, rows) {
|
|
|
15429
15438
|
({ row, content }) => ctx.vaultMemoryCollection.prepareCreate((record) => {
|
|
15430
15439
|
record._raw.id = row.memoryId;
|
|
15431
15440
|
record._setRaw("content", content);
|
|
15432
|
-
record._setRaw("scope", "
|
|
15441
|
+
record._setRaw("scope", "shared");
|
|
15433
15442
|
record._setRaw("folder_id", null);
|
|
15434
15443
|
record._setRaw("user_id", ctx.userId ?? null);
|
|
15435
15444
|
record._setRaw("is_deleted", false);
|
package/dist/expo/index.d.mts
CHANGED
|
@@ -6335,15 +6335,26 @@ declare function extractAndRetain(messages: AutoExtractMessage[], retainCtx: Ret
|
|
|
6335
6335
|
}>;
|
|
6336
6336
|
|
|
6337
6337
|
/**
|
|
6338
|
-
*
|
|
6339
|
-
*
|
|
6340
|
-
*
|
|
6341
|
-
*
|
|
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
|
|
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 `
|
|
7001
|
-
*
|
|
7002
|
-
*
|
|
7003
|
-
*
|
|
7004
|
-
*
|
|
7005
|
-
*
|
|
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).
|
package/dist/expo/index.d.ts
CHANGED
|
@@ -6335,15 +6335,26 @@ declare function extractAndRetain(messages: AutoExtractMessage[], retainCtx: Ret
|
|
|
6335
6335
|
}>;
|
|
6336
6336
|
|
|
6337
6337
|
/**
|
|
6338
|
-
*
|
|
6339
|
-
*
|
|
6340
|
-
*
|
|
6341
|
-
*
|
|
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
|
|
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 `
|
|
7001
|
-
*
|
|
7002
|
-
*
|
|
7003
|
-
*
|
|
7004
|
-
*
|
|
7005
|
-
*
|
|
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).
|
package/dist/expo/index.mjs
CHANGED
|
@@ -132,7 +132,7 @@ import {
|
|
|
132
132
|
upsertMessageOp,
|
|
133
133
|
useEncryption,
|
|
134
134
|
vaultMemoryToStored
|
|
135
|
-
} from "./chunk-
|
|
135
|
+
} from "./chunk-JA7CDJCA.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-
|
|
143
|
+
} from "./chunk-M4JSLFTQ.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-
|
|
157
|
+
} from "./chunk-O25LUKBK.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", "
|
|
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-
|
|
6871
|
+
const { consolidateMemory: doConsolidate } = await import("./consolidate-S3KFA2WT.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
|
callPortalJsonCompletion,
|
|
3
3
|
getLogger
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-O25LUKBK.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-
|
|
7172
|
+
const { recall: recall2 } = await import("./recall-2BWBJWGG.mjs");
|
|
7173
7173
|
let recallDegraded = [];
|
|
7174
7174
|
const result = await recall2(
|
|
7175
7175
|
query,
|
|
@@ -65,6 +65,7 @@ function defaultBaseUrl() {
|
|
|
65
65
|
}
|
|
66
66
|
var RESPONSE_FORMAT_OK = /* @__PURE__ */ new Set(["openai", "inclusionai", "deepseek"]);
|
|
67
67
|
var RESPONSE_SCHEMA_OK = /* @__PURE__ */ new Set(["openai"]);
|
|
68
|
+
var JSON_CONTRACT_REMINDER = "Output ONLY the strict JSON object requested. No prose, no explanation, no markdown.";
|
|
68
69
|
function supportsResponseFormat(model, variant = "json_object") {
|
|
69
70
|
const allow = variant === "json_schema" ? RESPONSE_SCHEMA_OK : RESPONSE_FORMAT_OK;
|
|
70
71
|
return model.split("/").some((seg) => allow.has(seg));
|
|
@@ -122,6 +123,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
122
123
|
const startedAt = Date.now();
|
|
123
124
|
const overBudget = () => req.totalTimeoutMs !== void 0 && Date.now() - startedAt >= req.totalTimeoutMs;
|
|
124
125
|
let lastFailure;
|
|
126
|
+
let reinforce = false;
|
|
125
127
|
const giveUp = () => {
|
|
126
128
|
req.onFailure?.(lastFailure ?? { reason: "network", attempts: maxAttempts });
|
|
127
129
|
return null;
|
|
@@ -137,8 +139,9 @@ async function callPortalJsonCompletion(req) {
|
|
|
137
139
|
timeoutMs: Math.max(
|
|
138
140
|
1,
|
|
139
141
|
Math.min(req.timeoutMs ?? 6e4, req.totalTimeoutMs - (Date.now() - startedAt))
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
+
),
|
|
143
|
+
...reinforce && { reinforceJsonContract: true }
|
|
144
|
+
} : reinforce ? { ...req, reinforceJsonContract: true } : req;
|
|
142
145
|
const outcome = await attemptPortalJson(attemptReq, endpoint);
|
|
143
146
|
if (outcome.kind === "ok") return outcome.value;
|
|
144
147
|
lastFailure = {
|
|
@@ -146,6 +149,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
146
149
|
...outcome.httpStatus !== void 0 && { httpStatus: outcome.httpStatus },
|
|
147
150
|
attempts: attempt
|
|
148
151
|
};
|
|
152
|
+
if (outcome.code === "invalid-json" || outcome.code === "null-completion") reinforce = true;
|
|
149
153
|
if (outcome.kind === "terminal") {
|
|
150
154
|
log.warn(`[${req.tag}] ${outcome.reason}`);
|
|
151
155
|
return giveUp();
|
|
@@ -184,6 +188,11 @@ async function attemptPortalJson(req, endpoint) {
|
|
|
184
188
|
// keeps the portal's detector reading them as genuine rather than markerless.
|
|
185
189
|
// See ../internalFlowMarker.
|
|
186
190
|
{ role: "system", content: withInternalFlowMarker(req.systemPrompt) },
|
|
191
|
+
// Retry-only. Kept as a SEPARATE message rather than appended to the system
|
|
192
|
+
// prompt so the marked prompt above stays byte-identical across attempts —
|
|
193
|
+
// the portal's internal-flow detector reads that one, and rewriting it on a
|
|
194
|
+
// retry would change what the detector sees for reasons unrelated to auth.
|
|
195
|
+
...req.reinforceJsonContract ? [{ role: "system", content: JSON_CONTRACT_REMINDER }] : [],
|
|
187
196
|
{ role: "user", content: req.userMessage }
|
|
188
197
|
];
|
|
189
198
|
if (prefill) messages.push({ role: "assistant", content: prefill });
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
notifyConsolidationFallback,
|
|
3
3
|
resolvePiiRedactor
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2XLHV2XW.mjs";
|
|
5
5
|
import {
|
|
6
6
|
callPortalJsonCompletion
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-O25LUKBK.mjs";
|
|
8
8
|
import "./chunk-GYKJOWEO.mjs";
|
|
9
9
|
|
|
10
10
|
// src/lib/memory/consolidate.ts
|
package/dist/react/index.cjs
CHANGED
|
@@ -6481,6 +6481,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
6481
6481
|
const startedAt = Date.now();
|
|
6482
6482
|
const overBudget = () => req.totalTimeoutMs !== void 0 && Date.now() - startedAt >= req.totalTimeoutMs;
|
|
6483
6483
|
let lastFailure;
|
|
6484
|
+
let reinforce = false;
|
|
6484
6485
|
const giveUp = () => {
|
|
6485
6486
|
req.onFailure?.(lastFailure ?? { reason: "network", attempts: maxAttempts });
|
|
6486
6487
|
return null;
|
|
@@ -6496,8 +6497,9 @@ async function callPortalJsonCompletion(req) {
|
|
|
6496
6497
|
timeoutMs: Math.max(
|
|
6497
6498
|
1,
|
|
6498
6499
|
Math.min(req.timeoutMs ?? 6e4, req.totalTimeoutMs - (Date.now() - startedAt))
|
|
6499
|
-
)
|
|
6500
|
-
|
|
6500
|
+
),
|
|
6501
|
+
...reinforce && { reinforceJsonContract: true }
|
|
6502
|
+
} : reinforce ? { ...req, reinforceJsonContract: true } : req;
|
|
6501
6503
|
const outcome = await attemptPortalJson(attemptReq, endpoint);
|
|
6502
6504
|
if (outcome.kind === "ok") return outcome.value;
|
|
6503
6505
|
lastFailure = {
|
|
@@ -6505,6 +6507,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
6505
6507
|
...outcome.httpStatus !== void 0 && { httpStatus: outcome.httpStatus },
|
|
6506
6508
|
attempts: attempt
|
|
6507
6509
|
};
|
|
6510
|
+
if (outcome.code === "invalid-json" || outcome.code === "null-completion") reinforce = true;
|
|
6508
6511
|
if (outcome.kind === "terminal") {
|
|
6509
6512
|
log.warn(`[${req.tag}] ${outcome.reason}`);
|
|
6510
6513
|
return giveUp();
|
|
@@ -6543,6 +6546,11 @@ async function attemptPortalJson(req, endpoint) {
|
|
|
6543
6546
|
// keeps the portal's detector reading them as genuine rather than markerless.
|
|
6544
6547
|
// See ../internalFlowMarker.
|
|
6545
6548
|
{ role: "system", content: withInternalFlowMarker(req.systemPrompt) },
|
|
6549
|
+
// Retry-only. Kept as a SEPARATE message rather than appended to the system
|
|
6550
|
+
// prompt so the marked prompt above stays byte-identical across attempts —
|
|
6551
|
+
// the portal's internal-flow detector reads that one, and rewriting it on a
|
|
6552
|
+
// retry would change what the detector sees for reasons unrelated to auth.
|
|
6553
|
+
...req.reinforceJsonContract ? [{ role: "system", content: JSON_CONTRACT_REMINDER }] : [],
|
|
6546
6554
|
{ role: "user", content: req.userMessage }
|
|
6547
6555
|
];
|
|
6548
6556
|
if (prefill) messages.push({ role: "assistant", content: prefill });
|
|
@@ -6705,7 +6713,7 @@ function extractCompletionContent(body) {
|
|
|
6705
6713
|
const content = first?.message?.content;
|
|
6706
6714
|
return typeof content === "string" ? content : null;
|
|
6707
6715
|
}
|
|
6708
|
-
var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, RETRYABLE_HTTP;
|
|
6716
|
+
var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, RETRYABLE_HTTP;
|
|
6709
6717
|
var init_portalLlm = __esm({
|
|
6710
6718
|
"src/lib/memory/portalLlm.ts"() {
|
|
6711
6719
|
"use strict";
|
|
@@ -6716,6 +6724,7 @@ var init_portalLlm = __esm({
|
|
|
6716
6724
|
init_taskType();
|
|
6717
6725
|
RESPONSE_FORMAT_OK = /* @__PURE__ */ new Set(["openai", "inclusionai", "deepseek"]);
|
|
6718
6726
|
RESPONSE_SCHEMA_OK = /* @__PURE__ */ new Set(["openai"]);
|
|
6727
|
+
JSON_CONTRACT_REMINDER = "Output ONLY the strict JSON object requested. No prose, no explanation, no markdown.";
|
|
6719
6728
|
RETRYABLE_HTTP = /* @__PURE__ */ new Set([408, 409, 425, 429]);
|
|
6720
6729
|
}
|
|
6721
6730
|
});
|
|
@@ -17067,7 +17076,7 @@ async function ingestPublishedPhotoMemoriesOp(ctx, rows) {
|
|
|
17067
17076
|
({ row, content }) => ctx.vaultMemoryCollection.prepareCreate((record) => {
|
|
17068
17077
|
record._raw.id = row.memoryId;
|
|
17069
17078
|
record._setRaw("content", content);
|
|
17070
|
-
record._setRaw("scope", "
|
|
17079
|
+
record._setRaw("scope", "shared");
|
|
17071
17080
|
record._setRaw("folder_id", null);
|
|
17072
17081
|
record._setRaw("user_id", ctx.userId ?? null);
|
|
17073
17082
|
record._setRaw("is_deleted", false);
|
package/dist/react/index.d.mts
CHANGED
|
@@ -7604,15 +7604,26 @@ declare function extractAndRetain(messages: AutoExtractMessage[], retainCtx: Ret
|
|
|
7604
7604
|
}>;
|
|
7605
7605
|
|
|
7606
7606
|
/**
|
|
7607
|
-
*
|
|
7608
|
-
*
|
|
7609
|
-
*
|
|
7610
|
-
*
|
|
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
|
|
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 `
|
|
8356
|
-
*
|
|
8357
|
-
*
|
|
8358
|
-
*
|
|
8359
|
-
*
|
|
8360
|
-
*
|
|
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).
|
package/dist/react/index.d.ts
CHANGED
|
@@ -7604,15 +7604,26 @@ declare function extractAndRetain(messages: AutoExtractMessage[], retainCtx: Ret
|
|
|
7604
7604
|
}>;
|
|
7605
7605
|
|
|
7606
7606
|
/**
|
|
7607
|
-
*
|
|
7608
|
-
*
|
|
7609
|
-
*
|
|
7610
|
-
*
|
|
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
|
|
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 `
|
|
8356
|
-
*
|
|
8357
|
-
*
|
|
8358
|
-
*
|
|
8359
|
-
*
|
|
8360
|
-
*
|
|
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).
|
package/dist/react/index.mjs
CHANGED
|
@@ -178,14 +178,14 @@ import {
|
|
|
178
178
|
updateVaultMemoryOp,
|
|
179
179
|
useEncryption,
|
|
180
180
|
vaultMemoryToStored
|
|
181
|
-
} from "./chunk-
|
|
181
|
+
} from "./chunk-DNSPMVBY.mjs";
|
|
182
182
|
import {
|
|
183
183
|
PiiRedactor,
|
|
184
184
|
createStreamingDeAnonymizer,
|
|
185
185
|
isPiiRedactor,
|
|
186
186
|
notifyConsolidationFallback,
|
|
187
187
|
resolvePiiRedactor
|
|
188
|
-
} from "./chunk-
|
|
188
|
+
} from "./chunk-2XLHV2XW.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-
|
|
202
|
+
} from "./chunk-O25LUKBK.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", "
|
|
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-
|
|
7689
|
+
const { consolidateMemory: doConsolidate } = await import("./consolidate-LSDBMQDW.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) {
|
|
@@ -52,6 +52,7 @@ function defaultBaseUrl() {
|
|
|
52
52
|
}
|
|
53
53
|
var RESPONSE_FORMAT_OK = /* @__PURE__ */ new Set(["openai", "inclusionai", "deepseek"]);
|
|
54
54
|
var RESPONSE_SCHEMA_OK = /* @__PURE__ */ new Set(["openai"]);
|
|
55
|
+
var JSON_CONTRACT_REMINDER = "Output ONLY the strict JSON object requested. No prose, no explanation, no markdown.";
|
|
55
56
|
function supportsResponseFormat(model, variant = "json_object") {
|
|
56
57
|
const allow = variant === "json_schema" ? RESPONSE_SCHEMA_OK : RESPONSE_FORMAT_OK;
|
|
57
58
|
return model.split("/").some((seg) => allow.has(seg));
|
|
@@ -109,6 +110,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
109
110
|
const startedAt = Date.now();
|
|
110
111
|
const overBudget = () => req.totalTimeoutMs !== void 0 && Date.now() - startedAt >= req.totalTimeoutMs;
|
|
111
112
|
let lastFailure;
|
|
113
|
+
let reinforce = false;
|
|
112
114
|
const giveUp = () => {
|
|
113
115
|
req.onFailure?.(lastFailure ?? { reason: "network", attempts: maxAttempts });
|
|
114
116
|
return null;
|
|
@@ -124,8 +126,9 @@ async function callPortalJsonCompletion(req) {
|
|
|
124
126
|
timeoutMs: Math.max(
|
|
125
127
|
1,
|
|
126
128
|
Math.min(req.timeoutMs ?? 6e4, req.totalTimeoutMs - (Date.now() - startedAt))
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
+
),
|
|
130
|
+
...reinforce && { reinforceJsonContract: true }
|
|
131
|
+
} : reinforce ? { ...req, reinforceJsonContract: true } : req;
|
|
129
132
|
const outcome = await attemptPortalJson(attemptReq, endpoint);
|
|
130
133
|
if (outcome.kind === "ok") return outcome.value;
|
|
131
134
|
lastFailure = {
|
|
@@ -133,6 +136,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
133
136
|
...outcome.httpStatus !== void 0 && { httpStatus: outcome.httpStatus },
|
|
134
137
|
attempts: attempt
|
|
135
138
|
};
|
|
139
|
+
if (outcome.code === "invalid-json" || outcome.code === "null-completion") reinforce = true;
|
|
136
140
|
if (outcome.kind === "terminal") {
|
|
137
141
|
log.warn(`[${req.tag}] ${outcome.reason}`);
|
|
138
142
|
return giveUp();
|
|
@@ -171,6 +175,11 @@ async function attemptPortalJson(req, endpoint) {
|
|
|
171
175
|
// keeps the portal's detector reading them as genuine rather than markerless.
|
|
172
176
|
// See ../internalFlowMarker.
|
|
173
177
|
{ role: "system", content: withInternalFlowMarker(req.systemPrompt) },
|
|
178
|
+
// Retry-only. Kept as a SEPARATE message rather than appended to the system
|
|
179
|
+
// prompt so the marked prompt above stays byte-identical across attempts —
|
|
180
|
+
// the portal's internal-flow detector reads that one, and rewriting it on a
|
|
181
|
+
// retry would change what the detector sees for reasons unrelated to auth.
|
|
182
|
+
...req.reinforceJsonContract ? [{ role: "system", content: JSON_CONTRACT_REMINDER }] : [],
|
|
174
183
|
{ role: "user", content: req.userMessage }
|
|
175
184
|
];
|
|
176
185
|
if (prefill) messages.push({ role: "assistant", content: prefill });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
callPortalJsonCompletion,
|
|
3
3
|
getLogger
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-7D3RI25K.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-
|
|
20926
|
+
const { recall: recall2 } = await import("./recall-VHU372MK.mjs");
|
|
20927
20927
|
let recallDegraded = [];
|
|
20928
20928
|
const result = await recall2(
|
|
20929
20929
|
query,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
notifyConsolidationFallback,
|
|
3
3
|
resolvePiiRedactor
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-GP6YY6H5.mjs";
|
|
5
5
|
import {
|
|
6
6
|
callPortalJsonCompletion
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-7D3RI25K.mjs";
|
|
8
8
|
import "./chunk-3RPGXXPZ.mjs";
|
|
9
9
|
|
|
10
10
|
// src/lib/memory/consolidate.ts
|
package/dist/server/index.cjs
CHANGED
|
@@ -20663,6 +20663,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
20663
20663
|
const startedAt = Date.now();
|
|
20664
20664
|
const overBudget = () => req.totalTimeoutMs !== void 0 && Date.now() - startedAt >= req.totalTimeoutMs;
|
|
20665
20665
|
let lastFailure;
|
|
20666
|
+
let reinforce = false;
|
|
20666
20667
|
const giveUp = () => {
|
|
20667
20668
|
req.onFailure?.(lastFailure ?? { reason: "network", attempts: maxAttempts });
|
|
20668
20669
|
return null;
|
|
@@ -20678,8 +20679,9 @@ async function callPortalJsonCompletion(req) {
|
|
|
20678
20679
|
timeoutMs: Math.max(
|
|
20679
20680
|
1,
|
|
20680
20681
|
Math.min(req.timeoutMs ?? 6e4, req.totalTimeoutMs - (Date.now() - startedAt))
|
|
20681
|
-
)
|
|
20682
|
-
|
|
20682
|
+
),
|
|
20683
|
+
...reinforce && { reinforceJsonContract: true }
|
|
20684
|
+
} : reinforce ? { ...req, reinforceJsonContract: true } : req;
|
|
20683
20685
|
const outcome = await attemptPortalJson(attemptReq, endpoint);
|
|
20684
20686
|
if (outcome.kind === "ok") return outcome.value;
|
|
20685
20687
|
lastFailure = {
|
|
@@ -20687,6 +20689,7 @@ async function callPortalJsonCompletion(req) {
|
|
|
20687
20689
|
...outcome.httpStatus !== void 0 && { httpStatus: outcome.httpStatus },
|
|
20688
20690
|
attempts: attempt
|
|
20689
20691
|
};
|
|
20692
|
+
if (outcome.code === "invalid-json" || outcome.code === "null-completion") reinforce = true;
|
|
20690
20693
|
if (outcome.kind === "terminal") {
|
|
20691
20694
|
log.warn(`[${req.tag}] ${outcome.reason}`);
|
|
20692
20695
|
return giveUp();
|
|
@@ -20725,6 +20728,11 @@ async function attemptPortalJson(req, endpoint) {
|
|
|
20725
20728
|
// keeps the portal's detector reading them as genuine rather than markerless.
|
|
20726
20729
|
// See ../internalFlowMarker.
|
|
20727
20730
|
{ role: "system", content: withInternalFlowMarker(req.systemPrompt) },
|
|
20731
|
+
// Retry-only. Kept as a SEPARATE message rather than appended to the system
|
|
20732
|
+
// prompt so the marked prompt above stays byte-identical across attempts —
|
|
20733
|
+
// the portal's internal-flow detector reads that one, and rewriting it on a
|
|
20734
|
+
// retry would change what the detector sees for reasons unrelated to auth.
|
|
20735
|
+
...req.reinforceJsonContract ? [{ role: "system", content: JSON_CONTRACT_REMINDER }] : [],
|
|
20728
20736
|
{ role: "user", content: req.userMessage }
|
|
20729
20737
|
];
|
|
20730
20738
|
if (prefill) messages.push({ role: "assistant", content: prefill });
|
|
@@ -20887,7 +20895,7 @@ function extractCompletionContent(body) {
|
|
|
20887
20895
|
const content = first?.message?.content;
|
|
20888
20896
|
return typeof content === "string" ? content : null;
|
|
20889
20897
|
}
|
|
20890
|
-
var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, RETRYABLE_HTTP;
|
|
20898
|
+
var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, RETRYABLE_HTTP;
|
|
20891
20899
|
var init_portalLlm = __esm({
|
|
20892
20900
|
"src/lib/memory/portalLlm.ts"() {
|
|
20893
20901
|
"use strict";
|
|
@@ -20898,6 +20906,7 @@ var init_portalLlm = __esm({
|
|
|
20898
20906
|
init_taskType();
|
|
20899
20907
|
RESPONSE_FORMAT_OK = /* @__PURE__ */ new Set(["openai", "inclusionai", "deepseek"]);
|
|
20900
20908
|
RESPONSE_SCHEMA_OK = /* @__PURE__ */ new Set(["openai"]);
|
|
20909
|
+
JSON_CONTRACT_REMINDER = "Output ONLY the strict JSON object requested. No prose, no explanation, no markdown.";
|
|
20901
20910
|
RETRYABLE_HTTP = /* @__PURE__ */ new Set([408, 409, 425, 429]);
|
|
20902
20911
|
}
|
|
20903
20912
|
});
|
|
@@ -27417,7 +27426,7 @@ async function ingestPublishedPhotoMemoriesOp(ctx, rows) {
|
|
|
27417
27426
|
({ row, content }) => ctx.vaultMemoryCollection.prepareCreate((record) => {
|
|
27418
27427
|
record._raw.id = row.memoryId;
|
|
27419
27428
|
record._setRaw("content", content);
|
|
27420
|
-
record._setRaw("scope", "
|
|
27429
|
+
record._setRaw("scope", "shared");
|
|
27421
27430
|
record._setRaw("folder_id", null);
|
|
27422
27431
|
record._setRaw("user_id", ctx.userId ?? null);
|
|
27423
27432
|
record._setRaw("is_deleted", false);
|
package/dist/server/index.d.mts
CHANGED
|
@@ -6253,15 +6253,26 @@ declare function extractAndRetain(messages: AutoExtractMessage[], retainCtx: Ret
|
|
|
6253
6253
|
}>;
|
|
6254
6254
|
|
|
6255
6255
|
/**
|
|
6256
|
-
*
|
|
6257
|
-
*
|
|
6258
|
-
*
|
|
6259
|
-
*
|
|
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
|
|
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 `
|
|
6857
|
-
*
|
|
6858
|
-
*
|
|
6859
|
-
*
|
|
6860
|
-
*
|
|
6861
|
-
*
|
|
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).
|
package/dist/server/index.d.ts
CHANGED
|
@@ -6253,15 +6253,26 @@ declare function extractAndRetain(messages: AutoExtractMessage[], retainCtx: Ret
|
|
|
6253
6253
|
}>;
|
|
6254
6254
|
|
|
6255
6255
|
/**
|
|
6256
|
-
*
|
|
6257
|
-
*
|
|
6258
|
-
*
|
|
6259
|
-
*
|
|
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
|
|
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 `
|
|
6857
|
-
*
|
|
6858
|
-
*
|
|
6859
|
-
*
|
|
6860
|
-
*
|
|
6861
|
-
*
|
|
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).
|
package/dist/server/index.mjs
CHANGED
|
@@ -141,12 +141,12 @@ import {
|
|
|
141
141
|
updateVaultMemoryOp,
|
|
142
142
|
upsertMessageOp,
|
|
143
143
|
vaultMemoryToStored
|
|
144
|
-
} from "./chunk-
|
|
144
|
+
} from "./chunk-O7TB36P3.mjs";
|
|
145
145
|
import {
|
|
146
146
|
createStreamingDeAnonymizer,
|
|
147
147
|
notifyConsolidationFallback,
|
|
148
148
|
resolvePiiRedactor
|
|
149
|
-
} from "./chunk-
|
|
149
|
+
} from "./chunk-GP6YY6H5.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-
|
|
160
|
+
} from "./chunk-7D3RI25K.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", "
|
|
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-
|
|
5703
|
+
const { consolidateMemory: doConsolidate } = await import("./consolidate-Z2JJP7LW.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) {
|