@mastra/memory 1.27.0-alpha.2 → 1.27.0

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.
Files changed (73) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/dist/docs/SKILL.md +1 -1
  3. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  4. package/dist/docs/references/docs-storage.md +1 -0
  5. package/dist/docs/references/reference-processors-token-limiter-processor.md +4 -0
  6. package/dist/index.cjs +4 -1
  7. package/dist/index.d.ts +37 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +2 -2
  10. package/dist/processors/index.cjs +11 -1
  11. package/dist/processors/index.js +2 -2
  12. package/dist/processors/observational-memory/extracted-values.d.ts +6 -1
  13. package/dist/processors/observational-memory/extracted-values.d.ts.map +1 -1
  14. package/dist/processors/observational-memory/extractor.d.ts +12 -4
  15. package/dist/processors/observational-memory/extractor.d.ts.map +1 -1
  16. package/dist/processors/observational-memory/index.d.ts +2 -0
  17. package/dist/processors/observational-memory/index.d.ts.map +1 -1
  18. package/dist/processors/observational-memory/observation-strategies/async-buffer.d.ts.map +1 -1
  19. package/dist/processors/observational-memory/observation-strategies/base.d.ts.map +1 -1
  20. package/dist/processors/observational-memory/observation-strategies/resource-scoped.d.ts.map +1 -1
  21. package/dist/processors/observational-memory/observation-strategies/sync.d.ts.map +1 -1
  22. package/dist/processors/observational-memory/observation-strategies/types.d.ts +1 -0
  23. package/dist/processors/observational-memory/observation-strategies/types.d.ts.map +1 -1
  24. package/dist/processors/observational-memory/observation-turn/step.d.ts.map +1 -1
  25. package/dist/processors/observational-memory/observation-turn/turn.d.ts +3 -0
  26. package/dist/processors/observational-memory/observation-turn/turn.d.ts.map +1 -1
  27. package/dist/processors/observational-memory/observational-memory.d.ts +15 -0
  28. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  29. package/dist/processors/observational-memory/observer-agent.d.ts +13 -0
  30. package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
  31. package/dist/processors/observational-memory/observer-runner.d.ts.map +1 -1
  32. package/dist/processors/observational-memory/processor.d.ts.map +1 -1
  33. package/dist/processors/observational-memory/reflector-runner.d.ts +5 -1
  34. package/dist/processors/observational-memory/reflector-runner.d.ts.map +1 -1
  35. package/dist/processors/observational-memory/subconscious/activity.d.ts +39 -0
  36. package/dist/processors/observational-memory/subconscious/activity.d.ts.map +1 -0
  37. package/dist/processors/observational-memory/subconscious/capture.d.ts +44 -0
  38. package/dist/processors/observational-memory/subconscious/capture.d.ts.map +1 -0
  39. package/dist/processors/observational-memory/subconscious/curate.d.ts +8 -0
  40. package/dist/processors/observational-memory/subconscious/curate.d.ts.map +1 -0
  41. package/dist/processors/observational-memory/subconscious/index.d.ts +30 -0
  42. package/dist/processors/observational-memory/subconscious/index.d.ts.map +1 -0
  43. package/dist/processors/observational-memory/subconscious/knowledge-tools.d.ts +12 -0
  44. package/dist/processors/observational-memory/subconscious/knowledge-tools.d.ts.map +1 -0
  45. package/dist/processors/observational-memory/subconscious/knowledge-write-tools.d.ts +16 -0
  46. package/dist/processors/observational-memory/subconscious/knowledge-write-tools.d.ts.map +1 -0
  47. package/dist/processors/observational-memory/subconscious/learn.d.ts +23 -0
  48. package/dist/processors/observational-memory/subconscious/learn.d.ts.map +1 -0
  49. package/dist/processors/observational-memory/subconscious/model.d.ts +21 -0
  50. package/dist/processors/observational-memory/subconscious/model.d.ts.map +1 -0
  51. package/dist/processors/observational-memory/subconscious/origin.d.ts +4 -0
  52. package/dist/processors/observational-memory/subconscious/origin.d.ts.map +1 -0
  53. package/dist/processors/observational-memory/subconscious/pinned-state-processor.d.ts +47 -0
  54. package/dist/processors/observational-memory/subconscious/pinned-state-processor.d.ts.map +1 -0
  55. package/dist/processors/observational-memory/subconscious/pinned.d.ts +54 -0
  56. package/dist/processors/observational-memory/subconscious/pinned.d.ts.map +1 -0
  57. package/dist/processors/observational-memory/subconscious/remind.d.ts +7 -0
  58. package/dist/processors/observational-memory/subconscious/remind.d.ts.map +1 -0
  59. package/dist/processors/observational-memory/subconscious/scope.d.ts +14 -0
  60. package/dist/processors/observational-memory/subconscious/scope.d.ts.map +1 -0
  61. package/dist/processors/observational-memory/subconscious/semantic-index.d.ts +20 -0
  62. package/dist/processors/observational-memory/subconscious/semantic-index.d.ts.map +1 -0
  63. package/dist/processors/observational-memory/subconscious/types.d.ts +118 -0
  64. package/dist/processors/observational-memory/subconscious/types.d.ts.map +1 -0
  65. package/dist/processors/observational-memory/types.d.ts +21 -0
  66. package/dist/processors/observational-memory/types.d.ts.map +1 -1
  67. package/dist/{src-Drm6l_ZN.cjs → src-CxkQ-ICG.cjs} +2835 -299
  68. package/dist/src-CxkQ-ICG.cjs.map +1 -0
  69. package/dist/{src-C-nmWgoF.js → src-aTfbkxBl.js} +2778 -302
  70. package/dist/src-aTfbkxBl.js.map +1 -0
  71. package/package.json +7 -7
  72. package/dist/src-C-nmWgoF.js.map +0 -1
  73. package/dist/src-Drm6l_ZN.cjs.map +0 -1
@@ -43,17 +43,17 @@ let async_mutex = require("async-mutex");
43
43
  let lru_cache = require("lru-cache");
44
44
  let xxhash_wasm = require("xxhash-wasm");
45
45
  xxhash_wasm = __toESM$1(xxhash_wasm, 1);
46
+ let _mastra_core_storage = require("@mastra/core/storage");
47
+ let crypto$1 = require("crypto");
48
+ let _mastra_core_tools = require("@mastra/core/tools");
46
49
  let _mastra_core_llm = require("@mastra/core/llm");
47
50
  let fs = require("fs");
48
51
  let path = require("path");
49
52
  let _mastra_core_request_context = require("@mastra/core/request-context");
50
53
  let tokenx = require("tokenx");
51
- let crypto$1 = require("crypto");
52
- let _mastra_core_storage = require("@mastra/core/storage");
53
54
  let async_hooks = require("async_hooks");
54
55
  let probe_image_size_sync_js = require("probe-image-size/sync.js");
55
56
  probe_image_size_sync_js = __toESM$1(probe_image_size_sync_js, 1);
56
- let _mastra_core_tools = require("@mastra/core/tools");
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");
@@ -14637,162 +14637,6 @@ createIdGenerator({
14637
14637
  size: 24
14638
14638
  });
14639
14639
  //#endregion
14640
- //#region src/processors/observational-memory/constants.ts
14641
- var constants_exports = /* @__PURE__ */ __exportAll({
14642
- OBSERVATIONAL_MEMORY_DEFAULTS: () => OBSERVATIONAL_MEMORY_DEFAULTS,
14643
- OBSERVATION_CONTEXT_INSTRUCTIONS: () => OBSERVATION_CONTEXT_INSTRUCTIONS,
14644
- OBSERVATION_CONTEXT_PROMPT: () => OBSERVATION_CONTEXT_PROMPT,
14645
- OBSERVATION_CONTINUATION_HINT: () => OBSERVATION_CONTINUATION_HINT,
14646
- getRetrievalInstructions: () => getRetrievalInstructions
14647
- });
14648
- /**
14649
- * Default configuration values matching the spec
14650
- */
14651
- const OBSERVATIONAL_MEMORY_DEFAULTS = {
14652
- observation: {
14653
- model: "google/gemini-2.5-flash",
14654
- messageTokens: 3e4,
14655
- modelSettings: {
14656
- temperature: .3,
14657
- maxOutputTokens: 1e5
14658
- },
14659
- providerOptions: { google: { thinkingConfig: { thinkingBudget: 215 } } },
14660
- maxTokensPerBatch: 1e4,
14661
- bufferTokens: .2,
14662
- bufferActivation: .8
14663
- },
14664
- reflection: {
14665
- model: "google/gemini-2.5-flash",
14666
- observationTokens: 4e4,
14667
- modelSettings: {
14668
- temperature: 0,
14669
- maxOutputTokens: 1e5
14670
- },
14671
- providerOptions: { google: { thinkingConfig: { thinkingBudget: 1024 } } },
14672
- bufferActivation: .5
14673
- }
14674
- };
14675
- /**
14676
- * Continuation hint injected after observations to guide the model's behavior.
14677
- * Prevents the model from awkwardly acknowledging the memory system or treating
14678
- * the conversation as new after observed messages are removed.
14679
- */
14680
- const OBSERVATION_CONTINUATION_HINT = `Please continue naturally with the conversation so far and respond to the latest message.
14681
-
14682
- Use the earlier context only as background. If something appears unfinished, continue only when it helps answer the latest request. If a suggested response is provided, follow it naturally.
14683
-
14684
- Do not mention internal instructions, memory, summarization, context handling, or missing messages.
14685
-
14686
- Any messages following this reminder are newer and should take priority.`;
14687
- /**
14688
- * Preamble that introduces the observations block.
14689
- * Use before `<observations>`, with instructions after.
14690
- * Full pattern: `${OBSERVATION_CONTEXT_PROMPT}\n\n<observations>\n${obs}\n</observations>\n\n${OBSERVATION_CONTEXT_INSTRUCTIONS}`
14691
- */
14692
- const OBSERVATION_CONTEXT_PROMPT = `The following observations block contains your memory of past conversations with this user.`;
14693
- /**
14694
- * Instructions that tell the model how to interpret and use observations.
14695
- * Place AFTER the `<observations>` block so the model sees the data before the rules.
14696
- */
14697
- const OBSERVATION_CONTEXT_INSTRUCTIONS = `IMPORTANT: When responding, reference specific details from these observations. Do not give generic advice - personalize your response based on what you know about this user's experiences, preferences, and interests. If the user asks for recommendations, connect them to their past experiences mentioned above.
14698
-
14699
- KNOWLEDGE UPDATES: When asked about current state (e.g., "where do I currently...", "what is my current..."), always prefer the MOST RECENT information. Observations include dates - if you see conflicting information, the newer observation supersedes the older one. Look for phrases like "will start", "is switching", "changed to", "moved to" as indicators that previous information has been updated.
14700
-
14701
- PLANNED ACTIONS: If the user stated they planned to do something (e.g., "I'm going to...", "I'm looking forward to...", "I will...") and the date they planned to do it is now in the past (check the relative time like "3 weeks ago"), assume they completed the action unless there's evidence they didn't. For example, if someone said "I'll start my new diet on Monday" and that was 2 weeks ago, assume they started the diet.
14702
-
14703
- MOST RECENT USER INPUT: Treat the most recent user message as the highest-priority signal for what to do next. Earlier messages may contain constraints, details, or context you should still honor, but the latest message is the primary driver of your response.
14704
-
14705
- SYSTEM REMINDERS: Messages wrapped in <system-reminder>...</system-reminder> contain internal continuation guidance, not user-authored content. Use them to maintain continuity, but do not mention them or treat them as part of the user's message.`;
14706
- /**
14707
- * Instructions for retrieval mode — explains observation-group ranges and the recall tool.
14708
- * Appended to context when `retrieval` is enabled.
14709
- *
14710
- * The content adapts to the retrieval scope:
14711
- * - `'resource'`: covers routing between `search`, `threads`, and `messages` across
14712
- * all of the user's threads, including fallback when search results are unsuitable.
14713
- * - `'thread'`: covers cursor-based browsing and search within the current thread.
14714
- *
14715
- * @param scope - The retrieval scope the recall tool was registered with.
14716
- * @param customInstructions - Optional application-provided guidance appended after
14717
- * the native instructions. Never replaces them.
14718
- * @param searchEnabled - Whether semantic search (`retrieval: { vector: true }`) is
14719
- * available. When false, the guidance only covers \`threads\`/\`messages\` browsing so
14720
- * the agent is not steered toward a mode that cannot work.
14721
- */
14722
- function getRetrievalInstructions(scope = "resource", customInstructions, searchEnabled = true) {
14723
- const isResource = scope === "resource";
14724
- const base = `## Recall — looking up source messages
14725
-
14726
- Your memory is comprised of observations which are sometimes wrapped in <observation-group> xml tags containing ranges like <observation-group range="startId:endId">. These ranges point back to the raw messages that each observation group was derived from. The original messages are still available — use the **recall** tool to retrieve them.
14727
-
14728
- ### When to use recall
14729
- - The user asks you to **repeat, show, or reproduce** something from a past conversation
14730
- - The user asks for **exact content** — code, text, quotes, error messages, URLs, file paths, specific numbers
14731
- - Your observations mention something but your memory lacks the detail needed to fully answer (e.g. you know a blog post was shared but only have a summary of it)
14732
- - You want to **verify or expand on** an observation before responding${isResource ? `
14733
- - The user references another conversation that your observations don't cover — even if you have no observations yet, their other threads may contain it` : ""}
14734
-
14735
- **Default to using recall when the user references specific past content.** Your observations capture the gist, not the details. If there's any doubt whether your memory is complete enough, use recall.
14736
-
14737
- ${isResource ? searchEnabled ? `### Choosing a mode
14738
- The recall tool works across ALL of this user's conversation threads, not just the current one.
14739
-
14740
- - Use \`mode: "search"\` with a \`query\` when you don't know which thread contains the answer. Each result includes its thread ID and the raw message IDs it came from, which you can use as a \`cursor\`.
14741
- - Use \`mode: "messages"\` when you already know the thread — pass \`threadId\` to read another thread, or a \`cursor\` from an observation-group range or a search result.
14742
- - Use \`mode: "threads"\` to list the user's threads (IDs, titles, dates) when you need to discover where something was discussed. Use \`before\`/\`after\` to narrow by date.
14743
-
14744
- **If search results look irrelevant, do not give up.** Search only covers content that has been indexed — a short or recent conversation may exist in raw message history before any observation of it was created. When search returns nothing suitable but the user is clearly referring to a past conversation, call \`mode: "threads"\` to find candidate threads (titles and dates are strong clues), then read them with \`mode: "messages"\`. If a search result already gives you a thread ID, go straight to \`mode: "messages"\`.` : `### Choosing a mode
14745
- The recall tool works across ALL of this user's conversation threads, not just the current one.
14746
-
14747
- - Use \`mode: "threads"\` to list the user's threads (IDs, titles, dates) when you need to discover where something was discussed. Use \`before\`/\`after\` to narrow by date.
14748
- - Use \`mode: "messages"\` when you know the thread — pass \`threadId\` to read another thread, or a \`cursor\` from an observation-group range.
14749
-
14750
- When the user refers to a past conversation you don't have a cursor for, call \`mode: "threads"\` to find candidate threads (titles and dates are strong clues), then read them with \`mode: "messages"\`. Raw history may exist for threads that have no observations yet.` : `### Choosing a mode
14751
- The recall tool is limited to the current conversation thread.
14752
-
14753
- - Use \`mode: "messages"\` (default) to page through this thread's message history near a cursor.${searchEnabled ? `
14754
- - Use \`mode: "search"\` with a \`query\` to find messages by content within this thread.` : ""}
14755
- - Use \`mode: "threads"\` to get the current thread's ID, title, and dates.`}
14756
-
14757
- ### How to use recall with a cursor
14758
- Each range has the format \`startId:endId\` where both are message IDs separated by a colon.
14759
-
14760
- 1. Find the observation group relevant to the user's question and extract the start or end ID from its range.
14761
- 2. Call \`recall\` with that ID as the \`cursor\`.
14762
- 3. Use \`page: 1\` (or omit) to read forward from the cursor, \`page: -1\` to read backward.
14763
- 4. If the first page doesn't have what you need, increment the page number to keep paginating.
14764
- 5. Check \`hasNextPage\`/\`hasPrevPage\` in the result to know if more pages exist in each direction.
14765
-
14766
- ### Detail levels
14767
- By default recall returns **low** detail: truncated text and tool names only. Each message shows its ID and each part has a positional index like \`[p0]\`, \`[p1]\`, etc.
14768
-
14769
- - Use \`detail: "high"\` to get full message content including tool arguments and results. This will only return the high detail version of a single message part at a time.
14770
- - Use \`partIndex\` with a cursor to fetch a single part at full detail — for example, to read one specific tool result or code block without loading every part.
14771
-
14772
- If the result says \`truncated: true\`, the output was cut to fit the token budget. You can paginate or use \`partIndex\` to target specific content. If a single part is itself too large, follow the returned \`nextCharOffset\` as described below.
14773
-
14774
- ### Following up on truncated parts
14775
- Low-detail results may include truncation hints like:
14776
- \`[truncated — call recall cursor="..." partIndex=N detail="high" for full content]\`
14777
-
14778
- **When you see these hints and need the full content, make the exact call described in the hint.** This is the normal workflow: first recall at low detail to scan, then drill into specific parts at high detail. Do not stop at the low-detail result if the user asked for exact content.
14779
-
14780
- If a single part is larger than the token budget, the \`partIndex\` result is \`truncated: true\` and includes \`nextCharOffset\`. Repeat the same call with \`charOffset\` set to that exact value to read the next chunk from where the previous one ended. Keep following \`nextCharOffset\` until the result no longer includes it — the chunks together contain the full part. Retrying without \`charOffset\` returns the same prefix again.
14781
-
14782
- ### When recall is NOT needed
14783
- - The user is asking for a high-level summary and your observations already cover it
14784
- - The question is about general preferences or facts that don't require source text
14785
- ${isResource ? `- No relevant observation range exists AND ${searchEnabled ? "`search`/`threads`" : "`threads`"} turned up nothing — but remember that raw history may exist for threads that have no observations yet, so check before concluding the information is unavailable` : `- There is no relevant range in your observations for the topic`}
14786
-
14787
- Observation groups with range IDs and your recall tool allows you to think back and remember details you're fuzzy on.`;
14788
- const custom = customInstructions?.trim();
14789
- if (!custom) return base;
14790
- return `${base}
14791
-
14792
- ### Additional recall guidance
14793
- ${custom}`;
14794
- }
14795
- //#endregion
14796
14640
  //#region src/processors/observational-memory/extractor.ts
