@anuma/sdk 9.1.5 → 9.1.6

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.
@@ -723,6 +723,12 @@ function assertTransportMatchesEndpoint(transport, endpoint) {
723
723
  `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.`
724
724
  );
725
725
  }
726
+ var TIME_BUDGET_EXHAUSTED = {
727
+ kind: "terminal",
728
+ code: "time-budget-exhausted",
729
+ reason: "time budget exhausted"
730
+ };
731
+ var MAX_TIMER_DELAY_MS = 2147483647;
726
732
  var RETRYABLE_HTTP = /* @__PURE__ */ new Set([408, 409, 425, 429]);
727
733
  function isRetryableStatus(status) {
728
734
  return status >= 500 || RETRYABLE_HTTP.has(status);
@@ -778,7 +784,8 @@ async function callPortalJsonCompletion(req) {
778
784
  }
779
785
  const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
780
786
  const startedAt = Date.now();
781
- const overBudget = () => req.totalTimeoutMs !== void 0 && Date.now() - startedAt >= req.totalTimeoutMs;
787
+ const deadlineAt = req.totalTimeoutMs === void 0 ? void 0 : startedAt + req.totalTimeoutMs;
788
+ const overBudget = () => deadlineAt !== void 0 && Date.now() >= deadlineAt;
782
789
  let lastFailure;
783
790
  let reinforce = false;
784
791
  const giveUp = () => {
@@ -791,17 +798,11 @@ async function callPortalJsonCompletion(req) {
791
798
  lastFailure = { reason: "time-budget-exhausted", attempts: attempt - 1 };
792
799
  break;
793
800
  }
794
- const attemptReq = req.totalTimeoutMs !== void 0 ? {
795
- ...req,
796
- timeoutMs: Math.max(
797
- 1,
798
- Math.min(req.timeoutMs ?? 6e4, req.totalTimeoutMs - (Date.now() - startedAt))
799
- ),
800
- ...reinforce && { reinforceJsonContract: true }
801
- } : reinforce ? { ...req, reinforceJsonContract: true } : req;
801
+ const attemptReq = reinforce ? { ...req, reinforceJsonContract: true } : req;
802
802
  const outcome = await attemptPortalJson(
803
803
  { ...attemptReq, transport },
804
- endpoint
804
+ endpoint,
805
+ deadlineAt
805
806
  );
806
807
  if (outcome.kind === "ok") {
807
808
  req.onAttempt?.({ attempt, ok: true });
@@ -841,13 +842,38 @@ async function callPortalJsonCompletion(req) {
841
842
  }
842
843
  return giveUp();
843
844
  }
844
- async function attemptPortalJson(req, endpoint) {
845
+ async function attemptPortalJson(req, endpoint, deadlineAt) {
846
+ const controller = new AbortController();
847
+ if (deadlineAt === void 0 || deadlineAt - Date.now() > MAX_TIMER_DELAY_MS) {
848
+ return requestPortalJson(req, endpoint, controller);
849
+ }
850
+ const remainingMs = deadlineAt - Date.now();
851
+ if (remainingMs <= 0) return TIME_BUDGET_EXHAUSTED;
852
+ let timer;
853
+ try {
854
+ const outcome = await Promise.race([
855
+ new Promise((resolve) => {
856
+ timer = setTimeout(() => {
857
+ resolve(TIME_BUDGET_EXHAUSTED);
858
+ controller.abort();
859
+ }, remainingMs);
860
+ }),
861
+ requestPortalJson(req, endpoint, controller, deadlineAt)
862
+ ]);
863
+ return Date.now() >= deadlineAt ? TIME_BUDGET_EXHAUSTED : outcome;
864
+ } finally {
865
+ clearTimeout(timer);
866
+ }
867
+ }
868
+ async function requestPortalJson(req, endpoint, controller, deadlineAt) {
845
869
  const baseUrl = req.baseUrl ?? defaultBaseUrl();
846
870
  const fetchImpl = req.fetchFn ?? fetch;
847
- const timeoutMs = req.timeoutMs ?? 6e4;
848
871
  const authHeaders = await resolvePortalAuthHeaders(req, req.tag);
872
+ const remainingMs = deadlineAt === void 0 ? Infinity : deadlineAt - Date.now();
873
+ if (controller.signal.aborted || remainingMs <= 0) return TIME_BUDGET_EXHAUSTED;
849
874
  if (authHeaders === null)
850
875
  return { kind: "terminal", code: "auth-unavailable", reason: "auth unavailable (no token)" };
876
+ const timeoutMs = Math.min(req.timeoutMs ?? 6e4, remainingMs);
851
877
  const usesPrefill = req.transport !== "responses" && req.model.startsWith("anthropic/");
852
878
  const prefill = usesPrefill ? "{" : "";
853
879
  const messages = [
@@ -857,7 +883,6 @@ async function attemptPortalJson(req, endpoint) {
857
883
  ];
858
884
  if (prefill) messages.push({ role: "assistant", content: prefill });
859
885
  const supportsJsonObjectFormat = supportsResponseFormat(req.model);
860
- const controller = new AbortController();
861
886
  const timer = setTimeout(() => controller.abort(), timeoutMs);
862
887
  const requestBody = req.transport === "responses" ? {
863
888
  model: req.model,
@@ -4,7 +4,7 @@ import {
4
4
  notifyConsolidationFallback,
5
5
  requiresResponsesTransport,
6
6
  resolvePiiRedactor
7
- } from "./chunk-XW3LYSDA.mjs";
7
+ } from "./chunk-WRZTH4OS.mjs";
8
8
  import "./chunk-GYKJOWEO.mjs";
9
9
 
10
10
  // src/lib/memory/consolidate.ts
@@ -875,7 +875,8 @@ async function callPortalJsonCompletion(req) {
875
875
  }
876
876
  const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
877
877
  const startedAt = Date.now();
878
- const overBudget = () => req.totalTimeoutMs !== void 0 && Date.now() - startedAt >= req.totalTimeoutMs;
878
+ const deadlineAt = req.totalTimeoutMs === void 0 ? void 0 : startedAt + req.totalTimeoutMs;
879
+ const overBudget = () => deadlineAt !== void 0 && Date.now() >= deadlineAt;
879
880
  let lastFailure;
880
881
  let reinforce = false;
881
882
  const giveUp = () => {
@@ -888,17 +889,11 @@ async function callPortalJsonCompletion(req) {
888
889
  lastFailure = { reason: "time-budget-exhausted", attempts: attempt - 1 };
889
890
  break;
890
891
  }
891
- const attemptReq = req.totalTimeoutMs !== void 0 ? {
892
- ...req,
893
- timeoutMs: Math.max(
894
- 1,
895
- Math.min(req.timeoutMs ?? 6e4, req.totalTimeoutMs - (Date.now() - startedAt))
896
- ),
897
- ...reinforce && { reinforceJsonContract: true }
898
- } : reinforce ? { ...req, reinforceJsonContract: true } : req;
892
+ const attemptReq = reinforce ? { ...req, reinforceJsonContract: true } : req;
899
893
  const outcome2 = await attemptPortalJson(
900
894
  { ...attemptReq, transport },
901
- endpoint
895
+ endpoint,
896
+ deadlineAt
902
897
  );
903
898
  if (outcome2.kind === "ok") {
904
899
  req.onAttempt?.({ attempt, ok: true });
@@ -938,13 +933,38 @@ async function callPortalJsonCompletion(req) {
938
933
  }
939
934
  return giveUp();
940
935
  }
941
- async function attemptPortalJson(req, endpoint) {
936
+ async function attemptPortalJson(req, endpoint, deadlineAt) {
937
+ const controller = new AbortController();
938
+ if (deadlineAt === void 0 || deadlineAt - Date.now() > MAX_TIMER_DELAY_MS) {
939
+ return requestPortalJson(req, endpoint, controller);
940
+ }
941
+ const remainingMs = deadlineAt - Date.now();
942
+ if (remainingMs <= 0) return TIME_BUDGET_EXHAUSTED;
943
+ let timer;
944
+ try {
945
+ const outcome2 = await Promise.race([
946
+ new Promise((resolve) => {
947
+ timer = setTimeout(() => {
948
+ resolve(TIME_BUDGET_EXHAUSTED);
949
+ controller.abort();
950
+ }, remainingMs);
951
+ }),
952
+ requestPortalJson(req, endpoint, controller, deadlineAt)
953
+ ]);
954
+ return Date.now() >= deadlineAt ? TIME_BUDGET_EXHAUSTED : outcome2;
955
+ } finally {
956
+ clearTimeout(timer);
957
+ }
958
+ }
959
+ async function requestPortalJson(req, endpoint, controller, deadlineAt) {
942
960
  const baseUrl = req.baseUrl ?? defaultBaseUrl();
943
961
  const fetchImpl = req.fetchFn ?? fetch;
944
- const timeoutMs = req.timeoutMs ?? 6e4;
945
962
  const authHeaders = await resolvePortalAuthHeaders(req, req.tag);
963
+ const remainingMs = deadlineAt === void 0 ? Infinity : deadlineAt - Date.now();
964
+ if (controller.signal.aborted || remainingMs <= 0) return TIME_BUDGET_EXHAUSTED;
946
965
  if (authHeaders === null)
947
966
  return { kind: "terminal", code: "auth-unavailable", reason: "auth unavailable (no token)" };
967
+ const timeoutMs = Math.min(req.timeoutMs ?? 6e4, remainingMs);
948
968
  const usesPrefill = req.transport !== "responses" && req.model.startsWith("anthropic/");
949
969
  const prefill = usesPrefill ? "{" : "";
950
970
  const messages = [
@@ -954,7 +974,6 @@ async function attemptPortalJson(req, endpoint) {
954
974
  ];
955
975
  if (prefill) messages.push({ role: "assistant", content: prefill });
956
976
  const supportsJsonObjectFormat = supportsResponseFormat(req.model);
957
- const controller = new AbortController();
958
977
  const timer = setTimeout(() => controller.abort(), timeoutMs);
959
978
  const requestBody = req.transport === "responses" ? {
960
979
  model: req.model,
@@ -1139,7 +1158,7 @@ function extractResponsesContent(body) {
1139
1158
  const text14 = 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("");
1140
1159
  return text14;
1141
1160
  }
1142
- var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, RESPONSES_ONLY_MODEL_PREFIXES, CHAT_TO_RESPONSES_FIELDS, FOREIGN_ENDPOINT_SUFFIX, RETRYABLE_HTTP;
1161
+ var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, RESPONSES_ONLY_MODEL_PREFIXES, CHAT_TO_RESPONSES_FIELDS, FOREIGN_ENDPOINT_SUFFIX, TIME_BUDGET_EXHAUSTED, MAX_TIMER_DELAY_MS, RETRYABLE_HTTP;
1143
1162
  var init_portalLlm = __esm({
1144
1163
  "src/lib/memory/portalLlm.ts"() {
1145
1164
  "use strict";
@@ -1159,6 +1178,12 @@ var init_portalLlm = __esm({
1159
1178
  chat: "/responses",
1160
1179
  responses: "/chat/completions"
1161
1180
  };
1181
+ TIME_BUDGET_EXHAUSTED = {
1182
+ kind: "terminal",
1183
+ code: "time-budget-exhausted",
1184
+ reason: "time budget exhausted"
1185
+ };
1186
+ MAX_TIMER_DELAY_MS = 2147483647;
1162
1187
  RETRYABLE_HTTP = /* @__PURE__ */ new Set([408, 409, 425, 429]);
1163
1188
  }
1164
1189
  });
@@ -14971,6 +14996,16 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
14971
14996
  const probe = await ctx.vaultMemoryCollection.find(id2);
14972
14997
  if (probe.isDeleted || probe.supersededBy || !isOwnedByCtxUser(ctx, probe)) return null;
14973
14998
  if (opts.freeFormOnly && isKinded(probe)) return null;
14999
+ const readStoredContent = probe._getRaw("content");
15000
+ if (opts.expectedContent !== void 0) {
15001
+ const current = await vaultMemoryToStored(
15002
+ probe,
15003
+ ctx.walletAddress,
15004
+ ctx.signMessage,
15005
+ ctx.embeddedWalletSigner
15006
+ );
15007
+ if (current.content !== opts.expectedContent) return null;
15008
+ }
14974
15009
  const levelAfter = (current) => {
14975
15010
  if (opts.level !== void 0) return opts.level;
14976
15011
  if (opts.scope === void 0) return resolveMemoryLevel(current.level, current.scope);
@@ -14978,7 +15013,7 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
14978
15013
  };
14979
15014
  assertValidKindLevel(opts.kind !== void 0 ? opts.kind : probe.kind, levelAfter(probe));
14980
15015
  const encryptedKindValue = await encryptKindValue(ctx, opts.kindValue);
14981
- const encryptedContent = ctx.walletAddress && ctx.signMessage ? await encryptVaultMemoryContent(
15016
+ const encryptedContent = opts.content !== void 0 && ctx.walletAddress && ctx.signMessage ? await encryptVaultMemoryContent(
14982
15017
  opts.content,
14983
15018
  ctx.walletAddress,
14984
15019
  ctx.signMessage,
@@ -14986,13 +15021,13 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
14986
15021
  ) : opts.content;
14987
15022
  let stale = false;
14988
15023
  const record2 = probe;
14989
- const originalUpdatedAt = record2.updatedAt.getTime();
14990
15024
  await ctx.database.write(async () => {
14991
15025
  await assertCanWrite2(ctx);
14992
- if (record2.isDeleted || record2.supersededBy || !isOwnedByCtxUser(ctx, record2) || opts.freeFormOnly && isKinded(record2)) {
15026
+ if (record2.isDeleted || record2.supersededBy || !isOwnedByCtxUser(ctx, record2) || opts.freeFormOnly && isKinded(record2) || opts.expectedContent !== void 0 && record2._getRaw("content") !== readStoredContent) {
14993
15027
  stale = true;
14994
15028
  return;
14995
15029
  }
15030
+ const originalUpdatedAt = record2.updatedAt.getTime();
14996
15031
  const nextKind = opts.kind !== void 0 ? opts.kind : record2.kind;
14997
15032
  const nextLevel = levelAfter(record2);
14998
15033
  assertValidKindLevel(nextKind, nextLevel);
@@ -15008,7 +15043,7 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
15008
15043
  replayedObservation = opts.observationSourceIds.every((id3) => observedSources.includes(id3));
15009
15044
  }
15010
15045
  await record2.update((r) => {
15011
- r._setRaw("content", encryptedContent);
15046
+ if (encryptedContent !== void 0) r._setRaw("content", encryptedContent);
15012
15047
  if (opts.level !== void 0) {
15013
15048
  r._setRaw("level", nextLevel);
15014
15049
  r._setRaw("scope", scopeForLevel(nextLevel));
@@ -15776,7 +15811,7 @@ function classifyDecay(m, now, policy) {
15776
15811
  if (m.source === "manual" || m.source === SOURCE_PHOTO) return "keep";
15777
15812
  if (isTimeSensitiveType(m.factType) && m.eventTimeEnd !== null) {
15778
15813
  if (m.eventTimeEnd < now - resolved.pastEventGraceMs) return "archive";
15779
- if (m.eventTimeEnd >= now) return "keep";
15814
+ return "keep";
15780
15815
  }
15781
15816
  if (now - lastActivityAt(m) > ttlForType(m.factType, resolved)) return "archive";
15782
15817
  return "keep";
@@ -18832,7 +18867,6 @@ async function retainWithPersistence(content, ctx, options = {}) {
18832
18867
  const factTypeUpdate = pickFactTypeUpdate(existing, options.factType);
18833
18868
  const resurrect = resurrectFields(existing);
18834
18869
  const updated = await ctx.persistence.update(targetId, {
18835
- content: existing.content,
18836
18870
  freeFormOnly: true,
18837
18871
  proofCountIncrement: 1,
18838
18872
  observationSourceIds: options.sourceChunkIds,
@@ -19071,7 +19105,6 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
19071
19105
  const factTypeUpdate = pickFactTypeUpdate(existing, options.factType);
19072
19106
  const resurrect = resurrectFields(existing);
19073
19107
  const updated = await ctx.persistence.update(decision.targetId, {
19074
- content: existing.content,
19075
19108
  freeFormOnly: true,
19076
19109
  proofCountIncrement: 1,
19077
19110
  observationSourceIds: options.sourceChunkIds,
@@ -19099,6 +19132,10 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
19099
19132
  };
19100
19133
  }
19101
19134
  if (decision.action === "update" && decision.targetId && decision.content) {
19135
+ const expectedContent = candidates.find(
19136
+ (candidate) => candidate.id === decision.targetId
19137
+ )?.content;
19138
+ if (expectedContent === void 0) return null;
19102
19139
  const existing = await ctx.persistence.get(decision.targetId);
19103
19140
  if (!existing || existing.supersededBy) {
19104
19141
  return abandonToRace(
@@ -19106,6 +19143,9 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
19106
19143
  `update: target ${decision.targetId} ${existing ? "superseded" : "deleted"} before the write`
19107
19144
  );
19108
19145
  }
19146
+ if (existing.content !== expectedContent) {
19147
+ return abandonToRace(options, `update: target ${decision.targetId} edited before the write`);
19148
+ }
19109
19149
  const mergedSourceIds = unionStrings(
19110
19150
  existing.sourceChunkIds ?? [],
19111
19151
  options.sourceChunkIds ?? []
@@ -19118,6 +19158,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
19118
19158
  const resurrect = resurrectFields(existing);
19119
19159
  const updated = await ctx.persistence.update(decision.targetId, {
19120
19160
  content: decision.content,
19161
+ expectedContent,
19121
19162
  freeFormOnly: true,
19122
19163
  proofCountIncrement: 1,
19123
19164
  observationSourceIds: options.sourceChunkIds,
@@ -19130,10 +19171,10 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
19130
19171
  ...factTypeUpdate !== void 0 && { factType: factTypeUpdate }
19131
19172
  });
19132
19173
  if (!updated) {
19133
- await assertMergeTargetGoneOrThrow(ctx, decision.targetId);
19174
+ await assertMergeTargetGoneOrThrow(ctx, decision.targetId, expectedContent);
19134
19175
  return abandonToRace(
19135
19176
  options,
19136
- `update: target ${decision.targetId} deleted mid-write (consolidated rewrite lost)`
19177
+ `update: target ${decision.targetId} changed mid-write (consolidated rewrite lost)`
19137
19178
  );
19138
19179
  }
19139
19180
  cacheRowVector(
@@ -19160,9 +19201,9 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
19160
19201
  );
19161
19202
  return null;
19162
19203
  }
19163
- async function assertMergeTargetGoneOrThrow(ctx, targetId) {
19204
+ async function assertMergeTargetGoneOrThrow(ctx, targetId, expectedContent) {
19164
19205
  const stillExists = await (ctx.persistence.getFresh ? ctx.persistence.getFresh(targetId) : ctx.persistence.get(targetId));
19165
- if (stillExists && (stillExists.kind === null || stillExists.kind === void 0)) {
19206
+ if (stillExists && (stillExists.kind === null || stillExists.kind === void 0) && (expectedContent === void 0 || stillExists.content === expectedContent)) {
19166
19207
  throw new RetainError(
19167
19208
  "merge-not-persisted",
19168
19209
  `retain: merge into memory ${targetId} failed to persist`
@@ -22574,16 +22615,16 @@ function createRemoteMemoryPipeline(options) {
22574
22615
  create: (input) => write(createRow(input), 0),
22575
22616
  update: async (id2, patch) => {
22576
22617
  const existing = await get(id2);
22577
- if (!existing || patch.freeFormOnly && existing.kind !== null && existing.kind !== void 0)
22618
+ if (!existing || patch.expectedContent !== void 0 && existing.content !== patch.expectedContent || patch.freeFormOnly && existing.kind !== null && existing.kind !== void 0)
22578
22619
  return null;
22579
22620
  const snapshot = snapshots.get(id2);
22580
22621
  const memory = {
22581
22622
  ...snapshot.memory,
22582
- content: patch.content,
22623
+ content: patch.content ?? snapshot.memory.content,
22583
22624
  updated_at: patch.preserveUpdatedAt ? snapshot.memory.updated_at : Math.max(Date.now(), (snapshot.memory.topics_extracted_at ?? 0) + 1)
22584
22625
  };
22585
22626
  if (patch.scope !== void 0) memory.scope = patch.scope;
22586
- if (patch.embedding === void 0 && patch.content !== snapshot.memory.content) {
22627
+ if (patch.embedding === void 0 && patch.content !== void 0 && patch.content !== snapshot.memory.content) {
22587
22628
  delete memory.embedding;
22588
22629
  delete memory.embedding_model;
22589
22630
  }
@@ -5669,7 +5669,8 @@ interface CreateVaultMemoryOptions {
5669
5669
  geohash?: string;
5670
5670
  }
5671
5671
  interface UpdateVaultMemoryOptions {
5672
- content: string;
5672
+ content?: string;
5673
+ expectedContent?: string;
5673
5674
  /** When true, the write is skipped (returns null) if the row carries a profile kind. */
5674
5675
  freeFormOnly?: boolean;
5675
5676
  /**
@@ -6273,9 +6274,10 @@ declare function ttlForType(factType: string | null, policy?: Partial<DecayPolic
6273
6274
  * null `eventTimeEnd` is still ongoing, so this rule does not fire for it.
6274
6275
  * 4. Age fallback — `now - updatedAt > TTL(factType)` → archive. Durable types
6275
6276
  * have Infinity TTL, so this never fires for them; null/unknown use medium.
6276
- * EXCEPTION: a `plan`/`ongoing_context` with a still-upcoming event
6277
- * (`eventTimeEnd >= now`) is kept — it must not age-archive before the event
6278
- * happens; only rule 3 (after it passes) may archive it.
6277
+ * EXCEPTION: a `plan` / `ongoing_context` with an upcoming event or an event
6278
+ * within the grace period (`eventTimeEnd >= now - pastEventGraceMs`) stays
6279
+ * active, even after its activity TTL expires. Rule 3 archives it after the
6280
+ * grace period ends.
6279
6281
  * 5. Otherwise → keep.
6280
6282
  *
6281
6283
  * @param m Plaintext decay inputs for one row.
@@ -6606,7 +6608,9 @@ type MemoryCreate = Pick<CreateVaultMemoryOptions, "content" | "scope" | "factTy
6606
6608
  * `embedding` (with `embeddingModel`) only when you already have the new one.
6607
6609
  * @public
6608
6610
  */
6609
- type MemoryUpdate = Pick<UpdateVaultMemoryOptions, "content" | "scope" | "factType" | "eventTime" | "embedding" | "embeddingModel" | "kind" | "kindValue" | "level">;
6611
+ type MemoryUpdate = Pick<UpdateVaultMemoryOptions, "scope" | "factType" | "eventTime" | "embedding" | "embeddingModel" | "kind" | "kindValue" | "level"> & {
6612
+ content: string;
6613
+ };
6610
6614
  /**
6611
6615
  * Fact-only recall. Conversation chunks are message storage, not memory, so
6612
6616
  * the chunk-lane knobs are not part of this surface; nor is `folderId` (see
@@ -5669,7 +5669,8 @@ interface CreateVaultMemoryOptions {
5669
5669
  geohash?: string;
5670
5670
  }
5671
5671
  interface UpdateVaultMemoryOptions {
5672
- content: string;
5672
+ content?: string;
5673
+ expectedContent?: string;
5673
5674
  /** When true, the write is skipped (returns null) if the row carries a profile kind. */
5674
5675
  freeFormOnly?: boolean;
5675
5676
  /**
@@ -6273,9 +6274,10 @@ declare function ttlForType(factType: string | null, policy?: Partial<DecayPolic
6273
6274
  * null `eventTimeEnd` is still ongoing, so this rule does not fire for it.
6274
6275
  * 4. Age fallback — `now - updatedAt > TTL(factType)` → archive. Durable types
6275
6276
  * have Infinity TTL, so this never fires for them; null/unknown use medium.
6276
- * EXCEPTION: a `plan`/`ongoing_context` with a still-upcoming event
6277
- * (`eventTimeEnd >= now`) is kept — it must not age-archive before the event
6278
- * happens; only rule 3 (after it passes) may archive it.
6277
+ * EXCEPTION: a `plan` / `ongoing_context` with an upcoming event or an event
6278
+ * within the grace period (`eventTimeEnd >= now - pastEventGraceMs`) stays
6279
+ * active, even after its activity TTL expires. Rule 3 archives it after the
6280
+ * grace period ends.
6279
6281
  * 5. Otherwise → keep.
6280
6282
  *
6281
6283
  * @param m Plaintext decay inputs for one row.
@@ -6606,7 +6608,9 @@ type MemoryCreate = Pick<CreateVaultMemoryOptions, "content" | "scope" | "factTy
6606
6608
  * `embedding` (with `embeddingModel`) only when you already have the new one.
6607
6609
  * @public
6608
6610
  */
6609
- type MemoryUpdate = Pick<UpdateVaultMemoryOptions, "content" | "scope" | "factType" | "eventTime" | "embedding" | "embeddingModel" | "kind" | "kindValue" | "level">;
6611
+ type MemoryUpdate = Pick<UpdateVaultMemoryOptions, "scope" | "factType" | "eventTime" | "embedding" | "embeddingModel" | "kind" | "kindValue" | "level"> & {
6612
+ content: string;
6613
+ };
6610
6614
  /**
6611
6615
  * Fact-only recall. Conversation chunks are message storage, not memory, so
6612
6616
  * the chunk-lane knobs are not part of this surface; nor is `folderId` (see
@@ -20,7 +20,7 @@ import {
20
20
  taskTypeHeader,
21
21
  validateEndpointOverride,
22
22
  withInternalFlowMarker
23
- } from "./chunk-XW3LYSDA.mjs";
23
+ } from "./chunk-WRZTH4OS.mjs";
24
24
  import {
25
25
  BASE_URL,
26
26
  MCP_R2_DOMAIN,
@@ -13345,6 +13345,16 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
13345
13345
  const probe = await ctx.vaultMemoryCollection.find(id2);
13346
13346
  if (probe.isDeleted || probe.supersededBy || !isOwnedByCtxUser(ctx, probe)) return null;
13347
13347
  if (opts.freeFormOnly && isKinded(probe)) return null;
13348
+ const readStoredContent = probe._getRaw("content");
13349
+ if (opts.expectedContent !== void 0) {
13350
+ const current = await vaultMemoryToStored(
13351
+ probe,
13352
+ ctx.walletAddress,
13353
+ ctx.signMessage,
13354
+ ctx.embeddedWalletSigner
13355
+ );
13356
+ if (current.content !== opts.expectedContent) return null;
13357
+ }
13348
13358
  const levelAfter = (current) => {
13349
13359
  if (opts.level !== void 0) return opts.level;
13350
13360
  if (opts.scope === void 0) return resolveMemoryLevel(current.level, current.scope);
@@ -13352,7 +13362,7 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
13352
13362
  };
13353
13363
  assertValidKindLevel(opts.kind !== void 0 ? opts.kind : probe.kind, levelAfter(probe));
13354
13364
  const encryptedKindValue = await encryptKindValue(ctx, opts.kindValue);
13355
- const encryptedContent = ctx.walletAddress && ctx.signMessage ? await encryptVaultMemoryContent(
13365
+ const encryptedContent = opts.content !== void 0 && ctx.walletAddress && ctx.signMessage ? await encryptVaultMemoryContent(
13356
13366
  opts.content,
13357
13367
  ctx.walletAddress,
13358
13368
  ctx.signMessage,
@@ -13360,13 +13370,13 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
13360
13370
  ) : opts.content;
13361
13371
  let stale = false;
13362
13372
  const record2 = probe;
13363
- const originalUpdatedAt = record2.updatedAt.getTime();
13364
13373
  await ctx.database.write(async () => {
13365
13374
  await assertCanWrite2(ctx);
13366
- if (record2.isDeleted || record2.supersededBy || !isOwnedByCtxUser(ctx, record2) || opts.freeFormOnly && isKinded(record2)) {
13375
+ if (record2.isDeleted || record2.supersededBy || !isOwnedByCtxUser(ctx, record2) || opts.freeFormOnly && isKinded(record2) || opts.expectedContent !== void 0 && record2._getRaw("content") !== readStoredContent) {
13367
13376
  stale = true;
13368
13377
  return;
13369
13378
  }
13379
+ const originalUpdatedAt = record2.updatedAt.getTime();
13370
13380
  const nextKind = opts.kind !== void 0 ? opts.kind : record2.kind;
13371
13381
  const nextLevel = levelAfter(record2);
13372
13382
  assertValidKindLevel(nextKind, nextLevel);
@@ -13382,7 +13392,7 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
13382
13392
  replayedObservation = opts.observationSourceIds.every((id3) => observedSources.includes(id3));
13383
13393
  }
13384
13394
  await record2.update((r) => {
13385
- r._setRaw("content", encryptedContent);
13395
+ if (encryptedContent !== void 0) r._setRaw("content", encryptedContent);
13386
13396
  if (opts.level !== void 0) {
13387
13397
  r._setRaw("level", nextLevel);
13388
13398
  r._setRaw("scope", scopeForLevel(nextLevel));
@@ -14149,7 +14159,7 @@ function classifyDecay(m, now, policy) {
14149
14159
  if (m.source === "manual" || m.source === SOURCE_PHOTO) return "keep";
14150
14160
  if (isTimeSensitiveType(m.factType) && m.eventTimeEnd !== null) {
14151
14161
  if (m.eventTimeEnd < now - resolved.pastEventGraceMs) return "archive";
14152
- if (m.eventTimeEnd >= now) return "keep";
14162
+ return "keep";
14153
14163
  }
14154
14164
  if (now - lastActivityAt(m) > ttlForType(m.factType, resolved)) return "archive";
14155
14165
  return "keep";
@@ -17182,7 +17192,6 @@ async function retainWithPersistence(content, ctx, options = {}) {
17182
17192
  const factTypeUpdate = pickFactTypeUpdate(existing, options.factType);
17183
17193
  const resurrect = resurrectFields(existing);
17184
17194
  const updated = await ctx.persistence.update(targetId, {
17185
- content: existing.content,
17186
17195
  freeFormOnly: true,
17187
17196
  proofCountIncrement: 1,
17188
17197
  observationSourceIds: options.sourceChunkIds,
@@ -17386,7 +17395,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
17386
17395
  content: m.content,
17387
17396
  similarity: m.similarity
17388
17397
  }));
17389
- const { consolidateMemory: doConsolidate } = await import("./consolidate-4CQJYKRK.mjs");
17398
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-HKL5EGZT.mjs");
17390
17399
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
17391
17400
  if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
17392
17401
  if (decision.action === "supersede" && decision.content) {
@@ -17421,7 +17430,6 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
17421
17430
  const factTypeUpdate = pickFactTypeUpdate(existing, options.factType);
17422
17431
  const resurrect = resurrectFields(existing);
17423
17432
  const updated = await ctx.persistence.update(decision.targetId, {
17424
- content: existing.content,
17425
17433
  freeFormOnly: true,
17426
17434
  proofCountIncrement: 1,
17427
17435
  observationSourceIds: options.sourceChunkIds,
@@ -17449,6 +17457,10 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
17449
17457
  };
17450
17458
  }
17451
17459
  if (decision.action === "update" && decision.targetId && decision.content) {
17460
+ const expectedContent = candidates.find(
17461
+ (candidate) => candidate.id === decision.targetId
17462
+ )?.content;
17463
+ if (expectedContent === void 0) return null;
17452
17464
  const existing = await ctx.persistence.get(decision.targetId);
17453
17465
  if (!existing || existing.supersededBy) {
17454
17466
  return abandonToRace(
@@ -17456,6 +17468,9 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
17456
17468
  `update: target ${decision.targetId} ${existing ? "superseded" : "deleted"} before the write`
17457
17469
  );
17458
17470
  }
17471
+ if (existing.content !== expectedContent) {
17472
+ return abandonToRace(options, `update: target ${decision.targetId} edited before the write`);
17473
+ }
17459
17474
  const mergedSourceIds = unionStrings(
17460
17475
  existing.sourceChunkIds ?? [],
17461
17476
  options.sourceChunkIds ?? []
@@ -17468,6 +17483,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
17468
17483
  const resurrect = resurrectFields(existing);
17469
17484
  const updated = await ctx.persistence.update(decision.targetId, {
17470
17485
  content: decision.content,
17486
+ expectedContent,
17471
17487
  freeFormOnly: true,
17472
17488
  proofCountIncrement: 1,
17473
17489
  observationSourceIds: options.sourceChunkIds,
@@ -17480,10 +17496,10 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
17480
17496
  ...factTypeUpdate !== void 0 && { factType: factTypeUpdate }
17481
17497
  });
17482
17498
  if (!updated) {
17483
- await assertMergeTargetGoneOrThrow(ctx, decision.targetId);
17499
+ await assertMergeTargetGoneOrThrow(ctx, decision.targetId, expectedContent);
17484
17500
  return abandonToRace(
17485
17501
  options,
17486
- `update: target ${decision.targetId} deleted mid-write (consolidated rewrite lost)`
17502
+ `update: target ${decision.targetId} changed mid-write (consolidated rewrite lost)`
17487
17503
  );
17488
17504
  }
17489
17505
  cacheRowVector(
@@ -17510,9 +17526,9 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
17510
17526
  );
17511
17527
  return null;
17512
17528
  }
17513
- async function assertMergeTargetGoneOrThrow(ctx, targetId) {
17529
+ async function assertMergeTargetGoneOrThrow(ctx, targetId, expectedContent) {
17514
17530
  const stillExists = await (ctx.persistence.getFresh ? ctx.persistence.getFresh(targetId) : ctx.persistence.get(targetId));
17515
- if (stillExists && (stillExists.kind === null || stillExists.kind === void 0)) {
17531
+ if (stillExists && (stillExists.kind === null || stillExists.kind === void 0) && (expectedContent === void 0 || stillExists.content === expectedContent)) {
17516
17532
  throw new RetainError(
17517
17533
  "merge-not-persisted",
17518
17534
  `retain: merge into memory ${targetId} failed to persist`
@@ -20899,16 +20915,16 @@ function createRemoteMemoryPipeline(options) {
20899
20915
  create: (input) => write(createRow(input), 0),
20900
20916
  update: async (id2, patch) => {
20901
20917
  const existing = await get(id2);
20902
- if (!existing || patch.freeFormOnly && existing.kind !== null && existing.kind !== void 0)
20918
+ if (!existing || patch.expectedContent !== void 0 && existing.content !== patch.expectedContent || patch.freeFormOnly && existing.kind !== null && existing.kind !== void 0)
20903
20919
  return null;
20904
20920
  const snapshot = snapshots.get(id2);
20905
20921
  const memory = {
20906
20922
  ...snapshot.memory,
20907
- content: patch.content,
20923
+ content: patch.content ?? snapshot.memory.content,
20908
20924
  updated_at: patch.preserveUpdatedAt ? snapshot.memory.updated_at : Math.max(Date.now(), (snapshot.memory.topics_extracted_at ?? 0) + 1)
20909
20925
  };
20910
20926
  if (patch.scope !== void 0) memory.scope = patch.scope;
20911
- if (patch.embedding === void 0 && patch.content !== snapshot.memory.content) {
20927
+ if (patch.embedding === void 0 && patch.content !== void 0 && patch.content !== snapshot.memory.content) {
20912
20928
  delete memory.embedding;
20913
20929
  delete memory.embedding_model;
20914
20930
  }