@mastra/memory 1.28.0-alpha.2 → 1.28.0-alpha.4

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.
@@ -57,8 +57,8 @@ probe_image_size_sync_js = __toESM$1(probe_image_size_sync_js, 1);
57
57
  let _mastra_core_schema = require("@mastra/core/schema");
58
58
  let _mastra_core_error = require("@mastra/core/error");
59
59
  let _mastra_core_processors = require("@mastra/core/processors");
60
- let util = require("util");
61
60
  let diff = require("diff");
61
+ let util = require("util");
62
62
  //#region ../_vendored/ai_v4/dist/dist-Cm5DZgxA.js
63
63
  var marker$6 = "vercel.ai.error";
64
64
  var symbol$6 = Symbol.for(marker$6);
@@ -3734,7 +3734,7 @@ function trimStartOfStream() {
3734
3734
  };
3735
3735
  }
3736
3736
  //#endregion
3737
- //#region ../_vendored/ai_v5/dist/dist-BuEMdYEn.js
3737
+ //#region ../_vendored/ai_v5/dist/dist-nVfjvSbw.js
3738
3738
  var marker$1$4 = "vercel.ai.error";
3739
3739
  var symbol$1$3 = Symbol.for(marker$1$4);
3740
3740
  var _a$1$3;
@@ -4287,6 +4287,7 @@ async function cancelResponseBody$1(response) {
4287
4287
  var initialGlobalFetch$1 = globalThis.fetch;
4288
4288
  isNodeDefaultFetch$1(initialGlobalFetch$1);
4289
4289
  function isNodeDefaultFetch$1(fetch) {
4290
+ if (typeof fetch !== "function") return false;
4290
4291
  const source = Function.prototype.toString.call(fetch);
4291
4292
  return source.includes("internal/deps/undici") || source.includes("lazy loading of undici");
4292
4293
  }
@@ -8622,7 +8623,7 @@ var object$1 = ({ schema: inputSchema }) => {
8622
8623
  };
8623
8624
  };
8624
8625
  //#endregion
8625
- //#region ../_vendored/ai_v6/dist/dist-BTWHzT8H.js
8626
+ //#region ../_vendored/ai_v6/dist/dist-Gd4EIxPM.js
8626
8627
  var marker$1 = "vercel.ai.error";
8627
8628
  var symbol$1$1 = Symbol.for(marker$1);
8628
8629
  var _a$1$1;
@@ -9196,6 +9197,7 @@ var DownloadError = class extends (_b$2 = AISDKError, _a$2 = symbol$2, _b$2) {
9196
9197
  var initialGlobalFetch = globalThis.fetch;
9197
9198
  isNodeDefaultFetch(initialGlobalFetch);
9198
9199
  function isNodeDefaultFetch(fetch) {
9200
+ if (typeof fetch !== "function") return false;
9199
9201
  const source = Function.prototype.toString.call(fetch);
9200
9202
  return source.includes("internal/deps/undici") || source.includes("lazy loading of undici");
9201
9203
  }
@@ -10774,7 +10776,7 @@ function withoutTrailingSlash(url) {
10774
10776
  return url == null ? void 0 : url.replace(/\/$/, "");
10775
10777
  }
10776
10778
  //#endregion
10777
- //#region ../_vendored/ai_v6/dist/dist-3jq9sPhA.js
10779
+ //#region ../_vendored/ai_v6/dist/dist-BoeKQsDe.js
10778
10780
  function getContext() {
10779
10781
  return { headers: {} };
10780
10782
  }
@@ -22434,6 +22436,151 @@ function createWorkingMemoryTool(config, options = {}) {
22434
22436
  };
22435
22437
  }
22436
22438
  //#endregion