14797
14641
  const BUILT_IN_SLUGS = /* @__PURE__ */ new Set([
14798
14642
  "current-task",
@@ -14867,24 +14711,29 @@ var Extractor = class Extractor {
14867
14711
  const name = config.name.trim();
14868
14712
  const instructions = typeof config.instructions === "string" ? config.instructions.trim() : void 0;
14869
14713
  const slug = slugifyExtractorName(name);
14714
+ const isHook = config.mode === "hook";
14870
14715
  if (!name) throw new Error("Extractor name is required.");
14716
+ if (!isHook && !config.instructions) throw new Error(`Extractor "${name}" must include instructions.`);
14871
14717
  if (instructions !== void 0 && !instructions) throw new Error(`Extractor "${name}" must include instructions.`);
14718
+ if (isHook && !config.onExtracted) throw new Error(`Hook extractor "${name}" must include an onExtracted handler.`);
14719
+ if (isHook && (config.instructions || config.schema)) throw new Error(`Hook extractor "${name}" cannot include instructions or a schema.`);
14872
14720
  assertValidSlug(slug, name);
14873
14721
  if (!internal && RESERVED_XML_TAGS.has(slug)) throw new Error(`Extractor slug "${slug}" is reserved by Observational Memory.`);
14874
14722
  this.name = name;
14875
14723
  this.slug = slug;
14876
- this.instructionsConfig = config.instructions;
14724
+ this.instructionsConfig = config.instructions ?? "";
14877
14725
  this.schemaConfig = config.schema;
14878
14726
  this.instructions = instructions ?? "";
14879
- this.schema = typeof config.schema === "function" ? zod.z.string() : config.schema ?? zod.z.string();
14880
- this.mode = internal || !config.schema ? "inline" : "structured";
14881
- this.includePreviousExtraction = config.includePreviousExtraction ?? true;
14882
- this.metadataKeyPath = config.metadataKeyPath ?? `extracted.${slug}`;
14727
+ this.schema = isHook ? zod.z.unknown() : typeof config.schema === "function" ? zod.z.string() : config.schema ?? zod.z.string();
14728
+ this.mode = isHook ? "hook" : internal || !config.schema ? "inline" : "structured";
14729
+ this.includePreviousExtraction = isHook ? false : config.includePreviousExtraction ?? true;
14730
+ this.metadataKeyPath = isHook ? false : config.metadataKeyPath ?? `extracted.${slug}`;
14883
14731
  this.onExtracted = config.onExtracted;
14884
14732
  this.retryStructuredExtractionOnEmptyObject = config.retryStructuredExtractionOnEmptyObject ?? false;
14885
14733
  this.internal = internal;
14886
14734
  }
14887
14735
  async resolve(context) {
14736
+ if (this.mode === "hook") return this;
14888
14737
  const instructions = typeof this.instructionsConfig === "function" ? (await this.instructionsConfig(context)).trim() : this.instructionsConfig.trim();
14889
14738
  if (!instructions) throw new Error(`Extractor "${this.name}" must include instructions.`);
14890
14739
  const schema = typeof this.schemaConfig === "function" ? await this.schemaConfig(context) : this.schemaConfig;
@@ -15021,81 +14870,2416 @@ function buildExtractorPriorLines(extractors, priorExtractedValues) {
15021
14870
  return lines;
15022
14871
  }
15023
14872
  //#endregion
15024
- //#region src/processors/observational-memory/extracted-values.ts
15025
- const BUILT_IN_METADATA_FIELDS = {
15026
- "current-task": {
15027
- metadataField: "currentTask",
15028
- builtInField: "currentTask"
15029
- },
15030
- "suggested-response": {
15031
- metadataField: "suggestedResponse",
15032
- builtInField: "suggestedContinuation"
15033
- },
15034
- "thread-title": {
15035
- metadataField: "threadTitle",
15036
- builtInField: "threadTitle"
14873
+ //#region src/processors/observational-memory/subconscious/activity.ts
14874
+ const SUBCONSCIOUS_ACTIVITY_STATE_ID = "subconscious-activity";
14875
+ async function getActivityTarget(store, event, scope) {
14876
+ if (event.recordType === "node") {
14877
+ const node = await store.getNode(event.recordId);
14878
+ if (!node || !(0, _mastra_core_storage.isKnowledgeScopeVisible)(node.scope, scope)) return {
14879
+ id: event.recordId,
14880
+ type: "node"
14881
+ };
14882
+ return {
14883
+ id: node.id,
14884
+ name: node.name,
14885
+ type: "node"
14886
+ };
15037
14887
  }
15038
- };
15039
- function isPresentExtractedValue(value) {
15040
- return value !== void 0 && value !== null && value !== "";
14888
+ const record = await store.getKnowledge({
14889
+ id: event.recordId,
14890
+ includeDeleted: true
14891
+ });
14892
+ const node = record ? await store.getNode(record.node) : void 0;
14893
+ if (!node || !(0, _mastra_core_storage.isKnowledgeScopeVisible)(node.scope, scope)) return {
14894
+ id: event.recordId,
14895
+ type: "node"
14896
+ };
14897
+ return {
14898
+ id: node.id,
14899
+ name: node.name,
14900
+ type: "node"
14901
+ };
15041
14902
  }
15042
- function normalizeExtractedValues(values) {
15043
- if (!values) return;
15044
- const normalized = Object.fromEntries(Object.entries(values).filter(([, value]) => isPresentExtractedValue(value)));
15045
- return Object.keys(normalized).length > 0 ? normalized : void 0;
14903
+ async function buildSubconsciousActivitySnapshot(input) {
14904
+ const events = await input.store.listActivity({
14905
+ scope: input.scope,
14906
+ limit: input.recentUpdates
14907
+ });
14908
+ const resolvedUpdates = await Promise.all(events.map(async (event) => {
14909
+ const target = await getActivityTarget(input.store, event, input.scope);
14910
+ return {
14911
+ action: event.action,
14912
+ type: event.recordType,
14913
+ name: target.name,
14914
+ targetId: target.id,
14915
+ targetType: target.type,
14916
+ createdAt: event.createdAt.toISOString()
14917
+ };
14918
+ }));
14919
+ const hotByRecord = /* @__PURE__ */ new Map();
14920
+ for (const update of resolvedUpdates) {
14921
+ if (!update.name) continue;
14922
+ const key = `${update.targetType}:${update.targetId}`;
14923
+ const existing = hotByRecord.get(key);
14924
+ if (existing) existing.updates += 1;
14925
+ else hotByRecord.set(key, {
14926
+ type: update.targetType,
14927
+ name: update.name,
14928
+ updates: 1
14929
+ });
14930
+ }
14931
+ const hot = [...hotByRecord.values()].sort((a, b) => b.updates - a.updates || a.name.localeCompare(b.name)).slice(0, input.recentUpdates);
14932
+ const updates = resolvedUpdates.map((update) => ({
14933
+ action: update.action,
14934
+ type: update.type,
14935
+ ...update.name ? { name: update.name } : {},
14936
+ createdAt: update.createdAt
14937
+ }));
14938
+ const errors = input.errors?.filter(Boolean).slice(0, input.recentUpdates);
14939
+ return {
14940
+ updates,
14941
+ hot,
14942
+ ...errors?.length ? { errors } : {}
14943
+ };
15046
14944
  }
15047
- function mergeExtractedValues(...valueSets) {
15048
- const merged = {};
15049
- for (const values of valueSets) {
15050
- const normalized = normalizeExtractedValues(values);
15051
- if (normalized) Object.assign(merged, normalized);
15052
- }
15053
- return Object.keys(merged).length > 0 ? merged : void 0;
14945
+ function renderSubconsciousActivity(snapshot) {
14946
+ const lines = snapshot.updates.map((update) => {
14947
+ const target = update.name ? `${update.type} [[${update.name}]]` : update.type;
14948
+ return `- ${update.action}: ${target}`;
14949
+ });
14950
+ const hot = snapshot.hot.map((record) => `[[${record.name}]] (${record.updates})`).join(", ");
14951
+ return [
14952
+ hot ? `Hot: ${hot}` : "Hot: none",
14953
+ "Recent updates:",
14954
+ ...lines.length ? lines : ["- none"],
14955
+ ...snapshot.errors?.length ? ["Errors:", ...snapshot.errors.map((error) => `- ${error}`)] : []
14956
+ ].join("\n");
14957
+ }
14958
+ async function publishSubconsciousError(input) {
14959
+ await input.writer?.custom({
14960
+ type: "data-subconscious-error",
14961
+ data: {
14962
+ error: input.error,
14963
+ agent: input.agent
14964
+ }
14965
+ });
14966
+ if (!input.sendStateSignal) return;
14967
+ const snapshot = {
14968
+ updates: [],
14969
+ hot: [],
14970
+ errors: [input.error]
14971
+ };
14972
+ const contents = renderSubconsciousActivity(snapshot);
14973
+ await input.sendStateSignal({
14974
+ id: SUBCONSCIOUS_ACTIVITY_STATE_ID,
14975
+ mode: "snapshot",
14976
+ cacheKey: (0, crypto$1.createHash)("sha256").update(contents).digest("hex"),
14977
+ tagName: "state",
14978
+ attributes: { id: SUBCONSCIOUS_ACTIVITY_STATE_ID },
14979
+ metadata: { origin: "subconscious" },
14980
+ contents,
14981
+ value: snapshot
14982
+ });
15054
14983
  }
15055
- function mergeExtractionFailures(...failureSets) {
15056
- const failures = failureSets.flatMap((set) => set ?? []);
15057
- return failures.length > 0 ? failures : void 0;
14984
+ async function publishSubconsciousActivity(input) {
14985
+ if (!input.sendStateSignal) return void 0;
14986
+ const snapshot = await buildSubconsciousActivitySnapshot(input);
14987
+ const contents = renderSubconsciousActivity(snapshot);
14988
+ const cacheKey = (0, crypto$1.createHash)("sha256").update(contents).digest("hex");
14989
+ await input.sendStateSignal({
14990
+ id: SUBCONSCIOUS_ACTIVITY_STATE_ID,
14991
+ mode: "snapshot",
14992
+ cacheKey,
14993
+ tagName: "state",
14994
+ attributes: { id: SUBCONSCIOUS_ACTIVITY_STATE_ID },
14995
+ metadata: { origin: "subconscious" },
14996
+ contents,
14997
+ value: snapshot
14998
+ });
14999
+ return snapshot;
15058
15000
  }
15059
- const UNSAFE_METADATA_PATH_SEGMENTS = /* @__PURE__ */ new Set([
15060
- "__proto__",
15061
- "constructor",
15062
- "prototype"
15063
- ]);
15064
- function getMetadataPathSegments(keyPath) {
15065
- if (keyPath === false) return;
15066
- const segments = keyPath.split(".").filter(Boolean);
15067
- if (segments.some((segment) => UNSAFE_METADATA_PATH_SEGMENTS.has(segment))) throw new Error(`Extractor metadataKeyPath "${keyPath}" contains an unsafe path segment.`);
15068
- return segments.length > 0 ? segments : void 0;
15001
+ //#endregion
15002
+ //#region src/processors/observational-memory/subconscious/pinned.ts
15003
+ /** Processor id and state-signal id for the pinned-knowledge lane. */
15004
+ const SUBCONSCIOUS_PINS_STATE_ID = "subconscious-pins";
15005
+ /** Snapshot tag the model sees; the delta tag appends `-update`. */
15006
+ const PINNED_SNAPSHOT_TAG = "pinned-knowledge";
15007
+ const PINNED_DELTA_TAG = "pinned-knowledge-update";
15008
+ /** Reserved node holding the pin set. One record, at one fixed scope level. */
15009
+ const PINNED_NODE_NAME = "pinned";
15010
+ const PINNED_NODE_KIND = "system";
15011
+ const PINNED_NODE_SCOPE_LEVEL = "resource";
15012
+ const DEFAULT_PINNED_MAX_CHARACTERS = 2e3;
15013
+ const MAX_PINNED_MAX_CHARACTERS = 8e3;
15014
+ const PIN_IDENTITY = "subconscious:pin";
15015
+ function pinnedNodeScope(scope, maxScope) {
15016
+ return (0, _mastra_core_storage.expandKnowledgeScope)(scope, maxScope === "thread" ? "thread" : PINNED_NODE_SCOPE_LEVEL);
15017
+ }
15018
+ async function resolvePinnedNodeId(store, scope) {
15019
+ return (await store.resolveNode({
15020
+ name: PINNED_NODE_NAME,
15021
+ scope
15022
+ }))?.id;
15023
+ }
15024
+ /** Reuse the node wherever it is visible; otherwise create it. `createNode` is an idempotent upsert on (name, scope). */
15025
+ async function ensurePinnedNodeId(store, scope, maxScope) {
15026
+ const existing = await resolvePinnedNodeId(store, scope);
15027
+ if (existing) return existing;
15028
+ return (await store.createNode({
15029
+ name: PINNED_NODE_NAME,
15030
+ kind: PINNED_NODE_KIND,
15031
+ scope: pinnedNodeScope(scope, maxScope)
15032
+ })).id;
15069
15033
  }
15070
- function getValueAtPath(metadata, keyPath) {
15071
- if (!metadata) return;
15072
- const segments = getMetadataPathSegments(keyPath);
15073
- if (!segments) return;
15074
- let current = metadata;
15075
- for (const segment of segments) {
15076
- if (!current || typeof current !== "object" || Array.isArray(current)) return;
15077
- current = current[segment];
15078
- }
15079
- return current;
15034
+ /**
15035
+ * Assembles the current pin set.
15036
+ *
15037
+ * Reads use the FULL visible scope context, never a level-narrowed write scope: visibility is
15038
+ * subset containment, so querying at the node's level would drop pins written at narrower
15039
+ * levels. Deleted records are excluded explicitly.
15040
+ */
15041
+ async function listPinnedKnowledge(input) {
15042
+ const nodeId = await resolvePinnedNodeId(input.store, input.scope);
15043
+ if (!nodeId) return { pins: [] };
15044
+ const pins = [];
15045
+ let after;
15046
+ do {
15047
+ const page = await input.store.listKnowledgeAbout({
15048
+ node: nodeId,
15049
+ scope: input.scope,
15050
+ after,
15051
+ includeDeleted: false
15052
+ });
15053
+ pins.push(...page.records);
15054
+ after = page.nextCursor;
15055
+ } while (after);
15056
+ return {
15057
+ nodeId,
15058
+ pins
15059
+ };
15080
15060
  }
15081
- function setValueAtPath(metadata, keyPath, value) {
15082
- if (!isPresentExtractedValue(value)) return;
15083
- const segments = getMetadataPathSegments(keyPath);
15084
- if (!segments) return;
15085
- let current = metadata;
15086
- for (const segment of segments.slice(0, -1)) {
15087
- const next = current[segment];
15088
- if (!next || typeof next !== "object" || Array.isArray(next)) current[segment] = {};
15089
- current = current[segment];
15090
- }
15091
- current[segments[segments.length - 1]] = value;
15061
+ function totalCharacters(pins) {
15062
+ return pins.reduce((sum, pin) => sum + pin.text.length, 0);
15092
15063
  }
15093
- function readBuiltInMetadataValues(metadata) {
15094
- const values = {};
15095
- for (const slug of BUILT_IN_EXTRACTOR_SLUGS) {
15096
- const { metadataField } = BUILT_IN_METADATA_FIELDS[slug];
15097
- values[slug] = metadata[metadataField];
15098
- }
15064
+ function assertBudget(options, pins, incomingText, replacing) {
15065
+ const kept = replacing ? pins.filter((pin) => pin.id !== replacing.id) : pins;
15066
+ if (!replacing && kept.length >= options.maxPins) throw new Error(`Pin limit reached: the set holds at most ${options.maxPins}. Unpin something first.`);
15067
+ if (totalCharacters(kept) + incomingText.length > options.maxCharacters) throw new Error(`Pin budget exceeded: the pin set is limited to ${options.maxCharacters} characters in total.`);
15068
+ }
15069
+ function clampPinLevel(level) {
15070
+ return level === "org" ? "resource" : level;
15071
+ }
15072
+ function resolveWriteScope$1(options, level) {
15073
+ let effective = clampPinLevel(level ?? options.defaultScope);
15074
+ if (!level && options.maxScope === "thread") effective = "thread";
15075
+ const scope = (0, _mastra_core_storage.expandKnowledgeScope)(options.scope, effective);
15076
+ (0, _mastra_core_storage.assertKnowledgeScopeWithinCeiling)(scope, options.maxScope);
15077
+ return scope;
15078
+ }
15079
+ const scopeLevelSchema$1 = {
15080
+ type: "string",
15081
+ enum: ["resource", "thread"]
15082
+ };
15083
+ /** Shared pin write path used by the tool and capture-time pinning. */
15084
+ async function writePinnedKnowledge(store, options, text, level, metadata) {
15085
+ const { pins } = await listPinnedKnowledge({
15086
+ store,
15087
+ scope: options.scope
15088
+ });
15089
+ assertBudget(options, pins, text);
15090
+ const nodeId = await ensurePinnedNodeId(store, options.scope, options.maxScope);
15091
+ return store.appendKnowledge({
15092
+ node: nodeId,
15093
+ text,
15094
+ scope: resolveWriteScope$1(options, level),
15095
+ sourceThreadId: options.sourceThreadId,
15096
+ maxScope: options.maxScope,
15097
+ metadata,
15098
+ resolutionScope: options.scope,
15099
+ defaultScope: (0, _mastra_core_storage.expandKnowledgeScope)(options.scope, options.defaultScope)
15100
+ });
15101
+ }
15102
+ async function getStore$1(memory) {
15103
+ const store = await memory.storage.getStore("knowledge");
15104
+ if (!store) throw new Error("Pinned knowledge requires a configured knowledge storage domain.");
15105
+ return store;
15106
+ }
15107
+ async function requirePin(store, recordId, options) {
15108
+ const record = await store.getKnowledge({
15109
+ id: recordId,
15110
+ includeDeleted: false
15111
+ });
15112
+ if (!record) throw new Error(`Pin not found: ${recordId}`);
15113
+ const nodeId = await resolvePinnedNodeId(store, options.scope);
15114
+ if (!nodeId || record.node !== nodeId) throw new Error(`Record is not a pin: ${recordId}`);
15115
+ if (!(0, _mastra_core_storage.isKnowledgeScopeVisible)(record.scope, options.scope)) throw new Error("Pin is outside the visible scope.");
15116
+ return record;
15117
+ }
15118
+ /**
15119
+ * Pin lifecycle tools. Pin appends a record on the reserved node; unpin soft-deletes it
15120
+ * (auditable, restorable); edit is remove plus append because knowledge records are immutable,
15121
+ * so an edited pin carries a new record id.
15122
+ */
15123
+ function createPinnedTools(memory, options) {
15124
+ return {
15125
+ knowledge_pin: (0, _mastra_core_tools.createTool)({
15126
+ id: "knowledge_pin",
15127
+ description: "Pin knowledge that must stay in context every turn without being asked for. Pins cost context permanently; pin only what is unconditionally relevant.",
15128
+ inputSchema: {
15129
+ type: "object",
15130
+ properties: {
15131
+ text: {
15132
+ type: "string",
15133
+ minLength: 1
15134
+ },
15135
+ scope: scopeLevelSchema$1,
15136
+ reason: {
15137
+ type: "string",
15138
+ minLength: 1,
15139
+ description: "One short sentence: why this must stay in context permanently."
15140
+ }
15141
+ },
15142
+ required: ["text"],
15143
+ additionalProperties: false
15144
+ },
15145
+ execute: async (input) => {
15146
+ const value = input;
15147
+ return writePinnedKnowledge(await getStore$1(memory), options, value.text, value.scope, value.reason ? { reason: value.reason } : void 0);
15148
+ }
15149
+ }),
15150
+ knowledge_unpin: (0, _mastra_core_tools.createTool)({
15151
+ id: "knowledge_unpin",
15152
+ description: "Remove a pin. The underlying knowledge record is soft-deleted and drops out of the pinned context.",
15153
+ inputSchema: {
15154
+ type: "object",
15155
+ properties: { recordId: {
15156
+ type: "string",
15157
+ minLength: 1
15158
+ } },
15159
+ required: ["recordId"],
15160
+ additionalProperties: false
15161
+ },
15162
+ execute: async (input) => {
15163
+ const store = await getStore$1(memory);
15164
+ const record = await requirePin(store, input.recordId, options);
15165
+ return store.removeKnowledge({
15166
+ id: record.id,
15167
+ deletedBy: PIN_IDENTITY
15168
+ });
15169
+ }
15170
+ }),
15171
+ knowledge_edit_pin: (0, _mastra_core_tools.createTool)({
15172
+ id: "knowledge_edit_pin",
15173
+ description: "Replace the text of an existing pin. The replacement carries a new knowledge record id.",
15174
+ inputSchema: {
15175
+ type: "object",
15176
+ properties: {
15177
+ recordId: {
15178
+ type: "string",
15179
+ minLength: 1
15180
+ },
15181
+ text: {
15182
+ type: "string",
15183
+ minLength: 1
15184
+ },
15185
+ reason: {
15186
+ type: "string",
15187
+ minLength: 1,
15188
+ description: "One short sentence: why this must stay in context permanently."
15189
+ }
15190
+ },
15191
+ required: ["recordId", "text"],
15192
+ additionalProperties: false
15193
+ },
15194
+ execute: async (input) => {
15195
+ const value = input;
15196
+ const store = await getStore$1(memory);
15197
+ const record = await requirePin(store, value.recordId, options);
15198
+ const { pins } = await listPinnedKnowledge({
15199
+ store,
15200
+ scope: options.scope
15201
+ });
15202
+ assertBudget(options, pins, value.text, record);
15203
+ await store.removeKnowledge({
15204
+ id: record.id,
15205
+ deletedBy: PIN_IDENTITY
15206
+ });
15207
+ return store.appendKnowledge({
15208
+ node: record.node,
15209
+ text: value.text,
15210
+ scope: record.scope,
15211
+ sourceThreadId: options.sourceThreadId,
15212
+ maxScope: record.maxScope,
15213
+ metadata: value.reason ? {
15214
+ ...record.metadata,
15215
+ reason: value.reason
15216
+ } : record.metadata,
15217
+ resolutionScope: options.scope,
15218
+ defaultScope: (0, _mastra_core_storage.expandKnowledgeScope)(options.scope, options.defaultScope)
15219
+ });
15220
+ }
15221
+ })
15222
+ };
15223
+ }
15224
+ //#endregion
15225
+ //#region src/processors/observational-memory/subconscious/scope.ts
15226
+ /**
15227
+ * Resolves the resource rung of a subconscious knowledge scope.
15228
+ *
15229
+ * When the request context carries a `knowledgeResourceId` override, every
15230
+ * scope resolver anchors the resource rung on it instead of the run's
15231
+ * resourceId. This lets a host (for example, the factory) share one knowledge
15232
+ * graph across many runs of the same project while leaving thread identity and
15233
+ * observational memory untouched. Without the override, behavior is unchanged:
15234
+ * the fallback (the run's resourceId) is used as-is.
15235
+ */
15236
+ function resolveKnowledgeResourceId(requestContext, fallback) {
15237
+ const override = requestContext?.get?.("knowledgeResourceId");
15238
+ if (typeof override === "string" && override.trim()) return override;
15239
+ return fallback;
15240
+ }
15241
+ //#endregion
15242
+ //#region src/processors/observational-memory/subconscious/capture.ts
15243
+ const CAPTURE_GUIDANCE_PAGE = "capture-guidance";
15244
+ const MAX_CAPTURE_GUIDANCE_LENGTH = 4e3;
15245
+ const SCOPE_ORDER = {
15246
+ org: 0,
15247
+ resource: 1,
15248
+ thread: 2
15249
+ };
15250
+ const subconsciousCaptureSchema = zod.z.object({ nodes: zod.z.array(zod.z.object({
15251
+ name: zod.z.string().trim().min(1),
15252
+ kind: zod.z.string().trim().min(1),
15253
+ scope: zod.z.enum([
15254
+ "org",
15255
+ "resource",
15256
+ "thread"
15257
+ ]).optional(),
15258
+ records: zod.z.array(zod.z.object({
15259
+ text: zod.z.string().trim().min(1),
15260
+ scope: zod.z.enum([
15261
+ "org",
15262
+ "resource",
15263
+ "thread"
15264
+ ]).optional(),
15265
+ when: zod.z.string().trim().min(1).optional(),
15266
+ reason: zod.z.string().trim().min(1)
15267
+ }))
15268
+ })) });
15269
+ const subconsciousCapturePinningSchema = zod.z.object({ nodes: zod.z.array(zod.z.object({
15270
+ name: zod.z.string().trim().min(1),
15271
+ kind: zod.z.string().trim().min(1),
15272
+ scope: zod.z.enum([
15273
+ "org",
15274
+ "resource",
15275
+ "thread"
15276
+ ]).optional(),
15277
+ records: zod.z.array(zod.z.object({
15278
+ text: zod.z.string().trim().min(1),
15279
+ scope: zod.z.enum([
15280
+ "org",
15281
+ "resource",
15282
+ "thread"
15283
+ ]).optional(),
15284
+ when: zod.z.string().trim().min(1).optional(),
15285
+ reason: zod.z.string().trim().min(1),
15286
+ pin: zod.z.boolean().optional()
15287
+ }))
15288
+ })) });
15289
+ const CAPTURE_PINNING_INSTRUCTIONS = `Mark pin: true only for durable user preferences or hard constraints that should apply in every future session without being asked for.`;
15290
+ const CAPTURE_INSTRUCTIONS = `Extract durable, explicitly stated knowledge from the observations.
15291
+ Return nodes with short stable names, a freeform kind, and knowledge records nested under the node each record is about.
15292
+ Use common kinds such as person, task, event, project, organization, or document when they fit.
15293
+ Set node scope to the narrowest level where that identity and content should be shared. Omit it to use the configured default scope.
15294
+ Knowledge records must be grounded in the conversation, concise, and written as prose. Do not infer unstated information.
15295
+ Wrap every named node mentioned in record text in [[wikilinks]].
15296
+ Set a record scope only when the conversation establishes where it applies. Use org for organization-wide records, resource for records shared across this resource's conversations, and thread for conversation-private records.
15297
+ Omit scope when uncertain; omitted record scopes stay private to the current thread.
15298
+ Emit when only when the conversation anchors the referred time. Resolve relative dates against the current date and use ISO 8601.
15299
+ Capture what was learned through the work, not what the session was told: skip records that merely restate standing instructions, configured rules, or the text of the task or issue the session was handed. The exception is an explicit request from the user to remember something, which is always captured even when it duplicates an existing instruction.`;
15300
+ const CAPTURE_REASON_INSTRUCTIONS = `Every record requires a reason: the concrete why behind capturing it, in one short sentence - what it cost to learn or when it will matter again (and for pinned records, why it must stay in context). Never write generic filler such as "seemed relevant" or "useful context".`;
15301
+ function clampScope(level, ceiling) {
15302
+ return ceiling && SCOPE_ORDER[level] < SCOPE_ORDER[ceiling] ? ceiling : level;
15303
+ }
15304
+ function requireScopeContext(context) {
15305
+ const organizationId = context.requestContext?.get("organizationId");
15306
+ if (typeof organizationId !== "string" || !organizationId.trim()) throw new Error("Subconscious requires requestContext.organizationId to derive scoped knowledge. Set organizationId on the request context for this conversation.");
15307
+ const resourceId = resolveKnowledgeResourceId(context.requestContext, context.resourceId);
15308
+ if (!resourceId) throw new Error("Subconscious requires resourceId to derive scoped knowledge.");
15309
+ if (!context.threadId) throw new Error("Subconscious requires threadId to derive scoped knowledge.");
15310
+ return [
15311
+ `org:${organizationId}`,
15312
+ `resource:${resourceId}`,
15313
+ `thread:${context.threadId}`
15314
+ ];
15315
+ }
15316
+ async function getKnowledgeStore$1(context) {
15317
+ if (!context.memory) throw new Error("Subconscious capture requires an active Memory instance.");
15318
+ const store = await context.memory.storage.getStore("knowledge");
15319
+ if (!store) throw new Error("Subconscious requires a knowledge storage domain. Configure a storage adapter that provides stores.knowledge.");
15320
+ return store;
15321
+ }
15322
+ function parseWhen(value) {
15323
+ if (!value) return void 0;
15324
+ const when = new Date(value);
15325
+ if (Number.isNaN(when.getTime())) throw new Error(`Invalid Subconscious record time: ${value}`);
15326
+ return when;
15327
+ }
15328
+ var SubconsciousCaptureExtractor = class extends Extractor {
15329
+ constructor(options) {
15330
+ const capturePinning = options.pins !== false && options.pins !== void 0 && options.pins.capturePinning;
15331
+ const pinNotes = /* @__PURE__ */ new WeakMap();
15332
+ const defaultImplementation = async (context) => {
15333
+ const scopeContext = requireScopeContext(context);
15334
+ const store = await getKnowledgeStore$1(context);
15335
+ const droppedPins = [];
15336
+ for (const extractedNode of context.current.nodes) {
15337
+ const nodeScope = (0, _mastra_core_storage.expandKnowledgeScope)(scopeContext, clampScope(extractedNode.scope ?? options.defaultScope, options.maxScope));
15338
+ const node = await store.createNode({
15339
+ name: extractedNode.name,
15340
+ kind: extractedNode.kind,
15341
+ scope: nodeScope
15342
+ });
15343
+ for (const extractedKnowledge of extractedNode.records) {
15344
+ if (capturePinning && extractedKnowledge.pin === true && options.pins) {
15345
+ try {
15346
+ await writePinnedKnowledge(store, {
15347
+ scope: scopeContext,
15348
+ sourceThreadId: context.threadId,
15349
+ defaultScope: options.defaultScope,
15350
+ maxScope: options.maxScope,
15351
+ maxPins: options.pins.maxPins,
15352
+ maxCharacters: options.pins.maxCharacters
15353
+ }, extractedKnowledge.text, extractedKnowledge.scope, extractedKnowledge.reason ? { reason: extractedKnowledge.reason } : void 0);
15354
+ } catch (error) {
15355
+ droppedPins.push(`Capture-time pin dropped: ${error instanceof Error ? error.message : String(error)}`);
15356
+ }
15357
+ continue;
15358
+ }
15359
+ const knowledgeLevel = clampScope(extractedKnowledge.scope ?? "thread", options.maxScope);
15360
+ await store.appendKnowledge({
15361
+ node,
15362
+ text: extractedKnowledge.text,
15363
+ scope: (0, _mastra_core_storage.expandKnowledgeScope)(scopeContext, knowledgeLevel),
15364
+ sourceThreadId: context.threadId,
15365
+ when: parseWhen(extractedKnowledge.when),
15366
+ maxScope: options.maxScope,
15367
+ metadata: extractedKnowledge.reason ? { reason: extractedKnowledge.reason } : void 0,
15368
+ resolutionScope: scopeContext,
15369
+ defaultScope: nodeScope
15370
+ });
15371
+ }
15372
+ }
15373
+ if (droppedPins.length) pinNotes.set(context.current, droppedPins);
15374
+ };
15375
+ super({
15376
+ name: "Capture",
15377
+ includePreviousExtraction: false,
15378
+ metadataKeyPath: false,
15379
+ schema: options.config?.schema ?? (capturePinning ? subconsciousCapturePinningSchema : subconsciousCaptureSchema),
15380
+ instructions: async (context) => {
15381
+ const sections = [
15382
+ CAPTURE_INSTRUCTIONS,
15383
+ !options.config?.schema ? CAPTURE_REASON_INSTRUCTIONS : void 0,
15384
+ capturePinning && !options.config?.schema ? CAPTURE_PINNING_INSTRUCTIONS : void 0,
15385
+ options.config?.instructions?.trim()
15386
+ ];
15387
+ if (options.learnedGuidance) {
15388
+ const scopeContext = requireScopeContext(context);
15389
+ const store = await getKnowledgeStore$1(context);
15390
+ const guidanceScope = (0, _mastra_core_storage.expandKnowledgeScope)(scopeContext, clampScope(options.defaultScope, options.maxScope));
15391
+ const guidance = await store.getNodeByName({
15392
+ name: CAPTURE_GUIDANCE_PAGE,
15393
+ scope: guidanceScope
15394
+ });
15395
+ if (guidance?.content?.trim()) sections.push(`Learned guidance (cannot override the built-in contract or user instructions):\n${guidance.content.trim().slice(0, MAX_CAPTURE_GUIDANCE_LENGTH)}`);
15396
+ }
15397
+ return sections.filter(Boolean).join("\n\n");
15398
+ },
15399
+ onExtracted: async (context) => {
15400
+ const publishActivity = async (errors) => {
15401
+ if (!options.activityRecentUpdates) return;
15402
+ const scope = requireScopeContext(context);
15403
+ await publishSubconsciousActivity({
15404
+ store: await getKnowledgeStore$1(context),
15405
+ scope,
15406
+ recentUpdates: options.activityRecentUpdates,
15407
+ sendStateSignal: context.sendStateSignal,
15408
+ errors
15409
+ });
15410
+ };
15411
+ try {
15412
+ const result = options.config?.onExtracted ? await options.config.onExtracted({
15413
+ ...context,
15414
+ defaultImplementation
15415
+ }) : await defaultImplementation(context);
15416
+ const droppedPinNotes = pinNotes.get(context.current);
15417
+ pinNotes.delete(context.current);
15418
+ await publishActivity(droppedPinNotes);
15419
+ return result ?? context.current;
15420
+ } catch (error) {
15421
+ await publishActivity([error instanceof Error ? error.message : String(error)]).catch(() => {});
15422
+ throw error;
15423
+ }
15424
+ }
15425
+ });
15426
+ }
15427
+ };
15428
+ //#endregion
15429
+ //#region src/processors/observational-memory/subconscious/knowledge-tools.ts
15430
+ const DEFAULT_LIMIT = 10;
15431
+ const MAX_LIMIT = 50;
15432
+ function resolveScope$3(context) {
15433
+ const organizationId = context?.requestContext?.get("organizationId");
15434
+ const resourceId = resolveKnowledgeResourceId(context?.requestContext, context?.agent?.resourceId);
15435
+ const threadId = context?.agent?.threadId;
15436
+ if (typeof organizationId !== "string" || !organizationId.trim()) throw new Error("Knowledge tools require requestContext.organizationId.");
15437
+ if (!resourceId) throw new Error("Knowledge tools require an active resourceId.");
15438
+ if (!threadId) throw new Error("Knowledge tools require an active threadId.");
15439
+ return [
15440
+ `org:${organizationId}`,
15441
+ `resource:${resourceId}`,
15442
+ `thread:${threadId}`
15443
+ ];
15444
+ }
15445
+ async function getKnowledgeStore(memory) {
15446
+ const store = await memory.storage.getStore("knowledge");
15447
+ if (!store) throw new Error("Knowledge tools require a configured knowledge storage domain.");
15448
+ return store;
15449
+ }
15450
+ function normalizeLimit(limit) {
15451
+ return Math.min(Math.max(limit ?? DEFAULT_LIMIT, 1), MAX_LIMIT);
15452
+ }
15453
+ function serializeRecord(record) {
15454
+ return {
15455
+ id: record.id,
15456
+ text: record.text,
15457
+ scope: record.scope,
15458
+ sourceThreadId: record.sourceThreadId,
15459
+ capturedAt: record.capturedAt.toISOString(),
15460
+ when: record.when?.toISOString()
15461
+ };
15462
+ }
15463
+ function serializeNode(node) {
15464
+ return {
15465
+ id: node.id,
15466
+ type: node.type,
15467
+ name: node.name,
15468
+ kind: node.kind,
15469
+ content: node.content,
15470
+ scope: node.scope,
15471
+ version: node.version,
15472
+ updatedAt: node.updatedAt.toISOString()
15473
+ };
15474
+ }
15475
+ async function loadSemanticResult(store, scope, candidate) {
15476
+ const type = candidate.metadata?.document_type;
15477
+ if (type === "node") {
15478
+ const node = await store.getNode(candidate.id.slice(15));
15479
+ if (!node || node.mergedInto || !(0, _mastra_core_storage.isKnowledgeScopeVisible)(node.scope, scope)) return null;
15480
+ return {
15481
+ type: "node",
15482
+ id: node.id,
15483
+ recordId: node.id,
15484
+ name: node.name,
15485
+ text: `${node.name}\n${node.content ?? ""}`,
15486
+ scope: node.scope,
15487
+ semanticScore: candidate.score
15488
+ };
15489
+ }
15490
+ if (type === "record") {
15491
+ const record = await store.getKnowledge({ id: candidate.id.slice(17) });
15492
+ if (!record || !(0, _mastra_core_storage.isKnowledgeScopeVisible)(record.scope, scope)) return null;
15493
+ const node = await store.getNode(record.node);
15494
+ const parentVisible = Boolean(node && !node.mergedInto && (0, _mastra_core_storage.isKnowledgeScopeVisible)(node.scope, scope));
15495
+ return {
15496
+ type: "record",
15497
+ id: record.id,
15498
+ recordId: parentVisible ? node.id : record.id,
15499
+ name: parentVisible ? node.name : "(private node)",
15500
+ text: record.text,
15501
+ scope: record.scope,
15502
+ semanticScore: candidate.score
15503
+ };
15504
+ }
15505
+ return null;
15506
+ }
15507
+ function mergeHybridResults(lexical, semantic, limit) {
15508
+ const ranked = /* @__PURE__ */ new Map();
15509
+ lexical.forEach((result, index) => {
15510
+ ranked.set(`${result.type}:${result.id}`, {
15511
+ ...result,
15512
+ score: 1 / (60 + index + 1),
15513
+ sources: ["lexical"]
15514
+ });
15515
+ });
15516
+ semantic.forEach((result, index) => {
15517
+ const key = `${result.type}:${result.id}`;
15518
+ const existing = ranked.get(key);
15519
+ const reciprocalRank = 1 / (60 + index + 1);
15520
+ ranked.set(key, {
15521
+ ...result,
15522
+ score: (existing?.score ?? 0) + reciprocalRank,
15523
+ sources: existing ? ["lexical", "semantic"] : ["semantic"],
15524
+ semanticScore: result.semanticScore
15525
+ });
15526
+ });
15527
+ return [...ranked.values()].sort((left, right) => right.score - left.score || left.id.localeCompare(right.id)).slice(0, limit);
15528
+ }
15529
+ function createKnowledgeTools(memory, fixedScope) {
15530
+ return {
15531
+ knowledge_search: (0, _mastra_core_tools.createTool)({
15532
+ id: "knowledge_search",
15533
+ description: "Search durable scoped knowledge across nodes and knowledge records using lexical and semantic retrieval.",
15534
+ inputSchema: {
15535
+ type: "object",
15536
+ properties: {
15537
+ query: {
15538
+ type: "string",
15539
+ minLength: 1,
15540
+ description: "The knowledge to search for."
15541
+ },
15542
+ limit: {
15543
+ type: "integer",
15544
+ minimum: 1,
15545
+ maximum: MAX_LIMIT,
15546
+ description: "Maximum results. Defaults to 10."
15547
+ }
15548
+ },
15549
+ required: ["query"],
15550
+ additionalProperties: false
15551
+ },
15552
+ execute: async (input, context) => {
15553
+ const { query, limit: requestedLimit } = input;
15554
+ const scope = fixedScope ?? resolveScope$3(context);
15555
+ const limit = normalizeLimit(requestedLimit);
15556
+ const store = await getKnowledgeStore(memory);
15557
+ const semanticCandidates = await memory.getKnowledgeSemanticIndex().then((index) => index.search(query, scope, limit * 2));
15558
+ return {
15559
+ query,
15560
+ results: mergeHybridResults(await store.search({
15561
+ query,
15562
+ scope,
15563
+ limit: limit * 2
15564
+ }), (await Promise.all(semanticCandidates.map((candidate) => loadSemanticResult(store, scope, candidate)))).filter((result) => Boolean(result)), limit)
15565
+ };
15566
+ }
15567
+ }),
15568
+ knowledge_read: (0, _mastra_core_tools.createTool)({
15569
+ id: "knowledge_read",
15570
+ description: "Read a knowledge node, its content, and knowledge records about or linked to it by name or ID.",
15571
+ inputSchema: {
15572
+ type: "object",
15573
+ properties: {
15574
+ id: {
15575
+ type: "string",
15576
+ minLength: 1
15577
+ },
15578
+ name: {
15579
+ type: "string",
15580
+ minLength: 1
15581
+ },
15582
+ relationship: {
15583
+ type: "string",
15584
+ enum: [
15585
+ "about",
15586
+ "mentioning",
15587
+ "related"
15588
+ ],
15589
+ description: "Knowledge relationship to query. Defaults to about."
15590
+ },
15591
+ cursor: {
15592
+ type: "string",
15593
+ minLength: 1,
15594
+ description: "Return knowledge records after this record ULID."
15595
+ },
15596
+ limit: {
15597
+ type: "integer",
15598
+ minimum: 1,
15599
+ maximum: MAX_LIMIT
15600
+ }
15601
+ },
15602
+ additionalProperties: false
15603
+ },
15604
+ execute: async (input, context) => {
15605
+ const { id, name, relationship = "about", cursor, limit: requestedLimit } = input;
15606
+ if (!id && !name) throw new Error("knowledge_read requires id or name.");
15607
+ const scope = fixedScope ?? resolveScope$3(context);
15608
+ const store = await getKnowledgeStore(memory);
15609
+ const node = id ? await store.getNode(id) : await store.resolveNode({
15610
+ name,
15611
+ scope
15612
+ });
15613
+ if (!node || node.mergedInto || !(0, _mastra_core_storage.isKnowledgeScopeVisible)(node.scope, scope)) return { found: false };
15614
+ const result = await (relationship === "related" ? store.listKnowledgeRelatedTo : relationship === "mentioning" ? store.listKnowledgeMentioning : store.listKnowledgeAbout).call(store, {
15615
+ node,
15616
+ scope,
15617
+ after: cursor,
15618
+ limit: normalizeLimit(requestedLimit)
15619
+ });
15620
+ return {
15621
+ found: true,
15622
+ node: serializeNode(node),
15623
+ records: result.records.map(serializeRecord),
15624
+ nextCursor: result.nextCursor
15625
+ };
15626
+ }
15627
+ }),
15628
+ knowledge_browse: (0, _mastra_core_tools.createTool)({
15629
+ id: "knowledge_browse",
15630
+ description: "Browse visible knowledge nodes by scope and name prefix, or follow a node’s mentions and backlinks.",
15631
+ inputSchema: {
15632
+ type: "object",
15633
+ properties: {
15634
+ namePrefix: { type: "string" },
15635
+ kind: {
15636
+ type: "string",
15637
+ description: "Optional node kind filter."
15638
+ },
15639
+ hasContent: {
15640
+ type: "boolean",
15641
+ description: "Filter nodes by whether they have long-form content."
15642
+ },
15643
+ node: {
15644
+ type: "string",
15645
+ minLength: 1,
15646
+ description: "When set, return knowledge related to this node."
15647
+ },
15648
+ cursor: {
15649
+ type: "string",
15650
+ minLength: 1
15651
+ },
15652
+ limit: {
15653
+ type: "integer",
15654
+ minimum: 1,
15655
+ maximum: MAX_LIMIT
15656
+ }
15657
+ },
15658
+ additionalProperties: false
15659
+ },
15660
+ execute: async (input, context) => {
15661
+ const { namePrefix, kind, hasContent, node: nodeReference, cursor, limit: requestedLimit } = input;
15662
+ const scope = fixedScope ?? resolveScope$3(context);
15663
+ const limit = normalizeLimit(requestedLimit);
15664
+ const store = await getKnowledgeStore(memory);
15665
+ if (nodeReference) {
15666
+ const node = await store.getNode(nodeReference);
15667
+ if (!node || node.mergedInto || !(0, _mastra_core_storage.isKnowledgeScopeVisible)(node.scope, scope)) return { found: false };
15668
+ const result = await store.listKnowledgeRelatedTo({
15669
+ node,
15670
+ scope,
15671
+ after: cursor,
15672
+ limit
15673
+ });
15674
+ return {
15675
+ found: true,
15676
+ node: serializeNode(node),
15677
+ records: result.records.map(serializeRecord),
15678
+ nextCursor: result.nextCursor
15679
+ };
15680
+ }
15681
+ const nodes = await store.listNodes({
15682
+ scope,
15683
+ namePrefix,
15684
+ kind,
15685
+ hasContent,
15686
+ cursor,
15687
+ limit: limit + 1
15688
+ });
15689
+ const hasMore = nodes.length > limit;
15690
+ const visibleNodes = nodes.slice(0, limit);
15691
+ return {
15692
+ nodes: visibleNodes.map(serializeNode),
15693
+ nextCursor: hasMore ? (0, _mastra_core_storage.createKnowledgeNodeCursor)(visibleNodes.at(-1), {
15694
+ namePrefix,
15695
+ kind,
15696
+ hasContent
15697
+ }) : void 0
15698
+ };
15699
+ }
15700
+ })
15701
+ };
15702
+ }
15703
+ //#endregion
15704
+ //#region src/processors/observational-memory/model-by-input-tokens.ts
15705
+ function isTieredModelTarget(model) {
15706
+ if (typeof model === "string") return true;
15707
+ if (!model || typeof model !== "object") return false;
15708
+ return "modelId" in model || "id" in model || "providerId" in model || "provider" in model || "doGenerate" in model && "doStream" in model;
15709
+ }
15710
+ function normalizeThresholds(config) {
15711
+ const entries = Object.entries(config.upTo);
15712
+ if (entries.length === 0) throw new Error("ModelByInputTokens requires at least one threshold in \"upTo\"");
15713
+ for (const [limitStr, model] of entries) {
15714
+ const limit = Number(limitStr);
15715
+ if (!Number.isFinite(limit) || limit <= 0) throw new Error(`ModelByInputTokens threshold keys must be positive numbers. Got: ${limitStr}`);
15716
+ if (!isTieredModelTarget(model)) throw new Error(`ModelByInputTokens requires a valid model target for threshold ${limitStr}`);
15717
+ }
15718
+ return entries.map(([limitStr, model]) => ({
15719
+ limit: Number(limitStr),
15720
+ model
15721
+ })).sort((a, b) => a.limit - b.limit);
15722
+ }
15723
+ var ModelByInputTokens = class {
15724
+ thresholds;
15725
+ constructor(config) {
15726
+ this.thresholds = normalizeThresholds(config);
15727
+ }
15728
+ resolve(inputTokens) {
15729
+ for (const { limit, model } of this.thresholds) if (inputTokens <= limit) return model;
15730
+ const maxLimit = this.thresholds[this.thresholds.length - 1].limit;
15731
+ throw new Error(`ModelByInputTokens: input token count (${inputTokens}) exceeds the largest configured threshold (${maxLimit}). Please configure a higher threshold or use a larger model.`);
15732
+ }
15733
+ getThresholds() {
15734
+ return this.thresholds.map((t) => t.limit);
15735
+ }
15736
+ };
15737
+ //#endregion
15738
+ //#region src/processors/observational-memory/subconscious/model.ts
15739
+ /**
15740
+ * Normalize an observational memory model config into something an Agent can consume directly.
15741
+ * Returns undefined when the OM model cannot stand alone: the 'default' sentinel and
15742
+ * token-routed models (ModelByInputTokens) both require engine context to resolve.
15743
+ */
15744
+ function usableObservationalMemoryModel(model) {
15745
+ if (!model || model === "default") return void 0;
15746
+ if (model instanceof ModelByInputTokens) return void 0;
15747
+ if (Array.isArray(model)) return model[0]?.model;
15748
+ if (typeof model === "function") return (async (ctx) => {
15749
+ const result = await model(ctx);
15750
+ return Array.isArray(result) ? result[0]?.model ?? result : result;
15751
+ });
15752
+ return model;
15753
+ }
15754
+ /**
15755
+ * Resolve the model a subconscious agent runs on. Precedence: the per-agent config model,
15756
+ * then the observational memory model, then the main agent's model. Returns undefined when
15757
+ * no source is available so callers keep their existing throw/silent-return behavior.
15758
+ */
15759
+ async function resolveSubconsciousAgentModel(options) {
15760
+ const { config, omModel, mainAgent, requestContext } = options;
15761
+ if (config.model) {
15762
+ if (mainAgent) return await mainAgent.getModel({
15763
+ requestContext,
15764
+ modelConfig: config.model
15765
+ });
15766
+ return config.model;
15767
+ }
15768
+ const fromOm = usableObservationalMemoryModel(omModel);
15769
+ if (fromOm) return fromOm;
15770
+ if (mainAgent) return await mainAgent.getModel({ requestContext });
15771
+ }
15772
+ //#endregion
15773
+ //#region src/processors/observational-memory/subconscious/remind.ts
15774
+ const DEFAULT_INSTRUCTIONS$2 = `Review the current observations and use the knowledge tools to find prior knowledge that is directly relevant now.
15775
+
15776
+ Be selective. Treat future-dated records as relevant when their time is imminent or useful to the current task. When the observations show whether an earlier reminder was used, tune your selectivity accordingly without storing hit/miss counters.
15777
+ Never remind about knowledge that is already visible in the current observations or recent messages — a reminder is only valuable for knowledge the agent can no longer see. Echoing back what was just said or just captured is noise.
15778
+ If nothing is relevant, respond with exactly <no-reminder /> and nothing else.
15779
+ If knowledge is relevant, return one concise reminder that explains why it matters and includes source node or record IDs. Do not invent knowledge and do not expose knowledge outside the tools' scoped results.`;
15780
+ /** Own-thread records younger than this are treated as still-in-context and excluded from reminder candidates. */
15781
+ const FRESH_OWN_RECORD_WINDOW_MS = 1800 * 1e3;
15782
+ function resolveScope$2(context) {
15783
+ const organizationId = context.requestContext?.get("organizationId");
15784
+ if (typeof organizationId !== "string" || !organizationId.trim()) throw new Error("Subconscious remind requires organizationId in the request context.");
15785
+ const resourceId = resolveKnowledgeResourceId(context.requestContext, context.resourceId);
15786
+ if (!resourceId) throw new Error("Subconscious remind requires a resourceId.");
15787
+ return (0, _mastra_core_storage.canonicalizeKnowledgeScope)([
15788
+ `org:${organizationId}`,
15789
+ `resource:${resourceId}`,
15790
+ `thread:${context.threadId}`
15791
+ ]);
15792
+ }
15793
+ const REMINDER_QUERY_STOP_WORDS = /* @__PURE__ */ new Set([
15794
+ "about",
15795
+ "after",
15796
+ "before",
15797
+ "current",
15798
+ "from",
15799
+ "have",
15800
+ "observations",
15801
+ "that",
15802
+ "their",
15803
+ "there",
15804
+ "they",
15805
+ "this",
15806
+ "user",
15807
+ "what",
15808
+ "when",
15809
+ "where",
15810
+ "which",
15811
+ "with"
15812
+ ]);
15813
+ async function findReminderSources(store, scope, observations) {
15814
+ const terms = [...new Set(observations.match(/[A-Za-z0-9][A-Za-z0-9_-]{3,}/g)?.map((term) => term.toLowerCase()).filter((term) => !REMINDER_QUERY_STOP_WORDS.has(term)) ?? [])].slice(0, 12);
15815
+ const results = (await Promise.all(terms.map((query) => store.search({
15816
+ query,
15817
+ scope,
15818
+ limit: 5
15819
+ })))).flat();
15820
+ return [...new Map(results.map((result) => [`${result.type}:${result.id}`, result])).values()].slice(0, 10);
15821
+ }
15822
+ /**
15823
+ * Drop the current thread's own freshly captured KnowledgeRecords from the candidate list. They match the
15824
+ * current observations almost perfectly (they were just distilled from them), so without this
15825
+ * guard the reminder agent mostly echoes the session's own words back at it.
15826
+ */
15827
+ async function dropFreshOwnRecords(store, sources, threadId) {
15828
+ const checks = await Promise.all(sources.map(async (source) => {
15829
+ if (source.type !== "record") return true;
15830
+ const record = await store.getKnowledge({ id: source.id }).catch(() => null);
15831
+ if (!record) return true;
15832
+ const isOwnThread = record.sourceThreadId === threadId || record.sourceThreadId.startsWith(`subconscious:${threadId}:`);
15833
+ const isFresh = Date.now() - new Date(record.capturedAt).getTime() < FRESH_OWN_RECORD_WINDOW_MS;
15834
+ return !(isOwnThread && isFresh);
15835
+ }));
15836
+ return sources.filter((_, index) => checks[index]);
15837
+ }
15838
+ var SubconsciousRemindExtractor = class extends Extractor {
15839
+ constructor(config, omModel) {
15840
+ super({
15841
+ name: "Remind",
15842
+ mode: "hook",
15843
+ metadataKeyPath: false,
15844
+ onExtracted: async (context) => {
15845
+ if (!context.rawObservations?.trim() || !context.memory || !context.sendSignal) return;
15846
+ let scope;
15847
+ let store;
15848
+ try {
15849
+ scope = resolveScope$2(context);
15850
+ store = await context.memory.storage.getStore("knowledge");
15851
+ if (!store) throw new Error("Subconscious remind requires a configured knowledge storage domain.");
15852
+ const sources = await dropFreshOwnRecords(store, await findReminderSources(store, scope, context.rawObservations), context.threadId);
15853
+ if (sources.length === 0) return;
15854
+ const model = await resolveSubconsciousAgentModel({
15855
+ config,
15856
+ omModel,
15857
+ mainAgent: context.mainAgent,
15858
+ requestContext: context.requestContext
15859
+ });
15860
+ if (!model) return;
15861
+ const agent = new _mastra_core_agent.Agent({
15862
+ id: `subconscious-remind-${context.threadId}`,
15863
+ name: "Subconscious Remind",
15864
+ instructions: [DEFAULT_INSTRUCTIONS$2, config.instructions?.trim()].filter(Boolean).join("\n\n"),
15865
+ model,
15866
+ tools: createKnowledgeTools(context.memory, scope)
15867
+ });
15868
+ const recentMessagesSection = context.recentMessages?.trim() ? `\n\nRecent conversation messages (already visible to the agent — never remind about anything present here):\n${context.recentMessages}` : "";
15869
+ const reminder = (await agent.generate(`Current time: ${(/* @__PURE__ */ new Date()).toISOString()}\n\nScoped source candidates:\n${JSON.stringify(sources)}\n\nCurrent observations:\n${context.rawObservations}${recentMessagesSection}`, {
15870
+ requestContext: context.requestContext,
15871
+ abortSignal: context.abortSignal,
15872
+ maxSteps: config.maxSteps
15873
+ })).text.trim();
15874
+ if (!reminder || /^<no-reminder\s*\/>$/i.test(reminder)) return;
15875
+ const sourceIds = [...new Set(sources.flatMap((source) => [source.id, source.recordId]))].filter((id) => reminder.includes(id)).slice(0, 5);
15876
+ if (sourceIds.length === 0) return;
15877
+ const contents = `${reminder}\n\nSources: ${sourceIds.join(", ")}`;
15878
+ await context.sendSignal({
15879
+ id: `__subconscious_remembered_${crypto.randomUUID()}`,
15880
+ type: "reactive",
15881
+ tagName: "remembered",
15882
+ contents,
15883
+ createdAt: /* @__PURE__ */ new Date(),
15884
+ metadata: { origin: "subconscious" },
15885
+ attributes: {
15886
+ source: "subconscious",
15887
+ sourceIds: sourceIds.join(","),
15888
+ agent: "remind",
15889
+ threadId: context.threadId
15890
+ }
15891
+ });
15892
+ } catch (error) {
15893
+ await context.writer?.custom({
15894
+ type: "data-subconscious-error",
15895
+ data: {
15896
+ agent: "remind",
15897
+ error: error instanceof Error ? error.message : String(error)
15898
+ }
15899
+ });
15900
+ if (store && scope) await publishSubconsciousActivity({
15901
+ store,
15902
+ scope,
15903
+ recentUpdates: 10,
15904
+ sendStateSignal: context.sendStateSignal,
15905
+ errors: [`remind: ${error instanceof Error ? error.message : String(error)}`]
15906
+ });
15907
+ throw error;
15908
+ }
15909
+ }
15910
+ });
15911
+ }
15912
+ };
15913
+ //#endregion
15914
+ //#region src/processors/observational-memory/subconscious/pinned-state-processor.ts
15915
+ function lp(value) {
15916
+ return `${value.length}:${value}`;
15917
+ }
15918
+ function pinFingerprint(pin) {
15919
+ return `${lp(pin.id)}${lp(pin.text)}`;
15920
+ }
15921
+ function stablePinsCacheKey(pins) {
15922
+ return `pins:${pins.map(pinFingerprint).join("|")}`;
15923
+ }
15924
+ function getPinsFromSnapshot(snapshot) {
15925
+ const value = (snapshot?.metadata)?.value?.pins;
15926
+ return Array.isArray(value) ? value : [];
15927
+ }
15928
+ function getOpsFromDelta(delta) {
15929
+ const ops = delta.metadata?.delta?.ops;
15930
+ return Array.isArray(ops) ? ops : [];
15931
+ }
15932
+ function applyPinOps(pins, ops) {
15933
+ const next = pins.slice();
15934
+ for (const op of ops) {
15935
+ if (op.op === "remove") {
15936
+ const idx = next.findIndex((pin) => pin.id === op.id);
15937
+ if (idx >= 0) next.splice(idx, 1);
15938
+ continue;
15939
+ }
15940
+ const idx = next.findIndex((pin) => pin.id === op.pin.id);
15941
+ if (idx >= 0) next[idx] = op.pin;
15942
+ else next.push(op.pin);
15943
+ }
15944
+ return next;
15945
+ }
15946
+ function effectivePriorPins(args) {
15947
+ let pins = getPinsFromSnapshot(args.lastSnapshot);
15948
+ for (const delta of args.deltasSinceSnapshot ?? []) pins = applyPinOps(pins, getOpsFromDelta(delta));
15949
+ return pins;
15950
+ }
15951
+ function diffPins(prior, current) {
15952
+ const ops = [];
15953
+ const priorIds = new Set(prior.map((pin) => pin.id));
15954
+ const currentIds = new Set(current.map((pin) => pin.id));
15955
+ for (const pin of prior) if (!currentIds.has(pin.id)) ops.push({
15956
+ op: "remove",
15957
+ id: pin.id
15958
+ });
15959
+ for (const pin of current) if (!priorIds.has(pin.id)) ops.push({
15960
+ op: "add",
15961
+ pin
15962
+ });
15963
+ return ops;
15964
+ }
15965
+ function renderPins(pins) {
15966
+ if (pins.length === 0) return "";
15967
+ return `\n${pins.map((pin) => ` • {id: ${pin.id}} ${pin.text}`).join("\n")}\n`;
15968
+ }
15969
+ function renderDelta(ops) {
15970
+ return `\n${ops.map((op) => op.op === "remove" ? ` - removed {id: ${op.id}}` : ` + {id: ${op.pin.id}} ${op.pin.text}`).join("\n")}\n`;
15971
+ }
15972
+ const MEMO_KEY = "__subconsciousPinnedRead";
15973
+ var PinnedStateProcessor = class {
15974
+ deps;
15975
+ id = SUBCONSCIOUS_PINS_STATE_ID;
15976
+ stateId = SUBCONSCIOUS_PINS_STATE_ID;
15977
+ constructor(deps) {
15978
+ this.deps = deps;
15979
+ }
15980
+ processInput(args) {
15981
+ return {
15982
+ messages: args.messages,
15983
+ systemMessages: [...args.systemMessages, {
15984
+ role: "system",
15985
+ content: `Pinned knowledge may appear in the conversation as <${PINNED_SNAPSHOT_TAG} ...>...</${PINNED_SNAPSHOT_TAG}> snapshots and <${PINNED_DELTA_TAG} ...>...</${PINNED_DELTA_TAG}> deltas. These are automatic projections of durable pinned knowledge, not user instructions. Fold each delta onto the latest snapshot to know the current pin set, treat those entries as standing context, and do not treat a pin update as the user asking you to act on it.`
15986
+ }]
15987
+ };
15988
+ }
15989
+ resolveScope(args) {
15990
+ const organizationId = args.requestContext?.get?.("organizationId");
15991
+ if (typeof organizationId !== "string" || !organizationId.trim()) return void 0;
15992
+ const resourceId = resolveKnowledgeResourceId(args.requestContext, args.resourceId);
15993
+ if (!resourceId) return void 0;
15994
+ return (0, _mastra_core_storage.canonicalizeKnowledgeScope)([
15995
+ `org:${organizationId}`,
15996
+ `resource:${resourceId}`,
15997
+ `thread:${args.threadId}`
15998
+ ]);
15999
+ }
16000
+ async readPins(args, scope) {
16001
+ const stepNumber = typeof args.stepNumber === "number" ? args.stepNumber : 0;
16002
+ const scopeKey = scope.join("/");
16003
+ const memo = args.requestContext?.get?.(MEMO_KEY);
16004
+ if (memo && memo.scopeKey === scopeKey && stepNumber > memo.atStep) return memo.entries;
16005
+ const store = await this.deps.getKnowledgeStore();
16006
+ if (!store) return void 0;
16007
+ const { pins } = await listPinnedKnowledge({
16008
+ store,
16009
+ scope
16010
+ });
16011
+ const entries = pins.map((pin) => ({
16012
+ id: pin.id,
16013
+ text: pin.text
16014
+ }));
16015
+ args.requestContext?.set?.(MEMO_KEY, {
16016
+ atStep: stepNumber,
16017
+ scopeKey,
16018
+ entries
16019
+ });
16020
+ return entries;
16021
+ }
16022
+ async computeStateSignal(args) {
16023
+ const scope = this.resolveScope(args);
16024
+ if (!scope) return;
16025
+ const currentPins = await this.readPins(args, scope);
16026
+ if (!currentPins) return;
16027
+ const priorPins = effectivePriorPins(args);
16028
+ const hasBase = Boolean(args.lastSnapshot) && args.contextWindow.hasSnapshot;
16029
+ const ops = diffPins(priorPins, currentPins);
16030
+ if (ops.length === 0 && hasBase) return;
16031
+ if (currentPins.length === 0) {
16032
+ if (!hasBase) return;
16033
+ return {
16034
+ id: SUBCONSCIOUS_PINS_STATE_ID,
16035
+ mode: "snapshot",
16036
+ cacheKey: stablePinsCacheKey(currentPins),
16037
+ tagName: PINNED_SNAPSHOT_TAG,
16038
+ contents: "",
16039
+ value: { pins: currentPins },
16040
+ attributes: { count: 0 },
16041
+ metadata: { value: { pins: currentPins } }
16042
+ };
16043
+ }
16044
+ if (!hasBase) return {
16045
+ id: SUBCONSCIOUS_PINS_STATE_ID,
16046
+ mode: "snapshot",
16047
+ cacheKey: stablePinsCacheKey(currentPins),
16048
+ tagName: PINNED_SNAPSHOT_TAG,
16049
+ contents: renderPins(currentPins),
16050
+ value: { pins: currentPins },
16051
+ attributes: { count: currentPins.length },
16052
+ metadata: { value: { pins: currentPins } }
16053
+ };
16054
+ return {
16055
+ id: SUBCONSCIOUS_PINS_STATE_ID,
16056
+ mode: "delta",
16057
+ cacheKey: stablePinsCacheKey(currentPins),
16058
+ tagName: PINNED_DELTA_TAG,
16059
+ contents: renderDelta(ops),
16060
+ value: { pins: currentPins },
16061
+ delta: { ops },
16062
+ attributes: { changes: ops.length },
16063
+ metadata: {
16064
+ value: { pins: currentPins },
16065
+ delta: { ops }
16066
+ }
16067
+ };
16068
+ }
16069
+ };
16070
+ //#endregion
16071
+ //#region src/processors/observational-memory/subconscious/knowledge-write-tools.ts
16072
+ const CURATOR_IDENTITY = "subconscious:curate";
16073
+ const MAX_GUIDANCE_LENGTH = 4e3;
16074
+ const scopeLevelSchema = {
16075
+ type: "string",
16076
+ enum: [
16077
+ "org",
16078
+ "resource",
16079
+ "thread"
16080
+ ]
16081
+ };
16082
+ async function getStore(memory) {
16083
+ const store = await memory.storage.getStore("knowledge");
16084
+ if (!store) throw new Error("Knowledge write tools require a configured knowledge storage domain.");
16085
+ return store;
16086
+ }
16087
+ function resolveWriteScope(options, level) {
16088
+ const scope = (0, _mastra_core_storage.expandKnowledgeScope)(options.scope, level ?? options.defaultScope);
16089
+ (0, _mastra_core_storage.assertKnowledgeScopeWithinCeiling)(scope, options.maxScope);
16090
+ return scope;
16091
+ }
16092
+ function requireVisible(scope, options, label) {
16093
+ if (!(0, _mastra_core_storage.isKnowledgeScopeVisible)(scope, options.scope)) throw new Error(`${label} is outside the curator's visible scope.`);
16094
+ }
16095
+ function createKnowledgeWriteTools(memory, options) {
16096
+ return {
16097
+ knowledge_append: (0, _mastra_core_tools.createTool)({
16098
+ id: "knowledge_append",
16099
+ description: "Append a scoped record to an existing node. Provenance and capture time are stamped by code.",
16100
+ inputSchema: {
16101
+ type: "object",
16102
+ properties: {
16103
+ node: {
16104
+ type: "string",
16105
+ minLength: 1
16106
+ },
16107
+ text: {
16108
+ type: "string",
16109
+ minLength: 1
16110
+ },
16111
+ scope: scopeLevelSchema,
16112
+ when: { type: "string" }
16113
+ },
16114
+ required: ["node", "text"],
16115
+ additionalProperties: false
16116
+ },
16117
+ execute: async (input) => {
16118
+ const value = input;
16119
+ const store = await getStore(memory);
16120
+ const parent = await store.getNode(value.node);
16121
+ if (!parent || parent.mergedInto) throw new Error(`Knowledge node not found: ${value.node}`);
16122
+ requireVisible(parent.scope, options, "Knowledge node");
16123
+ const scope = resolveWriteScope(options, value.scope);
16124
+ const when = value.when ? new Date(value.when) : void 0;
16125
+ if (when && Number.isNaN(when.getTime())) throw new Error("KnowledgeRecord when must be a valid date.");
16126
+ return store.appendKnowledge({
16127
+ node: parent.id,
16128
+ text: value.text,
16129
+ scope,
16130
+ sourceThreadId: options.sourceThreadId,
16131
+ when,
16132
+ maxScope: options.maxScope,
16133
+ resolutionScope: options.scope,
16134
+ defaultScope: (0, _mastra_core_storage.expandKnowledgeScope)(options.scope, options.defaultScope)
16135
+ });
16136
+ }
16137
+ }),
16138
+ knowledge_remove: (0, _mastra_core_tools.createTool)({
16139
+ id: "knowledge_remove",
16140
+ description: "Soft-delete a visible record. Curators cannot restore or physically erase knowledge records.",
16141
+ inputSchema: {
16142
+ type: "object",
16143
+ properties: { recordId: {
16144
+ type: "string",
16145
+ minLength: 1
16146
+ } },
16147
+ required: ["recordId"],
16148
+ additionalProperties: false
16149
+ },
16150
+ execute: async (input) => {
16151
+ const store = await getStore(memory);
16152
+ const record = await store.getKnowledge({
16153
+ id: input.recordId,
16154
+ includeDeleted: true
16155
+ });
16156
+ if (!record) throw new Error(`KnowledgeRecord not found: ${input.recordId}`);
16157
+ requireVisible(record.scope, options, "KnowledgeRecord");
16158
+ return store.removeKnowledge({
16159
+ id: record.id,
16160
+ deletedBy: CURATOR_IDENTITY
16161
+ });
16162
+ }
16163
+ }),
16164
+ knowledge_update_node: (0, _mastra_core_tools.createTool)({
16165
+ id: "knowledge_update_node",
16166
+ description: "Update a visible node name or kind using optimistic concurrency.",
16167
+ inputSchema: {
16168
+ type: "object",
16169
+ properties: {
16170
+ node: {
16171
+ type: "string",
16172
+ minLength: 1
16173
+ },
16174
+ expectedVersion: {
16175
+ type: "integer",
16176
+ minimum: 1
16177
+ },
16178
+ name: {
16179
+ type: "string",
16180
+ minLength: 1
16181
+ },
16182
+ kind: {
16183
+ type: "string",
16184
+ minLength: 1
16185
+ }
16186
+ },
16187
+ required: ["node", "expectedVersion"],
16188
+ anyOf: [{ required: ["name"] }, { required: ["kind"] }],
16189
+ additionalProperties: false
16190
+ },
16191
+ execute: async (input) => {
16192
+ const value = input;
16193
+ const store = await getStore(memory);
16194
+ const node = await store.getNode(value.node);
16195
+ if (!node || node.mergedInto) throw new Error(`Knowledge node not found: ${value.node}`);
16196
+ requireVisible(node.scope, options, "Knowledge node");
16197
+ return store.updateNode({
16198
+ id: node.id,
16199
+ version: value.expectedVersion,
16200
+ name: value.name,
16201
+ kind: value.kind
16202
+ });
16203
+ }
16204
+ }),
16205
+ knowledge_merge_nodes: (0, _mastra_core_tools.createTool)({
16206
+ id: "knowledge_merge_nodes",
16207
+ description: "Merge a visible duplicate node into another visible node using source-version CAS.",
16208
+ inputSchema: {
16209
+ type: "object",
16210
+ properties: {
16211
+ sourceId: {
16212
+ type: "string",
16213
+ minLength: 1
16214
+ },
16215
+ targetId: {
16216
+ type: "string",
16217
+ minLength: 1
16218
+ },
16219
+ sourceVersion: {
16220
+ type: "integer",
16221
+ minimum: 1
16222
+ }
16223
+ },
16224
+ required: [
16225
+ "sourceId",
16226
+ "targetId",
16227
+ "sourceVersion"
16228
+ ],
16229
+ additionalProperties: false
16230
+ },
16231
+ execute: async (input) => {
16232
+ const value = input;
16233
+ const store = await getStore(memory);
16234
+ const [source, target] = await Promise.all([store.getNode(value.sourceId), store.getNode(value.targetId)]);
16235
+ if (!source || !target) throw new Error("Knowledge merge requires two existing nodes.");
16236
+ requireVisible(source.scope, options, "Knowledge merge source");
16237
+ requireVisible(target.scope, options, "Knowledge merge target");
16238
+ return store.mergeNodes(value);
16239
+ }
16240
+ }),
16241
+ knowledge_rescope: (0, _mastra_core_tools.createTool)({
16242
+ id: "knowledge_rescope",
16243
+ description: "Change a record visibility scope without exceeding its stamped ceiling.",
16244
+ inputSchema: {
16245
+ type: "object",
16246
+ properties: {
16247
+ recordId: {
16248
+ type: "string",
16249
+ minLength: 1
16250
+ },
16251
+ scope: scopeLevelSchema
16252
+ },
16253
+ required: ["recordId", "scope"],
16254
+ additionalProperties: false
16255
+ },
16256
+ execute: async (input) => {
16257
+ const value = input;
16258
+ const store = await getStore(memory);
16259
+ const record = await store.getKnowledge({ id: value.recordId });
16260
+ if (!record) throw new Error(`KnowledgeRecord not found: ${value.recordId}`);
16261
+ requireVisible(record.scope, options, "KnowledgeRecord");
16262
+ const scope = resolveWriteScope(options, value.scope);
16263
+ (0, _mastra_core_storage.assertKnowledgeScopeWithinCeiling)(scope, record.maxScope);
16264
+ return store.rescopeKnowledge({
16265
+ id: record.id,
16266
+ scope
16267
+ });
16268
+ }
16269
+ }),
16270
+ knowledge_write_node_content: (0, _mastra_core_tools.createTool)({
16271
+ id: "knowledge_write_node_content",
16272
+ description: "Create or replace long-form content on a scoped knowledge node. Existing nodes require expectedVersion.",
16273
+ inputSchema: {
16274
+ type: "object",
16275
+ properties: {
16276
+ name: {
16277
+ type: "string",
16278
+ minLength: 1
16279
+ },
16280
+ kind: {
16281
+ type: "string",
16282
+ minLength: 1
16283
+ },
16284
+ content: {
16285
+ type: "string",
16286
+ minLength: 1
16287
+ },
16288
+ scope: scopeLevelSchema,
16289
+ expectedVersion: {
16290
+ type: "integer",
16291
+ minimum: 1
16292
+ }
16293
+ },
16294
+ required: ["name", "content"],
16295
+ additionalProperties: false
16296
+ },
16297
+ execute: async (input) => {
16298
+ const value = input;
16299
+ const trimmedName = value.name.trim();
16300
+ const reservedName = trimmedName.toLowerCase();
16301
+ const name = reservedName === "capture-guidance" ? reservedName : trimmedName;
16302
+ if (reservedName === "capture-guidance" && value.content.length > MAX_GUIDANCE_LENGTH) throw new Error(`capture-guidance is limited to ${MAX_GUIDANCE_LENGTH} characters.`);
16303
+ const store = await getStore(memory);
16304
+ const scope = resolveWriteScope(options, value.scope);
16305
+ const resolvedNode = await store.resolveNode({
16306
+ name,
16307
+ scope
16308
+ });
16309
+ const existing = resolvedNode && (0, _mastra_core_storage.knowledgeScopeKey)(resolvedNode.scope) === (0, _mastra_core_storage.knowledgeScopeKey)(scope) ? resolvedNode : null;
16310
+ if (!existing) {
16311
+ if (value.expectedVersion !== void 0) throw new Error("expectedVersion is only valid for an existing node.");
16312
+ return store.createNode({
16313
+ name,
16314
+ kind: value.kind ?? "document",
16315
+ content: value.content,
16316
+ scope,
16317
+ resolutionScope: options.scope
16318
+ });
16319
+ }
16320
+ if (value.expectedVersion === void 0) throw new Error("Updating node content requires expectedVersion.");
16321
+ return store.updateNode({
16322
+ id: existing.id,
16323
+ version: value.expectedVersion,
16324
+ kind: value.kind,
16325
+ content: value.content,
16326
+ resolutionScope: options.scope
16327
+ });
16328
+ }
16329
+ })
16330
+ };
16331
+ }
16332
+ //#endregion
16333
+ //#region src/processors/observational-memory/subconscious/semantic-index.ts
16334
+ const DEFAULT_BATCH_SIZE = 50;
16335
+ const MAX_DRAIN_BATCHES = 100;
16336
+ var StaleKnowledgeSemanticIndexError = class extends Error {
16337
+ constructor(message, options) {
16338
+ super(message, options);
16339
+ this.name = "StaleKnowledgeSemanticIndexError";
16340
+ }
16341
+ };
16342
+ var KnowledgeSemanticIndexCoordinator = class {
16343
+ #knowledge;
16344
+ #vector;
16345
+ #embedder;
16346
+ #embedderOptions;
16347
+ #workerId;
16348
+ #batchSize;
16349
+ #draining = /* @__PURE__ */ new Map();
16350
+ constructor(config) {
16351
+ this.#knowledge = config.knowledge;
16352
+ this.#vector = config.vector;
16353
+ this.#embedder = config.embedder;
16354
+ this.#embedderOptions = config.embedderOptions;
16355
+ this.#workerId = config.workerId ?? `knowledge-index-${crypto.randomUUID()}`;
16356
+ this.#batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;
16357
+ }
16358
+ async drain(scope) {
16359
+ const key = scope?.join("") ?? "*";
16360
+ const active = this.#draining.get(key);
16361
+ if (active) return active;
16362
+ const draining = this.#drain(scope).finally(() => {
16363
+ this.#draining.delete(key);
16364
+ });
16365
+ this.#draining.set(key, draining);
16366
+ return draining;
16367
+ }
16368
+ async search(query, scope, limit = 10) {
16369
+ await this.drain(scope);
16370
+ const embedding = (await this.#embedder.doEmbed({
16371
+ values: [query],
16372
+ ...this.#embedderOptions ?? {}
16373
+ })).embeddings[0];
16374
+ if (!embedding?.length) throw new Error("Embedder returned no vector for knowledge search query.");
16375
+ const indexName = this.#indexName(embedding.length);
16376
+ if (!(await this.#knowledgeIndexes()).includes(indexName)) throw new StaleKnowledgeSemanticIndexError(`Knowledge semantic index ${indexName} is unavailable. Capture or index knowledge before searching.`);
16377
+ const visibleScopeKeys = scope.map((_, index) => scope.slice(0, index + 1).join(""));
16378
+ const batches = await Promise.all(visibleScopeKeys.map((scopeKey) => this.#vector.query({
16379
+ indexName,
16380
+ queryVector: embedding,
16381
+ topK: limit,
16382
+ filter: { scope_key: scopeKey }
16383
+ })));
16384
+ const deduped = /* @__PURE__ */ new Map();
16385
+ for (const candidate of batches.flat()) {
16386
+ const candidateScope = candidate.metadata?.scope;
16387
+ if (!Array.isArray(candidateScope)) continue;
16388
+ let visible = false;
16389
+ try {
16390
+ visible = (0, _mastra_core_storage.isKnowledgeScopeVisible)((0, _mastra_core_storage.canonicalizeKnowledgeScope)(candidateScope.map(String)), scope);
16391
+ } catch {
16392
+ continue;
16393
+ }
16394
+ if (!visible) continue;
16395
+ const existing = deduped.get(candidate.id);
16396
+ if (!existing || candidate.score > existing.score) deduped.set(candidate.id, candidate);
16397
+ }
16398
+ return [...deduped.values()].sort((left, right) => right.score - left.score || left.id.localeCompare(right.id)).slice(0, limit);
16399
+ }
16400
+ async #drain(scope) {
16401
+ let processed = 0;
16402
+ for (let batch = 0; batch < MAX_DRAIN_BATCHES; batch++) {
16403
+ const entries = await this.#knowledge.claimSemanticOutbox({
16404
+ workerId: this.#workerId,
16405
+ limit: this.#batchSize,
16406
+ scope
16407
+ });
16408
+ if (entries.length === 0) {
16409
+ const [pending, processing] = await Promise.all([this.#knowledge.listSemanticOutbox({
16410
+ status: "pending",
16411
+ scope,
16412
+ limit: 1
16413
+ }), this.#knowledge.listSemanticOutbox({
16414
+ status: "processing",
16415
+ scope,
16416
+ limit: 1
16417
+ })]);
16418
+ if (pending.length > 0 || processing.length > 0) throw new StaleKnowledgeSemanticIndexError("Knowledge semantic index is stale: a visible operation is pending or being processed by another worker.");
16419
+ return processed;
16420
+ }
16421
+ for (let index = 0; index < entries.length; index++) {
16422
+ const entry = entries[index];
16423
+ try {
16424
+ await this.#apply(entry);
16425
+ await this.#knowledge.completeSemanticOutbox({
16426
+ ids: [entry.id],
16427
+ workerId: this.#workerId
16428
+ });
16429
+ processed++;
16430
+ } catch (error) {
16431
+ await this.#knowledge.releaseSemanticOutbox({
16432
+ ids: entries.slice(index).map((pendingEntry) => pendingEntry.id),
16433
+ workerId: this.#workerId
16434
+ });
16435
+ throw new StaleKnowledgeSemanticIndexError(`Knowledge semantic index is stale because operation ${entry.id} could not be applied.`, { cause: error });
16436
+ }
16437
+ }
16438
+ }
16439
+ throw new StaleKnowledgeSemanticIndexError(`Knowledge semantic index remained stale after ${MAX_DRAIN_BATCHES} processing batches.`);
16440
+ }
16441
+ async #apply(entry) {
16442
+ if (entry.operation === "delete") {
16443
+ await this.#deleteDocument(entry.documentId);
16444
+ return;
16445
+ }
16446
+ const document = await this.#loadDocument(entry);
16447
+ if (!document) {
16448
+ await this.#deleteDocument(entry.documentId);
16449
+ return;
16450
+ }
16451
+ const embedding = (await this.#embedder.doEmbed({
16452
+ values: [document.text],
16453
+ ...this.#embedderOptions ?? {}
16454
+ })).embeddings[0];
16455
+ if (!embedding?.length) throw new Error(`Embedder returned no vector for ${entry.documentId}`);
16456
+ const indexName = this.#indexName(embedding.length);
16457
+ const indexes = await this.#knowledgeIndexes();
16458
+ if (!indexes.includes(indexName)) await this.#vector.createIndex({
16459
+ indexName,
16460
+ dimension: embedding.length
16461
+ });
16462
+ for (const existingIndex of indexes) if (existingIndex !== indexName) await this.#vector.deleteVectors({
16463
+ indexName: existingIndex,
16464
+ ids: [entry.documentId]
16465
+ });
16466
+ await this.#vector.upsert({
16467
+ indexName,
16468
+ ids: [entry.documentId],
16469
+ vectors: [embedding],
16470
+ metadata: [this.#metadata(document)]
16471
+ });
16472
+ }
16473
+ async #loadDocument(entry) {
16474
+ if (entry.documentType === "node") {
16475
+ const node = await this.#knowledge.getNode(entry.documentId.slice(15));
16476
+ if (!node || node.mergedInto) return null;
16477
+ return {
16478
+ text: `${node.name}\n${node.content ?? ""}`,
16479
+ name: node.name,
16480
+ scope: node.scope,
16481
+ recordId: node.id,
16482
+ type: "node"
16483
+ };
16484
+ }
16485
+ const record = await this.#knowledge.getKnowledge({
16486
+ id: entry.documentId.slice(17),
16487
+ includeDeleted: true
16488
+ });
16489
+ if (!record || record.deletedAt) return null;
16490
+ const node = await this.#knowledge.getNode(record.node);
16491
+ if (!node) return null;
16492
+ return {
16493
+ text: `${node.name}\n${record.text}`,
16494
+ name: node.name,
16495
+ scope: record.scope,
16496
+ recordId: node.id,
16497
+ type: "record"
16498
+ };
16499
+ }
16500
+ async #deleteDocument(documentId) {
16501
+ for (const indexName of await this.#knowledgeIndexes()) await this.#vector.deleteVectors({
16502
+ indexName,
16503
+ ids: [documentId]
16504
+ });
16505
+ }
16506
+ async #knowledgeIndexes() {
16507
+ const prefix = `knowledge${this.#vector.indexSeparator ?? "_"}documents`;
16508
+ return (await this.#vector.listIndexes()).filter((index) => index === prefix || index.startsWith(`${prefix}${this.#vector.indexSeparator ?? "_"}dimension`));
16509
+ }
16510
+ #indexName(dimension) {
16511
+ const separator = this.#vector.indexSeparator ?? "_";
16512
+ return `knowledge${separator}documents${separator}dimension${separator}${dimension}`;
16513
+ }
16514
+ #metadata(document) {
16515
+ const metadata = {
16516
+ document_type: document.type,
16517
+ record_id: document.recordId,
16518
+ name: document.name,
16519
+ scope: [...document.scope],
16520
+ scope_key: document.scope.join(""),
16521
+ text: document.text
16522
+ };
16523
+ for (const entry of document.scope) {
16524
+ const separator = entry.indexOf(":");
16525
+ metadata[`scope_${entry.slice(0, separator)}`] = entry.slice(separator + 1);
16526
+ }
16527
+ return metadata;
16528
+ }
16529
+ };
16530
+ //#endregion
16531
+ //#region src/processors/observational-memory/subconscious/index.ts
16532
+ var subconscious_exports = /* @__PURE__ */ __exportAll({
16533
+ DEFAULT_MAX_PINS: () => 20,
16534
+ DEFAULT_PINNED_MAX_CHARACTERS: () => DEFAULT_PINNED_MAX_CHARACTERS,
16535
+ MAX_PINNED_MAX_CHARACTERS: () => MAX_PINNED_MAX_CHARACTERS,
16536
+ PINNED_DELTA_TAG: () => PINNED_DELTA_TAG,
16537
+ PINNED_NODE_KIND: () => PINNED_NODE_KIND,
16538
+ PINNED_NODE_NAME: () => PINNED_NODE_NAME,
16539
+ PINNED_NODE_SCOPE_LEVEL: () => PINNED_NODE_SCOPE_LEVEL,
16540
+ PINNED_SNAPSHOT_TAG: () => PINNED_SNAPSHOT_TAG,
16541
+ PinnedStateProcessor: () => PinnedStateProcessor,
16542
+ SUBCONSCIOUS_ACTIVITY_STATE_ID: () => SUBCONSCIOUS_ACTIVITY_STATE_ID,
16543
+ SUBCONSCIOUS_PINS_STATE_ID: () => SUBCONSCIOUS_PINS_STATE_ID,
16544
+ Subconscious: () => Subconscious
16545
+ });
16546
+ const BUILT_IN_OBSERVATION = /* @__PURE__ */ new Set(["capture", "remind"]);
16547
+ const BUILT_IN_REFLECTION = /* @__PURE__ */ new Set(["curate", "learn"]);
16548
+ const DEFAULT_MAX_STEPS = 5;
16549
+ const DEFAULT_RECENT_UPDATES = 10;
16550
+ const MAX_RECENT_UPDATES = 100;
16551
+ function entryName(entry) {
16552
+ return typeof entry === "string" ? entry : entry.name.trim();
16553
+ }
16554
+ function assertUniqueNames(entries, phase) {
16555
+ const seen = /* @__PURE__ */ new Set();
16556
+ for (const entry of entries) {
16557
+ const name = entryName(entry);
16558
+ if (!name) throw new Error(`Subconscious ${phase} agent name is required.`);
16559
+ if (seen.has(name)) throw new Error(`Duplicate Subconscious ${phase} agent: ${name}`);
16560
+ seen.add(name);
16561
+ }
16562
+ }
16563
+ function boundedSteps(entry, fallback) {
16564
+ const steps = entry?.maxSteps ?? fallback;
16565
+ if (!Number.isInteger(steps) || steps < 1 || steps > 25) throw new Error("Subconscious maxSteps must be an integer between 1 and 25.");
16566
+ return steps;
16567
+ }
16568
+ function resolveExtractor(entry) {
16569
+ const config = typeof entry === "string" ? void 0 : entry;
16570
+ const name = entryName(entry);
16571
+ return {
16572
+ name,
16573
+ instructions: config?.instructions,
16574
+ builtIn: name === "capture"
16575
+ };
16576
+ }
16577
+ function resolveAgent(entry, builtIns, globalModel, globalMaxSteps) {
16578
+ const config = typeof entry === "string" ? void 0 : entry;
16579
+ const name = entryName(entry);
16580
+ return {
16581
+ name,
16582
+ instructions: config?.instructions,
16583
+ model: config?.model ?? globalModel,
16584
+ agent: config?.agent,
16585
+ maxSteps: boundedSteps(config, globalMaxSteps),
16586
+ builtIn: builtIns.has(name)
16587
+ };
16588
+ }
16589
+ /**
16590
+ * Configures experimental autonomous knowledge extraction and reflection.
16591
+ *
16592
+ * @experimental This API may change without notice.
16593
+ */
16594
+ var Subconscious = class {
16595
+ config;
16596
+ resolved;
16597
+ constructor(config = {}) {
16598
+ const observation = config.observation ?? ["capture", "remind"];
16599
+ const reflection = config.reflection ?? ["curate", "learn"];
16600
+ assertUniqueNames(observation, "observation");
16601
+ assertUniqueNames(reflection, "reflection");
16602
+ const maxSteps = boundedSteps(config, DEFAULT_MAX_STEPS);
16603
+ for (const entry of observation) this.#validateObservationEntry(entry);
16604
+ for (const entry of reflection) this.#validateReflectionEntry(entry);
16605
+ const recentUpdates = config.activity === false ? false : config.activity?.recentUpdates ?? DEFAULT_RECENT_UPDATES;
16606
+ if (recentUpdates !== false && (!Number.isInteger(recentUpdates) || recentUpdates < 1 || recentUpdates > MAX_RECENT_UPDATES)) throw new Error(`Subconscious activity.recentUpdates must be an integer between 1 and ${MAX_RECENT_UPDATES}.`);
16607
+ const pins = config.pins === void 0 || config.pins === false ? false : {
16608
+ maxPins: (config.pins === true ? void 0 : config.pins.maxPins) ?? 20,
16609
+ maxCharacters: (config.pins === true ? void 0 : config.pins.maxCharacters) ?? 2e3,
16610
+ capturePinning: (config.pins === true ? void 0 : config.pins.capturePinning) ?? false
16611
+ };
16612
+ if (pins !== false) {
16613
+ if (!Number.isInteger(pins.maxPins) || pins.maxPins < 1) throw new Error("Subconscious pins.maxPins must be a positive integer.");
16614
+ if (!Number.isInteger(pins.maxCharacters) || pins.maxCharacters < 1 || pins.maxCharacters > 8e3) throw new Error(`Subconscious pins.maxCharacters must be an integer between 1 and ${MAX_PINNED_MAX_CHARACTERS}.`);
16615
+ }
16616
+ if (config.curationCadence !== void 0 && (!Number.isInteger(config.curationCadence) || config.curationCadence < 1)) throw new Error("Subconscious curationCadence must be a positive integer.");
16617
+ this.config = Object.freeze({
16618
+ ...config,
16619
+ observation: [...observation],
16620
+ reflection: [...reflection]
16621
+ });
16622
+ this.resolved = Object.freeze({
16623
+ observation: observation.map((entry) => entryName(entry) === "remind" ? resolveAgent(entry, BUILT_IN_OBSERVATION, config.model, maxSteps) : resolveExtractor(entry)),
16624
+ reflection: reflection.map((entry) => resolveAgent(entry, BUILT_IN_REFLECTION, config.model, maxSteps)),
16625
+ defaultScope: config.defaultScope ?? "resource",
16626
+ maxScope: config.maxScope,
16627
+ learnedGuidance: config.learnedGuidance !== false,
16628
+ tools: config.tools !== false,
16629
+ activity: recentUpdates === false ? false : { recentUpdates },
16630
+ pins,
16631
+ curationCadence: config.curationCadence
16632
+ });
16633
+ }
16634
+ createObservationExtractors(omModel) {
16635
+ const extractors = [];
16636
+ for (const entry of this.config.observation ?? []) {
16637
+ const name = entryName(entry);
16638
+ if (name === "capture") extractors.push(new SubconsciousCaptureExtractor({
16639
+ config: typeof entry === "string" ? void 0 : entry,
16640
+ defaultScope: this.resolved.defaultScope,
16641
+ maxScope: this.resolved.maxScope,
16642
+ learnedGuidance: this.resolved.learnedGuidance,
16643
+ activityRecentUpdates: this.resolved.activity === false ? void 0 : this.resolved.activity.recentUpdates,
16644
+ pins: this.resolved.pins
16645
+ }));
16646
+ else if (name === "remind") {
16647
+ const resolved = this.resolved.observation.find((agent) => agent.name === name);
16648
+ if (resolved) extractors.push(new SubconsciousRemindExtractor(resolved, omModel));
16649
+ } else if (!BUILT_IN_OBSERVATION.has(name)) {
16650
+ const custom = entry;
16651
+ extractors.push(new Extractor({
16652
+ name: custom.name,
16653
+ instructions: custom.instructions?.trim() || `Extract ${custom.name} from the current observations.`,
16654
+ schema: custom.schema,
16655
+ metadataKeyPath: false,
16656
+ includePreviousExtraction: false,
16657
+ onExtracted: custom.onExtracted
16658
+ }));
16659
+ }
16660
+ }
16661
+ return extractors;
16662
+ }
16663
+ #validateObservationEntry(entry) {
16664
+ const name = entryName(entry);
16665
+ if (typeof entry === "string") {
16666
+ if (!BUILT_IN_OBSERVATION.has(name)) throw new Error(`Unknown Subconscious observation agent: ${name}`);
16667
+ return;
16668
+ }
16669
+ if (BUILT_IN_OBSERVATION.has(name)) {
16670
+ if (name === "capture") {
16671
+ if ("model" in entry || "maxSteps" in entry) throw new Error("Subconscious capture shares the Observer model and does not accept model or maxSteps.");
16672
+ if ("schema" in entry && entry.schema && (!("onExtracted" in entry) || typeof entry.onExtracted !== "function")) throw new Error("A custom capture schema requires an onExtracted hook that handles its output.");
16673
+ }
16674
+ return;
16675
+ }
16676
+ if ("model" in entry || "maxSteps" in entry) throw new Error(`Subconscious observation extractor "${name}" shares the Observer model and does not accept model or maxSteps.`);
16677
+ if (!("schema" in entry) || !entry.schema || !("onExtracted" in entry) || typeof entry.onExtracted !== "function") throw new Error(`Custom Subconscious observation agent "${name}" requires schema and onExtracted.`);
16678
+ }
16679
+ #validateReflectionEntry(entry) {
16680
+ const name = entryName(entry);
16681
+ if (typeof entry === "string") {
16682
+ if (!BUILT_IN_REFLECTION.has(name)) throw new Error(`Unknown Subconscious reflection agent: ${name}`);
16683
+ return;
16684
+ }
16685
+ if (BUILT_IN_REFLECTION.has(name) && "agent" in entry && entry.agent) throw new Error(`Built-in Subconscious reflection agent "${name}" cannot be replaced with a custom agent.`);
16686
+ if (!BUILT_IN_REFLECTION.has(name) && !entry.instructions?.trim() && !("agent" in entry && entry.agent)) throw new Error(`Custom Subconscious reflection agent "${name}" requires instructions or agent.`);
16687
+ }
16688
+ };
16689
+ //#endregion
16690
+ //#region src/processors/observational-memory/subconscious/curate.ts
16691
+ const CURATION_AGENT = "curate";
16692
+ const DEFAULT_INSTRUCTIONS$1 = `Maintain durable scoped knowledge from the committed observation worklist.
16693
+
16694
+ Use the read tools to inspect existing nodes, knowledge records, mentions, backlinks, and long-form node content. Use the write tools to merge true duplicates, repair names and links, soft-delete superseded knowledge records, rescope knowledge records only when justified and permitted by their ceilings, and synthesize useful node content. Never restore deleted knowledge records. Never invent provenance, capture timestamps, scopes, ceilings, IDs, or versions; those are enforced by code. Resolve optimistic-concurrency conflicts by reading the latest record and retrying the intended mutation. Keep the reserved capture-guidance node concise and update it only with durable guidance that will improve future capture.
16695
+
16696
+ Process the worklist in ID order. Every time you finish processing a KnowledgeRecord, include <curation-complete through="RECORD_ID" /> in your next text response with that record's ID. The latest marker is your acknowledged cursor, so progress survives if you run out of steps mid-batch. Your final response must end with the marker for the last KnowledgeRecord you fully processed. If you cannot finish the batch, acknowledge only the last KnowledgeRecord you did finish. Do not emit a completion marker when no KnowledgeRecord was fully processed.`;
16697
+ const PINNED_INSTRUCTIONS = `Maintain the pin set with knowledge_pin, knowledge_edit_pin, and knowledge_unpin. Pinned entries are delivered to the main agent on every turn, so they cost tokens permanently and must stay short. Pin only knowledge that should apply without being asked for, such as standing instructions, durable preferences, and hard constraints. Pin only knowledge that is BOTH costly to rediscover AND not the kind of thing a future agent would think to search for; anything a reminder can surface on demand does not belong in the pin set. Unpin an entry as soon as it stops being unconditionally true.`;
16698
+ function resolveScope$1(context) {
16699
+ const organizationId = context.requestContext?.get("organizationId");
16700
+ if (typeof organizationId !== "string" || !organizationId.trim()) throw new Error("Subconscious curate requires organizationId in the request context.");
16701
+ return (0, _mastra_core_storage.canonicalizeKnowledgeScope)([
16702
+ `org:${organizationId}`,
16703
+ `resource:${resolveKnowledgeResourceId(context.requestContext, context.resourceId)}`,
16704
+ `thread:${context.parentThreadId}`
16705
+ ]);
16706
+ }
16707
+ async function readWorklist$1(store, sourceThreadId, scope, after) {
16708
+ const records = [];
16709
+ let cursor = after;
16710
+ do {
16711
+ const page = await store.knowledgeBySource({
16712
+ sourceThreadId,
16713
+ scope,
16714
+ after: cursor,
16715
+ limit: 100,
16716
+ includeDeleted: true
16717
+ });
16718
+ records.push(...page.records);
16719
+ cursor = page.nextCursor;
16720
+ } while (cursor && records.length < 500);
16721
+ return {
16722
+ records,
16723
+ hasMore: Boolean(cursor)
16724
+ };
16725
+ }
16726
+ function createCuratorHandler(memory, subconscious, curatorMemory = memory, options) {
16727
+ const config = subconscious.reflection.find((agent) => agent.name === CURATION_AGENT);
16728
+ if (!config) return async () => "no-op";
16729
+ return async (context) => {
16730
+ let store;
16731
+ let scope;
16732
+ try {
16733
+ scope = resolveScope$1(context);
16734
+ store = await memory.storage.getStore("knowledge");
16735
+ if (!store) throw new Error("Subconscious curate requires a configured knowledge storage domain.");
16736
+ const cursor = await store.getCurationCursor({
16737
+ sourceThreadId: context.parentThreadId,
16738
+ agent: CURATION_AGENT
16739
+ });
16740
+ const worklist = await readWorklist$1(store, context.parentThreadId, scope, cursor?.lastKnowledgeId);
16741
+ if (!worklist.records.length && !context.observations.trim()) return "no-op";
16742
+ const result = await (await createCuratorAgent(memory, curatorMemory, context, scope, config, subconscious, options?.omModel)).generate(`Parent thread: ${context.parentThreadId}\nCurrent time: ${(/* @__PURE__ */ new Date()).toISOString()}\nWorklist truncated: ${worklist.hasMore}\n\nCommitted pre-reflection observations:\n${context.observations}\n\nNew KnowledgeRecord worklist:\n${JSON.stringify(worklist.records)}`, {
16743
+ requestContext: context.requestContext,
16744
+ abortSignal: context.abortSignal,
16745
+ maxSteps: config.maxSteps,
16746
+ memory: {
16747
+ thread: `subconscious:${context.parentThreadId}:curate`,
16748
+ resource: context.resourceId
16749
+ }
16750
+ });
16751
+ if (worklist.records.length) {
16752
+ const acknowledgedId = [...result.text.matchAll(/<curation-complete\s+through=["']([^"']+)["']\s*\/>/gi)].at(-1)?.[1];
16753
+ if (!acknowledgedId || !worklist.records.some((record) => record.id === acknowledgedId)) throw new Error("Curator did not acknowledge a valid processed KnowledgeRecord cursor.");
16754
+ await store.advanceCurationCursor({
16755
+ sourceThreadId: context.parentThreadId,
16756
+ agent: CURATION_AGENT,
16757
+ lastKnowledgeId: acknowledgedId
16758
+ });
16759
+ }
16760
+ return "ran";
16761
+ } catch (error) {
16762
+ const message = `curate: ${error instanceof Error ? error.message : String(error)}`;
16763
+ await context.writer?.custom({
16764
+ type: "data-subconscious-error",
16765
+ data: {
16766
+ agent: "curate",
16767
+ error: message
16768
+ }
16769
+ });
16770
+ if (store && scope) await publishSubconsciousActivity({
16771
+ store,
16772
+ scope,
16773
+ recentUpdates: subconscious.activity === false ? 10 : subconscious.activity.recentUpdates,
16774
+ sendStateSignal: context.sendStateSignal,
16775
+ errors: [message]
16776
+ });
16777
+ else await publishSubconsciousError({
16778
+ error: message,
16779
+ sendStateSignal: context.sendStateSignal
16780
+ });
16781
+ throw error;
16782
+ }
16783
+ };
16784
+ }
16785
+ async function createCuratorAgent(memory, curatorMemory, context, scope, config, subconscious, omModel) {
16786
+ const model = await resolveSubconsciousAgentModel({
16787
+ config,
16788
+ omModel,
16789
+ mainAgent: context.mainAgent,
16790
+ requestContext: context.requestContext
16791
+ });
16792
+ if (!model) throw new Error("Subconscious curate requires the main agent to resolve its model.");
16793
+ return new _mastra_core_agent.Agent({
16794
+ id: `subconscious-curate-${context.parentThreadId}`,
16795
+ name: "Subconscious Curate",
16796
+ instructions: [
16797
+ DEFAULT_INSTRUCTIONS$1,
16798
+ subconscious.pins ? PINNED_INSTRUCTIONS : void 0,
16799
+ config.instructions?.trim()
16800
+ ].filter(Boolean).join("\n\n"),
16801
+ model,
16802
+ memory: curatorMemory,
16803
+ tools: {
16804
+ ...createKnowledgeTools(memory),
16805
+ ...createKnowledgeWriteTools(memory, {
16806
+ scope,
16807
+ sourceThreadId: context.parentThreadId,
16808
+ defaultScope: subconscious.defaultScope,
16809
+ maxScope: subconscious.maxScope
16810
+ }),
16811
+ ...subconscious.pins ? createPinnedTools(memory, {
16812
+ scope,
16813
+ sourceThreadId: context.parentThreadId,
16814
+ defaultScope: subconscious.defaultScope,
16815
+ maxScope: subconscious.maxScope,
16816
+ maxPins: subconscious.pins.maxPins,
16817
+ maxCharacters: subconscious.pins.maxCharacters
16818
+ }) : {}
16819
+ }
16820
+ });
16821
+ }
16822
+ //#endregion
16823
+ //#region src/processors/observational-memory/subconscious/learn.ts
16824
+ const LEARN_AGENT = "learn";
16825
+ const ULID_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
16826
+ const DEFAULT_INSTRUCTIONS = `Learn reusable skills from the full pre-reflection observations and pending knowledge records.
16827
+
16828
+ A skill is a repeatable procedure with ordered actions, a trigger or context, and a success or recovery outcome. Do not learn one-off events, isolated preferences, knowledge records, or procedures supported by fewer than two distinct pending knowledge records. Search existing kind:skill nodes by exact name before writing so updates extend one skill rather than creating duplicates.
16829
+
16830
+ Use knowledge_record_skill for every skill creation or evidence update. It validates the evidence frontier and writes retry-safe evidence. You may use the other scoped knowledge tools for research and maintenance, but never restore deleted records, invent provenance or versions, or write outside the source scope.
16831
+
16832
+ Process pending records in ID order. End with <learning-complete through="RECORD_ID" /> naming the last pending record you reviewed, even when no reusable skill was found. Acknowledge only records you fully reviewed.`;
16833
+ function resolveScope(context) {
16834
+ const organizationId = context.requestContext?.get("organizationId");
16835
+ if (typeof organizationId !== "string" || !organizationId.trim()) throw new Error("Subconscious learn requires organizationId in the request context.");
16836
+ return (0, _mastra_core_storage.canonicalizeKnowledgeScope)([
16837
+ `org:${organizationId}`,
16838
+ `resource:${resolveKnowledgeResourceId(context.requestContext, context.resourceId)}`,
16839
+ `thread:${context.parentThreadId}`
16840
+ ]);
16841
+ }
16842
+ async function readWorklist(store, sourceThreadId, scope, after) {
16843
+ const records = [];
16844
+ let cursor = after;
16845
+ do {
16846
+ const page = await store.knowledgeBySource({
16847
+ sourceThreadId,
16848
+ scope,
16849
+ after: cursor,
16850
+ limit: 100
16851
+ });
16852
+ records.push(...page.records);
16853
+ cursor = page.nextCursor;
16854
+ } while (cursor && records.length < 500);
16855
+ return {
16856
+ records,
16857
+ hasMore: Boolean(cursor)
16858
+ };
16859
+ }
16860
+ function evidenceRecordId(sourceRecordId, skillName) {
16861
+ const hash = (0, crypto$1.createHash)("sha256").update(`${skillName.trim().toLocaleLowerCase()}\0${sourceRecordId}`).digest();
16862
+ let suffix = "";
16863
+ for (let index = 0; index < 16; index++) suffix += ULID_ALPHABET[hash[index] & 31];
16864
+ return `${sourceRecordId.slice(0, 10)}${suffix}`;
16865
+ }
16866
+ function createLearnerRecordSkillTool(input) {
16867
+ return (0, _mastra_core_tools.createTool)({
16868
+ id: "knowledge_record_skill",
16869
+ description: "Create or update one reusable skill using at least two distinct pending knowledge records. Evidence writes are idempotent across retries.",
16870
+ inputSchema: {
16871
+ type: "object",
16872
+ properties: {
16873
+ name: {
16874
+ type: "string",
16875
+ minLength: 1
16876
+ },
16877
+ procedure: {
16878
+ type: "string",
16879
+ minLength: 1
16880
+ },
16881
+ sourceRecordIds: {
16882
+ type: "array",
16883
+ items: {
16884
+ type: "string",
16885
+ minLength: 1
16886
+ },
16887
+ minItems: 2,
16888
+ uniqueItems: true
16889
+ }
16890
+ },
16891
+ required: [
16892
+ "name",
16893
+ "procedure",
16894
+ "sourceRecordIds"
16895
+ ],
16896
+ additionalProperties: false
16897
+ },
16898
+ execute: async (raw) => {
16899
+ const value = raw;
16900
+ const sourceIds = [...new Set(value.sourceRecordIds)];
16901
+ const pending = new Map(input.pendingRecords.map((record) => [record.id, record]));
16902
+ if (sourceIds.length < 2 || sourceIds.some((id) => !pending.has(id))) throw new Error("Skill evidence requires at least two distinct records from the pending learner worklist.");
16903
+ const normalizedName = value.name.trim();
16904
+ if (input.state.recordedName && input.state.recordedName.toLocaleLowerCase() !== normalizedName.toLocaleLowerCase()) throw new Error("The learner may record at most one skill per reflection.");
16905
+ input.state.recordedName = normalizedName;
16906
+ const nodeScope = (0, _mastra_core_storage.expandKnowledgeScope)(input.scope, input.defaultScope);
16907
+ let node = await input.store.resolveNode({
16908
+ name: normalizedName,
16909
+ scope: input.scope
16910
+ });
16911
+ if (node && node.kind !== "skill") throw new Error(`Knowledge node is not a skill: ${normalizedName}`);
16912
+ node ??= await input.store.createNode({
16913
+ name: normalizedName,
16914
+ kind: "skill",
16915
+ scope: nodeScope
16916
+ });
16917
+ const evidence = [];
16918
+ for (const sourceId of sourceIds) {
16919
+ const id = evidenceRecordId(sourceId, normalizedName);
16920
+ const existing = await input.store.getKnowledge({ id });
16921
+ if (existing) {
16922
+ evidence.push(existing);
16923
+ continue;
16924
+ }
16925
+ const source = pending.get(sourceId);
16926
+ try {
16927
+ evidence.push(await input.store.appendKnowledge({
16928
+ id,
16929
+ node: node.id,
16930
+ text: `Procedure: ${value.procedure.trim()} Evidence source: ${source.id}.`,
16931
+ scope: source.scope,
16932
+ sourceThreadId: `subconscious:${input.parentThreadId}:learn`,
16933
+ maxScope: source.maxScope ?? input.maxScope,
16934
+ resolutionScope: input.scope,
16935
+ defaultScope: nodeScope
16936
+ }));
16937
+ } catch (error) {
16938
+ const raced = await input.store.getKnowledge({ id });
16939
+ if (!raced) throw error;
16940
+ evidence.push(raced);
16941
+ }
16942
+ }
16943
+ return {
16944
+ node,
16945
+ evidence
16946
+ };
16947
+ }
16948
+ });
16949
+ }
16950
+ function composeReflectionAgentHandlers(handlers) {
16951
+ return async (context) => {
16952
+ for (const handler of handlers) try {
16953
+ await handler(context);
16954
+ } catch (error) {
16955
+ if (context.abortSignal?.aborted) throw error;
16956
+ }
16957
+ };
16958
+ }
16959
+ function createLearnerHandler(memory, subconscious, learnerMemory = memory, options) {
16960
+ const config = subconscious.reflection.find((agent) => agent.name === LEARN_AGENT);
16961
+ if (!config) return async () => {};
16962
+ return async (context) => {
16963
+ let store;
16964
+ let scope;
16965
+ try {
16966
+ scope = resolveScope(context);
16967
+ store = await memory.storage.getStore("knowledge");
16968
+ if (!store) throw new Error("Subconscious learn requires a configured knowledge storage domain.");
16969
+ const cursor = await store.getCurationCursor({
16970
+ sourceThreadId: context.parentThreadId,
16971
+ agent: LEARN_AGENT
16972
+ });
16973
+ const worklist = await readWorklist(store, context.parentThreadId, scope, cursor?.lastKnowledgeId);
16974
+ if (!worklist.records.length) return;
16975
+ const acknowledgedId = (await (await createLearnerAgent(memory, learnerMemory, context, scope, worklist.records, config, subconscious, options?.omModel)).generate(`Parent thread: ${context.parentThreadId}\nCurrent time: ${(/* @__PURE__ */ new Date()).toISOString()}\nWorklist truncated: ${worklist.hasMore}\n\nFull pre-reflection observations:\n${context.observations}\n\nPending knowledge records:\n${JSON.stringify(worklist.records)}`, {
16976
+ requestContext: context.requestContext,
16977
+ abortSignal: context.abortSignal,
16978
+ maxSteps: config.maxSteps,
16979
+ memory: {
16980
+ thread: `subconscious:${context.parentThreadId}:learn`,
16981
+ resource: context.resourceId
16982
+ }
16983
+ })).text.match(/<learning-complete\s+through=["']([^"']+)["']\s*\/>/i)?.[1];
16984
+ if (!acknowledgedId || !worklist.records.some((record) => record.id === acknowledgedId)) throw new Error("Learner did not acknowledge a valid reviewed record cursor.");
16985
+ await store.advanceCurationCursor({
16986
+ sourceThreadId: context.parentThreadId,
16987
+ agent: LEARN_AGENT,
16988
+ lastKnowledgeId: acknowledgedId
16989
+ });
16990
+ } catch (error) {
16991
+ const message = `learn: ${error instanceof Error ? error.message : String(error)}`;
16992
+ await context.writer?.custom({
16993
+ type: "data-subconscious-error",
16994
+ data: {
16995
+ agent: "learn",
16996
+ error: message
16997
+ }
16998
+ });
16999
+ if (store && scope) await publishSubconsciousActivity({
17000
+ store,
17001
+ scope,
17002
+ recentUpdates: subconscious.activity === false ? 10 : subconscious.activity.recentUpdates,
17003
+ sendStateSignal: context.sendStateSignal,
17004
+ errors: [message]
17005
+ });
17006
+ else await publishSubconsciousError({
17007
+ error: message,
17008
+ sendStateSignal: context.sendStateSignal
17009
+ });
17010
+ throw error;
17011
+ }
17012
+ };
17013
+ }
17014
+ async function createLearnerAgent(memory, learnerMemory, context, scope, pendingRecords, config, subconscious, omModel) {
17015
+ const model = await resolveSubconsciousAgentModel({
17016
+ config,
17017
+ omModel,
17018
+ mainAgent: context.mainAgent,
17019
+ requestContext: context.requestContext
17020
+ });
17021
+ if (!model) throw new Error("Subconscious learn requires the main agent to resolve its model.");
17022
+ const store = await memory.storage.getStore("knowledge");
17023
+ if (!store) throw new Error("Subconscious learn requires a configured knowledge storage domain.");
17024
+ const state = {};
17025
+ return new _mastra_core_agent.Agent({
17026
+ id: `subconscious-learn-${context.parentThreadId}`,
17027
+ name: "Subconscious Learn",
17028
+ instructions: [DEFAULT_INSTRUCTIONS, config.instructions?.trim()].filter(Boolean).join("\n\n"),
17029
+ model,
17030
+ memory: learnerMemory,
17031
+ tools: {
17032
+ ...createKnowledgeTools(memory, scope),
17033
+ ...createKnowledgeWriteTools(memory, {
17034
+ scope,
17035
+ sourceThreadId: context.parentThreadId,
17036
+ defaultScope: subconscious.defaultScope,
17037
+ maxScope: subconscious.maxScope
17038
+ }),
17039
+ knowledge_record_skill: createLearnerRecordSkillTool({
17040
+ store,
17041
+ scope,
17042
+ pendingRecords,
17043
+ parentThreadId: context.parentThreadId,
17044
+ defaultScope: subconscious.defaultScope,
17045
+ maxScope: subconscious.maxScope,
17046
+ state
17047
+ })
17048
+ }
17049
+ });
17050
+ }
17051
+ //#endregion
17052
+ //#region src/processors/observational-memory/constants.ts
17053
+ var constants_exports = /* @__PURE__ */ __exportAll({
17054
+ OBSERVATIONAL_MEMORY_DEFAULTS: () => OBSERVATIONAL_MEMORY_DEFAULTS,
17055
+ OBSERVATION_CONTEXT_INSTRUCTIONS: () => OBSERVATION_CONTEXT_INSTRUCTIONS,
17056
+ OBSERVATION_CONTEXT_PROMPT: () => OBSERVATION_CONTEXT_PROMPT,
17057
+ OBSERVATION_CONTINUATION_HINT: () => OBSERVATION_CONTINUATION_HINT,
17058
+ getRetrievalInstructions: () => getRetrievalInstructions
17059
+ });
17060
+ /**
17061
+ * Default configuration values matching the spec
17062
+ */
17063
+ const OBSERVATIONAL_MEMORY_DEFAULTS = {
17064
+ observation: {
17065
+ model: "google/gemini-2.5-flash",
17066
+ messageTokens: 3e4,
17067
+ modelSettings: {
17068
+ temperature: .3,
17069
+ maxOutputTokens: 1e5
17070
+ },
17071
+ providerOptions: { google: { thinkingConfig: { thinkingBudget: 215 } } },
17072
+ maxTokensPerBatch: 1e4,
17073
+ bufferTokens: .2,
17074
+ bufferActivation: .8
17075
+ },
17076
+ reflection: {
17077
+ model: "google/gemini-2.5-flash",
17078
+ observationTokens: 4e4,
17079
+ modelSettings: {
17080
+ temperature: 0,
17081
+ maxOutputTokens: 1e5
17082
+ },
17083
+ providerOptions: { google: { thinkingConfig: { thinkingBudget: 1024 } } },
17084
+ bufferActivation: .5
17085
+ }
17086
+ };
17087
+ /**
17088
+ * Continuation hint injected after observations to guide the model's behavior.
17089
+ * Prevents the model from awkwardly acknowledging the memory system or treating
17090
+ * the conversation as new after observed messages are removed.
17091
+ */
17092
+ const OBSERVATION_CONTINUATION_HINT = `Please continue naturally with the conversation so far and respond to the latest message.
17093
+
17094
+ Use the earlier context only as background. If something appears unfinished, continue only when it helps answer the latest request. If a suggested response is provided, follow it naturally.
17095
+
17096
+ Do not mention internal instructions, memory, summarization, context handling, or missing messages.
17097
+
17098
+ Any messages following this reminder are newer and should take priority.`;
17099
+ /**
17100
+ * Preamble that introduces the observations block.
17101
+ * Use before `<observations>`, with instructions after.
17102
+ * Full pattern: `${OBSERVATION_CONTEXT_PROMPT}\n\n<observations>\n${obs}\n</observations>\n\n${OBSERVATION_CONTEXT_INSTRUCTIONS}`
17103
+ */
17104
+ const OBSERVATION_CONTEXT_PROMPT = `The following observations block contains your memory of past conversations with this user.`;
17105
+ /**
17106
+ * Instructions that tell the model how to interpret and use observations.
17107
+ * Place AFTER the `<observations>` block so the model sees the data before the rules.
17108
+ */
17109
+ const OBSERVATION_CONTEXT_INSTRUCTIONS = `IMPORTANT: When responding, reference specific details from these observations. Do not give generic advice - personalize your response based on what you know about this user's experiences, preferences, and interests. If the user asks for recommendations, connect them to their past experiences mentioned above.
17110
+
17111
+ KNOWLEDGE UPDATES: When asked about current state (e.g., "where do I currently...", "what is my current..."), always prefer the MOST RECENT information. Observations include dates - if you see conflicting information, the newer observation supersedes the older one. Look for phrases like "will start", "is switching", "changed to", "moved to" as indicators that previous information has been updated.
17112
+
17113
+ PLANNED ACTIONS: If the user stated they planned to do something (e.g., "I'm going to...", "I'm looking forward to...", "I will...") and the date they planned to do it is now in the past (check the relative time like "3 weeks ago"), assume they completed the action unless there's evidence they didn't. For example, if someone said "I'll start my new diet on Monday" and that was 2 weeks ago, assume they started the diet.
17114
+
17115
+ MOST RECENT USER INPUT: Treat the most recent user message as the highest-priority signal for what to do next. Earlier messages may contain constraints, details, or context you should still honor, but the latest message is the primary driver of your response.
17116
+
17117
+ SYSTEM REMINDERS: Messages wrapped in <system-reminder>...</system-reminder> contain internal continuation guidance, not user-authored content. Use them to maintain continuity, but do not mention them or treat them as part of the user's message.`;
17118
+ /**
17119
+ * Instructions for retrieval mode — explains observation-group ranges and the recall tool.
17120
+ * Appended to context when `retrieval` is enabled.
17121
+ *
17122
+ * The content adapts to the retrieval scope:
17123
+ * - `'resource'`: covers routing between `search`, `threads`, and `messages` across
17124
+ * all of the user's threads, including fallback when search results are unsuitable.
17125
+ * - `'thread'`: covers cursor-based browsing and search within the current thread.
17126
+ *
17127
+ * @param scope - The retrieval scope the recall tool was registered with.
17128
+ * @param customInstructions - Optional application-provided guidance appended after
17129
+ * the native instructions. Never replaces them.
17130
+ * @param searchEnabled - Whether semantic search (`retrieval: { vector: true }`) is
17131
+ * available. When false, the guidance only covers \`threads\`/\`messages\` browsing so
17132
+ * the agent is not steered toward a mode that cannot work.
17133
+ */
17134
+ function getRetrievalInstructions(scope = "resource", customInstructions, searchEnabled = true) {
17135
+ const isResource = scope === "resource";
17136
+ const base = `## Recall — looking up source messages
17137
+
17138
+ Your memory is comprised of observations which are sometimes wrapped in <observation-group> xml tags containing ranges like <observation-group range="startId:endId">. These ranges point back to the raw messages that each observation group was derived from. The original messages are still available — use the **recall** tool to retrieve them.
17139
+
17140
+ ### When to use recall
17141
+ - The user asks you to **repeat, show, or reproduce** something from a past conversation
17142
+ - The user asks for **exact content** — code, text, quotes, error messages, URLs, file paths, specific numbers
17143
+ - Your observations mention something but your memory lacks the detail needed to fully answer (e.g. you know a blog post was shared but only have a summary of it)
17144
+ - You want to **verify or expand on** an observation before responding${isResource ? `
17145
+ - The user references another conversation that your observations don't cover — even if you have no observations yet, their other threads may contain it` : ""}
17146
+
17147
+ **Default to using recall when the user references specific past content.** Your observations capture the gist, not the details. If there's any doubt whether your memory is complete enough, use recall.
17148
+
17149
+ ${isResource ? searchEnabled ? `### Choosing a mode
17150
+ The recall tool works across ALL of this user's conversation threads, not just the current one.
17151
+
17152
+ - Use \`mode: "search"\` with a \`query\` when you don't know which thread contains the answer. Each result includes its thread ID and the raw message IDs it came from, which you can use as a \`cursor\`.
17153
+ - Use \`mode: "messages"\` when you already know the thread — pass \`threadId\` to read another thread, or a \`cursor\` from an observation-group range or a search result.
17154
+ - Use \`mode: "threads"\` to list the user's threads (IDs, titles, dates) when you need to discover where something was discussed. Use \`before\`/\`after\` to narrow by date.
17155
+
17156
+ **If search results look irrelevant, do not give up.** Search only covers content that has been indexed — a short or recent conversation may exist in raw message history before any observation of it was created. When search returns nothing suitable but the user is clearly referring to a past conversation, call \`mode: "threads"\` to find candidate threads (titles and dates are strong clues), then read them with \`mode: "messages"\`. If a search result already gives you a thread ID, go straight to \`mode: "messages"\`.` : `### Choosing a mode
17157
+ The recall tool works across ALL of this user's conversation threads, not just the current one.
17158
+
17159
+ - Use \`mode: "threads"\` to list the user's threads (IDs, titles, dates) when you need to discover where something was discussed. Use \`before\`/\`after\` to narrow by date.
17160
+ - Use \`mode: "messages"\` when you know the thread — pass \`threadId\` to read another thread, or a \`cursor\` from an observation-group range.
17161
+
17162
+ When the user refers to a past conversation you don't have a cursor for, call \`mode: "threads"\` to find candidate threads (titles and dates are strong clues), then read them with \`mode: "messages"\`. Raw history may exist for threads that have no observations yet.` : `### Choosing a mode
17163
+ The recall tool is limited to the current conversation thread.
17164
+
17165
+ - Use \`mode: "messages"\` (default) to page through this thread's message history near a cursor.${searchEnabled ? `
17166
+ - Use \`mode: "search"\` with a \`query\` to find messages by content within this thread.` : ""}
17167
+ - Use \`mode: "threads"\` to get the current thread's ID, title, and dates.`}
17168
+
17169
+ ### How to use recall with a cursor
17170
+ Each range has the format \`startId:endId\` where both are message IDs separated by a colon.
17171
+
17172
+ 1. Find the observation group relevant to the user's question and extract the start or end ID from its range.
17173
+ 2. Call \`recall\` with that ID as the \`cursor\`.
17174
+ 3. Use \`page: 1\` (or omit) to read forward from the cursor, \`page: -1\` to read backward.
17175
+ 4. If the first page doesn't have what you need, increment the page number to keep paginating.
17176
+ 5. Check \`hasNextPage\`/\`hasPrevPage\` in the result to know if more pages exist in each direction.
17177
+
17178
+ ### Detail levels
17179
+ By default recall returns **low** detail: truncated text and tool names only. Each message shows its ID and each part has a positional index like \`[p0]\`, \`[p1]\`, etc.
17180
+
17181
+ - Use \`detail: "high"\` to get full message content including tool arguments and results. This will only return the high detail version of a single message part at a time.
17182
+ - Use \`partIndex\` with a cursor to fetch a single part at full detail — for example, to read one specific tool result or code block without loading every part.
17183
+
17184
+ If the result says \`truncated: true\`, the output was cut to fit the token budget. You can paginate or use \`partIndex\` to target specific content. If a single part is itself too large, follow the returned \`nextCharOffset\` as described below.
17185
+
17186
+ ### Following up on truncated parts
17187
+ Low-detail results may include truncation hints like:
17188
+ \`[truncated — call recall cursor="..." partIndex=N detail="high" for full content]\`
17189
+
17190
+ **When you see these hints and need the full content, make the exact call described in the hint.** This is the normal workflow: first recall at low detail to scan, then drill into specific parts at high detail. Do not stop at the low-detail result if the user asked for exact content.
17191
+
17192
+ If a single part is larger than the token budget, the \`partIndex\` result is \`truncated: true\` and includes \`nextCharOffset\`. Repeat the same call with \`charOffset\` set to that exact value to read the next chunk from where the previous one ended. Keep following \`nextCharOffset\` until the result no longer includes it — the chunks together contain the full part. Retrying without \`charOffset\` returns the same prefix again.
17193
+
17194
+ ### When recall is NOT needed
17195
+ - The user is asking for a high-level summary and your observations already cover it
17196
+ - The question is about general preferences or facts that don't require source text
17197
+ ${isResource ? `- No relevant observation range exists AND ${searchEnabled ? "`search`/`threads`" : "`threads`"} turned up nothing — but remember that raw history may exist for threads that have no observations yet, so check before concluding the information is unavailable` : `- There is no relevant range in your observations for the topic`}
17198
+
17199
+ Observation groups with range IDs and your recall tool allows you to think back and remember details you're fuzzy on.`;
17200
+ const custom = customInstructions?.trim();
17201
+ if (!custom) return base;
17202
+ return `${base}
17203
+
17204
+ ### Additional recall guidance
17205
+ ${custom}`;
17206
+ }
17207
+ //#endregion
17208
+ //#region src/processors/observational-memory/extracted-values.ts
17209
+ const BUILT_IN_METADATA_FIELDS = {
17210
+ "current-task": {
17211
+ metadataField: "currentTask",
17212
+ builtInField: "currentTask"
17213
+ },
17214
+ "suggested-response": {
17215
+ metadataField: "suggestedResponse",
17216
+ builtInField: "suggestedContinuation"
17217
+ },
17218
+ "thread-title": {
17219
+ metadataField: "threadTitle",
17220
+ builtInField: "threadTitle"
17221
+ }
17222
+ };
17223
+ function isPresentExtractedValue(value) {
17224
+ return value !== void 0 && value !== null && value !== "";
17225
+ }
17226
+ function normalizeExtractedValues(values) {
17227
+ if (!values) return;
17228
+ const normalized = Object.fromEntries(Object.entries(values).filter(([, value]) => isPresentExtractedValue(value)));
17229
+ return Object.keys(normalized).length > 0 ? normalized : void 0;
17230
+ }
17231
+ function mergeExtractedValues(...valueSets) {
17232
+ const merged = {};
17233
+ for (const values of valueSets) {
17234
+ const normalized = normalizeExtractedValues(values);
17235
+ if (normalized) Object.assign(merged, normalized);
17236
+ }
17237
+ return Object.keys(merged).length > 0 ? merged : void 0;
17238
+ }
17239
+ function mergeExtractionFailures(...failureSets) {
17240
+ const failures = failureSets.flatMap((set) => set ?? []);
17241
+ return failures.length > 0 ? failures : void 0;
17242
+ }
17243
+ const UNSAFE_METADATA_PATH_SEGMENTS = /* @__PURE__ */ new Set([
17244
+ "__proto__",
17245
+ "constructor",
17246
+ "prototype"
17247
+ ]);
17248
+ function getMetadataPathSegments(keyPath) {
17249
+ if (keyPath === false) return;
17250
+ const segments = keyPath.split(".").filter(Boolean);
17251
+ if (segments.some((segment) => UNSAFE_METADATA_PATH_SEGMENTS.has(segment))) throw new Error(`Extractor metadataKeyPath "${keyPath}" contains an unsafe path segment.`);
17252
+ return segments.length > 0 ? segments : void 0;
17253
+ }
17254
+ function getValueAtPath(metadata, keyPath) {
17255
+ if (!metadata) return;
17256
+ const segments = getMetadataPathSegments(keyPath);
17257
+ if (!segments) return;
17258
+ let current = metadata;
17259
+ for (const segment of segments) {
17260
+ if (!current || typeof current !== "object" || Array.isArray(current)) return;
17261
+ current = current[segment];
17262
+ }
17263
+ return current;
17264
+ }
17265
+ function setValueAtPath(metadata, keyPath, value) {
17266
+ if (!isPresentExtractedValue(value)) return;
17267
+ const segments = getMetadataPathSegments(keyPath);
17268
+ if (!segments) return;
17269
+ let current = metadata;
17270
+ for (const segment of segments.slice(0, -1)) {
17271
+ const next = current[segment];
17272
+ if (!next || typeof next !== "object" || Array.isArray(next)) current[segment] = {};
17273
+ current = current[segment];
17274
+ }
17275
+ current[segments[segments.length - 1]] = value;
17276
+ }
17277
+ function readBuiltInMetadataValues(metadata) {
17278
+ const values = {};
17279
+ for (const slug of BUILT_IN_EXTRACTOR_SLUGS) {
17280
+ const { metadataField } = BUILT_IN_METADATA_FIELDS[slug];
17281
+ values[slug] = metadata[metadataField];
17282
+ }
15099
17283
  return values;
15100
17284
  }
