@anuma/sdk 10.5.4 → 10.6.1

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.
@@ -10208,16 +10208,34 @@ async function resumeStream(options) {
10208
10208
  onData,
10209
10209
  onThinking,
10210
10210
  onFinish,
10211
- onError
10211
+ onError,
10212
+ piiRedactor
10212
10213
  } = options;
10213
10214
  const strategy = getStrategy(handle.apiType);
10214
10215
  const accumulator = createStreamAccumulator(handle.model || void 0);
10216
+ const contentDeAnon = piiRedactor && onData ? createStreamingDeAnonymizer(piiRedactor, onData) : null;
10217
+ const thinkingDeAnon = piiRedactor && onThinking ? createStreamingDeAnonymizer(piiRedactor, onThinking) : null;
10215
10218
  const contentSmoother = new StreamSmoother((text16) => {
10216
- if (onData) onData(text16);
10219
+ if (contentDeAnon) contentDeAnon.push(text16);
10220
+ else if (onData) onData(text16);
10217
10221
  }, smoothing);
10218
10222
  const thinkingSmoother = new StreamSmoother((text16) => {
10219
- if (onThinking) onThinking(text16);
10223
+ if (thinkingDeAnon) thinkingDeAnon.push(text16);
10224
+ else if (onThinking) onThinking(text16);
10220
10225
  }, smoothing);
10226
+ const flushOutput = () => {
10227
+ contentSmoother.flush();
10228
+ thinkingSmoother.flush();
10229
+ contentDeAnon?.flush();
10230
+ thinkingDeAnon?.flush();
10231
+ };
10232
+ const buildFinal = () => strategy.buildFinalResponse(
10233
+ piiRedactor ? {
10234
+ ...accumulator,
10235
+ content: piiRedactor.deAnonymize(accumulator.content),
10236
+ thinking: piiRedactor.deAnonymize(accumulator.thinking)
10237
+ } : accumulator
10238
+ );
10221
10239
  const idleController = new AbortController();
10222
10240
  let watchdog;
10223
10241
  let idleFired = false;
@@ -10262,9 +10280,8 @@ async function resumeStream(options) {
10262
10280
  if (signal) signal.removeEventListener("abort", onCallerAbort);
10263
10281
  };
10264
10282
  const buildInterrupted = (message) => {
10265
- contentSmoother.flush();
10266
- thinkingSmoother.flush();
10267
- const data = accumulator.toolCalls.size > 0 ? stripToolCalls(strategy.buildFinalResponse(accumulator)) : strategy.buildFinalResponse(accumulator);
10283
+ flushOutput();
10284
+ const data = accumulator.toolCalls.size > 0 ? stripToolCalls(buildFinal()) : buildFinal();
10268
10285
  return { data, error: message, interrupted: true };
10269
10286
  };