22439
+ //#region src/processors/working-memory-state/processor.ts
22440
+ /**
22441
+ * WorkingMemoryStateProcessor
22442
+ *
22443
+ * Experimental: delivers working memory to the model as a state signal instead
22444
+ * of folding it into the system message. Storage and the `setWorkingMemory`
22445
+ * tool are unchanged — this processor only changes the delivery path.
22446
+ *
22447
+ * Pattern matches `BrowserContextProcessor` in `@mastra/core/browser`:
22448
+ * - `stateId` namespaces the state lane on the thread.
22449
+ * - `cacheKey` is derived from the rendered payload so dedup is automatic.
22450
+ * - `contextWindow.hasSnapshot` re-injection ensures the model still sees the
22451
+ * current snapshot after older messages drop out of the window.
22452
+ *
22453
+ * Delta emission (markdown mode only): when a prior snapshot exists in the
22454
+ * context window, the processor emits a unified-diff delta against that
22455
+ * snapshot's contents. Schema mode and the snapshot fallback always emit a
22456
+ * full snapshot.
22457
+ *
22458
+ * @example
22459
+ * ```ts
22460
+ * new Memory({
22461
+ * options: {
22462
+ * workingMemory: {
22463
+ * enabled: true,
22464
+ * template: '...',
22465
+ * useStateSignals: true, // auto-attaches this processor
22466
+ * },
22467
+ * },
22468
+ * });
22469
+ * ```
22470
+ */
22471
+ const WORKING_MEMORY_STATE_ID = "working-memory";
22472
+ const WORKING_MEMORY_STATE_PROCESSOR_ID = "working-memory-state";
22473
+ var WorkingMemoryStateProcessor = class {
22474
+ memory;
22475
+ memoryConfig;
22476
+ id = WORKING_MEMORY_STATE_PROCESSOR_ID;
22477
+ stateId = WORKING_MEMORY_STATE_ID;
22478
+ constructor(memory, memoryConfig) {
22479
+ this.memory = memory;
22480
+ this.memoryConfig = memoryConfig;
22481
+ }
22482
+ async computeStateSignal(args) {
22483
+ const template = await this.memory.getWorkingMemoryTemplate({ memoryConfig: this.memoryConfig });
22484
+ if (!template) return;
22485
+ const contents = (await this.memory.getWorkingMemory({
22486
+ threadId: args.threadId,
22487
+ resourceId: args.resourceId,
22488
+ memoryConfig: this.memoryConfig
22489
+ }))?.trim();
22490
+ if (!contents) return;
22491
+ const cacheKey = stableWorkingMemoryCacheKey({
22492
+ format: template.format,
22493
+ data: contents
22494
+ });
22495
+ const shouldMakeSnapshot = !args.contextWindow.hasSnapshot;
22496
+ if (args.tracking?.currentCacheKey === cacheKey && !shouldMakeSnapshot) return;
22497
+ const scope = this.memory.getMergedThreadConfig(this.memoryConfig).workingMemory?.scope ?? "resource";
22498
+ const deltaCandidate = template.format === "markdown" && !shouldMakeSnapshot ? buildMarkdownDelta({
22499
+ lastSnapshot: args.lastSnapshot,
22500
+ deltasSinceSnapshot: args.deltasSinceSnapshot,
22501
+ nextContents: contents
22502
+ }) : void 0;
22503
+ if (deltaCandidate) return {
22504
+ id: WORKING_MEMORY_STATE_ID,
22505
+ mode: "delta",
22506
+ cacheKey,
22507
+ tagName: "working-memory",
22508
+ contents: deltaCandidate.contents,
22509
+ delta: deltaCandidate.contents,
22510
+ value: contents,
22511
+ attributes: {
22512
+ format: template.format,
22513
+ scope,
22514
+ patch: "unified-diff"
22515
+ }
22516
+ };
22517
+ return {
22518
+ id: WORKING_MEMORY_STATE_ID,
22519
+ mode: "snapshot",
22520
+ cacheKey,
22521
+ tagName: "working-memory",
22522
+ contents,
22523
+ value: contents,
22524
+ attributes: {
22525
+ format: template.format,
22526
+ scope
22527
+ }
22528
+ };
22529
+ }
22530
+ };
22531
+ /**
22532
+ * Stable cache key for the rendered working memory payload. Returns a SHA-256
22533
+ * digest so dedup metadata stays compact regardless of payload size (working
22534
+ * memory blobs can grow arbitrarily long).
22535
+ */
22536
+ function stableWorkingMemoryCacheKey(input) {
22537
+ const hash = (0, crypto$1.createHash)("sha256");
22538
+ hash.update(input.format);
22539
+ hash.update("\0");
22540
+ hash.update(input.data ?? "");
22541
+ return `sha256:${hash.digest("hex")}`;
22542
+ }
22543
+ /**
22544
+ * Build a unified-diff delta against the most recently emitted state. Prefers
22545
+ * the latest delta's `value` (the post-edit full text) when available, falling
22546
+ * back to the snapshot's `value` and finally the snapshot's `contents`. This
22547
+ * keeps deltas incremental (B→C) instead of cumulative against a stale
22548
+ * snapshot (A→C), which matters when many small edits land between snapshots.
22549
+ *
22550
+ * Returns undefined when:
22551
+ * - there's no prior state to diff against
22552
+ * - the prior state isn't a plain string (multimodal signal)
22553
+ *
22554
+ * In either case the caller falls back to emitting a full snapshot.
22555
+ */
22556
+ function buildMarkdownDelta(args) {
22557
+ const { lastSnapshot, deltasSinceSnapshot, nextContents } = args;
22558
+ const prior = pickStringValue(readSignalValue(deltasSinceSnapshot.at(-1))) ?? pickStringValue(readSignalValue(lastSnapshot)) ?? (typeof lastSnapshot?.contents === "string" ? lastSnapshot.contents : void 0);
22559
+ if (!prior) return;
22560
+ return { contents: renderHunksOnly(prior, nextContents) };
22561
+ }
22562
+ function pickStringValue(value) {
22563
+ return typeof value === "string" ? value : void 0;
22564
+ }
22565
+ function readSignalValue(signal) {
22566
+ return (signal?.metadata)?.value;
22567
+ }
22568
+ /**
22569
+ * Render a unified-diff-style patch body containing only `@@` hunks and their
22570
+ * lines — dropping the filename preamble (`Index:` / `===` / `---` / `+++`)
22571
+ * that `createPatch` emits and the `` trailer.
22572
+ * The preamble exists for tooling like `patch -p1` to know which file to
22573
+ * apply to; we only ever diff a single working-memory blob. The newline
22574
+ * trailer is semantically meaningless to the model and adds noise to the
22575
+ * state signal.
22576
+ */
22577
+ function renderHunksOnly(prior, next) {
22578
+ const { hunks } = (0, diff.structuredPatch)("", "", prior, next, "", "", { context: 0 });
22579
+ return hunks.map((hunk) => {
22580
+ return [`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`, ...hunk.lines.filter((line) => !line.startsWith("\"))].join("\n");
22581
+ }).join("\n");
22582
+ }
22583
+ //#endregion
22437
22584
  //#region src/processors/observational-memory/activation-ttl.ts
22438
22585
  const MINUTE = 6e4;
22439
22586
  const HOUR = 60 * MINUTE;
@@ -25412,6 +25559,21 @@ var ReflectorRunner = class {
25412
25559
  memory;
25413
25560
  onReflectionCommitted;
25414
25561
  mastra;
25562
+ /**
25563
+ * Per-lock-key observation-token count of the last unproductive synchronous
25564
+ * reflection (failed, or committed while still over the reflection
25565
+ * threshold). `call()` already runs the full bounded compression ladder for
25566
+ * a given input, so re-running it against unchanged observations cannot
25567
+ * succeed — threshold-triggered sync reflection is skipped while the count
25568
+ * is unchanged and retried as soon as the input differs.
25569
+ * Keyed by lock key (thread/resource), which is stable across generations —
25570
+ * record ids change every time a generation is created.
25571
+ *
25572
+ * Lifecycle: an entry is deleted when a sync reflection finishes under
25573
+ * threshold and replaced on each unproductive attempt. Entries are one
25574
+ * number per active thread/resource key and live for the runner's lifetime.
25575
+ */
25576
+ syncReflectionSuppression = /* @__PURE__ */ new Map();
25415
25577
  constructor(opts) {
25416
25578
  this.reflectionConfig = opts.reflectionConfig;
25417
25579
  this.observationConfig = opts.observationConfig;
@@ -25494,6 +25656,8 @@ var ReflectorRunner = class {
25494
25656
  };
25495
25657
  let reflectedTokens = 0;
25496
25658
  let attemptNumber = 0;
25659
+ /** True when the latest attempt returned an empty block for non-empty input. */
25660
+ let emptyOutput = false;
25497
25661
  while (currentLevel <= maxLevel) {
25498
25662
  attemptNumber++;
25499
25663
  const isRetry = attemptNumber > 1;
@@ -25557,14 +25721,18 @@ var ReflectorRunner = class {
25557
25721
  }
25558
25722
  finalProviderMetadata = result.providerMetadata ?? finalProviderMetadata;
25559
25723
  parsed = parseReflectorOutput(result.text, observations, activeExtractors);
25724
+ emptyOutput = !parsed.degenerate && observations.trim().length > 0 && parsed.observations.trim().length === 0;
25560
25725
  if (parsed.degenerate) {
25561
25726
  omDebug(`[OM:callReflector] attempt #${attemptNumber}: degenerate repetition detected, treating as compression failure. ${describeDegenerateOutput(result.text, 2e3)}`);
25562
25727
  reflectedTokens = originalTokens;
25728
+ } else if (emptyOutput) {
25729
+ omDebug(`[OM:callReflector] attempt #${attemptNumber}: empty observations block for non-empty input, treating as compression failure`);
25730
+ reflectedTokens = originalTokens;
25563
25731
  } else reflectedTokens = this.tokenCounter.countObservations(parsed.observations);
25564
25732
  omDebug(`[OM:callReflector] attempt #${attemptNumber} parsed: reflectedTokens=${reflectedTokens}, targetThreshold=${targetThreshold}, compressionValid=${validateCompression(reflectedTokens, targetThreshold)}, parsedObsLen=${parsed.observations?.length}, degenerate=${parsed.degenerate ?? false}`);
25565
- if (!parsed.degenerate && (validateCompression(reflectedTokens, targetThreshold) || currentLevel >= maxLevel)) break;
25566
- if (parsed.degenerate && currentLevel >= maxLevel) {
25567
- omDebug(`[OM:callReflector] degenerate output persists at maxLevel=${maxLevel}, breaking`);
25733
+ if (!parsed.degenerate && !emptyOutput && (validateCompression(reflectedTokens, targetThreshold) || currentLevel >= maxLevel)) break;
25734
+ if ((parsed.degenerate || emptyOutput) && currentLevel >= maxLevel) {
25735
+ omDebug(`[OM:callReflector] ${parsed.degenerate ? "degenerate" : "empty"} output persists at maxLevel=${maxLevel}, breaking`);
25568
25736
  break;
25569
25737
  }
25570
25738
  if (streamContext?.writer) {
@@ -25602,6 +25770,7 @@ var ReflectorRunner = class {
25602
25770
  }
25603
25771
  currentLevel = Math.min(currentLevel + 1, maxLevel);
25604
25772
  }
25773
+ if (observations.trim().length > 0 && parsed.observations.trim().length === 0) throw new Error(`Reflector produced empty output after ${attemptNumber} attempt(s)${parsed.degenerate ? " (degenerate repetition)" : ""} — refusing to commit an empty reflection over ${originalTokens} observation tokens`);
25605
25774
  const structuredExtraction = await extractStructuredValues({
25606
25775
  agent,
25607
25776
  source: "reflector",
@@ -25955,6 +26124,13 @@ var ReflectorRunner = class {
25955
26124
  return;
25956
26125
  }
25957
26126
  }
26127
+ if (activationTriggeredBy === "threshold") {
26128
+ const suppressedAtTokens = this.syncReflectionSuppression.get(lockKey);
26129
+ if (suppressedAtTokens !== void 0 && observationTokens === suppressedAtTokens) {
26130
+ omDebug(`[OM:reflect] skipping sync reflection — observations unchanged at ${observationTokens} tokens since the last unproductive attempt; retrying once they change`);
26131
+ return;
26132
+ }
26133
+ }
25958
26134
  await this.storage.setReflectingFlag(record.id, true);
25959
26135
  registerOp(record.id, "reflecting");
25960
26136
  const cycleId = crypto.randomUUID();
@@ -26015,6 +26191,8 @@ var ReflectorRunner = class {
26015
26191
  reflection: reflectResult.observations,
26016
26192
  tokenCount: reflectionTokenCount
26017
26193
  });
26194
+ if (reflectionTokenCount >= reflectThreshold) this.syncReflectionSuppression.set(lockKey, reflectionTokenCount);
26195
+ else this.syncReflectionSuppression.delete(lockKey);
26018
26196
  await this.notifyReflectionCommitted({
26019
26197
  parentThreadId: requestedThreadId ?? record.threadId ?? "",
26020
26198
  resourceId: record.resourceId ?? "",
@@ -26074,6 +26252,7 @@ var ReflectorRunner = class {
26074
26252
  }
26075
26253
  reflectionError = error instanceof Error ? error : new Error(String(error));
26076
26254
  if (lifecycleError !== void 0 || abortSignal?.aborted) throw error;
26255
+ this.syncReflectionSuppression.set(lockKey, observationTokens);
26077
26256
  omError("[OM] Reflection failed", error);
26078
26257
  } finally {
26079
26258
  try {
@@ -26420,10 +26599,24 @@ function getLatestStepParts(parts) {
26420
26599
  return parts;
26421
26600
  }
26422
26601
  /**
26423
- * Returns true when a message contains at least one part with visible user/assistant
26424
- * content (text, tool-invocation, reasoning, image, file). Messages that only carry
26425
- * internal `data-*` parts (buffering markers, observation markers, etc.) return false.
26602
+ * Returns true for persisted Working Memory state signals (`role: 'signal'`,
26603
+ * `signal.type: 'state'`, `state.id: 'working-memory'`). OM must not observe these:
26604
+ * they mirror memory OM itself manages, and re-observing them creates a self-feedback
26605
+ * loop that can overwrite stored working memory (#21961). Only the working-memory lane
26606
+ * is filtered — other state lanes are outside OM's write path, so re-observing them
26607
+ * cannot corrupt OM-managed state; excluding them broadly is a separate decision.
26426
26608
  */
26609
+ function isWorkingMemoryStateSignal(message) {
26610
+ if (message.role !== "signal") return false;
26611
+ const signal = message.content.metadata?.signal;
26612
+ if (!signal || typeof signal !== "object" || Array.isArray(signal)) return false;
26613
+ const signalRecord = signal;
26614
+ if ((signalRecord.type ?? message.type) !== "state") return false;
26615
+ const metadata = signalRecord.metadata;
26616
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) return false;
26617
+ const state = metadata.state;
26618
+ return !!state && typeof state === "object" && !Array.isArray(state) && state.id === "working-memory";
26619
+ }
26427
26620
  function messageHasVisibleContent(msg) {
26428
26621
  const content = msg.content;
26429
26622
  if (content?.parts && Array.isArray(content.parts)) return content.parts.some((p) => {
@@ -27194,7 +27387,7 @@ var ObservationalMemory = class ObservationalMemory {
27194
27387
  }
27195
27388
  const result = [];
27196
27389
  for (const msg of allMessages) {
27197
- if (msg.role === "system") continue;
27390
+ if (msg.role === "system" || isWorkingMemoryStateSignal(msg)) continue;
27198
27391
  if (observedMessageIds?.has(msg.id)) continue;
27199
27392
  const endMarkerIndex = findLastCompletedObservationBoundary(msg);
27200
27393
  if (this.hasInProgressObservation(msg)) result.push(msg);
@@ -27416,7 +27609,7 @@ var ObservationalMemory = class ObservationalMemory {
27416
27609
  },
27417
27610
  filter: startDate ? { dateRange: { start: startDate } } : void 0
27418
27611
  });
27419
- return result.messages.filter((msg) => msg.role !== "system");
27612
+ return result.messages.filter((msg) => msg.role !== "system" && !isWorkingMemoryStateSignal(msg));
27420
27613
  }
27421
27614
  /**
27422
27615
  * Format unobserved messages from other threads as <unobserved-context> blocks.
@@ -27875,7 +28068,7 @@ ${formattedMessages}
27875
28068
  direction: "ASC"
27876
28069
  },
27877
28070
  filter: startDate ? { dateRange: { start: startDate } } : void 0
27878
- })).messages.filter((m) => !this.observedMessageIds.has(m.id));
28071
+ })).messages.filter((message) => !this.observedMessageIds.has(message.id) && !isWorkingMemoryStateSignal(message));
27879
28072
  if (filtered.length > 0) messagesByThread.set(thread.id, filtered);
27880
28073
  }
27881
28074
  if (messagesByThread.size === 0) return void 0;
@@ -29301,151 +29494,6 @@ var observational_memory_exports = /* @__PURE__ */ __exportAll({
29301
29494
  wrapInObservationGroup: () => wrapInObservationGroup
29302
29495
  });
29303
29496
  //#endregion
29304
- //#region src/processors/working-memory-state/processor.ts
29305
- /**
29306
- * WorkingMemoryStateProcessor
29307
- *
29308
- * Experimental: delivers working memory to the model as a state signal instead
29309
- * of folding it into the system message. Storage and the `setWorkingMemory`
29310
- * tool are unchanged — this processor only changes the delivery path.
29311
- *
29312
- * Pattern matches `BrowserContextProcessor` in `@mastra/core/browser`:
29313
- * - `stateId` namespaces the state lane on the thread.
29314
- * - `cacheKey` is derived from the rendered payload so dedup is automatic.
29315
- * - `contextWindow.hasSnapshot` re-injection ensures the model still sees the
29316
- * current snapshot after older messages drop out of the window.
29317
- *
29318
- * Delta emission (markdown mode only): when a prior snapshot exists in the
29319
- * context window, the processor emits a unified-diff delta against that
29320
- * snapshot's contents. Schema mode and the snapshot fallback always emit a
29321
- * full snapshot.
29322
- *
29323
- * @example
29324
- * ```ts
29325
- * new Memory({
29326
- * options: {
29327
- * workingMemory: {
29328
- * enabled: true,
29329
- * template: '...',
29330
- * useStateSignals: true, // auto-attaches this processor
29331
- * },
29332
- * },
29333
- * });
29334
- * ```
29335
- */
29336
- const WORKING_MEMORY_STATE_ID = "working-memory";
29337
- const WORKING_MEMORY_STATE_PROCESSOR_ID = "working-memory-state";
29338
- var WorkingMemoryStateProcessor = class {
29339
- memory;
29340
- memoryConfig;
29341
- id = WORKING_MEMORY_STATE_PROCESSOR_ID;
29342
- stateId = WORKING_MEMORY_STATE_ID;
29343
- constructor(memory, memoryConfig) {
29344
- this.memory = memory;
29345
- this.memoryConfig = memoryConfig;
29346
- }
29347
- async computeStateSignal(args) {
29348
- const template = await this.memory.getWorkingMemoryTemplate({ memoryConfig: this.memoryConfig });
29349
- if (!template) return;
29350
- const contents = (await this.memory.getWorkingMemory({
29351
- threadId: args.threadId,
29352
- resourceId: args.resourceId,
29353
- memoryConfig: this.memoryConfig
29354
- }))?.trim();
29355
- if (!contents) return;
29356
- const cacheKey = stableWorkingMemoryCacheKey({
29357
- format: template.format,
29358
- data: contents
29359
- });
29360
- const shouldMakeSnapshot = !args.contextWindow.hasSnapshot;
29361
- if (args.tracking?.currentCacheKey === cacheKey && !shouldMakeSnapshot) return;
29362
- const scope = this.memory.getMergedThreadConfig(this.memoryConfig).workingMemory?.scope ?? "resource";
29363
- const deltaCandidate = template.format === "markdown" && !shouldMakeSnapshot ? buildMarkdownDelta({
29364
- lastSnapshot: args.lastSnapshot,
29365
- deltasSinceSnapshot: args.deltasSinceSnapshot,
29366
- nextContents: contents
29367
- }) : void 0;
29368
- if (deltaCandidate) return {
29369
- id: WORKING_MEMORY_STATE_ID,
29370
- mode: "delta",
29371
- cacheKey,
29372
- tagName: "working-memory",
29373
- contents: deltaCandidate.contents,
29374
- delta: deltaCandidate.contents,
29375
- value: contents,
29376
- attributes: {
29377
- format: template.format,
29378
- scope,
29379
- patch: "unified-diff"
29380
- }
29381
- };
29382
- return {
29383
- id: WORKING_MEMORY_STATE_ID,
29384
- mode: "snapshot",
29385
- cacheKey,
29386
- tagName: "working-memory",
29387
- contents,
29388
- value: contents,
29389
- attributes: {
29390
- format: template.format,
29391
- scope
29392
- }
29393
- };
29394
- }
29395
- };
29396
- /**
29397
- * Stable cache key for the rendered working memory payload. Returns a SHA-256
29398
- * digest so dedup metadata stays compact regardless of payload size (working
29399
- * memory blobs can grow arbitrarily long).
29400
- */
29401
- function stableWorkingMemoryCacheKey(input) {
29402
- const hash = (0, crypto$1.createHash)("sha256");
29403
- hash.update(input.format);
29404
- hash.update("\0");
29405
- hash.update(input.data ?? "");
29406
- return `sha256:${hash.digest("hex")}`;
29407
- }
29408
- /**
29409
- * Build a unified-diff delta against the most recently emitted state. Prefers
29410
- * the latest delta's `value` (the post-edit full text) when available, falling
29411
- * back to the snapshot's `value` and finally the snapshot's `contents`. This
29412
- * keeps deltas incremental (B→C) instead of cumulative against a stale
29413
- * snapshot (A→C), which matters when many small edits land between snapshots.
29414
- *
29415
- * Returns undefined when:
29416
- * - there's no prior state to diff against
29417
- * - the prior state isn't a plain string (multimodal signal)
29418
- *
29419
- * In either case the caller falls back to emitting a full snapshot.
29420
- */
29421
- function buildMarkdownDelta(args) {
29422
- const { lastSnapshot, deltasSinceSnapshot, nextContents } = args;
29423
- const prior = pickStringValue(readSignalValue(deltasSinceSnapshot.at(-1))) ?? pickStringValue(readSignalValue(lastSnapshot)) ?? (typeof lastSnapshot?.contents === "string" ? lastSnapshot.contents : void 0);
29424
- if (!prior) return;
29425
- return { contents: renderHunksOnly(prior, nextContents) };
29426
- }
29427
- function pickStringValue(value) {
29428
- return typeof value === "string" ? value : void 0;
29429
- }
29430
- function readSignalValue(signal) {
29431
- return (signal?.metadata)?.value;
29432
- }
29433
- /**
29434
- * Render a unified-diff-style patch body containing only `@@` hunks and their
29435
- * lines — dropping the filename preamble (`Index:` / `===` / `---` / `+++`)
29436
- * that `createPatch` emits and the `` trailer.
29437
- * The preamble exists for tooling like `patch -p1` to know which file to
29438
- * apply to; we only ever diff a single working-memory blob. The newline
29439
- * trailer is semantically meaningless to the model and adds noise to the
29440
- * state signal.
29441
- */
29442
- function renderHunksOnly(prior, next) {
29443
- const { hunks } = (0, diff.structuredPatch)("", "", prior, next, "", "", { context: 0 });
29444
- return hunks.map((hunk) => {
29445
- return [`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`, ...hunk.lines.filter((line) => !line.startsWith("\"))].join("\n");
29446
- }).join("\n");
29447
- }
29448
- //#endregion
29449
29497
  //#region src/processors/working-memory-state/index.ts
29450
29498
  var working_memory_state_exports = /* @__PURE__ */ __exportAll({
29451
29499
  WORKING_MEMORY_STATE_ID: () => WORKING_MEMORY_STATE_ID,
@@ -30542,6 +30590,7 @@ ${workingMemory}`;
30542
30590
  instruction: omConfig.observation.instruction,
30543
30591
  threadTitle: omConfig.observation.threadTitle,
30544
30592
  observeAttachments: omConfig.observation.observeAttachments,
30593
+ continuationHints: omConfig.observation.continuationHints,
30545
30594
  extract: omConfig.observation.extract
30546
30595
  } : void 0,
30547
30596
  reflection: omConfig.reflection ? {
@@ -30552,6 +30601,7 @@ ${workingMemory}`;
30552
30601
  bufferActivation: omConfig.reflection.bufferActivation,
30553
30602
  blockAfter: omConfig.reflection.blockAfter,
30554
30603
  instruction: omConfig.reflection.instruction,
30604
+ continuationHints: omConfig.reflection.continuationHints,
30555
30605
  extract: omConfig.reflection.extract
30556
30606
  } : void 0
30557
30607
  });
@@ -31759,4 +31809,4 @@ Object.defineProperty(exports, "wrapInObservationGroup", {
31759
31809
  }
31760
31810
  });
31761
31811
 
31762
- //# sourceMappingURL=src-CIBCjg2A.cjs.map
31812
+ //# sourceMappingURL=src-C7Ozl6Pf.cjs.map