{"version":3,"sources":["../src/processors/working-memory-state/processor.ts"],"names":["createHash","structuredPatch"],"mappings":"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","file":"chunk-43FJOLKM.cjs","sourcesContent":["/**\n * WorkingMemoryStateProcessor\n *\n * Experimental: delivers working memory to the model as a state signal instead\n * of folding it into the system message. Storage and the `setWorkingMemory`\n * tool are unchanged — this processor only changes the delivery path.\n *\n * Pattern matches `BrowserContextProcessor` in `@mastra/core/browser`:\n * - `stateId` namespaces the state lane on the thread.\n * - `cacheKey` is derived from the rendered payload so dedup is automatic.\n * - `contextWindow.hasSnapshot` re-injection ensures the model still sees the\n *   current snapshot after older messages drop out of the window.\n *\n * Delta emission (markdown mode only): when a prior snapshot exists in the\n * context window, the processor emits a unified-diff delta against that\n * snapshot's contents. Schema mode and the snapshot fallback always emit a\n * full snapshot.\n *\n * @example\n * ```ts\n * new Memory({\n *   options: {\n *     workingMemory: {\n *       enabled: true,\n *       template: '...',\n *       useStateSignals: true, // auto-attaches this processor\n *     },\n *   },\n * });\n * ```\n */\n\nimport { createHash } from 'node:crypto';\n\nimport type { MastraMemory, MemoryConfigInternal, WorkingMemoryTemplate } from '@mastra/core/memory';\nimport type {\n  ComputeStateSignalArgs,\n  ComputeStateSignalResult,\n  Processor,\n  ProcessorActiveStateSignal,\n} from '@mastra/core/processors';\nimport { structuredPatch } from 'diff';\n\nexport const WORKING_MEMORY_STATE_ID = 'working-memory';\nexport const WORKING_MEMORY_STATE_PROCESSOR_ID = 'working-memory-state';\n\nexport class WorkingMemoryStateProcessor implements Processor<typeof WORKING_MEMORY_STATE_PROCESSOR_ID> {\n  readonly id = WORKING_MEMORY_STATE_PROCESSOR_ID;\n  readonly stateId = WORKING_MEMORY_STATE_ID;\n\n  constructor(\n    private readonly memory: MastraMemory,\n    private readonly memoryConfig?: MemoryConfigInternal,\n  ) {}\n\n  async computeStateSignal(args: ComputeStateSignalArgs): Promise<ComputeStateSignalResult> {\n    const template = await this.memory.getWorkingMemoryTemplate({ memoryConfig: this.memoryConfig });\n    if (!template) return;\n\n    const data = await this.memory.getWorkingMemory({\n      threadId: args.threadId,\n      resourceId: args.resourceId,\n      memoryConfig: this.memoryConfig,\n    });\n\n    // Nothing stored yet — no state to broadcast. The setWorkingMemory tool\n    // description tells the model the expected shape; the signal carries state.\n    const contents = data?.trim();\n    if (!contents) return;\n\n    const cacheKey = stableWorkingMemoryCacheKey({ format: template.format, data: contents });\n    const shouldMakeSnapshot = !args.contextWindow.hasSnapshot;\n    if (args.tracking?.currentCacheKey === cacheKey && !shouldMakeSnapshot) return;\n\n    const mergedConfig = this.memory.getMergedThreadConfig(this.memoryConfig);\n    const scope = mergedConfig.workingMemory?.scope ?? 'resource';\n\n    const deltaCandidate =\n      template.format === 'markdown' && !shouldMakeSnapshot\n        ? buildMarkdownDelta({\n            lastSnapshot: args.lastSnapshot,\n            deltasSinceSnapshot: args.deltasSinceSnapshot,\n            nextContents: contents,\n          })\n        : undefined;\n\n    if (deltaCandidate) {\n      return {\n        id: WORKING_MEMORY_STATE_ID,\n        mode: 'delta',\n        cacheKey,\n        tagName: 'working-memory',\n        contents: deltaCandidate.contents,\n        delta: deltaCandidate.contents,\n        // Stash the full post-edit text on the signal so the next turn can\n        // diff against the most recently emitted state instead of the older\n        // snapshot. Invisible to the model.\n        value: contents,\n        attributes: {\n          format: template.format,\n          scope,\n          patch: 'unified-diff',\n        },\n      };\n    }\n\n    return {\n      id: WORKING_MEMORY_STATE_ID,\n      mode: 'snapshot',\n      cacheKey,\n      tagName: 'working-memory',\n      contents,\n      // Mirror contents in value so the first delta after a snapshot has a\n      // typed prior-state to diff against without falling back to contents.\n      value: contents,\n      attributes: {\n        format: template.format,\n        scope,\n      },\n    };\n  }\n}\n\n/**\n * Stable cache key for the rendered working memory payload. Returns a SHA-256\n * digest so dedup metadata stays compact regardless of payload size (working\n * memory blobs can grow arbitrarily long).\n */\nexport function stableWorkingMemoryCacheKey(input: {\n  format: WorkingMemoryTemplate['format'];\n  data: string | null;\n}): string {\n  const hash = createHash('sha256');\n  hash.update(input.format);\n  hash.update('\\0');\n  hash.update(input.data ?? '');\n  return `sha256:${hash.digest('hex')}`;\n}\n\n/**\n * Build a unified-diff delta against the most recently emitted state. Prefers\n * the latest delta's `value` (the post-edit full text) when available, falling\n * back to the snapshot's `value` and finally the snapshot's `contents`. This\n * keeps deltas incremental (B→C) instead of cumulative against a stale\n * snapshot (A→C), which matters when many small edits land between snapshots.\n *\n * Returns undefined when:\n * - there's no prior state to diff against\n * - the prior state isn't a plain string (multimodal signal)\n *\n * In either case the caller falls back to emitting a full snapshot.\n */\nfunction buildMarkdownDelta(args: {\n  lastSnapshot?: ProcessorActiveStateSignal;\n  deltasSinceSnapshot: ProcessorActiveStateSignal[];\n  nextContents: string;\n}): { contents: string } | undefined {\n  const { lastSnapshot, deltasSinceSnapshot, nextContents } = args;\n\n  // `value` is stored on the persisted signal's metadata (see applyStateSignal\n  // in @mastra/core/agent/state-signals). Read from there to recover the\n  // post-edit full text from the most recently emitted state.\n  const latestDelta = deltasSinceSnapshot.at(-1);\n  const prior =\n    pickStringValue(readSignalValue(latestDelta)) ??\n    pickStringValue(readSignalValue(lastSnapshot)) ??\n    (typeof lastSnapshot?.contents === 'string' ? lastSnapshot.contents : undefined);\n  ('');\n  if (!prior) return;\n\n  const patch = renderHunksOnly(prior, nextContents);\n\n  return { contents: patch };\n}\n\nfunction pickStringValue(value: unknown): string | undefined {\n  return typeof value === 'string' ? value : undefined;\n}\n\nfunction readSignalValue(signal: ProcessorActiveStateSignal | undefined): unknown {\n  return (signal?.metadata as { value?: unknown } | undefined)?.value;\n}\n\n/**\n * Render a unified-diff-style patch body containing only `@@` hunks and their\n * lines — dropping the filename preamble (`Index:` / `===` / `---` / `+++`)\n * that `createPatch` emits and the `\\ No newline at end of file` trailer.\n * The preamble exists for tooling like `patch -p1` to know which file to\n * apply to; we only ever diff a single working-memory blob. The newline\n * trailer is semantically meaningless to the model and adds noise to the\n * state signal.\n */\nfunction renderHunksOnly(prior: string, next: string): string {\n  const { hunks } = structuredPatch('', '', prior, next, '', '', { context: 0 });\n  return hunks\n    .map(hunk => {\n      const header = `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`;\n      const lines = hunk.lines.filter(line => !line.startsWith('\\\\ No newline at end of file'));\n      return [header, ...lines].join('\\n');\n    })\n    .join('\\n');\n}\n"]}