10270
10287
  if (combinedSignal.aborted) {
@@ -10312,18 +10329,16 @@ async function resumeStream(options) {
10312
10329
  const err = replayErr instanceof Error ? replayErr : new Error(String(replayErr));
10313
10330
  const statusCode = parseSseStatusCode(err);
10314
10331
  if (statusCode === 410) {
10315
- contentSmoother.flush();
10316
- thinkingSmoother.flush();
10332
+ flushOutput();
10317
10333
  throw new StreamExpiredError(handle.inferenceId);
10318
10334
  }
10319
10335
  if (err.name === "AbortError" || combinedSignal.aborted) {
10320
10336
  return buildInterrupted(abortMessage());
10321
10337
  }
10322
- contentSmoother.flush();
10323
- thinkingSmoother.flush();
10338
+ flushOutput();
10324
10339
  if (onError) onError(err);
10325
10340
  return {
10326
- data: strategy.buildFinalResponse(accumulator),
10341
+ data: buildFinal(),
10327
10342
  error: err.message,
10328
10343
  interrupted: false,
10329
10344
  statusCode
@@ -10339,7 +10354,9 @@ async function resumeStream(options) {
10339
10354
  draining = true;
10340
10355
  await Promise.all([contentSmoother.drain(), thinkingSmoother.drain()]);
10341
10356
  if (playbackAborted) return buildInterrupted(abortMessage());
10342
- const response = strategy.buildFinalResponse(accumulator);
10357
+ contentDeAnon?.flush();
10358
+ thinkingDeAnon?.flush();
10359
+ const response = buildFinal();
10343
10360
  const deliveredOutput = emittedOutput || (accumulator.toolCallEvents?.length ?? 0) > 0;
10344
10361
  if (onFinish && deliveredOutput) onFinish(response);
10345
10362
  return { data: response, error: null, interrupted: false, empty: !deliveredOutput };
@@ -18483,6 +18500,52 @@ async function getDecayCandidatesRawOp(ctx) {
18483
18500
  trustTier: raw.trust_tier ?? null
18484
18501
  }));
18485
18502
  }
18503
+ async function applyDecayTransitionsOp(ctx, candidates, opts) {
18504
+ assertVaultScopeForSweep(ctx);
18505
+ const byId = new Map(candidates.map((candidate) => [candidate.uniqueId, candidate]));
18506
+ const ids = Array.from(byId.keys());
18507
+ const changed = /* @__PURE__ */ new Set();
18508
+ const CHUNK = 500;
18509
+ for (let i = 0; i < ids.length; i += CHUNK) {
18510
+ let applied = [];
18511
+ try {
18512
+ await ctx.database.write(async () => {
18513
+ await assertCanWrite2(ctx);
18514
+ const records = await ctx.vaultMemoryCollection.query(
18515
+ Q9.where("id", Q9.oneOf(ids.slice(i, i + CHUNK))),
18516
+ ...ctx.userId !== void 0 ? [Q9.where("user_id", ctx.userId)] : []
18517
+ ).fetch();
18518
+ const eligible = records.filter((record2) => {
18519
+ if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) return false;
18520
+ if (opts.transition === "delete") {
18521
+ return record2.archivedAt !== null && opts.now - record2.archivedAt > opts.hardDeleteWindowMs;
18522
+ }
18523
+ const candidate = byId.get(record2.id);
18524
+ return record2.archivedAt === null && record2.updatedAt.getTime() === candidate.updatedAt && (record2.lastObservedAt ?? null) === (candidate.lastObservedAt ?? null);
18525
+ });
18526
+ const prepared = eligible.map(
18527
+ (record2) => record2.prepareUpdate((row) => {
18528
+ if (opts.transition === "archive") row._setRaw("archived_at", opts.now);
18529
+ else row._setRaw("is_deleted", true);
18530
+ })
18531
+ );
18532
+ if (prepared.length > 0) await ctx.database.batch(prepared);
18533
+ applied = eligible.map((record2) => record2.id);
18534
+ });
18535
+ } catch {
18536
+ continue;
18537
+ }
18538
+ for (const id2 of applied) changed.add(id2);
18539
+ if (opts.transition === "delete" && ctx.entityCtx) {
18540
+ try {
18541
+ await unlinkMemoryEntitiesOp(ctx.entityCtx, applied);
18542
+ } catch {
18543
+ continue;
18544
+ }
18545
+ }
18546
+ }
18547
+ return changed;
18548
+ }
18486
18549
  async function archiveVaultMemoryOp(ctx, id2, opts) {
18487
18550
  try {
18488
18551
  const record2 = await ctx.vaultMemoryCollection.find(id2);
@@ -23519,25 +23582,8 @@ function isBorderline(input) {
23519
23582
  function vaultDecayBackend(vaultCtx) {
23520
23583
  return {
23521
23584
  candidates: () => getDecayCandidatesRawOp(vaultCtx),
23522
- archive: async (rows, now) => {
23523
- const archived = /* @__PURE__ */ new Set();
23524
- for (const c of rows) {
23525
- const ok = await archiveVaultMemoryOp(vaultCtx, c.uniqueId, {
23526
- now,
23527
- expectedUpdatedAt: c.updatedAt,
23528
- expectedLastObservedAt: c.lastObservedAt ?? null
23529
- });
23530
- if (ok) archived.add(c.uniqueId);
23531
- }
23532
- return archived;
23533
- },
23534
- hardDelete: async (rows, deleteOptions) => {
23535
- const deleted = /* @__PURE__ */ new Set();
23536
- for (const c of rows) {
23537
- if (await hardDeleteDecayedOp(vaultCtx, c.uniqueId, deleteOptions)) deleted.add(c.uniqueId);
23538
- }
23539
- return deleted;
23540
- }
23585
+ archive: (rows, now) => applyDecayTransitionsOp(vaultCtx, rows, { transition: "archive", now }),
23586
+ hardDelete: (rows, deleteOptions) => applyDecayTransitionsOp(vaultCtx, rows, { transition: "delete", ...deleteOptions })
23541
23587
  };
23542
23588
  }
23543
23589
  function createDecaySweeper(options) {
@@ -25550,6 +25596,7 @@ function createLocalMemoryStore(options) {
25550
25596
  "trust_tier",
25551
25597
  "superseded_by",
25552
25598
  "visibility",
25599
+ "twin_opt_in",
25553
25600
  "published_at",
25554
25601
  "geohash",
25555
25602
  "topics",
@@ -26736,6 +26783,7 @@ function subscriptionState({ memory, version }) {
26736
26783
  memory.event_time_kind,
26737
26784
  memory.trust_tier,
26738
26785
  memory.visibility,
26786
+ memory.twin_opt_in,
26739
26787
  memory.published_at,
26740
26788
  memory.geohash,
26741
26789
  memory.topics,
@@ -34438,6 +34486,7 @@ function useChat(options) {
34438
34486
  onToolCallArgumentsDelta,
34439
34487
  onStepFinish,
34440
34488
  resumable,
34489
+ background = false,
34441
34490
  onStreamMeta,
34442
34491
  apiType: defaultApiType = "auto",
34443
34492
  smoothing,
@@ -34446,12 +34495,12 @@ function useChat(options) {
34446
34495
  piiRedaction,
34447
34496
  onPiiRedacted
34448
34497
  } = options || {};
34498
+ const keepAlive = resumable === true && background;
34449
34499
  const [isLoading, setIsLoading] = useState4(false);
34450
34500
  const abortControllerRef = useRef3(null);
34451
34501
  const requestIdRef = useRef3(0);
34452
- const pendingInferenceRef = useRef3(
34453
- null
34454
- );
34502
+ const detachControllerRef = useRef3(null);
34503
+ const pendingInferenceRef = useRef3(null);
34455
34504
  const cancelInference = useCallback3(
34456
34505
  (inferenceId, requestToken) => {
34457
34506
  void (async () => {
@@ -34479,7 +34528,95 @@ function useChat(options) {
34479
34528
  abortControllerRef.current = null;
34480
34529
  }
34481
34530
  }, []);
34482
- useEffect3(() => stop, [stop]);
34531
+ const keepAliveRef = useRef3(keepAlive);
34532
+ keepAliveRef.current = keepAlive;
34533
+ useEffect3(
34534
+ () => () => {
34535
+ if (keepAliveRef.current) {
34536
+ if (detachControllerRef.current) {
34537
+ detachControllerRef.current.abort();
34538
+ return;
34539
+ }
34540
+ pendingInferenceRef.current = null;
34541
+ abortControllerRef.current?.abort();
34542
+ abortControllerRef.current = null;
34543
+ return;
34544
+ }
34545
+ stop();
34546
+ },
34547
+ [stop]
34548
+ );
34549
+ const resumeStream2 = useCallback3(
34550
+ async (handle, opts) => {
34551
+ const getterValidation = validateTokenGetter(getToken);
34552
+ if (!getterValidation.valid) {
34553
+ return { data: null, error: getterValidation.message, interrupted: false };
34554
+ }
34555
+ const { headless, ...resumeOpts } = opts ?? {};
34556
+ if (!headless && abortControllerRef.current) {
34557
+ return { data: null, error: "A request is already in progress", interrupted: false };
34558
+ }
34559
+ const abortController = new AbortController();
34560
+ const requestId = headless ? null : ++requestIdRef.current;
34561
+ if (!headless) {
34562
+ abortControllerRef.current = abortController;
34563
+ pendingInferenceRef.current = {
34564
+ id: handle.inferenceId,
34565
+ round: -1,
34566
+ cancel: () => cancelInference(handle.inferenceId),
34567
+ handle
34568
+ };
34569
+ setIsLoading(true);
34570
+ }
34571
+ const ownsForeground = () => requestId !== null && requestIdRef.current === requestId;
34572
+ try {
34573
+ let token;
34574
+ try {
34575
+ token = await getToken();
34576
+ } catch (err) {
34577
+ const error = err instanceof Error ? err : new Error(String(err));
34578
+ if (!headless) onError?.(error);
34579
+ return { data: null, error: error.message, interrupted: false };
34580
+ }
34581
+ const tokenValidation = validateToken(token);
34582
+ if (!tokenValidation.valid) {
34583
+ return { data: null, error: tokenValidation.message, interrupted: false };
34584
+ }
34585
+ return await resumeStream({
34586
+ handle,
34587
+ token,
34588
+ baseUrl,
34589
+ smoothing,
34590
+ signal: abortController.signal,
34591
+ piiRedactor: typeof resolvedPiiRedaction === "object" ? resolvedPiiRedaction : void 0,
34592
+ ...headless ? {} : {
34593
+ onData: (chunk) => globalOnData?.(chunk),
34594
+ onThinking: (chunk) => globalOnThinking?.(chunk),
34595
+ onFinish,
34596
+ onError
34597
+ },
34598
+ ...resumeOpts
34599
+ });
34600
+ } finally {
34601
+ if (ownsForeground()) {
34602
+ setIsLoading(false);
34603
+ if (abortControllerRef.current === abortController) abortControllerRef.current = null;
34604
+ if (pendingInferenceRef.current?.round === -1) pendingInferenceRef.current = null;
34605
+ }
34606
+ }
34607
+ },
34608
+ [
34609
+ getToken,
34610
+ baseUrl,
34611
+ smoothing,
34612
+ cancelInference,
34613
+ resolvedPiiRedaction,
34614
+ globalOnData,
34615
+ globalOnThinking,
34616
+ onFinish,
34617
+ onError
34618
+ ]
34619
+ );
34483
34620
  const sendMessage = useCallback3(
34484
34621
  async ({
34485
34622
  messages,
@@ -34507,9 +34644,12 @@ function useChat(options) {
34507
34644
  stop();
34508
34645
  const abortController = new AbortController();
34509
34646
  abortControllerRef.current = abortController;
34647
+ const detachController = keepAlive ? new AbortController() : null;
34648
+ detachControllerRef.current = detachController;
34510
34649
  const requestId = ++requestIdRef.current;
34511
34650
  setIsLoading(true);
34512
- let succeeded = false;
34651
+ let keepServerStream = false;
34652
+ const deferredErrors = [];
34513
34653
  try {
34514
34654
  const tokenGetterValidation = validateTokenGetter(getToken);
34515
34655
  if (!tokenGetterValidation.valid) {
@@ -34573,6 +34713,7 @@ function useChat(options) {
34573
34713
  apiType: requestApiType ?? defaultApiType,
34574
34714
  endpointOverride,
34575
34715
  resumable,
34716
+ detachSignal: detachController?.signal,
34576
34717
  onStreamMeta: resumable || onStreamMeta ? (meta) => {
34577
34718
  if (requestId !== requestIdRef.current || abortController.signal.aborted) {
34578
34719
  if (resumable) cancelInference(meta.inferenceId, token);
@@ -34581,12 +34722,19 @@ function useChat(options) {
34581
34722
  const previous = pendingInferenceRef.current;
34582
34723
  if (previous && previous.round === meta.round && previous.id !== meta.inferenceId)
34583
34724
  previous.cancel();
34725
+ const handle2 = {
34726
+ inferenceId: meta.inferenceId,
34727
+ apiType: resolveApiType(requestApiType ?? defaultApiType, model),
34728
+ model,
34729
+ conversationId
34730
+ };
34584
34731
  pendingInferenceRef.current = {
34585
34732
  id: meta.inferenceId,
34586
34733
  round: meta.round,
34587
- cancel: resumable ? () => cancelInference(meta.inferenceId, token) : () => void 0
34734
+ cancel: resumable ? () => cancelInference(meta.inferenceId, token) : () => void 0,
34735
+ handle: handle2
34588
34736
  };
34589
- onStreamMeta?.(meta);
34737
+ onStreamMeta?.({ ...handle2, round: meta.round });
34590
34738
  } : void 0,
34591
34739
  temperature,
34592
34740
  maxOutputTokens,
@@ -34608,17 +34756,44 @@ function useChat(options) {
34608
34756
  if (globalOnThinking) globalOnThinking(chunk);
34609
34757
  },
34610
34758
  onFinish,
34611
- onError,
34759
+ onError: keepAlive ? (error) => deferredErrors.push(error) : onError,
34612
34760
  onToolCall,
34613
34761
  onServerToolCall,
34614
34762
  onToolCallArgumentsDelta,
34615
- onStepFinish,
34763
+ onStepFinish: keepAlive || onStepFinish ? (event) => {
34764
+ const pending2 = pendingInferenceRef.current;
34765
+ if (keepAlive && pending2 && requestIdRef.current === requestId) {
34766
+ pendingInferenceRef.current = { ...pending2, settled: true };
34767
+ }
34768
+ onStepFinish?.(event);
34769
+ } : void 0,
34616
34770
  preProcessors,
34617
34771
  hooks,
34618
34772
  piiRedaction: requestPiiRedaction ?? resolvedPiiRedaction,
34619
34773
  onPiiRedacted
34620
34774
  });
34621
- succeeded = result.error === null;
34775
+ const pending = pendingInferenceRef.current;
34776
+ const handle = pending && !pending.settled ? pending.handle : null;
34777
+ const detach = result.error !== null && keepAlive && handle && result.error !== "Request aborted";
34778
+ if (!detach) deferredErrors.forEach((error) => onError?.(error));
34779
+ if (result.error === null) {
34780
+ keepServerStream = true;
34781
+ return result;
34782
+ }
34783
+ if (detach) {
34784
+ keepServerStream = true;
34785
+ return {
34786
+ data: result.data,
34787
+ error: "Request detached",
34788
+ detached: true,
34789
+ resume: handle,
34790
+ toolsChecksum: result.toolsChecksum
34791
+ };
34792
+ }
34793
+ if ("detached" in result && result.detached) {
34794
+ keepServerStream = true;
34795
+ return { ...result, resume: null };
34796
+ }
34622
34797
  return result;
34623
34798
  } catch (err) {
34624
34799
  return createErrorResult(
@@ -34629,7 +34804,8 @@ function useChat(options) {
34629
34804
  if (requestIdRef.current === requestId) {
34630
34805
  setIsLoading(false);
34631
34806
  abortControllerRef.current = null;
34632
- if (!succeeded) pendingInferenceRef.current?.cancel();
34807
+ if (detachControllerRef.current === detachController) detachControllerRef.current = null;
34808
+ if (!keepServerStream) pendingInferenceRef.current?.cancel();
34633
34809
  pendingInferenceRef.current = null;
34634
34810
  }
34635
34811
  }
@@ -34648,6 +34824,7 @@ function useChat(options) {
34648
34824
  onToolCallArgumentsDelta,
34649
34825
  onStepFinish,
34650
34826
  resumable,
34827
+ keepAlive,
34651
34828
  onStreamMeta,
34652
34829
  defaultApiType,
34653
34830
  smoothing,
@@ -34660,7 +34837,8 @@ function useChat(options) {
34660
34837
  return {
34661
34838
  isLoading,
34662
34839
  sendMessage,
34663
- stop
34840
+ stop,
34841
+ resumeStream: resumeStream2
34664
34842
  };
34665
34843
  }
34666
34844
 
@@ -21163,6 +21163,7 @@ function createCryptoPricePreProcessor(options = {}) {
21163
21163
 
21164
21164
  // src/lib/chat/resumeStream.ts
21165
21165
  init_clientConfig();
21166
+ init_redactor();
21166
21167
 
21167
21168
  // src/lib/chat/toolLoop.ts
21168
21169
  init_clientConfig();
@@ -25385,16 +25386,34 @@ async function resumeStream(options) {
25385
25386
  onData,
25386
25387
  onThinking,
25387
25388
  onFinish,
25388
- onError
25389
+ onError,
25390
+ piiRedactor
25389
25391
  } = options;
25390
25392
  const strategy = getStrategy(handle.apiType);
25391
25393
  const accumulator = createStreamAccumulator(handle.model || void 0);
25394
+ const contentDeAnon = piiRedactor && onData ? createStreamingDeAnonymizer(piiRedactor, onData) : null;
25395
+ const thinkingDeAnon = piiRedactor && onThinking ? createStreamingDeAnonymizer(piiRedactor, onThinking) : null;
25392
25396
  const contentSmoother = new StreamSmoother((text12) => {
25393
- if (onData) onData(text12);
25397
+ if (contentDeAnon) contentDeAnon.push(text12);
25398
+ else if (onData) onData(text12);
25394
25399
  }, smoothing);
25395
25400
  const thinkingSmoother = new StreamSmoother((text12) => {
25396
- if (onThinking) onThinking(text12);
25401
+ if (thinkingDeAnon) thinkingDeAnon.push(text12);
25402
+ else if (onThinking) onThinking(text12);
25397
25403
  }, smoothing);
25404
+ const flushOutput = () => {
25405
+ contentSmoother.flush();
25406
+ thinkingSmoother.flush();
25407
+ contentDeAnon?.flush();
25408
+ thinkingDeAnon?.flush();
25409
+ };
25410
+ const buildFinal = () => strategy.buildFinalResponse(
25411
+ piiRedactor ? {
25412
+ ...accumulator,
25413
+ content: piiRedactor.deAnonymize(accumulator.content),
25414
+ thinking: piiRedactor.deAnonymize(accumulator.thinking)
25415
+ } : accumulator
25416
+ );
25398
25417
  const idleController = new AbortController();
25399
25418
  let watchdog;
25400
25419
  let idleFired = false;
@@ -25439,9 +25458,8 @@ async function resumeStream(options) {
25439
25458
  if (signal) signal.removeEventListener("abort", onCallerAbort);
25440
25459
  };
25441
25460
  const buildInterrupted = (message) => {
25442
- contentSmoother.flush();
25443
- thinkingSmoother.flush();
25444
- const data = accumulator.toolCalls.size > 0 ? stripToolCalls(strategy.buildFinalResponse(accumulator)) : strategy.buildFinalResponse(accumulator);
25461
+ flushOutput();
25462
+ const data = accumulator.toolCalls.size > 0 ? stripToolCalls(buildFinal()) : buildFinal();
25445
25463
  return { data, error: message, interrupted: true };
25446
25464
  };
25447
25465
  if (combinedSignal.aborted) {
@@ -25489,18 +25507,16 @@ async function resumeStream(options) {
25489
25507
  const err = replayErr instanceof Error ? replayErr : new Error(String(replayErr));
25490
25508
  const statusCode = parseSseStatusCode(err);
25491
25509
  if (statusCode === 410) {
25492
- contentSmoother.flush();
25493
- thinkingSmoother.flush();
25510
+ flushOutput();
25494
25511
  throw new StreamExpiredError(handle.inferenceId);
25495
25512
  }
25496
25513
  if (err.name === "AbortError" || combinedSignal.aborted) {
25497
25514
  return buildInterrupted(abortMessage());
25498
25515
  }
25499
- contentSmoother.flush();
25500
- thinkingSmoother.flush();
25516
+ flushOutput();
25501
25517
  if (onError) onError(err);
25502
25518
  return {
25503
- data: strategy.buildFinalResponse(accumulator),
25519
+ data: buildFinal(),
25504
25520
  error: err.message,
25505
25521
  interrupted: false,
25506
25522
  statusCode
@@ -25516,7 +25532,9 @@ async function resumeStream(options) {
25516
25532
  draining = true;
25517
25533
  await Promise.all([contentSmoother.drain(), thinkingSmoother.drain()]);
25518
25534
  if (playbackAborted) return buildInterrupted(abortMessage());
25519
- const response = strategy.buildFinalResponse(accumulator);
25535
+ contentDeAnon?.flush();
25536
+ thinkingDeAnon?.flush();
25537
+ const response = buildFinal();
25520
25538
  const deliveredOutput = emittedOutput || (accumulator.toolCallEvents?.length ?? 0) > 0;
25521
25539
  if (onFinish && deliveredOutput) onFinish(response);
25522
25540
  return { data: response, error: null, interrupted: false, empty: !deliveredOutput };
@@ -29969,6 +29987,52 @@ async function getDecayCandidatesRawOp(ctx) {
29969
29987
  trustTier: raw.trust_tier ?? null
29970
29988
  }));
29971
29989
  }
29990
+ async function applyDecayTransitionsOp(ctx, candidates, opts) {
29991
+ assertVaultScopeForSweep(ctx);
29992
+ const byId = new Map(candidates.map((candidate) => [candidate.uniqueId, candidate]));
29993
+ const ids = Array.from(byId.keys());
29994
+ const changed = /* @__PURE__ */ new Set();
29995
+ const CHUNK = 500;
29996
+ for (let i = 0; i < ids.length; i += CHUNK) {
29997
+ let applied = [];
29998
+ try {
29999
+ await ctx.database.write(async () => {
30000
+ await assertCanWrite2(ctx);
30001
+ const records = await ctx.vaultMemoryCollection.query(
30002
+ import_watermelondb19.Q.where("id", import_watermelondb19.Q.oneOf(ids.slice(i, i + CHUNK))),
30003
+ ...ctx.userId !== void 0 ? [import_watermelondb19.Q.where("user_id", ctx.userId)] : []
30004
+ ).fetch();
30005
+ const eligible = records.filter((record) => {
30006
+ if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return false;
30007
+ if (opts.transition === "delete") {
30008
+ return record.archivedAt !== null && opts.now - record.archivedAt > opts.hardDeleteWindowMs;
30009
+ }
30010
+ const candidate = byId.get(record.id);
30011
+ return record.archivedAt === null && record.updatedAt.getTime() === candidate.updatedAt && (record.lastObservedAt ?? null) === (candidate.lastObservedAt ?? null);
30012
+ });
30013
+ const prepared = eligible.map(
30014
+ (record) => record.prepareUpdate((row) => {
30015
+ if (opts.transition === "archive") row._setRaw("archived_at", opts.now);
30016
+ else row._setRaw("is_deleted", true);
30017
+ })
30018
+ );
30019
+ if (prepared.length > 0) await ctx.database.batch(prepared);
30020
+ applied = eligible.map((record) => record.id);
30021
+ });
30022
+ } catch {
30023
+ continue;
30024
+ }
30025
+ for (const id of applied) changed.add(id);
30026
+ if (opts.transition === "delete" && ctx.entityCtx) {
30027
+ try {
30028
+ await unlinkMemoryEntitiesOp(ctx.entityCtx, applied);
30029
+ } catch {
30030
+ continue;
30031
+ }
30032
+ }
30033
+ }
30034
+ return changed;
30035
+ }
29972
30036
  async function archiveVaultMemoryOp(ctx, id, opts) {
29973
30037
  try {
29974
30038
  const record = await ctx.vaultMemoryCollection.find(id);
@@ -34734,25 +34798,8 @@ function isBorderline(input) {
34734
34798
  function vaultDecayBackend(vaultCtx) {
34735
34799
  return {
34736
34800
  candidates: () => getDecayCandidatesRawOp(vaultCtx),
34737
- archive: async (rows, now) => {
34738
- const archived = /* @__PURE__ */ new Set();
34739
- for (const c of rows) {
34740
- const ok = await archiveVaultMemoryOp(vaultCtx, c.uniqueId, {
34741
- now,
34742
- expectedUpdatedAt: c.updatedAt,
34743
- expectedLastObservedAt: c.lastObservedAt ?? null
34744
- });
34745
- if (ok) archived.add(c.uniqueId);
34746
- }
34747
- return archived;
34748
- },
34749
- hardDelete: async (rows, deleteOptions) => {
34750
- const deleted = /* @__PURE__ */ new Set();
34751
- for (const c of rows) {
34752
- if (await hardDeleteDecayedOp(vaultCtx, c.uniqueId, deleteOptions)) deleted.add(c.uniqueId);
34753
- }
34754
- return deleted;
34755
- }
34801
+ archive: (rows, now) => applyDecayTransitionsOp(vaultCtx, rows, { transition: "archive", now }),
34802
+ hardDelete: (rows, deleteOptions) => applyDecayTransitionsOp(vaultCtx, rows, { transition: "delete", ...deleteOptions })
34756
34803
  };
34757
34804
  }
34758
34805
  function createDecaySweeper(options) {
@@ -36780,6 +36827,7 @@ function createLocalMemoryStore(options) {
36780
36827
  "trust_tier",
36781
36828
  "superseded_by",
36782
36829
  "visibility",
36830
+ "twin_opt_in",
36783
36831
  "published_at",
36784
36832
  "geohash",
36785
36833
  "topics",
@@ -2470,6 +2470,8 @@ interface ResumeStreamOptions {
2470
2470
  onData?: (chunk: string) => void;
2471
2471
  /** Thinking/reasoning deltas as they replay. */
2472
2472
  onThinking?: (chunk: string) => void;
2473
+ /** Restores the placeholders this redactor assigned when the original request was sent. */
2474
+ piiRedactor?: PiiRedactor;
2473
2475
  /** Called once on a clean completion. Never called for 410 nor interrupted terminals. */
2474
2476
  onFinish?: (response: ApiResponse) => void;
2475
2477
  /** Called on a transient/unexpected failure. Never called for 410 (throws) nor interrupted terminals. */
@@ -2470,6 +2470,8 @@ interface ResumeStreamOptions {
2470
2470
  onData?: (chunk: string) => void;
2471
2471
  /** Thinking/reasoning deltas as they replay. */
2472
2472
  onThinking?: (chunk: string) => void;
2473
+ /** Restores the placeholders this redactor assigned when the original request was sent. */
2474
+ piiRedactor?: PiiRedactor;
2473
2475
  /** Called once on a clean completion. Never called for 410 nor interrupted terminals. */
2474
2476
  onFinish?: (response: ApiResponse) => void;
2475
2477
  /** Called on a transient/unexpected failure. Never called for 410 (throws) nor interrupted terminals. */