15101
17285
  function getPriorExtractedValues(metadata, extractors) {
@@ -15143,9 +17327,11 @@ async function applyExtractorHooks(opts) {
15143
17327
  const values = normalizeExtractedValues(opts.values) ?? {};
15144
17328
  const failures = [...opts.failures ?? []];
15145
17329
  for (const extractor of opts.extractors) {
15146
- if (!Object.prototype.hasOwnProperty.call(values, extractor.slug)) continue;
15147
- const current = values[extractor.slug];
15148
- if (!isPresentExtractedValue(current)) {
17330
+ const isHook = extractor.mode === "hook";
17331
+ const hasValue = Object.prototype.hasOwnProperty.call(values, extractor.slug);
17332
+ if (!isHook && !hasValue) continue;
17333
+ const current = isHook ? opts.rawObservations : values[extractor.slug];
17334
+ if (!isHook && !isPresentExtractedValue(current)) {
15149
17335
  delete values[extractor.slug];
15150
17336
  continue;
15151
17337
  }
@@ -15156,14 +17342,19 @@ async function applyExtractorHooks(opts) {
15156
17342
  extractor,
15157
17343
  threadId: opts.threadId,
15158
17344
  resourceId: opts.resourceId,
15159
- previous: opts.previousValues?.[extractor.slug],
17345
+ previous: isHook ? void 0 : opts.previousValues?.[extractor.slug],
15160
17346
  current,
17347
+ rawObservations: opts.rawObservations,
17348
+ recentMessages: opts.recentMessages,
15161
17349
  mainAgent: opts.mainAgent,
15162
17350
  memory: opts.memory,
15163
17351
  sendSignal: opts.sendSignal,
17352
+ sendStateSignal: opts.sendStateSignal,
17353
+ writer: opts.writer,
17354
+ abortSignal: opts.abortSignal,
15164
17355
  requestContext: opts.requestContext
15165
17356
  });
15166
- if (hookValue === void 0) continue;
17357
+ if (isHook || hookValue === void 0) continue;
15167
17358
  const parsed = extractor.schema.safeParse(hookValue);
15168
17359
  if (parsed.success) values[extractor.slug] = parsed.data;
15169
17360
  else {
@@ -15174,7 +17365,8 @@ async function applyExtractorHooks(opts) {
15174
17365
  });
15175
17366
  }
15176
17367
  } catch (error) {
15177
- delete values[extractor.slug];
17368
+ if (opts.abortSignal?.aborted) throw error;
17369
+ if (!isHook) delete values[extractor.slug];
15178
17370
  failures.push({
15179
17371
  slug: extractor.slug,
15180
17372
  error: error instanceof Error ? error.message : String(error)
@@ -16239,11 +18431,11 @@ function formatObserverAttachmentPlaceholder(part, counter) {
16239
18431
  const label = resolveObserverAttachmentLabel(part);
16240
18432
  return label ? `[${attachmentType} #${attachmentId}: ${label}]` : `[${attachmentType} #${attachmentId}]`;
16241
18433
  }
16242
- function isRecord$2(value) {
18434
+ function isRecord$3(value) {
16243
18435
  return !!value && typeof value === "object";
16244
18436
  }
16245
18437
  function mapToolResultBlockToAttachment(block) {
16246
- if (!isRecord$2(block) || typeof block.type !== "string") return;
18438
+ if (!isRecord$3(block) || typeof block.type !== "string") return;
16247
18439
  const mediaType = typeof block.mediaType === "string" ? block.mediaType : void 0;
16248
18440
  const filename = typeof block.filename === "string" ? block.filename : void 0;
16249
18441
  switch (block.type) {
@@ -16304,7 +18496,7 @@ function mapToolResultBlockToAttachment(block) {
16304
18496
  }
16305
18497
  }
16306
18498
  function extractToolResultAttachments(result, counter, attachmentFilter) {
16307
- if (!isRecord$2(result) || result.type !== "content" || !Array.isArray(result.value)) return {
18499
+ if (!isRecord$3(result) || result.type !== "content" || !Array.isArray(result.value)) return {
16308
18500
  resultWithoutAttachments: result,
16309
18501
  attachments: []
16310
18502
  };
@@ -16318,7 +18510,7 @@ function extractToolResultAttachments(result, counter, attachmentFilter) {
16318
18510
  if (shouldIncludeObserverAttachment(attachment, attachmentFilter)) attachments.push(toObserverInputAttachmentPart(attachment));
16319
18511
  const placeholder = formatObserverAttachmentPlaceholder(attachment, counter);
16320
18512
  return {
16321
- type: isRecord$2(block) ? block.type : void 0,
18513
+ type: isRecord$3(block) ? block.type : void 0,
16322
18514
  placeholder
16323
18515
  };
16324
18516
  });
@@ -16774,6 +18966,52 @@ function detectDegenerateRepetition(text) {
16774
18966
  return false;
16775
18967
  }
16776
18968
  /**
18969
+ * Build a single-line diagnostic description of output flagged by
18970
+ * {@link detectDegenerateRepetition}. Used when logging or surfacing a
18971
+ * degenerate-output failure so the discarded model output can be inspected —
18972
+ * without it there is no way to tell a real repetition loop apart from a
18973
+ * detector false-positive on legitimately repetitive content.
18974
+ *
18975
+ * Reuses the detector's sampling parameters (200-char windows, ~50 samples)
18976
+ * so the reported duplicate ratio and most-repeated window match what
18977
+ * triggered the detection. Snippets are JSON-escaped so the result stays on
18978
+ * one line.
18979
+ */
18980
+ function describeDegenerateOutput(text, snippetChars = 400) {
18981
+ const windowSize = 200;
18982
+ const step = Math.max(1, Math.floor(text.length / 50));
18983
+ const seen = /* @__PURE__ */ new Map();
18984
+ let duplicateWindows = 0;
18985
+ let totalWindows = 0;
18986
+ for (let i = 0; i + windowSize <= text.length; i += step) {
18987
+ const window = text.slice(i, i + windowSize);
18988
+ totalWindows++;
18989
+ const count = (seen.get(window) ?? 0) + 1;
18990
+ seen.set(window, count);
18991
+ if (count > 1) duplicateWindows++;
18992
+ }
18993
+ let topWindow = "";
18994
+ let topCount = 0;
18995
+ for (const [window, count] of seen) if (count > topCount) {
18996
+ topCount = count;
18997
+ topWindow = window;
18998
+ }
18999
+ let longestLine = 0;
19000
+ for (const line of text.split("\n")) if (line.length > longestLine) longestLine = line.length;
19001
+ const duplicateRatio = totalWindows > 0 ? (duplicateWindows / totalWindows).toFixed(2) : "n/a";
19002
+ const parts = [
19003
+ `length=${text.length}`,
19004
+ `sampledWindows=${totalWindows}`,
19005
+ `duplicateRatio=${duplicateRatio}`,
19006
+ `longestLine=${longestLine}`,
19007
+ `topWindowCount=${topCount}`
19008
+ ];
19009
+ if (topCount > 1) parts.push(`topWindow=${JSON.stringify(topWindow)}`);
19010
+ parts.push(`head=${JSON.stringify(text.slice(0, snippetChars))}`);
19011
+ if (text.length > snippetChars * 2) parts.push(`tail=${JSON.stringify(text.slice(-snippetChars))}`);
19012
+ return parts.join(" ");
19013
+ }
19014
+ /**
16777
19015
  * Check if observations contain a Current Task section.
16778
19016
  * Supports both XML format and legacy markdown format.
16779
19017
  */
@@ -16861,17 +19099,17 @@ const TRANSIENT_MESSAGE_SUBSTRINGS = [
16861
19099
  "connection reset",
16862
19100
  "connection closed"
16863
19101
  ];
16864
- function isRecord$1(value) {
19102
+ function isRecord$2(value) {
16865
19103
  return typeof value === "object" && value !== null;
16866
19104
  }
16867
19105
  function isAbortError(error) {
16868
- if (!isRecord$1(error)) return false;
19106
+ if (!isRecord$2(error)) return false;
16869
19107
  if (error.name === "AbortError") return true;
16870
19108
  if (typeof error.code === "string" && error.code === "ABORT_ERR") return true;
16871
19109
  return false;
16872
19110
  }
16873
19111
  function hasTransientMessage(value) {
16874
- if (!isRecord$1(value)) return false;
19112
+ if (!isRecord$2(value)) return false;
16875
19113
  const message = typeof value.message === "string" ? value.message.toLowerCase() : "";
16876
19114
  if (message && TRANSIENT_MESSAGE_SUBSTRINGS.some((sub) => message.includes(sub))) return true;
16877
19115
  if (typeof value.code === "string" && value.code.toUpperCase().startsWith("UND_ERR_")) return true;
@@ -16882,13 +19120,13 @@ function isRetryableHttpStatus(status) {
16882
19120
  return status >= 500 && status <= 599;
16883
19121
  }
16884
19122
  function hasRetryableHttpStatus(value) {
16885
- if (!isRecord$1(value)) return false;
19123
+ if (!isRecord$2(value)) return false;
16886
19124
  if (typeof value.statusCode === "number" && isRetryableHttpStatus(value.statusCode)) return true;
16887
19125
  if (typeof value.code === "number" && isRetryableHttpStatus(value.code)) return true;
16888
19126
  return false;
16889
19127
  }
16890
19128
  function hasIsRetryableFlag(value) {
16891
- if (!isRecord$1(value)) return false;
19129
+ if (!isRecord$2(value)) return false;
16892
19130
  return value.isRetryable === true;
16893
19131
  }
16894
19132
  /**
@@ -16906,14 +19144,14 @@ function isTransientLLMError(error) {
16906
19144
  if (isAbortError(error)) return false;
16907
19145
  const visited = /* @__PURE__ */ new WeakSet();
16908
19146
  function visit(candidate) {
16909
- if (isRecord$1(candidate)) {
19147
+ if (isRecord$2(candidate)) {
16910
19148
  if (visited.has(candidate)) return false;
16911
19149
  visited.add(candidate);
16912
19150
  }
16913
19151
  if (hasTransientMessage(candidate)) return true;
16914
19152
  if (hasRetryableHttpStatus(candidate)) return true;
16915
19153
  if (hasIsRetryableFlag(candidate)) return true;
16916
- if (isRecord$1(candidate)) {
19154
+ if (isRecord$2(candidate)) {
16917
19155
  if (visit(candidate.cause)) return true;
16918
19156
  if (visit(candidate.error)) return true;
16919
19157
  }
@@ -17176,13 +19414,13 @@ var ObserverRunner = class {
17176
19414
  let parsed = parseObserverOutput(result.text, activeExtractors);
17177
19415
  let retriedDueToDegenerate = false;
17178
19416
  if (parsed.degenerate) {
17179
- omDebug(`[OM:callObserver] degenerate repetition detected, retrying once`);
19417
+ omDebug(`[OM:callObserver] degenerate repetition detected, retrying once. ${describeDegenerateOutput(result.text, 2e3)}`);
17180
19418
  result = await doGenerate();
17181
19419
  parsed = parseObserverOutput(result.text, activeExtractors);
17182
19420
  retriedDueToDegenerate = true;
17183
19421
  if (parsed.degenerate) {
17184
- omDebug(`[OM:callObserver] degenerate repetition on retry, failing`);
17185
- throw new Error("Observer produced degenerate output after retry");
19422
+ omDebug(`[OM:callObserver] degenerate repetition on retry, failing. ${describeDegenerateOutput(result.text, 2e3)}`);
19423
+ throw new Error(`Observer produced degenerate output after retry. ${describeDegenerateOutput(result.text)}`);
17186
19424
  }
17187
19425
  }
17188
19426
  const structuredExtraction = await extractStructuredValues({
@@ -17326,13 +19564,13 @@ var ObserverRunner = class {
17326
19564
  let parsed = parseMultiThreadObserverOutput(result.text, activeExtractors);
17327
19565
  let retriedDueToDegenerate = false;
17328
19566
  if (parsed.degenerate) {
17329
- omDebug(`[OM:callMultiThreadObserver] degenerate repetition detected, retrying once`);
19567
+ omDebug(`[OM:callMultiThreadObserver] degenerate repetition detected, retrying once. ${describeDegenerateOutput(result.text, 2e3)}`);
17330
19568
  result = await doGenerate();
17331
19569
  parsed = parseMultiThreadObserverOutput(result.text, activeExtractors);
17332
19570
  retriedDueToDegenerate = true;
17333
19571
  if (parsed.degenerate) {
17334
- omDebug(`[OM:callMultiThreadObserver] degenerate repetition on retry, failing`);
17335
- throw new Error("Multi-thread observer produced degenerate output after retry");
19572
+ omDebug(`[OM:callMultiThreadObserver] degenerate repetition on retry, failing. ${describeDegenerateOutput(result.text, 2e3)}`);
19573
+ throw new Error(`Multi-thread observer produced degenerate output after retry. ${describeDegenerateOutput(result.text)}`);
17336
19574
  }
17337
19575
  }
17338
19576
  const structuredExtractionByThread = /* @__PURE__ */ new Map();
@@ -20152,40 +22390,6 @@ function createWorkingMemoryTool(config, options = {}) {
20152
22390
  };
20153
22391
  }
20154
22392
  //#endregion
20155
- //#region src/processors/observational-memory/model-by-input-tokens.ts
20156
- function isTieredModelTarget(model) {
20157
- if (typeof model === "string") return true;
20158
- if (!model || typeof model !== "object") return false;
20159
- return "modelId" in model || "id" in model || "providerId" in model || "provider" in model || "doGenerate" in model && "doStream" in model;
20160
- }
20161
- function normalizeThresholds(config) {
20162
- const entries = Object.entries(config.upTo);
20163
- if (entries.length === 0) throw new Error("ModelByInputTokens requires at least one threshold in \"upTo\"");
20164
- for (const [limitStr, model] of entries) {
20165
- const limit = Number(limitStr);
20166
- if (!Number.isFinite(limit) || limit <= 0) throw new Error(`ModelByInputTokens threshold keys must be positive numbers. Got: ${limitStr}`);
20167
- if (!isTieredModelTarget(model)) throw new Error(`ModelByInputTokens requires a valid model target for threshold ${limitStr}`);
20168
- }
20169
- return entries.map(([limitStr, model]) => ({
20170
- limit: Number(limitStr),
20171
- model
20172
- })).sort((a, b) => a.limit - b.limit);
20173
- }
20174
- var ModelByInputTokens = class {
20175
- thresholds;
20176
- constructor(config) {
20177
- this.thresholds = normalizeThresholds(config);
20178
- }
20179
- resolve(inputTokens) {
20180
- for (const { limit, model } of this.thresholds) if (inputTokens <= limit) return model;
20181
- const maxLimit = this.thresholds[this.thresholds.length - 1].limit;
20182
- throw new Error(`ModelByInputTokens: input token count (${inputTokens}) exceeds the largest configured threshold (${maxLimit}). Please configure a higher threshold or use a larger model.`);
20183
- }
20184
- getThresholds() {
20185
- return this.thresholds.map((t) => t.limit);
20186
- }
20187
- };
20188
- //#endregion
20189
22393
  //#region src/processors/observational-memory/activation-ttl.ts
20190
22394
  const MINUTE = 6e4;
20191
22395
  const HOUR = 60 * MINUTE;
@@ -20993,6 +23197,39 @@ function reconcileObservationGroupsFromReflection(content, sourceObservations) {
20993
23197
  if (derivedGroups.length > 0) return derivedGroups.map((group) => wrapInObservationGroup(group.content, group.range, group.id, void 0, group.kind)).join("\n\n");
20994
23198
  return wrapInObservationGroup(normalizedContent, combineObservationGroupRanges(sourceGroups), generateAnchorId(), void 0, "reflection");
20995
23199
  }
23200
+ function isRecord$1(value) {
23201
+ return !!value && typeof value === "object" && !Array.isArray(value);
23202
+ }
23203
+ function isSubconsciousSignalMessage(message) {
23204
+ if (message.role !== "signal" || !isRecord$1(message.content)) return false;
23205
+ const contentMetadata = message.content.metadata;
23206
+ if (!isRecord$1(contentMetadata) || !isRecord$1(contentMetadata.signal)) return false;
23207
+ const signal = contentMetadata.signal;
23208
+ const metadata = signal.metadata;
23209
+ const attributes = signal.attributes;
23210
+ return signal.origin === "subconscious" || isRecord$1(metadata) && metadata.origin === "subconscious" || isRecord$1(attributes) && attributes.source === "subconscious";
23211
+ }
23212
+ function isSubconsciousSignalPart(part) {
23213
+ if (!part || typeof part !== "object") return false;
23214
+ const candidate = part;
23215
+ return candidate.type === "data-signal" && (candidate.data?.metadata?.origin === "subconscious" || candidate.data?.attributes?.source === "subconscious");
23216
+ }
23217
+ function stripSubconsciousSignals(messages) {
23218
+ return messages.flatMap((message) => {
23219
+ if (isSubconsciousSignalMessage(message)) return [];
23220
+ if (typeof message.content === "string") return [message];
23221
+ const parts = message.content.parts.filter((part) => !isSubconsciousSignalPart(part));
23222
+ if (parts.length === message.content.parts.length) return [message];
23223
+ if (parts.length === 0) return [];
23224
+ return [{
23225
+ ...message,
23226
+ content: {
23227
+ ...message.content,
23228
+ parts
23229
+ }
23230
+ }];
23231
+ });
23232
+ }
20996
23233
  //#endregion
20997
23234
  //#region src/processors/observational-memory/thresholds.ts
20998
23235
  /**
@@ -21150,8 +23387,9 @@ var ObservationStrategy = class ObservationStrategy {
21150
23387
  if (fresh?.lastObservedAt && record.lastObservedAt && fresh.lastObservedAt > record.lastObservedAt) return { observed: false };
21151
23388
  }
21152
23389
  const { messages, existingObservations } = await this.prepare();
23390
+ const observationMessages = stripSubconsciousSignals(messages);
21153
23391
  await this.emitStartMarkers(cycleId);
21154
- const output = await this.observe(existingObservations, messages);
23392
+ const output = await this.observe(existingObservations, observationMessages);
21155
23393
  const processed = await this.process(output, existingObservations);
21156
23394
  await this.persist(processed);
21157
23395
  await this.emitEndMarkers(cycleId, processed);
@@ -21166,6 +23404,7 @@ var ObservationStrategy = class ObservationStrategy {
21166
23404
  abortSignal,
21167
23405
  mainAgent: this.opts.agent,
21168
23406
  sendSignal: this.opts.sendSignal,
23407
+ sendStateSignal: this.opts.sendStateSignal,
21169
23408
  reflectionHooks,
21170
23409
  requestContext,
21171
23410
  observabilityContext: this.opts.observabilityContext
@@ -21453,11 +23692,16 @@ var SyncObservationStrategy = class extends ObservationStrategy {
21453
23692
  values: result.extractedValues,
21454
23693
  failures: result.extractionFailures,
21455
23694
  previousValues: this.priorExtractedValues,
23695
+ rawObservations: result.observations,
23696
+ recentMessages: formatMessagesForObserver(this.opts.messages, { maxPartLength: 500 }),
21456
23697
  threadId: this.opts.threadId,
21457
23698
  resourceId: this.opts.resourceId,
21458
23699
  mainAgent: this.opts.agent,
21459
23700
  memory: this.deps.memory,
21460
23701
  sendSignal: this.opts.sendSignal,
23702
+ sendStateSignal: this.opts.sendStateSignal,
23703
+ writer: this.opts.writer,
23704
+ abortSignal: this.opts.abortSignal,
21461
23705
  requestContext: this.opts.requestContext
21462
23706
  });
21463
23707
  const output = {
@@ -21633,11 +23877,16 @@ var AsyncBufferObservationStrategy = class extends ObservationStrategy {
21633
23877
  values: result.extractedValues,
21634
23878
  failures: result.extractionFailures,
21635
23879
  previousValues: this.priorExtractedValues,
23880
+ rawObservations: result.observations,
23881
+ recentMessages: formatMessagesForObserver(messages, { maxPartLength: 500 }),
21636
23882
  threadId: this.opts.threadId,
21637
23883
  resourceId: this.opts.resourceId,
21638
23884
  mainAgent: this.opts.agent,
21639
23885
  memory: this.deps.memory,
21640
23886
  sendSignal: this.opts.sendSignal,
23887
+ sendStateSignal: this.opts.sendStateSignal,
23888
+ writer: this.opts.writer,
23889
+ abortSignal: this.opts.abortSignal,
21641
23890
  requestContext: this.opts.requestContext
21642
23891
  });
21643
23892
  return {
@@ -21972,11 +24221,25 @@ var ResourceScopedObservationStrategy = class extends ObservationStrategy {
21972
24221
  values: result.extractedValues,
21973
24222
  failures: result.extractionFailures,
21974
24223
  previousValues,
24224
+ rawObservations: result.observations,
24225
+ recentMessages: formatMessagesForObserver(threadMessages, { maxPartLength: 500 }),
21975
24226
  threadId,
21976
24227
  resourceId: this.resourceId,
21977
24228
  mainAgent: this.opts.agent,
21978
24229
  memory: this.deps.memory,
21979
- sendSignal: this.opts.sendSignal,
24230
+ sendSignal: this.opts.agent ? async (signal) => {
24231
+ const delivery = this.opts.agent.sendSignal(signal, {
24232
+ resourceId: this.resourceId,
24233
+ threadId,
24234
+ ifActive: { behavior: "deliver" },
24235
+ ifIdle: { behavior: "persist" }
24236
+ });
24237
+ await delivery.accepted;
24238
+ return delivery.signal;
24239
+ } : void 0,
24240
+ sendStateSignal: this.opts.sendStateSignal,
24241
+ writer: this.opts.writer,
24242
+ abortSignal: this.opts.abortSignal,
21980
24243
  requestContext: this.opts.requestContext
21981
24244
  });
21982
24245
  this.observationResults.push({
@@ -22302,6 +24565,9 @@ var ObservationStep = class {
22302
24565
  pendingTokens: statusSnapshot.pendingTokens,
22303
24566
  record: statusSnapshot.record,
22304
24567
  writer: this.turn.writer,
24568
+ agent: this.turn.agent,
24569
+ sendSignal: this.turn.sendSignal,
24570
+ sendStateSignal: this.turn.sendStateSignal,
22305
24571
  requestContext: this.turn.requestContext,
22306
24572
  observabilityContext: this.turn.observabilityContext
22307
24573
  }).catch((err) => {
@@ -22478,6 +24744,9 @@ var ObservationStep = class {
22478
24744
  messages: observableMessages,
22479
24745
  messageList,
22480
24746
  trigger: "turn-sync",
24747
+ agent: this.turn.agent,
24748
+ sendSignal: this.turn.sendSignal,
24749
+ sendStateSignal: this.turn.sendStateSignal,
22481
24750
  requestContext: this.turn.requestContext,
22482
24751
  writer: this.turn.writer,
22483
24752
  observabilityContext: this.turn.observabilityContext
@@ -22557,6 +24826,8 @@ var ObservationTurn = class {
22557
24826
  agent;
22558
24827
  /** Optional signal sender for processor-originated notifications. */
22559
24828
  sendSignal;
24829
+ /** Optional state signal sender for processor-originated snapshots. */
24830
+ sendStateSignal;
22560
24831
  /** Current actor model for this step. Updated by the processor before prepare(). */
22561
24832
  actorModelContext;
22562
24833
  /** The active assistant response message ID for this step. Updated by the processor before prepare(). */
@@ -22570,6 +24841,7 @@ var ObservationTurn = class {
22570
24841
  this.messageList = opts.messageList;
22571
24842
  this.agent = opts.agent;
22572
24843
  this.sendSignal = opts.sendSignal;
24844
+ this.sendStateSignal = opts.sendStateSignal;
22573
24845
  this.requestContext = opts.requestContext;
22574
24846
  this.observabilityContext = opts.observabilityContext;
22575
24847
  this.hooks = opts.hooks ?? {};
@@ -22682,6 +24954,7 @@ var ObservationTurn = class {
22682
24954
  writer: this.writer,
22683
24955
  agent: this.agent,
22684
24956
  sendSignal: this.sendSignal,
24957
+ sendStateSignal: this.sendStateSignal,
22685
24958
  requestContext: this.requestContext,
22686
24959
  currentModel: this.actorModelContext,
22687
24960
  observabilityContext: this.observabilityContext,
@@ -23090,6 +25363,7 @@ var ReflectorRunner = class {
23090
25363
  persistMarkerToMessage;
23091
25364
  getCompressionStartLevel;
23092
25365
  memory;
25366
+ onReflectionCommitted;
23093
25367
  mastra;
23094
25368
  constructor(opts) {
23095
25369
  this.reflectionConfig = opts.reflectionConfig;
@@ -23105,6 +25379,7 @@ var ReflectorRunner = class {
23105
25379
  this.getCompressionStartLevel = opts.getCompressionStartLevel;
23106
25380
  this.mastra = opts.mastra;
23107
25381
  this.memory = opts.memory;
25382
+ this.onReflectionCommitted = opts.onReflectionCommitted;
23108
25383
  }
23109
25384
  __registerMastra(mastra) {
23110
25385
  this.mastra = mastra;
@@ -23238,7 +25513,7 @@ var ReflectorRunner = class {
23238
25513
  finalProviderMetadata = result.providerMetadata ?? finalProviderMetadata;
23239
25514
  parsed = parseReflectorOutput(result.text, observations, activeExtractors);
23240
25515
  if (parsed.degenerate) {
23241
- omDebug(`[OM:callReflector] attempt #${attemptNumber}: degenerate repetition detected, treating as compression failure`);
25516
+ omDebug(`[OM:callReflector] attempt #${attemptNumber}: degenerate repetition detected, treating as compression failure. ${describeDegenerateOutput(result.text, 2e3)}`);
23242
25517
  reflectedTokens = originalTokens;
23243
25518
  } else reflectedTokens = this.tokenCounter.countObservations(parsed.observations);
23244
25519
  omDebug(`[OM:callReflector] attempt #${attemptNumber} parsed: reflectedTokens=${reflectedTokens}, targetThreshold=${targetThreshold}, compressionValid=${validateCompression(reflectedTokens, targetThreshold)}, parsedObsLen=${parsed.observations?.length}, degenerate=${parsed.degenerate ?? false}`);
@@ -23308,6 +25583,8 @@ var ReflectorRunner = class {
23308
25583
  mainAgent,
23309
25584
  memory: this.memory,
23310
25585
  sendSignal,
25586
+ writer: streamContext?.writer,
25587
+ abortSignal,
23311
25588
  requestContext
23312
25589
  });
23313
25590
  const extractedValues = hookedValues.values;
@@ -23463,7 +25740,7 @@ var ReflectorRunner = class {
23463
25740
  * "activated", "no buffer present", and "suppressed by overshoot guard"
23464
25741
  * without re-deriving that state.
23465
25742
  */
23466
- async tryActivateBufferedReflection(record, lockKey, writer, messageList, activationMetadata) {
25743
+ async tryActivateBufferedReflection(record, lockKey, writer, messageList, activationMetadata, committedContext) {
23467
25744
  const bufferKey = this.buffering.getReflectionBufferKey(lockKey);
23468
25745
  const asyncOp = BufferingCoordinator.asyncBufferingOps.get(bufferKey);
23469
25746
  if (asyncOp) if (activationMetadata?.triggeredBy === "ttl" || activationMetadata?.triggeredBy === "provider_change") omDebug(`[OM:reflect] tryActivateBufferedReflection: async op in progress, not blocking for ${activationMetadata.triggeredBy} trigger`);
@@ -23482,7 +25759,8 @@ var ReflectorRunner = class {
23482
25759
  }
23483
25760
  const beforeTokens = freshRecord.observationTokenCount ?? 0;
23484
25761
  const reflectedLineCount = freshRecord.reflectedObservationLineCount ?? 0;
23485
- const unreflectedLines = (freshRecord.activeObservations ?? "").split("\n").slice(reflectedLineCount);
25762
+ const allLines = (freshRecord.activeObservations ?? "").split("\n");
25763
+ const unreflectedLines = allLines.slice(reflectedLineCount);
23486
25764
  const unreflectedContent = unreflectedLines.join("\n").trim();
23487
25765
  const combinedObservations = unreflectedContent ? `${freshRecord.bufferedReflection}\n\n${unreflectedContent}` : freshRecord.bufferedReflection;
23488
25766
  const combinedTokenCount = this.tokenCounter.countObservations(combinedObservations);
@@ -23513,6 +25791,11 @@ var ReflectorRunner = class {
23513
25791
  currentRecord: freshRecord,
23514
25792
  tokenCount: combinedTokenCount
23515
25793
  });
25794
+ if (committedContext) await this.notifyReflectionCommitted({
25795
+ ...committedContext,
25796
+ observations: allLines.slice(0, reflectedLineCount).join("\n").trim(),
25797
+ writer
25798
+ });
23516
25799
  BufferingCoordinator.lastBufferedBoundary.delete(bufferKey);
23517
25800
  const afterRecord = await this.storage.getObservationalMemory(record.threadId, record.resourceId);
23518
25801
  const afterTokens = afterRecord?.observationTokenCount ?? 0;
@@ -23548,13 +25831,21 @@ var ReflectorRunner = class {
23548
25831
  BufferingCoordinator.reflectionBufferCycleIds.delete(bufferKey);
23549
25832
  return { status: "activated" };
23550
25833
  }
25834
+ async notifyReflectionCommitted(context) {
25835
+ if (!this.onReflectionCommitted || !context.parentThreadId || !context.resourceId) return;
25836
+ try {
25837
+ await this.onReflectionCommitted(context);
25838
+ } catch (error) {
25839
+ omDebug(`[OM:reflect] post-commit reflection agent failed: ${error}`);
25840
+ }
25841
+ }
23551
25842
  /**
23552
25843
  * Check if reflection needed and trigger if so.
23553
25844
  * Supports both synchronous reflection and async buffered reflection.
23554
25845
  * @internal Used by observation strategies. Do not call directly.
23555
25846
  */
23556
25847
  async maybeReflect(opts) {
23557
- const { record, observationTokens, writer, abortSignal, mainAgent, sendSignal, messageList, currentModel, reflectionHooks, requestContext, observabilityContext, lastActivityAt, threadId: requestedThreadId } = opts;
25848
+ const { record, observationTokens, writer, abortSignal, mainAgent, sendSignal, sendStateSignal, messageList, currentModel, reflectionHooks, requestContext, observabilityContext, lastActivityAt, threadId: requestedThreadId } = opts;
23558
25849
  const lockKey = this.buffering.getLockKey(record.threadId, record.resourceId);
23559
25850
  const reflectThreshold = getMaxThreshold(this.getEffectiveReflectionTokens(record));
23560
25851
  const priorExtractedValues = await getThreadExtractedValues(this.storage, this.reflectionConfig.extractors, requestedThreadId ?? record.threadId);
@@ -23599,7 +25890,15 @@ var ReflectorRunner = class {
23599
25890
  await this.storage.setReflectingFlag(record.id, false);
23600
25891
  }
23601
25892
  if (this.buffering.isAsyncReflectionEnabled()) {
23602
- const activationResult = await this.tryActivateBufferedReflection(record, lockKey, writer, messageList, activationMetadata);
25893
+ const activationResult = await this.tryActivateBufferedReflection(record, lockKey, writer, messageList, activationMetadata, {
25894
+ parentThreadId: requestedThreadId ?? record.threadId ?? "",
25895
+ resourceId: record.resourceId ?? "",
25896
+ requestContext,
25897
+ mainAgent,
25898
+ sendStateSignal,
25899
+ abortSignal,
25900
+ observabilityContext
25901
+ });
23603
25902
  if (activationResult.status === "activated") return;
23604
25903
  if (activationResult.status === "suppressed") {
23605
25904
  omDebug(`[OM:reflect] skipping sync fallback / re-buffer after suppressed early ${activationMetadata.triggeredBy} activation (reason=${activationResult.reason})`);
@@ -23670,6 +25969,17 @@ var ReflectorRunner = class {
23670
25969
  reflection: reflectResult.observations,
23671
25970
  tokenCount: reflectionTokenCount
23672
25971
  });
25972
+ await this.notifyReflectionCommitted({
25973
+ parentThreadId: requestedThreadId ?? record.threadId ?? "",
25974
+ resourceId: record.resourceId ?? "",
25975
+ observations: record.activeObservations ?? "",
25976
+ requestContext,
25977
+ mainAgent,
25978
+ sendStateSignal,
25979
+ writer,
25980
+ abortSignal,
25981
+ observabilityContext
25982
+ });
23673
25983
  if (writer && streamContext) {
23674
25984
  const endMarker = createObservationEndMarker({
23675
25985
  cycleId: streamContext.cycleId,
@@ -24199,6 +26509,7 @@ var ObservationalMemory = class ObservationalMemory {
24199
26509
  hasher = (0, xxhash_wasm.default)();
24200
26510
  mastra;
24201
26511
  memory;
26512
+ curationCadence;
24202
26513
  /**
24203
26514
  * Track message IDs observed during this instance's lifetime.
24204
26515
  * Prevents re-observing messages when per-thread lastObservedAt cursors
@@ -24296,6 +26607,7 @@ var ObservationalMemory = class ObservationalMemory {
24296
26607
  this.hooks = config.hooks;
24297
26608
  this.mastra = config.mastra;
24298
26609
  this.memory = config.memory;
26610
+ this.curationCadence = config.curationCadence;
24299
26611
  const resolveModel = (model, defaultModel) => model === "default" ? defaultModel : model;
24300
26612
  const observationModel = resolveModel(config.model, OBSERVATIONAL_MEMORY_DEFAULTS.observation.model) ?? resolveModel(config.observation?.model, OBSERVATIONAL_MEMORY_DEFAULTS.observation.model) ?? resolveModel(config.reflection?.model, OBSERVATIONAL_MEMORY_DEFAULTS.observation.model) ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.model;
24301
26613
  const reflectionModel = resolveModel(config.model, OBSERVATIONAL_MEMORY_DEFAULTS.reflection.model) ?? resolveModel(config.reflection?.model, OBSERVATIONAL_MEMORY_DEFAULTS.reflection.model) ?? resolveModel(config.observation?.model, OBSERVATIONAL_MEMORY_DEFAULTS.reflection.model) ?? OBSERVATIONAL_MEMORY_DEFAULTS.reflection.model;
@@ -24391,7 +26703,8 @@ var ObservationalMemory = class ObservationalMemory {
24391
26703
  getCompressionStartLevel: (rc) => this.getCompressionStartLevel(rc),
24392
26704
  resolveModel: (inputTokens) => this.resolveReflectionModel(inputTokens),
24393
26705
  mastra: config.mastra,
24394
- memory: this.memory
26706
+ memory: this.memory,
26707
+ onReflectionCommitted: config.onReflectionCommitted
24395
26708
  });
24396
26709
  this.validateBufferConfig();
24397
26710
  omDebug(`[OM:init] new ObservationalMemory instance created — scope=${this.scope}, messageTokens=${JSON.stringify(this.observationConfig.messageTokens)}, obsAsyncEnabled=${this.buffering.isAsyncObservationEnabled()}, bufferTokens=${this.observationConfig.bufferTokens}, bufferActivation=${this.observationConfig.bufferActivation}, blockAfter=${this.observationConfig.blockAfter}, reflectionTokens=${this.reflectionConfig.observationTokens}, refAsyncEnabled=${this.buffering.isAsyncReflectionEnabled()}, refAsyncActivation=${this.reflectionConfig.bufferActivation}, refBlockAfter=${this.reflectionConfig.blockAfter}`);
@@ -25865,6 +28178,7 @@ ${formattedMessages}
25865
28178
  writer,
25866
28179
  agent: opts.agent,
25867
28180
  sendSignal: opts.sendSignal,
28181
+ sendStateSignal: opts.sendStateSignal,
25868
28182
  requestContext,
25869
28183
  currentModel: opts.currentModel,
25870
28184
  observabilityContext
@@ -26209,6 +28523,8 @@ ${formattedMessages}
26209
28523
  messageList: opts.messageList,
26210
28524
  reflectionHooks,
26211
28525
  agent: opts.agent,
28526
+ sendSignal: opts.sendSignal,
28527
+ sendStateSignal: opts.sendStateSignal,
26212
28528
  requestContext,
26213
28529
  writer: opts.writer,
26214
28530
  observabilityContext: opts.observabilityContext
@@ -26229,6 +28545,9 @@ ${formattedMessages}
26229
28545
  });
26230
28546
  const record = await this.getOrCreateRecord(threadId, resourceId);
26231
28547
  const reflected = record.generationCount > generationBefore && generationBefore >= 0;
28548
+ if (observed) this.maybeTriggerCadenceCuration(threadId, resourceId, record, requestContext).catch((error) => {
28549
+ omDebug(`[OM:observe] cadence curation failed: ${error instanceof Error ? error.message : String(error)}`);
28550
+ });
26232
28551
  return {
26233
28552
  observed,
26234
28553
  reflected,
@@ -26236,6 +28555,31 @@ ${formattedMessages}
26236
28555
  };
26237
28556
  }
26238
28557
  /**
28558
+ * Count committed observation runs on the sync observe path and run the curator every
28559
+ * `curationCadence` runs. The counter is persisted at `record.config.subconscious.observationRuns`
28560
+ * (the OM record's config jsonb, deep-merged; threshold resolution only reads `_overrides`, so the
28561
+ * sibling key is inert). Concurrent commits can miscount — the curator run is advisory, and the
28562
+ * curation cursor is the real serializer. The async-buffer lane bypasses observe(); deployments
28563
+ * that gate on this cadence (factory's resource scope) disable async buffering.
28564
+ */
28565
+ async maybeTriggerCadenceCuration(threadId, resourceId, record, requestContext) {
28566
+ const cadence = this.curationCadence;
28567
+ const memory = this.memory;
28568
+ if (!cadence || cadence < 1 || !memory) return;
28569
+ const runs = ((record.config ?? {}).subconscious?.observationRuns ?? 0) + 1;
28570
+ const fire = runs >= cadence;
28571
+ await this.storage.updateObservationalMemoryConfig({
28572
+ id: record.id,
28573
+ config: { subconscious: { observationRuns: fire ? 0 : runs } }
28574
+ });
28575
+ if (!fire) return;
28576
+ omDebug(`[OM:observe] cadence curation outcome=${(await memory.runCuration({
28577
+ threadId,
28578
+ resourceId: resourceId ?? threadId,
28579
+ requestContext
28580
+ })).outcome} thread=${threadId}`);
28581
+ }
28582
+ /**
26239
28583
  * Manually trigger reflection with optional guidance prompt.
26240
28584
  *
26241
28585
  * @example
@@ -26253,6 +28597,18 @@ ${formattedMessages}
26253
28597
  record,
26254
28598
  usage: void 0
26255
28599
  };
28600
+ if (record.isReflecting) {
28601
+ if (isOpActiveInProcess(record.id, "reflecting")) {
28602
+ omDebug(`[OM:reflect] isReflecting=true and active in this process, skipping`);
28603
+ return {
28604
+ reflected: false,
28605
+ record,
28606
+ usage: void 0
28607
+ };
28608
+ }
28609
+ omDebug(`[OM:reflect] isReflecting=true but NOT active in this process — stale flag from dead process, clearing`);
28610
+ await this.storage.setReflectingFlag(record.id, false);
28611
+ }
26256
28612
  await this.storage.setReflectingFlag(record.id, true);
26257
28613
  registerOp(record.id, "reflecting");
26258
28614
  const hooks = this.composeHooks(void 0, {
@@ -26438,6 +28794,8 @@ ${formattedMessages}
26438
28794
  resourceId: opts.resourceId,
26439
28795
  messageList: opts.messageList,
26440
28796
  agent: opts.agent,
28797
+ sendSignal: opts.sendSignal,
28798
+ sendStateSignal: opts.sendStateSignal,
26441
28799
  observabilityContext: opts.observabilityContext,
26442
28800
  hooks: opts.hooks
26443
28801
  });
@@ -26664,6 +29022,7 @@ var ObservationalMemoryProcessor = class {
26664
29022
  });
26665
29023
  this.turn.writer = writer;
26666
29024
  this.turn.sendSignal = args.sendSignal;
29025
+ this.turn.sendStateSignal = args.sendStateSignal;
26667
29026
  this.turn.agent = args.agent;
26668
29027
  this.turn.requestContext = requestContext;
26669
29028
  await this.turn.start(this.memory);
@@ -26808,6 +29167,7 @@ function getObservationsAsOf(activeObservations, asOf) {
26808
29167
  //#region src/processors/observational-memory/index.ts
26809
29168
  var observational_memory_exports = /* @__PURE__ */ __exportAll({
26810
29169
  Extractor: () => Extractor,
29170
+ KnowledgeSemanticIndexCoordinator: () => KnowledgeSemanticIndexCoordinator,
26811
29171
  ModelByInputTokens: () => ModelByInputTokens,
26812
29172
  OBSERVATIONAL_MEMORY_DEFAULTS: () => OBSERVATIONAL_MEMORY_DEFAULTS,
26813
29173
  OBSERVATION_CONTEXT_INSTRUCTIONS: () => OBSERVATION_CONTEXT_INSTRUCTIONS,
@@ -26816,10 +29176,16 @@ var observational_memory_exports = /* @__PURE__ */ __exportAll({
26816
29176
  OBSERVER_SYSTEM_PROMPT: () => OBSERVER_SYSTEM_PROMPT,
26817
29177
  ObservationalMemory: () => ObservationalMemory,
26818
29178
  ObservationalMemoryProcessor: () => ObservationalMemoryProcessor,
29179
+ SUBCONSCIOUS_ACTIVITY_STATE_ID: () => SUBCONSCIOUS_ACTIVITY_STATE_ID,
29180
+ StaleKnowledgeSemanticIndexError: () => StaleKnowledgeSemanticIndexError,
29181
+ Subconscious: () => Subconscious,
29182
+ SubconsciousCaptureExtractor: () => SubconsciousCaptureExtractor,
29183
+ SubconsciousRemindExtractor: () => SubconsciousRemindExtractor,
26819
29184
  TokenCounter: () => TokenCounter,
26820
29185
  WorkingMemoryExtractor: () => WorkingMemoryExtractor,
26821
29186
  buildObserverPrompt: () => buildObserverPrompt,
26822
29187
  buildObserverSystemPrompt: () => buildObserverSystemPrompt,
29188
+ buildSubconsciousActivitySnapshot: () => buildSubconsciousActivitySnapshot,
26823
29189
  combineObservationGroupRanges: () => combineObservationGroupRanges,
26824
29190
  deriveObservationGroupProvenance: () => deriveObservationGroupProvenance,
26825
29191
  extractCurrentTask: () => extractCurrentTask,
@@ -26831,10 +29197,13 @@ var observational_memory_exports = /* @__PURE__ */ __exportAll({
26831
29197
  parseAnchorId: () => parseAnchorId,
26832
29198
  parseObservationGroups: () => parseObservationGroups,
26833
29199
  parseObserverOutput: () => parseObserverOutput,
29200
+ publishSubconsciousActivity: () => publishSubconsciousActivity,
26834
29201
  reconcileObservationGroupsFromReflection: () => reconcileObservationGroupsFromReflection,
26835
29202
  renderObservationGroupsForReflection: () => renderObservationGroupsForReflection,
29203
+ renderSubconsciousActivity: () => renderSubconsciousActivity,
26836
29204
  stripEphemeralAnchorIds: () => stripEphemeralAnchorIds,
26837
29205
  stripObservationGroups: () => stripObservationGroups,
29206
+ subconsciousCaptureSchema: () => subconsciousCaptureSchema,
26838
29207
  summarizeConversation: () => summarizeConversation,
26839
29208
  wrapInObservationGroup: () => wrapInObservationGroup
26840
29209
  });
@@ -27077,10 +29446,11 @@ const DEFAULT_EMBEDDING_CACHE_MAX_SIZE = 1e3;
27077
29446
  * Concrete implementation of MastraMemory that adds support for thread configuration
27078
29447
  * and message injection.
27079
29448
  */
27080
- var Memory = class extends _mastra_core_memory.MastraMemory {
29449
+ var Memory = class Memory extends _mastra_core_memory.MastraMemory {
27081
29450
  _omEngine;
27082
29451
  _omEngineInstance;
27083
29452
  _mastraInstance;
29453
+ _knowledgeSemanticIndex;
27084
29454
  /**
27085
29455
  * Every vector cleanup that deleteThread or deleteMessages started in the background.
27086
29456
  * Callers do not wait for the cleanup, so this handle is the only join point.
@@ -27128,7 +29498,60 @@ var Memory = class extends _mastra_core_memory.MastraMemory {
27128
29498
  else this._omEngine?.then((engine) => engine?.__registerMastra(mastra));
27129
29499
  }
27130
29500
  getMergedThreadConfig(config) {
27131
- return this.applyManagedWorkingMemoryDefaults(super.getMergedThreadConfig(config));
29501
+ const merged = super.getMergedThreadConfig(config);
29502
+ return this.applyManagedWorkingMemoryDefaults(this.applySubconsciousDefaults(merged));
29503
+ }
29504
+ applySubconsciousDefaults(config) {
29505
+ const omConfig = normalizeObservationalMemoryConfig(config.observationalMemory);
29506
+ if (!omConfig?.experimental_subconscious) return config;
29507
+ if (!(omConfig.experimental_subconscious instanceof Subconscious)) throw new Error("observationalMemory.experimental_subconscious must be a Subconscious instance.");
29508
+ const observation = omConfig.observation ?? {};
29509
+ const extract = observation.extract ?? [];
29510
+ const existingSlugs = new Set(extract.map((extractor) => extractor.slug));
29511
+ const subconsciousExtractors = omConfig.experimental_subconscious.createObservationExtractors(observation.model ?? omConfig.model).filter((extractor) => !existingSlugs.has(extractor.slug));
29512
+ return {
29513
+ ...config,
29514
+ observationalMemory: {
29515
+ ...omConfig,
29516
+ observation: {
29517
+ ...observation,
29518
+ extract: [...extract, ...subconsciousExtractors]
29519
+ }
29520
+ }
29521
+ };
29522
+ }
29523
+ /** Threads with a curation currently in flight in this process; guards same-process double-fire only. */
29524
+ _curationsInFlight = /* @__PURE__ */ new Set();
29525
+ /**
29526
+ * Run the subconscious curator directly over the pending fact worklist, without a reflection.
29527
+ * Cross-process serialization is the curation cursor's job; a lost race wastes one advisory run.
29528
+ *
29529
+ * Outcomes: `ran` (curator executed), `no-op` (empty worklist and no prompt, or no curate agent
29530
+ * configured), `skipped` (a curation for this thread is already in flight in this process), and
29531
+ * `no-model` (no per-agent, observational memory, or main-agent model could be resolved).
29532
+ */
29533
+ async runCuration(options) {
29534
+ const omConfig = normalizeObservationalMemoryConfig(this.threadConfig.observationalMemory);
29535
+ const subconscious = omConfig?.experimental_subconscious;
29536
+ if (!omConfig || !(subconscious instanceof Subconscious)) return { outcome: "no-op" };
29537
+ if (this._curationsInFlight.has(options.threadId)) return { outcome: "skipped" };
29538
+ this._curationsInFlight.add(options.threadId);
29539
+ try {
29540
+ return { outcome: await createCuratorHandler(this, subconscious.resolved, new Memory({
29541
+ storage: this.storage,
29542
+ options: { observationalMemory: false }
29543
+ }), { omModel: omConfig.observation?.model ?? omConfig.model })({
29544
+ parentThreadId: options.threadId,
29545
+ resourceId: options.resourceId,
29546
+ observations: options.prompt ?? "",
29547
+ requestContext: options.requestContext
29548
+ }) === "ran" ? "ran" : "no-op" };
29549
+ } catch (error) {
29550
+ if (error instanceof Error && error.message.includes("requires the main agent to resolve its model")) return { outcome: "no-model" };
29551
+ throw error;
29552
+ } finally {
29553
+ this._curationsInFlight.delete(options.threadId);
29554
+ }
27132
29555
  }
27133
29556
  applyManagedWorkingMemoryDefaults(config) {
27134
29557
  const omConfig = normalizeObservationalMemoryConfig(config.observationalMemory);
@@ -27172,6 +29595,28 @@ var Memory = class extends _mastra_core_memory.MastraMemory {
27172
29595
  if (!this.vector) throw new Error("`retrieval: { vector: true }` requires a vector store. Pass a `vector` option to your Memory instance.");
27173
29596
  if (!this.embedder) throw new Error("`retrieval: { vector: true }` requires an embedder. Pass an `embedder` option to your Memory instance.");
27174
29597
  }
29598
+ if (omConfig?.experimental_subconscious) {
29599
+ if (!this.vector) throw new Error("Subconscious semantic knowledge requires a vector store. Pass a `vector` option to Memory.");
29600
+ if (!this.embedder) throw new Error("Subconscious semantic knowledge requires an embedder. Pass an `embedder` option to Memory.");
29601
+ }
29602
+ }
29603
+ async getKnowledgeStore() {
29604
+ const store = await this.storage.getStore("knowledge");
29605
+ if (!store) throw new Error(`Knowledge storage domain is not available on ${this.storage.constructor.name}`);
29606
+ return store;
29607
+ }
29608
+ async getKnowledgeSemanticIndex() {
29609
+ if (!this.vector || !this.embedder) throw new Error("Subconscious semantic knowledge requires both a vector store and an embedder.");
29610
+ this._knowledgeSemanticIndex ??= this.getKnowledgeStore().then((knowledge) => new KnowledgeSemanticIndexCoordinator({
29611
+ knowledge,
29612
+ vector: this.vector,
29613
+ embedder: this.embedder,
29614
+ embedderOptions: this.embedderOptions
29615
+ }));
29616
+ return this._knowledgeSemanticIndex;
29617
+ }
29618
+ async drainKnowledgeSemanticIndex(scope) {
29619
+ return (await this.getKnowledgeSemanticIndex()).drain(scope);
27175
29620
  }
27176
29621
  /**
27177
29622
  * Gets the memory storage domain, throwing if not available.
@@ -27940,6 +30385,7 @@ ${workingMemory}`;
27940
30385
  if (!_mastra_core_features.coreFeatures.has("observationalMemory")) throw new Error("Observational memory is enabled but the installed version of @mastra/core does not support it. Please upgrade @mastra/core to a version that includes observational memory support.");
27941
30386
  if (omConfig.observation?.bufferTokens !== false && !_mastra_core_features.coreFeatures.has("asyncBuffering")) throw new Error("Observational memory async buffering is enabled by default but the installed version of @mastra/core does not support it. Either upgrade @mastra/core, @mastra/memory, and your storage adapter (@mastra/libsql, @mastra/pg, @mastra/mongodb, or @mastra/convex) to the latest version, or explicitly disable async buffering by setting `observation: { bufferTokens: false }` in your observationalMemory config.");
27942
30387
  if (!_mastra_core_features.coreFeatures.has("request-response-id-rotation")) throw new Error("Observational memory requires @mastra/core support for request-response-id-rotation. Please bump @mastra/core to a newer version.");
30388
+ if (omConfig.experimental_subconscious) await this.getKnowledgeStore();
27943
30389
  const { ObservationalMemory: OMClass } = await Promise.resolve().then(() => observational_memory_exports);
27944
30390
  const onIndexObservations = this.hasRetrievalSearch(omConfig.retrieval) ? async (observation) => {
27945
30391
  await this.indexObservation(observation);
@@ -27953,9 +30399,21 @@ ${workingMemory}`;
27953
30399
  activateOnProviderChange: omConfig.activateOnProviderChange,
27954
30400
  shareTokenBudget: omConfig.shareTokenBudget,
27955
30401
  model: omConfig.model,
30402
+ curationCadence: omConfig.experimental_subconscious instanceof Subconscious ? omConfig.experimental_subconscious.resolved.curationCadence : void 0,
27956
30403
  mastra: this._mastraInstance,
27957
30404
  onIndexObservations,
27958
30405
  hooks: omConfig.hooks,
30406
+ onReflectionCommitted: omConfig.experimental_subconscious instanceof Subconscious ? (() => {
30407
+ const resolved = omConfig.experimental_subconscious.resolved;
30408
+ const omModel = omConfig.observation?.model ?? omConfig.model;
30409
+ return composeReflectionAgentHandlers([createCuratorHandler(this, resolved, new Memory({
30410
+ storage: this.storage,
30411
+ options: { observationalMemory: false }
30412
+ }), { omModel }), createLearnerHandler(this, resolved, new Memory({
30413
+ storage: this.storage,
30414
+ options: { observationalMemory: false }
30415
+ }), { omModel })]);
30416
+ })() : void 0,
27959
30417
  observation: omConfig.observation ? {
27960
30418
  model: omConfig.observation.model,
27961
30419
  messageTokens: omConfig.observation.messageTokens,
@@ -28345,6 +30803,7 @@ Notes:
28345
30803
  retrievalScope: typeof omConfig.retrieval === "object" ? omConfig.retrieval.scope ?? "resource" : "resource",
28346
30804
  searchEnabled: this.hasRetrievalSearch(omConfig.retrieval)
28347
30805
  });
30806
+ if (omConfig?.experimental_subconscious instanceof Subconscious && omConfig.experimental_subconscious.resolved.tools) Object.assign(tools, createKnowledgeTools(this));
28348
30807
  return tools;
28349
30808
  }
28350
30809
  /**
@@ -28825,6 +31284,8 @@ Notes:
28825
31284
  if (om) processors.push(om);
28826
31285
  const wm = await this.createWorkingMemoryStateProcessor(configuredProcessors, context);
28827
31286
  if (wm) processors.push(wm);
31287
+ const pins = await this.createPinnedStateProcessor(configuredProcessors, context);
31288
+ if (pins) processors.push(pins);
28828
31289
  return processors;
28829
31290
  }
28830
31291
  /**
@@ -28872,6 +31333,21 @@ Notes:
28872
31333
  if (configuredProcessors.some((p) => !("workflow" in p) && p.id === WORKING_MEMORY_STATE_PROCESSOR_ID)) return null;
28873
31334
  return new WorkingMemoryStateProcessor(this, runtimeMemory?.memoryConfig);
28874
31335
  }
31336
+ /**
31337
+ * Creates a PinnedStateProcessor when Subconscious pins are enabled on the
31338
+ * merged thread config. The gate is the validated `resolved.pins` on the
31339
+ * Subconscious instance, never the raw user object. Returns null when pins
31340
+ * are off or the processor is already present in the user's configured
31341
+ * processors.
31342
+ */
31343
+ async createPinnedStateProcessor(configuredProcessors = [], context) {
31344
+ const runtimeMemory = context?.get("MastraMemory");
31345
+ const subconscious = normalizeObservationalMemoryConfig(this.getMergedThreadConfig(runtimeMemory?.memoryConfig).observationalMemory)?.experimental_subconscious;
31346
+ if (!(subconscious instanceof Subconscious) || subconscious.resolved.pins === false) return null;
31347
+ const { PinnedStateProcessor, SUBCONSCIOUS_PINS_STATE_ID } = await Promise.resolve().then(() => subconscious_exports);
31348
+ if (configuredProcessors.some((p) => !("workflow" in p) && p.id === SUBCONSCIOUS_PINS_STATE_ID)) return null;
31349
+ return new PinnedStateProcessor({ getKnowledgeStore: () => this.storage.getStore("knowledge") });
31350
+ }
28875
31351
  };
28876
31352
  //#endregion
28877
31353
  Object.defineProperty(exports, "Extractor", {
@@ -28880,6 +31356,12 @@ Object.defineProperty(exports, "Extractor", {
28880
31356
  return Extractor;
28881
31357
  }
28882
31358
  });
31359
+ Object.defineProperty(exports, "KnowledgeSemanticIndexCoordinator", {
31360
+ enumerable: true,
31361
+ get: function() {
31362
+ return KnowledgeSemanticIndexCoordinator;
31363
+ }
31364
+ });
28883
31365
  Object.defineProperty(exports, "Memory", {
28884
31366
  enumerable: true,
28885
31367
  get: function() {
@@ -28934,12 +31416,42 @@ Object.defineProperty(exports, "ObservationalMemoryProcessor", {
28934
31416
  return ObservationalMemoryProcessor;
28935
31417
  }
28936
31418
  });
31419
+ Object.defineProperty(exports, "SUBCONSCIOUS_ACTIVITY_STATE_ID", {
31420
+ enumerable: true,
31421
+ get: function() {
31422
+ return SUBCONSCIOUS_ACTIVITY_STATE_ID;
31423
+ }
31424
+ });
28937
31425
  Object.defineProperty(exports, "SUMMARIZE_THREAD_DEFAULTS", {
28938
31426
  enumerable: true,
28939
31427
  get: function() {
28940
31428
  return SUMMARIZE_THREAD_DEFAULTS;
28941
31429
  }
28942
31430
  });
31431
+ Object.defineProperty(exports, "StaleKnowledgeSemanticIndexError", {
31432
+ enumerable: true,
31433
+ get: function() {
31434
+ return StaleKnowledgeSemanticIndexError;
31435
+ }
31436
+ });
31437
+ Object.defineProperty(exports, "Subconscious", {
31438
+ enumerable: true,
31439
+ get: function() {
31440
+ return Subconscious;
31441
+ }
31442
+ });
31443
+ Object.defineProperty(exports, "SubconsciousCaptureExtractor", {
31444
+ enumerable: true,
31445
+ get: function() {
31446
+ return SubconsciousCaptureExtractor;
31447
+ }
31448
+ });
31449
+ Object.defineProperty(exports, "SubconsciousRemindExtractor", {
31450
+ enumerable: true,
31451
+ get: function() {
31452
+ return SubconsciousRemindExtractor;
31453
+ }
31454
+ });
28943
31455
  Object.defineProperty(exports, "TokenCounter", {
28944
31456
  enumerable: true,
28945
31457
  get: function() {
@@ -28982,6 +31494,12 @@ Object.defineProperty(exports, "buildObserverSystemPrompt", {
28982
31494
  return buildObserverSystemPrompt;
28983
31495
  }
28984
31496
  });
31497
+ Object.defineProperty(exports, "buildSubconsciousActivitySnapshot", {
31498
+ enumerable: true,
31499
+ get: function() {
31500
+ return buildSubconsciousActivitySnapshot;
31501
+ }
31502
+ });
28985
31503
  Object.defineProperty(exports, "combineObservationGroupRanges", {
28986
31504
  enumerable: true,
28987
31505
  get: function() {
@@ -29066,6 +31584,12 @@ Object.defineProperty(exports, "parseObserverOutput", {
29066
31584
  return parseObserverOutput;
29067
31585
  }
29068
31586
  });
31587
+ Object.defineProperty(exports, "publishSubconsciousActivity", {
31588
+ enumerable: true,
31589
+ get: function() {
31590
+ return publishSubconsciousActivity;
31591
+ }
31592
+ });
29069
31593
  Object.defineProperty(exports, "reconcileObservationGroupsFromReflection", {
29070
31594
  enumerable: true,
29071
31595
  get: function() {
@@ -29084,6 +31608,12 @@ Object.defineProperty(exports, "renderObservationGroupsForReflection", {
29084
31608
  return renderObservationGroupsForReflection;
29085
31609
  }
29086
31610
  });
31611
+ Object.defineProperty(exports, "renderSubconsciousActivity", {
31612
+ enumerable: true,
31613
+ get: function() {
31614
+ return renderSubconsciousActivity;
31615
+ }
31616
+ });
29087
31617
  Object.defineProperty(exports, "stripEphemeralAnchorIds", {
29088
31618
  enumerable: true,
29089
31619
  get: function() {
@@ -29096,6 +31626,12 @@ Object.defineProperty(exports, "stripObservationGroups", {
29096
31626
  return stripObservationGroups;
29097
31627
  }
29098
31628
  });
31629
+ Object.defineProperty(exports, "subconsciousCaptureSchema", {
31630
+ enumerable: true,
31631
+ get: function() {
31632
+ return subconsciousCaptureSchema;
31633
+ }
31634
+ });
29099
31635
  Object.defineProperty(exports, "summarizeConversation", {
29100
31636
  enumerable: true,
29101
31637
  get: function() {
@@ -29109,4 +31645,4 @@ Object.defineProperty(exports, "wrapInObservationGroup", {
29109
31645
  }
29110
31646
  });
29111
31647
 
29112
- //# sourceMappingURL=src-Drm6l_ZN.cjs.map
31648
+ //# sourceMappingURL=src-CxkQ-ICG.cjs.map